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;
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;
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;
.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);
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);
}
}
+ 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) {
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))
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;
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;
}
}
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;
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;
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
.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();
.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();
private String componentId;
private String componentKey;
private String component;
+ private String branch;
private List<String> metricKeys;
private List<String> additionalFields;
private String developerId;
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;
}
.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())
optional string path = 9;
optional string language = 10;
repeated Measure measures = 11;
+ optional string branch = 12;
}
message Period {
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;
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;
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()