From 2cbeaa30237eb9188a55c53eae795a0a5265ecc0 Mon Sep 17 00:00:00 2001 From: Stas Vilchik Date: Tue, 19 Jan 2016 15:03:31 +0100 Subject: [PATCH] SONAR-7129 WS api/components/tree baseComponent and refId in response --- .../sonar/server/component/ws/TreeAction.java | 96 ++++++++++++++----- .../server/component/ws/tree-example.json | 6 ++ .../server/component/ws/TreeActionTest.java | 25 ++--- .../src/main/protobuf/ws-components.proto | 19 ++-- 4 files changed, 101 insertions(+), 45 deletions(-) diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ws/TreeAction.java b/server/sonar-server/src/main/java/org/sonar/server/component/ws/TreeAction.java index a67d38a0bf4..5fc9911c3b6 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/component/ws/TreeAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/component/ws/TreeAction.java @@ -19,10 +19,16 @@ */ 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.
" + - "Requires one of the following permissions:" + - "" + - "When limiting search with the %s parameter, directories are not returned.", + "Requires one of the following permissions:" + + "" + + "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: ") + .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:" + "") .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 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 searchReferenceComponentUuidsById(DbSession dbSession, List components) { + List referenceComponentIds = from(components) + .transform(ComponentDtoWithSnapshotIdToCopyResourceIdFunction.INSTANCE) + .filter(Predicates.notNull()) + .toList(); + if (referenceComponentIds.isEmpty()) { + return emptyMap(); + } + + List referenceComponents = dbClient.componentDao().selectByIds(dbSession, referenceComponentIds); + Map 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 components, Paging paging) { + private static TreeWsResponse buildResponse(ComponentDto baseComponent, List components, Map 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 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.emptyMap())); return response.build(); } @@ -274,4 +312,12 @@ public class TreeAction implements ComponentsWsAction { return treeWsRequest; } + + private enum ComponentDtoWithSnapshotIdToCopyResourceIdFunction implements Function { + INSTANCE; + @Override + public Long apply(@Nonnull ComponentDtoWithSnapshotId input) { + return input.getCopyResourceId(); + } + } } diff --git a/server/sonar-server/src/main/resources/org/sonar/server/component/ws/tree-example.json b/server/sonar-server/src/main/resources/org/sonar/server/component/ws/tree-example.json index 85fac3978a5..fd6c14a1bf8 100644 --- a/server/sonar-server/src/main/resources/org/sonar/server/component/ws/tree-example.json +++ b/server/sonar-server/src/main/resources/org/sonar/server/component/ws/tree-example.json @@ -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", diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ws/TreeActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ws/TreeActionTest.java index 63e46fc702b..cc5821ce126 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/component/ws/TreeActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/component/ws/TreeActionTest.java @@ -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(); diff --git a/sonar-ws/src/main/protobuf/ws-components.proto b/sonar-ws/src/main/protobuf/ws-components.proto index d9e86b0e0e4..9ff36b851a5 100644 --- a/sonar-ws/src/main/protobuf/ws-components.proto +++ b/sonar-ws/src/main/protobuf/ws-components.proto @@ -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; } -- 2.39.5