From 9d3436c83373e930762fffefc3430c9f50b46cf2 Mon Sep 17 00:00:00 2001 From: Teryk Bellahsene Date: Wed, 6 Apr 2016 19:19:51 +0200 Subject: [PATCH] SONAR-7528 WS api/measures/component handles developer measures --- .../server/measure/ws/ComponentAction.java | 28 +++++-- .../measure/ws/ComponentActionTest.java | 79 +++++++++++++++++-- .../org/sonar/db/measure/MeasureMapper.java | 2 +- .../org/sonar/db/measure/MeasureMapper.xml | 7 +- .../ws/client/measure/ComponentWsRequest.java | 22 ++++++ .../ws/client/measure/MeasuresService.java | 4 +- 6 files changed, 128 insertions(+), 14 deletions(-) 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 2c8b748e8ae..4ee10da7ab6 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 @@ -30,6 +30,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; +import javax.annotation.CheckForNull; import javax.annotation.Nullable; import org.sonar.api.resources.Qualifiers; import org.sonar.api.server.ws.Request; @@ -57,11 +58,12 @@ import static com.google.common.base.Objects.firstNonNull; import static com.google.common.collect.FluentIterable.from; import static java.lang.String.format; import static java.util.Collections.emptyList; -import static java.util.Collections.singletonList; 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.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.createDeveloperParameters; 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; @@ -75,6 +77,8 @@ import static org.sonarqube.ws.client.measure.MeasuresWsParameters.ADDITIONAL_PE import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_ADDITIONAL_FIELDS; import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_COMPONENT_ID; import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_COMPONENT_KEY; +import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_DEVELOPER_ID; +import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_DEVELOPER_KEY; import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_METRIC_KEYS; public class ComponentAction implements MeasuresWsAction { @@ -115,6 +119,7 @@ public class ComponentAction implements MeasuresWsAction { createMetricKeysParameter(action); createAdditionalFieldsParameter(action); + createDeveloperParameters(action); } @Override @@ -127,12 +132,13 @@ public class ComponentAction implements MeasuresWsAction { DbSession dbSession = dbClient.openSession(false); try { ComponentDto component = componentFinder.getByUuidOrKey(dbSession, request.getComponentId(), request.getComponentKey(), COMPONENT_ID_AND_KEY); + Long developerId = searchDeveloperId(dbSession, request); Optional refComponent = getReferenceComponent(dbSession, component); checkPermissions(component); SnapshotDto lastSnapshot = dbClient.snapshotDao().selectLastSnapshotByComponentId(dbSession, component.getId()); List metrics = searchMetrics(dbSession, request); List periods = snapshotToWsPeriods(lastSnapshot); - List measures = searchMeasures(dbSession, component, lastSnapshot, metrics, periods); + List measures = searchMeasures(dbSession, component, lastSnapshot, metrics, periods, developerId); return buildResponse(request, component, refComponent, measures, metrics, periods); } finally { @@ -140,6 +146,15 @@ public class ComponentAction implements MeasuresWsAction { } } + @CheckForNull + private Long searchDeveloperId(DbSession dbSession, ComponentWsRequest request) { + if ((request.getDeveloperId() == null && request.getDeveloperKey() == null)) { + return null; + } + + return componentFinder.getByUuidOrKey(dbSession, request.getDeveloperId(), request.getDeveloperKey(), DEVELOPER_ID_AND_KEY).getId(); + } + private Optional getReferenceComponent(DbSession dbSession, ComponentDto component) { if (component.getCopyResourceId() == null) { return Optional.absent(); @@ -193,13 +208,14 @@ public class ComponentAction implements MeasuresWsAction { return metrics; } - private List searchMeasures(DbSession dbSession, ComponentDto component, @Nullable SnapshotDto snapshot, List metrics, List periods) { + private List searchMeasures(DbSession dbSession, ComponentDto component, @Nullable SnapshotDto snapshot, List metrics, List periods, + @Nullable Long developerId) { if (snapshot == null) { return emptyList(); } List metricIds = Lists.transform(metrics, MetricDtoFunctions.toId()); - List measures = dbClient.measureDao().selectBySnapshotIdsAndMetricIds(dbSession, singletonList(snapshot.getId()), metricIds); + List measures = dbClient.measureDao().selectByDeveloperForSnapshotAndMetrics(dbSession, developerId, snapshot.getId(), metricIds); addBestValuesToMeasures(measures, component, metrics, periods); return measures; @@ -235,7 +251,9 @@ public class ComponentAction implements MeasuresWsAction { .setComponentId(request.param(PARAM_COMPONENT_ID)) .setComponentKey(request.param(PARAM_COMPONENT_KEY)) .setAdditionalFields(request.paramAsStrings(PARAM_ADDITIONAL_FIELDS)) - .setMetricKeys(request.mandatoryParamAsStrings(PARAM_METRIC_KEYS)); + .setMetricKeys(request.mandatoryParamAsStrings(PARAM_METRIC_KEYS)) + .setDeveloperId(request.param(PARAM_DEVELOPER_ID)) + .setDeveloperKey(request.param(PARAM_DEVELOPER_KEY)); checkRequest(!componentWsRequest.getMetricKeys().isEmpty(), "At least one metric key must be provided"); return componentWsRequest; } 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 804c274a898..343ad0613b6 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 @@ -42,12 +42,14 @@ import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.exceptions.ForbiddenException; 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.MediaTypes; import org.sonarqube.ws.WsMeasures.ComponentWsResponse; import static org.assertj.core.api.Assertions.assertThat; import static org.sonar.api.utils.DateUtils.parseDateTime; +import static org.sonar.db.component.ComponentTesting.newDeveloper; import static org.sonar.db.component.ComponentTesting.newFileDto; import static org.sonar.db.component.ComponentTesting.newProjectCopy; import static org.sonar.db.component.ComponentTesting.newProjectDto; @@ -58,9 +60,10 @@ import static org.sonar.db.metric.MetricTesting.newMetricDto; import static org.sonar.test.JsonAssert.assertJson; import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_ADDITIONAL_FIELDS; import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_COMPONENT_ID; +import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_DEVELOPER_ID; +import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_DEVELOPER_KEY; import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_METRIC_KEYS; - public class ComponentActionTest { private static final String PROJECT_UUID = "project-uuid"; @@ -140,6 +143,67 @@ public class ComponentActionTest { assertThat(response.getComponent().getRefKey()).isEqualTo("project-key"); } + @Test + public void developer_measure_by_developer_uuid() { + ComponentDto developer = newDeveloper("developer-name"); + componentDb.insertDeveloperAndSnapshot(developer); + ComponentDto project = newProjectDto("project-uuid"); + SnapshotDto projectSnapshot = componentDb.insertProjectAndSnapshot(project); + ComponentDto file = newFileDto(project, "file-uuid"); + SnapshotDto fileSnapshot = componentDb.insertComponentAndSnapshot(file, projectSnapshot); + MetricDto ncloc = insertNclocMetric(); + dbClient.measureDao().insert(dbSession, + newMeasureDto(ncloc, fileSnapshot.getId()).setValue(42.0d).setDeveloperId(null), + newMeasureDto(ncloc, fileSnapshot.getId()).setValue(1984.0d).setDeveloperId(developer.getId())); + db.commit(); + + ComponentWsResponse result = call(ws.newRequest() + .setParam(PARAM_COMPONENT_ID, "file-uuid") + .setParam(PARAM_DEVELOPER_ID, developer.uuid()) + .setParam(PARAM_METRIC_KEYS, "ncloc")); + + assertThat(result.getComponent().getMeasuresCount()).isEqualTo(1); + assertThat(result.getComponent().getMeasures(0).getValue()).isEqualTo("1984"); + } + + @Test + public void developer_measure_by_developer_key() { + ComponentDto developer = newDeveloper("developer-name"); + componentDb.insertDeveloperAndSnapshot(developer); + ComponentDto project = newProjectDto(PROJECT_UUID); + SnapshotDto projectSnapshot = componentDb.insertProjectAndSnapshot(project); + ComponentDto file = newFileDto(project, "file-uuid"); + SnapshotDto fileSnapshot = componentDb.insertComponentAndSnapshot(file, projectSnapshot); + MetricDto ncloc = insertNclocMetric(); + dbClient.measureDao().insert(dbSession, + newMeasureDto(ncloc, fileSnapshot.getId()).setValue(42.0d).setDeveloperId(null), + newMeasureDto(ncloc, fileSnapshot.getId()).setValue(1984.0d).setDeveloperId(developer.getId())); + db.commit(); + + ComponentWsResponse result = call(ws.newRequest() + .setParam(PARAM_COMPONENT_ID, "file-uuid") + .setParam(PARAM_DEVELOPER_KEY, developer.key()) + .setParam(PARAM_METRIC_KEYS, "ncloc")); + + assertThat(result.getComponent().getMeasuresCount()).isEqualTo(1); + assertThat(result.getComponent().getMeasures(0).getValue()).isEqualTo("1984"); + } + + @Test + public void fail_when_developer_is_not_found() { + expectedException.expect(NotFoundException.class); + expectedException.expectMessage("Component id 'unknown-developer-id' not found"); + + componentDb.insertProjectAndSnapshot(newProjectDto(PROJECT_UUID)); + insertNclocMetric(); + + call(ws.newRequest() + .setParam(PARAM_COMPONENT_ID, PROJECT_UUID) + .setParam(PARAM_METRIC_KEYS, "ncloc") + .setParam(PARAM_DEVELOPER_ID, "unknown-developer-id") + ); + } + @Test public void fail_when_a_metric_is_not_found() { componentDb.insertProjectAndSnapshot(newProjectDto(PROJECT_UUID)); @@ -174,13 +238,16 @@ public class ComponentActionTest { } private ComponentWsResponse newRequest(String componentUuid, String metricKeys) { - InputStream responseStream = ws.newRequest() - .setMediaType(MediaTypes.PROTOBUF) + return call(ws.newRequest() .setParam(PARAM_COMPONENT_ID, componentUuid) .setParam(PARAM_METRIC_KEYS, metricKeys) - .setParam(PARAM_ADDITIONAL_FIELDS, "metrics,periods") - .execute() - .getInputStream(); + .setParam(PARAM_ADDITIONAL_FIELDS, "metrics,periods")); + } + + private ComponentWsResponse call(TestRequest request) { + InputStream responseStream = request + .setMediaType(MediaTypes.PROTOBUF) + .execute().getInputStream(); try { return ComponentWsResponse.parseFrom(responseStream); diff --git a/sonar-db/src/main/java/org/sonar/db/measure/MeasureMapper.java b/sonar-db/src/main/java/org/sonar/db/measure/MeasureMapper.java index fb9d7254bcc..5c0910167c0 100644 --- a/sonar-db/src/main/java/org/sonar/db/measure/MeasureMapper.java +++ b/sonar-db/src/main/java/org/sonar/db/measure/MeasureMapper.java @@ -33,7 +33,7 @@ public interface MeasureMapper { List selectBySnapshotAndMetricKeys(@Param("snapshotId") long snapshotId, @Param("metricKeys") List metricKeys); - List selectByDeveloperForSnapshotAndMetrics(@Param("developerId") long developerId, @Param("snapshotId") long snapshotId, + List selectByDeveloperForSnapshotAndMetrics(@Nullable @Param("developerId") Long developerId, @Param("snapshotId") long snapshotId, @Param("metricIds") List metricIds); List selectBySnapshotAndMetrics(@Param("snapshotId") long snapshotId, @Param("metricIds") List input); diff --git a/sonar-db/src/main/resources/org/sonar/db/measure/MeasureMapper.xml b/sonar-db/src/main/resources/org/sonar/db/measure/MeasureMapper.xml index 7c97ed532e9..eb938f119ba 100644 --- a/sonar-db/src/main/resources/org/sonar/db/measure/MeasureMapper.xml +++ b/sonar-db/src/main/resources/org/sonar/db/measure/MeasureMapper.xml @@ -76,7 +76,12 @@ FROM project_measures pm pm.snapshot_id = #{snapshotId} - AND pm.person_id = #{developerId} + + AND pm.person_id = #{developerId} + + + AND pm.person_id is NULL + AND pm.metric_id=#{metricId} 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 df6a800f9d5..7230293033a 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 @@ -28,6 +28,8 @@ public class ComponentWsRequest { private String componentKey; private List metricKeys; private List additionalFields; + private String developerId; + private String developerKey; @CheckForNull public String getComponentId() { @@ -67,4 +69,24 @@ public class ComponentWsRequest { this.additionalFields = additionalFields; return this; } + + @CheckForNull + public String getDeveloperId() { + return developerId; + } + + public ComponentWsRequest setDeveloperId(@Nullable String developerId) { + this.developerId = developerId; + return this; + } + + @CheckForNull + public String getDeveloperKey() { + return developerKey; + } + + public ComponentWsRequest setDeveloperKey(@Nullable String developerKey) { + this.developerKey = developerKey; + return this; + } } 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 829b170b9a9..6eab2608301 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 @@ -70,7 +70,9 @@ public class MeasuresService extends BaseService { .setParam(PARAM_COMPONENT_ID, request.getComponentId()) .setParam(PARAM_COMPONENT_KEY, request.getComponentKey()) .setParam(PARAM_ADDITIONAL_FIELDS, inlineMultipleParamValue(request.getAdditionalFields())) - .setParam(PARAM_METRIC_KEYS, inlineMultipleParamValue(request.getMetricKeys())); + .setParam(PARAM_METRIC_KEYS, inlineMultipleParamValue(request.getMetricKeys())) + .setParam(PARAM_DEVELOPER_ID, request.getDeveloperId()) + .setParam(PARAM_DEVELOPER_KEY, request.getDeveloperKey()); return call(getRequest, ComponentWsResponse.parser()); } -- 2.39.5