From 2245ffea6e849eaca1e2a5d34597ecdb64c14b0e Mon Sep 17 00:00:00 2001 From: Benjamin Campomenosi Date: Thu, 28 Sep 2023 16:07:31 +0200 Subject: [PATCH] SONAR-20546 add inactiveRuleCount to `api/qualityprofiles/inheritance` --- .../it/java/org/sonar/db/rule/RuleDaoIT.java | 47 +++++++++++++++---- .../main/java/org/sonar/db/rule/RuleDao.java | 4 ++ .../java/org/sonar/db/rule/RuleMapper.java | 2 + .../org/sonar/db/rule/RuleMapper.xml | 18 +++++-- .../ws/InheritanceActionIT.java | 18 +++---- .../inheritance-buWide.json | 12 ++++- .../qualityprofile/ws/InheritanceAction.java | 11 ++++- .../main/protobuf/ws-qualityprofiles.proto | 1 + 8 files changed, 89 insertions(+), 24 deletions(-) diff --git a/server/sonar-db-dao/src/it/java/org/sonar/db/rule/RuleDaoIT.java b/server/sonar-db-dao/src/it/java/org/sonar/db/rule/RuleDaoIT.java index d8a5fb2d4aa..d42c105b682 100644 --- a/server/sonar-db-dao/src/it/java/org/sonar/db/rule/RuleDaoIT.java +++ b/server/sonar-db-dao/src/it/java/org/sonar/db/rule/RuleDaoIT.java @@ -63,6 +63,8 @@ import static org.assertj.core.api.Assertions.tuple; import static org.sonar.api.issue.impact.SoftwareQuality.MAINTAINABILITY; import static org.sonar.api.issue.impact.SoftwareQuality.RELIABILITY; import static org.sonar.api.issue.impact.SoftwareQuality.SECURITY; +import static org.sonar.api.rule.RuleStatus.*; +import static org.sonar.api.rule.RuleStatus.DEPRECATED; import static org.sonar.api.rule.RuleStatus.REMOVED; public class RuleDaoIT { @@ -478,7 +480,7 @@ public class RuleDaoIT { .setDescriptionFormat(RuleDto.Format.MARKDOWN) .addRuleDescriptionSectionDto(sectionDto) .addDefaultImpact(ruleDefaultImpactDto) - .setStatus(RuleStatus.DEPRECATED) + .setStatus(DEPRECATED) .setConfigKey("NewConfigKey") .setSeverity(Severity.INFO) .setIsTemplate(true) @@ -503,7 +505,7 @@ public class RuleDaoIT { RuleDto ruleDto = underTest.selectOrFailByKey(db.getSession(), RuleKey.of("plugin", "NewRuleKey")); assertThat(ruleDto.getUuid()).isNotNull(); assertThat(ruleDto.getName()).isEqualTo("new name"); - assertThat(ruleDto.getStatus()).isEqualTo(RuleStatus.DEPRECATED); + assertThat(ruleDto.getStatus()).isEqualTo(DEPRECATED); assertThat(ruleDto.getRuleKey()).isEqualTo("NewRuleKey"); assertThat(ruleDto.getRepositoryKey()).isEqualTo("plugin"); assertThat(ruleDto.getConfigKey()).isEqualTo("NewConfigKey"); @@ -526,7 +528,11 @@ public class RuleDaoIT { assertThat(ruleDto.getDescriptionFormat()).isEqualTo(RuleDto.Format.MARKDOWN); assertThat(ruleDto.getRuleDescriptionSectionDtos()).usingRecursiveFieldByFieldElementComparator() .containsOnly(sectionDto); - assertThat(ruleDto.getCleanCodeAttribute()).isEqualTo(CleanCodeAttribute.CLEAR); + assertCleanCodeInformation(ruleDto, CleanCodeAttribute.CLEAR, ruleDefaultImpactDto); + } + + private static void assertCleanCodeInformation(RuleDto ruleDto, CleanCodeAttribute attribute, ImpactDto ruleDefaultImpactDto) { + assertThat(ruleDto.getCleanCodeAttribute()).isEqualTo(attribute); assertThat(ruleDto.getDefaultImpacts()).usingRecursiveFieldByFieldElementComparator() .containsOnly(ruleDefaultImpactDto); } @@ -542,7 +548,7 @@ public class RuleDaoIT { .setName("new name") .setDescriptionFormat(RuleDto.Format.MARKDOWN) .addRuleDescriptionSectionDto(sectionDto) - .setStatus(RuleStatus.DEPRECATED) + .setStatus(DEPRECATED) .setConfigKey("NewConfigKey") .setSeverity(Severity.INFO) .setIsTemplate(true) @@ -566,7 +572,7 @@ public class RuleDaoIT { RuleDto ruleDto = underTest.selectOrFailByKey(db.getSession(), RuleKey.of("plugin", "NewRuleKey")); assertThat(ruleDto.getName()).isEqualTo("new name"); - assertThat(ruleDto.getStatus()).isEqualTo(RuleStatus.DEPRECATED); + assertThat(ruleDto.getStatus()).isEqualTo(DEPRECATED); assertThat(ruleDto.getRuleKey()).isEqualTo("NewRuleKey"); assertThat(ruleDto.getRepositoryKey()).isEqualTo("plugin"); assertThat(ruleDto.getConfigKey()).isEqualTo("NewConfigKey"); @@ -854,9 +860,10 @@ public class RuleDaoIT { .setDefaultValue("30") .setDescription("My Parameter"); - underTest.insertRuleParam(db.getSession(), ruleDefinitionDto, param); + DbSession session = db.getSession(); + underTest.insertRuleParam(session, ruleDefinitionDto, param); - assertThatThrownBy(() -> underTest.insertRuleParam(db.getSession(), ruleDefinitionDto, param)) + assertThatThrownBy(() -> underTest.insertRuleParam(session, ruleDefinitionDto, param)) .isInstanceOf(PersistenceException.class); } @@ -1171,15 +1178,37 @@ public class RuleDaoIT { assertThat(deprecatedRuleKeyDto1.getRuleUuid()).isEqualTo(r1.getUuid()); } + @Test + public void countByLanguage_shouldReturnExpectedNumberOfRules() { + insertMultipleRules(10, "js", false, READY); + insertMultipleRules(5, "js", true, READY); + insertMultipleRules(3, "kotlin", false, DEPRECATED); + insertMultipleRules(3, "java", false, REMOVED); + + db.getSession().commit(); + + assertThat(underTest.countByLanguage(db.getSession(), "js")).isEqualTo(10); + assertThat(underTest.countByLanguage(db.getSession(), "kotlin")).isEqualTo(3); + assertThat(underTest.countByLanguage(db.getSession(), "java")).isZero(); + + } + + private void insertMultipleRules(int rulesCount, String language, boolean isExternal, RuleStatus ruleStatus) { + for (int i = 0; i < rulesCount; i++) { + db.rules().insert(rule -> rule.setLanguage(language).setIsExternal(isExternal).setStatus(ruleStatus)); + } + } + @Test public void insertDeprecatedRuleKey_with_same_RuleKey_should_fail() { String repositoryKey = randomAlphanumeric(50); String ruleKey = randomAlphanumeric(50); - db.rules().insertDeprecatedKey(d -> d.setOldRepositoryKey(repositoryKey) + RuleDbTester ruleTester = db.rules(); + ruleTester.insertDeprecatedKey(d -> d.setOldRepositoryKey(repositoryKey) .setOldRuleKey(ruleKey)); assertThatThrownBy(() -> { - db.rules().insertDeprecatedKey(d -> d.setOldRepositoryKey(repositoryKey) + ruleTester.insertDeprecatedKey(d -> d.setOldRepositoryKey(repositoryKey) .setOldRuleKey(ruleKey)); }) .isInstanceOf(PersistenceException.class); diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDao.java index 6301c9356d5..a90a0b5cfeb 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDao.java @@ -246,4 +246,8 @@ public class RuleDao implements Dao { public void insert(DbSession dbSession, DeprecatedRuleKeyDto deprecatedRuleKey) { mapper(dbSession).insertDeprecatedRuleKey(deprecatedRuleKey); } + + public long countByLanguage(DbSession dbSession, String language) { + return mapper(dbSession).countByLanguage(language); + } } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleMapper.java index 5595860cad9..b4d901e2735 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleMapper.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleMapper.java @@ -49,6 +49,8 @@ public interface RuleMapper { List selectByLanguage(@Param("language") String language); + Long countByLanguage(@Param("language") String language); + void insertRule(RuleDto ruleDefinitionDto); void insertRuleDescriptionSection(@Param("ruleUuid") String ruleUuid, @Param("dto") RuleDescriptionSectionDto ruleDescriptionSectionDto); diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/rule/RuleMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/rule/RuleMapper.xml index e0518e7009d..20841b59837 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/rule/RuleMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/rule/RuleMapper.xml @@ -244,11 +244,23 @@ from rules r where - r.status in ('READY', 'DEPRECATED') - and r.is_external=${_false} - and r.language=#{language, jdbcType=VARCHAR} + + + + + r.status in ('READY', 'DEPRECATED', 'BETA') + and r.is_external=${_false} + and r.language=#{language, jdbcType=VARCHAR} + + insert into rule_desc_sections ( uuid, diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualityprofile/ws/InheritanceActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualityprofile/ws/InheritanceActionIT.java index a722ac77742..c3bf2ca77be 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualityprofile/ws/InheritanceActionIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualityprofile/ws/InheritanceActionIT.java @@ -137,19 +137,18 @@ public class InheritanceActionIT { @Test public void inheritance_parent_child() throws Exception { - RuleDto rule1 = db.rules().insert(); - RuleDto rule2 = db.rules().insert(); - RuleDto rule3 = db.rules().insert(); + String language = "java"; + RuleDto rule1 = db.rules().insert(r -> r.setLanguage(language)); + RuleDto rule2 = db.rules().insert(r -> r.setLanguage(language)); + RuleDto rule3 = db.rules().insert(r -> r.setLanguage(language)); ruleIndexer.commitAndIndex(db.getSession(), asList(rule1.getUuid(), rule2.getUuid(), rule3.getUuid())); - QProfileDto parent = db.qualityProfiles().insert(); + QProfileDto parent = db.qualityProfiles().insert(p -> p.setLanguage(language)); db.qualityProfiles().activateRule(parent, rule1); db.qualityProfiles().activateRule(parent, rule2); - long parentRules = 2; - QProfileDto child = db.qualityProfiles().insert(q -> q.setParentKee(parent.getKee())); + QProfileDto child = db.qualityProfiles().insert(q -> q.setParentKee(parent.getKee()).setLanguage(language)); db.qualityProfiles().activateRule(child, rule3); - long childRules = 1; activeRuleIndexer.indexAll(); @@ -163,10 +162,11 @@ public class InheritanceActionIT { InheritanceWsResponse result = InheritanceWsResponse.parseFrom(response); assertThat(result.getProfile().getKey()).isEqualTo(child.getKee()); - assertThat(result.getProfile().getActiveRuleCount()).isEqualTo(childRules); + assertThat(result.getProfile().getActiveRuleCount()).isEqualTo(1); + assertThat(result.getProfile().getInactiveRuleCount()).isEqualTo(2); assertThat(result.getAncestorsList()).extracting(InheritanceWsResponse.QualityProfile::getKey).containsExactly(parent.getKee()); - assertThat(result.getAncestorsList()).extracting(InheritanceWsResponse.QualityProfile::getActiveRuleCount).containsExactly(parentRules); + assertThat(result.getAncestorsList()).extracting(InheritanceWsResponse.QualityProfile::getActiveRuleCount).containsExactly(2L); } @Test diff --git a/server/sonar-webserver-webapi/src/it/resources/org/sonar/server/qualityprofile/ws/InheritanceActionIT/inheritance-buWide.json b/server/sonar-webserver-webapi/src/it/resources/org/sonar/server/qualityprofile/ws/InheritanceActionIT/inheritance-buWide.json index 3e71ac9ebc7..d26e671727d 100644 --- a/server/sonar-webserver-webapi/src/it/resources/org/sonar/server/qualityprofile/ws/InheritanceActionIT/inheritance-buWide.json +++ b/server/sonar-webserver-webapi/src/it/resources/org/sonar/server/qualityprofile/ws/InheritanceActionIT/inheritance-buWide.json @@ -5,6 +5,7 @@ "parent": "xoo-my-company-profile-12345", "activeRuleCount": 2, "overridingRuleCount": 1, + "inactiveRuleCount": 1, "isBuiltIn": false }, "ancestors": [ @@ -13,12 +14,16 @@ "name": "My Company Profile", "parent": "xoo-sonar-way", "activeRuleCount": 2, + "overridingRuleCount": 0, + "inactiveRuleCount": 1, "isBuiltIn": false }, { "key": "xoo-sonar-way", "name": "Sonar way", "activeRuleCount": 2, + "overridingRuleCount": 0, + "inactiveRuleCount": 1, "isBuiltIn": true } ], @@ -26,15 +31,20 @@ { "key": "xoo-for-project-one-34567", "name": "For Project One", + "parent": "xoo-my-bu-profile-23456", "activeRuleCount": 3, + "overridingRuleCount": 0, + "inactiveRuleCount": 0, "isBuiltIn": false }, { "key": "xoo-for-project-two-45678", "name": "For Project Two", + "parent": "xoo-my-bu-profile-23456", "activeRuleCount": 2, "overridingRuleCount": 1, + "inactiveRuleCount": 1, "isBuiltIn": false } ] -} +} \ No newline at end of file diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/InheritanceAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/InheritanceAction.java index a6340a13eb4..d4520969fd6 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/InheritanceAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/InheritanceAction.java @@ -20,9 +20,11 @@ package org.sonar.server.qualityprofile.ws; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; import org.sonar.api.resources.Languages; +import org.sonar.api.server.ws.Change; import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.WebService.NewAction; @@ -57,6 +59,7 @@ public class InheritanceAction implements QProfileWsAction { NewAction inheritance = context.createAction("inheritance") .setSince("5.2") .setDescription("Show a quality profile's ancestors and children.") + .setChangelog(new Change("10.3", "Field 'inactiveRuleCount' added to the response")) .setHandler(this) .setResponseExample(getClass().getResource("inheritance-example.json")); @@ -74,7 +77,7 @@ public class InheritanceAction implements QProfileWsAction { allProfiles.add(profile); allProfiles.addAll(ancestors); allProfiles.addAll(children); - Statistics statistics = new Statistics(dbSession, allProfiles); + Statistics statistics = new Statistics(dbSession, allProfiles, profile.getLanguage()); writeProtobuf(buildResponse(profile, ancestors, children, statistics), request, response); } @@ -131,6 +134,7 @@ public class InheritanceAction implements QProfileWsAction { .setName(qualityProfile.getName()) .setActiveRuleCount(statistics.countRulesByProfileKey.getOrDefault(key, 0L)) .setOverridingRuleCount(statistics.countOverridingRulesByProfileKey.getOrDefault(key, 0L)) + .setInactiveRuleCount(statistics.countInactiveRuleByProfileKey.getOrDefault(key, 0L)) .setIsBuiltIn(qualityProfile.isBuiltIn()); ofNullable(qualityProfile.getParentKee()).ifPresent(builder::setParent); return builder.build(); @@ -139,12 +143,15 @@ public class InheritanceAction implements QProfileWsAction { private class Statistics { private final Map countRulesByProfileKey; private final Map countOverridingRulesByProfileKey; + private final Map countInactiveRuleByProfileKey = new HashMap<>(); - private Statistics(DbSession dbSession, List profiles) { + private Statistics(DbSession dbSession, List profiles, String language) { ActiveRuleDao dao = dbClient.activeRuleDao(); ActiveRuleCountQuery.Builder builder = ActiveRuleCountQuery.builder(); countRulesByProfileKey = dao.countActiveRulesByQuery(dbSession, builder.setProfiles(profiles).build()); countOverridingRulesByProfileKey = dao.countActiveRulesByQuery(dbSession, builder.setProfiles(profiles).setInheritance(OVERRIDES).build()); + long totalRuleAvailable = dbClient.ruleDao().countByLanguage(dbSession, language); + countRulesByProfileKey.forEach((profileKey, activeRules) -> countInactiveRuleByProfileKey.put(profileKey, totalRuleAvailable - activeRules)); } } } diff --git a/sonar-ws/src/main/protobuf/ws-qualityprofiles.proto b/sonar-ws/src/main/protobuf/ws-qualityprofiles.proto index 4d9cd5d44ac..2cc8bba8afb 100644 --- a/sonar-ws/src/main/protobuf/ws-qualityprofiles.proto +++ b/sonar-ws/src/main/protobuf/ws-qualityprofiles.proto @@ -100,6 +100,7 @@ message InheritanceWsResponse { optional int64 activeRuleCount = 4; optional int64 overridingRuleCount = 5; optional bool isBuiltIn = 6; + optional int64 inactiveRuleCount = 7; } } -- 2.39.5