From: Dejan Milisavljevic <130993898+dejan-milisavljevic-sonarsource@users.noreply.github.com> Date: Mon, 16 Sep 2024 08:15:16 +0000 (+0200) Subject: SONAR-22912 Backup and restore prioritizedRule flag in Quality Profiles X-Git-Tag: 10.7.0.96327~98 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=742749c57fe65a1866305f6c004836b0eeb1bb0c;p=sonarqube.git SONAR-22912 Backup and restore prioritizedRule flag in Quality Profiles --- 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 843e08f9c33..c6a44bc5e3c 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 @@ -24,6 +24,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Objects; import java.util.Set; +import javax.annotation.CheckForNull; import org.sonar.api.rule.RuleKey; import org.sonar.api.rules.RuleType; import org.sonar.db.rule.SeverityUtil; @@ -38,6 +39,7 @@ public class ExportRuleDto { private String template = null; private Integer severity = null; private Integer type = null; + private Boolean prioritizedRule; private Set tags = new HashSet<>(); private List params = null; @@ -70,6 +72,11 @@ public class ExportRuleDto { return RuleType.valueOf(type); } + @CheckForNull + public Boolean getPrioritizedRule() { + return prioritizedRule; + } + 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 48ee62fbcef..503f81160e2 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.prioritized_rule as "prioritizedRule", r.plugin_rule_key as "rule", r.plugin_name as "repository", r.priority as "defaultSeverity", @@ -41,6 +42,7 @@ + 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 bc094c92cd2..f156feab153 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,9 +27,14 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.function.Consumer; +import java.util.stream.Stream; import javax.annotation.Nullable; -import org.junit.Rule; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.sonar.api.impl.utils.AlwaysIncreasingSystem2; import org.sonar.api.rule.RuleKey; import org.sonar.api.rule.RuleStatus; @@ -59,7 +64,7 @@ import static org.sonar.db.rule.RuleDescriptionSectionDto.createDefaultRuleDescr import static org.sonar.db.rule.RuleTesting.newRule; import static org.sonar.db.rule.RuleTesting.newRuleWithoutDescriptionSection; -public class QProfileBackuperImplIT { +class QProfileBackuperImplIT { private static final String EMPTY_BACKUP = "" + "foo" + @@ -69,8 +74,8 @@ public class QProfileBackuperImplIT { private final System2 system2 = new AlwaysIncreasingSystem2(); - @Rule - public DbTester db = DbTester.create(system2); + @RegisterExtension + private final DbTester db = DbTester.create(system2); private final DummyReset reset = new DummyReset(); private final QProfileFactory profileFactory = new DummyProfileFactory(); @@ -79,10 +84,10 @@ public class QProfileBackuperImplIT { private final QProfileBackuper underTest = new QProfileBackuperImpl(db.getDbClient(), reset, profileFactory, ruleCreator, new QProfileParser()); @Test - public void backup_generates_xml_file() { + void backup_generates_xml_file() { RuleDto rule = createRule(); QProfileDto profile = createProfile(rule.getLanguage()); - ActiveRuleDto activeRule = activate(profile, rule); + ActiveRuleDto activeRule = activate(profile, rule, ar -> ar.setPrioritizedRule(false)); StringWriter writer = new StringWriter(); underTest.backup(db.getSession(), profile, writer); @@ -103,7 +108,32 @@ public class QProfileBackuperImplIT { } @Test - public void backup_rules_having_parameters() { + void backup_prioritized_rule() { + RuleDto rule = createRule(); + QProfileDto profile = createProfile(rule.getLanguage()); + ActiveRuleDto activeRule = activate(profile, rule, ar -> ar.setPrioritizedRule(true)); + + StringWriter writer = new StringWriter(); + underTest.backup(db.getSession(), profile, writer); + + assertThat(writer).hasToString("" + + "" + profile.getName() + "" + + "" + profile.getLanguage() + "" + + "" + + "" + + "" + rule.getRepositoryKey() + "" + + "" + rule.getRuleKey() + "" + + "" + RuleType.valueOf(rule.getType()).name() + "" + + "" + activeRule.getSeverityString() + "" + + "true" + + "" + + "" + + "" + + ""); + } + + @Test + void backup_rules_having_parameters() { RuleDto rule = createRule(); RuleParamDto param = db.rules().insertRuleParam(rule); QProfileDto profile = createProfile(rule.getLanguage()); @@ -126,7 +156,7 @@ public class QProfileBackuperImplIT { } @Test - public void backup_empty_profile() { + void backup_empty_profile() { RuleDto rule = createRule(); QProfileDto profile = createProfile(rule.getLanguage()); @@ -141,7 +171,7 @@ public class QProfileBackuperImplIT { } @Test - public void backup_custom_rules_with_params() { + void backup_custom_rules_with_params() { RuleDto templateRule = db.rules().insert(ruleDefinitionDto -> ruleDefinitionDto .setIsTemplate(true)); RuleDto rule = db.rules().insert( @@ -176,7 +206,7 @@ public class QProfileBackuperImplIT { } @Test - public void backup_custom_rules_without_description_section() { + void backup_custom_rules_without_description_section() { var rule = newRuleWithoutDescriptionSection(); db.rules().insert(rule); RuleParamDto param = db.rules().insertRuleParam(rule); @@ -203,7 +233,7 @@ public class QProfileBackuperImplIT { } @Test - public void restore_backup_on_the_profile_specified_in_backup() { + void restore_backup_on_the_profile_specified_in_backup() { Reader backup = new StringReader(EMPTY_BACKUP); QProfileRestoreSummary summary = underTest.restore(db.getSession(), backup, (String) null); @@ -216,7 +246,7 @@ public class QProfileBackuperImplIT { } @Test - public void restore_detects_deprecated_rule_keys() { + void restore_detects_deprecated_rule_keys() { String ruleUuid = db.rules().insert(RuleKey.of("sonarjs", "s001")).getUuid(); db.rules().insertDeprecatedKey(c -> c.setRuleUuid(ruleUuid).setOldRuleKey("oldkey").setOldRepositoryKey("oldrepo")); @@ -245,7 +275,7 @@ public class QProfileBackuperImplIT { } @Test - public void restore_ignores_deprecated_rule_keys_if_new_key_is_already_present() { + void restore_ignores_deprecated_rule_keys_if_new_key_is_already_present() { String ruleUuid = db.rules().insert(RuleKey.of("sonarjs", "s001")).getUuid(); db.rules().insertDeprecatedKey(c -> c.setRuleUuid(ruleUuid).setOldRuleKey("oldkey").setOldRepositoryKey("oldrepo")); @@ -282,7 +312,7 @@ public class QProfileBackuperImplIT { } @Test - public void restore_backup_on_profile_having_different_name() { + void restore_backup_on_profile_having_different_name() { Reader backup = new StringReader(EMPTY_BACKUP); QProfileRestoreSummary summary = underTest.restore(db.getSession(), backup, "bar"); @@ -295,7 +325,7 @@ public class QProfileBackuperImplIT { } @Test - public void restore_resets_the_activated_rules() { + void restore_resets_the_activated_rules() { String ruleUuid = db.rules().insert(RuleKey.of("sonarjs", "s001")).getUuid(); Reader backup = new StringReader("" + "foo" + @@ -305,6 +335,7 @@ public class QProfileBackuperImplIT { "sonarjs" + "s001" + "BLOCKER" + + "true" + "" + "barbaz" + "" + @@ -317,12 +348,55 @@ public class QProfileBackuperImplIT { assertThat(reset.calledActivations).hasSize(1); RuleActivation activation = reset.calledActivations.get(0); assertThat(activation.getSeverity()).isEqualTo("BLOCKER"); + assertThat(activation.isPrioritizedRule()).isTrue(); assertThat(activation.getRuleUuid()).isEqualTo(ruleUuid); assertThat(activation.getParameter("bar")).isEqualTo("baz"); } + @ParameterizedTest + @MethodSource("prioritizedRules") + void restore_sets_correctly_the_prioritizedRule_flag(boolean prioritizedInProfile, Boolean prioritizedInBackup, boolean expected) { + RuleDto rule = createRule(); + QProfileDto profile = createProfile(rule.getLanguage()); + ActiveRuleDto activeRule = activate(profile, rule, ar -> ar.setPrioritizedRule(prioritizedInProfile)); + + Reader backup = new StringReader("" + + "" + profile.getName() + "" + + "" + profile.getLanguage() + "" + + "" + + "" + + "" + rule.getRepositoryKey() + "" + + "" + rule.getRuleKey() + "" + + "" + RuleType.valueOf(rule.getType()).name() + "" + + "" + activeRule.getSeverityString() + "" + + (prioritizedInBackup == null ? "" : "" + prioritizedInBackup + "") + + "" + + "" + + ""); + + underTest.restore(db.getSession(), backup, (String) null); + + assertThat(reset.calledActivations).hasSize(1); + RuleActivation activation = reset.calledActivations.get(0); + assertThat(activation.getSeverity()).isEqualTo(activeRule.getSeverityString()); + assertThat(activation.isPrioritizedRule()).isEqualTo(expected); + assertThat(activation.getRuleUuid()).isEqualTo(rule.getUuid()); + } + + private static Stream prioritizedRules() { + return Stream.of( + Arguments.of(true, false, false), + Arguments.of(true, null, false), + Arguments.of(true, true, true), + Arguments.of(false, false, false), + Arguments.of(false, null, false), + Arguments.of(false, true, true) + ); + } + + @Test - public void restore_custom_rule() { + void restore_custom_rule() { when(ruleCreator.create(any(), anyList())).then(invocation -> Collections.singletonList(db.rules().insert(RuleKey.of("sonarjs", "s001")))); Reader backup = new StringReader("" + @@ -353,7 +427,7 @@ public class QProfileBackuperImplIT { } @Test - public void restore_skips_rule_without_template_key_and_db_definition() { + void restore_skips_rule_without_template_key_and_db_definition() { String ruleUuid = db.rules().insert(RuleKey.of("sonarjs", "s001")).getUuid(); Reader backup = new StringReader("" + "foo" + @@ -385,7 +459,7 @@ public class QProfileBackuperImplIT { } @Test - public void copy_profile() { + void copy_profile() { RuleDto rule = createRule(); RuleParamDto param = db.rules().insertRuleParam(rule); QProfileDto from = createProfile(rule.getLanguage()); @@ -400,7 +474,7 @@ public class QProfileBackuperImplIT { } @Test - public void copy_profile_with_custom_rule() { + void copy_profile_with_custom_rule() { RuleDto templateRule = db.rules().insert(ruleDefinitionDto -> ruleDefinitionDto .setIsTemplate(true)); RuleDto rule = db.rules().insert( @@ -422,7 +496,7 @@ public class QProfileBackuperImplIT { } @Test - public void fail_to_restore_if_bad_xml_format() { + void fail_to_restore_if_bad_xml_format() { DbSession session = db.getSession(); StringReader backup = new StringReader(""); IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () -> underTest.restore(session, backup, (String) null)); @@ -431,7 +505,7 @@ public class QProfileBackuperImplIT { } @Test - public void fail_to_restore_if_not_xml_backup() { + void fail_to_restore_if_not_xml_backup() { DbSession session = db.getSession(); StringReader backup = new StringReader("foo"); assertThrows(IllegalArgumentException.class, () -> underTest.restore(session, backup, (String) null)); @@ -439,7 +513,7 @@ public class QProfileBackuperImplIT { } @Test - public void fail_to_restore_if_xml_is_not_well_formed() { + void fail_to_restore_if_xml_is_not_well_formed() { assertThatThrownBy(() -> { String notWellFormedXml = "\"profil\"\"language\" r.setIsExternal(true)); Reader backup = new StringReader("" + "foo" + @@ -496,8 +570,12 @@ public class QProfileBackuperImplIT { return db.qualityProfiles().activateRule(profile, rule); } + private ActiveRuleDto activate(QProfileDto profile, RuleDto rule, Consumer consumer) { + return db.qualityProfiles().activateRule(profile, rule, consumer); + } + private ActiveRuleDto activate(QProfileDto profile, RuleDto rule, RuleParamDto param) { - ActiveRuleDto activeRule = db.qualityProfiles().activateRule(profile, rule); + ActiveRuleDto activeRule = db.qualityProfiles().activateRule(profile, rule, ar -> ar.setPrioritizedRule(false)); ActiveRuleParamDto dto = ActiveRuleParamDto.createFor(param) .setValue("20") .setActiveRuleUuid(activeRule.getUuid()); 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 e95394078c6..410fe724eaf 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 @@ -32,6 +32,7 @@ class ImportedRule { private String name = null; private String type = null; private String severity = null; + private Boolean prioritizedRule = false; private String description = null; private Map parameters = null; public Map getParameters() { @@ -58,6 +59,10 @@ class ImportedRule { return severity; } + public Boolean getPrioritizedRule() { + return prioritizedRule; + } + public String getDescription() { return description; } @@ -72,6 +77,11 @@ class ImportedRule { return this; } + public ImportedRule setPrioritizedRule(Boolean prioritizedRule) { + this.prioritizedRule = prioritizedRule; + return this; + } + ImportedRule setDescription(String description) { this.description = description; 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 74e58d995a3..4c709b0a285 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 @@ -228,7 +228,7 @@ public class QProfileBackuperImpl implements QProfileBackuper { if (ruleDto == null) { continue; } - activatedRule.add(RuleActivation.create(ruleDto.getUuid(), r.getSeverity(), r.getParameters())); + activatedRule.add(RuleActivation.create(ruleDto.getUuid(), r.getSeverity(), 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 013a2990501..f0c1d16411a 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 @@ -53,6 +53,7 @@ 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_PRIORITIZED_RULE = "prioritizedRule"; private static final String ATTRIBUTE_TEMPLATE_KEY = "templateKey"; private static final String ATTRIBUTE_TYPE = "type"; private static final String ATTRIBUTE_DESCRIPTION = "description"; @@ -75,6 +76,9 @@ public class QProfileParser { xml.prop(ATTRIBUTE_KEY, ruleToExport.getRuleKey().rule()); xml.prop(ATTRIBUTE_TYPE, ruleToExport.getRuleType().name()); xml.prop(ATTRIBUTE_PRIORITY, ruleToExport.getSeverityString()); + if (Boolean.TRUE.equals(ruleToExport.getPrioritizedRule())) { + xml.prop(ATTRIBUTE_PRIORITIZED_RULE, ruleToExport.getPrioritizedRule()); + } if (ruleToExport.isCustomRule()) { xml.prop(ATTRIBUTE_NAME, ruleToExport.getName()); @@ -177,6 +181,8 @@ 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_PRIORITIZED_RULE, nodeName)) { + rule.setPrioritizedRule(Boolean.valueOf(StringUtils.trim(ruleCursor.collectDescendantText(false)))); } else if (StringUtils.equals(ATTRIBUTE_PARAMETERS, nodeName)) { SMInputCursor propsCursor = ruleCursor.childElementCursor(ATTRIBUTE_PARAMETER); readParameters(propsCursor, parameters); 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 2b8e3ed56af..084bed6dad2 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,36 +21,78 @@ package org.sonar.server.qualityprofile; import java.io.Reader; import java.io.StringReader; -import org.junit.Test; +import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; -public class QProfileParserTest { +class QProfileParserTest { @Test - public void readXml() { - Reader backup = new StringReader("" + - "" + - "custom rule" + - "js" + - "" + - "sonarjs" + - "s001" + - "CODE_SMELL" + - "CRITICAL" + - "custom rule name" + - "rule_mc8" + - "custom rule description" + - "" + - "bar" + - "baz" + - "" + - "" + - ""); + void readXml() { + Reader backup = new StringReader(""" + + + custom rule + js + + + sonarjs + s001 + CODE_SMELL + CRITICAL + custom rule name + rule_mc8 + custom rule description + + + bar + baz + + + + + sonarjs + s002 + CODE_SMELL + CRITICAL + true + custom rule name + rule_mc8 + custom rule description + + + bar + baz + + + + + sonarjs + s003 + CODE_SMELL + CRITICAL + false + custom rule name + rule_mc8 + custom rule description + + + bar + baz + + + + + """); var parser = new QProfileParser(); var importedQProfile = parser.readXml(backup); - assertThat(importedQProfile.getRules()).hasSize(1); + assertThat(importedQProfile.getRules()).hasSize(3); var importedRule = importedQProfile.getRules().get(0); assertThat(importedRule.getDescription()).isEqualTo("custom rule description"); + assertThat(importedRule.getPrioritizedRule()).isFalse(); + importedRule = importedQProfile.getRules().get(1); + assertThat(importedRule.getPrioritizedRule()).isTrue(); + importedRule = importedQProfile.getRules().get(2); + assertThat(importedRule.getPrioritizedRule()).isFalse(); } }