@@ -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); |
@@ -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); | |||
} | |||
} |
@@ -49,6 +49,8 @@ public interface RuleMapper { | |||
List<RuleDto> 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); |
@@ -244,11 +244,23 @@ | |||
from | |||
rules r | |||
where | |||
r.status in ('READY', 'DEPRECATED') | |||
and r.is_external=${_false} | |||
and r.language=#{language, jdbcType=VARCHAR} | |||
<include refid="conditionNotExternalRulesByLanguage" /> | |||
</select> | |||
<select id="countByLanguage" resultType="java.lang.Long"> | |||
select count(*) | |||
from | |||
rules r | |||
where | |||
<include refid="conditionNotExternalRulesByLanguage" /> | |||
</select> | |||
<sql id="conditionNotExternalRulesByLanguage"> | |||
r.status in ('READY', 'DEPRECATED', 'BETA') | |||
and r.is_external=${_false} | |||
and r.language=#{language, jdbcType=VARCHAR} | |||
</sql> | |||
<insert id="insertRuleDescriptionSection" parameterType="Map" useGeneratedKeys="false"> | |||
insert into rule_desc_sections ( | |||
uuid, |
@@ -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 |
@@ -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 | |||
} | |||
] | |||
} | |||
} |
@@ -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<String, Long> countRulesByProfileKey; | |||
private final Map<String, Long> countOverridingRulesByProfileKey; | |||
private final Map<String, Long> countInactiveRuleByProfileKey = new HashMap<>(); | |||
private Statistics(DbSession dbSession, List<QProfileDto> profiles) { | |||
private Statistics(DbSession dbSession, List<QProfileDto> 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)); | |||
} | |||
} | |||
} |
@@ -100,6 +100,7 @@ message InheritanceWsResponse { | |||
optional int64 activeRuleCount = 4; | |||
optional int64 overridingRuleCount = 5; | |||
optional bool isBuiltIn = 6; | |||
optional int64 inactiveRuleCount = 7; | |||
} | |||
} | |||