]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-9616 Support branch in api/measures/component
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Fri, 4 Aug 2017 09:52:07 +0000 (11:52 +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/measure/ws/ComponentAction.java
server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentDtoToWsComponent.java
server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentActionTest.java
sonar-ws/src/main/java/org/sonarqube/ws/client/measure/ComponentWsRequest.java
sonar-ws/src/main/java/org/sonarqube/ws/client/measure/MeasuresService.java
sonar-ws/src/main/protobuf/ws-measures.proto
sonar-ws/src/test/java/org/sonarqube/ws/client/measure/MeasuresServiceTest.java

index 5bcfd73345f5719ae0470f279ebbb43374087439..a23e33014e69682a9ce0dfad0f9808d84028361e 100644 (file)
@@ -55,12 +55,13 @@ import org.sonarqube.ws.WsMeasures;
 import org.sonarqube.ws.WsMeasures.ComponentWsResponse;
 import org.sonarqube.ws.client.measure.ComponentWsRequest;
 
+import static com.google.common.base.Preconditions.checkArgument;
 import static java.lang.String.format;
 import static java.util.Collections.emptyList;
 import static java.util.Collections.emptyMap;
 import static java.util.Collections.singletonMap;
 import static org.sonar.core.util.Uuids.UUID_EXAMPLE_01;
-import static org.sonar.server.component.ComponentFinder.ParamNames.COMPONENT_ID_AND_KEY;
+import static org.sonar.server.component.ComponentFinder.ParamNames.COMPONENT_ID_AND_COMPONENT;
 import static org.sonar.server.component.ComponentFinder.ParamNames.DEVELOPER_ID_AND_KEY;
 import static org.sonar.server.measure.ws.ComponentDtoToWsComponent.componentDtoToWsComponent;
 import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createAdditionalFieldsParameter;
@@ -68,6 +69,7 @@ import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createDeve
 import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createMetricKeysParameter;
 import static org.sonar.server.measure.ws.MetricDtoToWsMetric.metricDtoToWsMetric;
 import static org.sonar.server.measure.ws.SnapshotDtoToWsPeriods.snapshotToWsPeriods;
+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.checkRequest;
 import static org.sonar.server.ws.WsUtils.writeProtobuf;
@@ -77,6 +79,7 @@ import static org.sonarqube.ws.client.measure.MeasuresWsParameters.ADDITIONAL_PE
 import static org.sonarqube.ws.client.measure.MeasuresWsParameters.DEPRECATED_PARAM_COMPONENT_ID;
 import static org.sonarqube.ws.client.measure.MeasuresWsParameters.DEPRECATED_PARAM_COMPONENT_KEY;
 import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_ADDITIONAL_FIELDS;
+import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_BRANCH;
 import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_COMPONENT;
 import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_DEVELOPER_ID;
 import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_DEVELOPER_KEY;
@@ -118,6 +121,12 @@ public class ComponentAction implements MeasuresWsAction {
       .setExampleValue(UUID_EXAMPLE_01)
       .setDeprecatedSince("6.6");
 
+    action.createParam(PARAM_BRANCH)
+      .setDescription("Branch key")
+      .setExampleValue(KEY_BRANCH_EXAMPLE_001)
+      .setInternal(true)
+      .setSince("6.6");
+
     createMetricKeysParameter(action);
     createAdditionalFieldsParameter(action);
     createDeveloperParameters(action);
@@ -131,7 +140,7 @@ public class ComponentAction implements MeasuresWsAction {
 
   private ComponentWsResponse doHandle(ComponentWsRequest request) {
     try (DbSession dbSession = dbClient.openSession(false)) {
-      ComponentDto component = componentFinder.getByUuidOrKey(dbSession, request.getComponentId(), request.getComponent(), COMPONENT_ID_AND_KEY);
+      ComponentDto component = loadComponent(dbSession, request);
       Long developerId = searchDeveloperId(dbSession, request);
       Optional<ComponentDto> refComponent = getReferenceComponent(dbSession, component);
       checkPermissions(component);
@@ -144,6 +153,16 @@ public class ComponentAction implements MeasuresWsAction {
     }
   }
 
+  private ComponentDto loadComponent(DbSession dbSession, ComponentWsRequest request) {
+    String componentKey = request.getComponent();
+    String componentId = request.getComponentId();
+    String branch = request.getBranch();
+    checkArgument(componentId == null || branch == null, "'%s' and '%s' parameters cannot be used at the same time", DEPRECATED_PARAM_COMPONENT_ID, PARAM_BRANCH);
+    return branch == null
+      ? componentFinder.getByUuidOrKey(dbSession, componentId, componentKey, COMPONENT_ID_AND_COMPONENT)
+      : componentFinder.getByKeyAndBranch(dbSession, componentKey, branch);
+  }
+
   @CheckForNull
   private Long searchDeveloperId(DbSession dbSession, ComponentWsRequest request) {
     if (request.getDeveloperId() == null && request.getDeveloperKey() == null) {
@@ -251,6 +270,7 @@ public class ComponentAction implements MeasuresWsAction {
     ComponentWsRequest componentWsRequest = new ComponentWsRequest()
       .setComponentId(request.param(DEPRECATED_PARAM_COMPONENT_ID))
       .setComponent(request.param(PARAM_COMPONENT))
+      .setBranch(request.param(PARAM_BRANCH))
       .setAdditionalFields(request.paramAsStrings(PARAM_ADDITIONAL_FIELDS))
       .setMetricKeys(request.mandatoryParamAsStrings(PARAM_METRIC_KEYS))
       .setDeveloperId(request.param(PARAM_DEVELOPER_ID))
index df45ff19672a443d81ea1ceed45bcf97d5a8a3d4..3129de298a377d3b514e9ed8c23bb77f5f0625ce 100644 (file)
@@ -20,6 +20,7 @@
 package org.sonar.server.measure.ws;
 
 import java.util.Map;
+import org.sonar.core.util.Protobuf;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.measure.MeasureDto;
 import org.sonar.db.metric.MetricDto;
@@ -54,19 +55,13 @@ class ComponentDtoToWsComponent {
   static Component.Builder componentDtoToWsComponent(ComponentDto component) {
     Component.Builder wsComponent = Component.newBuilder()
       .setId(component.uuid())
-      .setKey(component.getDbKey())
+      .setKey(component.getKey())
       .setName(component.name())
       .setQualifier(component.qualifier());
-    if (component.path() != null) {
-      wsComponent.setPath(component.path());
-    }
-    if (component.description() != null) {
-      wsComponent.setDescription(component.description());
-    }
-    if (component.language() != null) {
-      wsComponent.setLanguage(component.language());
-    }
-
+    Protobuf.setNullable(component.getBranch(), wsComponent::setBranch);
+    Protobuf.setNullable(component.path(), wsComponent::setPath);
+    Protobuf.setNullable(component.description(), wsComponent::setDescription);
+    Protobuf.setNullable(component.language(), wsComponent::setLanguage);
     return wsComponent;
   }
 }
index 0e7ba8748a3908d860c9e2445760ee9baf9275ee..4a4012d84429bd0b0a3fb3acbed15bf3d9c22da6 100644 (file)
@@ -29,6 +29,7 @@ import org.sonar.api.web.UserRole;
 import org.sonar.db.DbTester;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.SnapshotDto;
+import org.sonar.db.measure.MeasureDto;
 import org.sonar.db.metric.MetricDto;
 import org.sonar.server.component.TestComponentFinder;
 import org.sonar.server.computation.task.projectanalysis.measure.Measure;
@@ -37,20 +38,26 @@ import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.exceptions.NotFoundException;
 import org.sonar.server.tester.UserSessionRule;
 import org.sonar.server.ws.WsActionTester;
+import org.sonarqube.ws.WsMeasures;
+import org.sonarqube.ws.WsMeasures.Component;
 import org.sonarqube.ws.Common;
 import org.sonarqube.ws.Common;
 import org.sonarqube.ws.WsMeasures;
 import org.sonarqube.ws.WsMeasures.Component;
 import org.sonarqube.ws.WsMeasures.ComponentWsResponse;
 
+import static java.lang.Double.parseDouble;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.tuple;
 import static org.sonar.api.utils.DateUtils.parseDateTime;
 import static org.sonar.api.web.UserRole.USER;
 import static org.sonar.db.component.ComponentTesting.newFileDto;
 import static org.sonar.db.component.ComponentTesting.newProjectCopy;
 import static org.sonar.db.component.SnapshotTesting.newAnalysis;
 import static org.sonar.test.JsonAssert.assertJson;
+import static org.sonarqube.ws.client.measure.MeasuresWsParameters.DEPRECATED_PARAM_COMPONENT_ID;
 import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_ADDITIONAL_FIELDS;
+import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_BRANCH;
 import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_COMPONENT;
 import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_DEVELOPER_ID;
 import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_METRIC_KEYS;
@@ -72,10 +79,15 @@ public class ComponentActionTest {
 
     assertThat(def.since()).isEqualTo("5.4");
     assertThat(def.params()).extracting(Param::key)
-      .containsExactlyInAnyOrder("componentId", "component", "metricKeys", "additionalFields", "developerId", "developerKey");
+      .containsExactlyInAnyOrder("componentId", "component", "branch", "metricKeys", "additionalFields", "developerId", "developerKey");
     assertThat(def.param("developerId").deprecatedSince()).isEqualTo("6.4");
     assertThat(def.param("developerKey").deprecatedSince()).isEqualTo("6.4");
     assertThat(def.param("componentId").deprecatedSince()).isEqualTo("6.6");
+
+    WebService.Param branch = def.param("branch");
+    assertThat(branch.since()).isEqualTo("6.6");
+    assertThat(branch.isInternal()).isTrue();
+    assertThat(branch.isRequired()).isFalse();
   }
 
   @Test
@@ -108,6 +120,29 @@ public class ComponentActionTest {
       .doesNotContain("metrics");
   }
 
+  @Test
+  public void branch() {
+    ComponentDto project = db.components().insertPrivateProject();
+    logAsUser(project);
+    ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("my_branch"));
+    SnapshotDto analysis = db.components().insertSnapshot(branch);
+    ComponentDto file = db.components().insertComponent(newFileDto(branch));
+    MetricDto complexity = insertComplexityMetric();
+    MeasureDto measure = db.measures().insertMeasure(file, analysis, complexity, m -> m.setValue(12.0d).setVariation(2.0d));
+
+    ComponentWsResponse response = ws.newRequest()
+      .setParam(PARAM_COMPONENT, file.getKey())
+      .setParam(PARAM_BRANCH, file.getBranch())
+      .setParam(PARAM_METRIC_KEYS, complexity.getKey())
+      .executeProtobuf(ComponentWsResponse.class);
+
+    assertThat(response.getComponent()).extracting(Component::getKey, Component::getBranch)
+      .containsExactlyInAnyOrder(file.getKey(), file.getBranch());
+    assertThat(response.getComponent().getMeasuresList())
+      .extracting(WsMeasures.Measure::getMetric, m -> parseDouble(m.getValue()))
+      .containsExactlyInAnyOrder(tuple(complexity.getKey(), measure.getValue()));
+  }
+
   @Test
   public void reference_uuid_in_the_response() {
     userSession.logIn().setRoot();
@@ -271,6 +306,40 @@ public class ComponentActionTest {
       .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")
+      .setParam(PARAM_METRIC_KEYS, "ncloc")
+      .execute();
+  }
+
+  @Test
+  public void fail_when_componentId_and_branch_params_are_used_together() {
+    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(IllegalArgumentException.class);
+    expectedException.expectMessage("'componentId' and 'branch' parameters cannot be used at the same time");
+
+    ws.newRequest()
+      .setParam(DEPRECATED_PARAM_COMPONENT_ID, file.uuid())
+      .setParam(PARAM_BRANCH, "my_branch")
+      .setParam(PARAM_METRIC_KEYS, "ncloc")
+      .execute();
+  }
+
   @Test
   public void json_example() {
     ComponentDto project = db.components().insertPrivateProject();
index 998ac57209fee0d63d11827013f07b8e5325a759..cc6e10ef7d9a82f10a2a9f7800ddfb015aa71edd 100644 (file)
@@ -27,6 +27,7 @@ public class ComponentWsRequest {
   private String componentId;
   private String componentKey;
   private String component;
+  private String branch;
   private List<String> metricKeys;
   private List<String> additionalFields;
   private String developerId;
@@ -78,6 +79,16 @@ public class ComponentWsRequest {
     return this;
   }
 
+  @CheckForNull
+  public String getBranch() {
+    return branch;
+  }
+
+  public ComponentWsRequest setBranch(@Nullable String branch) {
+    this.branch = branch;
+    return this;
+  }
+
   public List<String> getMetricKeys() {
     return metricKeys;
   }
index dca24806339cf868ffbc2019e937c09561e9ed3e..9c94c285667dd00fb559cc87253cfb6ed4937982 100644 (file)
@@ -83,6 +83,7 @@ public class MeasuresService extends BaseService {
       .setParam(DEPRECATED_PARAM_COMPONENT_ID, request.getComponentId())
       .setParam(DEPRECATED_PARAM_COMPONENT_KEY, request.getComponentKey())
       .setParam(PARAM_COMPONENT, request.getComponent())
+      .setParam(PARAM_BRANCH, request.getBranch())
       .setParam(PARAM_ADDITIONAL_FIELDS, inlineMultipleParamValue(request.getAdditionalFields()))
       .setParam(PARAM_METRIC_KEYS, inlineMultipleParamValue(request.getMetricKeys()))
       .setParam(PARAM_DEVELOPER_ID, request.getDeveloperId())
index 4c64b51ba474851f9a909b099372e3588f83c88e..199184eda3b34b56d06b6be3fde2d6c908619872 100644 (file)
@@ -75,6 +75,7 @@ message Component {
   optional string path = 9;
   optional string language = 10;
   repeated Measure measures = 11;
+  optional string branch = 12;
 }
 
 message Period {
index b83ac53ce90c6c1d87e22a390fb9adea5dfaf8ab..b42cc3e45ad24b8eca4e7e3e784286f5fe00433b 100644 (file)
@@ -25,6 +25,7 @@ import org.junit.Rule;
 import org.junit.Test;
 import org.sonarqube.ws.WsMeasures;
 import org.sonarqube.ws.WsMeasures.ComponentTreeWsResponse;
+import org.sonarqube.ws.WsMeasures.ComponentWsResponse;
 import org.sonarqube.ws.client.GetRequest;
 import org.sonarqube.ws.client.ServiceTester;
 import org.sonarqube.ws.client.WsConnector;
@@ -35,6 +36,8 @@ import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
 import static org.sonarqube.ws.client.measure.MeasuresWsParameters.DEPRECATED_PARAM_BASE_COMPONENT_ID;
 import static org.sonarqube.ws.client.measure.MeasuresWsParameters.DEPRECATED_PARAM_BASE_COMPONENT_KEY;
+import static org.sonarqube.ws.client.measure.MeasuresWsParameters.DEPRECATED_PARAM_COMPONENT_ID;
+import static org.sonarqube.ws.client.measure.MeasuresWsParameters.DEPRECATED_PARAM_COMPONENT_KEY;
 import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_ADDITIONAL_FIELDS;
 import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_BRANCH;
 import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_COMPONENT;
@@ -76,6 +79,35 @@ public class MeasuresServiceTest {
 
   private MeasuresService underTest = serviceTester.getInstanceUnderTest();
 
+  @Test
+  public void component() {
+    ComponentWsRequest request = new ComponentWsRequest()
+      .setComponentId(VALUE_BASE_COMPONENT_ID)
+      .setComponentKey(VALUE_BASE_COMPONENT_KEY)
+      .setComponent(VALUE_BASE_COMPONENT_KEY)
+      .setBranch("my_branch")
+      .setMetricKeys(VALUE_METRIC_KEYS)
+      .setAdditionalFields(VALUE_ADDITIONAL_FIELDS)
+      .setMetricKeys(VALUE_METRICS)
+      .setDeveloperId(VALUE_DEVELOPER_ID)
+      .setDeveloperKey(VALUE_DEVELOPER_KEY);
+
+    underTest.component(request);
+    GetRequest getRequest = serviceTester.getGetRequest();
+
+    assertThat(serviceTester.getGetParser()).isSameAs(ComponentWsResponse.parser());
+    serviceTester.assertThat(getRequest)
+      .hasParam(DEPRECATED_PARAM_COMPONENT_ID, VALUE_BASE_COMPONENT_ID)
+      .hasParam(DEPRECATED_PARAM_COMPONENT_KEY, VALUE_BASE_COMPONENT_KEY)
+      .hasParam(PARAM_COMPONENT, VALUE_BASE_COMPONENT_KEY)
+      .hasParam(PARAM_BRANCH, "my_branch")
+      .hasParam(PARAM_METRIC_KEYS, "ncloc,complexity")
+      .hasParam(PARAM_ADDITIONAL_FIELDS, "metrics")
+      .hasParam(PARAM_DEVELOPER_ID, VALUE_DEVELOPER_ID)
+      .hasParam(PARAM_DEVELOPER_KEY, VALUE_DEVELOPER_KEY)
+      .andNoOtherParam();
+  }
+
   @Test
   public void component_tree() {
     ComponentTreeWsRequest componentTreeRequest = new ComponentTreeWsRequest()