]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-21043 - Remove Elasticsearch usage for missing rules count
authorantoine.vinot <antoine.vinot@sonarsource.com>
Fri, 17 Nov 2023 16:03:52 +0000 (17:03 +0100)
committersonartech <sonartech@sonarsource.com>
Mon, 20 Nov 2023 20:02:38 +0000 (20:02 +0000)
server/sonar-db-dao/src/it/java/org/sonar/db/qualityprofile/ActiveRuleDaoIT.java
server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/ActiveRuleDao.java
server/sonar-db-dao/src/main/java/org/sonar/db/qualityprofile/ActiveRuleMapper.java
server/sonar-db-dao/src/main/resources/org/sonar/db/qualityprofile/ActiveRuleMapper.xml
server/sonar-webserver-webapi/src/it/java/org/sonar/server/qualityprofile/ws/ShowActionIT.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/qualityprofile/ws/ShowAction.java

index 5af54de947d16786bb73d92b37df6fb46c0ce213..9dfdd9f07d6a2654e10e449f49aaf2d456308e1f 100644 (file)
@@ -42,6 +42,7 @@ import static java.util.Arrays.asList;
 import static java.util.Collections.emptyList;
 import static java.util.Collections.singletonList;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatNoException;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.assertj.core.api.Assertions.entry;
 import static org.assertj.core.api.Assertions.tuple;
@@ -365,7 +366,7 @@ public class ActiveRuleDaoIT {
 
   @Test
   public void delete_does_not_fail_when_active_rule_does_not_exist() {
-    underTest.delete(dbSession, ActiveRuleKey.of(profile1, rule1.getKey()));
+    assertThatNoException().isThrownBy(() -> underTest.delete(dbSession, ActiveRuleKey.of(profile1, rule1.getKey())));
   }
 
   @Test
@@ -705,6 +706,41 @@ public class ActiveRuleDaoIT {
         tuple(ar3.getUuid(), ar3.getRuleKey().repository(), ar3.getRuleKey().rule(), profile2.getRulesProfileUuid(), ar3.getSeverity()));
   }
 
+  @Test
+  public void countMissingRules() {
+    db.qualityProfiles().activateRule(profile1, rule1);
+    db.qualityProfiles().activateRule(profile1, rule3);
+    db.qualityProfiles().activateRule(profile2, rule1);
+    db.qualityProfiles().activateRule(profile2, rule2);
+
+    int result = underTest.countMissingRules(dbSession, profile1.getRulesProfileUuid(), profile2.getRulesProfileUuid());
+
+    assertThat(result).isOne();
+  }
+
+  @Test
+  public void countMissingRules_whenNoRulesInCommon_shouldReturnNumberOfRulesInComparedToProfile() {
+    db.qualityProfiles().activateRule(profile1, rule1);
+    db.qualityProfiles().activateRule(profile2, rule2);
+    db.qualityProfiles().activateRule(profile2, rule3);
+
+    int result = underTest.countMissingRules(dbSession, profile1.getRulesProfileUuid(), profile2.getRulesProfileUuid());
+
+    assertThat(result).isEqualTo(2);
+  }
+
+  @Test
+  public void countMissingRules_whenSomeRulesRemoved_shouldNotCountRemovedRules() {
+    db.qualityProfiles().activateRule(profile1, rule1);
+    db.qualityProfiles().activateRule(profile2, rule2);
+    db.qualityProfiles().activateRule(profile2, rule3);
+    db.qualityProfiles().activateRule(profile2, removedRule);
+
+    int result = underTest.countMissingRules(dbSession, profile1.getRulesProfileUuid(), profile2.getRulesProfileUuid());
+
+    assertThat(result).isEqualTo(2);
+  }
+
   private static class Accumulator implements Consumer<IndexedActiveRuleDto> {
     private final List<IndexedActiveRuleDto> list = new ArrayList<>();
 
index 9dcc54cdd79610d8ecdee620671ff3603a3d1e1c..2fb65852630045435a324d4b443a8920c135de44 100644 (file)
@@ -220,6 +220,10 @@ public class ActiveRuleDao implements Dao {
     });
   }
 
+  public int countMissingRules(DbSession dbSession, String rulesProfileUuid, String compareToRulesProfileUuid) {
+    return mapper(dbSession).countMissingRules(rulesProfileUuid, compareToRulesProfileUuid);
+  }
+
   private static ActiveRuleMapper mapper(DbSession dbSession) {
     return dbSession.getMapper(ActiveRuleMapper.class);
   }
index 75ffd6180a608ce3b8c22c2285c0f118ab38accc..b95d10a2a9acb4ede46cc0d0125ebb11afde16dd 100644 (file)
@@ -84,4 +84,5 @@ public interface ActiveRuleMapper {
 
   void scrollByRuleProfileUuidForIndexing(@Param("ruleProfileUuid") String ruleProfileUuid, ResultHandler<IndexedActiveRuleDto> handler);
 
+  int countMissingRules(@Param("rulesProfileUuid") String rulesProfileUuid, @Param("compareToRulesProfileUuid") String compareToRulesProfileUuid);
 }
index d450e04b1a01bf7ea58360a849d33be0c97ae1a1..96c6fd22f172bae7a9011c24142aa7fe81e8831c 100644 (file)
     delete from active_rule_parameters
     where
       active_rule_uuid in
-    <foreach collection="activeRuleUuids" open="(" close=")" item="activeRuleUuid" separator=",">#{activeRuleUuid, jdbcType=VARCHAR}</foreach>
+    <foreach collection="activeRuleUuids" open="(" close=")" item="activeRuleUuid"
+             separator=",">#{activeRuleUuid, jdbcType=VARCHAR}</foreach>
   </delete>
 
   <select id="selectParamsByActiveRuleUuid" parameterType="String" resultType="ActiveRuleParam">
     group by oqp.uuid
   </select>
 
-  <select id="scrollAllForIndexing" resultType="org.sonar.db.qualityprofile.IndexedActiveRuleDto" fetchSize="${_scrollFetchSize}" resultSetType="FORWARD_ONLY">
+  <select id="scrollAllForIndexing" resultType="org.sonar.db.qualityprofile.IndexedActiveRuleDto" fetchSize="${_scrollFetchSize}"
+          resultSetType="FORWARD_ONLY">
     <include refid="scrollAllForIndexingSql"/>
   </select>
 
-  <select id="scrollByUuidsForIndexing" parameterType="map" resultType="org.sonar.db.qualityprofile.IndexedActiveRuleDto" fetchSize="${_scrollFetchSize}" resultSetType="FORWARD_ONLY">
+  <select id="scrollByUuidsForIndexing" parameterType="map" resultType="org.sonar.db.qualityprofile.IndexedActiveRuleDto"
+          fetchSize="${_scrollFetchSize}" resultSetType="FORWARD_ONLY">
     <include refid="scrollAllForIndexingSql"/>
     where ar.uuid in
     <foreach collection="uuids" open="(" close=")" item="uuid" separator=",">#{uuid, jdbcType=VARCHAR}</foreach>
   </select>
 
-  <select id="scrollByRuleProfileUuidForIndexing" parameterType="String" resultType="org.sonar.db.qualityprofile.IndexedActiveRuleDto" fetchSize="${_scrollFetchSize}" resultSetType="FORWARD_ONLY">
+  <select id="scrollByRuleProfileUuidForIndexing" parameterType="String" resultType="org.sonar.db.qualityprofile.IndexedActiveRuleDto"
+          fetchSize="${_scrollFetchSize}" resultSetType="FORWARD_ONLY">
     <include refid="scrollAllForIndexingSql"/>
     where rp.uuid = #{ruleProfileUuid, jdbcType=VARCHAR}
   </select>
     inner join rules_profiles rp on rp.uuid = ar.profile_uuid
     inner join rules r on r.uuid = ar.rule_uuid
   </sql>
+
+  <select id="countMissingRules" parameterType="String" resultType="int">
+    select count(ar.rule_uuid) from active_rules ar
+    inner join rules r on r.uuid = ar.rule_uuid
+    where r.status != 'REMOVED'
+    and not exists (
+      select 1 from active_rules ar2
+      where ar.rule_uuid = ar2.rule_uuid
+      and ar2.profile_uuid = #{rulesProfileUuid,jdbcType=VARCHAR})
+    and ar.profile_uuid = #{compareToRulesProfileUuid,jdbcType=VARCHAR}
+  </select>
+
 </mapper>
 
index c7fc1eb58de0023f946248780682147af5c2e453..576a1abc9dc9d4baed2c93eaa81eaf171eec632e 100644 (file)
  */
 package org.sonar.server.qualityprofile.ws;
 
+import com.tngtech.java.junit.dataprovider.DataProvider;
+import com.tngtech.java.junit.dataprovider.DataProviderRunner;
+import com.tngtech.java.junit.dataprovider.UseDataProvider;
+import java.util.function.Consumer;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 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.api.utils.System2;
 import org.sonar.db.DbTester;
 import org.sonar.db.qualityprofile.QProfileDto;
 import org.sonar.db.rule.RuleDto;
 import org.sonar.db.user.UserDto;
 import org.sonar.server.es.EsTester;
 import org.sonar.server.exceptions.NotFoundException;
-import org.sonar.server.qualityprofile.index.ActiveRuleIndexer;
-import org.sonar.server.rule.index.RuleIndex;
-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;
@@ -53,11 +54,12 @@ import static org.sonar.test.JsonAssert.assertJson;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_COMPARE_TO_SONAR_WAY;
 import static org.sonarqube.ws.client.qualityprofile.QualityProfileWsParameters.PARAM_KEY;
 
+@RunWith(DataProviderRunner.class)
 public class ShowActionIT {
 
-  private static Language XOO1 = newLanguage("xoo1");
-  private static Language XOO2 = newLanguage("xoo2");
-  private static Languages LANGUAGES = new Languages(XOO1, XOO2);
+  private final static Language XOO1 = newLanguage("xoo1");
+  private final static Language XOO2 = newLanguage("xoo2");
+  private final static Languages LANGUAGES = new Languages(XOO1, XOO2);
 
   @Rule
   public EsTester es = EsTester.create();
@@ -66,12 +68,8 @@ public class ShowActionIT {
   @Rule
   public UserSessionRule userSession = UserSessionRule.standalone();
 
-  private RuleIndexer ruleIndexer = new RuleIndexer(es.client(), db.getDbClient());
-  private ActiveRuleIndexer activeRuleIndexer = new ActiveRuleIndexer(db.getDbClient(), es.client());
-  private RuleIndex ruleIndex = new RuleIndex(es.client(), System2.INSTANCE);
-
   private WsActionTester ws = new WsActionTester(
-    new ShowAction(db.getDbClient(), new QProfileWsSupport(db.getDbClient(), userSession), LANGUAGES, ruleIndex));
+    new ShowAction(db.getDbClient(), new QProfileWsSupport(db.getDbClient(), userSession), LANGUAGES));
 
   @Test
   public void profile_info() {
@@ -162,8 +160,6 @@ public class ShowActionIT {
     db.qualityProfiles().activateRule(sonarWayProfile, commonRule);
     db.qualityProfiles().activateRule(sonarWayProfile, sonarWayRule1);
     db.qualityProfiles().activateRule(sonarWayProfile, sonarWayRule2);
-    ruleIndexer.indexAll();
-    activeRuleIndexer.indexAll();
 
     CompareToSonarWay result = call(ws.newRequest()
       .setParam(PARAM_KEY, profile.getKee())
@@ -182,8 +178,6 @@ public class ShowActionIT {
     RuleDto commonRule = db.rules().insertRule(r -> r.setLanguage(XOO1.getKey()));
     db.qualityProfiles().activateRule(profile, commonRule);
     db.qualityProfiles().activateRule(sonarWayProfile, commonRule);
-    ruleIndexer.indexAll();
-    activeRuleIndexer.indexAll();
 
     CompareToSonarWay result = call(ws.newRequest()
       .setParam(PARAM_KEY, profile.getKee())
@@ -195,50 +189,29 @@ public class ShowActionIT {
       .containsExactly(sonarWayProfile.getKee(), sonarWayProfile.getName(), 0L);
   }
 
-  @Test
-  public void no_comparison_when_sonar_way_does_not_exist() {
-    QProfileDto anotherSonarWayProfile = db.qualityProfiles().insert(p -> p.setIsBuiltIn(true).setName("Another Sonar way").setLanguage(XOO1.getKey()));
-    QProfileDto profile = db.qualityProfiles().insert(p -> p.setLanguage(XOO1.getKey()));
-
-    ShowResponse result = call(ws.newRequest()
-      .setParam(PARAM_KEY, profile.getKee())
-      .setParam(PARAM_COMPARE_TO_SONAR_WAY, "true"));
-
-    assertThat(result.hasCompareToSonarWay()).isFalse();
+  @DataProvider
+  public static Object[][] dataForComparison() {
+    Consumer<QProfileDto> sonarWay = p -> p.setIsBuiltIn(true).setName("Sonar way").setLanguage(XOO1.getKey());
+    Consumer<QProfileDto> notBuiltInSonarWay = p -> p.setIsBuiltIn(false).setName("Sonar way").setLanguage(XOO1.getKey());
+    Consumer<QProfileDto> anotherSonarWay = p -> p.setIsBuiltIn(true).setName("Another Sonar way").setLanguage(XOO1.getKey());
+    Consumer<QProfileDto> anotherBuiltIn = p -> p.setIsBuiltIn(true).setLanguage(XOO1.getKey());
+    Consumer<QProfileDto> profile = p -> p.setLanguage(XOO1.getKey());
+    return new Object[][] {
+      {profile, anotherSonarWay, "true"},
+      {anotherBuiltIn, sonarWay, "true"},
+      {profile, notBuiltInSonarWay, "true"},
+      {profile, sonarWay, "false"}};
   }
 
   @Test
-  public void no_comparison_when_profile_is_built_in() {
-    QProfileDto sonarWayProfile = db.qualityProfiles().insert(p -> p.setIsBuiltIn(true).setName("Sonar way").setLanguage(XOO1.getKey()));
-    QProfileDto anotherBuiltInProfile = db.qualityProfiles().insert(p -> p.setIsBuiltIn(true).setLanguage(XOO1.getKey()));
-
-    ShowResponse result = call(ws.newRequest()
-      .setParam(PARAM_KEY, anotherBuiltInProfile.getKee())
-      .setParam(PARAM_COMPARE_TO_SONAR_WAY, "true"));
-
-    assertThat(result.hasCompareToSonarWay()).isFalse();
-  }
-
-  @Test
-  public void no_comparison_if_sonar_way_is_not_built_in() {
-    QProfileDto sonarWayProfile = db.qualityProfiles().insert(p -> p.setIsBuiltIn(false).setName("Sonar way").setLanguage(XOO1.getKey()));
-    QProfileDto profile = db.qualityProfiles().insert(p -> p.setLanguage(XOO1.getKey()));
-
-    ShowResponse result = call(ws.newRequest()
-      .setParam(PARAM_KEY, profile.getKee())
-      .setParam(PARAM_COMPARE_TO_SONAR_WAY, "true"));
-
-    assertThat(result.hasCompareToSonarWay()).isFalse();
-  }
-
-  @Test
-  public void no_comparison_when_param_is_false() {
-    QProfileDto sonarWayProfile = db.qualityProfiles().insert(p -> p.setIsBuiltIn(true).setName("Sonar way").setLanguage(XOO1.getKey()));
-    QProfileDto profile = db.qualityProfiles().insert(p -> p.setLanguage(XOO1.getKey()));
+  @UseDataProvider("dataForComparison")
+  public void response_shouldNotHaveCompareToSonarWay(Consumer<QProfileDto> profileData, Consumer<QProfileDto> profileToCompareData, String paramCompareToSonarWay) {
+    db.qualityProfiles().insert(profileToCompareData);
+    QProfileDto profile = db.qualityProfiles().insert(profileData);
 
     ShowResponse result = call(ws.newRequest()
       .setParam(PARAM_KEY, profile.getKee())
-      .setParam(PARAM_COMPARE_TO_SONAR_WAY, "false"));
+      .setParam(PARAM_COMPARE_TO_SONAR_WAY, paramCompareToSonarWay));
 
     assertThat(result.hasCompareToSonarWay()).isFalse();
   }
@@ -261,7 +234,7 @@ public class ShowActionIT {
   @Test
   public void compare_to_sonar_way_over_sonarqube_way() {
     QProfileDto sonarWayProfile = db.qualityProfiles().insert(p -> p.setIsBuiltIn(true).setName("Sonar way").setLanguage(XOO1.getKey()));
-    QProfileDto sonarQubeWayProfile = db.qualityProfiles().insert(p -> p.setIsBuiltIn(true).setName("SonarQube way").setLanguage(XOO1.getKey()));
+    db.qualityProfiles().insert(p -> p.setIsBuiltIn(true).setName("SonarQube way").setLanguage(XOO1.getKey()));
     QProfileDto profile = db.qualityProfiles().insert(p -> p.setLanguage(XOO1.getKey()));
 
     CompareToSonarWay result = call(ws.newRequest()
@@ -291,18 +264,16 @@ public class ShowActionIT {
   public void fail_if_profile_language_is_not_supported() {
     QProfileDto profile = db.qualityProfiles().insert(p -> p.setKee("unknown-profile").setLanguage("kotlin"));
 
-    assertThatThrownBy(() -> {
-      call(ws.newRequest().setParam(PARAM_KEY, profile.getKee()));
-    })
+    TestRequest testRequest = ws.newRequest().setParam(PARAM_KEY, profile.getKee());
+    assertThatThrownBy(() -> call(testRequest))
       .isInstanceOf(NotFoundException.class)
       .hasMessage("Quality Profile with key 'unknown-profile' does not exist");
   }
 
   @Test
   public void fail_if_profile_does_not_exist() {
-    assertThatThrownBy(() -> {
-      call(ws.newRequest().setParam(PARAM_KEY, "unknown-profile"));
-    })
+    TestRequest testRequest = ws.newRequest().setParam(PARAM_KEY, "unknown-profile");
+    assertThatThrownBy(() -> call(testRequest))
       .isInstanceOf(NotFoundException.class)
       .hasMessage("Quality Profile with key 'unknown-profile' does not exist");
   }
@@ -331,7 +302,7 @@ public class ShowActionIT {
       .forEach(project -> db.qualityProfiles().associateWithProject(project, profile));
 
     ws = new WsActionTester(
-      new ShowAction(db.getDbClient(), new QProfileWsSupport(db.getDbClient(), userSession), new Languages(cs), ruleIndex));
+      new ShowAction(db.getDbClient(), new QProfileWsSupport(db.getDbClient(), userSession), new Languages(cs)));
     String result = ws.newRequest().setParam(PARAM_KEY, profile.getKee()).execute().getInput();
 
     assertJson(result).ignoreFields("rulesUpdatedAt", "lastUsed", "userUpdatedAt").isSimilarTo(ws.getDef().responseExampleAsString());
@@ -346,11 +317,13 @@ public class ShowActionIT {
     assertThat(action.since()).isEqualTo("6.5");
 
     WebService.Param profile = action.param("key");
+    assertThat(profile).isNotNull();
     assertThat(profile.isRequired()).isTrue();
     assertThat(profile.isInternal()).isFalse();
     assertThat(profile.description()).isNotEmpty();
 
     WebService.Param compareToSonarWay = action.param("compareToSonarWay");
+    assertThat(compareToSonarWay).isNotNull();
     assertThat(compareToSonarWay.isRequired()).isFalse();
     assertThat(compareToSonarWay.isInternal()).isTrue();
     assertThat(compareToSonarWay.description()).isNotEmpty();
index 4bcad7c17a23d010cdd135a5ebd9eb5e5e678a35..d2bf31ce028fa0a27c36a778f87d7d2fef752998 100644 (file)
@@ -34,9 +34,6 @@ import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 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;
 import org.sonarqube.ws.Qualityprofiles.ShowResponse;
 import org.sonarqube.ws.Qualityprofiles.ShowResponse.CompareToSonarWay;
@@ -61,13 +58,11 @@ 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, RuleIndex ruleIndex) {
+  public ShowAction(DbClient dbClient, QProfileWsSupport qProfileWsSupport, Languages languages) {
     this.dbClient = dbClient;
     this.qProfileWsSupport = qProfileWsSupport;
     this.languages = languages;
-    this.ruleIndex = ruleIndex;
   }
 
   @Override
@@ -137,10 +132,7 @@ public class ShowAction implements QProfileWsAction {
       return null;
     }
 
-    long missingRuleCount = ruleIndex.search(
-      new RuleQuery().setQProfile(profile).setActivation(false).setCompareToQProfile(sonarWay),
-      new SearchOptions().setLimit(1))
-      .getTotal();
+    long missingRuleCount = dbClient.activeRuleDao().countMissingRules(dbSession, profile.getRulesProfileUuid(), sonarWay.getRulesProfileUuid());
 
     return CompareToSonarWay.newBuilder()
       .setProfile(sonarWay.getKee())