]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-7129 WS api/components/tree baseComponent and refId in response
authorStas Vilchik <vilchiks@gmail.com>
Tue, 19 Jan 2016 14:03:31 +0000 (15:03 +0100)
committerTeryk Bellahsene <teryk.bellahsene@sonarsource.com>
Tue, 19 Jan 2016 16:32:24 +0000 (17:32 +0100)
server/sonar-server/src/main/java/org/sonar/server/component/ws/TreeAction.java
server/sonar-server/src/main/resources/org/sonar/server/component/ws/tree-example.json
server/sonar-server/src/test/java/org/sonar/server/component/ws/TreeActionTest.java
sonar-ws/src/main/protobuf/ws-components.proto

index a67d38a0bf4b60c5b29942607056e692d307f8fb..5fc9911c3b60b0cbfff03b66439700933fa57a7f 100644 (file)
  */
 package org.sonar.server.component.ws;
 
+import com.google.common.base.Function;
+import com.google.common.base.Predicates;
 import com.google.common.collect.ImmutableSortedSet;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
 import org.sonar.api.i18n.I18n;
 import org.sonar.api.resources.ResourceTypes;
 import org.sonar.api.server.ws.Request;
@@ -45,8 +51,9 @@ import org.sonarqube.ws.WsComponents.TreeWsResponse;
 import org.sonarqube.ws.client.component.TreeWsRequest;
 
 import static com.google.common.base.Objects.firstNonNull;
-import static com.google.common.collect.Sets.newHashSet;
+import static com.google.common.collect.FluentIterable.from;
 import static java.lang.String.format;
+import static java.util.Collections.emptyMap;
 import static org.sonar.core.util.Uuids.UUID_EXAMPLE_02;
 import static org.sonar.server.component.ComponentFinder.ParamNames.BASE_COMPONENT_ID_AND_KEY;
 import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException;
@@ -89,40 +96,46 @@ public class TreeAction implements ComponentsWsAction {
   public void define(WebService.NewController context) {
     WebService.NewAction action = context.createAction(ACTION_TREE)
       .setDescription(format("Navigate through components based on the chosen strategy. The %s or the %s parameter must be provided.<br>" +
-          "Requires one of the following permissions:" +
-          "<ul>" +
-          "<li>'Administer System'</li>" +
-          "<li>'Administer' rights on the specified project</li>" +
-          "<li>'Browse' on the specified project</li>" +
-          "</ul>" +
-          "When limiting search with the %s parameter, directories are not returned.",
+        "Requires one of the following permissions:" +
+        "<ul>" +
+        "<li>'Administer System'</li>" +
+        "<li>'Administer' rights on the specified project</li>" +
+        "<li>'Browse' on the specified project</li>" +
+        "</ul>" +
+        "When limiting search with the %s parameter, directories are not returned.",
         PARAM_BASE_COMPONENT_ID, PARAM_BASE_COMPONENT_KEY, Param.TEXT_QUERY))
       .setSince("5.4")
       .setResponseExample(getClass().getResource("tree-example.json"))
       .setHandler(this)
-      .addSearchQuery("sonar", "component names", "component keys")
       .addPagingParams(100, MAX_SIZE);
 
-    action.createSortParams(newHashSet(SORTS), NAME_SORT, true)
-      .setDescription("Comma-separated list of sort fields")
-      .setExampleValue(NAME_SORT + ", " + PATH_SORT);
-
     action.createParam(PARAM_BASE_COMPONENT_ID)
-      .setDescription("Base component id. The search is based on this component. It is not included in the response.")
+      .setDescription("Base component id. The search is based on this component.")
       .setExampleValue(UUID_EXAMPLE_02);
 
     action.createParam(PARAM_BASE_COMPONENT_KEY)
-      .setDescription("Base component key.The search is based on this component. It is not included in the response.")
+      .setDescription("Base component key.The search is based on this component.")
       .setExampleValue(KEY_PROJECT_EXAMPLE_001);
 
+    action.createSortParams(SORTS, NAME_SORT, true)
+      .setDescription("Comma-separated list of sort fields")
+      .setExampleValue(NAME_SORT + ", " + PATH_SORT);
+
+    action.createParam(Param.TEXT_QUERY)
+      .setDescription("Limit search to: <ul>" +
+        "<li>component names that contain the supplied string</li>" +
+        "<li>component keys that are exactly the same as the supplied string</li>" +
+        "</ul>")
+      .setExampleValue("FILE_NAM");
+
     createQualifiersParameter(action, newQualifierParameterContext(userSession, i18n, resourceTypes));
 
     action.createParam(PARAM_STRATEGY)
-      .setDescription("Strategy to search for base component children:" +
+      .setDescription("Strategy to search for base component descendants:" +
         "<ul>" +
-        "<li>children: return the direct children components of the base component. Grandchildren components are not returned</li>" +
-        "<li>all: return all the children components of the base component. Grandchildren are returned.</li>" +
-        "<li>leaves: return all the children components (files, in general) which don't have other children. They are the leaves of the component tree.</li>" +
+        "<li>children: return the children components of the base component. Grandchildren components are not returned</li>" +
+        "<li>all: return all the descendants components of the base component. Grandchildren are returned. Base component is not returned.</li>" +
+        "<li>leaves: return all the descendant components (files, in general) which don't have other children. They are the leaves of the component tree.</li>" +
         "</ul>")
       .setPossibleValues(STRATEGIES)
       .setDefaultValue(ALL_STRATEGY);
@@ -141,7 +154,7 @@ public class TreeAction implements ComponentsWsAction {
       checkPermissions(baseComponent);
       SnapshotDto baseSnapshot = dbClient.snapshotDao().selectLastSnapshotByComponentId(dbSession, baseComponent.getId());
       if (baseSnapshot == null) {
-        return emptyResponse(treeWsRequest);
+        return emptyResponse(baseComponent, treeWsRequest);
       }
 
       ComponentTreeQuery query = toComponentTreeQuery(treeWsRequest, baseSnapshot);
@@ -160,14 +173,33 @@ public class TreeAction implements ComponentsWsAction {
         default:
           throw new IllegalStateException("Unknown component tree strategy");
       }
+      Map<Long, String> referenceComponentUuidsById = searchReferenceComponentUuidsById(dbSession, components);
 
-      return buildResponse(components,
+      return buildResponse(baseComponent, components, referenceComponentUuidsById,
         Paging.forPageIndex(query.getPage()).withPageSize(query.getPageSize()).andTotal(total));
     } finally {
       dbClient.closeSession(dbSession);
     }
   }
 
+  private Map<Long, String> searchReferenceComponentUuidsById(DbSession dbSession, List<ComponentDtoWithSnapshotId> components) {
+    List<Long> referenceComponentIds = from(components)
+      .transform(ComponentDtoWithSnapshotIdToCopyResourceIdFunction.INSTANCE)
+      .filter(Predicates.<Long>notNull())
+      .toList();
+    if (referenceComponentIds.isEmpty()) {
+      return emptyMap();
+    }
+
+    List<ComponentDto> referenceComponents = dbClient.componentDao().selectByIds(dbSession, referenceComponentIds);
+    Map<Long, String> referenceComponentUuidsById = new HashMap<>();
+    for (ComponentDto referenceComponent : referenceComponents) {
+      referenceComponentUuidsById.put(referenceComponent.getId(), referenceComponent.uuid());
+    }
+
+    return referenceComponentUuidsById;
+  }
+
   private void checkPermissions(ComponentDto baseComponent) {
     String projectUuid = firstNonNull(baseComponent.projectUuid(), baseComponent.uuid());
     if (!userSession.hasPermission(GlobalPermissions.SYSTEM_ADMIN) &&
@@ -177,7 +209,8 @@ public class TreeAction implements ComponentsWsAction {
     }
   }
 
-  private static TreeWsResponse buildResponse(List<ComponentDtoWithSnapshotId> components, Paging paging) {
+  private static TreeWsResponse buildResponse(ComponentDto baseComponent, List<ComponentDtoWithSnapshotId> components, Map<Long, String> referenceComponentUuidsById,
+    Paging paging) {
     TreeWsResponse.Builder response = TreeWsResponse.newBuilder();
     response.getPagingBuilder()
       .setPageIndex(paging.pageIndex())
@@ -185,14 +218,15 @@ public class TreeAction implements ComponentsWsAction {
       .setTotal(paging.total())
       .build();
 
+    response.setBaseComponent(componentDtoToWsComponent(baseComponent, referenceComponentUuidsById));
     for (ComponentDto dto : components) {
-      response.addComponents(componentDtoToWsComponent(dto));
+      response.addComponents(componentDtoToWsComponent(dto, referenceComponentUuidsById));
     }
 
     return response.build();
   }
 
-  private static WsComponents.Component.Builder componentDtoToWsComponent(ComponentDto dto) {
+  private static WsComponents.Component.Builder componentDtoToWsComponent(ComponentDto dto, Map<Long, String> referenceComponentUuidsById) {
     WsComponents.Component.Builder wsComponent = WsComponents.Component.newBuilder()
       .setId(dto.uuid())
       .setKey(dto.key())
@@ -204,16 +238,20 @@ public class TreeAction implements ComponentsWsAction {
     if (dto.description() != null) {
       wsComponent.setDescription(dto.description());
     }
+    if (!referenceComponentUuidsById.isEmpty() && referenceComponentUuidsById.get(dto.getCopyResourceId()) != null) {
+      wsComponent.setRefId(referenceComponentUuidsById.get(dto.getCopyResourceId()));
+    }
 
     return wsComponent;
   }
 
-  private static TreeWsResponse emptyResponse(TreeWsRequest request) {
+  private static TreeWsResponse emptyResponse(ComponentDto baseComponent, TreeWsRequest request) {
     TreeWsResponse.Builder response = TreeWsResponse.newBuilder();
     response.getPagingBuilder()
       .setTotal(0)
       .setPageIndex(request.getPage())
       .setPageSize(request.getPageSize());
+    response.setBaseComponent(componentDtoToWsComponent(baseComponent, Collections.<Long, String>emptyMap()));
 
     return response.build();
   }
@@ -274,4 +312,12 @@ public class TreeAction implements ComponentsWsAction {
 
     return treeWsRequest;
   }
+
+  private enum ComponentDtoWithSnapshotIdToCopyResourceIdFunction implements Function<ComponentDtoWithSnapshotId, Long> {
+    INSTANCE;
+    @Override
+    public Long apply(@Nonnull ComponentDtoWithSnapshotId input) {
+      return input.getCopyResourceId();
+    }
+  }
 }
index 85fac3978a5bd17a9f94110c108535db35b599c7..fd6c14a1bf892f793bcbbb9d1e507a053edaafc5 100644 (file)
@@ -4,6 +4,12 @@
     "pageSize": 100,
     "total": 3
   },
+  "baseComponent": {
+    "id": "MY_PROJECT_ID",
+    "key": "MY_PROJECT_KEY",
+    "name": "Project Name",
+    "qualifier": "TRK"
+  },
   "components": [
     {
       "id": "AVHE6Jo2EplJjXTo0Rzw",
index 63e46fc702b1637d0fe42af2b08a5e98d6ed81a7..cc5821ce1261d3552b5e6c228e478c9fe89cb476 100644 (file)
@@ -259,12 +259,12 @@ public class TreeActionTest {
     WsComponents.TreeWsResponse response = WsComponents.TreeWsResponse.parseFrom(responseStream);
 
     assertThat(response.getComponentsList()).extracting("id").containsExactly("project-uuid-1-copy", "sub-view-uuid");
+    assertThat(response.getComponentsList()).extracting("refId").containsExactly("project-uuid-1", "");
   }
 
   @Test
   public void empty_response_for_provisioned_project() throws IOException {
     componentDb.insertComponent(newProjectDto("project-uuid"));
-    db.commit();
 
     InputStream responseStream = ws.newRequest()
       .setMediaType(MediaTypes.PROTOBUF)
@@ -272,6 +272,7 @@ public class TreeActionTest {
       .execute().getInputStream();
     WsComponents.TreeWsResponse response = WsComponents.TreeWsResponse.parseFrom(responseStream);
 
+    assertThat(response.getBaseComponent().getId()).isEqualTo("project-uuid");
     assertThat(response.getComponentsList()).isEmpty();
     assertThat(response.getPaging().getTotal()).isEqualTo(0);
     assertThat(response.getPaging().getPageSize()).isEqualTo(100);
@@ -353,7 +354,9 @@ public class TreeActionTest {
   }
 
   private ComponentDto initJsonExampleComponents() throws IOException {
-    ComponentDto project = newProjectDto("AVHE6JiwEplJjXTo0Rza");
+    ComponentDto project = newProjectDto("MY_PROJECT_ID")
+      .setKey("MY_PROJECT_KEY")
+      .setName("Project Name");
     SnapshotDto projectSnapshot = componentDb.insertProjectAndSnapshot(project);
     Date now = new Date();
     JsonParser jsonParser = new JsonParser();
@@ -362,15 +365,15 @@ public class TreeActionTest {
     for (JsonElement componentAsJsonElement : components) {
       JsonObject componentAsJsonObject = componentAsJsonElement.getAsJsonObject();
       componentDb.insertComponentAndSnapshot(new ComponentDto()
-          .setUuid(getJsonField(componentAsJsonObject, "id"))
-          .setKey(getJsonField(componentAsJsonObject, "key"))
-          .setName(getJsonField(componentAsJsonObject, "name"))
-          .setPath(getJsonField(componentAsJsonObject, "path"))
-          .setProjectUuid(project.projectUuid())
-          .setQualifier(getJsonField(componentAsJsonObject, "qualifier"))
-          .setDescription(getJsonField(componentAsJsonObject, "description"))
-          .setEnabled(true)
-          .setCreatedAt(now),
+        .setUuid(getJsonField(componentAsJsonObject, "id"))
+        .setKey(getJsonField(componentAsJsonObject, "key"))
+        .setName(getJsonField(componentAsJsonObject, "name"))
+        .setPath(getJsonField(componentAsJsonObject, "path"))
+        .setProjectUuid(project.projectUuid())
+        .setQualifier(getJsonField(componentAsJsonObject, "qualifier"))
+        .setDescription(getJsonField(componentAsJsonObject, "description"))
+        .setEnabled(true)
+        .setCreatedAt(now),
         projectSnapshot);
     }
     db.commit();
index d9e86b0e0e4dbc3e83a51f4e58e5d71b83cbc6cf..9ff36b851a53f78835b08b300ad07301a79442e5 100644 (file)
@@ -35,8 +35,8 @@ message SearchWsResponse {
 // WS api/components/tree
 message TreeWsResponse {
   optional sonarqube.ws.commons.Paging paging = 1;
-  optional string projectId = 2;
-  repeated Component components = 3;
+  optional Component baseComponent = 3;
+  repeated Component components = 4;
 }
 
 // WS api/components/show
@@ -48,11 +48,12 @@ message ShowWsResponse {
 
 message Component {
   optional string id = 1;
-  optional string key = 2;
-  optional string projectId = 3;
-  optional string name = 4;
-  optional string description = 5;
-  optional string qualifier = 6;
-  optional string path = 7;
-  optional string language = 8;
+  optional string refId = 2;
+  optional string key = 3;
+  optional string projectId = 4;
+  optional string name = 5;
+  optional string description = 6;
+  optional string qualifier = 7;
+  optional string path = 8;
+  optional string language = 9;
 }