From c63283fce767013e11ca6bf1c26170a11c0d48da Mon Sep 17 00:00:00 2001 From: OrlovAlexander Date: Mon, 14 Oct 2024 09:03:02 +0200 Subject: [PATCH] SONAR-23250 Support impacts in QP export-import --- .../QualityProfileExportDaoIT.java | 2 + .../db/qualityprofile/ExportRuleDto.java | 5 ++ .../QualityProfileExportMapper.xml | 2 + .../QualityProfileDbTester.java | 2 +- .../server/qualityprofile/RuleActivation.java | 8 +- .../QProfileBackuperImplIT.java | 79 ++++++++++++++++++- .../server/qualityprofile/ImportedRule.java | 13 ++- .../qualityprofile/QProfileBackuperImpl.java | 10 ++- .../server/qualityprofile/QProfileParser.java | 43 ++++++++++ .../server/qualityprofile/QProfileUtils.java | 46 +++++++++++ .../qualityprofile/builtin/RuleActivator.java | 6 +- .../qualityprofile/QProfileParserTest.java | 66 ++++++++++++++++ 12 files changed, 274 insertions(+), 8 deletions(-) create mode 100644 server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileUtils.java diff --git a/server/sonar-db-dao/src/it/java/org/sonar/db/qualityprofile/QualityProfileExportDaoIT.java b/server/sonar-db-dao/src/it/java/org/sonar/db/qualityprofile/QualityProfileExportDaoIT.java index d89759ca42a..75e3e435b93 100644 --- a/server/sonar-db-dao/src/it/java/org/sonar/db/qualityprofile/QualityProfileExportDaoIT.java +++ b/server/sonar-db-dao/src/it/java/org/sonar/db/qualityprofile/QualityProfileExportDaoIT.java @@ -104,6 +104,7 @@ class QualityProfileExportDaoIT { ActiveRuleDto activeCustomRule = activeRules.stream().filter(activeRuleDto -> activeRuleDto.getRuleKey().equals(customRule.getKey())).findFirst().get(); assertThat(exportCustomRuleDto.getSeverityString()).isEqualTo(activeCustomRule.getSeverityString()); + assertThat(exportCustomRuleDto.getImpacts()).isEqualTo(activeCustomRule.getImpactsString()); // verify regular rule ExportRuleDto exportRuleDto = results.stream().filter(regularRule -> !regularRule.isCustomRule()).findFirst().get(); @@ -119,6 +120,7 @@ class QualityProfileExportDaoIT { ActiveRuleDto activeRule = activeRules.stream().filter(activeRuleDto -> activeRuleDto.getRuleKey().equals(rule.getKey())).findFirst().get(); assertThat(exportRuleDto.getSeverityString()).isEqualTo(activeRule.getSeverityString()); + assertThat(exportRuleDto.getImpacts()).isEqualTo(activeRule.getImpactsString()); } @Test diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/ExportRuleDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/ExportRuleDto.java index c6a44bc5e3c..09fd58daeaa 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/ExportRuleDto.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/ExportRuleDto.java @@ -38,6 +38,7 @@ public class ExportRuleDto { private String extendedDescription = null; private String template = null; private Integer severity = null; + private String impacts = null; private Integer type = null; private Boolean prioritizedRule; private Set tags = new HashSet<>(); @@ -77,6 +78,10 @@ public class ExportRuleDto { return prioritizedRule; } + public String getImpacts() { + return impacts; + } + public Set getTags() { return tags; } diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/qualityprofile/QualityProfileExportMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/qualityprofile/QualityProfileExportMapper.xml index 503f81160e2..5406e85f3f1 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/qualityprofile/QualityProfileExportMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/qualityprofile/QualityProfileExportMapper.xml @@ -19,6 +19,7 @@ a.uuid as "activeRuleUuid", a.failure_level as "severity", + a.impacts as "impacts", a.prioritized_rule as "prioritizedRule", r.plugin_rule_key as "rule", r.plugin_name as "repository", @@ -42,6 +43,7 @@ + diff --git a/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/qualityprofile/QualityProfileDbTester.java b/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/qualityprofile/QualityProfileDbTester.java index 0863a4a6de5..40072e20d6b 100644 --- a/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/qualityprofile/QualityProfileDbTester.java +++ b/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/qualityprofile/QualityProfileDbTester.java @@ -27,7 +27,6 @@ import java.util.function.Consumer; import org.sonar.api.rule.Severity; import org.sonar.core.util.Uuids; import org.sonar.db.DbClient; -import org.sonar.db.DbSession; import org.sonar.db.DbTester; import org.sonar.db.project.ProjectDto; import org.sonar.db.rule.RuleDto; @@ -96,6 +95,7 @@ public class QualityProfileDbTester { public ActiveRuleDto activateRule(QProfileDto profile, RuleDto rule, Consumer consumer) { ActiveRuleDto activeRule = createFor(profile, rule) .setSeverity(Severity.ALL.get(random.nextInt(Severity.ALL.size()))) + .setImpacts(rule.getDefaultImpactsMap()) .setPrioritizedRule(random.nextBoolean()) .setCreatedAt(random.nextLong(Long.MAX_VALUE)) .setUpdatedAt(random.nextLong(Long.MAX_VALUE)); diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/qualityprofile/RuleActivation.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/qualityprofile/RuleActivation.java index a155d284fb9..61c94b5a7b5 100644 --- a/server/sonar-webserver-api/src/main/java/org/sonar/server/qualityprofile/RuleActivation.java +++ b/server/sonar-webserver-api/src/main/java/org/sonar/server/qualityprofile/RuleActivation.java @@ -68,9 +68,11 @@ public class RuleActivation { return new RuleActivation(ruleUuid, false, severity, prioritizedRule, parameters, Map.of()); } - public static RuleActivation create(String ruleUuid, @Nullable String severity, Map impactSeverities, - @Nullable Boolean prioritizedRule, - @Nullable Map parameters) { + public static RuleActivation create(String ruleUuid, @Nullable String severity, @Nullable Map impactSeverities, @Nullable Boolean prioritizedRule, @Nullable Map parameters) { + if (impactSeverities == null) { + impactSeverities = Map.of(); + } return new RuleActivation(ruleUuid, false, severity, prioritizedRule, parameters, impactSeverities); } diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualityprofile/QProfileBackuperImplIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualityprofile/QProfileBackuperImplIT.java index 71f271bd7ae..286ca058acb 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualityprofile/QProfileBackuperImplIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualityprofile/QProfileBackuperImplIT.java @@ -27,6 +27,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.function.Consumer; import java.util.stream.Stream; import javax.annotation.Nullable; @@ -49,8 +50,8 @@ import org.sonar.db.qualityprofile.QProfileDto; import org.sonar.db.qualityprofile.QualityProfileTesting; import org.sonar.db.rule.RuleDto; import org.sonar.db.rule.RuleParamDto; -import org.sonar.server.qualityprofile.builtin.QProfileName; import org.sonar.server.common.rule.RuleCreator; +import org.sonar.server.qualityprofile.builtin.QProfileName; import static java.nio.charset.StandardCharsets.UTF_8; import static org.assertj.core.api.Assertions.assertThat; @@ -60,6 +61,10 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static org.sonar.api.issue.impact.Severity.BLOCKER; +import static org.sonar.api.issue.impact.Severity.INFO; +import static org.sonar.api.issue.impact.SoftwareQuality.MAINTAINABILITY; +import static org.sonar.api.issue.impact.SoftwareQuality.SECURITY; import static org.sonar.db.rule.RuleDescriptionSectionDto.createDefaultRuleDescriptionSection; import static org.sonar.db.rule.RuleTesting.newRule; import static org.sonar.db.rule.RuleTesting.newRuleWithoutDescriptionSection; @@ -101,6 +106,12 @@ class QProfileBackuperImplIT { "" + rule.getRuleKey() + "" + "" + RuleType.valueOf(rule.getType()).name() + "" + "" + activeRule.getSeverityString() + "" + + "" + + "" + + "MAINTAINABILITY" + + "HIGH" + + "" + + "" + "" + "" + "" + @@ -125,6 +136,12 @@ class QProfileBackuperImplIT { "" + rule.getRuleKey() + "" + "" + RuleType.valueOf(rule.getType()).name() + "" + "" + activeRule.getSeverityString() + "" + + "" + + "" + + "MAINTAINABILITY" + + "HIGH" + + "" + + "" + "true" + "" + "" + @@ -148,6 +165,12 @@ class QProfileBackuperImplIT { "" + rule.getRuleKey() + "" + "" + RuleType.valueOf(rule.getType()).name() + "" + "" + activeRule.getSeverityString() + "" + + "" + + "" + + "MAINTAINABILITY" + + "HIGH" + + "" + + "" + "" + "" + param.getName() + "" + "20" + @@ -195,6 +218,12 @@ class QProfileBackuperImplIT { "" + rule.getKey().rule() + "" + "" + RuleType.valueOf(rule.getType()) + "" + "" + activeRule.getSeverityString() + "" + + "" + + "" + + "MAINTAINABILITY" + + "HIGH" + + "" + + "" + "" + rule.getName() + "" + "" + templateRule.getKey().rule() + "" + "" + rule.getDefaultRuleDescriptionSection().getContent() + "" + @@ -225,6 +254,12 @@ class QProfileBackuperImplIT { "" + rule.getKey().rule() + "" + "" + RuleType.valueOf(rule.getType()) + "" + "" + activeRule.getSeverityString() + "" + + "" + + "" + + "MAINTAINABILITY" + + "HIGH" + + "" + + "" + "" + "" + param.getName() + "" + "20" + @@ -458,6 +493,48 @@ class QProfileBackuperImplIT { assertThat(activation.getParameter("bar")).isEqualTo("baz"); } + @Test + void restore_should_override_impacts(){ + String ruleUuid = db.rules().insert(RuleKey.of("sonarjs", "s001")).getUuid(); + + Reader backup = new StringReader("" + + "foo" + + "js" + + "" + + "" + + "sonarjs" + + "s001" + + "BLOCKER" + + "" + + "" + + "MAINTAINABILITY" + + "BLOCKER" + + "" + + "" + + "SECURITY" + + "INFO" + + "" + + "" + + "" + + "barbaz" + + "" + + "" + + "" + + ""); + + underTest.restore(db.getSession(), backup, (String) null); + + assertThat(reset.calledActivations).hasSize(1); + RuleActivation activation = reset.calledActivations.get(0); + assertThat(activation.getSeverity()).isEqualTo("BLOCKER"); + assertThat(activation.getRuleUuid()).isEqualTo(ruleUuid); + assertThat(activation.getImpactSeverities()).isEqualTo(Map.of(MAINTAINABILITY, BLOCKER, SECURITY, INFO)); + db.getDbClient().activeRuleDao().selectByRuleUuid(db.getSession(), ruleUuid).forEach(ar -> { + assertThat(ar.getSeverityString()).isEqualTo("BLOCKER"); + assertThat(ar.getImpactsString()).isEqualTo("{\"MAINTAINABILITY\":\"BLOCKER\",\"SECURITY\":\"INFO\"}"); + }); + } + @Test void copy_profile() { RuleDto rule = createRule(); diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ImportedRule.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ImportedRule.java index 410fe724eaf..544a41022a4 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ImportedRule.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ImportedRule.java @@ -20,7 +20,7 @@ package org.sonar.server.qualityprofile; import java.util.Map; - +import org.sonar.api.issue.impact.SoftwareQuality; import org.sonar.api.rule.RuleKey; class ImportedRule { @@ -32,9 +32,11 @@ class ImportedRule { private String name = null; private String type = null; private String severity = null; + private Map impacts = Map.of(); private Boolean prioritizedRule = false; private String description = null; private Map parameters = null; + public Map getParameters() { return parameters; } @@ -59,6 +61,10 @@ class ImportedRule { return severity; } + public Map getImpacts() { + return impacts; + } + public Boolean getPrioritizedRule() { return prioritizedRule; } @@ -77,6 +83,11 @@ class ImportedRule { return this; } + ImportedRule setImpacts(Map impacts) { + this.impacts = impacts; + return this; + } + public ImportedRule setPrioritizedRule(Boolean prioritizedRule) { this.prioritizedRule = prioritizedRule; return this; diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileBackuperImpl.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileBackuperImpl.java index 4c709b0a285..e6f8b9b78e4 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileBackuperImpl.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileBackuperImpl.java @@ -50,6 +50,7 @@ import org.sonar.server.qualityprofile.builtin.QProfileName; import static com.google.common.base.Preconditions.checkArgument; import static java.util.function.Function.identity; import static java.util.stream.Collectors.toSet; +import static org.sonar.server.qualityprofile.QProfileUtils.parseImpactsToMap; @ServerSide public class QProfileBackuperImpl implements QProfileBackuper { @@ -95,6 +96,7 @@ public class QProfileBackuperImpl implements QProfileBackuper { importedRule.setRepository(ruleKey.repository()); importedRule.setKey(ruleKey.rule()); importedRule.setSeverity(exportRuleDto.getSeverityString()); + importedRule.setImpacts(exportRuleDto.getImpacts() != null ? parseImpactsToMap(exportRuleDto.getImpacts()) : Map.of()); if (exportRuleDto.isCustomRule()) { importedRule.setTemplate(exportRuleDto.getTemplateRuleKey().rule()); importedRule.setDescription(exportRuleDto.getDescriptionOrThrow()); @@ -213,6 +215,7 @@ public class QProfileBackuperImpl implements QProfileBackuper { return NewCustomRule.createForCustomRule(r.getRuleKey(), r.getTemplateKey()) .setName(r.getName()) .setSeverity(r.getSeverity()) + .setImpacts(r.getImpacts().entrySet().stream().map(i -> new NewCustomRule.Impact(i.getKey(), i.getValue())).toList()) .setStatus(RuleStatus.READY) .setPreventReactivation(true) .setType(RuleType.valueOf(r.getType())) @@ -228,7 +231,12 @@ public class QProfileBackuperImpl implements QProfileBackuper { if (ruleDto == null) { continue; } - activatedRule.add(RuleActivation.create(ruleDto.getUuid(), r.getSeverity(), r.getPrioritizedRule(), r.getParameters())); + activatedRule.add(RuleActivation.create( + ruleDto.getUuid(), + r.getSeverity(), + r.getImpacts(), + r.getPrioritizedRule(), + r.getParameters())); } return activatedRule; } diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileParser.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileParser.java index f0c1d16411a..e29e02bf6ca 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileParser.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileParser.java @@ -22,6 +22,7 @@ package org.sonar.server.qualityprofile; import java.io.Reader; import java.io.Writer; import java.util.ArrayList; +import java.util.EnumMap; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -36,6 +37,8 @@ import org.apache.commons.lang3.StringUtils; import org.codehaus.staxmate.SMInputFactory; import org.codehaus.staxmate.in.SMHierarchicCursor; import org.codehaus.staxmate.in.SMInputCursor; +import org.sonar.api.issue.impact.Severity; +import org.sonar.api.issue.impact.SoftwareQuality; import org.sonar.api.rule.RuleKey; import org.sonar.api.server.ServerSide; import org.sonar.api.utils.text.XmlWriter; @@ -43,6 +46,8 @@ import org.sonar.db.qualityprofile.ExportRuleDto; import org.sonar.db.qualityprofile.ExportRuleParamDto; import org.sonar.db.qualityprofile.QProfileDto; +import static org.sonar.server.qualityprofile.QProfileUtils.parseImpactsToMap; + @ServerSide public class QProfileParser { private static final String ATTRIBUTE_PROFILE = "profile"; @@ -53,6 +58,10 @@ public class QProfileParser { private static final String ATTRIBUTE_REPOSITORY_KEY = "repositoryKey"; private static final String ATTRIBUTE_KEY = "key"; private static final String ATTRIBUTE_PRIORITY = "priority"; + private static final String ATTRIBUTE_IMPACTS = "impacts"; + private static final String ATTRIBUTE_IMPACT = "impact"; + private static final String ATTRIBUTE_SEVERITY = "severity"; + private static final String ATTRIBUTE_SOFTWARE_QUALITY = "softwareQuality"; private static final String ATTRIBUTE_PRIORITIZED_RULE = "prioritizedRule"; private static final String ATTRIBUTE_TEMPLATE_KEY = "templateKey"; private static final String ATTRIBUTE_TYPE = "type"; @@ -76,6 +85,16 @@ public class QProfileParser { xml.prop(ATTRIBUTE_KEY, ruleToExport.getRuleKey().rule()); xml.prop(ATTRIBUTE_TYPE, ruleToExport.getRuleType().name()); xml.prop(ATTRIBUTE_PRIORITY, ruleToExport.getSeverityString()); + if (StringUtils.isNotEmpty(ruleToExport.getImpacts())) { + xml.begin(ATTRIBUTE_IMPACTS); + parseImpactsToMap(ruleToExport.getImpacts()).forEach((quality, severity) -> { + xml.begin(ATTRIBUTE_IMPACT); + xml.prop(ATTRIBUTE_SOFTWARE_QUALITY, quality.name()); + xml.prop(ATTRIBUTE_SEVERITY, severity.name()); + xml.end(); + }); + xml.end(); + } if (Boolean.TRUE.equals(ruleToExport.getPrioritizedRule())) { xml.prop(ATTRIBUTE_PRIORITIZED_RULE, ruleToExport.getPrioritizedRule()); } @@ -181,6 +200,11 @@ public class QProfileParser { rule.setDescription(StringUtils.trim(ruleCursor.collectDescendantText(false))); } else if (StringUtils.equals(ATTRIBUTE_PRIORITY, nodeName)) { rule.setSeverity(StringUtils.trim(ruleCursor.collectDescendantText(false))); + } else if (StringUtils.equals(ATTRIBUTE_IMPACTS, nodeName)) { + SMInputCursor impactsCursor = ruleCursor.childElementCursor(ATTRIBUTE_IMPACT); + Map impacts = new EnumMap<>(SoftwareQuality.class); + readImpacts(impactsCursor, impacts); + rule.setImpacts(impacts); } else if (StringUtils.equals(ATTRIBUTE_PRIORITIZED_RULE, nodeName)) { rule.setPrioritizedRule(Boolean.valueOf(StringUtils.trim(ruleCursor.collectDescendantText(false)))); } else if (StringUtils.equals(ATTRIBUTE_PARAMETERS, nodeName)) { @@ -209,4 +233,23 @@ public class QProfileParser { } } } + + private static void readImpacts(SMInputCursor impactsCursor, Map impacts) throws XMLStreamException { + while (impactsCursor.getNext() != null) { + SMInputCursor impactCursor = impactsCursor.childElementCursor(); + SoftwareQuality softwareQuality = null; + Severity severity = null; + while (impactCursor.getNext() != null) { + String nodeName = impactCursor.getLocalName(); + if (StringUtils.equals(ATTRIBUTE_SOFTWARE_QUALITY, nodeName)) { + softwareQuality = SoftwareQuality.valueOf(StringUtils.trim(impactCursor.collectDescendantText(false))); + } else if (StringUtils.equals(ATTRIBUTE_SEVERITY, nodeName)) { + severity = Severity.valueOf(StringUtils.trim(impactCursor.collectDescendantText(false))); + } + } + if (softwareQuality != null && severity != null) { + impacts.put(softwareQuality, severity); + } + } + } } diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileUtils.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileUtils.java new file mode 100644 index 00000000000..a78bc0cc9bc --- /dev/null +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/QProfileUtils.java @@ -0,0 +1,46 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 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.qualityprofile; + +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.EnumMap; +import java.util.Map; +import org.sonar.api.issue.impact.Severity; +import org.sonar.api.issue.impact.SoftwareQuality; + +public class QProfileUtils { + + private QProfileUtils() { + } + + public static Map parseImpactsToMap(String impacts) { + ObjectMapper mapper = new ObjectMapper(); + Map parsedMap = new EnumMap<>(SoftwareQuality.class); + try { + Map stringMap = mapper.readValue(impacts, Map.class); + for (Map.Entry entry : stringMap.entrySet()) { + parsedMap.put(SoftwareQuality.valueOf(entry.getKey()), Severity.valueOf(entry.getValue())); + } + } catch (Exception e) { + throw new IllegalArgumentException("The quality profile cannot be restored as it contains invalid impacts: " + impacts); + } + return parsedMap; + } +} diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/builtin/RuleActivator.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/builtin/RuleActivator.java index 98d6bf350b9..146b0d5c69c 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/builtin/RuleActivator.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/builtin/RuleActivator.java @@ -259,11 +259,15 @@ public class RuleActivator { @Nullable ActiveRuleWrapper parentActiveRule) { String requestSeverity = request.getSeverity(); if (requestSeverity != null) { + Map impactSeverities = request.getImpactSeverities(); + if (impactSeverities != null && !impactSeverities.isEmpty()) { + return new SeverityConfiguration(requestSeverity, impactSeverities); + } // When standard severity is requested to be overridden, we translate it to the impact to override return new SeverityConfiguration(requestSeverity, QProfileImpactSeverityMapper.mapImpactSeverities(requestSeverity, ruleDto.getDefaultImpactsMap(), ruleDto.getEnumType())); } else if (!request.getImpactSeverities().isEmpty()) { - // If an impact is request to be overridden, we translat it to the standard severity + // If an impact is request to be overridden, we translate it to the standard severity return new SeverityConfiguration( QProfileImpactSeverityMapper.mapSeverity(request.getImpactSeverities(), ruleDto.getEnumType(), ruleDto.getSeverityString()), request.getImpactSeverities()); diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/QProfileParserTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/QProfileParserTest.java index 084bed6dad2..accbcfa7e12 100644 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/QProfileParserTest.java +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/qualityprofile/QProfileParserTest.java @@ -21,9 +21,15 @@ package org.sonar.server.qualityprofile; import java.io.Reader; import java.io.StringReader; +import java.util.Map; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; +import static org.sonar.api.issue.impact.Severity.BLOCKER; +import static org.sonar.api.issue.impact.Severity.INFO; +import static org.sonar.api.issue.impact.SoftwareQuality.MAINTAINABILITY; +import static org.sonar.api.issue.impact.SoftwareQuality.SECURITY; class QProfileParserTest { @@ -40,6 +46,16 @@ class QProfileParserTest { s001 CODE_SMELL CRITICAL + + + MAINTAINABILITY + BLOCKER + + + SECURITY + INFO + + custom rule name rule_mc8 custom rule description @@ -72,6 +88,7 @@ class QProfileParserTest { CODE_SMELL CRITICAL false + custom rule name rule_mc8 custom rule description @@ -90,9 +107,58 @@ class QProfileParserTest { var importedRule = importedQProfile.getRules().get(0); assertThat(importedRule.getDescription()).isEqualTo("custom rule description"); assertThat(importedRule.getPrioritizedRule()).isFalse(); + assertThat(importedRule.getImpacts()).isEqualTo(Map.of(MAINTAINABILITY, BLOCKER, SECURITY, INFO)); importedRule = importedQProfile.getRules().get(1); assertThat(importedRule.getPrioritizedRule()).isTrue(); + assertThat(importedRule.getImpacts()).isEmpty(); importedRule = importedQProfile.getRules().get(2); assertThat(importedRule.getPrioritizedRule()).isFalse(); + assertThat(importedRule.getImpacts()).isEmpty(); + } + + @Test + //test that exception is thrown if impacts has incorrect arguments + void readXml_whenImpactsIncorrectArguments_shouldThrow() { + Reader backup = new StringReader(""" + + + custom rule + js + + + sonarjs + s001 + CODE_SMELL + CRITICAL + + + MAINTAINABILITY + BLOCKER + + + SECURITY + INFO + + + RELIABILITY + MAJOR + + + custom rule name + rule_mc8 + custom rule description + + + bar + baz + + + + + """); + var parser = new QProfileParser(); + assertThatThrownBy(() -> parser.readXml(backup)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("No enum constant org.sonar.api.issue.impact.Severity.MAJOR"); } } -- 2.39.5