From a4718d6858cc6e44080641cb1da4e8f5c9ee0762 Mon Sep 17 00:00:00 2001 From: lukasz-jarocki-sonarsource Date: Fri, 3 Nov 2023 14:54:02 +0100 Subject: [PATCH] SONAR-20892 Updated api/measures endpoints to return renamed metric --- build.gradle | 38 +++++++++ gradle.properties | 2 +- .../projectanalysis/issue/IssueCounter.java | 10 +-- .../step/PersistLiveMeasuresStep.java | 3 +- .../issue/IssueCounterTest.java | 10 ++- .../db/metric/RemovedMetricConverter.java | 64 +++++++++++++++ .../db/metric/RemovedMetricConverterTest.java | 60 ++++++++++++++ .../v104/RenameWontFixIssuesMetric.java | 2 +- .../server/startup/RegisterMetricsIT.java | 6 +- .../sonar/server/startup/RegisterMetrics.java | 8 +- .../server/measure/ws/ComponentActionIT.java | 35 ++++++++ .../measure/ws/ComponentTreeActionIT.java | 46 ++++++++++- .../server/measure/ws/SearchActionIT.java | 18 +++++ .../measure/ws/SearchHistoryActionIT.java | 38 ++++++++- .../live/MeasureUpdateFormulaFactoryImpl.java | 2 +- .../server/measure/ws/ComponentAction.java | 31 ++++--- .../measure/ws/ComponentDtoToWsComponent.java | 6 +- .../measure/ws/ComponentResponseCommon.java | 80 +++++++++++++++++++ .../measure/ws/ComponentTreeAction.java | 46 ++++++----- .../server/measure/ws/ComponentTreeData.java | 1 - .../measure/ws/ComponentTreeRequest.java | 3 +- .../server/measure/ws/MeasuresWsModule.java | 2 +- .../measure/ws/MetricDtoToWsMetric.java | 12 +++ .../sonar/server/measure/ws/SearchAction.java | 53 ++++++++---- .../measure/ws/SearchHistoryAction.java | 19 +++-- .../ws/SearchHistoryResponseFactory.java | 19 ++--- .../measure/ws/SearchHistoryResult.java | 10 +++ .../measure/ws/component_tree-example.json | 22 +++++ .../MeasureUpdateFormulaFactoryImplTest.java | 4 +- .../resources/org/sonar/l10n/core.properties | 3 +- 30 files changed, 559 insertions(+), 94 deletions(-) create mode 100644 server/sonar-db-dao/src/main/java/org/sonar/db/metric/RemovedMetricConverter.java create mode 100644 server/sonar-db-dao/src/test/java/org/sonar/db/metric/RemovedMetricConverterTest.java create mode 100644 server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentResponseCommon.java diff --git a/build.gradle b/build.gradle index 2c3868b1c6c..a058b18e45e 100644 --- a/build.gradle +++ b/build.gradle @@ -465,6 +465,44 @@ subprojects { } task generatePackageInfo { + doLast { + def allPathsContainingJavaFiles = [] as Set + + fileTree('src/main/java/').matching() { + include "*/**/*.java" + exclude "*/**/package-info.java" + }.forEach { + allPathsContainingJavaFiles << it.toPath().toFile().getParent(); + } + + allPathsContainingJavaFiles.each { + String packageInfoPath = it + "/package-info.java" + File packageInfoFile = new File (packageInfoPath) + if (!packageInfoFile.exists()) { + logger.warn("Creating file: " + packageInfoPath) + def packageName = packageInfoFile.getParent().takeAfter("src/main/java/").replaceAll("/", "\\.") + String packageInfoContent = applyPackageInfoTemplate(packageName) + packageInfoFile << packageInfoContent + } + } + + def allPackageInfoFiles = [] as Set + + fileTree('src/main/java/').matching() { + include "*/**/package-info.java" + }.forEach { + allPackageInfoFiles << it.toPath().toFile(); + } + + allPackageInfoFiles.forEach { + File packageInfoFile = it; + if (!allPathsContainingJavaFiles.contains(packageInfoFile.getParent())) { + logger.warn("Deleting package info file: " + packageInfoFile) + packageInfoFile.delete(); + } + } + + } } build.dependsOn(generatePackageInfo) generatePackageInfo.finalizedBy(licenseFormat) diff --git a/gradle.properties b/gradle.properties index 299b5e565bb..0f11af88c13 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ group=org.sonarsource.sonarqube version=10.4 -pluginApiVersion=10.2.0.1908 +pluginApiVersion=10.3.0.1933 description=Open source platform for continuous inspection of code quality projectTitle=SonarQube org.gradle.jvmargs=-Xmx2048m diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueCounter.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueCounter.java index 2eeab2ff2c3..389f862adbe 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueCounter.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueCounter.java @@ -39,6 +39,7 @@ import static org.sonar.api.issue.Issue.RESOLUTION_WONT_FIX; import static org.sonar.api.issue.Issue.STATUS_CONFIRMED; import static org.sonar.api.issue.Issue.STATUS_OPEN; import static org.sonar.api.issue.Issue.STATUS_REOPENED; +import static org.sonar.api.measures.CoreMetrics.ACCEPTED_ISSUES_KEY; 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; @@ -63,7 +64,6 @@ import static org.sonar.api.measures.CoreMetrics.REOPENED_ISSUES_KEY; import static org.sonar.api.measures.CoreMetrics.SECURITY_HOTSPOTS_KEY; import static org.sonar.api.measures.CoreMetrics.VIOLATIONS_KEY; import static org.sonar.api.measures.CoreMetrics.VULNERABILITIES_KEY; -import static org.sonar.api.measures.CoreMetrics.WONT_FIX_ISSUES_KEY; import static org.sonar.api.rule.Severity.BLOCKER; import static org.sonar.api.rule.Severity.CRITICAL; import static org.sonar.api.rule.Severity.INFO; @@ -169,7 +169,7 @@ public class IssueCounter extends IssueVisitor { addMeasure(component, REOPENED_ISSUES_KEY, currentCounters.counter().reopened); addMeasure(component, CONFIRMED_ISSUES_KEY, currentCounters.counter().confirmed); addMeasure(component, FALSE_POSITIVE_ISSUES_KEY, currentCounters.counter().falsePositives); - addMeasure(component, WONT_FIX_ISSUES_KEY, currentCounters.counter().wontFix); + addMeasure(component, ACCEPTED_ISSUES_KEY, currentCounters.counter().accepted); } private void addMeasuresByType(Component component) { @@ -221,7 +221,7 @@ public class IssueCounter extends IssueVisitor { private int reopened = 0; private int confirmed = 0; private int falsePositives = 0; - private int wontFix = 0; + private int accepted = 0; private final Multiset severityBag = HashMultiset.create(); private final EnumMultiset typeBag = EnumMultiset.create(RuleType.class); @@ -231,7 +231,7 @@ public class IssueCounter extends IssueVisitor { reopened += counter.reopened; confirmed += counter.confirmed; falsePositives += counter.falsePositives; - wontFix += counter.wontFix; + accepted += counter.accepted; severityBag.addAll(counter.severityBag); typeBag.addAll(counter.typeBag); } @@ -250,7 +250,7 @@ public class IssueCounter extends IssueVisitor { } else if (RESOLUTION_FALSE_POSITIVE.equals(issue.resolution())) { falsePositives++; } else if (RESOLUTION_WONT_FIX.equals(issue.resolution())) { - wontFix++; + accepted++; } switch (issue.status()) { case STATUS_OPEN: diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistLiveMeasuresStep.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistLiveMeasuresStep.java index 0d67496f255..3b922906b8a 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistLiveMeasuresStep.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistLiveMeasuresStep.java @@ -43,6 +43,7 @@ import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.measure.LiveMeasureDto; +import static org.sonar.api.measures.CoreMetrics.ACCEPTED_ISSUES_KEY; 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.CLASSES_KEY; @@ -108,7 +109,7 @@ public class PersistLiveMeasuresStep implements ComputationStep { MAJOR_VIOLATIONS_KEY, MINOR_VIOLATIONS_KEY, NCLOC_KEY, NCLOC_DATA_KEY, NCLOC_LANGUAGE_DISTRIBUTION_KEY, OPEN_ISSUES_KEY, RELIABILITY_RATING_KEY, RELIABILITY_REMEDIATION_EFFORT_KEY, REOPENED_ISSUES_KEY, SECURITY_HOTSPOTS_KEY, SECURITY_HOTSPOTS_REVIEWED_KEY, SECURITY_HOTSPOTS_REVIEWED_STATUS_KEY, SECURITY_HOTSPOTS_TO_REVIEW_STATUS_KEY, SECURITY_RATING_KEY, SECURITY_REMEDIATION_EFFORT_KEY, SECURITY_REVIEW_RATING_KEY, SQALE_DEBT_RATIO_KEY, TECHNICAL_DEBT_KEY, - SQALE_RATING_KEY, STATEMENTS_KEY, VIOLATIONS_KEY, VULNERABILITIES_KEY, WONT_FIX_ISSUES_KEY + SQALE_RATING_KEY, STATEMENTS_KEY, VIOLATIONS_KEY, VULNERABILITIES_KEY, ACCEPTED_ISSUES_KEY ); private final DbClient dbClient; private final MetricRepository metricRepository; diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IssueCounterTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IssueCounterTest.java index e7aa43128dc..a6d26b3b7c2 100644 --- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IssueCounterTest.java +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IssueCounterTest.java @@ -50,6 +50,8 @@ import static org.sonar.api.issue.Issue.STATUS_CLOSED; import static org.sonar.api.issue.Issue.STATUS_CONFIRMED; import static org.sonar.api.issue.Issue.STATUS_OPEN; import static org.sonar.api.issue.Issue.STATUS_RESOLVED; +import static org.sonar.api.measures.CoreMetrics.ACCEPTED_ISSUES; +import static org.sonar.api.measures.CoreMetrics.ACCEPTED_ISSUES_KEY; import static org.sonar.api.measures.CoreMetrics.BLOCKER_VIOLATIONS; import static org.sonar.api.measures.CoreMetrics.BLOCKER_VIOLATIONS_KEY; import static org.sonar.api.measures.CoreMetrics.BUGS; @@ -133,7 +135,7 @@ public class IssueCounterTest { .add(NEW_MINOR_VIOLATIONS) .add(NEW_INFO_VIOLATIONS) .add(FALSE_POSITIVE_ISSUES) - .add(WONT_FIX_ISSUES) + .add(ACCEPTED_ISSUES) .add(CODE_SMELLS) .add(BUGS) .add(VULNERABILITIES) @@ -200,10 +202,10 @@ public class IssueCounterTest { underTest.beforeComponent(PROJECT); underTest.afterComponent(PROJECT); - assertMeasures(FILE1, entry(VIOLATIONS_KEY, 1), entry(FALSE_POSITIVE_ISSUES_KEY, 1), entry(WONT_FIX_ISSUES_KEY, 1)); - assertMeasures(FILE2, entry(VIOLATIONS_KEY, 2), entry(FALSE_POSITIVE_ISSUES_KEY, 0), entry(WONT_FIX_ISSUES_KEY, 1)); + assertMeasures(FILE1, entry(VIOLATIONS_KEY, 1), entry(FALSE_POSITIVE_ISSUES_KEY, 1), entry(ACCEPTED_ISSUES_KEY, 1)); + assertMeasures(FILE2, entry(VIOLATIONS_KEY, 2), entry(FALSE_POSITIVE_ISSUES_KEY, 0), entry(ACCEPTED_ISSUES_KEY, 1)); assertMeasures(FILE3, entry(VIOLATIONS_KEY, 0)); - assertMeasures(PROJECT, entry(VIOLATIONS_KEY, 3), entry(FALSE_POSITIVE_ISSUES_KEY, 1), entry(WONT_FIX_ISSUES_KEY, 2)); + assertMeasures(PROJECT, entry(VIOLATIONS_KEY, 3), entry(FALSE_POSITIVE_ISSUES_KEY, 1), entry(ACCEPTED_ISSUES_KEY, 2)); } @Test diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/metric/RemovedMetricConverter.java b/server/sonar-db-dao/src/main/java/org/sonar/db/metric/RemovedMetricConverter.java new file mode 100644 index 00000000000..07c46eecfb7 --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/metric/RemovedMetricConverter.java @@ -0,0 +1,64 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 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.db.metric; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import org.sonar.api.measures.CoreMetrics; + +/* +* This class has been introduced to contain the usage of deprecated and renamed metric 'WONT_FIX' in one place. +* It should be removed in SQ 11.0. +*/ +public class RemovedMetricConverter { + public static final String REMOVED_METRIC = CoreMetrics.WONT_FIX_ISSUES_KEY; + public static final String REMOVED_METRIC_SHORT_NAME = CoreMetrics.WONT_FIX_ISSUES.getName(); + public static final String REMOVED_METRIC_DESCRIPTION = CoreMetrics.WONT_FIX_ISSUES.getDescription(); + public static final String DEPRECATED_METRIC_REPLACEMENT = CoreMetrics.ACCEPTED_ISSUES_KEY; + + private RemovedMetricConverter() { + // static methods only + } + + public static List withRemovedMetricAlias(Collection metrics) { + if (metrics.contains(REMOVED_METRIC)) { + Set newMetrics = new HashSet<>(metrics); + newMetrics.remove(REMOVED_METRIC); + newMetrics.add(DEPRECATED_METRIC_REPLACEMENT); + return newMetrics.stream().toList(); + } else { + return new ArrayList<>(metrics); + } + } + + @CheckForNull + public static String includeRenamedMetrics(@Nullable String metric) { + if (REMOVED_METRIC.equals(metric)) { + return DEPRECATED_METRIC_REPLACEMENT; + } else { + return metric; + } + } +} diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/metric/RemovedMetricConverterTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/metric/RemovedMetricConverterTest.java new file mode 100644 index 00000000000..f29df8268ff --- /dev/null +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/metric/RemovedMetricConverterTest.java @@ -0,0 +1,60 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 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.db.metric; + +import java.util.List; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class RemovedMetricConverterTest { + + @Test + public void withRemovedMetricAlias_whenListContainsWontFix_shouldReturnListWithAccepted() { + List coreMetrics = List.of("wont_fix_issues", "blocker_violations", "critical_violations"); + + List upToDateMetrics = RemovedMetricConverter.withRemovedMetricAlias(coreMetrics); + + assertThat(upToDateMetrics).containsExactlyInAnyOrder("accepted_issues", "blocker_violations", "critical_violations"); + } + + @Test + public void withRemovedMetricAlias_whenListContainsAccepted_shouldReturnListWithAccepted() { + List coreMetrics = List.of("accepted_issues", "blocker_violations", "critical_violations"); + + List upToDateMetrics = RemovedMetricConverter.withRemovedMetricAlias(coreMetrics); + + assertThat(upToDateMetrics).containsExactlyInAnyOrder("accepted_issues", "blocker_violations", "critical_violations"); + } + + @Test + public void includeRenamedMetrics_whenWontFixIssuesPassed_shouldReturnAccepted() { + String upToDateMetric = RemovedMetricConverter.includeRenamedMetrics("wont_fix_issues"); + + assertThat(upToDateMetric).isEqualTo("accepted_issues"); + } + + @Test + public void includeRenamedMetrics_whenAcceptedIssuesPassed_shouldReturnAccepted() { + String upToDateMetric = RemovedMetricConverter.includeRenamedMetrics("accepted_issues"); + + assertThat(upToDateMetric).isEqualTo("accepted_issues"); + } +} \ No newline at end of file diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v104/RenameWontFixIssuesMetric.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v104/RenameWontFixIssuesMetric.java index 5f35d1caadf..ea025607fd4 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v104/RenameWontFixIssuesMetric.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v104/RenameWontFixIssuesMetric.java @@ -27,7 +27,7 @@ import org.sonar.server.platform.db.migration.step.Upsert; public class RenameWontFixIssuesMetric extends DataChange { private static final String UPDATE_QUERY = """ - update metrics set name='accepted_issues', description='Accepted issues' where name='wont_fix_issues' + update metrics set name='accepted_issues', description='Accepted issues', short_name='Accepted issues' where name='wont_fix_issues' """; public RenameWontFixIssuesMetric(Database db) { diff --git a/server/sonar-webserver-core/src/it/java/org/sonar/server/startup/RegisterMetricsIT.java b/server/sonar-webserver-core/src/it/java/org/sonar/server/startup/RegisterMetricsIT.java index bf19912492a..35bfa6854a8 100644 --- a/server/sonar-webserver-core/src/it/java/org/sonar/server/startup/RegisterMetricsIT.java +++ b/server/sonar-webserver-core/src/it/java/org/sonar/server/startup/RegisterMetricsIT.java @@ -136,10 +136,12 @@ public class RegisterMetricsIT { } @Test - public void insert_core_metrics() { + public void insert_core_metrics_without_removed_metric() { register.start(); - assertThat(dbTester.countRowsOfTable("metrics")).isEqualTo(CoreMetrics.getMetrics().size()); + // Metric CoreMetrics.WONT_FIX_ISSUES was renamed to CoreMetrics.ACCEPTED_ISSUES in 10.3. + // We don't want to insert it anymore + assertThat(dbTester.countRowsOfTable("metrics")).isEqualTo(CoreMetrics.getMetrics().size() - 1); } @Test diff --git a/server/sonar-webserver-core/src/main/java/org/sonar/server/startup/RegisterMetrics.java b/server/sonar-webserver-core/src/main/java/org/sonar/server/startup/RegisterMetrics.java index 238c97dd8f6..8afa7232280 100644 --- a/server/sonar-webserver-core/src/main/java/org/sonar/server/startup/RegisterMetrics.java +++ b/server/sonar-webserver-core/src/main/java/org/sonar/server/startup/RegisterMetrics.java @@ -20,6 +20,7 @@ package org.sonar.server.startup; import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.FluentIterable; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -35,10 +36,11 @@ import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.metric.MetricDto; import org.sonar.server.metric.MetricToDto; +import org.springframework.beans.factory.annotation.Autowired; import static com.google.common.collect.FluentIterable.concat; import static com.google.common.collect.Lists.newArrayList; -import org.springframework.beans.factory.annotation.Autowired; +import static org.sonar.db.metric.RemovedMetricConverter.REMOVED_METRIC; public class RegisterMetrics implements Startable { @@ -65,7 +67,9 @@ public class RegisterMetrics implements Startable { @Override public void start() { - register(concat(CoreMetrics.getMetrics(), getPluginMetrics())); + FluentIterable metricsToRegister = concat(CoreMetrics.getMetrics(), getPluginMetrics()) + .filter(m -> !REMOVED_METRIC.equals(m.getKey())); + register(metricsToRegister); } @Override diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/measure/ws/ComponentActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/measure/ws/ComponentActionIT.java index c9520642092..bee0bba79ce 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/measure/ws/ComponentActionIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/measure/ws/ComponentActionIT.java @@ -46,6 +46,7 @@ import static java.lang.Double.parseDouble; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.tuple; +import static org.sonar.api.measures.Metric.ValueType.INT; import static org.sonar.api.utils.DateUtils.parseDateTime; import static org.sonar.api.web.UserRole.USER; import static org.sonar.db.component.BranchDto.DEFAULT_MAIN_BRANCH_NAME; @@ -355,6 +356,26 @@ public class ComponentActionIT { .hasMessage(String.format("Component '%s' on branch '%s' not found", file.getKey(), "another_branch")); } + @Test + public void shouldReturnRenamedMetric() { + ProjectData projectData = db.components().insertPrivateProject(p -> p.setKey("MY_PROJECT") + .setName("My Project")); + userSession.addProjectPermission(USER, projectData.getProjectDto()) + .registerBranches(projectData.getMainBranchDto()); + ComponentDto mainBranch = projectData.getMainBranchComponent(); + SnapshotDto analysis = db.components().insertSnapshot(mainBranch, s -> s.setPeriodDate(parseDateTime("2016-01-11T10:49:50+0100").getTime()) + .setPeriodMode("previous_version") + .setPeriodParam("1.0-SNAPSHOT")); + + MetricDto accepted_issues = insertAcceptedIssuesMetric(); + db.measures().insertLiveMeasure(mainBranch, accepted_issues, m -> m.setValue(10d)); + + db.commit(); + + ComponentWsResponse response = newRequest(projectData.projectKey(), "wont_fix_issues"); + assertThat(response.getMetrics().getMetrics(0).getKey()).isEqualTo("wont_fix_issues"); + } + @Test public void json_example() { ProjectData projectData = db.components().insertPrivateProject(); @@ -417,6 +438,20 @@ public class ComponentActionIT { assertJson(response).isSimilarTo(getClass().getResource("component-example.json")); } + private MetricDto insertAcceptedIssuesMetric() { + MetricDto acceptedIssues = db.measures().insertMetric(m -> m.setKey("accepted_issues") + .setShortName("Accepted Issues") + .setDescription("Accepted issues") + .setDomain("Issues") + .setValueType("INT") + .setDirection(-1) + .setQualitative(false) + .setHidden(false)); + db.commit(); + return acceptedIssues; + } + + private ComponentWsResponse newRequest(String componentKey, String metricKeys) { return ws.newRequest() .setParam(PARAM_COMPONENT, componentKey) diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/measure/ws/ComponentTreeActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/measure/ws/ComponentTreeActionIT.java index 8b885bf3c12..05a1513f222 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/measure/ws/ComponentTreeActionIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/measure/ws/ComponentTreeActionIT.java @@ -162,11 +162,16 @@ public class ComponentTreeActionIT { db.measures().insertLiveMeasure(dir, newViolations, m -> m.setValue(25.0d)); db.measures().insertLiveMeasure(mainBranch, newViolations, m -> m.setValue(255.0d)); + MetricDto accepted_issues = insertAcceptedIssuesMetric(); + db.measures().insertLiveMeasure(file1, accepted_issues, m -> m.setValue(10d)); + db.measures().insertLiveMeasure(dir, accepted_issues, m -> m.setValue(10d)); + db.measures().insertLiveMeasure(mainBranch, accepted_issues, m -> m.setValue(10d)); + db.commit(); String response = ws.newRequest() .setParam(PARAM_COMPONENT, mainBranch.getKey()) - .setParam(PARAM_METRIC_KEYS, "ncloc, complexity, new_violations") + .setParam(PARAM_METRIC_KEYS, "ncloc, complexity, new_violations, accepted_issues") .setParam(PARAM_ADDITIONAL_FIELDS, "metrics,period") .execute() .getInput(); @@ -179,6 +184,31 @@ public class ComponentTreeActionIT { .addProjectBranchMapping(projectData.projectUuid(), projectData.getMainBranchComponent()); } + @Test + public void shouldReturnRenamedMetric() { + ProjectData projectData = db.components().insertPrivateProject(p -> p.setKey("MY_PROJECT") + .setName("My Project")); + addProjectPermission(projectData); + ComponentDto mainBranch = projectData.getMainBranchComponent(); + SnapshotDto analysis = db.components().insertSnapshot(mainBranch, s -> s.setPeriodDate(parseDateTime("2016-01-11T10:49:50+0100").getTime()) + .setPeriodMode("previous_version") + .setPeriodParam("1.0-SNAPSHOT")); + + MetricDto accepted_issues = insertAcceptedIssuesMetric(); + db.measures().insertLiveMeasure(mainBranch, accepted_issues, m -> m.setValue(10d)); + + db.commit(); + + ComponentTreeWsResponse response = ws.newRequest() + .setParam(PARAM_COMPONENT, mainBranch.getKey()) + .setParam(PARAM_METRIC_KEYS, "wont_fix_issues") + .setParam(PARAM_ADDITIONAL_FIELDS, "metrics") + .executeProtobuf(ComponentTreeWsResponse.class); + + assertThat(response.getMetrics().getMetrics(0).getKey()).isEqualTo("wont_fix_issues"); + assertThat(response.getBaseComponent().getMeasures(0).getMetric()).isEqualTo("wont_fix_issues"); + } + @Test public void empty_response() { ProjectData projectData = db.components().insertPrivateProject(); @@ -1077,6 +1107,20 @@ public class ComponentTreeActionIT { return metric; } + private MetricDto insertAcceptedIssuesMetric() { + MetricDto metric = dbClient.metricDao().insert(dbSession, newMetricDto() + .setKey("accepted_issues") + .setShortName("Accepted Issues") + .setDescription("Accepted issues") + .setDomain("Issues") + .setValueType(INT.name()) + .setDirection(-1) + .setQualitative(false) + .setHidden(false)); + db.commit(); + return metric; + } + private MetricDto insertNclocMetric() { MetricDto metric = dbClient.metricDao().insert(dbSession, newMetricDto() .setKey("ncloc") diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/measure/ws/SearchActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/measure/ws/SearchActionIT.java index b854fe6b1a7..c81945d50ed 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/measure/ws/SearchActionIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/measure/ws/SearchActionIT.java @@ -117,6 +117,24 @@ public class SearchActionIT { assertThat(measure.getValue()).isEqualTo("15.5"); } + @Test + public void search_shouldReturnAcceptedIssuesMetric_whenIsCalledWithDeprecatedWontFixIssuesMetric() { + ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent(); + userSession.addProjectPermission(UserRole.USER, project); + MetricDto acceptedIssues = db.measures().insertMetric(m -> m.setValueType(INT.name()) + .setKey("accepted_issues") + .setShortName("Accepted Issues")); + db.measures().insertLiveMeasure(project, acceptedIssues, m -> m.setValue(10d)); + + SearchWsResponse result = call(singletonList(project.getKey()), singletonList("wont_fix_issues")); + + List measures = result.getMeasuresList(); + assertThat(measures).hasSize(1); + Measure measure = measures.get(0); + assertThat(measure.getMetric()).isEqualTo("wont_fix_issues"); + assertThat(measure.getValue()).isEqualTo("10"); + } + @Test public void return_best_value() { ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent(); diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/measure/ws/SearchHistoryActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/measure/ws/SearchHistoryActionIT.java index 885f0452411..aeec043073f 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/measure/ws/SearchHistoryActionIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/measure/ws/SearchHistoryActionIT.java @@ -90,6 +90,7 @@ public class SearchHistoryActionIT { private MetricDto nclocMetric; private MetricDto newViolationMetric; private MetricDto stringMetric; + private MetricDto acceptedIssuesMetric; @Before public void setUp() { @@ -101,6 +102,7 @@ public class SearchHistoryActionIT { complexityMetric = insertComplexityMetric(); newViolationMetric = insertNewViolationMetric(); stringMetric = insertStringMetric(); + acceptedIssuesMetric = insertAcceptedIssuesMetric(); } @Test @@ -145,18 +147,36 @@ public class SearchHistoryActionIT { @Test public void return_metrics() { dbClient.measureDao().insert(dbSession, newMeasureDto(complexityMetric, project.mainBranchUuid(), analysis).setValue(42.0d)); + dbClient.measureDao().insert(dbSession, newMeasureDto(acceptedIssuesMetric, project.mainBranchUuid(), analysis).setValue(10.0d)); db.commit(); SearchHistoryRequest request = SearchHistoryRequest.builder() .setComponent(project.projectKey()) - .setMetrics(asList(complexityMetric.getKey(), nclocMetric.getKey(), newViolationMetric.getKey())) + .setMetrics(asList(complexityMetric.getKey(), nclocMetric.getKey(), newViolationMetric.getKey(), acceptedIssuesMetric.getKey())) .build(); SearchHistoryResponse result = call(request); - assertThat(result.getMeasuresList()).hasSize(3) + assertThat(result.getMeasuresList()).hasSize(4) .extracting(HistoryMeasure::getMetric) - .containsExactly(complexityMetric.getKey(), nclocMetric.getKey(), newViolationMetric.getKey()); + .containsExactlyInAnyOrder(complexityMetric.getKey(), nclocMetric.getKey(), newViolationMetric.getKey(), acceptedIssuesMetric.getKey()); + } + + @Test + public void return_renamed_and_deprecated_metric() { + dbClient.measureDao().insert(dbSession, newMeasureDto(acceptedIssuesMetric, project.mainBranchUuid(), analysis).setValue(10.0d)); + db.commit(); + + SearchHistoryRequest request = SearchHistoryRequest.builder() + .setComponent(project.projectKey()) + .setMetrics(singletonList("wont_fix_issues")) + .build(); + + SearchHistoryResponse result = call(request); + + assertThat(result.getMeasuresList()).hasSize(1) + .extracting(HistoryMeasure::getMetric) + .containsExactlyInAnyOrder("wont_fix_issues"); } @Test @@ -557,4 +577,16 @@ public class SearchHistoryActionIT { db.commit(); return metric; } + + private MetricDto insertAcceptedIssuesMetric() { + MetricDto metric = dbClient.metricDao().insert(dbSession, newMetricDtoWithoutOptimization() + .setKey("accepted_issues") + .setShortName("Accepted Issues") + .setValueType("INT")) + .setDirection(-1) + .setQualitative(true) + .setHidden(false); + db.commit(); + return metric; + } } diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/live/MeasureUpdateFormulaFactoryImpl.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/live/MeasureUpdateFormulaFactoryImpl.java index 8b807b8ded6..93315bef077 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/live/MeasureUpdateFormulaFactoryImpl.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/live/MeasureUpdateFormulaFactoryImpl.java @@ -78,7 +78,7 @@ public class MeasureUpdateFormulaFactoryImpl implements MeasureUpdateFormulaFact new MeasureUpdateFormula(CoreMetrics.FALSE_POSITIVE_ISSUES, false, new AddChildren(), (context, issues) -> context.setValue(issues.countByResolution(Issue.RESOLUTION_FALSE_POSITIVE, false))), - new MeasureUpdateFormula(CoreMetrics.WONT_FIX_ISSUES, false, new AddChildren(), + new MeasureUpdateFormula(CoreMetrics.ACCEPTED_ISSUES, false, new AddChildren(), (context, issues) -> context.setValue(issues.countByResolution(Issue.RESOLUTION_WONT_FIX, false))), new MeasureUpdateFormula(CoreMetrics.OPEN_ISSUES, false, new AddChildren(), diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentAction.java index 4ab014e88af..939c4d4d2f2 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentAction.java @@ -55,6 +55,7 @@ import static java.lang.String.format; import static java.util.Collections.emptyMap; import static java.util.Collections.singletonList; import static java.util.Collections.singletonMap; +import static org.sonar.db.metric.RemovedMetricConverter.withRemovedMetricAlias; import static org.sonar.server.component.ws.MeasuresWsParameters.ACTION_COMPONENT; import static org.sonar.server.component.ws.MeasuresWsParameters.ADDITIONAL_METRICS; import static org.sonar.server.component.ws.MeasuresWsParameters.ADDITIONAL_PERIOD; @@ -65,9 +66,9 @@ import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_METRIC_KE import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_PULL_REQUEST; import static org.sonar.server.exceptions.BadRequestException.checkRequest; import static org.sonar.server.measure.ws.ComponentDtoToWsComponent.componentDtoToWsComponent; +import static org.sonar.server.measure.ws.ComponentResponseCommon.addMetricToResponseIncludingRenamedMetric; import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createAdditionalFieldsParameter; import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createMetricKeysParameter; -import static org.sonar.server.measure.ws.MetricDtoToWsMetric.metricDtoToWsMetric; import static org.sonar.server.measure.ws.SnapshotDtoToWsPeriod.snapshotToWsPeriods; import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001; import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001; @@ -95,14 +96,17 @@ public class ComponentAction implements MeasuresWsAction { .setResponseExample(getClass().getResource("component-example.json")) .setSince("5.4") .setChangelog( + new Change("10.3", "The metric 'wont_fix_issues' is now deprecated in the response. Consume 'accepted_issues' instead."), + new Change("10.3", "The use of 'wont_fix_issues' value in 'metricKeys' param is now deprecated. Use 'accepted_issues' instead."), + new Change("10.3", "Added new accepted value for the 'metricKeys' param: 'accepted_issues'."), new Change("10.1", String.format("The use of module keys in parameter '%s' is removed", PARAM_COMPONENT)), new Change("10.0", format("The use of the following metrics in 'metricKeys' parameter is not deprecated anymore: %s", - MeasuresWsModule.getDeprecatedMetrics())), + MeasuresWsModule.getDeprecatedMetricsInSonarQube93())), new Change("10.0", "the response field periods under measures field is removed."), new Change("10.0", "the option `periods` of 'additionalFields' request field is removed."), new Change("9.3", "When the new code period is set to 'reference branch', the response field 'date' under the 'period' field has been removed"), new Change("9.3", format("The use of the following metrics in 'metricKeys' parameter is deprecated: %s", - MeasuresWsModule.getDeprecatedMetrics())), + MeasuresWsModule.getDeprecatedMetricsInSonarQube93())), new Change("8.8", "deprecated response field 'id' has been removed"), new Change("8.8", "deprecated response field 'refId' has been removed."), new Change("8.1", "the response field periods under measures field is deprecated. Use period instead."), @@ -145,13 +149,13 @@ public class ComponentAction implements MeasuresWsAction { checkPermissions(component); SnapshotDto analysis = dbClient.snapshotDao().selectLastAnalysisByRootComponentUuid(dbSession, component.branchUuid()).orElse(null); - List metrics = searchMetrics(dbSession, new HashSet<>(request.getMetricKeys())); + List metrics = searchMetrics(dbSession, new HashSet<>(withRemovedMetricAlias(request.getMetricKeys()))); List measures = searchMeasures(dbSession, component, metrics); Map measuresByMetric = getMeasuresByMetric(measures, metrics); Optional period = snapshotToWsPeriods(analysis); Optional reference = getReference(dbSession, component); - return buildResponse(dbSession, request, component, reference, measuresByMetric, metrics, period); + return buildResponse(dbSession, request, component, reference, measuresByMetric, metrics, period, request.getMetricKeys()); } } @@ -229,30 +233,33 @@ public class ComponentAction implements MeasuresWsAction { } private ComponentWsResponse buildResponse(DbSession dbSession, ComponentRequest request, ComponentDto component, Optional reference, - Map measuresByMetric, Collection metrics, Optional period) { + Map measuresByMetric, Collection metrics, Optional period, + Collection requestedMetrics) { ComponentWsResponse.Builder response = ComponentWsResponse.newBuilder(); if (reference.isPresent()) { BranchDto refBranch = reference.get().getRefBranch(); ComponentDto refComponent = reference.get().getComponent(); response.setComponent(componentDtoToWsComponent(component, measuresByMetric, singletonMap(refComponent.uuid(), refComponent), - refBranch.isMain() ? null : refBranch.getBranchKey(), null)); + refBranch.isMain() ? null : refBranch.getBranchKey(), null, requestedMetrics)); } else { boolean isMainBranch = dbClient.branchDao().selectByUuid(dbSession, component.branchUuid()).map(BranchDto::isMain).orElse(true); - response.setComponent(componentDtoToWsComponent(component, measuresByMetric, emptyMap(), isMainBranch ? null : request.getBranch(), request.getPullRequest())); + response.setComponent(componentDtoToWsComponent(component, measuresByMetric, emptyMap(), isMainBranch ? null : request.getBranch(), + request.getPullRequest(), requestedMetrics)); } - setAdditionalFields(request, metrics, period, response); + setAdditionalFields(request, metrics, period, response, requestedMetrics); return response.build(); } - private static void setAdditionalFields(ComponentRequest request, Collection metrics, Optional period, ComponentWsResponse.Builder response) { + private static void setAdditionalFields(ComponentRequest request, Collection metrics, Optional period, + ComponentWsResponse.Builder response, Collection requestedMetrics) { List additionalFields = request.getAdditionalFields(); if (additionalFields != null) { if (additionalFields.contains(ADDITIONAL_METRICS)) { - for (MetricDto metric : metrics) { - response.getMetricsBuilder().addMetrics(metricDtoToWsMetric(metric)); + for (MetricDto metricDto : metrics) { + addMetricToResponseIncludingRenamedMetric(metric -> response.getMetricsBuilder().addMetrics(metric), requestedMetrics, metricDto); } } diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentDtoToWsComponent.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentDtoToWsComponent.java index 55760a0070c..4f31a2f3e92 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentDtoToWsComponent.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentDtoToWsComponent.java @@ -19,6 +19,7 @@ */ package org.sonar.server.measure.ws; +import java.util.Collection; import java.util.Map; import javax.annotation.Nullable; import org.sonar.db.component.ComponentDto; @@ -28,6 +29,7 @@ import org.sonarqube.ws.Measures; import org.sonarqube.ws.Measures.Component; import static java.util.Optional.ofNullable; +import static org.sonar.server.measure.ws.ComponentResponseCommon.addMeasureIncludingRenamedMetric; class ComponentDtoToWsComponent { private ComponentDtoToWsComponent() { @@ -36,7 +38,7 @@ class ComponentDtoToWsComponent { static Component.Builder componentDtoToWsComponent(ComponentDto component, Map measuresByMetric, Map referenceComponentsByUuid, @Nullable String branch, - @Nullable String pullRequest) { + @Nullable String pullRequest, Collection requestedMetrics) { Component.Builder wsComponent = componentDtoToWsComponent(component, branch, pullRequest); ComponentDto referenceComponent = referenceComponentsByUuid.get(component.getCopyComponentUuid()); @@ -47,7 +49,7 @@ class ComponentDtoToWsComponent { Measures.Measure.Builder measureBuilder = Measures.Measure.newBuilder(); for (Map.Entry entry : measuresByMetric.entrySet()) { MeasureDtoToWsMeasure.updateMeasureBuilder(measureBuilder, entry.getKey(), entry.getValue()); - wsComponent.addMeasures(measureBuilder); + addMeasureIncludingRenamedMetric(requestedMetrics, wsComponent, measureBuilder); measureBuilder.clear(); } diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentResponseCommon.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentResponseCommon.java new file mode 100644 index 00000000000..2fa908b6813 --- /dev/null +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentResponseCommon.java @@ -0,0 +1,80 @@ +/* + * SonarQube + * Copyright (C) 2009-2023 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.Collection; +import java.util.function.Consumer; +import org.sonar.db.metric.MetricDto; +import org.sonar.db.metric.RemovedMetricConverter; +import org.sonarqube.ws.Measures; + +import static org.sonar.db.metric.RemovedMetricConverter.REMOVED_METRIC; +import static org.sonar.db.metric.RemovedMetricConverter.DEPRECATED_METRIC_REPLACEMENT; +import static org.sonar.server.measure.ws.MetricDtoToWsMetric.metricDtoToWsMetric; +import static org.sonar.server.measure.ws.MetricDtoToWsMetric.wontFixToAcceptedWsMetric; + +public class ComponentResponseCommon { + + private ComponentResponseCommon() { + // static methods only + } + + static void addMetricToResponseIncludingRenamedMetric(Consumer responseBuilder, Collection requestedMetrics, + MetricDto metricDto) { + if (metricDto.getKey().equals(DEPRECATED_METRIC_REPLACEMENT)) { + if (requestedMetrics.contains(DEPRECATED_METRIC_REPLACEMENT)) { + responseBuilder.accept(metricDtoToWsMetric(metricDto)); + } + if (requestedMetrics.contains(REMOVED_METRIC)) { + responseBuilder.accept(wontFixToAcceptedWsMetric(metricDto)); + } + } else { + responseBuilder.accept(metricDtoToWsMetric(metricDto)); + } + } + + public static void addMetricToSearchHistoryResponseIncludingRenamedMetric(Measures.SearchHistoryResponse.Builder response, + Collection requestedMetrics, Measures.SearchHistoryResponse.HistoryMeasure.Builder measure) { + if (measure.getMetric().equals(DEPRECATED_METRIC_REPLACEMENT)) { + if (requestedMetrics.contains(DEPRECATED_METRIC_REPLACEMENT)) { + response.addMeasures(measure.build()); + } + if (requestedMetrics.contains(RemovedMetricConverter.REMOVED_METRIC)) { + response.addMeasures(measure.setMetric(RemovedMetricConverter.REMOVED_METRIC).build()); + } + } else { + response.addMeasures(measure.build()); + } + } + + static void addMeasureIncludingRenamedMetric(Collection requestedMetrics, Measures.Component.Builder componentBuilder, + Measures.Measure.Builder measureBuilder) { + if (measureBuilder.getMetric().equals(DEPRECATED_METRIC_REPLACEMENT)) { + if (requestedMetrics.contains(DEPRECATED_METRIC_REPLACEMENT)) { + componentBuilder.addMeasures(measureBuilder.build()); + } + if (requestedMetrics.contains(REMOVED_METRIC)) { + componentBuilder.addMeasures(measureBuilder.setMetric(REMOVED_METRIC).build()); + } + } else { + componentBuilder.addMeasures(measureBuilder.build()); + } + } +} diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentTreeAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentTreeAction.java index 3e089a836a9..d5006a14c02 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentTreeAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentTreeAction.java @@ -76,11 +76,14 @@ import static com.google.common.base.Preconditions.checkState; import static java.lang.String.format; import static java.util.Collections.emptyList; import static java.util.Collections.emptyMap; +import static java.util.Optional.ofNullable; 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; import static org.sonar.db.component.ComponentTreeQuery.Strategy.CHILDREN; import static org.sonar.db.component.ComponentTreeQuery.Strategy.LEAVES; +import static org.sonar.db.metric.RemovedMetricConverter.includeRenamedMetrics; +import static org.sonar.db.metric.RemovedMetricConverter.withRemovedMetricAlias; import static org.sonar.server.component.ws.MeasuresWsParameters.ACTION_COMPONENT_TREE; import static org.sonar.server.component.ws.MeasuresWsParameters.ADDITIONAL_METRICS; import static org.sonar.server.component.ws.MeasuresWsParameters.ADDITIONAL_PERIOD; @@ -96,16 +99,17 @@ import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_QUALIFIER import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_STRATEGY; import static org.sonar.server.exceptions.BadRequestException.checkRequest; import static org.sonar.server.measure.ws.ComponentDtoToWsComponent.componentDtoToWsComponent; +import static org.sonar.server.measure.ws.ComponentResponseCommon.addMeasureIncludingRenamedMetric; +import static org.sonar.server.measure.ws.ComponentResponseCommon.addMetricToResponseIncludingRenamedMetric; import static org.sonar.server.measure.ws.MeasureDtoToWsMeasure.updateMeasureBuilder; import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createAdditionalFieldsParameter; import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createMetricKeysParameter; -import static org.sonar.server.measure.ws.MetricDtoToWsMetric.metricDtoToWsMetric; import static org.sonar.server.measure.ws.SnapshotDtoToWsPeriod.snapshotToWsPeriods; import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001; import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001; import static org.sonar.server.ws.KeyExamples.KEY_PULL_REQUEST_EXAMPLE_001; -import static org.sonar.server.ws.WsParameterBuilder.QualifierParameterContext.newQualifierParameterContext; import static org.sonar.server.ws.WsParameterBuilder.createQualifiersParameter; +import static org.sonar.server.ws.WsParameterBuilder.QualifierParameterContext.newQualifierParameterContext; import static org.sonar.server.ws.WsUtils.writeProtobuf; /** @@ -177,13 +181,16 @@ public class ComponentTreeAction implements MeasuresWsAction { .setHandler(this) .addPagingParams(100, MAX_SIZE) .setChangelog( + new Change("10.3", "The metric 'wont_fix_issues' is now deprecated in the response. Consume 'accepted_issues' instead."), + new Change("10.3", "The use of 'wont_fix_issues' value in 'metricKeys' and 'metricSort' params is now deprecated. Use 'accepted_issues' instead."), + new Change("10.3", "Added new accepted value for the 'metricKeys' and 'metricSort' param: 'accepted_issues'."), new Change("10.1", String.format("The use of 'BRC' as value for parameter '%s' is removed", ComponentsWsParameters.PARAM_QUALIFIERS)), new Change("10.0", format("The use of the following metrics in 'metricKeys' parameter is not deprecated anymore: %s", - MeasuresWsModule.getDeprecatedMetrics())), + MeasuresWsModule.getDeprecatedMetricsInSonarQube93())), new Change("10.0", "the response field periods under measures field is removed."), new Change("10.0", "the option `periods` of 'additionalFields' request field is removed."), new Change("9.3", format("The use of the following metrics in 'metricKeys' parameter is deprecated: %s", - MeasuresWsModule.getDeprecatedMetrics())), + MeasuresWsModule.getDeprecatedMetricsInSonarQube93())), new Change("8.8", "parameter 'component' is now required"), new Change("8.8", "deprecated parameter 'baseComponentId' has been removed"), new Change("8.8", "deprecated parameter 'baseComponentKey' has been removed."), @@ -279,10 +286,12 @@ public class ComponentTreeAction implements MeasuresWsAction { Paging.forPageIndex( request.getPage()) .withPageSize(request.getPageSize()) - .andTotal(data.getComponentCount())); + .andTotal(data.getComponentCount()), + request.getMetricKeys()); } - private static ComponentTreeWsResponse buildResponse(ComponentTreeRequest request, ComponentTreeData data, Paging paging) { + private static ComponentTreeWsResponse buildResponse(ComponentTreeRequest request, ComponentTreeData data, Paging paging, + List requestedMetrics) { ComponentTreeWsResponse.Builder response = ComponentTreeWsResponse.newBuilder(); response.getPagingBuilder() .setPageIndex(paging.pageIndex()) @@ -295,7 +304,7 @@ public class ComponentTreeAction implements MeasuresWsAction { toWsComponent( data.getBaseComponent(), data.getMeasuresByComponentUuidAndMetric().row(data.getBaseComponent().uuid()), - data.getReferenceComponentsByUuid(), isMainBranch ? null : request.getBranch(), request.getPullRequest())); + data.getReferenceComponentsByUuid(), isMainBranch ? null : request.getBranch(), request.getPullRequest(), requestedMetrics)); for (ComponentDto componentDto : data.getComponents()) { if (componentDto.getCopyComponentUuid() != null) { @@ -303,23 +312,22 @@ public class ComponentTreeAction implements MeasuresWsAction { response.addComponents(toWsComponent( componentDto, data.getMeasuresByComponentUuidAndMetric().row(componentDto.uuid()), - data.getReferenceComponentsByUuid(), refBranch, null)); + data.getReferenceComponentsByUuid(), refBranch, null, requestedMetrics)); } else { response.addComponents(toWsComponent( componentDto, data.getMeasuresByComponentUuidAndMetric().row(componentDto.uuid()), - data.getReferenceComponentsByUuid(), isMainBranch ? null : request.getBranch(), request.getPullRequest())); + data.getReferenceComponentsByUuid(), isMainBranch ? null : request.getBranch(), request.getPullRequest(), requestedMetrics)); } } if (areMetricsInResponse(request)) { - Measures.Metrics.Builder metricsBuilder = response.getMetricsBuilder(); for (MetricDto metricDto : data.getMetrics()) { - metricsBuilder.addMetrics(metricDtoToWsMetric(metricDto)); + addMetricToResponseIncludingRenamedMetric(metric -> response.getMetricsBuilder().addMetrics(metric), requestedMetrics, metricDto); } } - List additionalFields = Optional.ofNullable(request.getAdditionalFields()).orElse(Collections.emptyList()); + List additionalFields = ofNullable(request.getAdditionalFields()).orElse(Collections.emptyList()); if (additionalFields.contains(ADDITIONAL_PERIOD) && data.getPeriod() != null) { response.setPeriod(data.getPeriod()); @@ -359,7 +367,7 @@ public class ComponentTreeAction implements MeasuresWsAction { .setAdditionalFields(request.paramAsStrings(PARAM_ADDITIONAL_FIELDS)) .setSort(request.paramAsStrings(Param.SORT)) .setAsc(request.paramAsBoolean(Param.ASCENDING)) - .setMetricSort(request.param(PARAM_METRIC_SORT)) + .setMetricSort(includeRenamedMetrics(request.param(PARAM_METRIC_SORT))) .setMetricSortFilter(request.mandatoryParam(PARAM_METRIC_SORT_FILTER)) .setMetricPeriodSort(request.paramAsInt(PARAM_METRIC_PERIOD_SORT)) .setPage(request.mandatoryParamAsInt(Param.PAGE)) @@ -367,7 +375,7 @@ public class ComponentTreeAction implements MeasuresWsAction { .setQuery(request.param(Param.TEXT_QUERY)); String metricSortValue = componentTreeRequest.getMetricSort(); checkRequest(!componentTreeRequest.getMetricKeys().isEmpty(), "The '%s' parameter must contain at least one metric key", PARAM_METRIC_KEYS); - List sorts = Optional.ofNullable(componentTreeRequest.getSort()).orElse(emptyList()); + List sorts = ofNullable(componentTreeRequest.getSort()).orElse(emptyList()); checkRequest(metricSortValue == null ^ sorts.contains(METRIC_SORT) ^ sorts.contains(METRIC_PERIOD_SORT), "To sort by a metric, the '%s' parameter must contain '%s' or '%s', and a metric key must be provided in the '%s' parameter", Param.SORT, METRIC_SORT, METRIC_PERIOD_SORT, PARAM_METRIC_SORT); @@ -382,7 +390,7 @@ public class ComponentTreeAction implements MeasuresWsAction { } private static Measures.Component.Builder toWsComponent(ComponentDto component, Map measures, - Map referenceComponentsByUuid, @Nullable String branch, @Nullable String pullRequest) { + Map referenceComponentsByUuid, @Nullable String branch, @Nullable String pullRequest, List requestedMetrics) { Measures.Component.Builder wsComponent = componentDtoToWsComponent(component, branch, pullRequest); ComponentDto referenceComponent = referenceComponentsByUuid.get(component.getCopyComponentUuid()); if (referenceComponent != null) { @@ -395,7 +403,7 @@ public class ComponentTreeAction implements MeasuresWsAction { ComponentTreeData.Measure measure = entry.getValue(); boolean onNewCode = entry.getKey().getKey().startsWith("new_"); updateMeasureBuilder(measureBuilder, entry.getKey(), measure.getValue(), measure.getData(), onNewCode); - wsComponent.addMeasures(measureBuilder); + addMeasureIncludingRenamedMetric(requestedMetrics, wsComponent, measureBuilder); measureBuilder.clear(); } return wsComponent; @@ -429,9 +437,9 @@ public class ComponentTreeAction implements MeasuresWsAction { ComponentTreeQuery componentTreeQuery = toComponentTreeQuery(wsRequest, baseComponent); List components = searchComponents(dbSession, componentTreeQuery); - List metrics = searchMetrics(dbSession, new HashSet<>(wsRequest.getMetricKeys())); - Table measuresByComponentUuidAndMetric = - searchMeasuresByComponentUuidAndMetric(dbSession, baseComponent, componentTreeQuery, components, metrics); + List metrics = searchMetrics(dbSession, new HashSet<>(withRemovedMetricAlias(ofNullable(wsRequest.getMetricKeys()).orElse(List.of())))); + Table measuresByComponentUuidAndMetric = searchMeasuresByComponentUuidAndMetric(dbSession, baseComponent, componentTreeQuery, + components, metrics); components = filterComponents(components, measuresByComponentUuidAndMetric, metrics, wsRequest); components = filterAuthorizedComponents(components); diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentTreeData.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentTreeData.java index 7ddd4bafb9f..36ee695d456 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentTreeData.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentTreeData.java @@ -75,7 +75,6 @@ class ComponentTreeData { return components; } - @CheckForNull int getComponentCount() { return componentCount; } diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentTreeRequest.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentTreeRequest.java index 0e0e4e0d50f..992968529b6 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentTreeRequest.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/ComponentTreeRequest.java @@ -36,7 +36,7 @@ class ComponentTreeRequest { private String metricSort; private Integer metricPeriodSort; private String metricSortFilter; - private List metricKeys; + private List metricKeys = List.of(); private Integer page; private Integer pageSize; @@ -139,7 +139,6 @@ class ComponentTreeRequest { return this; } - @CheckForNull public List getMetricKeys() { return metricKeys; } diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/MeasuresWsModule.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/MeasuresWsModule.java index 747b4079739..3c5e643e350 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/MeasuresWsModule.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/MeasuresWsModule.java @@ -33,7 +33,7 @@ public class MeasuresWsModule extends Module { } - public static String getDeprecatedMetrics() { + public static String getDeprecatedMetricsInSonarQube93() { return String.join(", ", "releasability_effort", "security_rating_effort", "reliability_rating_effort", "security_review_rating_effort", "maintainability_rating_effort", "last_change_on_maintainability_rating", "last_change_on_releasability_rating", "last_change_on_reliability_rating", "last_change_on_security_rating", "last_change_on_security_review_rating"); diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/MetricDtoToWsMetric.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/MetricDtoToWsMetric.java index 1511b81a6e1..9fafb5306a7 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/MetricDtoToWsMetric.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/MetricDtoToWsMetric.java @@ -23,6 +23,9 @@ import org.sonar.db.metric.MetricDto; import org.sonarqube.ws.Common.Metric; import static java.util.Optional.ofNullable; +import static org.sonar.db.metric.RemovedMetricConverter.REMOVED_METRIC; +import static org.sonar.db.metric.RemovedMetricConverter.REMOVED_METRIC_DESCRIPTION; +import static org.sonar.db.metric.RemovedMetricConverter.REMOVED_METRIC_SHORT_NAME; import static org.sonar.server.measure.ws.MeasureValueFormatter.formatNumericalValue; class MetricDtoToWsMetric { @@ -48,4 +51,13 @@ class MetricDtoToWsMetric { return metric.build(); } + + static Metric wontFixToAcceptedWsMetric(MetricDto metricDto) { + return metricDtoToWsMetric(metricDto) + .toBuilder() + .setKey(REMOVED_METRIC) + .setDescription(REMOVED_METRIC_DESCRIPTION) + .setName(REMOVED_METRIC_SHORT_NAME) + .build(); + } } diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/SearchAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/SearchAction.java index 740c38ef822..23ac6a65ce2 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/SearchAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/SearchAction.java @@ -20,6 +20,7 @@ package org.sonar.server.measure.ws; import com.google.common.collect.ImmutableSet; +import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; @@ -37,6 +38,7 @@ import org.sonar.db.DbSession; import org.sonar.db.component.ComponentDto; import org.sonar.db.measure.LiveMeasureDto; import org.sonar.db.metric.MetricDto; +import org.sonar.db.metric.RemovedMetricConverter; import org.sonar.server.user.UserSession; import org.sonarqube.ws.Measures.Measure; import org.sonarqube.ws.Measures.SearchWsResponse; @@ -50,6 +52,8 @@ import static org.sonar.api.resources.Qualifiers.APP; import static org.sonar.api.resources.Qualifiers.PROJECT; import static org.sonar.api.resources.Qualifiers.SUBVIEW; import static org.sonar.api.resources.Qualifiers.VIEW; +import static org.sonar.db.metric.RemovedMetricConverter.REMOVED_METRIC; +import static org.sonar.db.metric.RemovedMetricConverter.DEPRECATED_METRIC_REPLACEMENT; import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_METRIC_KEYS; import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_PROJECT_KEYS; import static org.sonar.server.exceptions.BadRequestException.checkRequest; @@ -77,17 +81,20 @@ public class SearchAction implements MeasuresWsAction { WebService.NewAction action = context.createAction("search") .setInternal(true) .setDescription("Search for project measures ordered by project names.
" + - "At most %d projects can be provided.
" + - "Returns the projects with the 'Browse' permission.", + "At most %d projects can be provided.
" + + "Returns the projects with the 'Browse' permission.", MAX_NB_PROJECTS) .setSince("6.2") .setResponseExample(getClass().getResource("search-example.json")) .setHandler(this) .setChangelog( + new Change("10.3", "The metric 'wont_fix_issues' is now deprecated in the response. Consume 'accepted_issues' instead."), + new Change("10.3", "The use of 'wont_fix_issues' value in 'metricKeys' param is now deprecated. Use 'accepted_issues' instead."), + new Change("10.3", "Added new accepted value for the 'metricKeys' param: 'accepted_issues'."), new Change("10.0", format("The use of the following metrics in 'metricKeys' parameter is not deprecated anymore: %s", - MeasuresWsModule.getDeprecatedMetrics())), + MeasuresWsModule.getDeprecatedMetricsInSonarQube93())), new Change("9.3", format("The use of the following metrics in 'metricKeys' parameter is deprecated: %s", - MeasuresWsModule.getDeprecatedMetrics()))); + MeasuresWsModule.getDeprecatedMetricsInSonarQube93()))); createMetricKeysParameter(action); @@ -151,10 +158,11 @@ public class SearchAction implements MeasuresWsAction { } private List searchMetrics() { - List dbMetrics = dbClient.metricDao().selectByKeys(dbSession, request.getMetricKeys()); + Collection metricKeysParamValue = RemovedMetricConverter.withRemovedMetricAlias(request.getMetricKeys()); + List dbMetrics = dbClient.metricDao().selectByKeys(dbSession, metricKeysParamValue); List metricKeys = dbMetrics.stream().map(MetricDto::getKey).toList(); - checkRequest(request.getMetricKeys().size() == dbMetrics.size(), "The following metrics are not found: %s", - String.join(", ", difference(request.getMetricKeys(), metricKeys))); + checkRequest(metricKeysParamValue.size() == dbMetrics.size(), "The following metrics are not found: %s", + String.join(", ", difference(metricKeysParamValue, metricKeys))); return dbMetrics; } @@ -190,17 +198,32 @@ public class SearchAction implements MeasuresWsAction { Function byComponentName = wsMeasure -> componentNamesByKey.get(wsMeasure.getComponent()); Measure.Builder measureBuilder = Measure.newBuilder(); - return measures.stream() - .map(dbMeasure -> { - updateMeasureBuilder(measureBuilder, dbMeasureToDbMetric.apply(dbMeasure), dbMeasure); - measureBuilder.setComponent(componentsByUuid.get(dbMeasure.getComponentUuid()).getKey()); - Measure measure = measureBuilder.build(); - measureBuilder.clear(); - return measure; - }) + List allMeasures = new ArrayList<>(); + for (LiveMeasureDto measure : measures) { + updateMeasureBuilder(measureBuilder, dbMeasureToDbMetric.apply(measure), measure); + measureBuilder.setComponent(componentsByUuid.get(measure.getComponentUuid()).getKey()); + Measure measureMsg = measureBuilder.build(); + addMeasureIncludingRenamedMetric(measureMsg, allMeasures, measureBuilder); + + measureBuilder.clear(); + } + return allMeasures.stream() .sorted(comparing(byMetricKey).thenComparing(byComponentName)) .toList(); } + + private void addMeasureIncludingRenamedMetric(Measure measureMsg, List allMeasures, Measure.Builder measureBuilder) { + if (measureBuilder.getMetric().equals(DEPRECATED_METRIC_REPLACEMENT)) { + if (request.getMetricKeys().contains(DEPRECATED_METRIC_REPLACEMENT)) { + allMeasures.add(measureMsg); + } + if (request.getMetricKeys().contains(REMOVED_METRIC)) { + allMeasures.add(measureBuilder.setMetric(REMOVED_METRIC).build()); + } + } else { + allMeasures.add(measureMsg); + } + } } private static class SearchRequest { diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/SearchHistoryAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/SearchHistoryAction.java index c3adcaf99b8..197430a0d96 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/SearchHistoryAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/SearchHistoryAction.java @@ -21,6 +21,7 @@ package org.sonar.server.measure.ws; import com.google.common.collect.Sets; import java.util.Date; +import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; @@ -46,6 +47,7 @@ import org.sonar.db.component.SnapshotQuery.SORT_ORDER; import org.sonar.db.measure.MeasureDto; import org.sonar.db.measure.PastMeasureQuery; import org.sonar.db.metric.MetricDto; +import org.sonar.db.metric.RemovedMetricConverter; import org.sonar.server.component.ComponentFinder; import org.sonar.server.user.UserSession; import org.sonar.server.ws.KeyExamples; @@ -93,10 +95,13 @@ public class SearchHistoryAction implements MeasuresWsAction { .setResponseExample(getClass().getResource("search_history-example.json")) .setSince("6.3") .setChangelog( + new Change("10.3", "The metric 'wont_fix_issues' is now deprecated in the response. Consume 'accepted_issues' instead."), + new Change("10.3", "The use of 'wont_fix_issues' value in 'metricKeys' param is now deprecated. Use 'accepted_issues' instead."), + new Change("10.3", "Added new accepted value for the 'metricKeys' param: 'accepted_issues'."), new Change("10.0", format("The use of the following metrics in 'metricKeys' parameter is not deprecated anymore: %s", - MeasuresWsModule.getDeprecatedMetrics())), + MeasuresWsModule.getDeprecatedMetricsInSonarQube93())), new Change("9.3", format("The use of the following metrics in 'metrics' parameter is deprecated: %s", - MeasuresWsModule.getDeprecatedMetrics())), + MeasuresWsModule.getDeprecatedMetricsInSonarQube93())), new Change("7.6", format("The use of module keys in parameter '%s' is deprecated", PARAM_COMPONENT))) .setHandler(this); @@ -165,7 +170,8 @@ public class SearchHistoryAction implements MeasuresWsAction { SearchHistoryResult result = new SearchHistoryResult(request.page, request.pageSize) .setComponent(component) .setAnalyses(searchAnalyses(dbSession, request, component)) - .setMetrics(searchMetrics(dbSession, request)); + .setMetrics(searchMetrics(dbSession, request)) + .setRequestedMetrics(request.getMetrics()); return result.setMeasures(searchMeasures(dbSession, request, result)); } }; @@ -203,9 +209,10 @@ public class SearchHistoryAction implements MeasuresWsAction { } private List searchMetrics(DbSession dbSession, SearchHistoryRequest request) { - List metrics = dbClient.metricDao().selectByKeys(dbSession, request.getMetrics()); - if (request.getMetrics().size() > metrics.size()) { - Set requestedMetrics = request.getMetrics().stream().collect(Collectors.toSet()); + List upToDateRequestedMetrics = RemovedMetricConverter.withRemovedMetricAlias(request.getMetrics()); + List metrics = dbClient.metricDao().selectByKeys(dbSession, upToDateRequestedMetrics); + if (upToDateRequestedMetrics.size() > metrics.size()) { + Set requestedMetrics = new HashSet<>(upToDateRequestedMetrics); Set foundMetrics = metrics.stream().map(MetricDto::getKey).collect(Collectors.toSet()); Set unfoundMetrics = Sets.difference(requestedMetrics, foundMetrics).immutableCopy(); diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/SearchHistoryResponseFactory.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/SearchHistoryResponseFactory.java index a5764f6459e..02454a86537 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/SearchHistoryResponseFactory.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/SearchHistoryResponseFactory.java @@ -35,6 +35,7 @@ import org.sonarqube.ws.Measures.SearchHistoryResponse.HistoryMeasure; import org.sonarqube.ws.Measures.SearchHistoryResponse.HistoryValue; import static org.sonar.api.utils.DateUtils.formatDateTime; +import static org.sonar.server.measure.ws.ComponentResponseCommon.addMetricToSearchHistoryResponseIncludingRenamedMetric; import static org.sonar.server.measure.ws.MeasureValueFormatter.formatMeasureValue; class SearchHistoryResponseFactory { @@ -67,23 +68,17 @@ class SearchHistoryResponseFactory { result.getMeasures().forEach(m -> measuresByMetricByAnalysis.put(metricsByUuid.get(m.getMetricUuid()), analysesByUuid.get(m.getAnalysisUuid()), m)); return response -> { - result.getMetrics().stream() - .map(clearMetric()) - .map(addMetric()) - .map(metric -> addValues(measuresByMetricByAnalysis.row(metric)).apply(metric)) - .forEach(metric -> response.addMeasures(measure)); + for (MetricDto metric : result.getMetrics()) { + measure.setMetric(metric.getKey()); + addValues(measuresByMetricByAnalysis.row(metric)).apply(metric); + addMetricToSearchHistoryResponseIncludingRenamedMetric(response, result.getRequestedMetrics(), measure); + measure.clear(); + } return response; }; } - private UnaryOperator addMetric() { - return metric -> { - measure.setMetric(metric.getKey()); - return metric; - }; - } - private UnaryOperator addValues(Map measuresByAnalysis) { return metric -> { result.getAnalyses().stream() diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/SearchHistoryResult.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/SearchHistoryResult.java index bc42f1d9477..8d5f99cdde3 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/SearchHistoryResult.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/measure/ws/SearchHistoryResult.java @@ -46,6 +46,7 @@ public class SearchHistoryResult { private List measures; private Common.Paging paging; private ComponentDto component; + private List requestedMetrics; public SearchHistoryResult(int page, int pageSize) { this.page = page; @@ -138,4 +139,13 @@ public class SearchHistoryResult { Common.Paging getPaging() { return requireNonNull(paging); } + + public SearchHistoryResult setRequestedMetrics(List requestedMetrics) { + this.requestedMetrics = requestedMetrics; + return this; + } + + public List getRequestedMetrics() { + return requestedMetrics; + } } diff --git a/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/measure/ws/component_tree-example.json b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/measure/ws/component_tree-example.json index 72d1c1669f8..6eaba1b4eb2 100644 --- a/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/measure/ws/component_tree-example.json +++ b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/measure/ws/component_tree-example.json @@ -15,6 +15,10 @@ "value": "255" } }, + { + "metric": "accepted_issues", + "value": "10" + }, { "metric": "complexity", "value": "42" @@ -43,6 +47,10 @@ "metric": "complexity", "value": "12" }, + { + "metric": "accepted_issues", + "value": "10" + }, { "metric": "ncloc", "value": "114" @@ -80,6 +88,10 @@ "metric": "complexity", "value": "35" }, + { + "metric": "accepted_issues", + "value": "10" + }, { "metric": "ncloc", "value": "217" @@ -118,6 +130,16 @@ "qualitative": true, "hidden": false, "bestValue": "0" + }, + { + "key": "accepted_issues", + "name": "Accepted Issues", + "description": "Accepted issues", + "domain": "Issues", + "type": "INT", + "higherValuesAreBetter": false, + "qualitative": false, + "hidden": false } ], "period": { diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/measure/live/MeasureUpdateFormulaFactoryImplTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/measure/live/MeasureUpdateFormulaFactoryImplTest.java index 6b40a6a72f4..38494be3b38 100644 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/measure/live/MeasureUpdateFormulaFactoryImplTest.java +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/measure/live/MeasureUpdateFormulaFactoryImplTest.java @@ -282,7 +282,7 @@ public class MeasureUpdateFormulaFactoryImplTest { public void count_resolved() { withNoIssues() .assertThatValueIs(CoreMetrics.FALSE_POSITIVE_ISSUES, 0) - .assertThatValueIs(CoreMetrics.WONT_FIX_ISSUES, 0); + .assertThatValueIs(CoreMetrics.ACCEPTED_ISSUES, 0); with( newResolvedGroup(Issue.RESOLUTION_FIXED, Issue.STATUS_RESOLVED).setCount(3), @@ -296,7 +296,7 @@ public class MeasureUpdateFormulaFactoryImplTest { newGroup(RuleType.VULNERABILITY).setCount(17), newGroup(RuleType.BUG).setCount(19)) .assertThatValueIs(CoreMetrics.FALSE_POSITIVE_ISSUES, 5) - .assertThatValueIs(CoreMetrics.WONT_FIX_ISSUES, 7 + 11); + .assertThatValueIs(CoreMetrics.ACCEPTED_ISSUES, 7 + 11); } @Test diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index f22fa371396..86084892ad5 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -3212,7 +3212,8 @@ metric.vulnerabilities.description=Vulnerabilities metric.vulnerabilities.name=Vulnerabilities metric.wont_fix_issues.description=Won't fix issues metric.wont_fix_issues.name=Won't Fix Issues - +metric.accepted_issues.description=Accepted issues +metric.accepted_issues.name=Accepted Issues #------------------------------------------------------------------------------ # -- 2.39.5