aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorDejan Milisavljevic <dejan.milisavljevic@sonarsource.com>2024-10-10 16:21:04 +0200
committersonartech <sonartech@sonarsource.com>2024-10-16 20:03:01 +0000
commit5038a353ca5917a647e5613859fbf62e756f4a21 (patch)
tree06f18963373820c45f2f08e355271a6b03379a07 /server
parent32cec354bfc868d591e6356ee85728c5ecba51d1 (diff)
downloadsonarqube-5038a353ca5917a647e5613859fbf62e756f4a21.tar.gz
sonarqube-5038a353ca5917a647e5613859fbf62e756f4a21.zip
SONAR-23250 Initial population of active_rules with impacts
Diffstat (limited to 'server')
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/ActiveRuleDao.java4
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/ActiveRuleMapper.java3
-rw-r--r--server/sonar-db-dao/src/main/resources/org/sonar/db/qualityprofile/ActiveRuleMapper.xml10
-rw-r--r--server/sonar-webserver-core/build.gradle1
-rw-r--r--server/sonar-webserver-core/src/it/java/org/sonar/server/rule/registration/RulesRegistrantIT.java247
-rw-r--r--server/sonar-webserver-core/src/main/java/org/sonar/server/rule/registration/ActiveRulesImpactInitializer.java97
-rw-r--r--server/sonar-webserver-core/src/main/java/org/sonar/server/rule/registration/RulesRegistrant.java8
-rw-r--r--server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevelStartup.java2
8 files changed, 313 insertions, 59 deletions
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/ActiveRuleDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/ActiveRuleDao.java
index 4e6501a043d..e085a3cc315 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/ActiveRuleDao.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/ActiveRuleDao.java
@@ -68,6 +68,10 @@ public class ActiveRuleDao implements Dao {
return mapper(dbSession).selectByRuleUuid(ruleUuid);
}
+ public List<ActiveRuleDto> selectByRepository(DbSession dbSession, String repositoryKey, String repositoryLanguage) {
+ return mapper(dbSession).selectByRepository(repositoryKey, repositoryLanguage);
+ }
+
public List<OrgActiveRuleDto> selectByRuleUuids(DbSession dbSession, List<String> uuids) {
return executeLargeInputs(uuids, chunk -> mapper(dbSession).selectByRuleUuids(chunk));
}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/ActiveRuleMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/ActiveRuleMapper.java
index 1e276615f37..d2b2dddfdbe 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/ActiveRuleMapper.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/ActiveRuleMapper.java
@@ -47,6 +47,9 @@ public interface ActiveRuleMapper {
List<ActiveRuleDto> selectByRuleUuid(String ruleUuid);
+ List<ActiveRuleDto> selectByRepository(@Param("repositoryKey") String repositoryKey,
+ @Param("repositoryLanguage") String repositoryLanguage);
+
List<OrgActiveRuleDto> selectByRuleUuids(@Param("ruleUuids") List<String> partitionOfRuleUuids);
List<OrgActiveRuleDto> selectByProfileUuid(String uuid);
diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/qualityprofile/ActiveRuleMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/qualityprofile/ActiveRuleMapper.xml
index 92a9b7fca53..10442858528 100644
--- a/server/sonar-db-dao/src/main/resources/org/sonar/db/qualityprofile/ActiveRuleMapper.xml
+++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/qualityprofile/ActiveRuleMapper.xml
@@ -201,6 +201,16 @@
r.uuid = #{ruleUuid, jdbcType=VARCHAR}
</select>
+ <select id="selectByRepository" parameterType="String" resultType="ActiveRule">
+ select
+ <include refid="activeRuleColumns"/>
+ from active_rules a
+ <include refid="activeRuleKeyJoin"/>
+ where
+ r.plugin_name = #{repositoryKey, jdbcType=VARCHAR}
+ and r.language = #{repositoryLanguage, jdbcType=VARCHAR}
+ </select>
+
<select id="selectByRuleUuids" parameterType="List" resultType="org.sonar.db.qualityprofile.OrgActiveRuleDto">
select
<include refid="orgActiveRuleColumns"/>
diff --git a/server/sonar-webserver-core/build.gradle b/server/sonar-webserver-core/build.gradle
index ecdf1cac652..79e113b5d29 100644
--- a/server/sonar-webserver-core/build.gradle
+++ b/server/sonar-webserver-core/build.gradle
@@ -68,6 +68,7 @@ dependencies {
testImplementation 'org.eclipse.jetty:jetty-servlet'
testImplementation 'org.hamcrest:hamcrest'
testImplementation 'org.junit.jupiter:junit-jupiter-api'
+ testImplementation 'org.junit.jupiter:junit-jupiter-params'
testImplementation 'org.mockito:mockito-core'
testImplementation 'org.sonarsource.api.plugin:sonar-plugin-api-test-fixtures'
testImplementation 'org.subethamail:subethasmtp'
diff --git a/server/sonar-webserver-core/src/it/java/org/sonar/server/rule/registration/RulesRegistrantIT.java b/server/sonar-webserver-core/src/it/java/org/sonar/server/rule/registration/RulesRegistrantIT.java
index b11ba508409..1cc61bd147f 100644
--- a/server/sonar-webserver-core/src/it/java/org/sonar/server/rule/registration/RulesRegistrantIT.java
+++ b/server/sonar-webserver-core/src/it/java/org/sonar/server/rule/registration/RulesRegistrantIT.java
@@ -19,9 +19,6 @@
*/
package org.sonar.server.rule.registration;
-import com.tngtech.java.junit.dataprovider.DataProvider;
-import com.tngtech.java.junit.dataprovider.DataProviderRunner;
-import com.tngtech.java.junit.dataprovider.UseDataProvider;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
@@ -33,9 +30,11 @@ import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.elasticsearch.common.util.set.Sets;
import org.jetbrains.annotations.Nullable;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
+import org.junit.jupiter.api.BeforeEach;
+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.MethodSource;
import org.sonar.api.impl.utils.TestSystem2;
import org.sonar.api.issue.impact.Severity;
import org.sonar.api.issue.impact.SoftwareQuality;
@@ -48,7 +47,6 @@ import org.sonar.api.server.debt.DebtRemediationFunction;
import org.sonar.api.server.rule.Context;
import org.sonar.api.server.rule.RuleDescriptionSection;
import org.sonar.api.server.rule.RulesDefinition;
-import org.sonar.api.testfixtures.log.LogTester;
import org.sonar.api.utils.DateUtils;
import org.sonar.api.utils.Version;
import org.sonar.core.platform.SonarQubeVersion;
@@ -62,6 +60,7 @@ import org.sonar.db.qualityprofile.ActiveRuleDto;
import org.sonar.db.qualityprofile.QProfileChangeDto;
import org.sonar.db.qualityprofile.QProfileChangeQuery;
import org.sonar.db.qualityprofile.QProfileDto;
+import org.sonar.db.qualityprofile.RulesProfileDto;
import org.sonar.db.rule.DeprecatedRuleKeyDto;
import org.sonar.db.rule.RuleDescriptionSectionContextDto;
import org.sonar.db.rule.RuleDescriptionSectionDto;
@@ -69,12 +68,16 @@ import org.sonar.db.rule.RuleDto;
import org.sonar.db.rule.RuleDto.Scope;
import org.sonar.db.rule.RuleParamDto;
import org.sonar.db.rule.RuleRepositoryDto;
+import org.sonar.db.rule.RuleTesting;
+import org.sonar.db.rule.SeverityUtil;
import org.sonar.server.es.EsTester;
import org.sonar.server.es.SearchIdResult;
import org.sonar.server.es.SearchOptions;
import org.sonar.server.es.metadata.MetadataIndex;
import org.sonar.server.plugins.DetectPluginChange;
import org.sonar.server.plugins.ServerPluginRepository;
+import org.sonar.server.property.InternalProperties;
+import org.sonar.server.property.MapInternalProperties;
import org.sonar.server.qualityprofile.ActiveRuleChange;
import org.sonar.server.qualityprofile.QProfileRules;
import org.sonar.server.qualityprofile.index.ActiveRuleIndexer;
@@ -121,8 +124,7 @@ import static org.sonar.db.rule.RuleDescriptionSectionDto.builder;
import static org.sonar.db.rule.RuleDescriptionSectionDto.createDefaultRuleDescriptionSection;
import static org.sonar.server.qualityprofile.ActiveRuleChange.Type.DEACTIVATED;
-@RunWith(DataProviderRunner.class)
-public class RulesRegistrantIT {
+class RulesRegistrantIT {
private static final String FAKE_PLUGIN_KEY = "unittest";
private static final Date DATE1 = DateUtils.parseDateTime("2014-01-01T19:10:03+0100");
@@ -139,12 +141,10 @@ public class RulesRegistrantIT {
private final TestSystem2 system = new TestSystem2().setNow(DATE1.getTime());
- @org.junit.Rule
- public DbTester db = DbTester.create(system);
- @org.junit.Rule
- public EsTester es = EsTester.create();
- @org.junit.Rule
- public LogTester logTester = new LogTester();
+ @RegisterExtension
+ private final DbTester db = DbTester.create(system);
+ @RegisterExtension
+ private final EsTester es = EsTester.create();
private final QProfileRules qProfileRules = mock();
private final WebServerRuleFinder webServerRuleFinder = mock();
@@ -153,9 +153,11 @@ public class RulesRegistrantIT {
private final UuidFactory uuidFactory = UuidFactoryFast.getInstance();
private final SonarQubeVersion sonarQubeVersion = new SonarQubeVersion(Version.create(10, 3));
+ private InternalProperties internalProperties;
private RuleIndexer ruleIndexer;
private ActiveRuleIndexer activeRuleIndexer;
private RuleIndex ruleIndex;
+ private ActiveRulesImpactInitializer activeRulesImpactInitializer;
private final RuleDescriptionSectionsGenerator ruleDescriptionSectionsGenerator = mock();
private final RuleDescriptionSectionsGeneratorResolver resolver = mock();
@@ -165,11 +167,13 @@ public class RulesRegistrantIT {
private final QualityProfileChangesUpdater qualityProfileChangesUpdater = mock();
private final DetectPluginChange detectPluginChange = mock();
- @Before
- public void before() {
+ @BeforeEach
+ void before() {
ruleIndexer = new RuleIndexer(es.client(), dbClient);
ruleIndex = new RuleIndex(es.client(), system);
activeRuleIndexer = new ActiveRuleIndexer(dbClient, es.client());
+ internalProperties = new MapInternalProperties();
+ activeRulesImpactInitializer = new ActiveRulesImpactInitializer(internalProperties, dbClient);
when(resolver.generateFor(any())).thenAnswer(answer -> {
RulesDefinition.Rule rule = answer.getArgument(0, RulesDefinition.Rule.class);
String description = rule.htmlDescription() == null ? rule.markdownDescription() : rule.htmlDescription();
@@ -190,7 +194,7 @@ public class RulesRegistrantIT {
}
@Test
- public void insert_new_rules() {
+ void insert_new_rules() {
executeWithPluginRules(new FakeRepositoryV1());
// verify db
@@ -236,7 +240,7 @@ public class RulesRegistrantIT {
}
@Test
- public void insert_new_external_rule() {
+ void insert_new_external_rule() {
executeWithPluginRules(new ExternalRuleRepository());
// verify db
@@ -270,8 +274,8 @@ public class RulesRegistrantIT {
}
@Test
- public void insert_then_remove_rule() {
- String ruleKey = secure().nextAlphanumeric(5);
+ void insert_then_remove_rule() {
+ String ruleKey = secure().randomAlphanumeric(5);
// register one rule
executeWithPluginRules(context -> {
@@ -314,7 +318,7 @@ public class RulesRegistrantIT {
}
@Test
- public void mass_insert_then_remove_rule() {
+ void mass_insert_then_remove_rule() {
int numberOfRules = 5000;
// register many rules
@@ -355,7 +359,7 @@ public class RulesRegistrantIT {
}
@Test
- public void delete_repositories_that_have_been_uninstalled() {
+ void delete_repositories_that_have_been_uninstalled() {
RuleRepositoryDto repository = new RuleRepositoryDto("findbugs", "java", "Findbugs");
DbSession dbSession = db.getSession();
db.getDbClient().ruleRepositoryDao().insert(dbSession, singletonList(repository));
@@ -367,7 +371,7 @@ public class RulesRegistrantIT {
}
@Test
- public void update_and_remove_rules_on_changes() {
+ void update_and_remove_rules_on_changes() {
executeWithPluginRules(new FakeRepositoryV1());
assertThat(dbClient.ruleDao().selectAll(db.getSession())).hasSize(3);
RuleDto rule1 = dbClient.ruleDao().selectOrFailByKey(db.getSession(), RULE_KEY1);
@@ -440,7 +444,7 @@ public class RulesRegistrantIT {
}
@Test
- public void add_new_tag() {
+ void add_new_tag() {
executeWithPluginRules(context -> {
NewRepository repo = context.createRepository("fake", "java");
repo.createRule("rule1")
@@ -467,7 +471,7 @@ public class RulesRegistrantIT {
}
@Test
- public void add_new_security_standards() {
+ void add_new_security_standards() {
executeWithPluginRules(context -> {
NewRepository repo = context.createRepository("fake", "java");
repo.createRule("rule1")
@@ -496,7 +500,7 @@ public class RulesRegistrantIT {
}
@Test
- public void update_only_rule_name() {
+ void update_only_rule_name() {
system.setNow(DATE1.getTime());
executeWithPluginRules(context -> {
NewRepository repo = context.createRepository("fake", "java");
@@ -525,7 +529,7 @@ public class RulesRegistrantIT {
}
@Test
- public void update_template_rule_key_should_also_update_custom_rules() {
+ void update_template_rule_key_should_also_update_custom_rules() {
system.setNow(DATE1.getTime());
executeWithPluginRules(context -> {
NewRepository repo = context.createRepository("squid", "java");
@@ -564,7 +568,7 @@ public class RulesRegistrantIT {
}
@Test
- public void update_if_rule_key_renamed_and_deprecated_key_declared() {
+ void update_if_rule_key_renamed_and_deprecated_key_declared() {
String ruleKey1 = "rule1";
String ruleKey2 = "rule2";
String repository = "fake";
@@ -606,7 +610,7 @@ public class RulesRegistrantIT {
}
@Test
- public void update_if_repository_changed_and_deprecated_key_declared() {
+ void update_if_repository_changed_and_deprecated_key_declared() {
String ruleKey = "rule";
String repository1 = "fake1";
String repository2 = "fake2";
@@ -647,9 +651,9 @@ public class RulesRegistrantIT {
assertThat(ruleIndex.search(new RuleQuery().setQueryText("Name1"), new SearchOptions()).getTotal()).isZero();
}
- @Test
- @UseDataProvider("allRenamingCases")
- public void update_if_only_renamed_and_deprecated_key_declared(String ruleKey1, String repo1, String ruleKey2, String repo2) {
+ @ParameterizedTest
+ @MethodSource("allRenamingCases")
+ void update_if_only_renamed_and_deprecated_key_declared(String ruleKey1, String repo1, String ruleKey2, String repo2) {
String name = "Name1";
String description = "Description";
system.setNow(DATE1.getTime());
@@ -685,8 +689,7 @@ public class RulesRegistrantIT {
.containsOnly(rule2.getUuid());
}
- @DataProvider
- public static Object[][] allRenamingCases() {
+ private static Object[][] allRenamingCases() {
return new Object[][]{
{"repo1", "rule1", "repo1", "rule2"},
{"repo1", "rule1", "repo2", "rule1"},
@@ -695,7 +698,7 @@ public class RulesRegistrantIT {
}
@Test
- public void update_if_repository_and_key_changed_and_deprecated_key_declared_among_others() {
+ void update_if_repository_and_key_changed_and_deprecated_key_declared_among_others() {
String ruleKey1 = "rule1";
String ruleKey2 = "rule2";
String repository1 = "fake1";
@@ -735,7 +738,7 @@ public class RulesRegistrantIT {
}
@Test
- public void update_only_rule_description() {
+ void update_only_rule_description() {
system.setNow(DATE1.getTime());
executeWithPluginRules(context -> {
NewRepository repo = context.createRepository("fake", "java");
@@ -764,7 +767,7 @@ public class RulesRegistrantIT {
}
@Test
- public void update_several_rule_descriptions() {
+ void update_several_rule_descriptions() {
system.setNow(DATE1.getTime());
RuleDescriptionSection section1context1 = createRuleDescriptionSection(HOW_TO_FIX_SECTION_KEY, "section1 ctx1 content", "ctx_1");
@@ -848,7 +851,7 @@ public class RulesRegistrantIT {
}
@Test
- public void rule_previously_created_as_adhoc_becomes_none_adhoc() {
+ void rule_previously_created_as_adhoc_becomes_none_adhoc() {
RuleDto rule = db.rules().insert(r -> r.setRepositoryKey("external_fake").setIsExternal(true).setIsAdHoc(true));
system.setNow(DATE2.getTime());
executeWithPluginRules(context -> {
@@ -864,7 +867,7 @@ public class RulesRegistrantIT {
}
@Test
- public void remove_no_more_defined_external_rule() {
+ void remove_no_more_defined_external_rule() {
RuleDto rule = db.rules().insert(r -> r.setRepositoryKey("external_fake")
.setStatus(READY)
.setIsExternal(true)
@@ -877,7 +880,7 @@ public class RulesRegistrantIT {
}
@Test
- public void do_not_remove_no_more_defined_ad_hoc_rule() {
+ void do_not_remove_no_more_defined_ad_hoc_rule() {
RuleDto rule = db.rules().insert(r -> r.setRepositoryKey("external_fake")
.setStatus(READY)
.setIsExternal(true)
@@ -890,7 +893,7 @@ public class RulesRegistrantIT {
}
@Test
- public void disable_then_enable_rule() {
+ void disable_then_enable_rule() {
// Install rule
system.setNow(DATE1.getTime());
executeWithPluginRules(new FakeRepositoryV1());
@@ -913,7 +916,7 @@ public class RulesRegistrantIT {
}
@Test
- public void do_not_update_rules_when_no_changes() {
+ void do_not_update_rules_when_no_changes() {
executeWithPluginRules(new FakeRepositoryV1());
assertThat(dbClient.ruleDao().selectAll(db.getSession())).hasSize(3);
@@ -926,7 +929,7 @@ public class RulesRegistrantIT {
}
@Test
- public void do_not_update_already_removed_rules() {
+ void do_not_update_already_removed_rules() {
executeWithPluginRules(new FakeRepositoryV1());
assertThat(dbClient.ruleDao().selectAll(db.getSession())).hasSize(3);
@@ -964,7 +967,7 @@ public class RulesRegistrantIT {
}
@Test
- public void mass_insert() {
+ void mass_insert() {
executeWithPluginRules(new BigRepository());
assertThat(db.countRowsOfTable("rules")).isEqualTo(BigRepository.SIZE);
assertThat(db.countRowsOfTable("rules_parameters")).isEqualTo(BigRepository.SIZE * 20);
@@ -972,7 +975,7 @@ public class RulesRegistrantIT {
}
@Test
- public void manage_repository_extensions() {
+ void manage_repository_extensions() {
executeWithPluginRules(new FindbugsRepository(), new FbContribRepository());
List<RuleDto> rules = dbClient.ruleDao().selectAll(db.getSession());
assertThat(rules).hasSize(2);
@@ -982,7 +985,7 @@ public class RulesRegistrantIT {
}
@Test
- public void remove_system_tags_when_plugin_does_not_provide_any() {
+ void remove_system_tags_when_plugin_does_not_provide_any() {
// Rule already exists in DB, with some system tags
db.rules().insert(new RuleDto()
.setRuleKey("rule1")
@@ -1004,7 +1007,7 @@ public class RulesRegistrantIT {
}
@Test
- public void rules_that_deprecate_previous_rule_must_be_recorded() {
+ void rules_that_deprecate_previous_rule_must_be_recorded() {
executeWithPluginRules(context -> {
NewRepository repo = context.createRepository("fake", "java");
createRule(repo, "rule1");
@@ -1026,7 +1029,7 @@ public class RulesRegistrantIT {
}
@Test
- public void rules_that_remove_deprecated_key_must_remove_records() {
+ void rules_that_remove_deprecated_key_must_remove_records() {
executeWithPluginRules(context -> {
NewRepository repo = context.createRepository("fake", "java");
createRule(repo, "rule1");
@@ -1057,7 +1060,7 @@ public class RulesRegistrantIT {
}
@Test
- public void declaring_two_rules_with_same_deprecated_RuleKey_should_throw_ISE() {
+ void declaring_two_rules_with_same_deprecated_RuleKey_should_throw_ISE() {
assertThatThrownBy(() -> {
executeWithPluginRules(context -> {
NewRepository repo = context.createRepository("fake", "java");
@@ -1073,7 +1076,7 @@ public class RulesRegistrantIT {
}
@Test
- public void declaring_a_rule_with_a_deprecated_RuleKey_still_used_should_throw_ISE() {
+ void declaring_a_rule_with_a_deprecated_RuleKey_still_used_should_throw_ISE() {
assertThatThrownBy(() -> {
executeWithPluginRules(context -> {
NewRepository repo = context.createRepository("fake", "java");
@@ -1088,7 +1091,7 @@ public class RulesRegistrantIT {
}
@Test
- public void updating_the_deprecated_to_a_new_ruleKey_should_throw_an_ISE() {
+ void updating_the_deprecated_to_a_new_ruleKey_should_throw_an_ISE() {
// On this new rule add a deprecated key
executeWithPluginRules(context -> createRule(context, "javascript", "javascript", "s103",
r -> r.addDeprecatedRuleKey("javascript", "linelength")));
@@ -1104,7 +1107,7 @@ public class RulesRegistrantIT {
}
@Test
- public void deprecate_rule_that_deprecated_another_rule() {
+ void deprecate_rule_that_deprecated_another_rule() {
executeWithPluginRules(context -> createRule(context, "javascript", "javascript", "s103"));
executeWithPluginRules(context -> createRule(context, "javascript", "javascript", "s104",
r -> r.addDeprecatedRuleKey("javascript", "s103")));
@@ -1116,7 +1119,7 @@ public class RulesRegistrantIT {
}
@Test
- public void declaring_a_rule_with_an_existing_RuleKey_still_used_should_throw_IAE() {
+ void declaring_a_rule_with_an_existing_RuleKey_still_used_should_throw_IAE() {
assertThatThrownBy(() -> {
executeWithPluginRules(context -> {
NewRepository repo = context.createRepository("fake", "java");
@@ -1130,7 +1133,7 @@ public class RulesRegistrantIT {
}
@Test
- public void removed_rule_should_appear_in_changelog() {
+ void removed_rule_should_appear_in_changelog() {
//GIVEN
QProfileDto qProfileDto = db.qualityProfiles().insert();
RuleDto ruleDto = db.rules().insert(RULE_KEY1);
@@ -1146,7 +1149,7 @@ public class RulesRegistrantIT {
}
@Test
- public void removed_rule_should_be_deleted_when_renamed_repository() {
+ void removed_rule_should_be_deleted_when_renamed_repository() {
//GIVEN
RuleDto removedRuleDto = db.rules().insert(RuleKey.of("old_repo", "removed_rule"));
RuleDto renamedRuleDto = db.rules().insert(RuleKey.of("old_repo", "renamed_rule"));
@@ -1158,7 +1161,7 @@ public class RulesRegistrantIT {
}
@Test
- public void builtin_rules_should_be_updated_even_if_no_plugin_updates() {
+ void builtin_rules_should_be_updated_even_if_no_plugin_updates() {
RulesDefinition builtInRepoV1 = context -> createRule(context, "builtin", "sca", "rule1", rule -> rule.setName("Name before"));
RulesDefinition pluginRepo = context -> createRule(context, "java", "java", "rule2");
@@ -1184,6 +1187,134 @@ public class RulesRegistrantIT {
assertThatCode(() -> dbClient.ruleDao().selectOrFailByKey(db.getSession(), RuleKey.of("java", "rule2"))).doesNotThrowAnyException();
}
+ @Test
+ void impacts_on_active_rules_when_rule_and_active_rule_severity_are_same_should_use_rule_impacts() {
+ RuleDto ruleDto = createAndActivateRuleOnQProfile("rule1", "java", "fake", Severity.LOW, Severity.LOW);
+
+ executeWithPluginRules(context -> {
+ NewRepository repo = context.createRepository("fake", "java");
+ repo.createRule("rule1")
+ .setName("any")
+ .setHtmlDescription("html")
+ .addDefaultImpact(SoftwareQuality.MAINTAINABILITY, Severity.BLOCKER)
+ .setSeverity(ruleDto.getSeverityString());
+ repo.done();
+ });
+
+ assertThat(internalProperties.read("activeRules.impacts.populated")).isEqualTo(Optional.of("true"));
+
+ List<ActiveRuleDto> activeRules = dbClient.activeRuleDao().selectByRuleUuid(db.getSession(), ruleDto.getUuid());
+ assertThat(activeRules).hasSize(1);
+ assertThat(activeRules.get(0).getImpactsString()).isEqualTo("{\"MAINTAINABILITY\":\"BLOCKER\"}");
+ }
+
+ @Test
+ void impacts_on_active_rules_when_active_rule_severity_changed_should_initialize_with_active_rule_severity() {
+ RuleDto ruleDto = createAndActivateRuleOnQProfile("rule1", "java", "fake", Severity.LOW, Severity.INFO);
+
+ executeWithPluginRules(context -> {
+ NewRepository repo = context.createRepository("fake", "java");
+ repo.createRule("rule1")
+ .setName("any")
+ .setHtmlDescription("html")
+ .addDefaultImpact(SoftwareQuality.MAINTAINABILITY, Severity.BLOCKER)
+ .setSeverity(SeverityUtil.getSeverityFromOrdinal(2));
+ repo.done();
+ });
+
+ assertThat(internalProperties.read("activeRules.impacts.populated")).isEqualTo(Optional.of("true"));
+
+ List<ActiveRuleDto> activeRules = dbClient.activeRuleDao().selectByRuleUuid(db.getSession(), ruleDto.getUuid());
+ assertThat(activeRules).hasSize(1);
+ assertThat(activeRules.get(0).getImpactsString()).isEqualTo("{\"MAINTAINABILITY\":\"INFO\"}");
+ }
+
+ @Test
+ void impacts_on_active_rules_when_rule_impact_changed_should_use_active_rule_severity() {
+ RuleDto ruleDto = createAndActivateRuleOnQProfile("rule1", "java", "fake", Severity.LOW, Severity.LOW);
+
+ executeWithPluginRules(context -> {
+ NewRepository repo = context.createRepository("fake", "java");
+ repo.createRule("rule1")
+ .setName("any")
+ .setHtmlDescription("html")
+ .addDefaultImpact(SoftwareQuality.SECURITY, Severity.HIGH)
+ .setSeverity(SeverityUtil.getSeverityFromOrdinal(2));
+ repo.done();
+ });
+
+ assertThat(internalProperties.read("activeRules.impacts.populated")).isEqualTo(Optional.of("true"));
+
+ List<ActiveRuleDto> activeRules = dbClient.activeRuleDao().selectByRuleUuid(db.getSession(), ruleDto.getUuid());
+ assertThat(activeRules).hasSize(1);
+ assertThat(activeRules.get(0).getImpactsString()).isEqualTo("{\"SECURITY\":\"LOW\"}");
+ }
+
+ @Test
+ void impacts_on_active_rules_when_rule_impact_changed_and_active_rule_and_rule_severity_different_should_use_rule_changed_impact() {
+ RuleDto ruleDto = createAndActivateRuleOnQProfile("rule1", "java", "fake", Severity.HIGH, Severity.INFO);
+
+ executeWithPluginRules(context -> {
+ NewRepository repo = context.createRepository("fake", "java");
+ repo.createRule("rule1")
+ .setName("any")
+ .setHtmlDescription("html")
+ .addDefaultImpact(SoftwareQuality.SECURITY, Severity.LOW)
+ .setSeverity(SeverityUtil.getSeverityFromOrdinal(2));
+ repo.done();
+ });
+
+ assertThat(internalProperties.read("activeRules.impacts.populated")).isEqualTo(Optional.of("true"));
+
+ List<ActiveRuleDto> activeRules = dbClient.activeRuleDao().selectByRuleUuid(db.getSession(), ruleDto.getUuid());
+ assertThat(activeRules).hasSize(1);
+ assertThat(activeRules.get(0).getImpactsString()).isEqualTo("{\"SECURITY\":\"INFO\"}");
+ }
+
+ @Test
+ void impacts_on_active_rules_should_not_be_recomputed_if_completion_flag_is_set() {
+
+ internalProperties.write("activeRules.impacts.populated", Boolean.TRUE.toString());
+
+ RuleDto ruleDto = createAndActivateRuleOnQProfile("rule1", "java", "fake", Severity.LOW, Severity.LOW);
+
+ executeWithPluginRules(context -> {
+ NewRepository repo = context.createRepository("fake", "java");
+ repo.createRule("rule1")
+ .setName("any")
+ .setHtmlDescription("html");
+ repo.done();
+ });
+
+ List<ActiveRuleDto> activeRules = dbClient.activeRuleDao().selectByRuleUuid(db.getSession(), ruleDto.getUuid());
+ assertThat(activeRules).hasSize(1);
+ assertThat(activeRules.get(0).getImpactsString()).isNull();
+ }
+
+ private RuleDto createAndActivateRuleOnQProfile(String ruleKey, String language, String repositoryKey, Severity ruleSeverity,
+ Severity activeRuleSeverity) {
+ RulesProfileDto rulesProfileDto = new RulesProfileDto()
+ .setUuid(uuidFactory.create())
+ .setName("any");
+ dbClient.qualityProfileDao().insert(db.getSession(), rulesProfileDto);
+
+ RuleDto ruleDto = RuleTesting.newRule()
+ .setRuleKey(ruleKey)
+ .setSeverity(ruleSeverity.ordinal())
+ .replaceAllDefaultImpacts(List.of(new ImpactDto(SoftwareQuality.MAINTAINABILITY, Severity.HIGH)))
+ .setLanguage(language)
+ .setRepositoryKey(repositoryKey);
+ dbClient.ruleDao().insert(db.getSession(), ruleDto);
+
+ ActiveRuleDto activeRuleDto = new ActiveRuleDto()
+ .setProfileUuid(rulesProfileDto.getUuid())
+ .setRuleUuid(ruleDto.getUuid())
+ .setSeverity(activeRuleSeverity.ordinal());
+ dbClient.activeRuleDao().insert(db.getSession(), activeRuleDto);
+
+ db.commit();
+ return ruleDto;
+ }
private void executeWithPluginRules(RulesDefinition... defs) {
ServerPluginRepository pluginRepository = mock(ServerPluginRepository.class);
@@ -1197,7 +1328,7 @@ public class RulesRegistrantIT {
reset(webServerRuleFinder);
RulesRegistrant task = new RulesRegistrant(loader, qProfileRules, dbClient, ruleIndexer, activeRuleIndexer, system, webServerRuleFinder, metadataIndex,
- rulesKeyVerifier, startupRuleUpdater, newRuleCreator, qualityProfileChangesUpdater, sonarQubeVersion, detectPluginChange);
+ rulesKeyVerifier, startupRuleUpdater, newRuleCreator, qualityProfileChangesUpdater, sonarQubeVersion, detectPluginChange, activeRulesImpactInitializer);
task.start();
// Execute a commit to refresh session state as the task is using its own session
db.getSession().commit();
diff --git a/server/sonar-webserver-core/src/main/java/org/sonar/server/rule/registration/ActiveRulesImpactInitializer.java b/server/sonar-webserver-core/src/main/java/org/sonar/server/rule/registration/ActiveRulesImpactInitializer.java
new file mode 100644
index 00000000000..bcce3553966
--- /dev/null
+++ b/server/sonar-webserver-core/src/main/java/org/sonar/server/rule/registration/ActiveRulesImpactInitializer.java
@@ -0,0 +1,97 @@
+/*
+ * 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.rule.registration;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.sonar.api.issue.impact.Severity;
+import org.sonar.api.issue.impact.SoftwareQuality;
+import org.sonar.api.rules.RuleType;
+import org.sonar.api.server.rule.RulesDefinition;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.issue.ImpactDto;
+import org.sonar.db.qualityprofile.ActiveRuleDto;
+import org.sonar.db.rule.RuleDto;
+import org.sonar.db.rule.SeverityUtil;
+import org.sonar.server.property.InternalProperties;
+import org.sonar.server.qualityprofile.QProfileImpactSeverityMapper;
+
+import static java.util.stream.Collectors.toUnmodifiableMap;
+
+/**
+ * Creates the initial impacts on active_rules, based on the rules. This class is necessary only up to 10 LTA version and can be removed
+ * later
+ */
+public class ActiveRulesImpactInitializer {
+ private static final String ACTIVE_RULE_IMPACT_INITIAL_POPULATION_DONE = "activeRules.impacts.populated";
+
+ private final InternalProperties internalProperties;
+ private final DbClient dbClient;
+
+ ActiveRulesImpactInitializer(InternalProperties internalProperties, DbClient dbClient) {
+ this.internalProperties = internalProperties;
+ this.dbClient = dbClient;
+ }
+
+ public void createImpactsOnActiveRules(RulesRegistrationContext context, RulesDefinition.Repository repoDef, DbSession dbSession) {
+
+ if (Boolean.parseBoolean(internalProperties.read(ACTIVE_RULE_IMPACT_INITIAL_POPULATION_DONE).orElse("false"))) {
+ return;
+ }
+
+ Map<String, RuleDto> rules = new HashMap<>(repoDef.rules().size());
+
+ for (RulesDefinition.Rule ruleDef : repoDef.rules()) {
+ context.getDbRuleFor(ruleDef).ifPresent(ruleDto -> rules.put(ruleDto.getUuid(), ruleDto));
+ }
+
+ context.getAllModified().forEach(r -> rules.put(r.getUuid(), r));
+
+ List<ActiveRuleDto> activeRules = dbClient.activeRuleDao().selectByRepository(dbSession, repoDef.key(), repoDef.language());
+
+ for (ActiveRuleDto activeRuleDto : activeRules) {
+ RuleDto rule = rules.get(activeRuleDto.getRuleUuid());
+ if (rule == null || rule.getEnumType() == RuleType.SECURITY_HOTSPOT) {
+ continue;
+ }
+
+ Map<SoftwareQuality, Severity> impacts = toImpactMap(rule.getDefaultImpacts());
+
+ if (!activeRuleDto.getSeverity().equals(rule.getSeverity())) {
+ impacts = QProfileImpactSeverityMapper.mapImpactSeverities(SeverityUtil.getSeverityFromOrdinal(activeRuleDto.getSeverity()), impacts, rule.getEnumType());
+ }
+ activeRuleDto.setImpacts(impacts);
+ dbClient.activeRuleDao().update(dbSession, activeRuleDto);
+ }
+
+ }
+
+ private static Map<SoftwareQuality, Severity> toImpactMap(Collection<ImpactDto> impacts) {
+ return impacts.stream()
+ .collect(toUnmodifiableMap(ImpactDto::getSoftwareQuality, ImpactDto::getSeverity));
+ }
+
+ public void markInitialPopulationDone() {
+ internalProperties.write(ACTIVE_RULE_IMPACT_INITIAL_POPULATION_DONE, "true");
+ }
+}
diff --git a/server/sonar-webserver-core/src/main/java/org/sonar/server/rule/registration/RulesRegistrant.java b/server/sonar-webserver-core/src/main/java/org/sonar/server/rule/registration/RulesRegistrant.java
index 9a022988bab..bafd97fda77 100644
--- a/server/sonar-webserver-core/src/main/java/org/sonar/server/rule/registration/RulesRegistrant.java
+++ b/server/sonar-webserver-core/src/main/java/org/sonar/server/rule/registration/RulesRegistrant.java
@@ -83,11 +83,13 @@ public class RulesRegistrant implements Startable {
private final QualityProfileChangesUpdater qualityProfileChangesUpdater;
private final SonarQubeVersion sonarQubeVersion;
private final DetectPluginChange detectPluginChange;
+ private final ActiveRulesImpactInitializer activeRulesImpactInitializer;
public RulesRegistrant(RuleDefinitionsLoader defLoader, QProfileRules qProfileRules, DbClient dbClient, RuleIndexer ruleIndexer,
ActiveRuleIndexer activeRuleIndexer, System2 system2, WebServerRuleFinder webServerRuleFinder,
MetadataIndex metadataIndex, RulesKeyVerifier rulesKeyVerifier, StartupRuleUpdater startupRuleUpdater,
- NewRuleCreator newRuleCreator, QualityProfileChangesUpdater qualityProfileChangesUpdater, SonarQubeVersion sonarQubeVersion, DetectPluginChange detectPluginChange) {
+ NewRuleCreator newRuleCreator, QualityProfileChangesUpdater qualityProfileChangesUpdater, SonarQubeVersion sonarQubeVersion,
+ DetectPluginChange detectPluginChange, ActiveRulesImpactInitializer activeRulesImpactInitializer) {
this.defLoader = defLoader;
this.qProfileRules = qProfileRules;
this.dbClient = dbClient;
@@ -102,6 +104,7 @@ public class RulesRegistrant implements Startable {
this.qualityProfileChangesUpdater = qualityProfileChangesUpdater;
this.sonarQubeVersion = sonarQubeVersion;
this.detectPluginChange = detectPluginChange;
+ this.activeRulesImpactInitializer = activeRulesImpactInitializer;
}
@Override
@@ -125,12 +128,15 @@ public class RulesRegistrant implements Startable {
throw new IllegalStateException("Language is mandatory for repository " + repoDef.key());
}
Set<PluginRuleUpdate> pluginRuleUpdates = registerRules(rulesRegistrationContext, repoDef.rules(), dbSession);
+
if (!repoDef.isExternal()) {
// External rules are not part of quality profiles
+ activeRulesImpactInitializer.createImpactsOnActiveRules(rulesRegistrationContext, repoDef, dbSession);
qualityProfileChangesUpdater.createQprofileChangesForRuleUpdates(dbSession, pluginRuleUpdates);
}
dbSession.commit();
}
+ activeRulesImpactInitializer.markInitialPopulationDone();
processRemainingDbRules(rulesRegistrationContext, dbSession);
List<ActiveRuleChange> changes = anyPluginChanged ? removeActiveRulesOnStillExistingRepositories(dbSession, rulesRegistrationContext, rulesRepositories) : List.of();
dbSession.commit();
diff --git a/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevelStartup.java b/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevelStartup.java
index c7272a49d90..e331d7d6551 100644
--- a/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevelStartup.java
+++ b/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevelStartup.java
@@ -46,6 +46,7 @@ import org.sonar.server.rule.registration.QualityProfileChangesUpdater;
import org.sonar.server.rule.registration.RulesKeyVerifier;
import org.sonar.server.rule.registration.RulesRegistrant;
import org.sonar.server.rule.registration.StartupRuleUpdater;
+import org.sonar.server.rule.registration.ActiveRulesImpactInitializer;
import org.sonar.server.startup.RegisterMetrics;
import org.sonar.server.startup.RegisterPermissionTemplates;
import org.sonar.server.startup.RegisterPlugins;
@@ -73,6 +74,7 @@ public class PlatformLevelStartup extends PlatformLevel {
LegacyHotspotRuleDescriptionSectionsGenerator.class,
LegacyIssueRuleDescriptionSectionsGenerator.class,
RulesRegistrant.class,
+ ActiveRulesImpactInitializer.class,
NewRuleCreator.class,
RulesKeyVerifier.class,
StartupRuleUpdater.class,