]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-9482 Show ws counts missing rules compared to Sonar way
authorTeryk Bellahsene <teryk.bellahsene@sonarsource.com>
Thu, 29 Jun 2017 13:01:11 +0000 (15:01 +0200)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Tue, 4 Jul 2017 14:29:36 +0000 (16:29 +0200)
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/ShowAction.java
server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndex.java
server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleQuery.java
server/sonar-server/src/main/resources/org/sonar/server/qualityprofile/ws/show-example.json
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ShowActionTest.java
server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndexTest.java

index 629cc55025a1a5bd3bac5704ab7b0fe04ee41063..cff1902a7b48a1225426224f3b0bf697ef15d8fd 100644 (file)
@@ -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();
   }
 
index 7a623e10dd811957360c69b107e3ad4c2236b3b7..240145724e6f57ef89b10d787d9fb25f9ed3809c 100644 (file)
@@ -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;
index bdd4e25843e1dcf99012c33ab24c31dc1c310457..376e3f5f0356903e71d7b2e762d0bf4d3c2b98bf 100644 (file)
@@ -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;
+  }
 }
index a628efa37c27c3a05325c4c5a86f81eab4e7d642..3d047742da6b6fda07306c7f3fef970d4f3f11da 100644 (file)
     "projectCount": 7,
     "rulesUpdatedAt": "2016-12-22T19:10:03+0100",
     "lastUsed": "2016-12-01T19:10:03+0100"
-  },
-  "compareToSonarWay": {
-    "profile": "iU5OvuD2FLz",
-    "profileName": "Sonar way",
-    "missingRuleCount": 4
   }
 }
index 4393bc8d638f01d38e38ce554b39232e3f0d1941..4d06ff2ff1b78213f80755817924ec1ff19dea39 100644 (file)
@@ -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;
@@ -54,6 +62,8 @@ public class ShowActionTest {
   private static Language XOO2 = newLanguage("xoo2");
   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
@@ -61,8 +71,12 @@ public class ShowActionTest {
   @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) {
index b4f3d8e912e80f6d06652ec4da62e046806f67f7..807d239bfeb1f51f48cf02d522868a3a7d1c7bef 100644 (file)
@@ -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);