summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/component/ComponentFinder.java9
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeAction.java6
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeDataLoader.java34
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/measure/ws/MeasuresWsParametersBuilder.java13
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/ws/KeyExamples.java1
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentTreeActionTest.java106
-rw-r--r--sonar-db/src/main/java/org/sonar/db/measure/MeasureDao.java13
-rw-r--r--sonar-db/src/main/java/org/sonar/db/measure/MeasureMapper.java4
-rw-r--r--sonar-db/src/main/resources/org/sonar/db/measure/MeasureMapper.xml9
-rw-r--r--sonar-db/src/test/java/org/sonar/db/measure/MeasureDaoTest.java75
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/measure/ComponentTreeWsRequest.java22
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/measure/MeasuresService.java4
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/measure/MeasuresWsParameters.java2
-rw-r--r--sonar-ws/src/test/java/org/sonarqube/ws/client/measure/MeasuresServiceTest.java10
14 files changed, 284 insertions, 24 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ComponentFinder.java b/server/sonar-server/src/main/java/org/sonar/server/component/ComponentFinder.java
index d2b84d00be2..41554dd51ce 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/component/ComponentFinder.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/component/ComponentFinder.java
@@ -67,9 +67,9 @@ public class ComponentFinder {
/**
* A project can be:
* <ul>
- * <li>a project – ex: SonarQube</li>
- * <li>a view – ex: Language Team</li>
- * <li>a developer – ex: Simon Brandhof</li>
+ * <li>a project – ex: SonarQube</li>
+ * <li>a view – ex: Language Team</li>
+ * <li>a developer – ex: Simon Brandhof</li>
* </ul>
*/
public ComponentDto getRootComponentOrModuleByUuidOrKey(DbSession dbSession, @Nullable String projectUuid, @Nullable String projectKey, ResourceTypes resourceTypes) {
@@ -106,7 +106,8 @@ public class ComponentFinder {
UUID_AND_KEY("uuid", "key"),
ID_AND_KEY("id", "key"),
COMPONENT_ID_AND_KEY("componentId", "componentKey"),
- BASE_COMPONENT_ID_AND_KEY("baseComponentId", "baseComponentKey");
+ BASE_COMPONENT_ID_AND_KEY("baseComponentId", "baseComponentKey"),
+ DEVELOPER_ID_AND_KEY("developerId", "developerKey");
private final String uuidParamName;
private final String keyParamName;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeAction.java b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeAction.java
index c85ee281759..8af4ffe482b 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeAction.java
@@ -39,6 +39,7 @@ import static java.lang.String.format;
import static org.sonar.core.util.Uuids.UUID_EXAMPLE_02;
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.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
@@ -52,6 +53,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_BASE_COMPONENT_ID;
import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_BASE_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;
import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_METRIC_PERIOD_SORT;
import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_METRIC_SORT;
@@ -151,6 +154,7 @@ public class ComponentTreeAction implements MeasuresWsAction {
createMetricKeysParameter(action);
createAdditionalFieldsParameter(action);
+ createDeveloperParameters(action);
createQualifiersParameter(action, newQualifierParameterContext(userSession, i18n, resourceTypes));
action.createParam(PARAM_STRATEGY)
@@ -250,6 +254,8 @@ public class ComponentTreeAction implements MeasuresWsAction {
.setAsc(request.paramAsBoolean(Param.ASCENDING))
.setMetricSort(request.param(PARAM_METRIC_SORT))
.setMetricPeriodSort(request.paramAsInt(PARAM_METRIC_PERIOD_SORT))
+ .setDeveloperId(request.param(PARAM_DEVELOPER_ID))
+ .setDeveloperKey(request.param(PARAM_DEVELOPER_KEY))
.setPage(request.mandatoryParamAsInt(Param.PAGE))
.setPageSize(request.mandatoryParamAsInt(Param.PAGE_SIZE))
.setQuery(request.param(Param.TEXT_QUERY));
diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeDataLoader.java b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeDataLoader.java
index fd26d5ba2b6..789a2171d00 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeDataLoader.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeDataLoader.java
@@ -30,6 +30,7 @@ import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.collect.Table;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
@@ -38,6 +39,7 @@ import java.util.Map;
import java.util.Set;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import org.sonar.api.resources.Qualifiers;
import org.sonar.api.resources.ResourceTypes;
import org.sonar.api.utils.Paging;
@@ -65,7 +67,9 @@ import static com.google.common.collect.Sets.newHashSet;
import static java.lang.String.format;
import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonList;
+import static java.util.Objects.requireNonNull;
import static org.sonar.server.component.ComponentFinder.ParamNames.BASE_COMPONENT_ID_AND_KEY;
+import static org.sonar.server.component.ComponentFinder.ParamNames.DEVELOPER_ID_AND_KEY;
import static org.sonar.server.measure.ws.ComponentTreeAction.ALL_STRATEGY;
import static org.sonar.server.measure.ws.ComponentTreeAction.CHILDREN_STRATEGY;
import static org.sonar.server.measure.ws.ComponentTreeAction.LEAVES_STRATEGY;
@@ -101,6 +105,7 @@ public class ComponentTreeDataLoader {
.setBaseComponent(baseComponent)
.build();
}
+ Long developerId = searchDeveloperId(dbSession, wsRequest);
ComponentTreeQuery dbQuery = toComponentTreeQuery(wsRequest, baseSnapshot);
ComponentDtosAndTotal componentDtosAndTotal = searchComponents(dbSession, dbQuery, wsRequest);
@@ -109,7 +114,7 @@ public class ComponentTreeDataLoader {
List<MetricDto> metrics = searchMetrics(dbSession, wsRequest);
List<WsMeasures.Period> periods = snapshotToWsPeriods(baseSnapshot);
Table<String, MetricDto, MeasureDto> measuresByComponentUuidAndMetric = searchMeasuresByComponentUuidAndMetric(dbSession, baseComponent, baseSnapshot, components, metrics,
- periods);
+ periods, developerId);
components = sortComponents(components, wsRequest, metrics, measuresByComponentUuidAndMetric);
components = paginateComponents(components, componentCount, wsRequest);
@@ -129,6 +134,15 @@ public class ComponentTreeDataLoader {
}
}
+ @CheckForNull
+ private Long searchDeveloperId(DbSession dbSession, ComponentTreeWsRequest wsRequest) {
+ if (wsRequest.getDeveloperId() == null && wsRequest.getDeveloperKey() == null) {
+ return null;
+ }
+
+ return componentFinder.getByUuidOrKey(dbSession, wsRequest.getDeveloperId(), wsRequest.getDeveloperKey(), DEVELOPER_ID_AND_KEY).getId();
+ }
+
private Map<Long, ComponentDto> searchReferenceComponentsById(DbSession dbSession, List<ComponentDtoWithSnapshotId> components) {
List<Long> referenceComponentIds = from(components)
.transform(ComponentDtoWithSnapshotIdToCopyResourceIdFunction.INSTANCE)
@@ -148,7 +162,11 @@ public class ComponentTreeDataLoader {
}
private ComponentDtosAndTotal searchComponents(DbSession dbSession, ComponentTreeQuery dbQuery, ComponentTreeWsRequest wsRequest) {
- switch (wsRequest.getStrategy()) {
+ if (dbQuery.getQualifiers() != null && dbQuery.getQualifiers().isEmpty()) {
+ return new ComponentDtosAndTotal(Collections.<ComponentDtoWithSnapshotId>emptyList(), 0);
+ }
+ String strategy = requireNonNull(wsRequest.getStrategy());
+ switch (strategy) {
case CHILDREN_STRATEGY:
return new ComponentDtosAndTotal(
dbClient.componentDao().selectDirectChildren(dbSession, dbQuery),
@@ -164,11 +182,12 @@ public class ComponentTreeDataLoader {
}
private List<MetricDto> searchMetrics(DbSession dbSession, ComponentTreeWsRequest request) {
- List<MetricDto> metrics = dbClient.metricDao().selectByKeys(dbSession, request.getMetricKeys());
- if (metrics.size() < request.getMetricKeys().size()) {
+ List<String> metricKeys = requireNonNull(request.getMetricKeys());
+ List<MetricDto> metrics = dbClient.metricDao().selectByKeys(dbSession, metricKeys);
+ if (metrics.size() < metricKeys.size()) {
List<String> foundMetricKeys = Lists.transform(metrics, MetricDtoFunctions.toKey());
Set<String> missingMetricKeys = Sets.difference(
- new LinkedHashSet<>(request.getMetricKeys()),
+ new LinkedHashSet<>(metricKeys),
new LinkedHashSet<>(foundMetricKeys));
throw new NotFoundException(format("The following metric keys are not found: %s", Joiner.on(", ").join(missingMetricKeys)));
@@ -179,7 +198,7 @@ public class ComponentTreeDataLoader {
private Table<String, MetricDto, MeasureDto> searchMeasuresByComponentUuidAndMetric(DbSession dbSession, ComponentDto baseComponent, SnapshotDto baseSnapshot,
List<ComponentDtoWithSnapshotId> components, List<MetricDto> metrics,
- List<WsMeasures.Period> periods) {
+ List<WsMeasures.Period> periods, @Nullable Long developerId) {
Map<Long, ComponentDto> componentsBySnapshotId = new HashMap<>();
componentsBySnapshotId.put(baseSnapshot.getId(), baseComponent);
for (ComponentDtoWithSnapshotId component : components) {
@@ -187,7 +206,8 @@ public class ComponentTreeDataLoader {
}
Map<Integer, MetricDto> metricsById = Maps.uniqueIndex(metrics, MetricDtoFunctions.toId());
- List<MeasureDto> measureDtos = dbClient.measureDao().selectBySnapshotIdsAndMetricIds(dbSession,
+ List<MeasureDto> measureDtos = dbClient.measureDao().selectByDeveloperAndSnapshotIdsAndMetricIds(dbSession,
+ developerId,
new ArrayList<>(componentsBySnapshotId.keySet()),
new ArrayList<>(metricsById.keySet()));
diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/MeasuresWsParametersBuilder.java b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/MeasuresWsParametersBuilder.java
index 4107953931a..2f24e466991 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/MeasuresWsParametersBuilder.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/MeasuresWsParametersBuilder.java
@@ -21,9 +21,13 @@ package org.sonar.server.measure.ws;
import org.sonar.api.server.ws.WebService.NewAction;
import org.sonar.api.server.ws.WebService.NewParam;
+import org.sonar.core.util.Uuids;
+import org.sonar.server.ws.KeyExamples;
import static org.sonarqube.ws.client.measure.MeasuresWsParameters.ADDITIONAL_FIELDS;
import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_ADDITIONAL_FIELDS;
+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;
class MeasuresWsParametersBuilder {
@@ -46,4 +50,13 @@ class MeasuresWsParametersBuilder {
.setExampleValue("ncloc,complexity,violations");
}
+ static void createDeveloperParameters(NewAction action) {
+ action.createParam(PARAM_DEVELOPER_ID)
+ .setDescription("Developer id. If set, developer's measures are returned.")
+ .setExampleValue(Uuids.UUID_EXAMPLE_01);
+ action.createParam(PARAM_DEVELOPER_KEY)
+ .setDescription("Developer key. If set, developer's measures are returned.")
+ .setExampleValue(KeyExamples.KEY_DEVELOPER_EXAMPLE_001);
+ }
+
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/ws/KeyExamples.java b/server/sonar-server/src/main/java/org/sonar/server/ws/KeyExamples.java
index 4f57cc635b9..72ecdebd799 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/ws/KeyExamples.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/ws/KeyExamples.java
@@ -22,6 +22,7 @@ package org.sonar.server.ws;
public class KeyExamples {
public static final String KEY_FILE_EXAMPLE_001 = "my_project:/src/foo/Bar.php";
public static final String KEY_PROJECT_EXAMPLE_001 = "my_project";
+ public static final String KEY_DEVELOPER_EXAMPLE_001 = "DEV:ada@lovelace.com";
private KeyExamples() {
// prevent instantiation
diff --git a/server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentTreeActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentTreeActionTest.java
index c087d4c949c..cc932d405b9 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentTreeActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentTreeActionTest.java
@@ -65,6 +65,8 @@ import static org.sonar.db.component.ComponentTesting.newProjectDto;
import static org.sonar.db.component.SnapshotTesting.newSnapshotForProject;
import static org.sonar.db.measure.MeasureTesting.newMeasureDto;
import static org.sonar.db.metric.MetricTesting.newMetricDto;
+import static org.sonar.server.measure.ws.ComponentTreeAction.CHILDREN_STRATEGY;
+import static org.sonar.server.measure.ws.ComponentTreeAction.LEAVES_STRATEGY;
import static org.sonar.server.measure.ws.ComponentTreeAction.METRIC_PERIOD_SORT;
import static org.sonar.server.measure.ws.ComponentTreeAction.METRIC_SORT;
import static org.sonar.server.measure.ws.ComponentTreeAction.NAME_SORT;
@@ -72,6 +74,8 @@ 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.PARAM_ADDITIONAL_FIELDS;
import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_BASE_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;
import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_METRIC_PERIOD_SORT;
import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_METRIC_SORT;
@@ -318,6 +322,108 @@ public class ComponentTreeActionTest {
}
@Test
+ public void load_developer_measures_by_developer_uuid() {
+ ComponentDto developer = newDeveloper("developer").setUuid("developer-uuid");
+ ComponentDto project = newProjectDto("project-uuid").setKey("project-key");
+ SnapshotDto developerSnapshot = componentDb.insertDeveloperAndSnapshot(developer);
+ SnapshotDto projectSnapshot = componentDb.insertProjectAndSnapshot(project);
+ SnapshotDto file1Snapshot = componentDb.insertComponentAndSnapshot(newFileDto(project, "file1-uuid"), projectSnapshot);
+ SnapshotDto file2Snapshot = componentDb.insertComponentAndSnapshot(newFileDto(project, "file2-uuid"), projectSnapshot);
+ componentDb.insertComponentAndSnapshot(newDevProjectCopy("project-uuid-copy", project, developer), developerSnapshot);
+ MetricDto ncloc = insertNclocMetric();
+ dbClient.measureDao().insert(dbSession,
+ newMeasureDto(ncloc, projectSnapshot.getId()).setDeveloperId(developer.getId()),
+ newMeasureDto(ncloc, file1Snapshot.getId())
+ .setValue(3d)
+ .setDeveloperId(developer.getId()),
+ // measures are not specific to the developer
+ newMeasureDto(ncloc, file1Snapshot.getId()).setDeveloperId(null),
+ newMeasureDto(ncloc, file2Snapshot.getId()).setDeveloperId(null));
+ db.commit();
+
+ ComponentTreeWsResponse response = call(ws.newRequest()
+ .setParam(PARAM_BASE_COMPONENT_ID, "project-uuid")
+ .setParam(PARAM_DEVELOPER_ID, "developer-uuid")
+ .setParam(PARAM_STRATEGY, CHILDREN_STRATEGY)
+ .setParam(PARAM_METRIC_KEYS, "ncloc"));
+
+ assertThat(response.getComponentsCount()).isEqualTo(2);
+ WsMeasures.Component file = response.getComponents(0);
+ assertThat(file.getId()).isEqualTo("file1-uuid");
+ assertThat(file.getMeasuresCount()).isEqualTo(1);
+ assertThat(file.getMeasures(0).getValue()).isEqualTo("3");
+ }
+
+ @Test
+ public void load_developer_measures_by_developer_key() {
+ ComponentDto developer = newDeveloper("developer").setUuid("developer-uuid");
+ ComponentDto project = newProjectDto("project-uuid").setKey("project-key");
+ SnapshotDto developerSnapshot = componentDb.insertDeveloperAndSnapshot(developer);
+ SnapshotDto projectSnapshot = componentDb.insertProjectAndSnapshot(project);
+ SnapshotDto file1Snapshot = componentDb.insertComponentAndSnapshot(newFileDto(project, "file1-uuid"), projectSnapshot);
+ componentDb.insertComponentAndSnapshot(newDevProjectCopy("project-uuid-copy", project, developer), developerSnapshot);
+ MetricDto ncloc = insertNclocMetric();
+ dbClient.measureDao().insert(dbSession,
+ newMeasureDto(ncloc, file1Snapshot.getId())
+ .setValue(3d)
+ .setDeveloperId(developer.getId()));
+ db.commit();
+
+ ComponentTreeWsResponse response = call(ws.newRequest()
+ .setParam(PARAM_BASE_COMPONENT_ID, "project-uuid")
+ .setParam(PARAM_DEVELOPER_KEY, developer.key())
+ .setParam(PARAM_METRIC_KEYS, "ncloc"));
+
+ assertThat(response.getComponentsCount()).isEqualTo(1);
+ WsMeasures.Component file = response.getComponents(0);
+ assertThat(file.getId()).isEqualTo("file1-uuid");
+ assertThat(file.getMeasuresCount()).isEqualTo(1);
+ assertThat(file.getMeasures(0).getValue()).isEqualTo("3");
+ }
+
+ @Test
+ public void load_measures_when_no_leave_qualifier() {
+ resourceTypes.setLeavesQualifiers();
+ String projectUuid = "project-uuid";
+ ComponentDto project = newProjectDto(projectUuid);
+ SnapshotDto projectSnapshot = componentDb.insertProjectAndSnapshot(project);
+ componentDb.insertComponentAndSnapshot(newFileDto(project), projectSnapshot);
+ insertNclocMetric();
+
+ ComponentTreeWsResponse result = call(ws.newRequest()
+ .setParam(PARAM_BASE_COMPONENT_ID, projectUuid)
+ .setParam(PARAM_STRATEGY, LEAVES_STRATEGY)
+ .setParam(PARAM_METRIC_KEYS, "ncloc")
+ );
+
+ assertThat(result.getBaseComponent().getId()).isEqualTo(projectUuid);
+ assertThat(result.getComponentsCount()).isEqualTo(0);
+ }
+
+ @Test
+ public void fail_when_developer_is_unknown() {
+ expectedException.expect(NotFoundException.class);
+
+ ComponentDto developer = newDeveloper("developer").setUuid("developer-uuid");
+ ComponentDto project = newProjectDto("project-uuid").setKey("project-key");
+ SnapshotDto developerSnapshot = componentDb.insertDeveloperAndSnapshot(developer);
+ SnapshotDto projectSnapshot = componentDb.insertProjectAndSnapshot(project);
+ SnapshotDto file1Snapshot = componentDb.insertComponentAndSnapshot(newFileDto(project, "file1-uuid"), projectSnapshot);
+ componentDb.insertComponentAndSnapshot(newDevProjectCopy("project-uuid-copy", project, developer), developerSnapshot);
+ MetricDto ncloc = insertNclocMetric();
+ dbClient.measureDao().insert(dbSession,
+ newMeasureDto(ncloc, file1Snapshot.getId())
+ .setValue(3d)
+ .setDeveloperId(developer.getId()));
+ db.commit();
+
+ call(ws.newRequest()
+ .setParam(PARAM_BASE_COMPONENT_ID, "project-uuid")
+ .setParam(PARAM_DEVELOPER_KEY, "unknown-developer-key")
+ .setParam(PARAM_METRIC_KEYS, "ncloc"));
+ }
+
+ @Test
public void fail_when_metric_keys_parameter_is_empty() {
componentDb.insertProjectAndSnapshot(newProjectDto("project-uuid"));
diff --git a/sonar-db/src/main/java/org/sonar/db/measure/MeasureDao.java b/sonar-db/src/main/java/org/sonar/db/measure/MeasureDao.java
index a73d92b128b..69724405aab 100644
--- a/sonar-db/src/main/java/org/sonar/db/measure/MeasureDao.java
+++ b/sonar-db/src/main/java/org/sonar/db/measure/MeasureDao.java
@@ -26,6 +26,7 @@ import java.util.List;
import java.util.Set;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
import org.sonar.db.Dao;
import org.sonar.db.DatabaseUtils;
import org.sonar.db.DbSession;
@@ -69,7 +70,7 @@ public class MeasureDao implements Dao {
}
public List<PastMeasureDto> selectByComponentUuidAndProjectSnapshotIdAndMetricIds(final DbSession session, final String componentUuid, final long projectSnapshotId,
- Set<Integer> metricIds) {
+ Set<Integer> metricIds) {
return DatabaseUtils.executeLargeInputs(metricIds, new Function<List<Integer>, List<PastMeasureDto>>() {
@Override
public List<PastMeasureDto> apply(List<Integer> ids) {
@@ -82,7 +83,8 @@ public class MeasureDao implements Dao {
/**
* Used by plugin Developer Cockpit
*/
- public List<MeasureDto> selectByDeveloperForSnapshotAndMetrics(final DbSession dbSession, final long developerId, final long snapshotId, Collection<Integer> metricIds) {
+ public List<MeasureDto> selectByDeveloperForSnapshotAndMetrics(final DbSession dbSession, @Nullable final Long developerId, final long snapshotId,
+ Collection<Integer> metricIds) {
return DatabaseUtils.executeLargeInputs(metricIds, new Function<List<Integer>, List<MeasureDto>>() {
@Override
@Nonnull
@@ -109,11 +111,16 @@ public class MeasureDao implements Dao {
* Used by plugin Developer Cockpit
*/
public List<MeasureDto> selectBySnapshotIdsAndMetricIds(final DbSession dbSession, List<Long> snapshotIds, final List<Integer> metricIds) {
+ return selectByDeveloperAndSnapshotIdsAndMetricIds(dbSession, null, snapshotIds, metricIds);
+ }
+
+ public List<MeasureDto> selectByDeveloperAndSnapshotIdsAndMetricIds(final DbSession dbSession, @Nullable final Long developerId, List<Long> snapshotIds,
+ final List<Integer> metricIds) {
return DatabaseUtils.executeLargeInputs(snapshotIds, new Function<List<Long>, List<MeasureDto>>() {
@Override
@Nonnull
public List<MeasureDto> apply(@Nonnull List<Long> input) {
- return mapper(dbSession).selectBySnapshotIdsAndMetricIds(input, metricIds);
+ return mapper(dbSession).selectByDeveloperAndSnapshotIdsAndMetricIds(developerId, input, metricIds);
}
});
}
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 82bf90bd0f1..fb9d7254bcc 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
@@ -22,6 +22,7 @@ package org.sonar.db.measure;
import java.util.Collection;
import java.util.List;
import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
import org.apache.ibatis.annotations.Param;
public interface MeasureMapper {
@@ -37,7 +38,8 @@ public interface MeasureMapper {
List<MeasureDto> selectBySnapshotAndMetrics(@Param("snapshotId") long snapshotId, @Param("metricIds") List<Integer> input);
- List<MeasureDto> selectBySnapshotIdsAndMetricIds(@Param("snapshotIds") List<Long> snapshotIds, @Param("metricIds") List<Integer> metricIds);
+ List<MeasureDto> selectByDeveloperAndSnapshotIdsAndMetricIds(@Nullable @Param("developerId") Long developerId, @Param("snapshotIds") List<Long> snapshotIds,
+ @Param("metricIds") List<Integer> metricIds);
List<MeasureDto> selectProjectMeasuresByDeveloperForMetrics(@Param("developerId") long developerId, @Param("metricIds") Collection<Integer> metricIds);
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 4f9f8211536..7c97ed532e9 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
@@ -84,7 +84,7 @@
</where>
</select>
- <select id="selectBySnapshotIdsAndMetricIds" parameterType="map" resultType="Measure">
+ <select id="selectByDeveloperAndSnapshotIdsAndMetricIds" parameterType="map" resultType="Measure">
SELECT
<include refid="measureColumns"/>
FROM project_measures pm
@@ -98,7 +98,12 @@
<foreach item="metricId" collection="metricIds" open="(" separator="," close=")">
#{metricId}
</foreach>
- AND pm.person_id is NULL
+ <if test="developerId==null">
+ AND pm.person_id is NULL
+ </if>
+ <if test="developerId!=null">
+ AND pm.person_id=#{developerId}
+ </if>
</where>
</select>
diff --git a/sonar-db/src/test/java/org/sonar/db/measure/MeasureDaoTest.java b/sonar-db/src/test/java/org/sonar/db/measure/MeasureDaoTest.java
index d85f6cbbfce..2e7c592e568 100644
--- a/sonar-db/src/test/java/org/sonar/db/measure/MeasureDaoTest.java
+++ b/sonar-db/src/test/java/org/sonar/db/measure/MeasureDaoTest.java
@@ -35,14 +35,23 @@ import org.sonar.api.utils.System2;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
+import org.sonar.db.component.ComponentDbTester;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentTesting;
import org.sonar.db.component.SnapshotDto;
+import org.sonar.db.metric.MetricDto;
+import org.sonar.db.metric.MetricTesting;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Sets.newHashSet;
+import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.guava.api.Assertions.assertThat;
+import static org.sonar.db.component.ComponentTesting.newDevProjectCopy;
+import static org.sonar.db.component.ComponentTesting.newDeveloper;
+import static org.sonar.db.component.ComponentTesting.newFileDto;
+import static org.sonar.db.component.ComponentTesting.newProjectDto;
+import static org.sonar.db.measure.MeasureTesting.newMeasureDto;
public class MeasureDaoTest {
@@ -59,6 +68,7 @@ public class MeasureDaoTest {
final DbSession dbSession = db.getSession();
MeasureDao underTest = dbClient.measureDao();
+ ComponentDbTester componentDb = new ComponentDbTester(db);
@Test
public void get_value_by_key() {
@@ -326,7 +336,62 @@ public class MeasureDaoTest {
assertThat(measureDtos).isEmpty();
}
- // TODO add test for selectBySnapshotIdsAndMetricIds
+ @Test
+ public void selectSnapshotIdsAndMetricIds() {
+ MetricDto metric = dbClient.metricDao().insert(dbSession, MetricTesting.newMetricDto());
+ ComponentDto project = newProjectDto();
+ SnapshotDto projectSnapshot = componentDb.insertProjectAndSnapshot(project);
+ SnapshotDto fileSnapshot = componentDb.insertComponentAndSnapshot(newFileDto(project, "file-uuid"), projectSnapshot);
+ ComponentDto developer = newDeveloper("Ray Bradbury");
+ SnapshotDto developerSnapshot = componentDb.insertDeveloperAndSnapshot(developer);
+ componentDb.insertComponentAndSnapshot(newDevProjectCopy("project-copy-uuid", project, developer), developerSnapshot);
+ underTest.insert(dbSession,
+ newMeasureDto(metric, developerSnapshot.getId()).setDeveloperId(developer.getId()),
+ newMeasureDto(metric, projectSnapshot.getId()),
+ newMeasureDto(metric, fileSnapshot.getId()));
+ dbSession.commit();
+
+ List<MeasureDto> result = underTest.selectBySnapshotIdsAndMetricIds(dbSession,
+ newArrayList(developerSnapshot.getId(), projectSnapshot.getId(), fileSnapshot.getId()),
+ singletonList(metric.getId()));
+
+ assertThat(result)
+ .hasSize(2)
+ .extracting("snapshotId")
+ .containsOnly(projectSnapshot.getId(), fileSnapshot.getId())
+ .doesNotContain(developerSnapshot.getId());
+ }
+
+ @Test
+ public void selectDeveloperAndSnapshotIdsAndMetricIds() {
+ MetricDto metric = dbClient.metricDao().insert(dbSession, MetricTesting.newMetricDto());
+ ComponentDto project = newProjectDto();
+ SnapshotDto projectSnapshot = componentDb.insertProjectAndSnapshot(project);
+ SnapshotDto fileSnapshot = componentDb.insertComponentAndSnapshot(newFileDto(project, "file-uuid"), projectSnapshot);
+ ComponentDto developer = newDeveloper("Ray Bradbury");
+ SnapshotDto developerSnapshot = componentDb.insertDeveloperAndSnapshot(developer);
+ componentDb.insertComponentAndSnapshot(newDevProjectCopy("project-copy-uuid", project, developer), developerSnapshot);
+
+ underTest.insert(dbSession,
+ newMeasureDto(metric, developerSnapshot.getId()).setDeveloperId(developer.getId()),
+ newMeasureDto(metric, projectSnapshot.getId()).setDeveloperId(developer.getId()),
+ newMeasureDto(metric, projectSnapshot.getId()).setDeveloperId(null),
+ newMeasureDto(metric, fileSnapshot.getId()).setDeveloperId(developer.getId()));
+ dbSession.commit();
+
+ List<MeasureDto> result = underTest.selectByDeveloperAndSnapshotIdsAndMetricIds(dbSession,
+ developer.getId(),
+ newArrayList(developerSnapshot.getId(), projectSnapshot.getId(), fileSnapshot.getId()),
+ singletonList(metric.getId()));
+
+ assertThat(result)
+ .hasSize(3)
+ .extracting("snapshotId")
+ .containsOnly(developerSnapshot.getId(), projectSnapshot.getId(), fileSnapshot.getId());
+ assertThat(result)
+ .extracting("developerId")
+ .containsOnly(developer.getId());
+ }
@Test
public void selectProjectMeasuresByDeveloperForMetrics_returns_empty_on_empty_db() {
@@ -397,7 +462,7 @@ public class MeasureDaoTest {
}
private ComponentDto insertProject(String uuid) {
- ComponentDto projectDto = ComponentTesting.newProjectDto(uuid);
+ ComponentDto projectDto = newProjectDto(uuid);
return insertComponent(projectDto);
}
@@ -434,8 +499,7 @@ public class MeasureDaoTest {
.setVariation(5, 5.0d)
.setAlertStatus("alert")
.setAlertText("alert-text")
- .setDescription("measure-description")
- );
+ .setDescription("measure-description"));
dbSession.commit();
db.assertDbUnit(getClass(), "insert-result.xml", new String[] {"id"}, "project_measures");
@@ -454,8 +518,7 @@ public class MeasureDaoTest {
.setSnapshotId(3L)
.setMetricId(4)
.setComponentId(6L)
- .setValue(4.0d)
- );
+ .setValue(4.0d));
dbSession.commit();
assertThat(db.countRowsOfTable("project_measures")).isEqualTo(2);
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/measure/ComponentTreeWsRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/measure/ComponentTreeWsRequest.java
index 9194be603f6..86727a6c5c3 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/measure/ComponentTreeWsRequest.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/measure/ComponentTreeWsRequest.java
@@ -38,6 +38,8 @@ public class ComponentTreeWsRequest {
private List<String> metricKeys;
private Integer page;
private Integer pageSize;
+ private String developerId;
+ private String developerKey;
@CheckForNull
public String getBaseComponentId() {
@@ -168,4 +170,24 @@ public class ComponentTreeWsRequest {
this.metricPeriodSort = metricPeriodSort;
return this;
}
+
+ @CheckForNull
+ public String getDeveloperId() {
+ return developerId;
+ }
+
+ public ComponentTreeWsRequest setDeveloperId(@Nullable String developerId) {
+ this.developerId = developerId;
+ return this;
+ }
+
+ @CheckForNull
+ public String getDeveloperKey() {
+ return developerKey;
+ }
+
+ public ComponentTreeWsRequest 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 7fb0019e03f..829b170b9a9 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
@@ -33,6 +33,8 @@ import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_BASE_CO
import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_BASE_COMPONENT_KEY;
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;
import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_METRIC_SORT;
import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_QUALIFIERS;
@@ -51,6 +53,8 @@ public class MeasuresService extends BaseService {
.setParam(PARAM_QUALIFIERS, inlineMultipleParamValue(request.getQualifiers()))
.setParam(PARAM_METRIC_KEYS, inlineMultipleParamValue(request.getMetricKeys()))
.setParam(PARAM_ADDITIONAL_FIELDS, inlineMultipleParamValue(request.getAdditionalFields()))
+ .setParam(PARAM_DEVELOPER_ID, request.getDeveloperId())
+ .setParam(PARAM_DEVELOPER_KEY, request.getDeveloperKey())
.setParam("q", request.getQuery())
.setParam("p", request.getPage())
.setParam("ps", request.getPageSize())
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/measure/MeasuresWsParameters.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/measure/MeasuresWsParameters.java
index 4aca69b797e..b06b762c8e8 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/measure/MeasuresWsParameters.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/measure/MeasuresWsParameters.java
@@ -42,6 +42,8 @@ public class MeasuresWsParameters {
public static final String PARAM_ADDITIONAL_FIELDS = "additionalFields";
public static final String PARAM_COMPONENT_ID = "componentId";
public static final String PARAM_COMPONENT_KEY = "componentKey";
+ public static final String PARAM_DEVELOPER_ID = "developerId";
+ public static final String PARAM_DEVELOPER_KEY = "developerKey";
public static final String ADDITIONAL_METRICS = "metrics";
public static final String ADDITIONAL_PERIODS = "periods";
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 9e3bd05e6b9..869dd6fcd21 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
@@ -34,6 +34,8 @@ import static org.mockito.Mockito.mock;
import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_ADDITIONAL_FIELDS;
import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_BASE_COMPONENT_ID;
import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_BASE_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;
import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_METRIC_SORT;
import static org.sonarqube.ws.client.measure.MeasuresWsParameters.PARAM_QUALIFIERS;
@@ -52,6 +54,8 @@ public class MeasuresServiceTest {
private static final int VALUE_PAGE = 42;
private static final int VALUE_PAGE_SIZE = 1984;
private static final String VALUE_QUERY = "query-sq";
+ private static final String VALUE_DEVELOPER_ID = "developer-id";
+ private static final String VALUE_DEVELOPER_KEY = "developer-key";
@Rule
public ServiceTester<MeasuresService> serviceTester = new ServiceTester<>(new MeasuresService(mock(WsConnector.class)));
@@ -72,7 +76,9 @@ public class MeasuresServiceTest {
.setMetricSort(VALUE_METRIC_SORT)
.setPage(VALUE_PAGE)
.setPageSize(VALUE_PAGE_SIZE)
- .setQuery(VALUE_QUERY);
+ .setQuery(VALUE_QUERY)
+ .setDeveloperId(VALUE_DEVELOPER_ID)
+ .setDeveloperKey(VALUE_DEVELOPER_KEY);
underTest.componentTree(componentTreeRequest);
GetRequest getRequest = serviceTester.getGetRequest();
@@ -91,6 +97,8 @@ public class MeasuresServiceTest {
.hasParam("p", VALUE_PAGE)
.hasParam("ps", VALUE_PAGE_SIZE)
.hasParam("q", VALUE_QUERY)
+ .hasParam(PARAM_DEVELOPER_ID, VALUE_DEVELOPER_ID)
+ .hasParam(PARAM_DEVELOPER_KEY, VALUE_DEVELOPER_KEY)
.andNoOtherParam();
}
}