aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorTeryk Bellahsene <teryk.bellahsene@sonarsource.com>2016-04-06 18:36:39 +0200
committerTeryk Bellahsene <teryk.bellahsene@sonarsource.com>2016-04-07 13:54:46 +0200
commit616389be7c3eecd9a979aafec6d42c4445a1558a (patch)
tree837260d69fa50bda415c5f06bee1c46095747ab6 /server
parent3049d9176376e1907980a692c318c9d0299b8bd8 (diff)
downloadsonarqube-616389be7c3eecd9a979aafec6d42c4445a1558a.tar.gz
sonarqube-616389be7c3eecd9a979aafec6d42c4445a1558a.zip
SONAR-7528 WS api/measures/component_tree handles developer measures
Diffstat (limited to 'server')
-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
6 files changed, 158 insertions, 11 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"));