diff options
6 files changed, 130 insertions, 28 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/ShowAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/ShowAction.java index 629cc55025a..cff1902a7b4 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/ShowAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/ShowAction.java @@ -35,6 +35,9 @@ import org.sonar.db.DbSession; import org.sonar.db.organization.OrganizationDto; import org.sonar.db.qualityprofile.ActiveRuleCountQuery; import org.sonar.db.qualityprofile.QProfileDto; +import org.sonar.server.es.SearchOptions; +import org.sonar.server.rule.index.RuleIndex; +import org.sonar.server.rule.index.RuleQuery; import org.sonarqube.ws.QualityProfiles.ShowWsResponse; import org.sonarqube.ws.QualityProfiles.ShowWsResponse.CompareToSonarWay; import org.sonarqube.ws.QualityProfiles.ShowWsResponse.QualityProfile; @@ -57,11 +60,13 @@ public class ShowAction implements QProfileWsAction { private final DbClient dbClient; private final QProfileWsSupport qProfileWsSupport; private final Languages languages; + private final RuleIndex ruleIndex; - public ShowAction(DbClient dbClient, QProfileWsSupport qProfileWsSupport, Languages languages) { + public ShowAction(DbClient dbClient, QProfileWsSupport qProfileWsSupport, Languages languages, RuleIndex ruleIndex) { this.dbClient = dbClient; this.qProfileWsSupport = qProfileWsSupport; this.languages = languages; + this.ruleIndex = ruleIndex; } @Override @@ -132,9 +137,15 @@ public class ShowAction implements QProfileWsAction { return null; } + long missingRuleCount = ruleIndex.search( + new RuleQuery().setQProfile(profile).setActivation(false).setCompareToQProfile(sonarWay), + new SearchOptions().setLimit(1)) + .getTotal(); + return CompareToSonarWay.newBuilder() .setProfile(sonarWay.getKee()) .setProfileName(sonarWay.getName()) + .setMissingRuleCount(missingRuleCount) .build(); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndex.java b/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndex.java index 7a623e10dd8..240145724e6 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndex.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndex.java @@ -20,8 +20,6 @@ package org.sonar.server.rule.index; import com.google.common.base.Joiner; -import com.google.common.collect.Collections2; -import com.google.common.collect.ImmutableList; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -56,6 +54,7 @@ import org.sonar.api.rule.RuleKey; import org.sonar.api.rule.RuleStatus; import org.sonar.api.rule.Severity; import org.sonar.api.rules.RuleType; +import org.sonar.core.util.stream.MoreCollectors; import org.sonar.db.organization.OrganizationDto; import org.sonar.db.qualityprofile.QProfileDto; import org.sonar.server.es.EsClient; @@ -65,6 +64,8 @@ import org.sonar.server.es.SearchOptions; import org.sonar.server.es.StickyFacetBuilder; import static com.google.common.base.Preconditions.checkArgument; +import static java.lang.Boolean.FALSE; +import static java.lang.Boolean.TRUE; import static java.util.Optional.ofNullable; import static org.elasticsearch.index.query.QueryBuilders.boolQuery; import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery; @@ -114,10 +115,11 @@ public class RuleIndex { public static final String FACET_TYPES = "types"; public static final String FACET_OLD_DEFAULT = "true"; - public static final List<String> ALL_STATUSES_EXCEPT_REMOVED = ImmutableList.copyOf( - Collections2.filter( - Collections2.transform(Arrays.asList(RuleStatus.values()), RuleStatus::toString), - input -> !RuleStatus.REMOVED.toString().equals(input))); + public static final List<String> ALL_STATUSES_EXCEPT_REMOVED = Arrays.stream(RuleStatus.values()) + .filter(status -> !RuleStatus.REMOVED.equals(status)) + .map(RuleStatus::toString) + .collect(MoreCollectors.toList()); + private static final String AGGREGATION_NAME = "_ref"; private static final String AGGREGATION_NAME_FOR_TAGS = "tagsAggregation"; private final EsClient client; @@ -296,29 +298,35 @@ public class RuleIndex { if (query.getActivation() != null && profile != null) { // ActiveRule Filter (profile and inheritance) - BoolQueryBuilder childrenFilter = boolQuery(); - addTermFilter(childrenFilter, FIELD_ACTIVE_RULE_PROFILE_UUID, profile.getRulesProfileUuid()); - addTermFilter(childrenFilter, FIELD_ACTIVE_RULE_INHERITANCE, query.getInheritance()); - addTermFilter(childrenFilter, FIELD_ACTIVE_RULE_SEVERITY, query.getActiveSeverities()); + BoolQueryBuilder activeRuleFilter = boolQuery(); + addTermFilter(activeRuleFilter, FIELD_ACTIVE_RULE_PROFILE_UUID, profile.getRulesProfileUuid()); + addTermFilter(activeRuleFilter, FIELD_ACTIVE_RULE_INHERITANCE, query.getInheritance()); + addTermFilter(activeRuleFilter, FIELD_ACTIVE_RULE_SEVERITY, query.getActiveSeverities()); // ChildQuery QueryBuilder childQuery; - if (childrenFilter.hasClauses()) { - childQuery = childrenFilter; + if (activeRuleFilter.hasClauses()) { + childQuery = activeRuleFilter; } else { childQuery = matchAllQuery(); } - if (Boolean.TRUE.equals(query.getActivation())) { + if (TRUE.equals(query.getActivation())) { filters.put("activation", QueryBuilders.hasChildQuery(INDEX_TYPE_ACTIVE_RULE.getType(), childQuery)); - } else if (Boolean.FALSE.equals(query.getActivation())) { + } else if (FALSE.equals(query.getActivation())) { filters.put("activation", boolQuery().mustNot( QueryBuilders.hasChildQuery(INDEX_TYPE_ACTIVE_RULE.getType(), childQuery))); } + QProfileDto compareToQProfile = query.getCompareToQProfile(); + if (compareToQProfile != null) { + filters.put("comparison", + QueryBuilders.hasChildQuery(INDEX_TYPE_ACTIVE_RULE.getType(), + boolQuery().must(QueryBuilders.termQuery(FIELD_ACTIVE_RULE_PROFILE_UUID, compareToQProfile.getRulesProfileUuid())))); + } } return filters; diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleQuery.java b/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleQuery.java index bdd4e25843e..376e3f5f035 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleQuery.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleQuery.java @@ -43,6 +43,7 @@ public class RuleQuery { private Collection<RuleType> types; private Boolean activation; private QProfileDto profile; + private QProfileDto compareToQProfile; private Collection<String> inheritance; private Collection<String> activeSeverities; private String templateKey; @@ -272,4 +273,14 @@ public class RuleQuery { this.organization = o; return this; } + + @CheckForNull + public QProfileDto getCompareToQProfile() { + return compareToQProfile; + } + + public RuleQuery setCompareToQProfile(@Nullable QProfileDto compareToQProfile) { + this.compareToQProfile = compareToQProfile; + return this; + } } diff --git a/server/sonar-server/src/main/resources/org/sonar/server/qualityprofile/ws/show-example.json b/server/sonar-server/src/main/resources/org/sonar/server/qualityprofile/ws/show-example.json index a628efa37c2..3d047742da6 100644 --- a/server/sonar-server/src/main/resources/org/sonar/server/qualityprofile/ws/show-example.json +++ b/server/sonar-server/src/main/resources/org/sonar/server/qualityprofile/ws/show-example.json @@ -12,10 +12,5 @@ "projectCount": 7, "rulesUpdatedAt": "2016-12-22T19:10:03+0100", "lastUsed": "2016-12-01T19:10:03+0100" - }, - "compareToSonarWay": { - "profile": "iU5OvuD2FLz", - "profileName": "Sonar way", - "missingRuleCount": 4 } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ShowActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ShowActionTest.java index 4393bc8d638..4d06ff2ff1b 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ShowActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ShowActionTest.java @@ -23,14 +23,22 @@ package org.sonar.server.qualityprofile.ws; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.sonar.api.config.MapSettings; import org.sonar.api.resources.Language; import org.sonar.api.resources.Languages; import org.sonar.api.server.ws.WebService; import org.sonar.api.utils.DateUtils; import org.sonar.db.DbTester; import org.sonar.db.qualityprofile.QProfileDto; +import org.sonar.db.rule.RuleDefinitionDto; +import org.sonar.server.es.EsTester; import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.organization.TestDefaultOrganizationProvider; +import org.sonar.server.qualityprofile.index.ActiveRuleIndexer; +import org.sonar.server.qualityprofile.index.ActiveRuleIteratorFactory; +import org.sonar.server.rule.index.RuleIndex; +import org.sonar.server.rule.index.RuleIndexDefinition; +import org.sonar.server.rule.index.RuleIndexer; import org.sonar.server.tester.UserSessionRule; import org.sonar.server.ws.TestRequest; import org.sonar.server.ws.WsActionTester; @@ -55,14 +63,20 @@ public class ShowActionTest { private static Languages LANGUAGES = new Languages(XOO1, XOO2); @Rule + public EsTester es = new EsTester(new RuleIndexDefinition(new MapSettings())); + @Rule public DbTester db = DbTester.create(); @Rule public UserSessionRule userSession = UserSessionRule.standalone(); @Rule public ExpectedException expectedException = ExpectedException.none(); + private RuleIndexer ruleIndexer = new RuleIndexer(es.client(), db.getDbClient()); + private ActiveRuleIndexer activeRuleIndexer = new ActiveRuleIndexer(db.getDbClient(), es.client(), new ActiveRuleIteratorFactory(db.getDbClient())); + private RuleIndex ruleIndex = new RuleIndex(es.client()); + private WsActionTester ws = new WsActionTester( - new ShowAction(db.getDbClient(), new QProfileWsSupport(db.getDbClient(), userSession, TestDefaultOrganizationProvider.from(db)), LANGUAGES)); + new ShowAction(db.getDbClient(), new QProfileWsSupport(db.getDbClient(), userSession, TestDefaultOrganizationProvider.from(db)), LANGUAGES, ruleIndex)); @Test public void test_definition() { @@ -161,6 +175,41 @@ public class ShowActionTest { public void compare_to_sonar_way_profile() { QProfileDto sonarWayProfile = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setIsBuiltIn(true).setName("Sonar way").setLanguage(XOO1.getKey())); QProfileDto profile = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setLanguage(XOO1.getKey())); + RuleDefinitionDto commonRule = db.rules().insertRule(r -> r.setLanguage(XOO1.getKey())).getDefinition(); + RuleDefinitionDto sonarWayRule1 = db.rules().insertRule(r -> r.setLanguage(XOO1.getKey())).getDefinition(); + RuleDefinitionDto sonarWayRule2 = db.rules().insertRule(r -> r.setLanguage(XOO1.getKey())).getDefinition(); + RuleDefinitionDto profileRule1 = db.rules().insertRule(r -> r.setLanguage(XOO1.getKey())).getDefinition(); + RuleDefinitionDto profileRule2 = db.rules().insertRule(r -> r.setLanguage(XOO1.getKey())).getDefinition(); + RuleDefinitionDto profileRule3 = db.rules().insertRule(r -> r.setLanguage(XOO1.getKey())).getDefinition(); + db.qualityProfiles().activateRule(profile, commonRule); + db.qualityProfiles().activateRule(profile, profileRule1); + db.qualityProfiles().activateRule(profile, profileRule2); + db.qualityProfiles().activateRule(profile, profileRule3); + db.qualityProfiles().activateRule(sonarWayProfile, commonRule); + db.qualityProfiles().activateRule(sonarWayProfile, sonarWayRule1); + db.qualityProfiles().activateRule(sonarWayProfile, sonarWayRule2); + ruleIndexer.indexOnStartup(ruleIndexer.getIndexTypes()); + activeRuleIndexer.indexOnStartup(activeRuleIndexer.getIndexTypes()); + + CompareToSonarWay result = call(ws.newRequest() + .setParam(PARAM_PROFILE, profile.getKee()) + .setParam(PARAM_COMPARE_TO_SONAR_WAY, "true")) + .getCompareToSonarWay(); + + assertThat(result) + .extracting(CompareToSonarWay::getProfile, CompareToSonarWay::getProfileName, CompareToSonarWay::getMissingRuleCount) + .containsExactly(sonarWayProfile.getKee(), sonarWayProfile.getName(), 2L); + } + + @Test + public void compare_to_sonar_way_profile_when_same_active_rules() { + QProfileDto sonarWayProfile = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setIsBuiltIn(true).setName("Sonar way").setLanguage(XOO1.getKey())); + QProfileDto profile = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setLanguage(XOO1.getKey())); + RuleDefinitionDto commonRule = db.rules().insertRule(r -> r.setLanguage(XOO1.getKey())).getDefinition(); + db.qualityProfiles().activateRule(profile, commonRule); + db.qualityProfiles().activateRule(sonarWayProfile, commonRule); + ruleIndexer.indexOnStartup(ruleIndexer.getIndexTypes()); + activeRuleIndexer.indexOnStartup(activeRuleIndexer.getIndexTypes()); CompareToSonarWay result = call(ws.newRequest() .setParam(PARAM_PROFILE, profile.getKee()) @@ -168,13 +217,14 @@ public class ShowActionTest { .getCompareToSonarWay(); assertThat(result) - .extracting(CompareToSonarWay::getProfile, CompareToSonarWay::getProfileName) - .containsExactly(sonarWayProfile.getKee(), sonarWayProfile.getName()); + .extracting(CompareToSonarWay::getProfile, CompareToSonarWay::getProfileName, CompareToSonarWay::getMissingRuleCount) + .containsExactly(sonarWayProfile.getKee(), sonarWayProfile.getName(), 0L); } @Test public void no_comparison_when_sonar_way_does_not_exist() { - QProfileDto anotherSonarWayProfile = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setIsBuiltIn(true).setName("Another Sonar way").setLanguage(XOO1.getKey())); + QProfileDto anotherSonarWayProfile = db.qualityProfiles().insert(db.getDefaultOrganization(), + p -> p.setIsBuiltIn(true).setName("Another Sonar way").setLanguage(XOO1.getKey())); QProfileDto profile = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setLanguage(XOO1.getKey())); ShowWsResponse result = call(ws.newRequest() @@ -228,7 +278,7 @@ public class ShowActionTest { CompareToSonarWay result = call(ws.newRequest() .setParam(PARAM_PROFILE, profile.getKee()) .setParam(PARAM_COMPARE_TO_SONAR_WAY, "true")) - .getCompareToSonarWay(); + .getCompareToSonarWay(); assertThat(result) .extracting(CompareToSonarWay::getProfile, CompareToSonarWay::getProfileName) @@ -244,7 +294,7 @@ public class ShowActionTest { CompareToSonarWay result = call(ws.newRequest() .setParam(PARAM_PROFILE, profile.getKee()) .setParam(PARAM_COMPARE_TO_SONAR_WAY, "true")) - .getCompareToSonarWay(); + .getCompareToSonarWay(); assertThat(result) .extracting(CompareToSonarWay::getProfile, CompareToSonarWay::getProfileName) @@ -292,10 +342,11 @@ public class ShowActionTest { .mapToObj(i -> db.components().insertPrivateProject()) .forEach(project -> db.qualityProfiles().associateWithProject(project, profile)); - ws = new WsActionTester(new ShowAction(db.getDbClient(), new QProfileWsSupport(db.getDbClient(), userSession, TestDefaultOrganizationProvider.from(db)), new Languages(cs))); + ws = new WsActionTester( + new ShowAction(db.getDbClient(), new QProfileWsSupport(db.getDbClient(), userSession, TestDefaultOrganizationProvider.from(db)), new Languages(cs), ruleIndex)); String result = ws.newRequest().setParam(PARAM_PROFILE, profile.getKee()).execute().getInput(); - assertJson(result).ignoreFields("rulesUpdatedAt", "lastUsed", "userUpdatedAt", "compareToSonarWay").isSimilarTo(ws.getDef().responseExampleAsString()); + assertJson(result).ignoreFields("rulesUpdatedAt", "lastUsed", "userUpdatedAt").isSimilarTo(ws.getDef().responseExampleAsString()); } private ShowWsResponse call(TestRequest request) { diff --git a/server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndexTest.java b/server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndexTest.java index b4f3d8e912e..807d239bfeb 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndexTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndexTest.java @@ -383,6 +383,32 @@ public class RuleIndexTest { assertThat(underTest.search(query, new SearchOptions()).getIds()).hasSize(2); } + @Test + public void compare_to_another_profile() { + String xoo = "xoo"; + QProfileDto profile = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setLanguage(xoo)); + QProfileDto anotherProfile = db.qualityProfiles().insert(db.getDefaultOrganization(), p -> p.setLanguage(xoo)); + RuleDefinitionDto commonRule = db.rules().insertRule(r -> r.setLanguage(xoo)).getDefinition(); + RuleDefinitionDto profileRule1 = db.rules().insertRule(r -> r.setLanguage(xoo)).getDefinition(); + RuleDefinitionDto profileRule2 = db.rules().insertRule(r -> r.setLanguage(xoo)).getDefinition(); + RuleDefinitionDto profileRule3 = db.rules().insertRule(r -> r.setLanguage(xoo)).getDefinition(); + RuleDefinitionDto anotherProfileRule1 = db.rules().insertRule(r -> r.setLanguage(xoo)).getDefinition(); + RuleDefinitionDto anotherProfileRule2 = db.rules().insertRule(r -> r.setLanguage(xoo)).getDefinition(); + db.qualityProfiles().activateRule(profile, commonRule); + db.qualityProfiles().activateRule(profile, profileRule1); + db.qualityProfiles().activateRule(profile, profileRule2); + db.qualityProfiles().activateRule(profile, profileRule3); + db.qualityProfiles().activateRule(anotherProfile, commonRule); + db.qualityProfiles().activateRule(anotherProfile, anotherProfileRule1); + db.qualityProfiles().activateRule(anotherProfile, anotherProfileRule2); + index(); + + verifySearch(newRuleQuery().setActivation(false).setQProfile(profile).setCompareToQProfile(anotherProfile), anotherProfileRule1, anotherProfileRule2); + verifySearch(newRuleQuery().setActivation(true).setQProfile(profile).setCompareToQProfile(anotherProfile), commonRule); + verifySearch(newRuleQuery().setActivation(true).setQProfile(profile).setCompareToQProfile(profile), commonRule, profileRule1, profileRule2, profileRule3); + verifySearch(newRuleQuery().setActivation(false).setQProfile(profile).setCompareToQProfile(profile)); + } + @SafeVarargs private final RuleDefinitionDto createRule(Consumer<RuleDefinitionDto>... consumers) { return db.rules().insert(consumers); |