]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-9616 Support branch in api/components/show
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Fri, 4 Aug 2017 13:44:51 +0000 (15:44 +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/ComponentDtoToWsComponent.java
server/sonar-server/src/main/java/org/sonar/server/component/ws/ShowAction.java
server/sonar-server/src/test/java/org/sonar/server/component/ws/ShowActionTest.java
sonar-ws/src/main/java/org/sonarqube/ws/client/component/ComponentsWsParameters.java
sonar-ws/src/main/java/org/sonarqube/ws/client/component/ShowWsRequest.java
sonar-ws/src/main/protobuf/ws-components.proto

index e229d882783537cd24f09c74ce22685183b0b179..6646126f7e086d8f58fbb5106e721d5e5ca4b5ec 100644 (file)
@@ -58,9 +58,10 @@ class ComponentDtoToWsComponent {
     WsComponents.Component.Builder wsComponent = WsComponents.Component.newBuilder()
       .setOrganization(organizationDtoKey)
       .setId(dto.uuid())
-      .setKey(dto.getDbKey())
+      .setKey(dto.getKey())
       .setName(dto.name())
       .setQualifier(dto.qualifier());
+    setNullable(emptyToNull(dto.getBranch()), wsComponent::setBranch);
     setNullable(emptyToNull(dto.path()), wsComponent::setPath);
     setNullable(emptyToNull(dto.description()), wsComponent::setDescription);
     setNullable(emptyToNull(dto.language()), wsComponent::setLanguage);
index 96b6cc10d88dd30c650449bdac82a349790920fe..f0953bee69322f8819b422a50a46aecbd5eaab8f 100644 (file)
@@ -33,19 +33,22 @@ import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.SnapshotDto;
 import org.sonar.db.organization.OrganizationDto;
 import org.sonar.server.component.ComponentFinder;
-import org.sonar.server.component.ComponentFinder.ParamNames;
 import org.sonar.server.user.UserSession;
 import org.sonarqube.ws.WsComponents.ShowWsResponse;
 import org.sonarqube.ws.client.component.ShowWsRequest;
 
+import static com.google.common.base.Preconditions.checkArgument;
 import static java.lang.String.format;
 import static org.sonar.core.util.Uuids.UUID_EXAMPLE_01;
+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.WsUtils.writeProtobuf;
 import static org.sonarqube.ws.client.component.ComponentsWsParameters.ACTION_SHOW;
 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.measure.MeasuresWsParameters.PARAM_BRANCH;
 
 public class ShowAction implements ComponentsWsAction {
   private final UserSession userSession;
@@ -85,6 +88,12 @@ public class ShowAction implements ComponentsWsAction {
       .setDescription("Component key")
       .setDeprecatedKey("key", "6.4")
       .setExampleValue(KEY_PROJECT_EXAMPLE_001);
+
+    action.createParam(PARAM_BRANCH)
+      .setDescription("Branch key")
+      .setExampleValue(KEY_BRANCH_EXAMPLE_001)
+      .setInternal(true)
+      .setSince("6.6");
   }
 
   @Override
@@ -97,7 +106,7 @@ public class ShowAction implements ComponentsWsAction {
 
   private ShowWsResponse doHandle(ShowWsRequest request) {
     try (DbSession dbSession = dbClient.openSession(false)) {
-      ComponentDto component = getComponentByUuidOrKey(dbSession, request);
+      ComponentDto component = loadComponent(dbSession, request);
       Optional<SnapshotDto> lastAnalysis = dbClient.snapshotDao().selectLastAnalysisByComponentUuid(dbSession, component.projectUuid());
       List<ComponentDto> ancestors = dbClient.componentDao().selectAncestors(dbSession, component);
       OrganizationDto organizationDto = componentFinder.getOrganization(dbSession, component);
@@ -105,8 +114,14 @@ public class ShowAction implements ComponentsWsAction {
     }
   }
 
-  private ComponentDto getComponentByUuidOrKey(DbSession dbSession, ShowWsRequest request) {
-    ComponentDto component = componentFinder.getByUuidOrKey(dbSession, request.getId(), request.getKey(), ParamNames.COMPONENT_ID_AND_COMPONENT);
+  private ComponentDto loadComponent(DbSession dbSession, ShowWsRequest request) {
+    String componentId = request.getId();
+    String componentKey = request.getKey();
+    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);
+    ComponentDto component = branch == null
+      ? componentFinder.getByUuidOrKey(dbSession, componentId, componentKey, COMPONENT_ID_AND_COMPONENT)
+      : componentFinder.getByKeyAndBranch(dbSession, componentKey, branch);
     userSession.checkComponentPermission(UserRole.USER, component);
     return component;
   }
@@ -125,6 +140,7 @@ public class ShowAction implements ComponentsWsAction {
   private static ShowWsRequest toShowWsRequest(Request request) {
     return new ShowWsRequest()
       .setId(request.param(PARAM_COMPONENT_ID))
-      .setKey(request.param(PARAM_COMPONENT));
+      .setKey(request.param(PARAM_COMPONENT))
+      .setBranch(request.param(PARAM_BRANCH));
   }
 }
index ad0b6ccf7002fb6c491e78278e44925c244ee178..b0c7fa776dc4f866b9e069e51f09d6a1676a7510 100644 (file)
@@ -29,6 +29,7 @@ import org.sonar.api.resources.Qualifiers;
 import org.sonar.api.server.ws.Change;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.api.utils.System2;
+import org.sonar.api.web.UserRole;
 import org.sonar.db.DbTester;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.organization.OrganizationDto;
@@ -38,7 +39,7 @@ import org.sonar.server.exceptions.NotFoundException;
 import org.sonar.server.tester.UserSessionRule;
 import org.sonar.server.ws.TestRequest;
 import org.sonar.server.ws.WsActionTester;
-import org.sonarqube.ws.WsComponents;
+import org.sonarqube.ws.WsComponents.Component;
 import org.sonarqube.ws.WsComponents.ShowWsResponse;
 
 import static org.assertj.core.api.Assertions.assertThat;
@@ -52,6 +53,7 @@ import static org.sonar.db.component.ComponentTesting.newModuleDto;
 import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
 import static org.sonar.db.component.SnapshotTesting.newAnalysis;
 import static org.sonar.test.JsonAssert.assertJson;
+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;
 
@@ -77,6 +79,7 @@ public class ShowActionTest {
       tuple("6.4", "The field 'id' is deprecated in the response"),
       tuple("6.4", "The 'visibility' field is added to the response"),
       tuple("6.5", "Leak period date is added to the response"));
+    assertThat(action.params()).extracting(WebService.Param::key).containsExactlyInAnyOrder("component", "componentId", "branch");
 
     WebService.Param componentId = action.param(PARAM_COMPONENT_ID);
     assertThat(componentId.isRequired()).isFalse();
@@ -92,6 +95,11 @@ public class ShowActionTest {
     assertThat(component.exampleValue()).isNotNull();
     assertThat(component.deprecatedKey()).isEqualTo("key");
     assertThat(component.deprecatedKeySince()).isEqualTo("6.4");
+
+    WebService.Param branch = action.param(PARAM_BRANCH);
+    assertThat(branch.isInternal()).isTrue();
+    assertThat(branch.isRequired()).isFalse();
+    assertThat(branch.since()).isEqualTo("6.6");
   }
 
   @Test
@@ -154,7 +162,7 @@ public class ShowActionTest {
     ShowWsResponse response = newRequest(null, file.getDbKey());
 
     assertThat(response.getComponent().getKey()).isEqualTo(file.getDbKey());
-    assertThat(response.getAncestorsList()).extracting(WsComponents.Component::getKey).containsOnly(directory.getDbKey(), module.getDbKey(), project.getDbKey());
+    assertThat(response.getAncestorsList()).extracting(Component::getKey).containsOnly(directory.getDbKey(), module.getDbKey(), project.getDbKey());
   }
 
   @Test
@@ -210,7 +218,7 @@ public class ShowActionTest {
     ShowWsResponse response = newRequest(null, file.getDbKey());
 
     String expectedDate = formatDateTime(new Date(3_000_000_000L));
-    assertThat(response.getAncestorsList()).extracting(WsComponents.Component::getAnalysisDate)
+    assertThat(response.getAncestorsList()).extracting(Component::getAnalysisDate)
       .containsOnly(expectedDate, expectedDate, expectedDate);
   }
 
@@ -253,6 +261,30 @@ public class ShowActionTest {
     assertThat(result.getComponent().hasVisibility()).isFalse();
   }
 
+  @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(newFileDto(directory));
+
+    ShowWsResponse response = ws.newRequest()
+      .setParam(PARAM_COMPONENT, file.getKey())
+      .setParam(PARAM_BRANCH, branchKey)
+      .executeProtobuf(ShowWsResponse.class);
+
+    assertThat(response.getComponent()).extracting(Component::getKey, Component::getBranch)
+      .containsExactlyInAnyOrder(file.getKey(), branchKey);
+    assertThat(response.getAncestorsList()).extracting(Component::getKey, Component::getBranch)
+      .containsExactlyInAnyOrder(
+        tuple(directory.getKey(), branchKey),
+        tuple(module.getKey(), branchKey),
+        tuple(branch.getKey(), branchKey));
+  }
+
   @Test
   public void throw_ForbiddenException_if_user_doesnt_have_browse_permission_on_project() {
     userSession.logIn();
@@ -283,6 +315,37 @@ public class ShowActionTest {
     newRequest(null, "file-key");
   }
 
+  @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();
+    ComponentDto file = db.components().insertComponent(newFileDto(project));
+    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", file.getKey(), "another_branch"));
+
+    ws.newRequest()
+      .setParam(PARAM_COMPONENT, file.getKey())
+      .setParam(PARAM_BRANCH, "another_branch")
+      .execute();
+  }
+
   private ShowWsResponse newRequest(@Nullable String uuid, @Nullable String key) {
     TestRequest request = ws.newRequest();
     if (uuid != null) {
index de4f3016a8a19b69214eaeae024a8cf6a8e82581..fb0a980be3fa980ef20606f74c2c3deda65d8d71 100644 (file)
@@ -38,6 +38,7 @@ public class ComponentsWsParameters {
   public static final String PARAM_FILTER = "filter";
   public static final String PARAM_COMPONENT_ID = "componentId";
   public static final String PARAM_COMPONENT = "component";
+  public static final String PARAM_BRANCH = "branch";
 
   private ComponentsWsParameters() {
     // static utility class
index 696b280a078bc18e615a0926aa316ba3aa09e25d..1ada1585b8d6fd9c55116c3732740f7d7c6883cb 100644 (file)
@@ -23,10 +23,9 @@ import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 
 public class ShowWsRequest {
-  @CheckForNull
   private String id;
-  @CheckForNull
   private String key;
+  private String branch;
 
   @CheckForNull
   public String getId() {
@@ -47,4 +46,14 @@ public class ShowWsRequest {
     this.key = key;
     return this;
   }
+
+  @CheckForNull
+  public String getBranch() {
+    return branch;
+  }
+
+  public ShowWsRequest setBranch(@Nullable String branch) {
+    this.branch = branch;
+    return this;
+  }
 }
index eba01af28f6454fa1cacccaffb9ad38b7ede022b..595c580a00123289312b160696c11b0bbd6eb743 100644 (file)
@@ -120,6 +120,7 @@ message Component {
   optional string leakPeriodDate = 16;
   // Root project key
   optional string project = 17;
+  optional string branch = 18;
 
   message Tags {
     repeated string tags = 1;