]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-9616 Support branch in api/measures/component_tree
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Fri, 4 Aug 2017 10:02:40 +0000 (12:02 +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/ComponentTreeAction.java
server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeDataLoader.java
server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentTreeActionTest.java
sonar-ws/src/main/java/org/sonarqube/ws/client/measure/ComponentTreeWsRequest.java
sonar-ws/src/main/java/org/sonarqube/ws/client/measure/MeasuresService.java
sonar-ws/src/test/java/org/sonarqube/ws/client/measure/MeasuresServiceTest.java

index b3e3c5b5ae63f105853e4d6537e82846945dcd45..8c894c4dae9121e9fdad6bc5fb9ed9d74fafa775 100644 (file)
@@ -54,6 +54,7 @@ import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createAddi
 import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createDeveloperParameters;
 import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createMetricKeysParameter;
 import static org.sonar.server.measure.ws.MetricDtoToWsMetric.metricDtoToWsMetric;
+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;
@@ -65,6 +66,7 @@ import static org.sonarqube.ws.client.measure.MeasuresWsParameters.ADDITIONAL_PE
 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.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;
@@ -165,6 +167,12 @@ public class ComponentTreeAction implements MeasuresWsAction {
       .setExampleValue(KEY_PROJECT_EXAMPLE_001)
       .setDeprecatedKey(DEPRECATED_PARAM_BASE_COMPONENT_KEY, "6.6");
 
+    action.createParam(PARAM_BRANCH)
+      .setDescription("Branch key")
+      .setExampleValue(KEY_BRANCH_EXAMPLE_001)
+      .setInternal(true)
+      .setSince("6.6");
+
     action.createParam(PARAM_METRIC_SORT)
       .setDescription(
         format("Metric key to sort by. The '%s' parameter must contain the '%s' or '%s' value. It must be part of the '%s' parameter", Param.SORT, METRIC_SORT, METRIC_PERIOD_SORT,
@@ -283,6 +291,7 @@ public class ComponentTreeAction implements MeasuresWsAction {
     ComponentTreeWsRequest componentTreeWsRequest = new ComponentTreeWsRequest()
       .setBaseComponentId(request.param(DEPRECATED_PARAM_BASE_COMPONENT_ID))
       .setComponent(request.param(PARAM_COMPONENT))
+      .setBranch(request.param(PARAM_BRANCH))
       .setMetricKeys(metricKeys)
       .setStrategy(request.mandatoryParam(PARAM_STRATEGY))
       .setQualifiers(request.paramAsStrings(PARAM_QUALIFIERS))
index b454bebd4fa6df938c8733702dc35255a5aed43f..e599bb77803258808b0cc21eb06590a389776956 100644 (file)
@@ -74,6 +74,8 @@ import static org.sonar.server.measure.ws.ComponentTreeAction.LEAVES_STRATEGY;
 import static org.sonar.server.measure.ws.ComponentTreeAction.STRATEGIES;
 import static org.sonar.server.measure.ws.ComponentTreeAction.WITH_MEASURES_ONLY_METRIC_SORT_FILTER;
 import static org.sonar.server.measure.ws.SnapshotDtoToWsPeriods.snapshotToWsPeriods;
+import static org.sonarqube.ws.client.measure.MeasuresWsParameters.DEPRECATED_PARAM_BASE_COMPONENT_ID;
+import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_BRANCH;
 
 public class ComponentTreeDataLoader {
   private static final Set<String> QUALIFIERS_ELIGIBLE_FOR_BEST_VALUE = ImmutableSet.of(Qualifiers.FILE, Qualifiers.UNIT_TEST_FILE);
@@ -93,7 +95,7 @@ public class ComponentTreeDataLoader {
 
   ComponentTreeData load(ComponentTreeWsRequest wsRequest) {
     try (DbSession dbSession = dbClient.openSession(false)) {
-      ComponentDto baseComponent = componentFinder.getByUuidOrKey(dbSession, wsRequest.getBaseComponentId(), wsRequest.getComponent(), BASE_COMPONENT_ID_AND_KEY);
+      ComponentDto baseComponent = loadComponent(dbSession, wsRequest);
       checkPermissions(baseComponent);
       Optional<SnapshotDto> baseSnapshot = dbClient.snapshotDao().selectLastAnalysisByRootComponentUuid(dbSession, baseComponent.projectUuid());
       if (!baseSnapshot.isPresent()) {
@@ -128,6 +130,16 @@ public class ComponentTreeDataLoader {
     }
   }
 
+  private ComponentDto loadComponent(DbSession dbSession, ComponentTreeWsRequest request) {
+    String componentKey = request.getComponent();
+    String componentId = request.getBaseComponentId();
+    String branch = request.getBranch();
+    checkArgument(componentId == null || branch == null, "'%s' and '%s' parameters cannot be used at the same time", DEPRECATED_PARAM_BASE_COMPONENT_ID, PARAM_BRANCH);
+    return branch == null
+      ? componentFinder.getByUuidOrKey(dbSession, componentId, componentKey, BASE_COMPONENT_ID_AND_KEY)
+      : componentFinder.getByKeyAndBranch(dbSession, componentKey, branch);
+  }
+
   @CheckForNull
   private Long searchDeveloperId(DbSession dbSession, ComponentTreeWsRequest wsRequest) {
     if (wsRequest.getDeveloperId() == null && wsRequest.getDeveloperKey() == null) {
index b9f233b5693b707f56cd97634d9cf44e7879157f..2c3e8e2cfd584e2089272385444be1cc48839f3f 100644 (file)
@@ -22,7 +22,6 @@ package org.sonar.server.measure.ws;
 import com.google.common.base.Joiner;
 import java.util.List;
 import java.util.stream.IntStream;
-import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
@@ -39,6 +38,7 @@ import org.sonar.db.component.ComponentDbTester;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.ResourceTypesRule;
 import org.sonar.db.component.SnapshotDto;
+import org.sonar.db.measure.MeasureDto;
 import org.sonar.db.metric.MetricDto;
 import org.sonar.db.metric.MetricTesting;
 import org.sonar.server.component.ComponentFinder;
@@ -52,6 +52,7 @@ import org.sonarqube.ws.Common;
 import org.sonarqube.ws.WsMeasures;
 import org.sonarqube.ws.WsMeasures.ComponentTreeWsResponse;
 
+import static java.lang.Double.parseDouble;
 import static java.lang.String.format;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.tuple;
@@ -60,6 +61,11 @@ import static org.sonar.api.measures.Metric.ValueType.DISTRIB;
 import static org.sonar.api.measures.Metric.ValueType.FLOAT;
 import static org.sonar.api.measures.Metric.ValueType.INT;
 import static org.sonar.api.measures.Metric.ValueType.RATING;
+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.api.server.ws.WebService.Param.SORT;
 import static org.sonar.api.utils.DateUtils.parseDateTime;
 import static org.sonar.api.web.UserRole.USER;
@@ -78,6 +84,7 @@ import static org.sonar.test.JsonAssert.assertJson;
 import static org.sonarqube.ws.client.measure.MeasuresWsParameters.ADDITIONAL_PERIODS;
 import static org.sonarqube.ws.client.measure.MeasuresWsParameters.DEPRECATED_PARAM_BASE_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_METRIC_KEYS;
 import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_METRIC_PERIOD_SORT;
@@ -88,14 +95,17 @@ import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_STRATEG
 
 public class ComponentTreeActionTest {
   @Rule
-  public UserSessionRule userSession = UserSessionRule.standalone();
+  public UserSessionRule userSession = UserSessionRule.standalone().logIn().setRoot();
   @Rule
   public ExpectedException expectedException = ExpectedException.none();
   @Rule
   public DbTester db = DbTester.create(System2.INSTANCE);
 
   private I18nRule i18n = new I18nRule();
-  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 DbSession dbSession = db.getSession();
@@ -105,13 +115,6 @@ public class ComponentTreeActionTest {
       new ComponentTreeDataLoader(dbClient, new ComponentFinder(dbClient, resourceTypes), userSession, resourceTypes),
       i18n, resourceTypes));
 
-  @Before
-  public void setUp() {
-    userSession.logIn().setRoot();
-    resourceTypes.setChildrenQualifiers(Qualifiers.MODULE, Qualifiers.FILE, Qualifiers.DIRECTORY);
-    resourceTypes.setLeavesQualifiers(Qualifiers.FILE, Qualifiers.UNIT_TEST_FILE);
-  }
-
   @Test
   public void json_example() {
     ComponentDto project = db.components().insertPrivateProject(p -> p.setDbKey("MY_PROJECT")
@@ -124,19 +127,19 @@ public class ComponentTreeActionTest {
       .setDbKey("com.sonarsource:java-markdown:src/main/java/com/sonarsource/markdown/impl/ElementImpl.java")
       .setName("ElementImpl.java")
       .setLanguage("java")
-      .setQualifier(Qualifiers.FILE)
+      .setQualifier(FILE)
       .setPath("src/main/java/com/sonarsource/markdown/impl/ElementImpl.java"));
     ComponentDto file2 = componentDb.insertComponent(newFileDto(project, null)
       .setUuid("AVIwDXE_bJbJqrw6wFwJ")
       .setDbKey("com.sonarsource:java-markdown:src/test/java/com/sonarsource/markdown/impl/ElementImplTest.java")
       .setName("ElementImplTest.java")
       .setLanguage("java")
-      .setQualifier(Qualifiers.UNIT_TEST_FILE)
+      .setQualifier(UNIT_TEST_FILE)
       .setPath("src/test/java/com/sonarsource/markdown/impl/ElementImplTest.java"));
     ComponentDto dir = componentDb.insertComponent(newDirectory(project, "src/main/java/com/sonarsource/markdown/impl")
       .setUuid("AVIwDXE-bJbJqrw6wFv8")
       .setDbKey("com.sonarsource:java-markdown:src/main/java/com/sonarsource/markdown/impl")
-      .setQualifier(Qualifiers.DIRECTORY));
+      .setQualifier(DIRECTORY));
 
     MetricDto complexity = insertComplexityMetric();
     dbClient.measureDao().insert(dbSession,
@@ -487,6 +490,28 @@ public class ComponentTreeActionTest {
     assertThat(result.getComponentsCount()).isEqualTo(0);
   }
 
+  @Test
+  public void branch() {
+    ComponentDto project = db.components().insertPrivateProject();
+    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));
+
+    ComponentTreeWsResponse response = ws.newRequest()
+      .setParam(PARAM_COMPONENT, file.getKey())
+      .setParam(PARAM_BRANCH, file.getBranch())
+      .setParam(PARAM_METRIC_KEYS, complexity.getKey())
+      .executeProtobuf(WsMeasures.ComponentTreeWsResponse.class);
+
+    assertThat(response.getBaseComponent()).extracting(WsMeasures.Component::getKey, WsMeasures.Component::getBranch)
+      .containsExactlyInAnyOrder(file.getKey(), file.getBranch());
+    assertThat(response.getBaseComponent().getMeasuresList())
+      .extracting(WsMeasures.Measure::getMetric, m -> parseDouble(m.getValue()))
+      .containsExactlyInAnyOrder(tuple(complexity.getKey(), measure.getValue()));
+  }
+
   @Test
   public void return_deprecated_id_in_the_response() {
     ComponentDto project = db.components().insertPrivateProject();
@@ -796,6 +821,23 @@ public class ComponentTreeActionTest {
       .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();
+  }
+
   private static MetricDto newMetricDto() {
     return MetricTesting.newMetricDto()
       .setWorstValue(null)
index d6c550eae920e2e9df78542d579d83a002359df4..0a89515828a0e7db684351159235903becb6070a 100644 (file)
@@ -28,6 +28,7 @@ public class ComponentTreeWsRequest {
   private String baseComponentId;
   private String baseComponentKey;
   private String component;
+  private String branch;
   private String strategy;
   private List<String> qualifiers;
   private List<String> additionalFields;
@@ -89,6 +90,16 @@ public class ComponentTreeWsRequest {
     return this;
   }
 
+  @CheckForNull
+  public String getBranch() {
+    return branch;
+  }
+
+  public ComponentTreeWsRequest setBranch(@Nullable String branch) {
+    this.branch = branch;
+    return this;
+  }
+
   @CheckForNull
   public String getStrategy() {
     return strategy;
index 9c94c285667dd00fb559cc87253cfb6ed4937982..db8b7e4bde23f47e3bfb5c123a87bfd5bce43678 100644 (file)
@@ -61,6 +61,7 @@ public class MeasuresService extends BaseService {
       .setParam(DEPRECATED_PARAM_BASE_COMPONENT_ID, request.getBaseComponentId())
       .setParam(DEPRECATED_PARAM_BASE_COMPONENT_KEY, request.getBaseComponentKey())
       .setParam(PARAM_COMPONENT, request.getComponent())
+      .setParam(PARAM_BRANCH, request.getBranch())
       .setParam(PARAM_STRATEGY, request.getStrategy())
       .setParam(PARAM_QUALIFIERS, inlineMultipleParamValue(request.getQualifiers()))
       .setParam(PARAM_METRIC_KEYS, inlineMultipleParamValue(request.getMetricKeys()))
index b42cc3e45ad24b8eca4e7e3e784286f5fe00433b..c8d4e66d339626bcfe9fb5af04682c52c7bcd9d3 100644 (file)
@@ -114,6 +114,7 @@ public class MeasuresServiceTest {
       .setBaseComponentId(VALUE_BASE_COMPONENT_ID)
       .setBaseComponentKey(VALUE_BASE_COMPONENT_KEY)
       .setComponent(VALUE_BASE_COMPONENT_KEY)
+      .setBranch("my_branch")
       .setMetricKeys(VALUE_METRIC_KEYS)
       .setStrategy(VALUE_STRATEGY)
       .setQualifiers(VALUE_QUALIFIERS)
@@ -136,6 +137,7 @@ public class MeasuresServiceTest {
       .hasParam(DEPRECATED_PARAM_BASE_COMPONENT_ID, VALUE_BASE_COMPONENT_ID)
       .hasParam(DEPRECATED_PARAM_BASE_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_STRATEGY, VALUE_STRATEGY)
       .hasParam(PARAM_QUALIFIERS, "FIL,PRJ")