public interface LiveMeasureMapper {
List<LiveMeasureDto> selectByComponentUuidsAndMetricIds(
- @Param("componentUuids") List<String> componentUuids,
+ @Param("componentUuids") Collection<String> componentUuids,
@Param("metricIds") Collection<Integer> metricIds);
List<LiveMeasureDto> selectByComponentUuidsAndMetricKeys(
- @Param("componentUuids") List<String> componentUuids,
+ @Param("componentUuids") Collection<String> componentUuids,
@Param("metricKeys") Collection<String> metricKeys);
LiveMeasureDto selectByComponentUuidAndMetricKey(
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableSortedSet;
-import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
+import java.util.Collection;
import java.util.HashMap;
-import java.util.LinkedHashSet;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
+import java.util.stream.Collectors;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.sonar.api.resources.Qualifiers;
import org.sonar.core.util.stream.MoreCollectors;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
+import org.sonar.db.component.BranchType;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.SnapshotDto;
import org.sonar.db.measure.LiveMeasureDto;
public void define(WebService.NewController context) {
WebService.NewAction action = context.createAction(ACTION_COMPONENT)
.setDescription(format("Return component with specified measures. The %s or the %s parameter must be provided.<br>" +
- "Requires the following permission: 'Browse' on the project of specified component.",
+ "Requires the following permission: 'Browse' on the project of specified component.",
DEPRECATED_PARAM_COMPONENT_ID, PARAM_COMPONENT))
.setResponseExample(getClass().getResource("component-example.json"))
.setSince("5.4")
private ComponentWsResponse doHandle(ComponentRequest request) {
try (DbSession dbSession = dbClient.openSession(false)) {
- ComponentDto component = loadComponent(dbSession, request);
- Optional<ComponentDto> refComponent = getReferenceComponent(dbSession, component);
+ String branch = request.getBranch();
+ String pullRequest = request.getPullRequest();
+ ComponentDto component = loadComponent(dbSession, request, branch, pullRequest);
checkPermissions(component);
SnapshotDto analysis = dbClient.snapshotDao().selectLastAnalysisByRootComponentUuid(dbSession, component.projectUuid()).orElse(null);
- List<MetricDto> metrics = searchMetrics(dbSession, request);
- Optional<Measures.Period> period = snapshotToWsPeriods(analysis);
+
+ boolean isSLBorPR = isSLBorPR(dbSession, component, branch, pullRequest);
+
+ Set<String> metricKeysToRequest = new HashSet<>(request.metricKeys);
+
+ if (isSLBorPR) {
+ SLBorPRMeasureFix.addReplacementMetricKeys(metricKeysToRequest);
+ }
+
+ List<MetricDto> metrics = searchMetrics(dbSession, metricKeysToRequest);
List<LiveMeasureDto> measures = searchMeasures(dbSession, component, metrics);
+ Map<MetricDto, LiveMeasureDto> measuresByMetric = getMeasuresByMetric(measures, metrics);
+
+ if (isSLBorPR) {
+ Set<String> originalMetricKeys = new HashSet<>(request.metricKeys);
+ SLBorPRMeasureFix.createReplacementMeasures(metrics, measuresByMetric, originalMetricKeys);
+ SLBorPRMeasureFix.removeMetricsNotRequested(metrics, originalMetricKeys);
+ }
+
+ Optional<Measures.Period> period = snapshotToWsPeriods(analysis);
+ Optional<ComponentDto> refComponent = getReferenceComponent(dbSession, component);
+ return buildResponse(request, component, refComponent, measuresByMetric, metrics, period);
+ }
+ }
+
+ public List<MetricDto> searchMetrics(DbSession dbSession, Collection<String> metricKeys) {
+ List<MetricDto> metrics = dbClient.metricDao().selectByKeys(dbSession, metricKeys);
+ if (metrics.size() < metricKeys.size()) {
+ Set<String> foundMetricKeys = metrics.stream().map(MetricDto::getKey).collect(Collectors.toSet());
+ Set<String> missingMetricKeys = metricKeys.stream().filter(m -> !foundMetricKeys.contains(m)).collect(Collectors.toSet());
+ throw new NotFoundException(format("The following metric keys are not found: %s", Joiner.on(", ").join(missingMetricKeys)));
+ }
+
+ return metrics;
+ }
+
+ private List<LiveMeasureDto> searchMeasures(DbSession dbSession, ComponentDto component, Collection<MetricDto> metrics) {
+ Set<Integer> metricIds = metrics.stream().map(MetricDto::getId).collect(Collectors.toSet());
+ List<LiveMeasureDto> measures = dbClient.liveMeasureDao().selectByComponentUuidsAndMetricIds(dbSession, singletonList(component.uuid()), metricIds);
+ addBestValuesToMeasures(measures, component, metrics);
+ return measures;
+ }
+
+ private static Map<MetricDto, LiveMeasureDto> getMeasuresByMetric(List<LiveMeasureDto> measures, Collection<MetricDto> metrics) {
+ Map<Integer, MetricDto> metricsById = Maps.uniqueIndex(metrics, MetricDto::getId);
+ Map<MetricDto, LiveMeasureDto> measuresByMetric = new HashMap<>();
+ for (LiveMeasureDto measure : measures) {
+ MetricDto metric = metricsById.get(measure.getMetricId());
+ measuresByMetric.put(metric, measure);
+ }
+ return measuresByMetric;
+ }
+
+ /**
+ * Conditions for best value measure:
+ * <ul>
+ * <li>component is a production file or test file</li>
+ * <li>metric is optimized for best value</li>
+ * </ul>
+ */
+ private static void addBestValuesToMeasures(List<LiveMeasureDto> measures, ComponentDto component, Collection<MetricDto> metrics) {
+ if (!QUALIFIERS_ELIGIBLE_FOR_BEST_VALUE.contains(component.qualifier())) {
+ return;
+ }
+
+ List<MetricDtoWithBestValue> metricWithBestValueList = metrics.stream()
+ .filter(MetricDtoFunctions.isOptimizedForBestValue())
+ .map(MetricDtoWithBestValue::new)
+ .collect(MoreCollectors.toList(metrics.size()));
+ Map<Integer, LiveMeasureDto> measuresByMetricId = Maps.uniqueIndex(measures, LiveMeasureDto::getMetricId);
+
+ for (MetricDtoWithBestValue metricWithBestValue : metricWithBestValueList) {
+ if (measuresByMetricId.get(metricWithBestValue.getMetric().getId()) == null) {
+ measures.add(metricWithBestValue.getBestValue());
+ }
+ }
+ }
- return buildResponse(request, component, refComponent, measures, metrics, period);
+ private boolean isSLBorPR(DbSession dbSession, ComponentDto component, @Nullable String branch, @Nullable String pullRequest) {
+ if (branch != null) {
+ return dbClient.branchDao().selectByUuid(dbSession, component.projectUuid())
+ .map(b -> b.getBranchType() == BranchType.SHORT).orElse(false);
}
+ return pullRequest != null;
}
- private ComponentDto loadComponent(DbSession dbSession, ComponentRequest request) {
+ private ComponentDto loadComponent(DbSession dbSession, ComponentRequest request, @Nullable String branch, @Nullable String pullRequest) {
String componentKey = request.getComponent();
String componentId = request.getComponentId();
- String branch = request.getBranch();
- String pullRequest = request.getPullRequest();
checkArgument(componentId == null || (branch == null && pullRequest == null), "Parameter '%s' cannot be used at the same time as '%s' or '%s'",
DEPRECATED_PARAM_COMPONENT_ID, PARAM_BRANCH, PARAM_PULL_REQUEST);
+
if (branch == null && pullRequest == null) {
return componentFinder.getByUuidOrKey(dbSession, componentId, componentKey, COMPONENT_ID_AND_KEY);
}
+
checkRequest(componentKey != null, "The '%s' parameter is missing", PARAM_COMPONENT);
return componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, componentKey, branch, pullRequest);
}
}
private static ComponentWsResponse buildResponse(ComponentRequest request, ComponentDto component, Optional<ComponentDto> refComponent,
- List<LiveMeasureDto> measures, List<MetricDto> metrics, Optional<Measures.Period> period) {
+ Map<MetricDto, LiveMeasureDto> measuresByMetric, Collection<MetricDto> metrics, Optional<Measures.Period> period) {
ComponentWsResponse.Builder response = ComponentWsResponse.newBuilder();
- Map<Integer, MetricDto> metricsById = Maps.uniqueIndex(metrics, MetricDto::getId);
- Map<MetricDto, LiveMeasureDto> measuresByMetric = new HashMap<>();
- for (LiveMeasureDto measure : measures) {
- MetricDto metric = metricsById.get(measure.getMetricId());
- measuresByMetric.put(metric, measure);
- }
+
if (refComponent.isPresent()) {
response.setComponent(componentDtoToWsComponent(component, measuresByMetric, singletonMap(refComponent.get().uuid(), refComponent.get())));
} else {
return response.build();
}
- private List<MetricDto> searchMetrics(DbSession dbSession, ComponentRequest request) {
- List<MetricDto> metrics = dbClient.metricDao().selectByKeys(dbSession, request.getMetricKeys());
- if (metrics.size() < request.getMetricKeys().size()) {
- List<String> foundMetricKeys = Lists.transform(metrics, MetricDto::getKey);
- Set<String> missingMetricKeys = Sets.difference(
- new LinkedHashSet<>(request.getMetricKeys()),
- new LinkedHashSet<>(foundMetricKeys));
-
- throw new NotFoundException(format("The following metric keys are not found: %s", Joiner.on(", ").join(missingMetricKeys)));
- }
-
- return metrics;
- }
-
- private List<LiveMeasureDto> searchMeasures(DbSession dbSession, ComponentDto component, List<MetricDto> metrics) {
- List<Integer> metricIds = Lists.transform(metrics, MetricDto::getId);
- List<LiveMeasureDto> measures = dbClient.liveMeasureDao().selectByComponentUuidsAndMetricIds(dbSession, singletonList(component.uuid()), metricIds);
- addBestValuesToMeasures(measures, component, metrics);
- return measures;
- }
-
- /**
- * Conditions for best value measure:
- * <ul>
- * <li>component is a production file or test file</li>
- * <li>metric is optimized for best value</li>
- * </ul>
- */
- private static void addBestValuesToMeasures(List<LiveMeasureDto> measures, ComponentDto component, List<MetricDto> metrics) {
- if (!QUALIFIERS_ELIGIBLE_FOR_BEST_VALUE.contains(component.qualifier())) {
- return;
- }
-
- List<MetricDtoWithBestValue> metricWithBestValueList = metrics.stream()
- .filter(MetricDtoFunctions.isOptimizedForBestValue())
- .map(MetricDtoWithBestValue::new)
- .collect(MoreCollectors.toList(metrics.size()));
- Map<Integer, LiveMeasureDto> measuresByMetricId = Maps.uniqueIndex(measures, LiveMeasureDto::getMetricId);
-
- for (MetricDtoWithBestValue metricWithBestValue : metricWithBestValueList) {
- if (measuresByMetricId.get(metricWithBestValue.getMetric().getId()) == null) {
- measures.add(metricWithBestValue.getBestValue());
- }
- }
- }
-
private static ComponentRequest toComponentWsRequest(Request request) {
ComponentRequest componentRequest = new ComponentRequest()
.setComponentId(request.param(DEPRECATED_PARAM_COMPONENT_ID))
import org.sonar.core.util.stream.MoreCollectors;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
+import org.sonar.db.component.BranchType;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ComponentTreeQuery;
import org.sonar.db.component.ComponentTreeQuery.Strategy;
import static java.lang.String.format;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap;
-import static java.util.Objects.requireNonNull;
import static org.sonar.api.measures.Metric.ValueType.DATA;
import static org.sonar.api.measures.Metric.ValueType.DISTRIB;
import static org.sonar.api.utils.Paging.offset;
.build();
}
+ Set<String> requestedMetricKeys = new HashSet<>(wsRequest.getMetricKeys());
+ Set<String> metricKeysToSearch = new HashSet<>(requestedMetricKeys);
+
+ boolean isSLBorPR = isSLBorPR(dbSession, baseComponent, wsRequest.getBranch(), wsRequest.getPullRequest());
+ if (isSLBorPR) {
+ SLBorPRMeasureFix.addReplacementMetricKeys(metricKeysToSearch);
+ }
+
ComponentTreeQuery componentTreeQuery = toComponentTreeQuery(wsRequest, baseComponent);
List<ComponentDto> components = searchComponents(dbSession, componentTreeQuery);
- List<MetricDto> metrics = searchMetrics(dbSession, wsRequest);
+
+ List<MetricDto> metrics = searchMetrics(dbSession, metricKeysToSearch);
Table<String, MetricDto, ComponentTreeData.Measure> measuresByComponentUuidAndMetric = searchMeasuresByComponentUuidAndMetric(dbSession, baseComponent, componentTreeQuery,
components,
metrics);
+ if (isSLBorPR) {
+ SLBorPRMeasureFix.removeMetricsNotRequested(metrics, requestedMetricKeys);
+ SLBorPRMeasureFix.createReplacementMeasures(metrics, measuresByComponentUuidAndMetric, requestedMetricKeys);
+ }
+
components = filterComponents(components, measuresByComponentUuidAndMetric, metrics, wsRequest);
components = sortComponents(components, wsRequest, metrics, measuresByComponentUuidAndMetric);
}
}
+ private boolean isSLBorPR(DbSession dbSession, ComponentDto component, @Nullable String branch, @Nullable String pullRequest) {
+ if (branch != null) {
+ return dbClient.branchDao().selectByUuid(dbSession, component.projectUuid())
+ .map(b -> b.getBranchType() == BranchType.SHORT).orElse(false);
+ }
+ return pullRequest != null;
+ }
+
private ComponentDto loadComponent(DbSession dbSession, ComponentTreeRequest request) {
String componentId = request.getBaseComponentId();
String componentKey = request.getComponent();
return dbClient.componentDao().selectDescendants(dbSession, componentTreeQuery);
}
- private List<MetricDto> searchMetrics(DbSession dbSession, ComponentTreeRequest request) {
- List<String> metricKeys = requireNonNull(request.getMetricKeys());
+ private List<MetricDto> searchMetrics(DbSession dbSession, Set<String> metricKeys) {
List<MetricDto> metrics = dbClient.metricDao().selectByKeys(dbSession, metricKeys);
if (metrics.size() < metricKeys.size()) {
List<String> foundMetricKeys = Lists.transform(metrics, MetricDto::getKey);
private String data;
private double variation;
+ public Measure(@Nullable String data, @Nullable Double value, @Nullable Double variation) {
+ this.data = data;
+ this.value = toPrimitive(value);
+ this.variation = toPrimitive(variation);
+ }
+
private Measure(LiveMeasureDto measureDto) {
- this.value = toPrimitive(measureDto.getValue());
- this.data = measureDto.getDataAsString();
- this.variation = toPrimitive(measureDto.getVariation());
+ this(measureDto.getDataAsString(), measureDto.getValue(), measureDto.getVariation());
}
public double getValue() {
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.measure.ws;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Table;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.sonar.db.measure.LiveMeasureDto;
+import org.sonar.db.metric.MetricDto;
+
+import static org.sonar.api.measures.CoreMetrics.BLOCKER_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.BUGS_KEY;
+import static org.sonar.api.measures.CoreMetrics.CODE_SMELLS_KEY;
+import static org.sonar.api.measures.CoreMetrics.CRITICAL_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.INFO_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.MAJOR_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.MINOR_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_BLOCKER_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_BUGS_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_CODE_SMELLS_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_CRITICAL_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_INFO_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_MAJOR_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_MINOR_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_RELIABILITY_RATING_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_RELIABILITY_REMEDIATION_EFFORT_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_RATING_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_SECURITY_REMEDIATION_EFFORT_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_TECHNICAL_DEBT_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_VULNERABILITIES_KEY;
+import static org.sonar.api.measures.CoreMetrics.RELIABILITY_RATING_KEY;
+import static org.sonar.api.measures.CoreMetrics.RELIABILITY_REMEDIATION_EFFORT_KEY;
+import static org.sonar.api.measures.CoreMetrics.SECURITY_RATING_KEY;
+import static org.sonar.api.measures.CoreMetrics.SECURITY_REMEDIATION_EFFORT_KEY;
+import static org.sonar.api.measures.CoreMetrics.TECHNICAL_DEBT_KEY;
+import static org.sonar.api.measures.CoreMetrics.VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.VULNERABILITIES_KEY;
+
+/**
+ * See SONAR-11736
+ * This class should be removed in 8.0.
+ */
+class SLBorPRMeasureFix {
+ static final BiMap<String, String> METRICS;
+
+ static {
+ METRICS = HashBiMap.create();
+
+ METRICS.put(NEW_VIOLATIONS_KEY, VIOLATIONS_KEY);
+
+ // issue severities
+ METRICS.put(NEW_BLOCKER_VIOLATIONS_KEY, BLOCKER_VIOLATIONS_KEY);
+ METRICS.put(NEW_CRITICAL_VIOLATIONS_KEY, CRITICAL_VIOLATIONS_KEY);
+ METRICS.put(NEW_MAJOR_VIOLATIONS_KEY, MAJOR_VIOLATIONS_KEY);
+ METRICS.put(NEW_MINOR_VIOLATIONS_KEY, MINOR_VIOLATIONS_KEY);
+ METRICS.put(NEW_INFO_VIOLATIONS_KEY, INFO_VIOLATIONS_KEY);
+
+ // issue types
+ METRICS.put(NEW_BUGS_KEY, BUGS_KEY);
+ METRICS.put(NEW_CODE_SMELLS_KEY, CODE_SMELLS_KEY);
+ METRICS.put(NEW_VULNERABILITIES_KEY, VULNERABILITIES_KEY);
+
+ // ratings
+ METRICS.put(NEW_SECURITY_RATING_KEY, SECURITY_RATING_KEY);
+ METRICS.put(NEW_RELIABILITY_RATING_KEY, RELIABILITY_RATING_KEY);
+
+ // effort
+ METRICS.put(NEW_TECHNICAL_DEBT_KEY, TECHNICAL_DEBT_KEY);
+ METRICS.put(NEW_SECURITY_REMEDIATION_EFFORT_KEY, SECURITY_REMEDIATION_EFFORT_KEY);
+ METRICS.put(NEW_RELIABILITY_REMEDIATION_EFFORT_KEY, RELIABILITY_REMEDIATION_EFFORT_KEY);
+ }
+
+ private SLBorPRMeasureFix() {
+ // static only
+ }
+
+ static void addReplacementMetricKeys(Collection<String> metricKeys) {
+ Set<String> keysToAdd = metricKeys.stream()
+ .filter(METRICS::containsKey)
+ .map(METRICS::get)
+ .collect(Collectors.toSet());
+ metricKeys.addAll(keysToAdd);
+ }
+
+ static void removeMetricsNotRequested(List<MetricDto> metrics, Set<String> requestedMetricKeys) {
+ metrics.removeIf(m -> !requestedMetricKeys.contains(m.getKey()));
+ }
+
+ static void createReplacementMeasures(List<MetricDto> metrics, Table<String, MetricDto, ComponentTreeData.Measure> measuresByComponentUuidAndMetric,
+ Set<String> requestedMetricKeys) {
+ Map<String, MetricDto> metricByKey = Maps.uniqueIndex(metrics, MetricDto::getKey);
+
+ for (MetricDto metric : measuresByComponentUuidAndMetric.columnKeySet()) {
+ Map<String, ComponentTreeData.Measure> newEntries = new HashMap<>();
+
+ String originalKey = METRICS.inverse().get(metric.getKey());
+ if (originalKey != null && requestedMetricKeys.contains(originalKey)) {
+ for (Map.Entry<String, ComponentTreeData.Measure> e : measuresByComponentUuidAndMetric.column(metric).entrySet()) {
+ newEntries.put(e.getKey(), copyMeasureToVariation(e.getValue()));
+ }
+
+ MetricDto originalMetric = metricByKey.get(originalKey);
+ newEntries.forEach((k, v) -> measuresByComponentUuidAndMetric.put(k, originalMetric, v));
+ }
+ }
+
+ List<MetricDto> toRemove = measuresByComponentUuidAndMetric.columnKeySet().stream().filter(m -> !requestedMetricKeys.contains(m.getKey())).collect(Collectors.toList());
+ measuresByComponentUuidAndMetric.columnKeySet().removeAll(toRemove);
+ }
+
+ static void createReplacementMeasures(List<MetricDto> metrics, Map<MetricDto, LiveMeasureDto> measuresByMetric, Set<String> requestedMetricKeys) {
+ Map<String, MetricDto> metricByKey = Maps.uniqueIndex(metrics, MetricDto::getKey);
+ Map<MetricDto, LiveMeasureDto> newEntries = new HashMap<>();
+
+ for (Map.Entry<MetricDto, LiveMeasureDto> e : measuresByMetric.entrySet()) {
+ String originalKey = METRICS.inverse().get(e.getKey().getKey());
+
+ if (originalKey != null && requestedMetricKeys.contains(originalKey)) {
+ MetricDto metricDto = metricByKey.get(originalKey);
+ newEntries.put(metricDto, copyMeasureToVariation(e.getValue(), metricDto.getId()));
+ }
+ }
+
+ measuresByMetric.entrySet().removeIf(e -> !requestedMetricKeys.contains(e.getKey().getKey()));
+ measuresByMetric.putAll(newEntries);
+ }
+
+ private static ComponentTreeData.Measure copyMeasureToVariation(ComponentTreeData.Measure measure) {
+ return new ComponentTreeData.Measure(null, null, measure.getValue());
+ }
+
+ private static LiveMeasureDto copyMeasureToVariation(LiveMeasureDto dto, Integer metricId) {
+ LiveMeasureDto copy = new LiveMeasureDto();
+ copy.setVariation(dto.getValue());
+ copy.setProjectUuid(dto.getProjectUuid());
+ copy.setComponentUuid(dto.getComponentUuid());
+ copy.setMetricId(metricId);
+ return copy;
+ }
+}
*/
package org.sonar.server.measure.ws;
+import java.util.function.Function;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
.containsExactlyInAnyOrder(tuple(complexity.getKey(), measure.getValue()));
}
+ @Test
+ public void new_issue_count_measures_are_transformed_in_pr() {
+ ComponentDto project = db.components().insertPrivateProject();
+ userSession.addProjectPermission(UserRole.USER, project);
+ ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("pr-123").setBranchType(PULL_REQUEST));
+ SnapshotDto analysis = db.components().insertSnapshot(branch);
+ ComponentDto file = db.components().insertComponent(newFileDto(branch));
+ MetricDto bugs = db.measures().insertMetric(m1 -> m1.setKey("bugs").setValueType("INT"));
+ MetricDto newBugs = db.measures().insertMetric(m1 -> m1.setKey("new_bugs").setValueType("INT"));
+ MetricDto violations = db.measures().insertMetric(m1 -> m1.setKey("violations").setValueType("INT"));
+ MetricDto newViolations = db.measures().insertMetric(m1 -> m1.setKey("new_violations").setValueType("INT"));
+ LiveMeasureDto bugMeasure = db.measures().insertLiveMeasure(file, bugs, m -> m.setValue(12.0d).setVariation(null));
+ LiveMeasureDto newBugMeasure = db.measures().insertLiveMeasure(file, newBugs, m -> m.setVariation(1d).setValue(null));
+ LiveMeasureDto violationMeasure = db.measures().insertLiveMeasure(file, violations, m -> m.setValue(20.0d).setVariation(null));
+
+ ComponentWsResponse response = ws.newRequest()
+ .setParam(PARAM_COMPONENT, file.getKey())
+ .setParam(PARAM_PULL_REQUEST, "pr-123")
+ .setParam(PARAM_METRIC_KEYS, newBugs.getKey() + "," + bugs.getKey() + "," + newViolations.getKey())
+ .executeProtobuf(ComponentWsResponse.class);
+
+ assertThat(response.getComponent()).extracting(Component::getKey, Component::getPullRequest)
+ .containsExactlyInAnyOrder(file.getKey(), "pr-123");
+
+ Function<Measures.Measure, Double> extractVariation = m -> {
+ if (m.getPeriods().getPeriodsValueCount() > 0) {
+ return parseDouble(m.getPeriods().getPeriodsValue(0).getValue());
+ }
+ return null;
+ };
+ assertThat(response.getComponent().getMeasuresList())
+ .extracting(Measures.Measure::getMetric, extractVariation, m -> m.getValue().isEmpty() ? null : parseDouble(m.getValue()))
+ .containsExactlyInAnyOrder(
+ tuple(newBugs.getKey(), bugMeasure.getValue(), null),
+ tuple(bugs.getKey(), null, bugMeasure.getValue()),
+ tuple(newViolations.getKey(), violationMeasure.getValue(), null));
+ }
+
+ @Test
+ public void new_issue_count_measures_are_not_transformed_if_they_dont_exist_in_pr() {
+ ComponentDto project = db.components().insertPrivateProject();
+ userSession.addProjectPermission(UserRole.USER, project);
+ ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("pr-123").setBranchType(PULL_REQUEST));
+ SnapshotDto analysis = db.components().insertSnapshot(branch);
+ ComponentDto file = db.components().insertComponent(newFileDto(branch));
+ MetricDto bugs = db.measures().insertMetric(m1 -> m1.setKey("bugs").setOptimizedBestValue(false).setValueType("INT"));
+ MetricDto newBugs = db.measures().insertMetric(m1 -> m1.setKey("new_bugs").setOptimizedBestValue(false).setValueType("INT"));
+
+ ComponentWsResponse response = ws.newRequest()
+ .setParam(PARAM_COMPONENT, file.getKey())
+ .setParam(PARAM_PULL_REQUEST, "pr-123")
+ .setParam(PARAM_METRIC_KEYS, newBugs.getKey() + "," + bugs.getKey())
+ .executeProtobuf(ComponentWsResponse.class);
+
+ assertThat(response.getComponent()).extracting(Component::getKey, Component::getPullRequest)
+ .containsExactlyInAnyOrder(file.getKey(), "pr-123");
+
+ assertThat(response.getComponent().getMeasuresList()).isEmpty();
+ }
+
@Test
public void reference_uuid_in_the_response() {
userSession.logIn().setRoot();
import com.google.common.base.Joiner;
import java.util.List;
+import java.util.function.Function;
import java.util.stream.IntStream;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
+import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.measures.Metric;
import org.sonar.api.server.ws.WebService.Param;
import org.sonar.api.utils.System2;
import static org.sonar.api.utils.DateUtils.parseDateTime;
import static org.sonar.api.web.UserRole.USER;
import static org.sonar.db.component.BranchType.PULL_REQUEST;
+import static org.sonar.db.component.BranchType.SHORT;
import static org.sonar.db.component.ComponentTesting.newDirectory;
import static org.sonar.db.component.ComponentTesting.newFileDto;
import static org.sonar.db.component.ComponentTesting.newProjectCopy;
.containsExactlyInAnyOrder(tuple(complexity.getKey(), measure.getValue()));
}
+ @Test
+ public void fix_pull_request_new_issue_count_metrics() {
+ OrganizationDto organization = db.organizations().insert();
+ ComponentDto project = db.components().insertPrivateProject(organization);
+ ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("pr-123").setBranchType(PULL_REQUEST));
+ SnapshotDto analysis = db.components().insertSnapshot(branch);
+ ComponentDto file = db.components().insertComponent(newFileDto(branch));
+ MetricDto bug = db.measures().insertMetric(m -> m.setValueType(INT.name()).setKey(CoreMetrics.BUGS_KEY));
+ MetricDto newBug = db.measures().insertMetric(m -> m.setValueType(INT.name()).setKey(CoreMetrics.NEW_BUGS_KEY));
+
+ LiveMeasureDto measure = db.measures().insertLiveMeasure(file, bug, m -> m.setValue(12.0d));
+
+ ComponentTreeWsResponse response = ws.newRequest()
+ .setParam(PARAM_COMPONENT, file.getKey())
+ .setParam(PARAM_PULL_REQUEST, "pr-123")
+ .setParam(PARAM_METRIC_KEYS, newBug.getKey())
+ .executeProtobuf(ComponentTreeWsResponse.class);
+
+ assertThat(response.getBaseComponent()).extracting(Component::getKey, Component::getPullRequest)
+ .containsExactlyInAnyOrder(file.getKey(), "pr-123");
+ assertThat(response.getBaseComponent().getMeasuresList())
+ .extracting(Measure::getMetric, m -> parseDouble(m.getPeriods().getPeriodsValue(0).getValue()), Measure::getValue)
+ .containsExactlyInAnyOrder(tuple(newBug.getKey(), measure.getValue(), ""));
+ }
+
+ @Test
+ public void new_issue_count_measures_are_transformed_in_slb() {
+ OrganizationDto organization = db.organizations().insert();
+ ComponentDto project = db.components().insertPrivateProject(organization);
+ ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("slb").setBranchType(SHORT));
+ SnapshotDto analysis = db.components().insertSnapshot(branch);
+ ComponentDto file = db.components().insertComponent(newFileDto(branch));
+ MetricDto bug = db.measures().insertMetric(m -> m.setValueType(INT.name()).setKey(CoreMetrics.BUGS_KEY));
+ MetricDto newBug = db.measures().insertMetric(m -> m.setValueType(INT.name()).setKey(CoreMetrics.NEW_BUGS_KEY));
+
+ LiveMeasureDto measure = db.measures().insertLiveMeasure(file, bug, m -> m.setValue(12.0d).setVariation(null));
+
+ ComponentTreeWsResponse response = ws.newRequest()
+ .setParam(PARAM_COMPONENT, file.getKey())
+ .setParam(PARAM_BRANCH, "slb")
+ .setParam(PARAM_METRIC_KEYS, newBug.getKey() + "," + bug.getKey())
+ .executeProtobuf(ComponentTreeWsResponse.class);
+
+ Function<Measure, Double> extractVariation = m -> {
+ if (m.getPeriods().getPeriodsValueCount() > 0) {
+ return parseDouble(m.getPeriods().getPeriodsValue(0).getValue());
+ }
+ return null;
+ };
+
+ assertThat(response.getBaseComponent()).extracting(Component::getKey, Component::getBranch)
+ .containsExactlyInAnyOrder(file.getKey(), "slb");
+ assertThat(response.getBaseComponent().getMeasuresList())
+ .extracting(Measure::getMetric, extractVariation, m -> m.getValue().isEmpty() ? null : parseDouble(m.getValue()))
+ .containsExactlyInAnyOrder(
+ tuple(newBug.getKey(), measure.getValue(), null),
+ tuple(bug.getKey(), null, measure.getValue()));
+ }
+
+ @Test
+ public void new_issue_count_measures_are_not_transformed_if_they_dont_exist_in_slb() {
+ OrganizationDto organization = db.organizations().insert();
+ ComponentDto project = db.components().insertPrivateProject(organization);
+ ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("slb").setBranchType(SHORT));
+ SnapshotDto analysis = db.components().insertSnapshot(branch);
+ ComponentDto file = db.components().insertComponent(newFileDto(branch));
+ MetricDto bug = db.measures().insertMetric(m -> m.setValueType(INT.name()).setKey(CoreMetrics.BUGS_KEY));
+ MetricDto newBug = db.measures().insertMetric(m -> m.setValueType(INT.name()).setKey(CoreMetrics.NEW_BUGS_KEY));
+
+ ComponentTreeWsResponse response = ws.newRequest()
+ .setParam(PARAM_COMPONENT, file.getKey())
+ .setParam(PARAM_BRANCH, "slb")
+ .setParam(PARAM_METRIC_KEYS, newBug.getKey() + "," + bug.getKey())
+ .executeProtobuf(ComponentTreeWsResponse.class);
+
+ assertThat(response.getBaseComponent()).extracting(Component::getKey, Component::getBranch)
+ .containsExactlyInAnyOrder(file.getKey(), "slb");
+ assertThat(response.getBaseComponent().getMeasuresList())
+ .isEmpty();
+ }
+
@Test
public void return_deprecated_id_in_the_response() {
ComponentDto project = db.components().insertPrivateProject();
*/
package org.sonar.server.measure.ws;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-
import org.junit.Test;
-import org.sonar.api.i18n.I18n;
-import org.sonar.api.resources.ResourceTypes;
import org.sonar.api.server.ws.WebService;
import org.sonar.server.ws.WsTester;
+import static org.assertj.core.api.Assertions.assertThat;
+
public class MeasuresWsTest {
WsTester ws = new WsTester(
new MeasuresWs(
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.measure.ws;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.annotation.Nullable;
+import org.junit.Test;
+import org.sonar.db.measure.LiveMeasureDto;
+import org.sonar.db.metric.MetricDto;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.tuple;
+import static org.sonar.api.measures.CoreMetrics.BUGS_KEY;
+import static org.sonar.api.measures.CoreMetrics.MINOR_VIOLATIONS_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_BUGS_KEY;
+import static org.sonar.api.measures.CoreMetrics.NEW_MINOR_VIOLATIONS_KEY;
+
+public class SLBorPRMeasureFixTest {
+
+ @Test
+ public void should_add_replacement_metrics() {
+ List<String> metricList = new ArrayList<>(Arrays.asList(NEW_BUGS_KEY, NEW_MINOR_VIOLATIONS_KEY));
+ SLBorPRMeasureFix.addReplacementMetricKeys(metricList);
+ assertThat(metricList).contains(BUGS_KEY, NEW_BUGS_KEY, MINOR_VIOLATIONS_KEY, NEW_MINOR_VIOLATIONS_KEY);
+ }
+
+ @Test
+ public void should_remove_metrics_not_initially_requested() {
+ Set<String> originalMetricList = new HashSet<>(Arrays.asList(NEW_BUGS_KEY, MINOR_VIOLATIONS_KEY, NEW_MINOR_VIOLATIONS_KEY));
+ MetricDto dto1 = new MetricDto().setKey(BUGS_KEY).setId(1);
+ MetricDto dto2 = new MetricDto().setKey(NEW_BUGS_KEY).setId(2);
+ MetricDto dto3 = new MetricDto().setKey(MINOR_VIOLATIONS_KEY).setId(3);
+ MetricDto dto4 = new MetricDto().setKey(NEW_MINOR_VIOLATIONS_KEY).setId(4);
+
+ List<MetricDto> metricList = new ArrayList<>(Arrays.asList(dto1, dto2, dto3, dto4));
+
+ SLBorPRMeasureFix.removeMetricsNotRequested(metricList, originalMetricList);
+ assertThat(metricList).containsOnly(dto2, dto3, dto4);
+ }
+
+ @Test
+ public void should_transform_measures() {
+ Set<String> requestedKeys = new HashSet<>(Arrays.asList(NEW_BUGS_KEY, MINOR_VIOLATIONS_KEY, NEW_MINOR_VIOLATIONS_KEY));
+
+ MetricDto bugsMetric = new MetricDto().setKey(BUGS_KEY).setId(1);
+ MetricDto newBugsMetric = new MetricDto().setKey(NEW_BUGS_KEY).setId(2);
+ MetricDto violationsMetric = new MetricDto().setKey(MINOR_VIOLATIONS_KEY).setId(3);
+ MetricDto newViolationsMetric = new MetricDto().setKey(NEW_MINOR_VIOLATIONS_KEY).setId(4);
+
+ List<MetricDto> metricList = Arrays.asList(bugsMetric, newBugsMetric, violationsMetric, newViolationsMetric);
+
+ LiveMeasureDto bugs = createLiveMeasure(bugsMetric.getId(), 10.0, null);
+ LiveMeasureDto newBugs = createLiveMeasure(newBugsMetric.getId(), null, 5.0);
+ LiveMeasureDto violations = createLiveMeasure(violationsMetric.getId(), 20.0, null);
+ LiveMeasureDto newViolations = createLiveMeasure(newViolationsMetric.getId(), null, 3.0);
+
+ Map<MetricDto, LiveMeasureDto> measureByMetric = new HashMap<>();
+ measureByMetric.put(bugsMetric, bugs);
+ measureByMetric.put(newBugsMetric, newBugs);
+ measureByMetric.put(violationsMetric, violations);
+ measureByMetric.put(newViolationsMetric, newViolations);
+
+ SLBorPRMeasureFix.createReplacementMeasures(metricList, measureByMetric, requestedKeys);
+ assertThat(measureByMetric.entrySet()).extracting(e -> e.getKey().getKey(), e -> e.getValue().getValue(), e -> e.getValue().getVariation())
+ .containsOnly(tuple(NEW_BUGS_KEY, null, 10.0),
+ tuple(MINOR_VIOLATIONS_KEY, 20.0, null),
+ tuple(NEW_MINOR_VIOLATIONS_KEY, null, 20.0));
+ }
+
+ private static LiveMeasureDto createLiveMeasure(int metricId, @Nullable Double value, @Nullable Double variation) {
+ return new LiveMeasureDto().setMetricId(metricId).setVariation(variation).setValue(value);
+ }
+}