package org.sonar.server.measure.ws;
import java.util.Map;
+import java.util.function.Function;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.measure.MeasureDto;
import org.sonar.db.metric.MetricDto;
-import org.sonarqube.ws.WsMeasures;
+import org.sonarqube.ws.WsMeasures.Component;
+import org.sonarqube.ws.WsMeasures.SearchWsResponse;
import static org.sonar.server.measure.ws.MeasureDtoToWsMeasure.measureDtoToWsMeasure;
// static methods only
}
- static WsMeasures.Component.Builder componentDtoToWsComponent(ComponentDto component, Map<MetricDto, MeasureDto> measuresByMetric,
+ static Component.Builder componentDtoToWsComponent(ComponentDto component, Map<MetricDto, MeasureDto> measuresByMetric,
Map<String, ComponentDto> referenceComponentsByUuid) {
- WsMeasures.Component.Builder wsComponent = componentDtoToWsComponent(component);
+ Component.Builder wsComponent = componentDtoToWsComponent(component);
ComponentDto referenceComponent = referenceComponentsByUuid.get(component.getCopyResourceUuid());
if (referenceComponent != null) {
return wsComponent;
}
- static WsMeasures.Component dbToWsComponent(ComponentDto dbComponent, Iterable<WsMeasures.Measure> measures) {
- return componentDtoToWsComponent(dbComponent).addAllMeasures(measures).build();
+ static Function<ComponentDto, SearchWsResponse.Component> dbToWsComponent() {
+ return dbComponent -> SearchWsResponse.Component.newBuilder()
+ .setId(dbComponent.uuid())
+ .setKey(dbComponent.key())
+ .setName(dbComponent.name())
+ .build();
}
- static WsMeasures.Component.Builder componentDtoToWsComponent(ComponentDto component) {
- WsMeasures.Component.Builder wsComponent = WsMeasures.Component.newBuilder()
+ static Component.Builder componentDtoToWsComponent(ComponentDto component) {
+ Component.Builder wsComponent = Component.newBuilder()
.setId(component.uuid())
.setKey(component.key())
.setName(component.name())
import org.sonar.db.measure.MeasureDto;
import org.sonar.db.metric.MetricDto;
import org.sonarqube.ws.WsMeasures;
+import org.sonarqube.ws.WsMeasures.Measure;
import static org.sonar.server.measure.ws.MeasureValueFormatter.formatMeasureValue;
import static org.sonar.server.measure.ws.MeasureValueFormatter.formatNumericalValue;
// static methods
}
- static WsMeasures.Measure measureDtoToWsMeasure(MetricDto metricDto, MeasureDto measureDto) {
+ /**
+ * @see #measureDtoToWsMeasure(MetricDto, MeasureDto)
+ * add component uuid to the WS Measure object
+ *
+ */
+ static Measure dbToWsMeasure(MeasureDto dbMeasure, MetricDto dbMetric) {
+ return map(dbMetric, dbMeasure).setComponent(dbMeasure.getComponentUuid()).build();
+ }
+
+ static Measure measureDtoToWsMeasure(MetricDto metricDto, MeasureDto measureDto) {
+ return map(metricDto, measureDto).build();
+ }
+
+ private static Measure.Builder map(MetricDto dbMetric, MeasureDto dbMeasure) {
try {
- WsMeasures.Measure.Builder measure = WsMeasures.Measure.newBuilder();
- measure.setMetric(metricDto.getKey());
+ Measure.Builder measure = Measure.newBuilder();
+ measure.setMetric(dbMetric.getKey());
// a measure value can be null, new_violations metric for example
- if (measureDto.getValue() != null
- || measureDto.getData() != null) {
- measure.setValue(formatMeasureValue(measureDto, metricDto));
+ if (dbMeasure.getValue() != null
+ || dbMeasure.getData() != null) {
+ measure.setValue(formatMeasureValue(dbMeasure, dbMetric));
}
WsMeasures.PeriodValue.Builder periodBuilder = WsMeasures.PeriodValue.newBuilder();
for (int i = 1; i <= 5; i++) {
- if (measureDto.getVariation(i) != null) {
+ if (dbMeasure.getVariation(i) != null) {
measure.getPeriodsBuilder().addPeriodsValue(periodBuilder
.clear()
.setIndex(i)
- .setValue(formatNumericalValue(measureDto.getVariation(i), metricDto)));
+ .setValue(formatNumericalValue(dbMeasure.getVariation(i), dbMetric)));
}
}
- return measure.build();
+ return measure;
} catch (Exception e) {
- throw new IllegalStateException(String.format("Error while mapping a measure of metric key '%s' and parameters %s", metricDto.getKey(), measureDto.toString()), e);
+ throw new IllegalStateException(String.format("Error while mapping a measure of metric key '%s' and parameters %s", dbMetric.getKey(), dbMeasure.toString()), e);
}
}
}
import org.sonar.db.measure.MeasureQuery;
import org.sonar.db.metric.MetricDto;
import org.sonarqube.ws.WsMeasures;
+import org.sonarqube.ws.WsMeasures.Measure;
import org.sonarqube.ws.WsMeasures.SearchWsResponse;
+import org.sonarqube.ws.WsMeasures.SearchWsResponse.Component;
import org.sonarqube.ws.client.measure.SearchRequest;
import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.collect.Maps.immutableEntry;
+import static java.util.Comparator.comparing;
import static java.util.Objects.requireNonNull;
import static java.util.function.Function.identity;
import static org.sonar.core.util.Uuids.UUID_EXAMPLE_01;
import static org.sonar.core.util.Uuids.UUID_EXAMPLE_02;
import static org.sonar.core.util.Uuids.UUID_EXAMPLE_03;
import static org.sonar.server.measure.ws.ComponentDtoToWsComponent.dbToWsComponent;
-import static org.sonar.server.measure.ws.MeasureDtoToWsMeasure.measureDtoToWsMeasure;
+import static org.sonar.server.measure.ws.MeasureDtoToWsMeasure.dbToWsMeasure;
import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createMetricKeysParameter;
import static org.sonar.server.measure.ws.MetricDtoWithBestValue.buildBestMeasure;
import static org.sonar.server.measure.ws.MetricDtoWithBestValue.isEligibleForBestValue;
}
SearchWsResponse build() {
- this.request = setRequest();
+ this.request = createRequest();
this.components = searchComponents();
this.metrics = searchMetrics();
this.measures = searchMeasures();
return dbClient.snapshotDao().selectLastAnalysesByRootComponentUuids(dbSession, projectUuids);
}
- private SearchRequest setRequest() {
+ private SearchRequest createRequest() {
request = SearchRequest.builder()
.setMetricKeys(httpRequest.mandatoryParamAsStrings(PARAM_METRIC_KEYS))
.setComponentIds(httpRequest.paramAsStrings(PARAM_COMPONENT_IDS))
.setComponentKeys(httpRequest.paramAsStrings(PARAM_COMPONENT_KEYS))
.build();
- this.components = searchComponents();
- this.metrics = searchMetrics();
-
return request;
}
requireNonNull(components);
requireNonNull(metrics);
- Set<String> projectUuids = components.stream().map(ComponentDto::projectUuid).collect(Collectors.toSet());
-
- dbClient.snapshotDao().selectLastAnalysesByRootComponentUuids(dbSession, projectUuids).stream()
- .map(SnapshotDtoToWsPeriods::snapshotToWsPeriods);
-
return dbClient.measureDao().selectByQuery(dbSession, MeasureQuery.builder()
.setComponentUuids(components.stream().map(ComponentDto::uuid).collect(Collectors.toList()))
.setMetricIds(metrics.stream().map(MetricDto::getId).collect(Collectors.toList()))
requireNonNull(components);
requireNonNull(snapshots);
- Map<Integer, MetricDto> metricsById = metrics.stream().collect(Collectors.toMap(MetricDto::getId, identity()));
+ List<Measure> wsMeasures = buildWsMeasures();
+ List<Component> wsComponents = buildWsComponents();
- ImmutableMultimap<String, WsMeasures.Measure> wsMeasuresByComponentUuid = Stream
- .concat(measures.stream(), buildBestMeasures().stream())
- .collect(Collectors.toMap(identity(), MeasureDto::getComponentUuid))
- .entrySet().stream()
- .map(entry -> immutableEntry(
- measureDtoToWsMeasure(metricsById.get(entry.getKey().getMetricId()), entry.getKey()),
- entry.getValue()))
- .sorted((e1, e2) -> e1.getKey().getMetric().compareTo(e2.getKey().getMetric()))
- .collect(Collector.of(
- ImmutableMultimap::<String, WsMeasures.Measure>builder,
- (result, entry) -> result.put(entry.getValue(), entry.getKey()),
- (result1, result2) -> {
- throw new IllegalStateException("Parallel execution forbidden while building WS measures");
- },
- ImmutableMultimap.Builder::build));
+ return SearchWsResponse.newBuilder()
+ .addAllMeasures(wsMeasures)
+ .addAllComponents(wsComponents)
+ .build();
+ }
+ private List<Component> buildWsComponents() {
return components.stream()
- .map(dbComponent -> dbToWsComponent(dbComponent, wsMeasuresByComponentUuid.get(dbComponent.uuid())))
- .sorted((c1, c2) -> c1.getName().compareTo(c2.getName()))
- .collect(Collector.of(
- SearchWsResponse::newBuilder,
- SearchWsResponse.Builder::addComponents,
- (result1, result2) -> {
- throw new IllegalStateException("Parallel execution forbidden while building SearchWsResponse");
- },
- SearchWsResponse.Builder::build));
+ .map(dbToWsComponent())
+ .sorted(comparing(Component::getName))
+ .collect(Collectors.toList());
+ }
+
+ private List<Measure> buildWsMeasures() {
+ Map<String, String> componentNamesByUuid = components.stream().collect(Collectors.toMap(ComponentDto::uuid, ComponentDto::name));
+ Map<Integer, MetricDto> metricsById = metrics.stream().collect(Collectors.toMap(MetricDto::getId, identity()));
+
+ Function<MeasureDto, MetricDto> dbMeasureToDbMetric = dbMeasure -> metricsById.get(dbMeasure.getMetricId());
+ Function<Measure, String> byMetricKey = Measure::getMetric;
+ Function<Measure, String> byComponentName = wsMeasure -> componentNamesByUuid.get(wsMeasure.getComponent());
+
+ return Stream
+ .concat(measures.stream(), buildBestMeasures().stream())
+ .map(dbMeasure -> dbToWsMeasure(dbMeasure, dbMeasureToDbMetric.apply(dbMeasure)))
+ .sorted(comparing(byMetricKey).thenComparing(byComponentName))
+ .collect(Collectors.toList());
}
private List<MeasureDto> buildBestMeasures() {
{
- "components": [
+ "measures": [
{
- "id": "AVIwDXE-bJbJqrw6wFv5",
- "key": "com.sonarsource:java-markdown:src/main/java/com/sonarsource/markdown/impl/ElementImpl.java",
- "name": "ElementImpl.java",
- "qualifier": "FIL",
- "path": "src/main/java/com/sonarsource/markdown/impl/ElementImpl.java",
- "language": "java",
- "measures": [
+ "metric": "complexity",
+ "component": "AVIwDXE-bJbJqrw6wFv5",
+ "value": "12"
+ },
+ {
+ "metric": "complexity",
+ "component": "project-id",
+ "value": "42"
+ },
+ {
+ "metric": "complexity",
+ "component": "AVIwDXE-bJbJqrw6wFv8",
+ "value": "35",
+ "periods": [
+ {
+ "index": 2,
+ "value": "0"
+ }
+ ]
+ },
+ {
+ "metric": "ncloc",
+ "component": "AVIwDXE-bJbJqrw6wFv5",
+ "value": "114"
+ },
+ {
+ "metric": "ncloc",
+ "component": "project-id",
+ "value": "1984"
+ },
+ {
+ "metric": "ncloc",
+ "component": "AVIwDXE-bJbJqrw6wFv8",
+ "value": "217",
+ "periods": [
+ {
+ "index": 2,
+ "value": "0"
+ }
+ ]
+ },
+ {
+ "metric": "new_violations",
+ "component": "AVIwDXE-bJbJqrw6wFv5",
+ "periods": [
{
- "metric": "complexity",
- "value": "12"
+ "index": 1,
+ "value": "25"
},
{
- "metric": "ncloc",
- "value": "114"
+ "index": 2,
+ "value": "0"
},
{
- "metric": "new_violations",
- "periods": [
- {
- "index": 1,
- "value": "25"
- },
- {
- "index": 2,
- "value": "0"
- },
- {
- "index": 3,
- "value": "25"
- }
- ]
+ "index": 3,
+ "value": "25"
}
]
},
{
- "id": "AVIwDXE_bJbJqrw6wFwJ",
- "key": "com.sonarsource:java-markdown:src/test/java/com/sonarsource/markdown/impl/ElementImplTest.java",
- "name": "ElementImplTest.java",
- "qualifier": "UTS",
- "path": "src/test/java/com/sonarsource/markdown/impl/ElementImplTest.java",
- "language": "java",
- "measures": [
+ "metric": "new_violations",
+ "component": "AVIwDXE_bJbJqrw6wFwJ",
+ "periods": [
+ {
+ "index": 1,
+ "value": "0"
+ },
+ {
+ "index": 2,
+ "value": "0"
+ },
{
- "metric": "new_violations",
- "periods": [
- {
- "index": 1,
- "value": "0"
- },
- {
- "index": 2,
- "value": "0"
- },
- {
- "index": 3,
- "value": "0"
- }
- ]
+ "index": 3,
+ "value": "0"
}
]
},
{
- "id": "project-id",
- "key": "MY_PROJECT",
- "name": "My Project",
- "description": "My Project Description",
- "qualifier": "TRK",
- "measures": [
+ "metric": "new_violations",
+ "component": "project-id",
+ "periods": [
{
- "metric": "complexity",
- "value": "42"
+ "index": 1,
+ "value": "255"
},
{
- "metric": "ncloc",
- "value": "1984"
+ "index": 2,
+ "value": "0"
},
{
- "metric": "new_violations",
- "periods": [
- {
- "index": 1,
- "value": "255"
- },
- {
- "index": 2,
- "value": "0"
- },
- {
- "index": 3,
- "value": "255"
- }
- ]
+ "index": 3,
+ "value": "255"
}
]
},
{
- "id": "AVIwDXE-bJbJqrw6wFv8",
- "key": "com.sonarsource:java-markdown:src/main/java/com/sonarsource/markdown/impl",
- "name": "src/main/java/com/sonarsource/markdown/impl",
- "qualifier": "DIR",
- "path": "src/main/java/com/sonarsource/markdown/impl",
- "measures": [
+ "metric": "new_violations",
+ "component": "AVIwDXE-bJbJqrw6wFv8",
+ "periods": [
{
- "metric": "complexity",
- "value": "35",
- "periods": [
- {
- "index": 2,
- "value": "0"
- }
- ]
+ "index": 1,
+ "value": "25"
},
{
- "metric": "ncloc",
- "value": "217",
- "periods": [
- {
- "index": 2,
- "value": "0"
- }
- ]
+ "index": 2,
+ "value": "0"
},
{
- "metric": "new_violations",
- "periods": [
- {
- "index": 1,
- "value": "25"
- },
- {
- "index": 2,
- "value": "0"
- },
- {
- "index": 3,
- "value": "25"
- }
- ]
+ "index": 3,
+ "value": "25"
}
]
}
+ ],
+ "components": [
+ {
+ "id": "AVIwDXE-bJbJqrw6wFv5",
+ "key": "com.sonarsource:java-markdown:src/main/java/com/sonarsource/markdown/impl/ElementImpl.java",
+ "name": "ElementImpl.java"
+ },
+ {
+ "id": "AVIwDXE_bJbJqrw6wFwJ",
+ "key": "com.sonarsource:java-markdown:src/test/java/com/sonarsource/markdown/impl/ElementImplTest.java",
+ "name": "ElementImplTest.java"
+ },
+ {
+ "id": "project-id",
+ "key": "MY_PROJECT",
+ "name": "My Project"
+ },
+ {
+ "id": "AVIwDXE-bJbJqrw6wFv8",
+ "key": "com.sonarsource:java-markdown:src/main/java/com/sonarsource/markdown/impl",
+ "name": "src/main/java/com/sonarsource/markdown/impl"
+ }
]
}
import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.ws.TestRequest;
import org.sonar.server.ws.WsActionTester;
-import org.sonarqube.ws.WsMeasures;
-import org.sonarqube.ws.WsMeasures.Component;
+import org.sonarqube.ws.WsMeasures.Measure;
import org.sonarqube.ws.WsMeasures.SearchWsResponse;
import static com.google.common.collect.Lists.newArrayList;
.getInput();
assertJson(result).withStrictArrayOrder().isSimilarTo(ws.getDef().responseExampleAsString());
-
}
@Test
SearchWsResponse result = callByComponentUuids(singletonList(dbComponent.uuid()), singletonList("complexity"));
assertThat(result.getComponentsCount()).isEqualTo(1);
- Component wsComponent = result.getComponents(0);
- assertThat(wsComponent.getMeasuresCount()).isEqualTo(0);
+ SearchWsResponse.Component wsComponent = result.getComponents(0);
assertThat(wsComponent.getId()).isEqualTo(dbComponent.uuid());
assertThat(wsComponent.getKey()).isEqualTo(dbComponent.key());
- assertThat(wsComponent.getQualifier()).isEqualTo(dbComponent.qualifier());
assertThat(wsComponent.getName()).isEqualTo(dbComponent.name());
- assertThat(wsComponent.getDescription()).isEqualTo(dbComponent.description());
- assertThat(wsComponent.getProjectId()).isEqualTo("");
- assertThat(wsComponent.getLanguage()).isEqualTo("");
- assertThat(wsComponent.getPath()).isEqualTo("");
- assertThat(wsComponent.getRefId()).isEqualTo("");
- assertThat(wsComponent.getRefKey()).isEqualTo("");
+ }
+
+ @Test
+ public void search_by_component_uuid() {
+ ComponentDto project = componentDb.insertProject();
+ insertComplexityMetric();
+
+ SearchWsResponse result = callByComponentUuids(singletonList(project.uuid()), singletonList("complexity"));
+
+ assertThat(result.getComponentsCount()).isEqualTo(1);
+ assertThat(result.getComponents(0).getId()).isEqualTo(project.uuid());
}
@Test
SearchWsResponse result = callByComponentUuids(newArrayList(directoryDto.uuid(), file.uuid()), newArrayList("ncloc", "coverage", "new_violations"));
// directory is not eligible for best value
- assertThat(result.getComponentsList().get(0).getMeasuresList()).extracting("metric").containsOnly("coverage");
+ assertThat(result.getMeasuresList().stream()
+ .filter(measure -> directoryDto.uuid().equals(measure.getComponent()))
+ .map(Measure::getMetric))
+ .containsOnly("coverage");
// file measures
- List<WsMeasures.Measure> fileMeasures = result.getComponentsList().get(1).getMeasuresList();
- assertThat(fileMeasures).extracting("metric").containsOnly("ncloc", "coverage", "new_violations");
- assertThat(fileMeasures).extracting("value").containsOnly("100", "15.5", "");
+ List<Measure> fileMeasures = result.getMeasuresList().stream().filter(measure -> file.uuid().equals(measure.getComponent())).collect(Collectors.toList());
+ assertThat(fileMeasures).extracting(Measure::getMetric).containsOnly("ncloc", "coverage", "new_violations");
+ assertThat(fileMeasures).extracting(Measure::getValue).containsOnly("100", "15.5", "");
}
@Test
// WS api/measures/search
message SearchWsResponse {
- repeated Component components = 1;
+ repeated Measure measures = 1;
+ repeated Component components = 2;
+
+ message Component {
+ optional string id = 1;
+ optional string key = 2;
+ optional string name = 3;
+ }
}
message Component {
optional string id = 1;
optional string key = 2;
- optional string refId = 3;
- optional string refKey = 4;
- optional string projectId = 5;
- optional string name = 6;
+ optional string name = 3;
+ optional string refId = 4;
+ optional string refKey = 5;
+ optional string projectId = 6;
optional string description = 7;
optional string qualifier = 8;
optional string path = 9;
message Measure {
optional string metric = 1;
- optional string value = 2;
- optional PeriodsValue periods = 3;
+ optional string component = 2;
+ optional string value = 3;
+ optional PeriodsValue periods = 4;
}
message PeriodsValue {