]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-9206 Enhance /api/components/search_projects response with organizations
authorGuillaume Jambet <guillaume.jambet@sonarsource.com>
Thu, 8 Mar 2018 11:17:35 +0000 (12:17 +0100)
committerGuillaume Jambet <guillaume.jambet@gmail.com>
Fri, 16 Mar 2018 09:01:03 +0000 (10:01 +0100)
server/sonar-server/src/main/java/org/sonar/server/component/ws/SearchProjectsAction.java
server/sonar-server/src/main/java/org/sonar/server/component/ws/SuggestionsAction.java
server/sonar-server/src/main/resources/org/sonar/server/component/ws/search_projects-example.json
server/sonar-server/src/test/java/org/sonar/server/component/ws/SearchProjectsActionTest.java
server/sonar-server/src/test/java/org/sonar/server/component/ws/SuggestionsActionTest.java
sonar-ws/src/main/protobuf/ws-components.proto

index 3e6fdee58ea587972f8ce8d93fa65f5ae4f9b39c..b936d79a242a98dd2500bfdb32f8e709e7c57eb2 100644 (file)
@@ -23,6 +23,7 @@ import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Ordering;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -58,16 +59,19 @@ import org.sonar.server.measure.index.ProjectMeasuresQuery;
 import org.sonar.server.project.Visibility;
 import org.sonar.server.user.UserSession;
 import org.sonarqube.ws.Common;
+import org.sonarqube.ws.Components;
 import org.sonarqube.ws.Components.Component;
 import org.sonarqube.ws.Components.SearchProjectsWsResponse;
 
 import static com.google.common.base.MoreObjects.firstNonNull;
 import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.collect.ImmutableList.of;
 import static com.google.common.collect.Sets.newHashSet;
 import static java.lang.String.format;
 import static java.util.Collections.emptyMap;
 import static java.util.Objects.requireNonNull;
 import static org.sonar.api.measures.CoreMetrics.ALERT_STATUS_KEY;
+import static org.sonar.api.server.ws.WebService.Param.FACETS;
 import static org.sonar.api.server.ws.WebService.Param.FIELDS;
 import static org.sonar.api.utils.DateUtils.formatDateTime;
 import static org.sonar.core.util.Protobuf.setNullable;
@@ -92,9 +96,11 @@ public class SearchProjectsAction implements ComponentsWsAction {
 
   public static final int MAX_PAGE_SIZE = 500;
   public static final int DEFAULT_PAGE_SIZE = 100;
+  private static final String ALL = "_all";
+  private static final String ORGANIZATIONS = "organizations";
   private static final String ANALYSIS_DATE = "analysisDate";
   private static final String LEAK_PERIOD_DATE = "leakPeriodDate";
-  private static final Set<String> POSSIBLE_FIELDS = newHashSet(ANALYSIS_DATE, LEAK_PERIOD_DATE);
+  private static final Set<String> POSSIBLE_FIELDS = newHashSet(ALL, ORGANIZATIONS, ANALYSIS_DATE, LEAK_PERIOD_DATE);
 
   private final DbClient dbClient;
   private final ProjectMeasuresIndex index;
@@ -130,7 +136,7 @@ public class SearchProjectsAction implements ComponentsWsAction {
       .setRequired(false)
       .setInternal(true)
       .setSince("6.3");
-    action.createParam(Param.FACETS)
+    action.createParam(FACETS)
       .setDescription("Comma-separated list of the facets to be computed. No facet is computed by default.")
       .setPossibleValues(SUPPORTED_FACETS.stream().sorted().collect(MoreCollectors.toList(SUPPORTED_FACETS.size())));
     action
@@ -299,11 +305,16 @@ public class SearchProjectsAction implements ComponentsWsAction {
       .setAsc(httpRequest.mandatoryParamAsBoolean(Param.ASCENDING))
       .setPage(httpRequest.mandatoryParamAsInt(Param.PAGE))
       .setPageSize(httpRequest.mandatoryParamAsInt(Param.PAGE_SIZE));
-    if (httpRequest.hasParam(Param.FACETS)) {
-      request.setFacets(httpRequest.paramAsStrings(Param.FACETS));
+    if (httpRequest.hasParam(FACETS)) {
+      request.setFacets(httpRequest.mandatoryParamAsStrings(FACETS));
     }
     if (httpRequest.hasParam(FIELDS)) {
-      request.setAdditionalFields(httpRequest.paramAsStrings(FIELDS));
+      List<String> paramsAsString = httpRequest.mandatoryParamAsStrings(FIELDS);
+      if (paramsAsString.contains(ALL)) {
+        request.setAdditionalFields(of(ORGANIZATIONS, ANALYSIS_DATE, LEAK_PERIOD_DATE));
+      } else {
+        request.setAdditionalFields(paramsAsString);
+      }
     }
     return request.build();
   }
@@ -312,6 +323,11 @@ public class SearchProjectsAction implements ComponentsWsAction {
     Function<ComponentDto, Component> dbToWsComponent = new DbToWsComponent(request, organizationsByUuid, searchResults.favoriteProjectUuids, searchResults.analysisByProjectUuid,
       userSession.isLoggedIn());
 
+    Map<String, OrganizationDto> organizationsByUuidForAdditionalInfo = new HashMap<>();
+    if (request.additionalFields.contains(ORGANIZATIONS)) {
+      organizationsByUuidForAdditionalInfo.putAll(organizationsByUuid);
+    }
+
     return Stream.of(SearchProjectsWsResponse.newBuilder())
       .map(response -> response.setPaging(Common.Paging.newBuilder()
         .setPageIndex(request.getPage())
@@ -323,6 +339,15 @@ public class SearchProjectsAction implements ComponentsWsAction {
           .forEach(response::addComponents);
         return response;
       })
+      .map(response -> {
+        organizationsByUuidForAdditionalInfo.values().stream().forEach(
+          dto -> response.addOrganizations(
+            Components.Organization.newBuilder()
+              .setKey(dto.getKey())
+              .setName(dto.getName())
+              .build()));
+        return response;
+      })
       .map(response -> addFacets(searchResults, response))
       .map(SearchProjectsWsResponse.Builder::build)
       .findFirst()
@@ -532,6 +557,7 @@ public class SearchProjectsAction implements ComponentsWsAction {
     public static RequestBuilder builder() {
       return new RequestBuilder();
     }
+
   }
 
   static class RequestBuilder {
index 3593bac8a499e7f712d5ba6692e0179630ab29df..6436512c13ffcfe9e54992342f202258d8a42df5 100644 (file)
@@ -72,7 +72,7 @@ import static org.sonar.core.util.stream.MoreCollectors.toSet;
 import static org.sonar.server.component.index.SuggestionQuery.DEFAULT_LIMIT;
 import static org.sonar.server.es.DefaultIndexSettings.MINIMUM_NGRAM_LENGTH;
 import static org.sonar.server.ws.WsUtils.writeProtobuf;
-import static org.sonarqube.ws.Components.SuggestionsWsResponse.Organization;
+import static org.sonarqube.ws.Components.Organization;
 import static org.sonarqube.ws.Components.SuggestionsWsResponse.newBuilder;
 import static org.sonarqube.ws.client.component.ComponentsWsParameters.ACTION_SUGGESTIONS;
 
index 41d9cfa4c751ae2a3c7b2b234a41c187effea141..942f218e1c5913d42f46fcee09c08a74e820ee82 100644 (file)
@@ -4,6 +4,16 @@
     "pageSize": 100,
     "total": 3
   },
+  "organizations": [
+    {
+      "key": "my-org-key-1",
+      "name": "Foo"
+    },
+    {
+      "key": "my-org-key-2",
+      "name": "Bar"
+    }
+  ],
   "components": [
     {
       "organization": "my-org-key-1",
index e1774f31f658a17a207f12260f2e3027eecc79ee..72b331123976e03f3aee82c0c6ad9711c387d378 100644 (file)
@@ -54,7 +54,6 @@ import org.sonar.server.measure.index.ProjectMeasuresIndexer;
 import org.sonar.server.permission.index.AuthorizationTypeSupport;
 import org.sonar.server.permission.index.PermissionIndexerTester;
 import org.sonar.server.tester.UserSessionRule;
-import org.sonar.server.ws.KeyExamples;
 import org.sonar.server.ws.TestRequest;
 import org.sonar.server.ws.WsActionTester;
 import org.sonarqube.ws.Common;
@@ -89,6 +88,9 @@ import static org.sonar.core.util.stream.MoreCollectors.toList;
 import static org.sonar.server.computation.task.projectanalysis.metric.Metric.MetricType.DATA;
 import static org.sonar.server.computation.task.projectanalysis.metric.Metric.MetricType.PERCENT;
 import static org.sonar.server.computation.task.projectanalysis.metric.Metric.MetricType.RATING;
+import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
+import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_002;
+import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_003;
 import static org.sonar.test.JsonAssert.assertJson;
 import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_FILTER;
 import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_ORGANIZATION;
@@ -183,9 +185,9 @@ public class SearchProjectsActionTest {
     assertThat(asc.defaultValue()).isEqualTo("true");
     assertThat(asc.possibleValues()).containsOnly("true", "false", "yes", "no");
 
-    Param additionalFields = def.param("f");
-    assertThat(additionalFields.defaultValue()).isNull();
-    assertThat(additionalFields.possibleValues()).containsOnly("analysisDate", "leakPeriodDate");
+    Param f = def.param("f");
+    assertThat(f.defaultValue()).isNull();
+    assertThat(f.possibleValues()).containsOnly("_all", "organizations", "analysisDate", "leakPeriodDate");
 
     Param facets = def.param("facets");
     assertThat(facets.defaultValue()).isNull();
@@ -196,31 +198,40 @@ public class SearchProjectsActionTest {
   @Test
   public void json_example() {
     userSession.logIn();
-    OrganizationDto organization1Dto = db.organizations().insertForKey("my-org-key-1");
-    OrganizationDto organization2Dto = db.organizations().insertForKey("my-org-key-2");
+    OrganizationDto organization2Dto = db.organizations().insert(dto-> dto.setKey("my-org-key-2").setName("Bar"));
+    OrganizationDto organization1Dto = db.organizations().insert(dto-> dto.setKey("my-org-key-1").setName("Foo"));
+
     MetricDto coverage = db.measures().insertMetric(c -> c.setKey(COVERAGE).setValueType(PERCENT.name()));
     ComponentDto project1 = insertProject(organization1Dto, c -> c
-      .setDbKey(KeyExamples.KEY_PROJECT_EXAMPLE_001)
+      .setDbKey(KEY_PROJECT_EXAMPLE_001)
       .setName("My Project 1")
       .setTagsString("finance, java"),
       new Measure(coverage, c -> c.setValue(80d)));
     ComponentDto project2 = insertProject(organization1Dto, c -> c
-      .setDbKey(KeyExamples.KEY_PROJECT_EXAMPLE_002)
+      .setDbKey(KEY_PROJECT_EXAMPLE_002)
       .setName("My Project 2"),
       new Measure(coverage, c -> c.setValue(90d)));
     ComponentDto project3 = insertProject(organization2Dto, c -> c
-      .setDbKey(KeyExamples.KEY_PROJECT_EXAMPLE_003)
+      .setDbKey(KEY_PROJECT_EXAMPLE_003)
       .setName("My Project 3")
       .setTagsString("sales, offshore, java"),
       new Measure(coverage, c -> c.setValue(20d)));
     addFavourite(project1);
 
-    String jsonResult = ws.newRequest().setParam(Param.FACETS, COVERAGE).execute().getInput();
-    SearchProjectsWsResponse protobufResult = ws.newRequest().setParam(Param.FACETS, COVERAGE).executeProtobuf(SearchProjectsWsResponse.class);
+    String jsonResult = ws.newRequest()
+      .setParam(FACETS, COVERAGE)
+      .setParam(FIELDS, "_all")
+      .execute().getInput();
 
     assertJson(jsonResult).withStrictArrayOrder().ignoreFields("id").isSimilarTo(ws.getDef().responseExampleAsString());
     assertJson(ws.getDef().responseExampleAsString()).ignoreFields("id").withStrictArrayOrder().isSimilarTo(jsonResult);
-    assertThat(protobufResult.getComponentsList()).extracting(Component::getId).containsExactly(project1.uuid(), project2.uuid(), project3.uuid());
+
+    SearchProjectsWsResponse protobufResult = ws.newRequest()
+      .setParam(FACETS, COVERAGE)
+      .executeProtobuf(SearchProjectsWsResponse.class);
+
+    assertThat(protobufResult.getComponentsList()).extracting(Component::getId)
+      .containsExactly(project1.uuid(), project2.uuid(), project3.uuid());
   }
 
   @Test
index 24b26f4ce15db1a22ccb011d8ec88a1e415f35df..5d8c55557c05b27a629077e4ebc054cc146a8c8b 100644 (file)
@@ -49,12 +49,12 @@ import org.sonar.server.tester.UserSessionRule;
 import org.sonar.server.ws.TestRequest;
 import org.sonar.server.ws.TestResponse;
 import org.sonar.server.ws.WsActionTester;
-import org.sonarqube.ws.MediaTypes;
+import org.sonarqube.ws.Components.Organization;
 import org.sonarqube.ws.Components.SuggestionsWsResponse;
 import org.sonarqube.ws.Components.SuggestionsWsResponse.Category;
-import org.sonarqube.ws.Components.SuggestionsWsResponse.Organization;
 import org.sonarqube.ws.Components.SuggestionsWsResponse.Project;
 import org.sonarqube.ws.Components.SuggestionsWsResponse.Suggestion;
+import org.sonarqube.ws.MediaTypes;
 
 import static java.util.Arrays.asList;
 import static java.util.Collections.singletonList;
index a995eb42cbc9c5c2d3d14f8cad8c0bcd65f7f69f..0218de0edeed75e27d4dfd17ddfb077d37de87c0 100644 (file)
@@ -69,11 +69,6 @@ message SuggestionsWsResponse {
     optional bool isFavorite = 7;
   }
 
-  message Organization {
-    optional string key = 1;
-    optional string name = 2;
-  }
-
   message Project {
     optional string key = 1;
     optional string name = 2;
@@ -83,8 +78,9 @@ message SuggestionsWsResponse {
 // WS api/components/search_projects
 message SearchProjectsWsResponse {
   optional sonarqube.ws.commons.Paging paging = 1;
-  repeated Component components = 2;
-  optional sonarqube.ws.commons.Facets facets = 3;
+  repeated Organization organizations = 2;
+  repeated Component components = 3;
+  optional sonarqube.ws.commons.Facets facets = 4;
 }
 
 // WS api/components/provisioned
@@ -129,3 +125,10 @@ message Component {
   }
 }
 
+message Organization {
+  optional string key = 1;
+  optional string name = 2;
+}
+
+
+