]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-9616 Support branch in api/components/tree
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Fri, 4 Aug 2017 14:06:11 +0000 (16:06 +0200)
committerJanos Gyerik <janos.gyerik@sonarsource.com>
Tue, 12 Sep 2017 08:55:10 +0000 (10:55 +0200)
server/sonar-server/src/main/java/org/sonar/server/component/ws/TreeAction.java
server/sonar-server/src/test/java/org/sonar/server/component/ws/TreeActionTest.java
sonar-ws/src/main/java/org/sonarqube/ws/client/component/TreeWsRequest.java

index 058deb8d2e721038fad6a1d10207d9450413e8ca..0fc4dd804aaa7e31b3215784e080146c34fc6844 100644 (file)
@@ -54,6 +54,7 @@ import org.sonarqube.ws.WsComponents;
 import org.sonarqube.ws.WsComponents.TreeWsResponse;
 import org.sonarqube.ws.client.component.TreeWsRequest;
 
+import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.collect.FluentIterable.from;
 import static java.lang.String.CASE_INSENSITIVE_ORDER;
 import static java.lang.String.format;
@@ -64,6 +65,7 @@ import static org.sonar.db.component.ComponentTreeQuery.Strategy.CHILDREN;
 import static org.sonar.db.component.ComponentTreeQuery.Strategy.LEAVES;
 import static org.sonar.server.component.ComponentFinder.ParamNames.COMPONENT_ID_AND_COMPONENT;
 import static org.sonar.server.component.ws.ComponentDtoToWsComponent.componentDtoToWsComponent;
+import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001;
 import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
 import static org.sonar.server.ws.WsParameterBuilder.createQualifiersParameter;
 import static org.sonar.server.ws.WsParameterBuilder.QualifierParameterContext.newQualifierParameterContext;
@@ -74,6 +76,7 @@ import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_COM
 import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_COMPONENT_ID;
 import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_QUALIFIERS;
 import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_STRATEGY;
+import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_BRANCH;
 
 public class TreeAction implements ComponentsWsAction {
 
@@ -131,6 +134,12 @@ public class TreeAction implements ComponentsWsAction {
       .setDeprecatedKey("baseComponentKey", "6.4")
       .setExampleValue(KEY_PROJECT_EXAMPLE_001);
 
+    action.createParam(PARAM_BRANCH)
+      .setDescription("Branch key")
+      .setExampleValue(KEY_BRANCH_EXAMPLE_001)
+      .setInternal(true)
+      .setSince("6.6");
+
     action.createSortParams(SORTS, NAME_SORT, true)
       .setDescription("Comma-separated list of sort fields")
       .setExampleValue(NAME_SORT + ", " + PATH_SORT);
@@ -164,8 +173,7 @@ public class TreeAction implements ComponentsWsAction {
 
   private TreeWsResponse doHandle(TreeWsRequest treeWsRequest) {
     try (DbSession dbSession = dbClient.openSession(false)) {
-      ComponentDto baseComponent = componentFinder.getByUuidOrKey(dbSession, treeWsRequest.getBaseComponentId(), treeWsRequest.getBaseComponentKey(),
-        COMPONENT_ID_AND_COMPONENT);
+      ComponentDto baseComponent = loadComponent(dbSession, treeWsRequest);
       checkPermissions(baseComponent);
       OrganizationDto organizationDto = componentFinder.getOrganization(dbSession, baseComponent);
 
@@ -182,6 +190,16 @@ public class TreeAction implements ComponentsWsAction {
     }
   }
 
+  private ComponentDto loadComponent(DbSession dbSession, TreeWsRequest request) {
+    String componentId = request.getBaseComponentId();
+    String componentKey = request.getBaseComponentKey();
+    String branch = request.getBranch();
+    checkArgument(componentId == null || branch == null, "'%s' and '%s' parameters cannot be used at the same time", PARAM_COMPONENT_ID, PARAM_BRANCH);
+    return branch == null
+      ? componentFinder.getByUuidOrKey(dbSession, componentId, componentKey, COMPONENT_ID_AND_COMPONENT)
+      : componentFinder.getByKeyAndBranch(dbSession, componentKey, branch);
+  }
+
   private Map<String, ComponentDto> searchReferenceComponentsByUuid(DbSession dbSession, List<ComponentDto> components) {
     List<String> referenceComponentIds = components.stream()
       .map(ComponentDto::getCopyResourceUuid)
@@ -270,6 +288,7 @@ public class TreeAction implements ComponentsWsAction {
     TreeWsRequest treeWsRequest = new TreeWsRequest()
       .setBaseComponentId(request.param(PARAM_COMPONENT_ID))
       .setBaseComponentKey(request.param(PARAM_COMPONENT))
+      .setBranch(request.param(PARAM_BRANCH))
       .setStrategy(request.mandatoryParam(PARAM_STRATEGY))
       .setQuery(request.param(Param.TEXT_QUERY))
       .setQualifiers(request.paramAsStrings(PARAM_QUALIFIERS))
index 98f6a1d5cb5b9b6db27ecd7d8a22a1beb2f7b4bf..24e3c70c28751a3e9655f00356d9b652bdda5ac3 100644 (file)
@@ -28,13 +28,13 @@ import java.util.Date;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 import org.apache.commons.io.IOUtils;
-import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.mockito.Mockito;
 import org.sonar.api.i18n.I18n;
-import org.sonar.api.resources.Qualifiers;
+import org.sonar.api.server.ws.Change;
+import org.sonar.api.server.ws.WebService;
 import org.sonar.api.server.ws.WebService.Param;
 import org.sonar.api.utils.System2;
 import org.sonar.api.web.UserRole;
@@ -53,10 +53,17 @@ import org.sonar.server.exceptions.NotFoundException;
 import org.sonar.server.tester.UserSessionRule;
 import org.sonar.server.ws.WsActionTester;
 import org.sonar.test.JsonAssert;
+import org.sonarqube.ws.WsComponents;
 import org.sonarqube.ws.WsComponents.TreeWsResponse;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.tuple;
+import static org.sonar.api.resources.Qualifiers.DIRECTORY;
+import static org.sonar.api.resources.Qualifiers.FILE;
+import static org.sonar.api.resources.Qualifiers.MODULE;
+import static org.sonar.api.resources.Qualifiers.PROJECT;
+import static org.sonar.api.resources.Qualifiers.UNIT_TEST_FILE;
 import static org.sonar.db.component.ComponentTesting.newChildComponent;
 import static org.sonar.db.component.ComponentTesting.newDirectory;
 import static org.sonar.db.component.ComponentTesting.newModuleDto;
@@ -64,6 +71,7 @@ import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
 import static org.sonar.db.component.ComponentTesting.newProjectCopy;
 import static org.sonar.db.component.ComponentTesting.newSubView;
 import static org.sonar.db.component.ComponentTesting.newView;
+import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_BRANCH;
 import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_COMPONENT;
 import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_COMPONENT_ID;
 import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_QUALIFIERS;
@@ -77,17 +85,46 @@ public class TreeActionTest {
   @Rule
   public DbTester db = DbTester.create(System2.INSTANCE);
 
-  private ResourceTypesRule resourceTypes = new ResourceTypesRule().setRootQualifiers(Qualifiers.PROJECT);
+  private ResourceTypesRule resourceTypes = new ResourceTypesRule()
+    .setRootQualifiers(PROJECT)
+    .setChildrenQualifiers(MODULE, FILE, DIRECTORY)
+    .setLeavesQualifiers(FILE, UNIT_TEST_FILE);
   private ComponentDbTester componentDb = new ComponentDbTester(db);
   private DbClient dbClient = db.getDbClient();
 
-  private WsActionTester ws;
+  private WsActionTester ws = new WsActionTester(new TreeAction(dbClient, new ComponentFinder(dbClient, resourceTypes), resourceTypes, userSession, Mockito.mock(I18n.class)));
 
-  @Before
-  public void setUp() {
-    ws = new WsActionTester(new TreeAction(dbClient, new ComponentFinder(dbClient, resourceTypes), resourceTypes, userSession, Mockito.mock(I18n.class)));
-    resourceTypes.setChildrenQualifiers(Qualifiers.MODULE, Qualifiers.FILE, Qualifiers.DIRECTORY);
-    resourceTypes.setLeavesQualifiers(Qualifiers.FILE, Qualifiers.UNIT_TEST_FILE);
+  @Test
+  public void verify_definition() throws Exception {
+    WebService.Action action = ws.getDef();
+
+    assertThat(action.since()).isEqualTo("5.4");
+    assertThat(action.description()).isNotNull();
+    assertThat(action.responseExample()).isNotNull();
+    assertThat(action.changelog()).extracting(Change::getVersion, Change::getDescription).containsExactlyInAnyOrder(
+      tuple("6.4", "The field 'id' is deprecated in the response"));
+    assertThat(action.params()).extracting(Param::key).containsExactlyInAnyOrder("component", "componentId", "branch", "qualifiers", "strategy",
+      "q", "s", "p", "asc", "ps");
+
+    Param componentId = action.param(PARAM_COMPONENT_ID);
+    assertThat(componentId.isRequired()).isFalse();
+    assertThat(componentId.description()).isNotNull();
+    assertThat(componentId.exampleValue()).isNotNull();
+    assertThat(componentId.deprecatedSince()).isEqualTo("6.4");
+    assertThat(componentId.deprecatedKey()).isEqualTo("baseComponentId");
+    assertThat(componentId.deprecatedKeySince()).isEqualTo("6.4");
+
+    Param component = action.param(PARAM_COMPONENT);
+    assertThat(component.isRequired()).isFalse();
+    assertThat(component.description()).isNotNull();
+    assertThat(component.exampleValue()).isNotNull();
+    assertThat(component.deprecatedKey()).isEqualTo("baseComponentKey");
+    assertThat(component.deprecatedKeySince()).isEqualTo("6.4");
+
+    Param branch = action.param(PARAM_BRANCH);
+    assertThat(branch.isInternal()).isTrue();
+    assertThat(branch.isRequired()).isFalse();
+    assertThat(branch.since()).isEqualTo("6.6");
   }
 
   @Test
@@ -176,7 +213,7 @@ public class TreeActionTest {
 
     TreeWsResponse response = ws.newRequest()
       .setParam(PARAM_STRATEGY, "all")
-      .setParam(PARAM_QUALIFIERS, Qualifiers.FILE)
+      .setParam(PARAM_QUALIFIERS, FILE)
       .setParam(PARAM_COMPONENT_ID, "project-uuid").executeProtobuf(TreeWsResponse.class);
 
     assertThat(response.getComponentsList()).extracting("id").containsExactly("file-uuid-1", "file-uuid-2");
@@ -199,7 +236,7 @@ public class TreeActionTest {
     TreeWsResponse response = ws.newRequest()
       .setParam(PARAM_STRATEGY, "leaves")
       .setParam(PARAM_COMPONENT_ID, "project-uuid")
-      .setParam(PARAM_QUALIFIERS, Qualifiers.FILE).executeProtobuf(TreeWsResponse.class);
+      .setParam(PARAM_QUALIFIERS, FILE).executeProtobuf(TreeWsResponse.class);
 
     assertThat(response.getComponentsCount()).isEqualTo(3);
     assertThat(response.getPaging().getTotal()).isEqualTo(3);
@@ -282,6 +319,29 @@ public class TreeActionTest {
     assertThat(response.getComponents(0).getRefId()).isEqualTo("project-uuid");
   }
 
+  @Test
+  public void branch() {
+    ComponentDto project = db.components().insertPrivateProject();
+    userSession.addProjectPermission(UserRole.USER, project);
+    String branchKey = "my_branch";
+    ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey(branchKey));
+    ComponentDto module = db.components().insertComponent(newModuleDto(branch));
+    ComponentDto directory = db.components().insertComponent(newDirectory(module, "dir"));
+    ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(directory));
+
+    TreeWsResponse response = ws.newRequest()
+      .setParam(PARAM_COMPONENT, module.getKey())
+      .setParam(PARAM_BRANCH, branchKey)
+      .executeProtobuf(TreeWsResponse.class);
+
+    assertThat(response.getBaseComponent()).extracting(WsComponents.Component::getKey, WsComponents.Component::getBranch)
+      .containsExactlyInAnyOrder(module.getKey(), branchKey);
+    assertThat(response.getComponentsList()).extracting(WsComponents.Component::getKey, WsComponents.Component::getBranch)
+      .containsExactlyInAnyOrder(
+        tuple(directory.getKey(), branchKey),
+        tuple(file.getKey(), branchKey));
+  }
+
   @Test
   public void fail_when_not_enough_privileges() {
     ComponentDto project = componentDb.insertComponent(newPrivateProjectDto(db.organizations().insert(), "project-uuid"));
@@ -377,6 +437,36 @@ public class TreeActionTest {
     ws.newRequest().execute();
   }
 
+  @Test
+  public void fail_when_componentId_and_branch_params_are_used_together() {
+    ComponentDto project = db.components().insertPrivateProject();
+    userSession.addProjectPermission(UserRole.USER, project);
+    ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("my_branch"));
+
+    expectedException.expect(IllegalArgumentException.class);
+    expectedException.expectMessage("'componentId' and 'branch' parameters cannot be used at the same time");
+
+    ws.newRequest()
+      .setParam(PARAM_COMPONENT_ID, branch.uuid())
+      .setParam(PARAM_BRANCH, "my_branch")
+      .execute();
+  }
+
+  @Test
+  public void fail_if_branch_does_not_exist() {
+    ComponentDto project = db.components().insertPrivateProject();
+    userSession.addProjectPermission(UserRole.USER, project);
+    db.components().insertProjectBranch(project, b -> b.setKey("my_branch"));
+
+    expectedException.expect(NotFoundException.class);
+    expectedException.expectMessage(String.format("Component '%s' on branch '%s' not found", project.getKey(), "another_branch"));
+
+    ws.newRequest()
+      .setParam(PARAM_COMPONENT, project.getKey())
+      .setParam(PARAM_BRANCH, "another_branch")
+      .execute();
+  }
+
   private static ComponentDto newFileDto(ComponentDto moduleOrProject, @Nullable ComponentDto directory, int i) {
     return ComponentTesting.newFileDto(moduleOrProject, directory, "file-uuid-" + i)
       .setName("file-name-" + i)
index 2a7faa736b2e910dce840c64e2da631f7b2895e7..7b03a70262e729a699cc54a7b934baad9816003e 100644 (file)
@@ -24,25 +24,18 @@ import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 
 public class TreeWsRequest {
-  @CheckForNull
   private String baseComponentId;
-  @CheckForNull
   private String baseComponentKey;
-  @CheckForNull
+  private String branch;
   private String strategy;
-  @CheckForNull
   private List<String> qualifiers;
-  @CheckForNull
   private String query;
-  @CheckForNull
   private List<String> sort;
-  @CheckForNull
   private Boolean asc;
-  @CheckForNull
   private Integer page;
-  @CheckForNull
   private Integer pageSize;
 
+  @CheckForNull
   public String getBaseComponentId() {
     return baseComponentId;
   }
@@ -52,6 +45,7 @@ public class TreeWsRequest {
     return this;
   }
 
+  @CheckForNull
   public String getBaseComponentKey() {
     return baseComponentKey;
   }
@@ -61,6 +55,17 @@ public class TreeWsRequest {
     return this;
   }
 
+  @CheckForNull
+  public String getBranch() {
+    return branch;
+  }
+
+  public TreeWsRequest setBranch(@Nullable String branch) {
+    this.branch = branch;
+    return this;
+  }
+
+  @CheckForNull
   public String getStrategy() {
     return strategy;
   }
@@ -70,6 +75,7 @@ public class TreeWsRequest {
     return this;
   }
 
+  @CheckForNull
   public List<String> getQualifiers() {
     return qualifiers;
   }
@@ -79,6 +85,7 @@ public class TreeWsRequest {
     return this;
   }
 
+  @CheckForNull
   public String getQuery() {
     return query;
   }
@@ -88,6 +95,7 @@ public class TreeWsRequest {
     return this;
   }
 
+  @CheckForNull
   public List<String> getSort() {
     return sort;
   }
@@ -101,25 +109,27 @@ public class TreeWsRequest {
     return asc;
   }
 
-  public TreeWsRequest setAsc(boolean asc) {
+  public TreeWsRequest setAsc(@Nullable Boolean asc) {
     this.asc = asc;
     return this;
   }
 
+  @CheckForNull
   public Integer getPage() {
     return page;
   }
 
-  public TreeWsRequest setPage(int page) {
+  public TreeWsRequest setPage(@Nullable Integer page) {
     this.page = page;
     return this;
   }
 
+  @CheckForNull
   public Integer getPageSize() {
     return pageSize;
   }
 
-  public TreeWsRequest setPageSize(int pageSize) {
+  public TreeWsRequest setPageSize(@Nullable Integer pageSize) {
     this.pageSize = pageSize;
     return this;
   }