From: Julien Lancelot Date: Fri, 4 Aug 2017 09:52:07 +0000 (+0200) Subject: SONAR-9616 Support branch in api/measures/component X-Git-Tag: 6.6-RC1~380^2~121 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=4f500d461015eb6edc471677961c72e8e68b87bf;p=sonarqube.git SONAR-9616 Support branch in api/measures/component --- diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentAction.java b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentAction.java index 5bcfd73345f..a23e33014e6 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentAction.java @@ -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 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)) diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentDtoToWsComponent.java b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentDtoToWsComponent.java index df45ff19672..3129de298a3 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentDtoToWsComponent.java +++ b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentDtoToWsComponent.java @@ -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; } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentActionTest.java index 0e7ba8748a3..4a4012d8442 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentActionTest.java @@ -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(); diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/measure/ComponentWsRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/measure/ComponentWsRequest.java index 998ac57209f..cc6e10ef7d9 100644 --- a/sonar-ws/src/main/java/org/sonarqube/ws/client/measure/ComponentWsRequest.java +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/measure/ComponentWsRequest.java @@ -27,6 +27,7 @@ public class ComponentWsRequest { private String componentId; private String componentKey; private String component; + private String branch; private List metricKeys; private List 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 getMetricKeys() { return metricKeys; } diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/measure/MeasuresService.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/measure/MeasuresService.java index dca24806339..9c94c285667 100644 --- a/sonar-ws/src/main/java/org/sonarqube/ws/client/measure/MeasuresService.java +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/measure/MeasuresService.java @@ -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()) diff --git a/sonar-ws/src/main/protobuf/ws-measures.proto b/sonar-ws/src/main/protobuf/ws-measures.proto index 4c64b51ba47..199184eda3b 100644 --- a/sonar-ws/src/main/protobuf/ws-measures.proto +++ b/sonar-ws/src/main/protobuf/ws-measures.proto @@ -75,6 +75,7 @@ message Component { optional string path = 9; optional string language = 10; repeated Measure measures = 11; + optional string branch = 12; } message Period { diff --git a/sonar-ws/src/test/java/org/sonarqube/ws/client/measure/MeasuresServiceTest.java b/sonar-ws/src/test/java/org/sonarqube/ws/client/measure/MeasuresServiceTest.java index b83ac53ce90..b42cc3e45ad 100644 --- a/sonar-ws/src/test/java/org/sonarqube/ws/client/measure/MeasuresServiceTest.java +++ b/sonar-ws/src/test/java/org/sonarqube/ws/client/measure/MeasuresServiceTest.java @@ -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()