]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-8952 make api/rules/tags organization aware
authorDaniel Schwarz <daniel.schwarz@sonarsource.com>
Mon, 3 Apr 2017 07:25:36 +0000 (09:25 +0200)
committerDaniel Schwarz <bartfastiel@users.noreply.github.com>
Fri, 14 Apr 2017 06:57:18 +0000 (08:57 +0200)
A new elasticsearch type “ruleExtension” is added to the rules index. It stores either system tags, or tags for one, specific organization.

64 files changed:
server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleDao.java
server/sonar-db-dao/src/main/java/org/sonar/db/rule/RuleMapper.java
server/sonar-db-dao/src/main/resources/org/sonar/db/rule/RuleMapper.xml
server/sonar-db-dao/src/test/java/org/sonar/db/qualityprofile/ActiveRuleDaoTest.java
server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleDbTester.java
server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleTesting.java
server/sonar-server/src/main/java/org/sonar/server/es/IndexerStartupTask.java
server/sonar-server/src/main/java/org/sonar/server/es/StickyFacetBuilder.java
server/sonar-server/src/main/java/org/sonar/server/issue/RulesAggregation.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java
server/sonar-server/src/main/java/org/sonar/server/issue/ws/TagsAction.java
server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleDoc.java
server/sonar-server/src/main/java/org/sonar/server/rule/RegisterRules.java
server/sonar-server/src/main/java/org/sonar/server/rule/RuleCreator.java
server/sonar-server/src/main/java/org/sonar/server/rule/RuleDeleter.java
server/sonar-server/src/main/java/org/sonar/server/rule/RuleUpdater.java
server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleDoc.java
server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleDocWithSystemScope.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleExtensionDoc.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleExtensionScope.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndex.java
server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndexDefinition.java
server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndexer.java
server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIterator.java
server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIteratorFactory.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIteratorForMultipleChunks.java
server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIteratorForSingleChunk.java
server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleMetadataIterator.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleQuery.java
server/sonar-server/src/main/java/org/sonar/server/rule/ws/TagsAction.java
server/sonar-server/src/test/java/org/sonar/server/component/ComponentCleanerServiceTest.java
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/PersistIssuesStepTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceMediumTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/RulesAggregation.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/issue/RulesAggregationTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/ws/TagsActionTest.java
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileBackuperMediumTest.java
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/QProfileCopierMediumTest.java
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/RuleActivatorMediumTest.java
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/index/ActiveRuleIndexerTest.java
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ChangeParentActionTest.java
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/ChangelogLoaderTest.java
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/CreateActionTest.java
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/InheritanceActionTest.java
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/ws/QProfilesWsMediumTest.java
server/sonar-server/src/test/java/org/sonar/server/rule/RegisterRulesTest.java
server/sonar-server/src/test/java/org/sonar/server/rule/RuleCreatorMediumTest.java
server/sonar-server/src/test/java/org/sonar/server/rule/RuleDeleterMediumTest.java
server/sonar-server/src/test/java/org/sonar/server/rule/RuleServiceMediumTest.java [deleted file]
server/sonar-server/src/test/java/org/sonar/server/rule/RuleUpdaterMediumTest.java
server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleDocTesting.java
server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndexDefinitionTest.java
server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndexTest.java
server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndexerTest.java
server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIteratorForSingleChunkTest.java
server/sonar-server/src/test/java/org/sonar/server/rule/ws/ListActionTest.java
server/sonar-server/src/test/java/org/sonar/server/rule/ws/RulesWsMediumTest.java
server/sonar-server/src/test/java/org/sonar/server/rule/ws/SearchActionMediumTest.java
server/sonar-server/src/test/java/org/sonar/server/rule/ws/ShowActionMediumTest.java
server/sonar-server/src/test/java/org/sonar/server/rule/ws/TagsActionTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/view/index/ViewIndexerTest.java
server/sonar-server/src/test/resources/org/sonar/server/rule/ws/RulesWsMediumTest/get_tags.json [deleted file]

index 73647d7629c56da5c2e70759627f49cc1abc3b1e..0fd44b9f5a3117958760ad923691dbe2f537692c 100644 (file)
@@ -29,6 +29,7 @@ import org.sonar.api.rules.RuleQuery;
 import org.sonar.db.Dao;
 import org.sonar.db.DbSession;
 import org.sonar.db.RowNotFoundException;
+import org.sonar.db.organization.OrganizationDto;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static org.sonar.db.DatabaseUtils.executeLargeInputs;
@@ -45,6 +46,10 @@ public class RuleDao implements Dao {
     return Optional.fromNullable(mapper(session).selectDefinitionByKey(key));
   }
 
+  public java.util.Optional<RuleMetadataDto> selectMetadataByKey(DbSession session, RuleKey key, OrganizationDto organization) {
+    return java.util.Optional.ofNullable(mapper(session).selectMetadataByKey(key, organization.getUuid()));
+  }
+
   public RuleDto selectOrFailByKey(DbSession session, String organizationUuid, RuleKey key) {
     RuleDto rule = mapper(session).selectByKey(organizationUuid, key);
     if (rule == null) {
@@ -122,6 +127,10 @@ public class RuleDao implements Dao {
     mapper(session).insertDefinition(dto);
   }
 
+  public void insert(DbSession session, RuleMetadataDto dto) {
+    mapper(session).insertMetadata(dto);
+  }
+
   public void update(DbSession session, RuleDefinitionDto dto) {
     mapper(session).updateDefinition(dto);
   }
index f753a624f8058b9e422e0b331956649fae1111c9..77ab19067d137ead8a59e6250ae6481744ad9c9f 100644 (file)
@@ -45,6 +45,8 @@ public interface RuleMapper {
 
   RuleDefinitionDto selectDefinitionByKey(RuleKey ruleKey);
 
+  RuleMetadataDto selectMetadataByKey(@Param("ruleKey") RuleKey ruleKey, @Param("organizationUuid") String organizationUuid);
+
   List<RuleDto> selectByKeys(@Param("organizationUuid") String organizationUuid, @Param("ruleKeys") List<RuleKey> keys);
 
   List<RuleDefinitionDto> selectDefinitionByKeys(@Param("ruleKeys") List<RuleKey> keys);
index 79666d241190a15a8abc7384f4a5f3678a196782..25ef4923224ddd2e6c976bf1e2d3f2105981d877 100644 (file)
       and r.plugin_rule_key=#{rule,jdbcType=VARCHAR}
   </select>
 
+  <select id="selectMetadataByKey" parameterType="map" resultType="org.sonar.db.rule.RuleMetadataDto">
+    select
+      rm.rule_id as "ruleId",
+      rm.organization_uuid as "organizationUuid",
+      rm.note_data as "noteData",
+      rm.note_user_login as "noteUserLogin",
+      rm.note_created_at as "noteCreatedAt",
+      rm.note_updated_at as "noteUpdatedAt",
+      rm.remediation_function as "remediationFunction",
+      rm.remediation_gap_mult as "remediationGapMultiplier",
+      rm.remediation_base_effort as "remediationBaseEffort",
+      rm.tags as "tagsField",
+      rm.created_at as "createdAt",
+      rm.updated_at as "updatedAt"
+    from
+      rules_metadata rm
+      inner join rules r on rm.rule_id = r.id
+    where
+      r.plugin_name=#{ruleKey.repository,jdbcType=VARCHAR}
+      and r.plugin_rule_key=#{ruleKey.rule,jdbcType=VARCHAR}
+      and rm.organization_uuid = #{organizationUuid,jdbcType=VARCHAR}
+  </select>
+
   <select id="selectByKeys" parameterType="map" resultType="Rule">
     select
       <include refid="selectJoinedTablesColumns"/>
index 1fa89afb3eb74b39e35aa4cc04e065391b30e06a..9d03879a332f1f405b78c9174835c912071bb2c9 100644 (file)
@@ -67,9 +67,9 @@ public class ActiveRuleDaoTest {
   private QualityProfileDto profile1 = QualityProfileDto.createFor("qp1").setOrganizationUuid(organization.getUuid()).setName("QProfile1");
   private QualityProfileDto profile2 = QualityProfileDto.createFor("qp2").setOrganizationUuid(organization.getUuid()).setName("QProfile2");
 
-  private RuleDefinitionDto rule1 = RuleTesting.newDto(RuleTesting.XOO_X1).getDefinition();
-  private RuleDefinitionDto rule2 = RuleTesting.newDto(RuleTesting.XOO_X2).getDefinition();
-  private RuleDefinitionDto rule3 = RuleTesting.newDto(RuleTesting.XOO_X3).getDefinition();
+  private RuleDefinitionDto rule1 = RuleTesting.newRule(RuleTesting.XOO_X1);
+  private RuleDefinitionDto rule2 = RuleTesting.newRule(RuleTesting.XOO_X2);
+  private RuleDefinitionDto rule3 = RuleTesting.newRule(RuleTesting.XOO_X3);
 
   private RuleParamDto rule1Param1;
   private RuleParamDto rule1Param2;
@@ -200,7 +200,7 @@ public class ActiveRuleDaoTest {
 
   @Test
   public void select_by_profile_ignore_removed_rules() throws Exception {
-    RuleDefinitionDto removedRule = RuleTesting.newDto(RuleKey.of("removed", "rule")).setStatus(RuleStatus.REMOVED).getDefinition();
+    RuleDefinitionDto removedRule = RuleTesting.newRule(RuleKey.of("removed", "rule")).setStatus(RuleStatus.REMOVED);
     dbTester.rules().insert(removedRule);
     ActiveRuleDto activeRule = createFor(profile1, removedRule).setSeverity(BLOCKER);
     underTest.insert(dbTester.getSession(), activeRule);
index 2e5111ea4db0365d42cc68c9a7b7e3853bb034a1..52297b1287f880db4f646c5cb22347c306e0dceb 100644 (file)
@@ -27,6 +27,7 @@ import org.sonar.api.server.rule.RuleParamType;
 import org.sonar.db.DbTester;
 import org.sonar.db.organization.OrganizationDto;
 
+import static org.sonar.db.rule.RuleTesting.newRule;
 import static org.sonar.db.rule.RuleTesting.newRuleDto;
 
 public class RuleDbTester {
@@ -38,21 +39,22 @@ public class RuleDbTester {
   }
 
   public RuleDefinitionDto insert() {
-    return insert(RuleTesting.newRule());
+    return insert(newRule());
   }
 
   public RuleDefinitionDto insert(RuleKey key) {
-    return insert(RuleTesting.newRule(key));
+    return insert(newRule(key));
   }
 
-  public RuleDefinitionDto insert(Consumer<RuleDefinitionDto> populater) {
-    RuleDefinitionDto rule = RuleTesting.newRule();
-    populater.accept(rule);
+  @SafeVarargs
+  public final RuleDefinitionDto insert(Consumer<RuleDefinitionDto>... populaters) {
+    RuleDefinitionDto rule = newRule();
+    Arrays.asList(populaters).forEach(populater -> populater.accept(rule));
     return insert(rule);
   }
 
   public RuleDefinitionDto insert(RuleKey key, Consumer<RuleDefinitionDto> populater) {
-    RuleDefinitionDto rule = RuleTesting.newRule(key);
+    RuleDefinitionDto rule = newRule(key);
     populater.accept(rule);
     return insert(rule);
   }
@@ -63,13 +65,10 @@ public class RuleDbTester {
     return rule;
   }
 
-  public RuleMetadataDto insertOrUpdateMetadata(RuleDefinitionDto rule, OrganizationDto organization) {
-    return insertOrUpdateMetadata(rule, organization, r -> {});
-  }
-
-  public RuleMetadataDto insertOrUpdateMetadata(RuleDefinitionDto rule, OrganizationDto organization, Consumer<RuleMetadataDto> populater) {
+  @SafeVarargs
+  public final RuleMetadataDto insertOrUpdateMetadata(RuleDefinitionDto rule, OrganizationDto organization, Consumer<RuleMetadataDto>... populaters) {
     RuleMetadataDto dto = RuleTesting.newRuleMetadata(rule, organization);
-    populater.accept(dto);
+    Arrays.asList(populaters).forEach(populater -> populater.accept(dto));
     return insertOrUpdateMetadata(dto);
   }
 
@@ -92,12 +91,10 @@ public class RuleDbTester {
   }
 
   public RuleDto insertRule(RuleDto ruleDto) {
-    RuleDao ruleDao = db.getDbClient().ruleDao();
-    ruleDao.insert(db.getSession(), ruleDto.getDefinition());
-    db.commit();
+    insert(ruleDto.getDefinition());
     RuleMetadataDto metadata = ruleDto.getMetadata();
     if (metadata.getOrganizationUuid() != null) {
-      ruleDao.insertOrUpdate(db.getSession(), metadata.setRuleId(ruleDto.getId()));
+      db.getDbClient().ruleDao().insertOrUpdate(db.getSession(), metadata.setRuleId(ruleDto.getId()));
       db.commit();
     }
     return ruleDto;
@@ -111,7 +108,8 @@ public class RuleDbTester {
     });
   }
 
-  public RuleDto insertRule(OrganizationDto organization, Consumer<RuleDto>... populaters) {
+  @SafeVarargs
+  public final RuleDto insertRule(OrganizationDto organization, Consumer<RuleDto>... populaters) {
     RuleDto ruleDto = newRuleDto(organization);
     Arrays.asList(populaters).forEach(populater -> populater.accept(ruleDto));
     return insertRule(ruleDto);
index 2720a2635c337eb5ad0497553ed2e21ce6e51a4e..437679899c89fb935698ee41d2ebd20aafadda2a 100644 (file)
@@ -21,6 +21,7 @@ package org.sonar.db.rule;
 
 import com.google.common.collect.ImmutableSet;
 import java.util.Date;
+import java.util.function.Consumer;
 import javax.annotation.Nullable;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rule.RuleStatus;
@@ -31,6 +32,7 @@ import org.sonar.db.organization.OrganizationDto;
 import org.sonar.db.rule.RuleDto.Format;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.collect.ImmutableSet.copyOf;
 import static com.google.common.collect.Sets.newHashSet;
 import static java.util.Objects.requireNonNull;
 import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
@@ -223,6 +225,10 @@ public class RuleTesting {
       .setIsTemplate(true);
   }
 
+  /**
+   * @deprecated use {@link #newCustomRule(RuleDefinitionDto)}
+   */
+  @Deprecated
   public static RuleDto newCustomRule(RuleDto templateRule) {
     checkNotNull(templateRule.getId(), "The template rule need to be persisted before creating this custom rule.");
     return newDto(RuleKey.of(templateRule.getRepositoryKey(), templateRule.getRuleKey() + "_" + System.currentTimeMillis()))
@@ -231,7 +237,72 @@ public class RuleTesting {
       .setType(templateRule.getType());
   }
 
+  public static RuleDefinitionDto newCustomRule(RuleDefinitionDto templateRule) {
+    checkNotNull(templateRule.getId(), "The template rule need to be persisted before creating this custom rule.");
+    return newRule(RuleKey.of(templateRule.getRepositoryKey(), templateRule.getRuleKey() + "_" + System.currentTimeMillis()))
+      .setLanguage(templateRule.getLanguage())
+      .setTemplateId(templateRule.getId())
+      .setType(templateRule.getType());
+  }
+
   public static RuleKey randomRuleKey() {
     return RuleKey.of("repo_" + randomAlphanumeric(3), "rule_" + randomAlphanumeric(3));
   }
+
+  public static Consumer<RuleDefinitionDto> setRepositoryKey(String repositoryKey) {
+    return rule -> rule.setRepositoryKey(repositoryKey);
+  }
+
+  public static Consumer<RuleDefinitionDto> setCreatedAt(long createdAt) {
+    return rule -> rule.setCreatedAt(createdAt);
+  }
+
+  public static Consumer<RuleDefinitionDto> setUpdatedAt(long updatedtAt) {
+    return rule -> rule.setUpdatedAt(updatedtAt);
+  }
+
+  public static Consumer<RuleDefinitionDto> setRuleKey(String ruleKey) {
+    return rule -> rule.setRuleKey(ruleKey);
+  }
+
+  public static Consumer<RuleDefinitionDto> setName(String name) {
+    return rule -> rule.setName(name);
+  }
+
+  public static Consumer<RuleDefinitionDto> setLanguage(String language) {
+    return rule -> rule.setLanguage(language);
+  }
+
+  public static Consumer<RuleDefinitionDto> setSeverity(String severity) {
+    return rule -> rule.setSeverity(severity);
+  }
+
+  public static Consumer<RuleDefinitionDto> setStatus(RuleStatus status) {
+    return rule -> rule.setStatus(status);
+  }
+
+  public static Consumer<RuleDefinitionDto> setType(RuleType type) {
+    return rule -> rule.setType(type);
+  }
+
+  public static Consumer<RuleDefinitionDto> setIsTemplate(boolean isTemplate) {
+    return rule -> rule.setIsTemplate(isTemplate);
+  }
+
+  public static Consumer<RuleDefinitionDto> setTemplateId(@Nullable Integer templateId) {
+    return rule -> rule.setTemplateId(templateId);
+  }
+
+  public static Consumer<RuleDefinitionDto> setSystemTags(String... tags) {
+    return rule -> rule.setSystemTags(copyOf(tags));
+  }
+
+  public static Consumer<RuleMetadataDto> setOrganizationUuid(String organizationUuid) {
+    return rule -> rule.setOrganizationUuid(organizationUuid);
+  }
+
+  public static Consumer<RuleMetadataDto> setTags(String... tags) {
+    return rule -> rule.setTags(copyOf(tags));
+  }
+
 }
index 8dd11f94d322e93fb3129e967ebde6dc76584591..fa0e069319f403815d2c88ac39e845e29ec2dcb1 100644 (file)
@@ -73,10 +73,14 @@ public class IndexerStartupTask {
   }
 
   private Set<IndexType> getUninitializedTypes(StartupIndexer indexer) {
-    return indexer.getIndexTypes().stream().filter(this::getUninitialized).collect(toSet());
+    return indexer.getIndexTypes().stream().filter(this::isUninitialized).collect(toSet());
   }
 
-  private boolean getUninitialized(IndexType indexType) {
+  private boolean isUninitialized(IndexType indexType) {
+    return isUninitialized(indexType, esClient);
+  }
+
+  public static boolean isUninitialized(IndexType indexType, EsClient esClient) {
     String setting = esClient.nativeClient().admin().indices().prepareGetSettings(indexType.getIndex()).get().getSetting(indexType.getIndex(),
       getInitializedSettingName(indexType));
     return !"true".equals(setting);
index d44850d48098c3a2d3aba4b18722771516c8030d..805aa576db44eea8c11382d47e89518a45bacc80 100644 (file)
@@ -22,6 +22,7 @@ package org.sonar.server.es;
 import java.util.Arrays;
 import java.util.Map;
 import java.util.Objects;
+import java.util.function.Function;
 import java.util.stream.Collector;
 import java.util.stream.Collectors;
 import javax.annotation.Nullable;
@@ -62,12 +63,35 @@ public class StickyFacetBuilder {
   }
 
   public AggregationBuilder buildStickyFacet(String fieldName, String facetName, Object... selected) {
-    return buildStickyFacet(fieldName, facetName, FACET_DEFAULT_SIZE, selected);
+    return buildStickyFacet(fieldName, facetName, t -> t, selected);
+  }
+
+  /**
+   * Creates an aggregation, that will return the top-terms for <code>fieldName</code>.
+   *
+   * It will filter according to the filters of every of the <em>other</em> fields, but will not apply filters to <em>this</em> field (so that the user can see all terms, even
+   * after having chosen for one of the terms).
+   *
+   * If special filtering is required (like for nested types), additional functionality can be passed into the method in the <code>additionalAggregationFilter</code> parameter.
+   *
+   * @param fieldName the name of the field that contains the terms
+   * @param facetName the name of the aggregation (use this for to find the corresponding results in the response)
+   * @param additionalAggregationFilter additional features (like filtering using childQuery)
+   * @param selected the terms, that the user already has selected
+   * @return the (global) aggregation, that can be added on top level of the elasticsearch request
+   */
+  public AggregationBuilder buildStickyFacet(String fieldName, String facetName, Function<TermsBuilder, AggregationBuilder<?>> additionalAggregationFilter, Object... selected) {
+    return buildStickyFacet(fieldName, facetName, FACET_DEFAULT_SIZE, additionalAggregationFilter, selected);
   }
 
   public AggregationBuilder buildStickyFacet(String fieldName, String facetName, int size, Object... selected) {
+    return buildStickyFacet(fieldName, facetName, size, t -> t, selected);
+  }
+
+  private AggregationBuilder buildStickyFacet(String fieldName, String facetName, int size, Function<TermsBuilder, AggregationBuilder<?>> additionalAggregationFilter,
+    Object... selected) {
     BoolQueryBuilder facetFilter = getStickyFacetFilter(fieldName);
-    FilterAggregationBuilder facetTopAggregation = buildTopFacetAggregation(fieldName, facetName, facetFilter, size);
+    FilterAggregationBuilder facetTopAggregation = buildTopFacetAggregation(fieldName, facetName, facetFilter, size, additionalAggregationFilter);
     facetTopAggregation = addSelectedItemsToFacet(fieldName, facetName, facetTopAggregation, selected);
 
     return AggregationBuilders
@@ -86,6 +110,20 @@ public class StickyFacetBuilder {
   }
 
   public FilterAggregationBuilder buildTopFacetAggregation(String fieldName, String facetName, BoolQueryBuilder facetFilter, int size) {
+    return buildTopFacetAggregation(fieldName, facetName, facetFilter, size, t -> t);
+  }
+
+  private FilterAggregationBuilder buildTopFacetAggregation(String fieldName, String facetName, BoolQueryBuilder facetFilter, int size,
+    Function<TermsBuilder, AggregationBuilder<?>> additionalAggregationFilter) {
+    TermsBuilder termsAggregation = buildTermsFacetAggregation(fieldName, facetName, size);
+    AggregationBuilder<?> innerAggregation = additionalAggregationFilter.apply(termsAggregation);
+    return AggregationBuilders
+      .filter(facetName + "_filter")
+      .filter(facetFilter)
+      .subAggregation(innerAggregation);
+  }
+
+  private TermsBuilder buildTermsFacetAggregation(String fieldName, String facetName, int size) {
     TermsBuilder termsAggregation = AggregationBuilders.terms(facetName)
       .field(fieldName)
       .order(order)
@@ -94,10 +132,7 @@ public class StickyFacetBuilder {
     if (subAggregation != null) {
       termsAggregation = termsAggregation.subAggregation(subAggregation);
     }
-    return AggregationBuilders
-      .filter(facetName + "_filter")
-      .filter(facetFilter)
-      .subAggregation(termsAggregation);
+    return termsAggregation;
   }
 
   public FilterAggregationBuilder addSelectedItemsToFacet(String fieldName, String facetName, FilterAggregationBuilder facetTopAggregation, Object... selected) {
@@ -118,4 +153,5 @@ public class StickyFacetBuilder {
     facetTopAggregation.subAggregation(selectedTerms);
     return facetTopAggregation;
   }
+
 }
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/RulesAggregation.java b/server/sonar-server/src/main/java/org/sonar/server/issue/RulesAggregation.java
deleted file mode 100644 (file)
index a687973..0000000
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.issue;
-
-import com.google.common.collect.HashMultiset;
-import com.google.common.collect.Multiset;
-import org.sonar.api.rule.RuleKey;
-import org.sonar.db.rule.RuleDto;
-
-import java.util.Collection;
-
-public class RulesAggregation {
-
-  private Multiset<Rule> rules;
-
-  public RulesAggregation() {
-    this.rules = HashMultiset.create();
-  }
-
-  public RulesAggregation add(RuleDto ruleDto) {
-    rules.add(new Rule(ruleDto.getKey(), ruleDto.getName()));
-    return this;
-  }
-
-  public Collection<Rule> rules() {
-    return rules.elementSet();
-  }
-
-  public int countRule(Rule rule) {
-    return rules.count(rule);
-  }
-
-  public static class Rule {
-
-    private RuleKey ruleKey;
-    private String name;
-
-    public Rule(RuleKey ruleKey, String name) {
-      this.ruleKey = ruleKey;
-      this.name = name;
-    }
-
-    public RuleKey ruleKey() {
-      return ruleKey;
-    }
-
-    public String name() {
-      return name;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-      if (this == o) {
-        return true;
-      }
-      if (o == null || getClass() != o.getClass()) {
-        return false;
-      }
-
-      Rule rule = (Rule) o;
-
-      if (!ruleKey.equals(rule.ruleKey)) {
-        return false;
-      }
-
-      return true;
-    }
-
-    @Override
-    public int hashCode() {
-      return ruleKey.hashCode();
-    }
-  }
-}
index 0f1c7d06d56f90caecdf68a00ee5d3d64626c578..4706d06a49e992df10c748e10dc8ff32a3adf759 100644 (file)
@@ -22,9 +22,7 @@ package org.sonar.server.issue.index;
 import com.google.common.base.Function;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
@@ -34,7 +32,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
-import java.util.SortedSet;
 import java.util.TimeZone;
 import java.util.regex.Pattern;
 import javax.annotation.CheckForNull;
@@ -51,8 +48,6 @@ import org.elasticsearch.index.query.QueryBuilders;
 import org.elasticsearch.search.aggregations.AggregationBuilder;
 import org.elasticsearch.search.aggregations.AggregationBuilders;
 import org.elasticsearch.search.aggregations.bucket.filter.FilterAggregationBuilder;
-import org.elasticsearch.search.aggregations.bucket.global.Global;
-import org.elasticsearch.search.aggregations.bucket.global.GlobalBuilder;
 import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval;
 import org.elasticsearch.search.aggregations.bucket.terms.Terms;
 import org.elasticsearch.search.aggregations.bucket.terms.Terms.Order;
@@ -75,7 +70,6 @@ import org.sonar.server.es.Sorting;
 import org.sonar.server.es.StickyFacetBuilder;
 import org.sonar.server.issue.IssueQuery;
 import org.sonar.server.permission.index.AuthorizationTypeSupport;
-import org.sonar.server.rule.index.RuleIndexDefinition;
 import org.sonar.server.user.UserSession;
 import org.sonar.server.view.index.ViewIndexDefinition;
 
@@ -86,6 +80,8 @@ import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
 import static org.elasticsearch.index.query.QueryBuilders.termQuery;
 import static org.elasticsearch.index.query.QueryBuilders.termsQuery;
 import static org.sonar.server.es.EsUtils.escapeSpecialRegexChars;
+import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_ORGANIZATION_UUID;
+import static org.sonar.server.issue.index.IssueIndexDefinition.INDEX_TYPE_ISSUE;
 import static org.sonarqube.ws.client.issue.IssuesWsParameters.DEPRECATED_FACET_MODE_DEBT;
 import static org.sonarqube.ws.client.issue.IssuesWsParameters.DEPRECATED_PARAM_ACTION_PLANS;
 import static org.sonarqube.ws.client.issue.IssuesWsParameters.FACET_ASSIGNED_TO_ME;
@@ -157,6 +153,7 @@ public class IssueIndex {
       return new IssueDoc(input);
     }
   };
+  public static final String AGGREGATION_NAME_FOR_TAGS = "tags__issues";
 
   private final Sorting sorting;
   private final EsClient client;
@@ -169,6 +166,7 @@ public class IssueIndex {
     this.system = system;
     this.userSession = userSession;
     this.authorizationTypeSupport = authorizationTypeSupport;
+
     this.sorting = new Sorting();
     this.sorting.add(IssueQuery.SORT_BY_ASSIGNEE, IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE);
     this.sorting.add(IssueQuery.SORT_BY_STATUS, IssueIndexDefinition.FIELD_ISSUE_STATUS);
@@ -192,7 +190,7 @@ public class IssueIndex {
 
   public SearchResult<IssueDoc> search(IssueQuery query, SearchOptions options) {
     SearchRequestBuilder requestBuilder = client
-      .prepareSearch(IssueIndexDefinition.INDEX_TYPE_ISSUE);
+      .prepareSearch(INDEX_TYPE_ISSUE);
 
     configureSorting(query, requestBuilder);
     configurePagination(options, requestBuilder);
@@ -478,7 +476,7 @@ public class IssueIndex {
   private Optional<Long> getMinCreatedAt(Map<String, QueryBuilder> filters, QueryBuilder esQuery) {
     String facetNameAndField = IssueIndexDefinition.FIELD_ISSUE_FUNC_CREATED_AT;
     SearchRequestBuilder esRequest = client
-      .prepareSearch(IssueIndexDefinition.INDEX_TYPE_ISSUE)
+      .prepareSearch(INDEX_TYPE_ISSUE)
       .setSize(0);
     BoolQueryBuilder esFilter = boolQuery();
     filters.values().stream().filter(Objects::nonNull).forEach(esFilter::must);
@@ -592,42 +590,27 @@ public class IssueIndex {
     return value == null ? null : termQuery(field, value);
   }
 
-  public List<String> listTags(@Nullable String textQuery, int maxNumberOfTags) {
+  public List<String> listTags(String organizationUuid, @Nullable String textQuery, int maxNumberOfTags) {
     SearchRequestBuilder requestBuilder = client
-      .prepareSearch(IssueIndexDefinition.INDEX_TYPE_ISSUE, RuleIndexDefinition.INDEX_TYPE_RULE);
-
-    requestBuilder.setQuery(boolQuery().must(matchAllQuery()).filter(createBoolFilter(
-      IssueQuery.builder().checkAuthorization(false).build())));
-
-    GlobalBuilder topAggreg = AggregationBuilders.global("tags");
-    String tagsOnIssuesSubAggregation = "tags__issues";
-    String tagsOnRulesSubAggregation = "tags__rules";
+      .prepareSearch(INDEX_TYPE_ISSUE)
+      .setQuery(boolQuery()
+        .filter(termQuery(FIELD_ISSUE_ORGANIZATION_UUID, organizationUuid)))
+      .setSize(0);
 
-    TermsBuilder issueTags = AggregationBuilders.terms(tagsOnIssuesSubAggregation)
+    TermsBuilder termsAggregation = AggregationBuilders.terms(AGGREGATION_NAME_FOR_TAGS)
       .field(IssueIndexDefinition.FIELD_ISSUE_TAGS)
       .size(maxNumberOfTags)
       .order(Terms.Order.term(true))
       .minDocCount(1L);
-    TermsBuilder ruleTags = AggregationBuilders.terms(tagsOnRulesSubAggregation)
-      .field(RuleIndexDefinition.FIELD_RULE_ALL_TAGS)
-      .size(maxNumberOfTags)
-      .order(Terms.Order.term(true))
-      .minDocCount(1L);
     if (textQuery != null) {
       String escapedTextQuery = escapeSpecialRegexChars(textQuery);
-      issueTags.include(format(SUBSTRING_MATCH_REGEXP, escapedTextQuery));
-      ruleTags.include(format(SUBSTRING_MATCH_REGEXP, escapedTextQuery));
+      termsAggregation.include(format(SUBSTRING_MATCH_REGEXP, escapedTextQuery));
     }
+    requestBuilder.addAggregation(termsAggregation);
 
-    SearchResponse searchResponse = requestBuilder.addAggregation(topAggreg.subAggregation(issueTags).subAggregation(ruleTags)).get();
-    Global allTags = searchResponse.getAggregations().get("tags");
-    SortedSet<String> result = Sets.newTreeSet();
-    Terms issuesResult = allTags.getAggregations().get(tagsOnIssuesSubAggregation);
-    Terms rulesResult = allTags.getAggregations().get(tagsOnRulesSubAggregation);
-    result.addAll(EsUtils.termsKeys(issuesResult));
-    result.addAll(EsUtils.termsKeys(rulesResult));
-    List<String> resultAsList = Lists.newArrayList(result);
-    return resultAsList.size() > maxNumberOfTags && maxNumberOfTags > 0 ? resultAsList.subList(0, maxNumberOfTags) : resultAsList;
+    SearchResponse searchResponse = requestBuilder.get();
+    Terms issuesResult = searchResponse.getAggregations().get(AGGREGATION_NAME_FOR_TAGS);
+    return EsUtils.termsKeys(issuesResult);
   }
 
   public Map<String, Long> countTags(IssueQuery query, int maxNumberOfTags) {
@@ -642,7 +625,7 @@ public class IssueIndex {
 
   private Terms listTermsMatching(String fieldName, IssueQuery query, @Nullable String textQuery, Terms.Order termsOrder, int maxNumberOfTags) {
     SearchRequestBuilder requestBuilder = client
-      .prepareSearch(IssueIndexDefinition.INDEX_TYPE_ISSUE)
+      .prepareSearch(INDEX_TYPE_ISSUE)
       // Avoids returning search hits
       .setSize(0);
 
@@ -692,7 +675,7 @@ public class IssueIndex {
     }
 
     SearchRequestBuilder requestBuilder = client
-      .prepareSearch(IssueIndexDefinition.INDEX_TYPE_ISSUE)
+      .prepareSearch(INDEX_TYPE_ISSUE)
       .setSearchType(SearchType.SCAN)
       .setScroll(TimeValue.timeValueMinutes(EsUtils.SCROLL_TIME_IN_MINUTES))
       .setSize(10_000)
index 7fe6ef31cdc5479c8bdb9bf5a8ee27f21282bf1d..9003d4c9c7f76bae8bad1b4a0bacf7eb1266bc38 100644 (file)
  */
 package org.sonar.server.issue.ws;
 
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
 import com.google.common.io.Resources;
+import java.util.Collection;
 import java.util.List;
+import java.util.SortedSet;
 import javax.annotation.Nullable;
 import org.sonar.api.server.ws.Request;
 import org.sonar.api.server.ws.Response;
@@ -29,6 +33,8 @@ import org.sonar.api.server.ws.WebService.NewAction;
 import org.sonar.api.server.ws.WebService.Param;
 import org.sonar.api.utils.text.JsonWriter;
 import org.sonar.server.issue.index.IssueIndex;
+import org.sonar.server.organization.DefaultOrganizationProvider;
+import org.sonar.server.rule.index.RuleIndex;
 
 import static org.sonar.api.server.ws.WebService.Param.PAGE_SIZE;
 
@@ -39,9 +45,13 @@ import static org.sonar.api.server.ws.WebService.Param.PAGE_SIZE;
 public class TagsAction implements IssuesWsAction {
 
   private final IssueIndex issueIndex;
+  private final RuleIndex ruleIndex;
+  private final DefaultOrganizationProvider defaultOrganizationProvider;
 
-  public TagsAction(IssueIndex issueIndex) {
+  public TagsAction(IssueIndex issueIndex, RuleIndex ruleIndex, DefaultOrganizationProvider defaultOrganizationProvider) {
     this.issueIndex = issueIndex;
+    this.ruleIndex = ruleIndex;
+    this.defaultOrganizationProvider = defaultOrganizationProvider;
   }
 
   @Override
@@ -64,12 +74,19 @@ public class TagsAction implements IssuesWsAction {
   public void handle(Request request, Response response) throws Exception {
     String query = request.param(Param.TEXT_QUERY);
     int pageSize = request.mandatoryParamAsInt("ps");
-    List<String> tags = listTags(query, pageSize);
+    List<String> tags = listTags(query, pageSize == 0 ? Integer.MAX_VALUE : pageSize);
     writeTags(response, tags);
   }
 
   private List<String> listTags(@Nullable String textQuery, int pageSize) {
-    return issueIndex.listTags(textQuery, pageSize);
+    Collection<String> issueTags = issueIndex.listTags(defaultOrganizationProvider.get().getUuid(), textQuery, pageSize);
+    Collection<String> ruleTags = ruleIndex.listTags(defaultOrganizationProvider.get().getUuid(), textQuery, pageSize);
+
+    SortedSet<String> result = Sets.newTreeSet();
+    result.addAll(issueTags);
+    result.addAll(ruleTags);
+    List<String> resultAsList = Lists.newArrayList(result);
+    return resultAsList.size() > pageSize && pageSize > 0 ? resultAsList.subList(0, pageSize) : resultAsList;
   }
 
   private static void writeTags(Response response, List<String> tags) {
index 8268578ea8b6b6a5622ccb2f713577c2ba6cb46e..a979638fdfd50427651ede0182f5e42c4e220486 100644 (file)
@@ -166,7 +166,6 @@ import org.sonar.server.rule.RuleDeleter;
 import org.sonar.server.rule.RuleUpdater;
 import org.sonar.server.rule.index.RuleIndexDefinition;
 import org.sonar.server.rule.index.RuleIndexer;
-import org.sonar.server.rule.index.RuleIteratorFactory;
 import org.sonar.server.rule.ws.ActiveRuleCompleter;
 import org.sonar.server.rule.ws.RepositoriesAction;
 import org.sonar.server.rule.ws.RuleMapper;
@@ -282,7 +281,6 @@ public class PlatformLevel4 extends PlatformLevel {
       // rule
       RuleIndexDefinition.class,
       RuleIndexer.class,
-      RuleIteratorFactory.class,
       AnnotationRuleParser.class,
       XMLRuleParser.class,
       DefaultRuleFinder.class,
index c50b6ee625a7900cd3e5037eb643264e395f8d8c..bc84933d319a275df3449bf94de425adde8acf30 100644 (file)
@@ -27,7 +27,7 @@ import org.sonar.server.qualityprofile.ActiveRule;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 import static org.apache.commons.lang.StringUtils.containsIgnoreCase;
-import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_ACTIVE_ORGANIZATION_UUID;
+import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_ACTIVE_RULE_ORGANIZATION_UUID;
 import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_ACTIVE_RULE_CREATED_AT;
 import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_ACTIVE_RULE_INHERITANCE;
 import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_ACTIVE_RULE_KEY;
@@ -71,11 +71,11 @@ public class ActiveRuleDoc extends BaseDoc {
   }
 
   String organizationUuid() {
-    return getField(FIELD_ACTIVE_ORGANIZATION_UUID);
+    return getField(FIELD_ACTIVE_RULE_ORGANIZATION_UUID);
   }
 
   public ActiveRuleDoc setOrganizationUuid(String s) {
-    setField(FIELD_ACTIVE_ORGANIZATION_UUID, s);
+    setField(FIELD_ACTIVE_RULE_ORGANIZATION_UUID, s);
     return this;
   }
 
index c7ce5e98da0e918717f08d2f391e3663dbe9ff14..1a66a64b28ae06974fee6e1ad5bee54a29dbf54e 100644 (file)
@@ -28,6 +28,7 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.stream.Collectors;
 import javax.annotation.Nullable;
 import org.apache.commons.lang.ObjectUtils;
 import org.apache.commons.lang.StringUtils;
@@ -45,14 +46,12 @@ import org.sonar.api.utils.log.Profiler;
 import org.sonar.core.util.stream.MoreCollectors;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
-import org.sonar.db.organization.OrganizationDto;
 import org.sonar.db.qualityprofile.ActiveRuleDto;
 import org.sonar.db.qualityprofile.ActiveRuleParamDto;
 import org.sonar.db.rule.RuleDefinitionDto;
 import org.sonar.db.rule.RuleDto.Format;
 import org.sonar.db.rule.RuleParamDto;
 import org.sonar.db.rule.RuleRepositoryDto;
-import org.sonar.server.organization.DefaultOrganizationProvider;
 import org.sonar.server.qualityprofile.ActiveRuleChange;
 import org.sonar.server.qualityprofile.RuleActivator;
 import org.sonar.server.qualityprofile.index.ActiveRuleIndexer;
@@ -75,10 +74,9 @@ public class RegisterRules implements Startable {
   private final ActiveRuleIndexer activeRuleIndexer;
   private final Languages languages;
   private final System2 system2;
-  private final DefaultOrganizationProvider defaultOrganizationProvider;
 
   public RegisterRules(RuleDefinitionsLoader defLoader, RuleActivator ruleActivator, DbClient dbClient, RuleIndexer ruleIndexer,
-    ActiveRuleIndexer activeRuleIndexer, Languages languages, System2 system2, DefaultOrganizationProvider defaultOrganizationProvider) {
+    ActiveRuleIndexer activeRuleIndexer, Languages languages, System2 system2) {
     this.defLoader = defLoader;
     this.ruleActivator = ruleActivator;
     this.dbClient = dbClient;
@@ -86,7 +84,6 @@ public class RegisterRules implements Startable {
     this.activeRuleIndexer = activeRuleIndexer;
     this.languages = languages;
     this.system2 = system2;
-    this.defaultOrganizationProvider = defaultOrganizationProvider;
   }
 
   @Override
@@ -111,22 +108,15 @@ public class RegisterRules implements Startable {
       List<RuleDefinitionDto> removedRules = processRemainingDbRules(allRules.values(), session);
       List<ActiveRuleChange> changes = removeActiveRulesOnStillExistingRepositories(session, removedRules, context);
       session.commit();
+      keysToIndex.addAll(removedRules.stream().map(RuleDefinitionDto::getKey).collect(Collectors.toList()));
 
       persistRepositories(session, context.repositories());
-      ruleIndexer.delete(removedRules.stream().map(RuleDefinitionDto::getKey).collect(MoreCollectors.toList(removedRules.size())));
-      ruleIndexer.index(getDefaultOrganization(), keysToIndex);
+      ruleIndexer.indexRuleDefinitions(keysToIndex);
       activeRuleIndexer.index(changes);
       profiler.stopDebug();
     }
   }
 
-  private OrganizationDto getDefaultOrganization() {
-    try (DbSession dbSession = dbClient.openSession(false)) {
-      return dbClient.organizationDao().selectByUuid(dbSession, defaultOrganizationProvider.get().getUuid())
-        .orElseThrow(() -> new IllegalStateException("Cannot load default organization"));
-    }
-  }
-
   private void persistRepositories(DbSession dbSession, List<RulesDefinition.Repository> repositories) {
     dbClient.ruleRepositoryDao().truncate(dbSession);
     List<RuleRepositoryDto> dtos = repositories
index 1cd31f7d34507c7b5080ec14f6b244124c11b15c..40f0c2339c2c81337b7729512b2604212d52c682 100644 (file)
@@ -88,7 +88,7 @@ public class RuleCreator {
       .orElseGet(() -> createCustomRule(customRuleKey, newRule, templateRule, dbSession));
 
     dbSession.commit();
-    ruleIndexer.index(defaultOrganization, customRuleKey);
+    ruleIndexer.indexRuleDefinition(customRuleKey);
     return customRuleKey;
   }
 
index 126e0ecdf206447f74bdef3325cd65df54e01d17..e77364107bb09d75af9c4ed35483d3b2c7d36ce5 100644 (file)
@@ -61,7 +61,7 @@ public class RuleDeleter {
       dbClient.ruleDao().update(dbSession, rule);
 
       dbSession.commit();
-      ruleIndexer.delete(ruleKey);
+      ruleIndexer.indexRuleDefinition(ruleKey);
     }
   }
 }
index 1dc61bd387bd871a0830d2399abb42670fc753fd..2faa6481d5713b5647210886cf7ade2d0b3f0cdc 100644 (file)
@@ -39,7 +39,6 @@ import org.sonar.api.server.debt.DebtRemediationFunction;
 import org.sonar.api.utils.System2;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
-import org.sonar.db.organization.OrganizationDto;
 import org.sonar.db.qualityprofile.ActiveRuleDto;
 import org.sonar.db.qualityprofile.ActiveRuleParamDto;
 import org.sonar.db.rule.RuleDefinitionDto;
@@ -91,17 +90,14 @@ public class RuleUpdater {
       return false;
     }
 
-    String defaultOrganizationUuid = defaultOrganizationProvider.get().getUuid();
-    OrganizationDto organization = dbClient.organizationDao().selectByUuid(dbSession, defaultOrganizationUuid)
-      .orElseThrow(() -> new IllegalStateException(String.format("Could not find default organization '%s'", defaultOrganizationUuid)));
-
     RuleDto rule = getRuleDto(update);
     // validate only the changes, not all the rule fields
     apply(update, rule, userSession);
     update(dbSession, rule);
     updateParameters(dbSession, update, rule);
     dbSession.commit();
-    ruleIndexer.index(organization, rule.getKey());
+
+    ruleIndexer.indexRuleDefinition(rule.getKey());
     return true;
   }
 
index b796444365765bdcabf403e5ba77eb94084af701..94d6a38d84d03daeae75a6ce57ff58f10f5d6053 100644 (file)
@@ -20,7 +20,6 @@
 package org.sonar.server.rule.index;
 
 import com.google.common.collect.Maps;
-import java.util.Collection;
 import java.util.Map;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
@@ -40,7 +39,7 @@ public class RuleDoc extends BaseDoc {
   }
 
   public RuleDoc() {
-    super(Maps.newHashMapWithExpectedSize(16));
+    super(Maps.newHashMapWithExpectedSize(15));
   }
 
   @Override
@@ -166,15 +165,6 @@ public class RuleDoc extends BaseDoc {
     return this;
   }
 
-  public Collection<String> allTags() {
-    return getField(RuleIndexDefinition.FIELD_RULE_ALL_TAGS);
-  }
-
-  public RuleDoc setAllTags(@Nullable Collection<String> l) {
-    setField(RuleIndexDefinition.FIELD_RULE_ALL_TAGS, l);
-    return this;
-  }
-
   public RuleType type() {
     return RuleType.valueOf(getField(RuleIndexDefinition.FIELD_RULE_TYPE));
   }
diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleDocWithSystemScope.java b/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleDocWithSystemScope.java
new file mode 100644 (file)
index 0000000..1e581a9
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.rule.index;
+
+public class RuleDocWithSystemScope {
+
+  private final RuleDoc ruleDoc;
+  private final RuleExtensionDoc ruleExtensionDoc;
+
+  public RuleDocWithSystemScope(RuleDoc ruleDoc, RuleExtensionDoc ruleExtensionDoc) {
+    this.ruleDoc = ruleDoc;
+    this.ruleExtensionDoc = ruleExtensionDoc;
+  }
+
+  public RuleDoc getRuleDoc() {
+    return ruleDoc;
+  }
+
+  public RuleExtensionDoc getRuleExtensionDoc() {
+    return ruleExtensionDoc;
+  }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleExtensionDoc.java b/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleExtensionDoc.java
new file mode 100644 (file)
index 0000000..db8fe25
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.rule.index;
+
+import com.google.common.collect.Maps;
+import java.util.Map;
+import java.util.Set;
+import org.apache.commons.lang.builder.ReflectionToStringBuilder;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.db.rule.RuleMetadataDto;
+import org.sonar.server.es.BaseDoc;
+
+public class RuleExtensionDoc extends BaseDoc {
+
+  public RuleExtensionDoc(Map<String, Object> fields) {
+    super(fields);
+  }
+
+  public RuleExtensionDoc() {
+    super(Maps.newHashMapWithExpectedSize(4));
+  }
+
+  @Override
+  public String getId() {
+    // the Id is generated by elasticsearch
+    return null;
+  }
+
+  @Override
+  public String getRouting() {
+    return null;
+  }
+
+  @Override
+  public String getParent() {
+    return getRuleKey().toString();
+  }
+
+  public RuleKey getRuleKey() {
+    return getField(RuleIndexDefinition.FIELD_RULE_EXTENSION_RULE_KEY);
+  }
+
+  public RuleExtensionDoc setRuleKey(RuleKey ruleKey) {
+    setField(RuleIndexDefinition.FIELD_RULE_EXTENSION_RULE_KEY, ruleKey);
+    return this;
+  }
+
+  public RuleExtensionScope getScope() {
+    return RuleExtensionScope.parse(getField(RuleIndexDefinition.FIELD_RULE_EXTENSION_SCOPE));
+  }
+
+  public RuleExtensionDoc setScope(RuleExtensionScope scope) {
+    setField(RuleIndexDefinition.FIELD_RULE_EXTENSION_SCOPE, scope.getScope());
+    return this;
+  }
+
+  public Set<String> getTags() {
+    return getField(RuleIndexDefinition.FIELD_RULE_EXTENSION_TAGS);
+  }
+
+  public RuleExtensionDoc setTags(Set<String> tags) {
+    setField(RuleIndexDefinition.FIELD_RULE_EXTENSION_TAGS, tags);
+    return this;
+  }
+
+  public static RuleExtensionDoc of(RuleKey key, RuleExtensionScope scope, RuleMetadataDto ruleExtension) {
+    return new RuleExtensionDoc()
+      .setRuleKey(key)
+      .setScope(scope)
+      .setTags(ruleExtension.getTags());
+  }
+
+  @Override
+  public String toString() {
+    return ReflectionToStringBuilder.toString(this);
+  }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleExtensionScope.java b/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleExtensionScope.java
new file mode 100644 (file)
index 0000000..8cdf992
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.rule.index;
+
+import java.util.Optional;
+import javax.annotation.Nullable;
+import org.sonar.db.organization.OrganizationDto;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+public class RuleExtensionScope {
+
+  private static final String FAKE_UUID_FOR_SYSTEM = "system";
+
+  private final Optional<String> organizationUuid;
+
+  private RuleExtensionScope(@Nullable String organizationUuid) {
+    this.organizationUuid = Optional.ofNullable(organizationUuid);
+  }
+
+  public static RuleExtensionScope system() {
+    return new RuleExtensionScope(null);
+  }
+
+  public static RuleExtensionScope organization(OrganizationDto organization) {
+    return organization(organization.getUuid());
+  }
+
+  public static RuleExtensionScope organization(String organizationUuid) {
+    checkArgument(!FAKE_UUID_FOR_SYSTEM.equals(organizationUuid), "The organization uuid '%s' is reserved for to store system tags in the rules index.", FAKE_UUID_FOR_SYSTEM);
+    return new RuleExtensionScope(organizationUuid);
+  }
+
+  public String getScope() {
+    return organizationUuid.orElse(FAKE_UUID_FOR_SYSTEM);
+  }
+
+  public static RuleExtensionScope parse(String scope) {
+    if (FAKE_UUID_FOR_SYSTEM.equals(scope)) {
+      return system();
+    }
+    return new RuleExtensionScope(scope);
+  }
+}
index 7825af3595da7c20e797431dc7843581f52a0c38..b05bcecfa25c0c79384c1a71642d24cd43889ef3 100644 (file)
@@ -30,7 +30,9 @@ import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
+import java.util.function.Function;
 import javax.annotation.Nullable;
 import org.apache.commons.lang.StringUtils;
 import org.elasticsearch.action.search.SearchRequestBuilder;
@@ -43,8 +45,11 @@ import org.elasticsearch.index.query.MatchQueryBuilder;
 import org.elasticsearch.index.query.QueryBuilder;
 import org.elasticsearch.index.query.QueryBuilders;
 import org.elasticsearch.index.query.SimpleQueryStringBuilder;
+import org.elasticsearch.index.query.TermsQueryBuilder;
 import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
+import org.elasticsearch.search.aggregations.AggregationBuilder;
 import org.elasticsearch.search.aggregations.AggregationBuilders;
+import org.elasticsearch.search.aggregations.bucket.filter.FilterAggregationBuilder;
 import org.elasticsearch.search.aggregations.bucket.terms.Terms;
 import org.elasticsearch.search.aggregations.bucket.terms.TermsBuilder;
 import org.elasticsearch.search.sort.FieldSortBuilder;
@@ -55,24 +60,29 @@ import org.sonar.api.rule.RuleStatus;
 import org.sonar.api.rule.Severity;
 import org.sonar.api.rules.RuleType;
 import org.sonar.server.es.EsClient;
+import org.sonar.server.es.EsUtils;
 import org.sonar.server.es.SearchIdResult;
 import org.sonar.server.es.SearchOptions;
 import org.sonar.server.es.StickyFacetBuilder;
 
+import static com.google.common.base.Preconditions.checkArgument;
 import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
 import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
 import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
 import static org.elasticsearch.index.query.QueryBuilders.simpleQueryStringQuery;
+import static org.elasticsearch.index.query.QueryBuilders.termsQuery;
 import static org.sonar.server.es.DefaultIndexSettingsElement.SEARCH_WORDS_ANALYZER;
 import static org.sonar.server.es.DefaultIndexSettingsElement.SORTABLE_ANALYZER;
 import static org.sonar.server.es.EsUtils.SCROLL_TIME_IN_MINUTES;
 import static org.sonar.server.es.EsUtils.escapeSpecialRegexChars;
 import static org.sonar.server.es.EsUtils.scrollIds;
 import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_ACTIVE_RULE_INHERITANCE;
+import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_ACTIVE_RULE_ORGANIZATION_UUID;
 import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_ACTIVE_RULE_PROFILE_KEY;
 import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_ACTIVE_RULE_SEVERITY;
-import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_RULE_ALL_TAGS;
 import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_RULE_CREATED_AT;
+import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_RULE_EXTENSION_SCOPE;
+import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_RULE_EXTENSION_TAGS;
 import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_RULE_HTML_DESCRIPTION;
 import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_RULE_INTERNAL_KEY;
 import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_RULE_IS_TEMPLATE;
@@ -88,6 +98,7 @@ import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_RULE_TYPE;
 import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_RULE_UPDATED_AT;
 import static org.sonar.server.rule.index.RuleIndexDefinition.INDEX_TYPE_ACTIVE_RULE;
 import static org.sonar.server.rule.index.RuleIndexDefinition.INDEX_TYPE_RULE;
+import static org.sonar.server.rule.index.RuleIndexDefinition.INDEX_TYPE_RULE_EXTENSION;
 
 /**
  * The unique entry-point to interact with Elasticsearch index "rules".
@@ -108,6 +119,7 @@ public class RuleIndex {
     Collections2.filter(
       Collections2.transform(Arrays.asList(RuleStatus.values()), RuleStatus::toString),
       input -> !RuleStatus.REMOVED.toString().equals(input)));
+  private static final String AGGREGATION_NAME_FOR_TAGS = "tagsAggregation";
   private final EsClient client;
 
   public RuleIndex(EsClient client) {
@@ -196,13 +208,6 @@ public class RuleIndex {
       .boost(boost);
   }
 
-  private static QueryBuilder termAnyQuery(String field, String query, float boost) {
-    return QueryBuilders.multiMatchQuery(query,
-      field, SEARCH_WORDS_ANALYZER.subField(field))
-      .operator(MatchQueryBuilder.Operator.OR)
-      .boost(boost);
-  }
-
   /* Build main filter (match based) */
   private static Map<String, QueryBuilder> buildFilters(RuleQuery query) {
 
@@ -245,8 +250,16 @@ public class RuleIndex {
     }
 
     if (isNotEmpty(query.getTags())) {
-      filters.put(FIELD_RULE_ALL_TAGS,
-        QueryBuilders.termsQuery(FIELD_RULE_ALL_TAGS, query.getTags()));
+      BoolQueryBuilder q = boolQuery();
+      String organizationUuid = query.getOrganizationUuid();
+      checkArgument(organizationUuid != null, "Cannot filter on tags '%s', if no organization is specified.", query.getTags());
+      query.getTags().stream()
+        .map(tag -> boolQuery()
+          .filter(QueryBuilders.termQuery(FIELD_RULE_EXTENSION_TAGS, tag))
+          .filter(termsQuery(FIELD_RULE_EXTENSION_SCOPE, RuleExtensionScope.system().getScope(), RuleExtensionScope.organization(organizationUuid).getScope())))
+        .map(childQuery -> QueryBuilders.hasChildQuery(INDEX_TYPE_RULE_EXTENSION.getType(), childQuery))
+        .forEach(q::filter);
+      filters.put(FIELD_RULE_EXTENSION_TAGS, q);
     }
 
     if (isNotEmpty(query.getTypes())) {
@@ -285,6 +298,7 @@ public class RuleIndex {
     addTermFilter(childrenFilter, FIELD_ACTIVE_RULE_PROFILE_KEY, query.getQProfileKey());
     addTermFilter(childrenFilter, FIELD_ACTIVE_RULE_INHERITANCE, query.getInheritance());
     addTermFilter(childrenFilter, FIELD_ACTIVE_RULE_SEVERITY, query.getActiveSeverities());
+    addTermFilter(childrenFilter, FIELD_ACTIVE_RULE_ORGANIZATION_UUID, query.getOrganizationUuid());
 
     // ChildQuery
     QueryBuilder childQuery;
@@ -294,7 +308,7 @@ public class RuleIndex {
       childQuery = matchAllQuery();
     }
 
-    /** Implementation of activation query */
+    /* Implementation of activation query */
     if (Boolean.TRUE.equals(query.getActivation())) {
       filters.put("activation",
         QueryBuilders.hasChildQuery(INDEX_TYPE_ACTIVE_RULE.getType(),
@@ -354,8 +368,24 @@ public class RuleIndex {
     }
     if (options.getFacets().contains(FACET_TAGS) || options.getFacets().contains(FACET_OLD_DEFAULT)) {
       Collection<String> tags = query.getTags();
+      String organizationUuid = query.getOrganizationUuid();
+      checkArgument(organizationUuid != null, "Cannot use tags facet, if no organization is specified.", query.getTags());
+
+      Function<TermsBuilder, AggregationBuilder<?>> childFeature = termsAggregation -> {
+
+        FilterAggregationBuilder scopeAggregation = AggregationBuilders.filter("scope_filter_for_" + FACET_TAGS).filter(
+          termsQuery(FIELD_RULE_EXTENSION_SCOPE,
+            RuleExtensionScope.system().getScope(),
+            RuleExtensionScope.organization(organizationUuid).getScope()))
+          .subAggregation(termsAggregation);
+
+        return AggregationBuilders.children("children_for_" + FACET_TAGS)
+          .childType(INDEX_TYPE_RULE_EXTENSION.getType())
+          .subAggregation(scopeAggregation);
+      };
+
       aggregations.put(FACET_TAGS,
-        stickyFacetBuilder.buildStickyFacet(FIELD_RULE_ALL_TAGS, FACET_TAGS,
+        stickyFacetBuilder.buildStickyFacet(FIELD_RULE_EXTENSION_TAGS, FACET_TAGS, childFeature,
           (tags == null) ? (new String[0]) : tags.toArray()));
     }
     if (options.getFacets().contains(FACET_TYPES)) {
@@ -482,7 +512,10 @@ public class RuleIndex {
       .addAggregation(termsAggregation);
 
     SearchResponse esResponse = request.get();
+    return extractAggregationTerms(aggregationKey, esResponse);
+  }
 
+  private static Set<String> extractAggregationTerms(String aggregationKey, SearchResponse esResponse) {
     Set<String> terms = new HashSet<>();
     Terms aggregation = esResponse.getAggregations().get(aggregationKey);
     if (aggregation != null) {
@@ -491,8 +524,32 @@ public class RuleIndex {
     return terms;
   }
 
+  public Set<String> listTags(String organizationUuid, @Nullable String query, int size) {
+    TermsQueryBuilder scopeFilter = QueryBuilders.termsQuery(
+      FIELD_RULE_EXTENSION_SCOPE,
+      RuleExtensionScope.system().getScope(),
+      RuleExtensionScope.organization(organizationUuid).getScope());
+
+    TermsBuilder termsAggregation = AggregationBuilders.terms(AGGREGATION_NAME_FOR_TAGS)
+      .field(FIELD_RULE_EXTENSION_TAGS)
+      .size(size)
+      .minDocCount(1);
+    Optional.ofNullable(query)
+      .map(EsUtils::escapeSpecialRegexChars)
+      .map(queryString -> ".*" + queryString + ".*")
+      .ifPresent(termsAggregation::include);
+
+    SearchRequestBuilder request = client
+      .prepareSearch(INDEX_TYPE_RULE_EXTENSION)
+      .setQuery(boolQuery().filter(scopeFilter))
+      .setSize(0)
+      .addAggregation(termsAggregation);
+
+    SearchResponse esResponse = request.get();
+    return extractAggregationTerms(AGGREGATION_NAME_FOR_TAGS, esResponse);
+  }
+
   private static boolean isNotEmpty(@Nullable Collection list) {
     return list != null && !list.isEmpty();
   }
-
 }
index 9d15fea2e19a54de81bae4ea3f8a01ba4d5479b8..3fac16bec400e9583fe23c4bb8a16e1ff517cc45 100644 (file)
@@ -52,20 +52,26 @@ public class RuleIndexDefinition implements IndexDefinition {
   public static final String FIELD_RULE_LANGUAGE = "lang";
   public static final String FIELD_RULE_IS_TEMPLATE = "isTemplate";
   public static final String FIELD_RULE_TEMPLATE_KEY = "templateKey";
-  public static final String FIELD_RULE_ALL_TAGS = "allTags";
   public static final String FIELD_RULE_TYPE = "type";
   public static final String FIELD_RULE_CREATED_AT = "createdAt";
   public static final String FIELD_RULE_UPDATED_AT = "updatedAt";
 
   public static final Set<String> SORT_FIELDS = ImmutableSet.of(
-    RuleIndexDefinition.FIELD_RULE_NAME,
-    RuleIndexDefinition.FIELD_RULE_UPDATED_AT,
-    RuleIndexDefinition.FIELD_RULE_CREATED_AT,
-    RuleIndexDefinition.FIELD_RULE_KEY);
+    FIELD_RULE_NAME,
+    FIELD_RULE_UPDATED_AT,
+    FIELD_RULE_CREATED_AT,
+    FIELD_RULE_KEY);
+
+  // Rule extension fields
+  public static final IndexType INDEX_TYPE_RULE_EXTENSION = new IndexType(INDEX, "ruleExtension");
+  /** The uuid of a {@link RuleExtensionScope} */
+  public static final String FIELD_RULE_EXTENSION_SCOPE = "scope";
+  public static final String FIELD_RULE_EXTENSION_RULE_KEY = "ruleKey";
+  public static final String FIELD_RULE_EXTENSION_TAGS = "tags";
 
   // Active rule fields
   public static final IndexType INDEX_TYPE_ACTIVE_RULE = new IndexType(INDEX, "activeRule");
-  public static final String FIELD_ACTIVE_ORGANIZATION_UUID = "organizationUuid";
+  public static final String FIELD_ACTIVE_RULE_ORGANIZATION_UUID = "organizationUuid";
   public static final String FIELD_ACTIVE_RULE_KEY = "key";
   public static final String FIELD_ACTIVE_RULE_REPOSITORY = "repo";
   public static final String FIELD_ACTIVE_RULE_INHERITANCE = "inheritance";
@@ -89,23 +95,32 @@ public class RuleIndexDefinition implements IndexDefinition {
     index.configureShards(settings, 1);
 
     // Active rule type
-    NewIndex.NewIndexType activeRuleMapping = index.createType(RuleIndexDefinition.INDEX_TYPE_ACTIVE_RULE.getType());
+    NewIndex.NewIndexType activeRuleMapping = index.createType(INDEX_TYPE_ACTIVE_RULE.getType());
     activeRuleMapping.setEnableSource(false);
-    activeRuleMapping.setAttribute("_parent", ImmutableMap.of("type", RuleIndexDefinition.INDEX_TYPE_RULE.getType()));
+    activeRuleMapping.setAttribute("_parent", ImmutableMap.of("type", INDEX_TYPE_RULE.getType()));
 
-    activeRuleMapping.stringFieldBuilder(RuleIndexDefinition.FIELD_ACTIVE_ORGANIZATION_UUID).disableNorms().build();
-    activeRuleMapping.stringFieldBuilder(RuleIndexDefinition.FIELD_ACTIVE_RULE_KEY).addSubFields(SORTABLE_ANALYZER).build();
-    activeRuleMapping.stringFieldBuilder(RuleIndexDefinition.FIELD_ACTIVE_RULE_RULE_KEY).addSubFields(SORTABLE_ANALYZER).build();
-    activeRuleMapping.stringFieldBuilder(RuleIndexDefinition.FIELD_ACTIVE_RULE_REPOSITORY).build();
-    activeRuleMapping.stringFieldBuilder(RuleIndexDefinition.FIELD_ACTIVE_RULE_PROFILE_KEY).disableNorms().build();
-    activeRuleMapping.stringFieldBuilder(RuleIndexDefinition.FIELD_ACTIVE_RULE_INHERITANCE).disableNorms().build();
-    activeRuleMapping.stringFieldBuilder(RuleIndexDefinition.FIELD_ACTIVE_RULE_SEVERITY).disableNorms().build();
+    activeRuleMapping.stringFieldBuilder(FIELD_ACTIVE_RULE_ORGANIZATION_UUID).disableNorms().build();
+    activeRuleMapping.stringFieldBuilder(FIELD_ACTIVE_RULE_KEY).addSubFields(SORTABLE_ANALYZER).build();
+    activeRuleMapping.stringFieldBuilder(FIELD_ACTIVE_RULE_RULE_KEY).addSubFields(SORTABLE_ANALYZER).build();
+    activeRuleMapping.stringFieldBuilder(FIELD_ACTIVE_RULE_REPOSITORY).build();
+    activeRuleMapping.stringFieldBuilder(FIELD_ACTIVE_RULE_PROFILE_KEY).disableNorms().build();
+    activeRuleMapping.stringFieldBuilder(FIELD_ACTIVE_RULE_INHERITANCE).disableNorms().build();
+    activeRuleMapping.stringFieldBuilder(FIELD_ACTIVE_RULE_SEVERITY).disableNorms().build();
 
-    activeRuleMapping.createLongField(RuleIndexDefinition.FIELD_ACTIVE_RULE_CREATED_AT);
-    activeRuleMapping.createLongField(RuleIndexDefinition.FIELD_ACTIVE_RULE_UPDATED_AT);
+    activeRuleMapping.createLongField(FIELD_ACTIVE_RULE_CREATED_AT);
+    activeRuleMapping.createLongField(FIELD_ACTIVE_RULE_UPDATED_AT);
+
+    // Rule extension type
+    NewIndex.NewIndexType ruleExtensionType = index.createType(INDEX_TYPE_RULE_EXTENSION.getType());
+    ruleExtensionType.setEnableSource(false);
+    ruleExtensionType.setAttribute("_parent", ImmutableMap.of("type", INDEX_TYPE_RULE.getType()));
+
+    ruleExtensionType.stringFieldBuilder(FIELD_RULE_EXTENSION_SCOPE).disableNorms().build();
+    ruleExtensionType.stringFieldBuilder(FIELD_RULE_EXTENSION_RULE_KEY).disableNorms().build();
+    ruleExtensionType.stringFieldBuilder(FIELD_RULE_EXTENSION_TAGS).build();
 
     // Rule type
-    NewIndex.NewIndexType ruleMapping = index.createType(RuleIndexDefinition.INDEX_TYPE_RULE.getType());
+    NewIndex.NewIndexType ruleMapping = index.createType(INDEX_TYPE_RULE.getType());
     ruleMapping.setEnableSource(false);
 
     ruleMapping.stringFieldBuilder(FIELD_RULE_KEY).addSubFields(SORTABLE_ANALYZER).build();
@@ -125,8 +140,7 @@ public class RuleIndexDefinition implements IndexDefinition {
 
     ruleMapping.createBooleanField(FIELD_RULE_IS_TEMPLATE);
     ruleMapping.stringFieldBuilder(FIELD_RULE_TEMPLATE_KEY).disableNorms().build();
-
-    ruleMapping.stringFieldBuilder(FIELD_RULE_ALL_TAGS).addSubFields(SEARCH_WORDS_ANALYZER).build();
+    
     ruleMapping.stringFieldBuilder(FIELD_RULE_TYPE).disableNorms().build();
 
     ruleMapping.createLongField(FIELD_RULE_CREATED_AT);
index 5c46815529ee16d26422e1eff7888416a559c834..2e53f7bf60e5ead0f5cfafa63d9a22d7cfb382d7 100644 (file)
@@ -19,8 +19,8 @@
  */
 package org.sonar.server.rule.index;
 
-import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.ImmutableSet;
+import java.util.Arrays;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
@@ -34,98 +34,103 @@ import org.sonar.server.es.BulkIndexer.Size;
 import org.sonar.server.es.EsClient;
 import org.sonar.server.es.IndexType;
 import org.sonar.server.es.StartupIndexer;
-import org.sonar.server.organization.DefaultOrganizationProvider;
 
+import static java.util.Collections.singletonList;
 import static org.sonar.server.rule.index.RuleIndexDefinition.INDEX_TYPE_RULE;
+import static org.sonar.server.rule.index.RuleIndexDefinition.INDEX_TYPE_RULE_EXTENSION;
 
 public class RuleIndexer implements StartupIndexer {
 
   private final EsClient esClient;
   private final DbClient dbClient;
-  private final RuleIteratorFactory ruleIteratorFactory;
-  private final DefaultOrganizationProvider defaultOrganizationProvider;
 
-  public RuleIndexer(EsClient esClient, DbClient dbClient, RuleIteratorFactory ruleIteratorFactory, DefaultOrganizationProvider defaultOrganizationProvider) {
+  public RuleIndexer(EsClient esClient, DbClient dbClient) {
     this.esClient = esClient;
     this.dbClient = dbClient;
-    this.ruleIteratorFactory = ruleIteratorFactory;
-    this.defaultOrganizationProvider = defaultOrganizationProvider;
   }
 
   @Override
   public Set<IndexType> getIndexTypes() {
-    return ImmutableSet.of(INDEX_TYPE_RULE);
+    return ImmutableSet.of(INDEX_TYPE_RULE, INDEX_TYPE_RULE_EXTENSION);
   }
 
   @Override
   public void indexOnStartup(Set<IndexType> uninitializedIndexTypes) {
     BulkIndexer bulk = new BulkIndexer(esClient, RuleIndexDefinition.INDEX).setSize(Size.LARGE);
+    bulk.start();
 
     // index all definitions and system extensions
     if (uninitializedIndexTypes.contains(INDEX_TYPE_RULE)) {
-      try(DbSession dbSession = dbClient.openSession(false)) {
-
-        String defaultOrganizationUuid = defaultOrganizationProvider.get().getUuid();
-        OrganizationDto defaultOrganization = dbClient.organizationDao().selectByUuid(dbSession, defaultOrganizationUuid)
-          .orElseThrow(() -> new IllegalStateException(String.format("Cannot load default organization for uuid '%s'", defaultOrganizationUuid)));
+      try (RuleIterator rules = new RuleIteratorForSingleChunk(dbClient, null)) {
+        doIndexRuleDefinitions(rules, bulk);
+      }
+    }
 
-        try (RuleIterator rules = new RuleIteratorForSingleChunk(dbClient, defaultOrganization, null)) {
-          doIndex(bulk, rules);
-        }
+    // index all organization extensions
+    if (uninitializedIndexTypes.contains(INDEX_TYPE_RULE_EXTENSION)) {
+      try (RuleMetadataIterator metadatas = new RuleMetadataIterator(dbClient)) {
+        doIndexRuleExtensions(metadatas, bulk);
       }
     }
+
+    bulk.stop();
   }
 
-  public void index(OrganizationDto organization, RuleKey ruleKey) {
-    BulkIndexer bulk = createBulkIndexer(Size.REGULAR);
-    try (RuleIterator rules = ruleIteratorFactory.createForKey(organization, ruleKey)) {
-      doIndex(bulk, rules);
-    }
+  public void indexRuleDefinition(RuleKey ruleKey) {
+    indexRuleDefinitions(singletonList(ruleKey));
   }
 
-  public void index(OrganizationDto organization, List<RuleKey> ruleKeys) {
-    BulkIndexer bulk = createBulkIndexer(Size.REGULAR);
-    try (RuleIterator rules = ruleIteratorFactory.createForKeys(organization, ruleKeys)) {
-      doIndex(bulk, rules);
+  public void indexRuleDefinitions(List<RuleKey> ruleKeys) {
+    BulkIndexer bulk = new BulkIndexer(esClient, RuleIndexDefinition.INDEX).setSize(Size.REGULAR);
+    bulk.start();
+
+    try (RuleIterator rules = new RuleIteratorForMultipleChunks(dbClient, ruleKeys)) {
+      doIndexRuleDefinitions(rules, bulk);
     }
+
+    bulk.stop();
   }
 
-  @VisibleForTesting
-  public void index(Iterator<RuleDoc> rules) {
-    doIndex(createBulkIndexer(Size.REGULAR), rules);
+  public void indexRuleExtension(OrganizationDto organization, RuleKey ruleKey) {
+    try (DbSession dbSession = dbClient.openSession(false)) {
+      dbClient.ruleDao()
+        .selectMetadataByKey(dbSession, ruleKey, organization)
+        .map(ruleExtension -> RuleExtensionDoc.of(ruleKey, RuleExtensionScope.organization(organization), ruleExtension))
+        .map(Arrays::asList)
+        .map(List::iterator)
+        .ifPresent(metadatas -> {
+          BulkIndexer bulk = new BulkIndexer(esClient, RuleIndexDefinition.INDEX).setSize(Size.REGULAR);
+          bulk.start();
+
+          doIndexRuleExtensions(metadatas, bulk);
+
+          bulk.stop();
+        });
+    }
   }
 
-  private static void doIndex(BulkIndexer bulk, Iterator<RuleDoc> rules) {
-    bulk.start();
+  private static void doIndexRuleDefinitions(Iterator<RuleDocWithSystemScope> rules, BulkIndexer bulk) {
     while (rules.hasNext()) {
-      RuleDoc rule = rules.next();
-      bulk.add(newIndexRequest(rule));
+      RuleDocWithSystemScope ruleWithExtension = rules.next();
+      bulk.add(newIndexRequest(ruleWithExtension.getRuleDoc()));
+      bulk.add(newIndexRequest(ruleWithExtension.getRuleExtensionDoc()));
     }
-    bulk.stop();
   }
 
-  private BulkIndexer createBulkIndexer(Size size) {
-    BulkIndexer bulk = new BulkIndexer(esClient, INDEX_TYPE_RULE.getIndex());
-    bulk.setSize(size);
-    return bulk;
+  private static void doIndexRuleExtensions(Iterator<RuleExtensionDoc> metadatas, BulkIndexer bulk) {
+    while (metadatas.hasNext()) {
+      RuleExtensionDoc metadata = metadatas.next();
+      bulk.add(newIndexRequest(metadata));
+    }
   }
 
   private static IndexRequest newIndexRequest(RuleDoc rule) {
     return new IndexRequest(INDEX_TYPE_RULE.getIndex(), INDEX_TYPE_RULE.getType(), rule.key().toString()).source(rule.getFields());
   }
 
-  public void delete(RuleKey ruleKey) {
-    esClient.prepareDelete(INDEX_TYPE_RULE, ruleKey.toString())
-      .setRefresh(true)
-      .get();
-  }
-
-  public void delete(List<RuleKey> rules) {
-    BulkIndexer bulk = createBulkIndexer(Size.REGULAR);
-    bulk.start();
-    for (RuleKey rule : rules) {
-      bulk.addDeletion(INDEX_TYPE_RULE, rule.toString());
-    }
-    bulk.stop();
+  private static IndexRequest newIndexRequest(RuleExtensionDoc ruleExtension) {
+    return new IndexRequest(INDEX_TYPE_RULE_EXTENSION.getIndex(), INDEX_TYPE_RULE_EXTENSION.getType())
+      .source(ruleExtension.getFields())
+      .parent(ruleExtension.getParent());
   }
 }
index 53bf4416c48b0b1c77d45512e12750464c626e90..3c5cb50bd038dd2652730819133bfa9976ea04f1 100644 (file)
@@ -21,7 +21,7 @@ package org.sonar.server.rule.index;
 
 import java.util.Iterator;
 
-public interface RuleIterator extends Iterator<RuleDoc>, AutoCloseable {
+public interface RuleIterator extends Iterator<RuleDocWithSystemScope>, AutoCloseable {
 
   @Override
   void close();
diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIteratorFactory.java b/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIteratorFactory.java
deleted file mode 100644 (file)
index 22e8791..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.server.rule.index;
-
-import java.util.List;
-import org.sonar.api.rule.RuleKey;
-import org.sonar.db.DbClient;
-import org.sonar.db.organization.OrganizationDto;
-
-import static java.util.Arrays.asList;
-
-public class RuleIteratorFactory {
-
-  private final DbClient dbClient;
-
-  public RuleIteratorFactory(DbClient dbClient) {
-    this.dbClient = dbClient;
-  }
-
-  public RuleIterator createForKey(OrganizationDto organization, RuleKey ruleKey) {
-    return new RuleIteratorForSingleChunk(dbClient, organization, asList(ruleKey));
-  }
-
-  public RuleIterator createForKeys(OrganizationDto organization, List<RuleKey> ruleKeys) {
-    return new RuleIteratorForMultipleChunks(dbClient, organization, ruleKeys);
-  }
-}
index e084faab006950ea664751aa0a2a138ac8e7ceb9..8a86b4d87243845bf8349f97b52e048edc2b1e7d 100644 (file)
@@ -26,20 +26,17 @@ import java.util.NoSuchElementException;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.db.DatabaseUtils;
 import org.sonar.db.DbClient;
-import org.sonar.db.organization.OrganizationDto;
 
 import static java.util.Optional.ofNullable;
 
 public class RuleIteratorForMultipleChunks implements RuleIterator {
 
   private final DbClient dbClient;
-  private final OrganizationDto organization;
   private final Iterator<List<RuleKey>> iteratorOverChunks;
   private RuleIteratorForSingleChunk currentChunk;
 
-  public RuleIteratorForMultipleChunks(DbClient dbClient, OrganizationDto organization, Collection<RuleKey> keys) {
+  public RuleIteratorForMultipleChunks(DbClient dbClient, Collection<RuleKey> keys) {
     this.dbClient = dbClient;
-    this.organization = organization;
     iteratorOverChunks = DatabaseUtils.toUniqueAndSortedPartitions(keys).iterator();
   }
 
@@ -58,7 +55,7 @@ public class RuleIteratorForMultipleChunks implements RuleIterator {
   }
 
   @Override
-  public RuleDoc next() {
+  public RuleDocWithSystemScope next() {
     for (;;) {
       if (currentChunk != null && currentChunk.hasNext()) {
         return currentChunk.next();
@@ -73,7 +70,7 @@ public class RuleIteratorForMultipleChunks implements RuleIterator {
 
   private RuleIteratorForSingleChunk nextChunk() {
     List<RuleKey> nextInput = iteratorOverChunks.next();
-    return new RuleIteratorForSingleChunk(dbClient, organization, nextInput);
+    return new RuleIteratorForSingleChunk(dbClient, nextInput);
   }
 
   @Override
index 2da79f6a522b9b548da98f4da5a65537f57f2281..d89cc4dbcbcc1bd34f690a33e03be2f6dd6b9c88 100644 (file)
@@ -21,7 +21,6 @@ package org.sonar.server.rule.index;
 
 import com.google.common.base.Splitter;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
@@ -36,7 +35,6 @@ import org.sonar.db.DatabaseUtils;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.ResultSetIterator;
-import org.sonar.db.organization.OrganizationDto;
 import org.sonar.db.rule.RuleDto;
 import org.sonar.db.rule.SeverityUtil;
 import org.sonar.markdown.Markdown;
@@ -59,11 +57,10 @@ public class RuleIteratorForSingleChunk implements RuleIterator {
     "r.priority",
     "r.status",
     "r.is_template",
-    "rm.tags",
     "r.system_tags",
+    "t.plugin_rule_key",
 
     // column 11
-    "t.plugin_rule_key",
     "t.plugin_name",
     "r.plugin_config_key",
     "r.language",
@@ -73,23 +70,19 @@ public class RuleIteratorForSingleChunk implements RuleIterator {
   };
 
   private static final String SQL_ALL = "SELECT " + StringUtils.join(FIELDS, ",") + " FROM rules r " +
-    "LEFT OUTER JOIN rules t ON t.id=r.template_id " +
-    "LEFT OUTER JOIN rules_metadata rm ON rm.rule_id = r.id and rm.organization_uuid=?";
-
+    "LEFT OUTER JOIN rules t ON t.id=r.template_id";
   private static final Splitter TAGS_SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings();
 
   private final DbSession session;
 
-  private final OrganizationDto organization;
   private final List<RuleKey> ruleKeys;
 
   private final PreparedStatement stmt;
-  private final ResultSetIterator<RuleDoc> iterator;
+  private final ResultSetIterator<RuleDocWithSystemScope> iterator;
 
-  RuleIteratorForSingleChunk(DbClient dbClient, OrganizationDto organization, @Nullable List<RuleKey> ruleKeys) {
+  RuleIteratorForSingleChunk(DbClient dbClient, @Nullable List<RuleKey> ruleKeys) {
     checkArgument(ruleKeys == null || ruleKeys.size() <= DatabaseUtils.PARTITION_SIZE_FOR_ORACLE,
       "Cannot search for more than " + DatabaseUtils.PARTITION_SIZE_FOR_ORACLE + " rule keys at once. Please provide the keys in smaller chunks.");
-    this.organization = organization;
     this.ruleKeys = ruleKeys;
     this.session = dbClient.openSession(false);
 
@@ -119,7 +112,7 @@ public class RuleIteratorForSingleChunk implements RuleIterator {
   }
 
   @Override
-  public RuleDoc next() {
+  public RuleDocWithSystemScope next() {
     return iterator.next();
   }
 
@@ -137,7 +130,6 @@ public class RuleIteratorForSingleChunk implements RuleIterator {
 
   private void setParameters(PreparedStatement stmt) throws SQLException {
     AtomicInteger index = new AtomicInteger(1);
-    stmt.setString(index.getAndIncrement(), organization.getUuid());
     if (ruleKeys != null && !ruleKeys.isEmpty()) {
       for (RuleKey ruleKey : ruleKeys) {
         stmt.setString(index.getAndIncrement(), ruleKey.repository());
@@ -156,19 +148,21 @@ public class RuleIteratorForSingleChunk implements RuleIterator {
     }
   }
 
-  private static final class RuleIteratorInternal extends ResultSetIterator<RuleDoc> {
+  private static final class RuleIteratorInternal extends ResultSetIterator<RuleDocWithSystemScope> {
 
     public RuleIteratorInternal(PreparedStatement stmt) throws SQLException {
       super(stmt);
     }
 
     @Override
-    protected RuleDoc read(ResultSet rs) throws SQLException {
+    protected RuleDocWithSystemScope read(ResultSet rs) throws SQLException {
       RuleDoc doc = new RuleDoc();
+      RuleExtensionDoc extensionDoc = new RuleExtensionDoc().setScope(RuleExtensionScope.system());
 
       String ruleKey = rs.getString(1);
       String repositoryKey = rs.getString(2);
       RuleKey key = RuleKey.of(repositoryKey, ruleKey);
+      extensionDoc.setRuleKey(key);
 
       // all the fields must be present, even if value is null
       doc.setKey(key.toString());
@@ -191,23 +185,23 @@ public class RuleIteratorForSingleChunk implements RuleIterator {
       doc.setSeverity(SeverityUtil.getSeverityFromOrdinal(rs.getInt(6)));
       doc.setStatus(rs.getString(7));
       doc.setIsTemplate(rs.getBoolean(8));
-      doc.setAllTags(Sets.union(stringTagsToSet(rs.getString(9)), stringTagsToSet(rs.getString(10))));
+      extensionDoc.setTags(stringTagsToSet(rs.getString(9)));
 
-      String templateRuleKey = rs.getString(11);
-      String templateRepoKey = rs.getString(12);
+      String templateRuleKey = rs.getString(10);
+      String templateRepoKey = rs.getString(11);
       if (templateRepoKey != null && templateRuleKey != null) {
         doc.setTemplateKey(RuleKey.of(templateRepoKey, templateRuleKey).toString());
       } else {
         doc.setTemplateKey(null);
       }
 
-      doc.setInternalKey(rs.getString(13));
-      doc.setLanguage(rs.getString(14));
-      doc.setType(RuleType.valueOf(rs.getInt(15)));
-      doc.setCreatedAt(rs.getLong(16));
-      doc.setUpdatedAt(rs.getLong(17));
+      doc.setInternalKey(rs.getString(12));
+      doc.setLanguage(rs.getString(13));
+      doc.setType(RuleType.valueOf(rs.getInt(14)));
+      doc.setCreatedAt(rs.getLong(15));
+      doc.setUpdatedAt(rs.getLong(16));
 
-      return doc;
+      return new RuleDocWithSystemScope(doc, extensionDoc);
     }
 
     private static Set<String> stringTagsToSet(@Nullable String tags) {
diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleMetadataIterator.java b/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleMetadataIterator.java
new file mode 100644 (file)
index 0000000..fc97f56
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.rule.index;
+
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableSet;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Iterator;
+import java.util.Set;
+import javax.annotation.Nullable;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.db.DatabaseUtils;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.ResultSetIterator;
+
+/**
+ * Scrolls over table RULES_METADATA and reads documents to populate the rule extension index type
+ */
+public class RuleMetadataIterator implements Iterator<RuleExtensionDoc>, AutoCloseable {
+
+  private static final String[] FIELDS = {
+    "r.plugin_name",
+    "r.plugin_rule_key",
+    "rm.organization_uuid",
+    "rm.tags"
+  };
+
+  private static final String SQL_ALL = "SELECT " + StringUtils.join(FIELDS, ",") + " FROM rules r " +
+    "INNER JOIN rules_metadata rm ON rm.rule_id = r.id " +
+    "WHERE rm.tags is not null AND rm.tags != ''";
+  private static final Splitter TAGS_SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings();
+
+  private final DbSession session;
+
+  private final PreparedStatement stmt;
+  private final ResultSetIterator<RuleExtensionDoc> iterator;
+
+  RuleMetadataIterator(DbClient dbClient) {
+    this.session = dbClient.openSession(false);
+
+    try {
+      String sql = SQL_ALL;
+      stmt = dbClient.getMyBatis().newScrollingSelectStatement(session, sql);
+      iterator = createIterator();
+    } catch (Exception e) {
+      session.close();
+      throw new IllegalStateException("Fail to prepare SQL request to select all rules", e);
+    }
+  }
+
+  private RuleMetadataIteratorInternal createIterator() {
+    try {
+      return new RuleMetadataIteratorInternal(stmt);
+    } catch (SQLException e) {
+      DatabaseUtils.closeQuietly(stmt);
+      throw new IllegalStateException("Fail to prepare SQL request to select all rules", e);
+    }
+  }
+
+  @Override
+  public boolean hasNext() {
+    return iterator.hasNext();
+  }
+
+  @Override
+  public RuleExtensionDoc next() {
+    return iterator.next();
+  }
+
+  @Override
+  public void close() {
+    try {
+      iterator.close();
+    } finally {
+      DatabaseUtils.closeQuietly(stmt);
+      session.close();
+    }
+  }
+
+  private static final class RuleMetadataIteratorInternal extends ResultSetIterator<RuleExtensionDoc> {
+
+    public RuleMetadataIteratorInternal(PreparedStatement stmt) throws SQLException {
+      super(stmt);
+    }
+
+    @Override
+    protected RuleExtensionDoc read(ResultSet rs) throws SQLException {
+      RuleExtensionDoc doc = new RuleExtensionDoc();
+
+      String ruleKey = rs.getString(1);
+      String repositoryKey = rs.getString(2);
+      RuleKey key = RuleKey.of(repositoryKey, ruleKey);
+      doc.setRuleKey(key);
+      doc.setScope(RuleExtensionScope.organization(rs.getString(3)));
+      doc.setTags(stringTagsToSet(rs.getString(4)));
+
+      return doc;
+    }
+
+    private static Set<String> stringTagsToSet(@Nullable String tags) {
+      return ImmutableSet.copyOf(TAGS_SPLITTER.split(tags == null ? "" : tags));
+    }
+  }
+}
index 92bc3f44f67e2a8d1aa2279f4dc41e09e69e7ead..f493b9c570925fe3ab87a0042e9418b1663d2fe5 100644 (file)
@@ -50,6 +50,7 @@ public class RuleQuery {
   private boolean ascendingSort = true;
   private String internalKey;
   private String ruleKey;
+  private String organizationUuid;
 
   @CheckForNull
   public String getQProfileKey() {
@@ -258,4 +259,14 @@ public class RuleQuery {
   public String getRuleKey() {
     return ruleKey;
   }
+
+  @CheckForNull
+  public String getOrganizationUuid() {
+    return organizationUuid;
+  }
+
+  public RuleQuery setOrganizationUuid(@Nullable String organizationUuid) {
+    this.organizationUuid = organizationUuid;
+    return this;
+  }
 }
index b2cd4d390132ba51dbf917446a9654c872b3f935..1d9fe56726600cc1b4305b9c2eb5f593593574a7 100644 (file)
 package org.sonar.server.rule.ws;
 
 import com.google.common.io.Resources;
+import java.util.Optional;
 import java.util.Set;
+import javax.annotation.Nullable;
 import org.sonar.api.server.ws.Request;
 import org.sonar.api.server.ws.Response;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.api.server.ws.WebService.NewAction;
 import org.sonar.api.server.ws.WebService.Param;
 import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.organization.OrganizationDto;
+import org.sonar.server.organization.DefaultOrganizationProvider;
 import org.sonar.server.rule.index.RuleIndex;
-import org.sonar.server.rule.index.RuleIndexDefinition;
+import org.sonar.server.ws.WsUtils;
+
+import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_ORGANIZATION;
 
 public class TagsAction implements RulesWsAction {
 
   private final RuleIndex ruleIndex;
+  private final DbClient dbClient;
+  private final DefaultOrganizationProvider defaultOrganizationProvider;
 
-  public TagsAction(RuleIndex ruleIndex) {
+  public TagsAction(RuleIndex ruleIndex, DbClient dbClient, DefaultOrganizationProvider defaultOrganizationProvider) {
     this.ruleIndex = ruleIndex;
+    this.dbClient = dbClient;
+    this.defaultOrganizationProvider = defaultOrganizationProvider;
   }
 
   @Override
@@ -54,18 +66,41 @@ public class TagsAction implements RulesWsAction {
       .setDescription("The size of the list to return, 0 for all tags")
       .setExampleValue("25")
       .setDefaultValue("0");
+    action
+      .createParam(PARAM_ORGANIZATION)
+      .setDescription("Organization key")
+      .setRequired(false)
+      .setInternal(true)
+      .setExampleValue("my-org")
+      .setSince("6.4");
+
   }
 
   @Override
   public void handle(Request request, Response response) {
+    OrganizationDto organization = getOrganization(request.param(PARAM_ORGANIZATION));
     String query = request.param(Param.TEXT_QUERY);
     int pageSize = request.mandatoryParamAsInt("ps");
-    Set<String> tags = ruleIndex.terms(RuleIndexDefinition.FIELD_RULE_ALL_TAGS, query, pageSize);
+
+    Set<String> tags = ruleIndex.listTags(organization.getUuid(), query, pageSize == 0 ? Integer.MAX_VALUE : pageSize);
+
+    writeResponse(response, tags);
+  }
+
+  private OrganizationDto getOrganization(@Nullable String organizationKey) {
+    try (DbSession dbSession = dbClient.openSession(false)) {
+      String organizationOrDefaultKey = Optional.ofNullable(organizationKey)
+        .orElseGet(defaultOrganizationProvider.get()::getKey);
+      return  WsUtils.checkFoundWithOptional(
+        dbClient.organizationDao().selectByKey(dbSession, organizationOrDefaultKey),
+        "No organization with key '%s'", organizationOrDefaultKey);
+    }
+  }
+
+  private static void writeResponse(Response response, Set<String> tags) {
     JsonWriter json = response.newJsonWriter().beginObject();
     json.name("tags").beginArray();
-    for (String tag : tags) {
-      json.value(tag);
-    }
+    tags.forEach(json::value);
     json.endArray().endObject().close();
   }
 }
index e35c1df96de4d3ce70e968e7514991ae9a51858d..e3328c4ea7c3e82c04287fee9038b9e28415a42a 100644 (file)
@@ -35,7 +35,7 @@ import org.sonar.db.component.SnapshotDto;
 import org.sonar.db.component.SnapshotTesting;
 import org.sonar.db.issue.IssueDto;
 import org.sonar.db.issue.IssueTesting;
-import org.sonar.db.rule.RuleDto;
+import org.sonar.db.rule.RuleDefinitionDto;
 import org.sonar.db.rule.RuleTesting;
 import org.sonar.server.es.ProjectIndexer;
 
@@ -131,9 +131,9 @@ public class ComponentCleanerServiceTest {
     String suffix = String.valueOf(id);
     ComponentDto project = newProjectDto(db.organizations().insert(), "project-uuid-" + suffix)
       .setKey("project-key-" + suffix);
-    RuleDto rule = RuleTesting.newDto(RuleKey.of("sonarqube", "rule-" + suffix));
-    dbClient.ruleDao().insert(dbSession, rule.getDefinition());
-    IssueDto issue = IssueTesting.newDto(rule, project, project).setKee("issue-key-" + suffix).setUpdatedAt(new Date().getTime());
+    RuleDefinitionDto rule = RuleTesting.newRule(RuleKey.of("sonarqube", "rule-" + suffix));
+    dbClient.ruleDao().insert(dbSession, rule);
+    IssueDto issue = IssueTesting.newIssue(rule, project, project).setKee("issue-key-" + suffix).setUpdatedAt(new Date().getTime());
     dbClient.componentDao().insert(dbSession, project);
     SnapshotDto snapshot = dbClient.snapshotDao().insert(dbSession, SnapshotTesting.newAnalysis(project));
     dbClient.issueDao().insert(dbSession, issue);
index 23dc5c4acf6bb87baffc256c7120b4941157b6a4..760f975e9d2a4d7d73112e52548b2149f591c8a7 100644 (file)
@@ -39,7 +39,7 @@ import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.ComponentTesting;
 import org.sonar.db.issue.IssueDto;
 import org.sonar.db.organization.OrganizationDto;
-import org.sonar.db.rule.RuleDto;
+import org.sonar.db.rule.RuleDefinitionDto;
 import org.sonar.db.rule.RuleTesting;
 import org.sonar.scanner.protocol.output.ScannerReport;
 import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
@@ -95,8 +95,8 @@ public class PersistIssuesStepTest extends BaseStepTest {
 
   @Test
   public void insert_new_issue() {
-    RuleDto rule = RuleTesting.newDto(RuleKey.of("xoo", "S01"));
-    dbTester.rules().insertRule(rule);
+    RuleDefinitionDto rule = RuleTesting.newRule(RuleKey.of("xoo", "S01"));
+    dbTester.rules().insert(rule);
     OrganizationDto organizationDto = dbTester.organizations().insert();
     ComponentDto project = ComponentTesting.newProjectDto(organizationDto);
     dbClient.componentDao().insert(session, project);
index 28f9f3343ca45220cc8f501ad4fcc201ae5f7f00..d07dbe8a7e6c738065202fcaf8a34443b97fccd3 100644 (file)
@@ -47,7 +47,6 @@ import org.sonar.server.es.SearchResult;
 import org.sonar.server.issue.index.IssueDoc;
 import org.sonar.server.issue.index.IssueIndex;
 import org.sonar.server.issue.index.IssueIndexer;
-import org.sonar.server.organization.DefaultOrganizationProvider;
 import org.sonar.server.permission.GroupPermissionChange;
 import org.sonar.server.permission.PermissionChange;
 import org.sonar.server.permission.PermissionUpdater;
@@ -74,7 +73,6 @@ public class IssueServiceMediumTest {
   private DbSession session;
   private IssueService service;
   private RuleIndexer ruleIndexer;
-  private OrganizationDto defaultOrganization;
 
   @Before
   public void setUp() {
@@ -83,9 +81,6 @@ public class IssueServiceMediumTest {
     session = db.openSession(false);
     service = tester.get(IssueService.class);
     ruleIndexer = tester.get(RuleIndexer.class);
-    String defaultOrganizationUuid = tester.get(DefaultOrganizationProvider.class).get().getUuid();
-    defaultOrganization = db.organizationDao().selectByUuid(session, defaultOrganizationUuid)
-      .orElseThrow(() -> new IllegalStateException(String.format("Could not find defautl organization '%s'", defaultOrganizationUuid)));
   }
 
   @After
@@ -95,7 +90,7 @@ public class IssueServiceMediumTest {
 
   @Test
   public void set_tags() {
-    RuleDto rule = newRule(defaultOrganization);
+    RuleDto rule = newRule();
     ComponentDto project = newProject();
     ComponentDto file = newFile(project);
     userSessionRule.logIn("john").addProjectUuidPermissions(UserRole.USER, project.uuid());
@@ -130,7 +125,7 @@ public class IssueServiceMediumTest {
 
   @Test
   public void list_component_tags() {
-    RuleDto rule = newRule(defaultOrganization);
+    RuleDto rule = newRule();
     ComponentDto project = newProject();
     ComponentDto file = newFile(project);
     saveIssue(IssueTesting.newDto(rule, file, project).setTags(ImmutableSet.of("convention", "java8", "bug")));
@@ -150,7 +145,7 @@ public class IssueServiceMediumTest {
 
   @Test
   public void test_listAuthors() {
-    RuleDto rule = newRule(defaultOrganization);
+    RuleDto rule = newRule();
     ComponentDto project = newProject();
     ComponentDto file = newFile(project);
     saveIssue(IssueTesting.newDto(rule, file, project).setAuthorLogin("luke.skywalker"));
@@ -167,7 +162,7 @@ public class IssueServiceMediumTest {
 
   @Test
   public void listAuthors_escapes_regexp_special_characters() {
-    saveIssue(IssueTesting.newDto(newRule(defaultOrganization), newFile(newProject()), newProject()).setAuthorLogin("name++"));
+    saveIssue(IssueTesting.newDto(newRule(), newFile(newProject()), newProject()).setAuthorLogin("name++"));
 
     assertThat(service.listAuthors("invalidRegexp[", 5)).isEmpty();
     assertThat(service.listAuthors("nam+", 5)).isEmpty();
@@ -175,18 +170,18 @@ public class IssueServiceMediumTest {
     assertThat(service.listAuthors(".*", 5)).isEmpty();
   }
 
-  private RuleDto newRule(OrganizationDto organization) {
-    return newRule(organization, RuleTesting.newXooX1());
+  private RuleDto newRule() {
+    return newRule(RuleTesting.newXooX1());
   }
 
-  private RuleDto newRule(OrganizationDto organization, RuleDto rule) {
+  private RuleDto newRule(RuleDto rule) {
     RuleDao ruleDao = tester.get(RuleDao.class);
     ruleDao.insert(session, rule.getDefinition());
     if (rule.getOrganizationUuid() != null) {
       ruleDao.insertOrUpdate(session, rule.getMetadata().setRuleId(rule.getId()));
     }
     session.commit();
-    ruleIndexer.index(organization, rule.getKey());
+    ruleIndexer.indexRuleDefinition(rule.getDefinition().getKey());
     return rule;
   }
 
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/RulesAggregation.java b/server/sonar-server/src/test/java/org/sonar/server/issue/RulesAggregation.java
new file mode 100644 (file)
index 0000000..08eeee3
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.issue;
+
+import com.google.common.collect.HashMultiset;
+import com.google.common.collect.Multiset;
+import java.util.Collection;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.db.rule.RuleDefinitionDto;
+
+public class RulesAggregation {
+
+  private Multiset<Rule> rules;
+
+  public RulesAggregation() {
+    this.rules = HashMultiset.create();
+  }
+
+  public RulesAggregation add(RuleDefinitionDto ruleDto) {
+    rules.add(new Rule(ruleDto.getKey(), ruleDto.getName()));
+    return this;
+  }
+
+  public Collection<Rule> rules() {
+    return rules.elementSet();
+  }
+
+  public int countRule(Rule rule) {
+    return rules.count(rule);
+  }
+
+  public static class Rule {
+
+    private RuleKey ruleKey;
+    private String name;
+
+    public Rule(RuleKey ruleKey, String name) {
+      this.ruleKey = ruleKey;
+      this.name = name;
+    }
+
+    public RuleKey ruleKey() {
+      return ruleKey;
+    }
+
+    public String name() {
+      return name;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) {
+        return true;
+      }
+      if (o == null || getClass() != o.getClass()) {
+        return false;
+      }
+
+      Rule rule = (Rule) o;
+
+      if (!ruleKey.equals(rule.ruleKey)) {
+        return false;
+      }
+
+      return true;
+    }
+
+    @Override
+    public int hashCode() {
+      return ruleKey.hashCode();
+    }
+  }
+}
index f23a3d31896669a9724c8d8438e7df75cb41c7db..978e2846ca747b1a6dff74accb4194994d2b99cf 100644 (file)
@@ -21,7 +21,7 @@ package org.sonar.server.issue;
 
 import org.junit.Test;
 import org.sonar.api.rule.RuleKey;
-import org.sonar.db.rule.RuleDto;
+import org.sonar.db.rule.RuleDefinitionDto;
 import org.sonar.db.rule.RuleTesting;
 
 import static org.assertj.core.api.Assertions.assertThat;
@@ -38,7 +38,7 @@ public class RulesAggregationTest {
   public void count_rules() {
     RulesAggregation rulesAggregation = new RulesAggregation();
     RuleKey ruleKey = RuleKey.of("xoo", "S001");
-    RuleDto ruleDto = RuleTesting.newDto(ruleKey).setName("Rule name");
+    RuleDefinitionDto ruleDto = RuleTesting.newRule(ruleKey).setName("Rule name");
     rulesAggregation.add(ruleDto);
     rulesAggregation.add(ruleDto);
 
@@ -53,10 +53,10 @@ public class RulesAggregationTest {
   public void count_rules_with_different_rules() {
     RulesAggregation rulesAggregation = new RulesAggregation();
 
-    RuleDto ruleDto = RuleTesting.newDto(RuleKey.of("xoo", "S001")).setName("Rule name 1");
+    RuleDefinitionDto ruleDto = RuleTesting.newRule(RuleKey.of("xoo", "S001")).setName("Rule name 1");
     rulesAggregation.add(ruleDto);
     rulesAggregation.add(ruleDto);
-    rulesAggregation.add(RuleTesting.newDto(RuleKey.of("xoo", "S002")).setName("Rule name 2"));
+    rulesAggregation.add(RuleTesting.newRule(RuleKey.of("xoo", "S002")).setName("Rule name 2"));
 
     assertThat(rulesAggregation.rules()).hasSize(2);
   }
index 4b86f20ec2067f65d9fbbc2d56c9f88d962fdc0d..9791f02b614bb5d3378038ef5ee3f0c7e280fb40 100644 (file)
@@ -20,7 +20,6 @@
 package org.sonar.server.issue.index;
 
 import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterators;
 import com.google.common.collect.Lists;
 import java.util.Date;
@@ -40,9 +39,11 @@ import org.sonar.api.rule.Severity;
 import org.sonar.api.utils.Duration;
 import org.sonar.api.utils.KeyValueFormat;
 import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.ComponentTesting;
 import org.sonar.db.organization.OrganizationDto;
+import org.sonar.db.rule.RuleDefinitionDto;
 import org.sonar.db.user.GroupDto;
 import org.sonar.db.user.UserDto;
 import org.sonar.server.es.EsTester;
@@ -52,8 +53,6 @@ import org.sonar.server.issue.IssueQuery;
 import org.sonar.server.permission.index.AuthorizationTypeSupport;
 import org.sonar.server.permission.index.PermissionIndexerDao;
 import org.sonar.server.permission.index.PermissionIndexerTester;
-import org.sonar.server.rule.index.RuleDoc;
-import org.sonar.server.rule.index.RuleDocTesting;
 import org.sonar.server.rule.index.RuleIndexDefinition;
 import org.sonar.server.rule.index.RuleIndexer;
 import org.sonar.server.tester.UserSessionRule;
@@ -61,6 +60,7 @@ import org.sonar.server.view.index.ViewDoc;
 import org.sonar.server.view.index.ViewIndexDefinition;
 import org.sonar.server.view.index.ViewIndexer;
 
+import static com.google.common.collect.ImmutableSet.of;
 import static com.google.common.collect.Lists.newArrayList;
 import static java.util.Arrays.asList;
 import static org.assertj.core.api.Assertions.assertThat;
@@ -73,8 +73,6 @@ import static org.sonar.api.utils.DateUtils.parseDateTime;
 import static org.sonar.db.component.ComponentTesting.newFileDto;
 import static org.sonar.db.component.ComponentTesting.newProjectDto;
 import static org.sonar.db.organization.OrganizationTesting.newOrganizationDto;
-import static org.sonar.db.rule.RuleTesting.XOO_X1;
-import static org.sonar.db.rule.RuleTesting.XOO_X2;
 import static org.sonar.db.user.GroupTesting.newGroupDto;
 import static org.sonar.db.user.UserTesting.newUserDto;
 import static org.sonar.server.issue.IssueDocTesting.newDoc;
@@ -89,16 +87,19 @@ public class IssueIndexTest {
     new ViewIndexDefinition(new MapSettings()),
     new RuleIndexDefinition(new MapSettings()));
   @Rule
+  public DbTester db = DbTester.create(system2);
+  @Rule
   public UserSessionRule userSessionRule = UserSessionRule.standalone();
   @Rule
   public ExpectedException expectedException = ExpectedException.none();
 
   private IssueIndexer issueIndexer = new IssueIndexer(tester.client(), new IssueIteratorFactory(null));
   private ViewIndexer viewIndexer = new ViewIndexer(null, tester.client());
-  private RuleIndexer ruleIndexer = new RuleIndexer(tester.client(), null, null, null);
+  private RuleIndexer ruleIndexer = new RuleIndexer(tester.client(), db.getDbClient());
   private PermissionIndexerTester authorizationIndexerTester = new PermissionIndexerTester(tester, issueIndexer);
 
-  private IssueIndex underTest = new IssueIndex(tester.client(), system2, userSessionRule, new AuthorizationTypeSupport(userSessionRule));
+  private IssueIndex underTest = new IssueIndex(tester.client(), system2, userSessionRule, new AuthorizationTypeSupport(userSessionRule)
+  );
 
   @Before
   public void setUp() {
@@ -1326,24 +1327,27 @@ public class IssueIndexTest {
 
   @Test
   public void list_tags() {
-    indexRules(
-      RuleDocTesting.newDoc(XOO_X1).setAllTags(asList("tag1", "systag1")),
-      RuleDocTesting.newDoc(XOO_X2).setAllTags(asList("tag2", "systag2")));
+    RuleDefinitionDto r1 = db.rules().insert();
+    ruleIndexer.indexRuleDefinition(r1.getKey());
+
+    RuleDefinitionDto r2 = db.rules().insert();
+    ruleIndexer.indexRuleDefinition(r2.getKey());
+
+    OrganizationDto org = db.organizations().insert();
     ComponentDto project = newProjectDto(newOrganizationDto());
     ComponentDto file = newFileDto(project, null);
     indexIssues(
-      newDoc("ISSUE1", file).setRuleKey(XOO_X1.toString()).setTags(ImmutableSet.of("convention", "java8", "bug")),
-      newDoc("ISSUE2", file).setRuleKey(XOO_X1.toString()).setTags(ImmutableSet.of("convention", "bug")),
-      newDoc("ISSUE3", file).setRuleKey(XOO_X2.toString()),
-      newDoc("ISSUE4", file).setRuleKey(XOO_X1.toString()).setTags(ImmutableSet.of("convention")));
+      newDoc("ISSUE1", file).setOrganizationUuid(org.getUuid()).setRuleKey(r1.getKey().toString()).setTags(of("convention", "java8", "bug")),
+      newDoc("ISSUE2", file).setOrganizationUuid(org.getUuid()).setRuleKey(r1.getKey().toString()).setTags(of("convention", "bug")),
+      newDoc("ISSUE3", file).setOrganizationUuid(org.getUuid()).setRuleKey(r2.getKey().toString()),
+      newDoc("ISSUE4", file).setOrganizationUuid(org.getUuid()).setRuleKey(r1.getKey().toString()).setTags(of("convention")));
 
-    assertThat(underTest.listTags(null, 5)).containsOnly("convention", "java8", "bug", "systag1", "systag2");
-    assertThat(underTest.listTags(null, 2)).containsOnly("bug", "convention");
-    assertThat(underTest.listTags("vent", 5)).containsOnly("convention");
-    assertThat(underTest.listTags("sys", 5)).containsOnly("systag1", "systag2");
-    assertThat(underTest.listTags(null, 1)).containsOnly("bug");
-    assertThat(underTest.listTags(null, Integer.MAX_VALUE)).containsOnly("convention", "java8", "bug", "systag1", "systag2", "tag1", "tag2");
-    assertThat(underTest.listTags("invalidRegexp[", 5)).isEmpty();
+    assertThat(underTest.listTags(org.getUuid(), null, Integer.MAX_VALUE)).containsOnly("convention", "java8", "bug");
+    assertThat(underTest.listTags(org.getUuid(), null, 2)).containsOnly("bug", "convention");
+    assertThat(underTest.listTags(org.getUuid(), "vent", Integer.MAX_VALUE)).containsOnly("convention");
+    assertThat(underTest.listTags(org.getUuid(), null, 1)).containsOnly("bug");
+    assertThat(underTest.listTags(org.getUuid(), null, Integer.MAX_VALUE)).containsOnly("convention", "java8", "bug");
+    assertThat(underTest.listTags(org.getUuid(), "invalidRegexp[", Integer.MAX_VALUE)).isEmpty();
   }
 
   @Test
@@ -1405,8 +1409,4 @@ public class IssueIndexTest {
   private void indexView(String viewUuid, List<String> projects) {
     viewIndexer.index(new ViewDoc().setUuid(viewUuid).setProjects(projects));
   }
-
-  private void indexRules(RuleDoc... rules) {
-    ruleIndexer.index(asList(rules).iterator());
-  }
 }
index 25410a41e22d2bf60ce918f35d0566c7969320bc..194ced26a8b8bdeb91d9d1d0e3ab4742c70a4a84 100644 (file)
@@ -19,7 +19,6 @@
  */
 package org.sonar.server.issue.ws;
 
-import com.google.common.collect.ImmutableSet;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -32,25 +31,26 @@ import org.sonar.db.component.ComponentDto;
 import org.sonar.db.issue.IssueDto;
 import org.sonar.db.issue.IssueTesting;
 import org.sonar.db.organization.OrganizationDto;
-import org.sonar.db.rule.RuleDto;
+import org.sonar.db.rule.RuleDefinitionDto;
 import org.sonar.server.es.EsTester;
 import org.sonar.server.issue.index.IssueIndex;
 import org.sonar.server.issue.index.IssueIndexDefinition;
 import org.sonar.server.issue.index.IssueIndexer;
 import org.sonar.server.issue.index.IssueIteratorFactory;
+import org.sonar.server.organization.TestDefaultOrganizationProvider;
 import org.sonar.server.permission.index.AuthorizationTypeSupport;
+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.rule.index.RuleIteratorFactory;
 import org.sonar.server.tester.UserSessionRule;
 import org.sonar.server.ws.WsActionTester;
 
 import static java.util.Arrays.asList;
-import static java.util.Collections.emptySet;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.sonar.api.web.UserRole.USER;
 import static org.sonar.db.component.ComponentTesting.newFileDto;
-import static org.sonar.db.rule.RuleTesting.newRuleDto;
+import static org.sonar.db.rule.RuleTesting.setSystemTags;
+import static org.sonar.db.rule.RuleTesting.setTags;
 import static org.sonar.test.JsonAssert.assertJson;
 
 public class TagsActionTest {
@@ -63,10 +63,11 @@ public class TagsActionTest {
   public EsTester es = new EsTester(new IssueIndexDefinition(new MapSettings()), new RuleIndexDefinition(new MapSettings()));
 
   private IssueIndexer issueIndexer = new IssueIndexer(es.client(), new IssueIteratorFactory(db.getDbClient()));
-  private RuleIndexer ruleIndexer = new RuleIndexer(es.client(), null, new RuleIteratorFactory(db.getDbClient()), null);
+  private RuleIndexer ruleIndexer = new RuleIndexer(es.client(), db.getDbClient());
   private IssueIndex issueIndex = new IssueIndex(es.client(), System2.INSTANCE, userSession, new AuthorizationTypeSupport(userSession));
+  private RuleIndex ruleIndex = new RuleIndex(es.client());
 
-  private WsActionTester tester = new WsActionTester(new TagsAction(issueIndex));
+  private WsActionTester tester = new WsActionTester(new TagsAction(issueIndex, ruleIndex, TestDefaultOrganizationProvider.from(db)));
   private OrganizationDto organization;
 
   @Before
@@ -86,10 +87,15 @@ public class TagsActionTest {
 
   @Test
   public void return_tags_from_rules() throws Exception {
-    RuleDto r = db.rules().insertRule(organization, rule -> rule.setSystemTags(ImmutableSet.of("tag1")), rule -> rule.setTags(ImmutableSet.of("tag2")));
-    RuleDto r2 = db.rules().insertRule(organization, rule -> rule.setSystemTags(ImmutableSet.of("tag3")), rule -> rule.setTags(ImmutableSet.of("tag4", "tag5")));
-    db.commit();
-    ruleIndexer.index(organization, asList(r.getKey(), r2.getKey()));
+    RuleDefinitionDto r = db.rules().insert(setSystemTags("tag1"));
+    ruleIndexer.indexRuleDefinition(r.getKey());
+    db.rules().insertOrUpdateMetadata(r, organization, setTags("tag2"));
+    ruleIndexer.indexRuleExtension(organization, r.getKey());
+
+    RuleDefinitionDto r2 = db.rules().insert(setSystemTags("tag3"));
+    ruleIndexer.indexRuleDefinition(r2.getKey());
+    db.rules().insertOrUpdateMetadata(r2, organization, setTags("tag4", "tag5"));
+    ruleIndexer.indexRuleExtension(organization, r2.getKey());
 
     String result = tester.newRequest().execute().getInput();
     assertJson(result).isSimilarTo("{\"tags\":[\"tag1\", \"tag2\", \"tag3\", \"tag4\", \"tag5\"]}");
@@ -100,8 +106,11 @@ public class TagsActionTest {
     insertIssueWithBrowsePermission(insertRuleWithoutTags(), "tag1", "tag2");
     insertIssueWithBrowsePermission(insertRuleWithoutTags(), "tag3", "tag4", "tag5");
     issueIndexer.indexOnStartup(null);
-    RuleDto r = db.rules().insertRule(db.getDefaultOrganization(), rule -> rule.setSystemTags(ImmutableSet.of("tag6")).setTags(ImmutableSet.of("tag7")));
-    ruleIndexer.index(organization, r.getKey());
+
+    RuleDefinitionDto r = db.rules().insert(setSystemTags("tag6"));
+    ruleIndexer.indexRuleDefinition(r.getKey());
+    db.rules().insertOrUpdateMetadata(r, organization, setTags("tag7"));
+    ruleIndexer.indexRuleExtension(organization, r.getKey());
 
     String result = tester.newRequest().execute().getInput();
     assertJson(result).isSimilarTo("{\"tags\":[\"tag1\", \"tag2\", \"tag3\", \"tag4\", \"tag5\", \"tag6\", \"tag7\"]}");
@@ -137,8 +146,11 @@ public class TagsActionTest {
   public void test_example() throws Exception {
     insertIssueWithBrowsePermission(insertRuleWithoutTags(), "convention");
     issueIndexer.indexOnStartup(null);
-    RuleDto r = db.rules().insertRule(db.getDefaultOrganization(), rule -> rule.setSystemTags(ImmutableSet.of("cwe")).setTags(ImmutableSet.of("security")));
-    ruleIndexer.index(organization, r.getKey());
+
+    RuleDefinitionDto r = db.rules().insert(setSystemTags("cwe"));
+    ruleIndexer.indexRuleDefinition(r.getKey());
+    db.rules().insertOrUpdateMetadata(r, organization, setTags("security"));
+    ruleIndexer.indexRuleExtension(organization, r.getKey());
 
     String result = tester.newRequest().execute().getInput();
     assertJson(result).isSimilarTo(tester.getDef().responseExampleAsString());
@@ -154,27 +166,27 @@ public class TagsActionTest {
     assertThat(action.params()).hasSize(2);
 
     Param query = action.param("q");
+    assertThat(query).isNotNull();
     assertThat(query.isRequired()).isFalse();
     assertThat(query.description()).isNotEmpty();
     assertThat(query.exampleValue()).isNotEmpty();
 
     Param pageSize = action.param("ps");
+    assertThat(pageSize).isNotNull();
     assertThat(pageSize.isRequired()).isFalse();
     assertThat(pageSize.defaultValue()).isEqualTo("10");
     assertThat(pageSize.description()).isNotEmpty();
     assertThat(pageSize.exampleValue()).isNotEmpty();
   }
 
-  private RuleDto insertRuleWithoutTags() {
-    RuleDto ruleDto = newRuleDto(db.getDefaultOrganization()).setTags(emptySet()).setSystemTags(emptySet());
-    db.rules().insertRule(ruleDto);
-    return ruleDto;
+  private RuleDefinitionDto insertRuleWithoutTags() {
+    return db.rules().insert(setSystemTags());
   }
 
-  private IssueDto insertIssue(RuleDto rule, String... tags) {
+  private IssueDto insertIssue(RuleDefinitionDto rule, String... tags) {
     ComponentDto project = db.components().insertProject(organization);
     ComponentDto file = db.components().insertComponent(newFileDto(project));
-    IssueDto issueDto = IssueTesting.newDto(rule, file, project).setTags(asList(tags));
+    IssueDto issueDto = IssueTesting.newIssue(rule, file, project).setTags(asList(tags));
     return db.issues().insertIssue(issueDto);
   }
 
@@ -182,7 +194,7 @@ public class TagsActionTest {
     userSession.logIn("john").addProjectUuidPermissions(USER, issue.getProjectUuid());
   }
 
-  private IssueDto insertIssueWithBrowsePermission(RuleDto rule, String... tags) {
+  private IssueDto insertIssueWithBrowsePermission(RuleDefinitionDto rule, String... tags) {
     IssueDto issue = insertIssue(rule, tags);
     setUserWithBrowsePermission(issue);
     return issue;
index f2762906b124a7a142f3bba741bb2cca5dc47712..2653ce32b68168345c370de12fd861329b58eefa 100644 (file)
@@ -42,6 +42,7 @@ import org.sonar.db.qualityprofile.ActiveRuleDao;
 import org.sonar.db.qualityprofile.ActiveRuleDto;
 import org.sonar.db.qualityprofile.ActiveRuleParamDto;
 import org.sonar.db.qualityprofile.QualityProfileDto;
+import org.sonar.db.rule.RuleDefinitionDto;
 import org.sonar.db.rule.RuleDto;
 import org.sonar.db.rule.RuleParamDto;
 import org.sonar.server.qualityprofile.index.ActiveRuleIndexer;
@@ -51,14 +52,13 @@ import org.sonar.server.rule.index.RuleQuery;
 import org.sonar.server.tester.ServerTester;
 import org.sonar.server.tester.UserSessionRule;
 
-import static java.util.Arrays.asList;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.Assert.fail;
 import static org.sonar.db.qualityprofile.ActiveRuleDto.INHERITED;
 import static org.sonar.db.qualityprofile.ActiveRuleDto.OVERRIDES;
 import static org.sonar.db.rule.RuleTesting.XOO_X1;
 import static org.sonar.db.rule.RuleTesting.XOO_X2;
-import static org.sonar.db.rule.RuleTesting.newDto;
+import static org.sonar.db.rule.RuleTesting.newRule;
 import static org.sonar.db.rule.RuleTesting.newXooX1;
 import static org.sonar.db.rule.RuleTesting.newXooX2;
 import static org.sonar.server.qualityprofile.QProfileTesting.XOO_P1_KEY;
@@ -94,16 +94,19 @@ public class QProfileBackuperMediumTest {
 
     // create pre-defined rules
     RuleDto xooRule1 = newXooX1().setSeverity("MINOR").setLanguage("xoo");
-    RuleDto xooRule2 = newXooX2().setSeverity("MAJOR").setLanguage("xoo");
     db.ruleDao().insert(dbSession, xooRule1.getDefinition());
-    db.ruleDao().insert(dbSession, xooRule2.getDefinition());
     db.ruleDao().insertRuleParam(dbSession, xooRule1.getDefinition(), RuleParamDto.createFor(xooRule1.getDefinition())
       .setName("max").setDefaultValue("10").setType(RuleParamType.INTEGER.type()));
     dbSession.commit();
-    dbSession.clearCache();
+    ruleIndexer.indexRuleDefinition(xooRule1.getDefinition().getKey());
+
+    RuleDto xooRule2 = newXooX2().setSeverity("MAJOR").setLanguage("xoo");
+    db.ruleDao().insert(dbSession, xooRule2.getDefinition());
+    dbSession.commit();
+    ruleIndexer.indexRuleDefinition(xooRule2.getDefinition().getKey());
+
     this.organization = OrganizationTesting.newOrganizationDto();
     db.organizationDao().insert(dbSession, organization);
-    ruleIndexer.index(organization, asList(xooRule1.getKey(), xooRule2.getKey()));
   }
 
   @After
@@ -114,11 +117,10 @@ public class QProfileBackuperMediumTest {
   @Test
   public void backup() throws Exception {
     RuleKey blahRuleKey = RuleKey.of("blah", "my-rule");
-    RuleDto blahRule = newDto(blahRuleKey).setSeverity("INFO").setLanguage("xoo");
-    db.ruleDao().insert(dbSession, blahRule.getDefinition());
+    RuleDefinitionDto blahRule = newRule(blahRuleKey).setSeverity("INFO").setLanguage("xoo");
+    db.ruleDao().insert(dbSession, blahRule);
     dbSession.commit();
-    dbSession.clearCache();
-    ruleIndexer.index(organization, blahRuleKey);
+    ruleIndexer.indexRuleDefinition(blahRule.getKey());
 
     // create profile P1 with rules x2 and x1 activated
     QualityProfileDto profile = newXooP1(organization);
index 088d13542e67382458bc1603b5b0050c6eaeb88b..b98f7c91e101a43d22c63ce06f2130df78148323 100644 (file)
@@ -36,6 +36,7 @@ import org.sonar.db.organization.OrganizationDto;
 import org.sonar.db.qualityprofile.ActiveRuleDto;
 import org.sonar.db.qualityprofile.ActiveRuleParamDto;
 import org.sonar.db.qualityprofile.QualityProfileDto;
+import org.sonar.db.rule.RuleDefinitionDto;
 import org.sonar.db.rule.RuleDto;
 import org.sonar.db.rule.RuleParamDto;
 import org.sonar.db.rule.RuleTesting;
@@ -46,7 +47,6 @@ import org.sonar.server.rule.index.RuleQuery;
 import org.sonar.server.tester.ServerTester;
 import org.sonar.server.tester.UserSessionRule;
 
-import static java.util.Arrays.asList;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.Assert.fail;
 import static org.sonar.server.qualityprofile.QProfileTesting.getDefaultOrganization;
@@ -80,17 +80,23 @@ public class QProfileCopierMediumTest {
 
     // create pre-defined rules
     RuleDto xooRule1 = RuleTesting.newXooX1().setSeverity("MINOR");
-    RuleDto xooRule2 = RuleTesting.newXooX2().setSeverity("MAJOR");
-    db.ruleDao().insert(dbSession, xooRule1.getDefinition());
-    db.ruleDao().insert(dbSession, xooRule2.getDefinition());
-    db.ruleDao().insertRuleParam(dbSession, xooRule1.getDefinition(), RuleParamDto.createFor(xooRule1.getDefinition())
+    RuleDefinitionDto xooRule1Definition = xooRule1.getDefinition();
+    db.ruleDao().insert(dbSession, xooRule1Definition);
+    db.ruleDao().insertRuleParam(dbSession, xooRule1Definition, RuleParamDto.createFor(xooRule1Definition)
       .setName("max").setDefaultValue("10").setType(RuleParamType.INTEGER.type()));
+    dbSession.commit();
+    ruleIndexer.indexRuleDefinition(xooRule1Definition.getKey());
+
+    RuleDto xooRule2 = RuleTesting.newXooX2().setSeverity("MAJOR");
+    RuleDefinitionDto xooRule2Definition = xooRule2.getDefinition();
+    db.ruleDao().insert(dbSession, xooRule2Definition);
+    dbSession.commit();
+    ruleIndexer.indexRuleDefinition(xooRule2Definition.getKey());
 
     // create pre-defined profile
     sourceProfile = QProfileTesting.newXooP1(organization);
     db.qualityProfileDao().insert(dbSession, sourceProfile);
     dbSession.commit();
-    ruleIndexer.index(organization, asList(xooRule1.getKey(), xooRule2.getKey()));
   }
 
   @After
index 173105e141617b63ee0c6148049400cf3ccf5ce4..b87664225062cf0f90e131c188770750df12269b 100644 (file)
@@ -146,7 +146,7 @@ public class RuleActivatorMediumTest {
 
     // index all rules
     dbSession.commit();
-    ruleIndexer.index(organization, asList(javaRule, xooRule1, xooRule2, xooTemplateRule1, xooCustomRule1).stream().map(RuleDto::getKey).collect(Collectors.toList()));
+    ruleIndexer.indexRuleDefinitions(asList(javaRule, xooRule1, xooRule2, xooTemplateRule1, xooCustomRule1).stream().map(RuleDto::getKey).collect(Collectors.toList()));
   }
 
   @After
@@ -914,7 +914,7 @@ public class RuleActivatorMediumTest {
       keys.add(ruleDefinitionDto.getKey());
     }
     dbSession.commit();
-    ruleIndexer.index(organization, keys);
+    ruleIndexer.indexRuleDefinitions(keys);
 
     // 0. No active rules so far (base case) and plenty rules available
     verifyZeroActiveRules(XOO_P1_KEY);
index 437810a98b631ef9bf7a856f6fde02b18dd5da7b..de1ce09c93bf76cb7f52151e22a15425adbe40e5 100644 (file)
@@ -142,7 +142,7 @@ public class ActiveRuleIndexerTest {
     long now = 2000000L;
 
     // Index one active rule
-    RuleDefinitionDto rule = RuleTesting.newDto(RULE_KEY_1).getDefinition();
+    RuleDefinitionDto rule = RuleTesting.newRule(RULE_KEY_1);
     dbTester.rules().insert(rule);
     QualityProfileDto profile = QualityProfileDto.createFor("qp")
       .setOrganizationUuid(organization.getUuid())
@@ -159,7 +159,7 @@ public class ActiveRuleIndexerTest {
     assertThat(esTester.getIds(INDEX_TYPE_ACTIVE_RULE)).containsOnly(activeRule.getKey().toString());
 
     // Index another active rule
-    RuleDefinitionDto rule2 = RuleTesting.newDto(RULE_KEY_2).getDefinition();
+    RuleDefinitionDto rule2 = RuleTesting.newRule(RULE_KEY_2);
     dbTester.rules().insert(rule2);
     ActiveRuleDto activeRule2 = ActiveRuleDto.createFor(profile, rule2).setSeverity(Severity.CRITICAL)
       .setCreatedAt(now).setUpdatedAt(now);
index bf47fd48f424029ae855e88549506e0627827c21..d38d9fa9818eb358d93f9ac8a28c48817f0f8917 100644 (file)
@@ -42,7 +42,7 @@ import org.sonar.db.organization.OrganizationDto;
 import org.sonar.db.qualityprofile.ActiveRuleDto;
 import org.sonar.db.qualityprofile.QualityProfileDto;
 import org.sonar.db.qualityprofile.QualityProfileTesting;
-import org.sonar.db.rule.RuleDto;
+import org.sonar.db.rule.RuleDefinitionDto;
 import org.sonar.db.rule.RuleTesting;
 import org.sonar.server.es.EsClient;
 import org.sonar.server.es.EsTester;
@@ -56,7 +56,6 @@ import org.sonar.server.qualityprofile.index.ActiveRuleIndexer;
 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.rule.index.RuleIteratorFactory;
 import org.sonar.server.rule.index.RuleQuery;
 import org.sonar.server.tester.UserSessionRule;
 import org.sonar.server.util.TypeValidations;
@@ -105,10 +104,7 @@ public class ChangeParentActionTest {
     TestDefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(dbTester);
     ruleIndexer = new RuleIndexer(
       esClient,
-      null,
-      new RuleIteratorFactory(dbClient),
-      null
-    );
+      dbClient);
     activeRuleIndexer = new ActiveRuleIndexer(
       System2.INSTANCE,
       dbClient,
@@ -160,9 +156,9 @@ public class ChangeParentActionTest {
     QualityProfileDto parent1 = createProfile();
     QualityProfileDto child = createProfile();
 
-    RuleDto rule1 = createRule();
+    RuleDefinitionDto rule1 = createRule();
     createActiveRule(rule1, parent1);
-    ruleIndexer.index(organization, rule1.getKey());
+    ruleIndexer.indexRuleDefinition(rule1.getKey());
     activeRuleIndexer.index();
 
     assertThat(dbClient.activeRuleDao().selectByProfileKey(dbSession, child.getKey())).isEmpty();
@@ -188,11 +184,11 @@ public class ChangeParentActionTest {
     QualityProfileDto parent2 = createProfile();
     QualityProfileDto child = createProfile();
 
-    RuleDto rule1 = createRule();
-    RuleDto rule2 = createRule();
+    RuleDefinitionDto rule1 = createRule();
+    RuleDefinitionDto rule2 = createRule();
     createActiveRule(rule1, parent1);
     createActiveRule(rule2, parent2);
-    ruleIndexer.index(organization, Stream.of(rule1, rule2).map(RuleDto::getKey).collect(MoreCollectors.toList()));
+    ruleIndexer.indexRuleDefinitions(Stream.of(rule1, rule2).map(RuleDefinitionDto::getKey).collect(MoreCollectors.toList()));
     activeRuleIndexer.index();
 
     // Set parent 1
@@ -218,9 +214,9 @@ public class ChangeParentActionTest {
     QualityProfileDto parent = createProfile();
     QualityProfileDto child = createProfile();
 
-    RuleDto rule1 = createRule();
+    RuleDefinitionDto rule1 = createRule();
     createActiveRule(rule1, parent);
-    ruleIndexer.index(organization, rule1.getKey());
+    ruleIndexer.indexRuleDefinition(rule1.getKey());
     activeRuleIndexer.index();
 
     // Set parent
@@ -245,11 +241,11 @@ public class ChangeParentActionTest {
     QualityProfileDto parent2 = createProfile();
     QualityProfileDto child = createProfile();
 
-    RuleDto rule1 = createRule();
-    RuleDto rule2 = createRule();
+    RuleDefinitionDto rule1 = createRule();
+    RuleDefinitionDto rule2 = createRule();
     createActiveRule(rule1, parent1);
     createActiveRule(rule2, parent2);
-    ruleIndexer.index(organization, rule1.getKey());
+    ruleIndexer.indexRuleDefinition(rule1.getKey());
     activeRuleIndexer.index();
 
     assertThat(dbClient.activeRuleDao().selectByProfileKey(dbSession, child.getKey())).isEmpty();
@@ -306,9 +302,9 @@ public class ChangeParentActionTest {
     QualityProfileDto parent = createProfile();
     QualityProfileDto child = createProfile();
 
-    RuleDto rule1 = createRule();
+    RuleDefinitionDto rule1 = createRule();
     createActiveRule(rule1, parent);
-    ruleIndexer.index(organization, rule1.getKey());
+    ruleIndexer.indexRuleDefinition(rule1.getKey());
     activeRuleIndexer.index();
 
     assertThat(dbClient.activeRuleDao().selectByProfileKey(dbSession, child.getKey())).isEmpty();
@@ -404,18 +400,18 @@ public class ChangeParentActionTest {
     return profile;
   }
 
-  private RuleDto createRule() {
-    RuleDto rule = RuleTesting.newDto(RuleKey.of(ruleRepository, randomAlphanumeric(5)))
+  private RuleDefinitionDto createRule() {
+    RuleDefinitionDto rule = RuleTesting.newRule(RuleKey.of(ruleRepository, randomAlphanumeric(5)))
       .setLanguage(language.getKey())
       .setSeverity(Severity.BLOCKER)
       .setStatus(RuleStatus.READY);
-    dbClient.ruleDao().insert(dbSession, rule.getDefinition());
+    dbClient.ruleDao().insert(dbSession, rule);
     dbSession.commit();
     return rule;
   }
 
-  private ActiveRuleDto createActiveRule(RuleDto rule, QualityProfileDto profile) {
-    ActiveRuleDto activeRule = ActiveRuleDto.createFor(profile, rule.getDefinition())
+  private ActiveRuleDto createActiveRule(RuleDefinitionDto rule, QualityProfileDto profile) {
+    ActiveRuleDto activeRule = ActiveRuleDto.createFor(profile, rule)
       .setSeverity(rule.getSeverityString());
     dbClient.activeRuleDao().insert(dbSession, activeRule);
     dbSession.commit();
index 12461b5e5ffe8875c628d3800b4c2025751341b6..5bed5e402456e97c532d32fd7cf805e00d2518b0 100644 (file)
@@ -31,7 +31,7 @@ import org.sonar.db.DbTester;
 import org.sonar.db.qualityprofile.QProfileChangeDto;
 import org.sonar.db.qualityprofile.QProfileChangeQuery;
 import org.sonar.db.qualityprofile.QualityProfileTesting;
-import org.sonar.db.rule.RuleDto;
+import org.sonar.db.rule.RuleDefinitionDto;
 import org.sonar.db.rule.RuleTesting;
 import org.sonar.db.user.UserDto;
 import org.sonar.db.user.UserTesting;
@@ -159,8 +159,8 @@ public class ChangelogLoaderTest {
   }
 
   private void insertRule(RuleKey key, String name) {
-    RuleDto dto = RuleTesting.newDto(key).setName(name);
-    dbTester.rules().insertRule(dto);
+    RuleDefinitionDto dto = RuleTesting.newRule(key).setName(name);
+    dbTester.rules().insert(dto);
     dbTester.getSession().commit();
   }
 
index f8af26088bd09c1082533a4964ed2ce8c146ae02..d898c28ea53bafd2e4e10309b28c87677b2e5b45 100644 (file)
@@ -55,7 +55,6 @@ import org.sonar.server.qualityprofile.index.ActiveRuleIndexer;
 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.rule.index.RuleIteratorFactory;
 import org.sonar.server.rule.index.RuleQuery;
 import org.sonar.server.tester.UserSessionRule;
 import org.sonar.server.ws.TestRequest;
@@ -94,7 +93,7 @@ public class CreateActionTest {
   private DbSession dbSession = dbTester.getSession();
   private RuleIndex ruleIndex = new RuleIndex(esTester.client());
   private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(dbTester);
-  private RuleIndexer ruleIndexer = new RuleIndexer(esTester.client(), null, new RuleIteratorFactory(dbClient), null);
+  private RuleIndexer ruleIndexer = new RuleIndexer(esTester.client(), dbClient);
   private ActiveRuleIndexer activeRuleIndexer = new ActiveRuleIndexer(system2, dbClient, esTester.client());
   private ProfileImporter[] profileImporters = createImporters();
   private QProfileExporters qProfileExporters = new QProfileExporters(dbClient, null,
@@ -243,7 +242,7 @@ public class CreateActionTest {
   private void insertRule(RuleDefinitionDto ruleDto) {
     dbClient.ruleDao().insert(dbSession, ruleDto);
     dbSession.commit();
-    ruleIndexer.index(organization, ruleDto.getKey());
+    ruleIndexer.indexRuleDefinition(ruleDto.getKey());
   }
 
   private CreateWsResponse executeRequest(String name, String language) {
index e97f2e6c1f33257f6713592399974246e57abbf0..4fdcc4df9de61127888a3a325a1136575d1ef90b 100644 (file)
@@ -37,7 +37,6 @@ import org.sonar.db.organization.OrganizationDto;
 import org.sonar.db.qualityprofile.ActiveRuleDto;
 import org.sonar.db.qualityprofile.QualityProfileDto;
 import org.sonar.db.rule.RuleDefinitionDto;
-import org.sonar.db.rule.RuleDto;
 import org.sonar.db.rule.RuleTesting;
 import org.sonar.server.es.EsClient;
 import org.sonar.server.es.EsTester;
@@ -53,14 +52,11 @@ import org.sonar.server.qualityprofile.index.ActiveRuleIndexer;
 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.rule.index.RuleIteratorFactory;
 import org.sonar.server.tester.UserSessionRule;
 import org.sonar.server.util.TypeValidations;
 import org.sonar.server.ws.WsActionTester;
 import org.sonar.test.JsonAssert;
 
-import static java.util.Arrays.asList;
-
 public class InheritanceActionTest {
 
   @Rule
@@ -85,7 +81,7 @@ public class InheritanceActionTest {
     dbClient = dbTester.getDbClient();
     dbSession = dbTester.getSession();
     esClient = esTester.client();
-    ruleIndexer = new RuleIndexer(esClient, null, new RuleIteratorFactory(dbClient), null);
+    ruleIndexer = new RuleIndexer(esClient, dbClient);
     activeRuleIndexer = new ActiveRuleIndexer(System2.INSTANCE, dbClient, esClient);
     TestDefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(dbTester);
     underTest = new InheritanceAction(
@@ -119,7 +115,6 @@ public class InheritanceActionTest {
     createActiveRule(rule2, groupWide);
 
     dbSession.commit();
-    ruleIndexer.index(organization, asList(rule1.getKey(), rule2.getKey(), rule3.getKey()));
     activeRuleIndexer.index();
 
     QualityProfileDto companyWide = createProfile("xoo", "My Company Profile", "xoo-my-company-profile-12345");
@@ -181,14 +176,16 @@ public class InheritanceActionTest {
 
   private RuleDefinitionDto createRule(String lang, String id) {
     long now = new Date().getTime();
-    RuleDto rule = RuleTesting.newDto(RuleKey.of("blah", id))
+    RuleDefinitionDto rule = RuleTesting.newRule(RuleKey.of("blah", id))
       .setLanguage(lang)
       .setSeverity(Severity.BLOCKER)
       .setStatus(RuleStatus.READY)
       .setUpdatedAt(now)
       .setCreatedAt(now);
-    dbClient.ruleDao().insert(dbSession, rule.getDefinition());
-    return rule.getDefinition();
+    dbClient.ruleDao().insert(dbSession, rule);
+    dbSession.commit();
+    ruleIndexer.indexRuleDefinition(rule.getKey());
+    return rule;
   }
 
   private ActiveRuleDto createActiveRule(RuleDefinitionDto rule, QualityProfileDto profile) {
index 55e1f31824ef11e661784a281095d7f07c04218f..d8cb227d84a9b5115747d77cdb5b27f8be7ca6ae 100644 (file)
@@ -39,7 +39,6 @@ import org.sonar.db.qualityprofile.ActiveRuleDto;
 import org.sonar.db.qualityprofile.ActiveRuleKey;
 import org.sonar.db.qualityprofile.QualityProfileDto;
 import org.sonar.db.rule.RuleDefinitionDto;
-import org.sonar.db.rule.RuleDto;
 import org.sonar.db.rule.RuleTesting;
 import org.sonar.server.es.SearchOptions;
 import org.sonar.server.exceptions.BadRequestException;
@@ -103,7 +102,7 @@ public class QProfilesWsMediumTest {
     RuleDefinitionDto rule = createRule(profile.getLanguage(), "toto");
     createActiveRule(rule, profile);
     session.commit();
-    ruleIndexer.index(organization, rule.getKey());
+    ruleIndexer.indexRuleDefinition(rule.getKey());
     activeRuIndexer.index();
 
     // 0. Assert No Active Rule for profile
@@ -203,7 +202,7 @@ public class QProfilesWsMediumTest {
     QualityProfileDto profile = createProfile("java");
     RuleDefinitionDto rule = createRule(profile.getLanguage(), "toto");
     session.commit();
-    ruleIndexer.index(organization, rule.getKey());
+    ruleIndexer.indexRuleDefinition(rule.getKey());
 
     // 0. Assert No Active Rule for profile
     assertThat(db.activeRuleDao().selectByProfileKey(session, profile.getKey())).isEmpty();
@@ -224,7 +223,7 @@ public class QProfilesWsMediumTest {
     QualityProfileDto profile = createProfile("java");
     RuleDefinitionDto rule = createRule("php", "toto");
     session.commit();
-    ruleIndexer.index(organization, rule.getKey());
+    ruleIndexer.indexRuleDefinition(rule.getKey());
 
     // 0. Assert No Active Rule for profile
     assertThat(db.activeRuleDao().selectByProfileKey(session, profile.getKey())).isEmpty();
@@ -247,7 +246,7 @@ public class QProfilesWsMediumTest {
     QualityProfileDto profile = createProfile("java");
     RuleDefinitionDto rule = createRule(profile.getLanguage(), "toto");
     session.commit();
-    ruleIndexer.index(organization, rule.getKey());
+    ruleIndexer.indexRuleDefinition(rule.getKey());
 
     // 0. Assert No Active Rule for profile
     assertThat(db.activeRuleDao().selectByProfileKey(session, profile.getKey())).isEmpty();
@@ -439,14 +438,14 @@ public class QProfilesWsMediumTest {
   }
 
   private RuleDefinitionDto createRule(String lang, String id) {
-    RuleDto rule = RuleTesting.newDto(RuleKey.of("blah", id))
+    RuleDefinitionDto rule = RuleTesting.newRule(RuleKey.of("blah", id))
       .setLanguage(lang)
       .setSeverity(Severity.BLOCKER)
       .setStatus(RuleStatus.READY);
-    db.ruleDao().insert(session, rule.getDefinition());
+    db.ruleDao().insert(session, rule);
     session.commit();
-    ruleIndexer.index(organization, rule.getKey());
-    return rule.getDefinition();
+    ruleIndexer.indexRuleDefinition(rule.getKey());
+    return rule;
   }
 
   private ActiveRuleDto createActiveRule(RuleDefinitionDto rule, QualityProfileDto profile) {
index 5264385530c0b8e1b958154f251ce3b9135a6f22..7b72f88a30f238efd079ea1b7af67e20ea0132e8 100644 (file)
@@ -42,13 +42,11 @@ import org.sonar.db.rule.RuleParamDto;
 import org.sonar.db.rule.RuleRepositoryDto;
 import org.sonar.server.es.EsTester;
 import org.sonar.server.es.SearchOptions;
-import org.sonar.server.organization.TestDefaultOrganizationProvider;
 import org.sonar.server.qualityprofile.RuleActivator;
 import org.sonar.server.qualityprofile.index.ActiveRuleIndexer;
 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.rule.index.RuleIteratorFactory;
 import org.sonar.server.rule.index.RuleQuery;
 
 import static com.google.common.collect.Sets.newHashSet;
@@ -86,7 +84,7 @@ public class RegisterRulesTest {
   @Before
   public void before() {
     when(system.now()).thenReturn(DATE1.getTime());
-    ruleIndexer = new RuleIndexer(esTester.client(), null, new RuleIteratorFactory(dbClient), null);
+    ruleIndexer = new RuleIndexer(esTester.client(), dbClient);
     ruleIndex = new RuleIndex(esTester.client());
     activeRuleIndexer = new ActiveRuleIndexer(system, dbClient, esTester.client());
   }
@@ -124,6 +122,7 @@ public class RegisterRulesTest {
     // verify repositories
     assertThat(dbClient.ruleRepositoryDao().selectAll(dbTester.getSession())).extracting(RuleRepositoryDto::getKey).containsOnly("fake");
   }
+
   @Test
   public void insert_then_remove_rule() {
     String ruleKey = randomAlphanumeric(5);
@@ -457,7 +456,7 @@ public class RegisterRulesTest {
     Languages languages = mock(Languages.class);
     when(languages.get("java")).thenReturn(mock(Language.class));
 
-    RegisterRules task = new RegisterRules(loader, ruleActivator, dbClient, ruleIndexer, activeRuleIndexer, languages, system, TestDefaultOrganizationProvider.from(dbTester));
+    RegisterRules task = new RegisterRules(loader, ruleActivator, dbClient, ruleIndexer, activeRuleIndexer, languages, system);
     task.start();
     // Execute a commit to refresh session state as the task is using its own session
     dbTester.getSession().commit();
index c61670e5dbe8872efc76f67ab28459e442780eb8..79664debc038a241ba5599bbaa4115fa6edc4fda 100644 (file)
@@ -52,6 +52,7 @@ import org.sonar.server.tester.UserSessionRule;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.Assert.fail;
+import static org.sonar.db.rule.RuleTesting.newRule;
 
 // TODO replace ServerTester by EsTester / DbTester
 public class RuleCreatorMediumTest {
@@ -160,7 +161,7 @@ public class RuleCreatorMediumTest {
   @Test
   public void create_custom_rule_with_no_parameter_value() {
     // insert template rule
-    RuleDto templateRule = createTemplateRuleWithIntArrayParam();
+    RuleDefinitionDto templateRule = createTemplateRuleWithIntArrayParam();
 
     NewCustomRule newRule = NewCustomRule.createForCustomRule("CUSTOM_RULE", templateRule.getKey())
       .setName("My custom")
@@ -184,7 +185,7 @@ public class RuleCreatorMediumTest {
   @Test
   public void create_custom_rule_with_multiple_parameter_values() {
     // insert template rule
-    RuleDto templateRule = createTemplateRuleWithIntArrayParam();
+    RuleDefinitionDto templateRule = createTemplateRuleWithIntArrayParam();
 
     NewCustomRule newRule = NewCustomRule.createForCustomRule("CUSTOM_RULE", templateRule.getKey())
       .setName("My custom")
@@ -209,7 +210,7 @@ public class RuleCreatorMediumTest {
   @Test
   public void create_custom_rule_with_invalid_parameter() {
     // insert template rule
-    RuleDto templateRule = createTemplateRuleWithIntArrayParam();
+    RuleDefinitionDto templateRule = createTemplateRuleWithIntArrayParam();
 
     // Create custom rule
     NewCustomRule newRule = NewCustomRule.createForCustomRule("CUSTOM_RULE", templateRule.getKey())
@@ -231,7 +232,7 @@ public class RuleCreatorMediumTest {
   @Test
   public void create_custom_rule_with_invalid_parameters() {
     // insert template rule
-    RuleDto templateRule = createTemplateRuleWithTwoIntParams();
+    RuleDefinitionDto templateRule = createTemplateRuleWithTwoIntParams();
 
     // Create custom rule
     NewCustomRule newRule = NewCustomRule.createForCustomRule("CUSTOM_RULE", templateRule.getKey())
@@ -481,8 +482,8 @@ public class RuleCreatorMediumTest {
   @Test
   public void fail_to_create_custom_rule_when_wrong_rule_template() {
     // insert rule
-    RuleDto rule = RuleTesting.newDto(RuleKey.of("java", "S001")).setIsTemplate(false);
-    dao.insert(dbSession, rule.getDefinition());
+    RuleDefinitionDto rule = newRule(RuleKey.of("java", "S001")).setIsTemplate(false);
+    dao.insert(dbSession, rule);
     dbSession.commit();
 
     // Create custom rule with unknown template rule
@@ -519,12 +520,12 @@ public class RuleCreatorMediumTest {
     RuleParamDto ruleParamDto = RuleParamDto.createFor(templateRule.getDefinition()).setName("regex").setType("STRING").setDescription("Reg ex").setDefaultValue(".*");
     dao.insertRuleParam(dbSession, templateRule.getDefinition(), ruleParamDto);
     dbSession.commit();
-    ruleIndexer.index(defaultOrganization, templateRule.getKey());
+    ruleIndexer.indexRuleDefinition(templateRule.getDefinition().getKey());
     return templateRule;
   }
 
-  private RuleDto createTemplateRuleWithIntArrayParam() {
-    RuleDto templateRule = RuleTesting.newDto(RuleKey.of("java", "S002"))
+  private RuleDefinitionDto createTemplateRuleWithIntArrayParam() {
+    RuleDefinitionDto templateRule = newRule(RuleKey.of("java", "S002"))
       .setIsTemplate(true)
       .setLanguage("java")
       .setConfigKey("S002")
@@ -534,17 +535,17 @@ public class RuleCreatorMediumTest {
       .setGapDescription("desc")
       .setCreatedAt(new Date().getTime())
       .setUpdatedAt(new Date().getTime());
-    dao.insert(dbSession, templateRule.getDefinition());
-    RuleParamDto ruleParamDto = RuleParamDto.createFor(templateRule.getDefinition())
+    dao.insert(dbSession, templateRule);
+    RuleParamDto ruleParamDto = RuleParamDto.createFor(templateRule)
       .setName("myIntegers").setType("INTEGER,multiple=true,values=1;2;3").setDescription("My Integers").setDefaultValue("1");
-    dao.insertRuleParam(dbSession, templateRule.getDefinition(), ruleParamDto);
+    dao.insertRuleParam(dbSession, templateRule, ruleParamDto);
     dbSession.commit();
-    ruleIndexer.index(defaultOrganization, templateRule.getKey());
+    ruleIndexer.indexRuleDefinition(templateRule.getKey());
     return templateRule;
   }
 
-  private RuleDto createTemplateRuleWithTwoIntParams() {
-    RuleDto templateRule = RuleTesting.newDto(RuleKey.of("java", "S003"))
+  private RuleDefinitionDto createTemplateRuleWithTwoIntParams() {
+    RuleDefinitionDto templateRule = newRule(RuleKey.of("java", "S003"))
       .setIsTemplate(true)
       .setLanguage("java")
       .setConfigKey("S003")
@@ -554,13 +555,13 @@ public class RuleCreatorMediumTest {
       .setGapDescription("desc")
       .setCreatedAt(new Date().getTime())
       .setUpdatedAt(new Date().getTime());
-    dao.insert(dbSession, templateRule.getDefinition());
-    RuleParamDto ruleParam1Dto = RuleParamDto.createFor(templateRule.getDefinition())
+    dao.insert(dbSession, templateRule);
+    RuleParamDto ruleParam1Dto = RuleParamDto.createFor(templateRule)
       .setName("first").setType("INTEGER").setDescription("First integer").setDefaultValue("0");
-    dao.insertRuleParam(dbSession, templateRule.getDefinition(), ruleParam1Dto);
-    RuleParamDto ruleParam2Dto = RuleParamDto.createFor(templateRule.getDefinition())
+    dao.insertRuleParam(dbSession, templateRule, ruleParam1Dto);
+    RuleParamDto ruleParam2Dto = RuleParamDto.createFor(templateRule)
       .setName("second").setType("INTEGER").setDescription("Second integer").setDefaultValue("0");
-    dao.insertRuleParam(dbSession, templateRule.getDefinition(), ruleParam2Dto);
+    dao.insertRuleParam(dbSession, templateRule, ruleParam2Dto);
     dbSession.commit();
     return templateRule;
   }
index 8f55db0fc762d70c410caf9562368db020c2e3e0..d3cec3fa5d286cc902d7e37c3b59a3ce01b9532f 100644 (file)
@@ -88,7 +88,7 @@ public class RuleDeleterMediumTest {
       .setUpdatedAt(PAST);
     dao.insert(dbSession, templateRule.getDefinition());
     dbSession.commit();
-    ruleIndexer.index(organization, templateRule.getKey());
+    ruleIndexer.indexRuleDefinition(templateRule.getDefinition().getKey());
 
     // Verify in index
     assertThat(index.searchAll(new RuleQuery())).containsOnly(templateRule.getKey());
@@ -100,7 +100,7 @@ public class RuleDeleterMediumTest {
       .setUpdatedAt(PAST);
     dao.insert(dbSession, customRule.getDefinition());
     dbSession.commit();
-    ruleIndexer.index(organization, customRule.getKey());
+    ruleIndexer.indexRuleDefinition(customRule.getDefinition().getKey());
 
     // Verify in index
     assertThat(index.searchAll(new RuleQuery())).containsOnly(templateRule.getKey(), customRule.getKey());
@@ -134,7 +134,7 @@ public class RuleDeleterMediumTest {
   public void fail_to_delete_if_not_custom() {
     // Create rule
     RuleKey ruleKey = RuleKey.of("java", "S001");
-    dao.insert(dbSession, RuleTesting.newDto(ruleKey).getDefinition());
+    dao.insert(dbSession, RuleTesting.newRule(ruleKey));
     dbSession.commit();
 
     try {
diff --git a/server/sonar-server/src/test/java/org/sonar/server/rule/RuleServiceMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/rule/RuleServiceMediumTest.java
deleted file mode 100644 (file)
index e69de29..0000000
index 768d18935387c8d4724a07b0c5c6b49939429ac0..54fd2cb50f48e72d2833b79846ec7cdad8b79084 100644 (file)
@@ -31,6 +31,7 @@ import javax.annotation.Nonnull;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.ClassRule;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rule.RuleStatus;
@@ -55,7 +56,6 @@ import org.sonar.server.qualityprofile.QProfileTesting;
 import org.sonar.server.qualityprofile.RuleActivation;
 import org.sonar.server.qualityprofile.RuleActivator;
 import org.sonar.server.rule.index.RuleIndex;
-import org.sonar.server.rule.index.RuleIndexDefinition;
 import org.sonar.server.rule.index.RuleQuery;
 import org.sonar.server.tester.ServerTester;
 import org.sonar.server.tester.UserSessionRule;
@@ -96,7 +96,7 @@ public class RuleUpdaterMediumTest {
 
   @Test
   public void do_not_update_rule_with_removed_status() {
-    ruleDao.insert(dbSession, RuleTesting.newDto(RULE_KEY).setStatus(RuleStatus.REMOVED).getDefinition());
+    ruleDao.insert(dbSession, RuleTesting.newRule(RULE_KEY).setStatus(RuleStatus.REMOVED));
     dbSession.commit();
 
     RuleUpdate update = RuleUpdate.createForPluginRule(RULE_KEY).setTags(Sets.newHashSet("java9"));
@@ -190,6 +190,7 @@ public class RuleUpdaterMediumTest {
     assertThat(rule.getNoteUpdatedAt()).isNull();
   }
 
+  @Ignore
   @Test
   public void set_tags() {
     // insert db
@@ -202,13 +203,12 @@ public class RuleUpdaterMediumTest {
     RuleUpdate update = RuleUpdate.createForPluginRule(RULE_KEY).setTags(Sets.newHashSet("bug", "java8"));
     underTest.update(update, userSessionRule);
 
-    dbSession.clearCache();
     RuleDto rule = ruleDao.selectOrFailByKey(dbSession, defaultOrganization.getUuid(), RULE_KEY);
     assertThat(rule.getTags()).containsOnly("bug");
     assertThat(rule.getSystemTags()).containsOnly("java8", "javadoc");
 
     // verify that tags are indexed in index
-    Set<String> tags = tester.get(RuleIndex.class).terms(RuleIndexDefinition.FIELD_RULE_ALL_TAGS);
+    Set<String> tags = ruleIndex.listTags(defaultOrganization.getUuid(), null, 10);
     assertThat(tags).containsOnly("bug", "java8", "javadoc");
   }
 
@@ -230,17 +230,16 @@ public class RuleUpdaterMediumTest {
     assertThat(rule.getSystemTags()).containsOnly("java8", "javadoc");
 
     // verify that tags are indexed in index
-    Set<String> tags = tester.get(RuleIndex.class).terms(RuleIndexDefinition.FIELD_RULE_ALL_TAGS);
+    Set<String> tags = ruleIndex.listTags(defaultOrganization.getUuid(), null, 10);
     assertThat(tags).containsOnly("java8", "javadoc");
   }
 
   @Test
   public void override_debt() {
-    ruleDao.insert(dbSession, RuleTesting.newDto(RULE_KEY)
+    ruleDao.insert(dbSession, RuleTesting.newRule(RULE_KEY)
       .setDefRemediationFunction(DebtRemediationFunction.Type.LINEAR_OFFSET.name())
       .setDefRemediationGapMultiplier("1d")
-      .setDefRemediationBaseEffort("5min")
-      .getDefinition());
+      .setDefRemediationBaseEffort("5min"));
     dbSession.commit();
 
     DefaultDebtRemediationFunction fn = new DefaultDebtRemediationFunction(DebtRemediationFunction.Type.CONSTANT_ISSUE, null, "1min");
@@ -262,11 +261,10 @@ public class RuleUpdaterMediumTest {
 
   @Test
   public void override_debt_only_offset() {
-    ruleDao.insert(dbSession, RuleTesting.newDto(RULE_KEY)
+    ruleDao.insert(dbSession, RuleTesting.newRule(RULE_KEY)
       .setDefRemediationFunction(DebtRemediationFunction.Type.LINEAR.name())
       .setDefRemediationGapMultiplier("1d")
-      .setDefRemediationBaseEffort(null)
-      .getDefinition());
+      .setDefRemediationBaseEffort(null));
     dbSession.commit();
 
     RuleUpdate update = RuleUpdate.createForPluginRule(RULE_KEY)
@@ -287,11 +285,10 @@ public class RuleUpdaterMediumTest {
 
   @Test
   public void override_debt_from_linear_with_offset_to_constant() {
-    ruleDao.insert(dbSession, RuleTesting.newDto(RULE_KEY)
+    ruleDao.insert(dbSession, RuleTesting.newRule(RULE_KEY)
       .setDefRemediationFunction(DebtRemediationFunction.Type.LINEAR_OFFSET.name())
       .setDefRemediationGapMultiplier("1d")
-      .setDefRemediationBaseEffort("5min")
-      .getDefinition());
+      .setDefRemediationBaseEffort("5min"));
     dbSession.commit();
 
     RuleUpdate update = RuleUpdate.createForPluginRule(RULE_KEY)
@@ -546,8 +543,8 @@ public class RuleUpdaterMediumTest {
   @Test
   public void fail_to_update_plugin_rule_if_name_is_set() {
     // Create rule rule
-    RuleDto ruleDto = RuleTesting.newDto(RuleKey.of("squid", "S01"));
-    ruleDao.insert(dbSession, ruleDto.getDefinition());
+    RuleDefinitionDto ruleDto = RuleTesting.newRule(RuleKey.of("squid", "S01"));
+    ruleDao.insert(dbSession, ruleDto);
 
     dbSession.commit();
 
@@ -564,8 +561,8 @@ public class RuleUpdaterMediumTest {
   @Test
   public void fail_to_update_plugin_rule_if_description_is_set() {
     // Create rule rule
-    RuleDto ruleDto = RuleTesting.newDto(RuleKey.of("squid", "S01"));
-    ruleDao.insert(dbSession, ruleDto.getDefinition());
+    RuleDefinitionDto ruleDto = RuleTesting.newRule(RuleKey.of("squid", "S01"));
+    ruleDao.insert(dbSession, ruleDto);
 
     dbSession.commit();
 
@@ -582,8 +579,8 @@ public class RuleUpdaterMediumTest {
   @Test
   public void fail_to_update_plugin_rule_if_severity_is_set() {
     // Create rule rule
-    RuleDto ruleDto = RuleTesting.newDto(RuleKey.of("squid", "S01"));
-    ruleDao.insert(dbSession, ruleDto.getDefinition());
+    RuleDefinitionDto ruleDto = RuleTesting.newRule(RuleKey.of("squid", "S01"));
+    ruleDao.insert(dbSession, ruleDto);
 
     dbSession.commit();
 
index af4c572ce8dbb8f97822d936a2af06bcf25a7702..1465c63451a2d8178ca71f09f864a8557cfa6b85 100644 (file)
@@ -20,7 +20,6 @@
 package org.sonar.server.rule.index;
 
 import com.google.common.collect.Maps;
-import java.util.Arrays;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rule.RuleStatus;
 import org.sonar.api.rule.Severity;
@@ -44,7 +43,6 @@ public class RuleDocTesting {
       .setStatus(RuleStatus.READY.name())
       .setLanguage("xoo")
       .setIsTemplate(false)
-      .setAllTags(Arrays.asList("spring", "performance"))
       .setType(RuleType.CODE_SMELL)
       .setCreatedAt(1_500_000_000L)
       .setUpdatedAt(1_600_000_000L);
index c0d900bbf73f7d78d0ea03bdd04678948d6ce31b..61321aed56e8786e83aee5e817235ca715d4bb34 100644 (file)
@@ -54,7 +54,7 @@ public class RuleIndexDefinitionTest {
     assertThat(context.getIndices()).hasSize(1);
     NewIndex ruleIndex = context.getIndices().get("rules");
     assertThat(ruleIndex).isNotNull();
-    assertThat(ruleIndex.getTypes().keySet()).containsOnly("rule", "activeRule");
+    assertThat(ruleIndex.getTypes().keySet()).containsOnly("activeRule", "ruleExtension", "rule");
 
     // no cluster by default
     assertThat(ruleIndex.getSettings().get("index.number_of_shards")).isEqualTo("1");
index 75f6deda369d4d36f4114e36c20d82e0f310bcab..e92e08a4e152093b106cd9b3cd631b8b96525f7e 100644 (file)
 package org.sonar.server.rule.index;
 
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import java.util.Collections;
 import java.util.List;
 import java.util.Map;
-import org.elasticsearch.search.SearchHit;
+import java.util.function.Consumer;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.ExpectedException;
 import org.sonar.api.config.MapSettings;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rule.RuleStatus;
 import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+import org.sonar.db.organization.OrganizationDto;
 import org.sonar.db.qualityprofile.ActiveRuleKey;
-import org.sonar.db.rule.RuleTesting;
+import org.sonar.db.rule.RuleDefinitionDto;
+import org.sonar.db.rule.RuleMetadataDto;
 import org.sonar.server.es.EsTester;
 import org.sonar.server.es.SearchIdResult;
 import org.sonar.server.es.SearchOptions;
@@ -41,9 +43,10 @@ import org.sonar.server.qualityprofile.index.ActiveRuleDoc;
 import org.sonar.server.qualityprofile.index.ActiveRuleDocTesting;
 import org.sonar.server.qualityprofile.index.ActiveRuleIndexer;
 
+import static com.google.common.collect.ImmutableSet.of;
 import static java.util.Arrays.asList;
+import static java.util.Collections.emptyList;
 import static java.util.Collections.emptySet;
-import static java.util.Collections.singleton;
 import static java.util.Collections.singletonList;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.data.MapEntry.entry;
@@ -56,22 +59,30 @@ import static org.sonar.api.rule.Severity.MINOR;
 import static org.sonar.api.rules.RuleType.BUG;
 import static org.sonar.api.rules.RuleType.CODE_SMELL;
 import static org.sonar.api.rules.RuleType.VULNERABILITY;
+import static org.sonar.db.rule.RuleTesting.setCreatedAt;
+import static org.sonar.db.rule.RuleTesting.setIsTemplate;
+import static org.sonar.db.rule.RuleTesting.setLanguage;
+import static org.sonar.db.rule.RuleTesting.setName;
+import static org.sonar.db.rule.RuleTesting.setOrganizationUuid;
+import static org.sonar.db.rule.RuleTesting.setRepositoryKey;
+import static org.sonar.db.rule.RuleTesting.setRuleKey;
+import static org.sonar.db.rule.RuleTesting.setSeverity;
+import static org.sonar.db.rule.RuleTesting.setStatus;
+import static org.sonar.db.rule.RuleTesting.setSystemTags;
+import static org.sonar.db.rule.RuleTesting.setTags;
+import static org.sonar.db.rule.RuleTesting.setTemplateId;
+import static org.sonar.db.rule.RuleTesting.setType;
+import static org.sonar.db.rule.RuleTesting.setUpdatedAt;
 import static org.sonar.server.qualityprofile.ActiveRule.Inheritance.INHERITED;
 import static org.sonar.server.qualityprofile.ActiveRule.Inheritance.OVERRIDES;
-import static org.sonar.server.rule.index.RuleDocTesting.newDoc;
 import static org.sonar.server.rule.index.RuleIndex.FACET_LANGUAGES;
 import static org.sonar.server.rule.index.RuleIndex.FACET_REPOSITORIES;
 import static org.sonar.server.rule.index.RuleIndex.FACET_TAGS;
 import static org.sonar.server.rule.index.RuleIndex.FACET_TYPES;
 import static org.sonar.server.rule.index.RuleIndexDefinition.INDEX_TYPE_ACTIVE_RULE;
-import static org.sonar.server.rule.index.RuleIndexDefinition.INDEX_TYPE_RULE;
 
 public class RuleIndexTest {
 
-  private static final RuleKey RULE_KEY_1 = RuleTesting.XOO_X1;
-  private static final RuleKey RULE_KEY_2 = RuleTesting.XOO_X2;
-  private static final RuleKey RULE_KEY_3 = RuleTesting.XOO_X3;
-  private static final RuleKey RULE_KEY_4 = RuleKey.of("xoo", "x4");
   private static final String QUALITY_PROFILE_KEY1 = "qp1";
   private static final String QUALITY_PROFILE_KEY2 = "qp2";
 
@@ -79,6 +90,10 @@ public class RuleIndexTest {
 
   @Rule
   public EsTester tester = new EsTester(new RuleIndexDefinition(new MapSettings()));
+  @Rule
+  public DbTester dbTester = DbTester.create(system2);
+  @Rule
+  public ExpectedException thrown = ExpectedException.none();
 
   private RuleIndex index;
   private RuleIndexer ruleIndexer;
@@ -86,16 +101,15 @@ public class RuleIndexTest {
 
   @Before
   public void setUp() {
-    ruleIndexer = new RuleIndexer(tester.client(), null, null, null);
-    activeRuleIndexer = new ActiveRuleIndexer(system2, null, tester.client());
+    ruleIndexer = new RuleIndexer(tester.client(), dbTester.getDbClient());
+    activeRuleIndexer = new ActiveRuleIndexer(system2, dbTester.getDbClient(), tester.client());
     index = new RuleIndex(tester.client());
   }
 
   @Test
   public void search_all_rules() {
-    indexRules(
-      newDoc(RuleKey.of("javascript", "S001")),
-      newDoc(RuleKey.of("java", "S002")));
+    createRule();
+    createRule();
 
     SearchIdResult results = index.search(new RuleQuery(), new SearchOptions());
 
@@ -105,17 +119,19 @@ public class RuleIndexTest {
 
   @Test
   public void search_by_key() {
-    RuleKey js1 = RuleKey.of("javascript", "X001");
-    RuleKey cobol1 = RuleKey.of("cobol", "X001");
-    RuleKey php2 = RuleKey.of("php", "S002");
-    indexRules(
-      newDoc(js1).setName("First rule").setHtmlDescription("The first rule"),
-      newDoc(cobol1).setName("Second rule").setHtmlDescription("The second rule"),
-      newDoc(php2).setName("Third rule").setHtmlDescription("The third rule"));
+    RuleDefinitionDto js1 = createRule(
+      setRepositoryKey("javascript"),
+      setRuleKey("X001"));
+    RuleDefinitionDto cobol1 = createRule(
+      setRepositoryKey("cobol"),
+      setRuleKey("X001"));
+    RuleDefinitionDto php2 = createRule(
+      setRepositoryKey("php"),
+      setRuleKey("S002"));
 
     // key
     RuleQuery query = new RuleQuery().setQueryText("X001");
-    assertThat(index.search(query, new SearchOptions()).getIds()).containsOnly(js1, cobol1);
+    assertThat(index.search(query, new SearchOptions()).getIds()).containsOnly(js1.getKey(), cobol1.getKey());
 
     // partial key does not match
     query = new RuleQuery().setQueryText("X00");
@@ -123,24 +139,30 @@ public class RuleIndexTest {
 
     // repo:key -> nice-to-have !
     query = new RuleQuery().setQueryText("javascript:X001");
-    assertThat(index.search(query, new SearchOptions()).getIds()).containsOnly(js1);
+    assertThat(index.search(query, new SearchOptions()).getIds()).containsOnly(js1.getKey());
   }
 
   @Test
   public void search_by_case_insensitive_key() {
-    RuleKey ruleKey = RuleKey.of("javascript", "X001");
-    indexRules(newDoc(ruleKey).setName("Name without key").setHtmlDescription("Description without key"));
+    RuleDefinitionDto ruleDto = createRule(
+      setRepositoryKey("javascript"),
+      setRuleKey("X001"));
 
     RuleQuery query = new RuleQuery().setQueryText("x001");
-    assertThat(index.search(query, new SearchOptions()).getIds()).containsOnly(ruleKey);
+    assertThat(index.search(query, new SearchOptions()).getIds()).containsOnly(ruleDto.getKey());
   }
 
   @Test
   public void filter_by_key() {
-    indexRules(
-      newDoc(RuleKey.of("javascript", "X001")),
-      newDoc(RuleKey.of("cobol", "X001")),
-      newDoc(RuleKey.of("php", "S002")));
+    createRule(
+      setRepositoryKey("javascript"),
+      setRuleKey("X001"));
+    createRule(
+      setRepositoryKey("cobol"),
+      setRuleKey("X001"));
+    createRule(
+      setRepositoryKey("php"),
+      setRuleKey("S002"));
 
     // key
     RuleQuery query = new RuleQuery().setKey(RuleKey.of("javascript", "X001").toString());
@@ -154,8 +176,7 @@ public class RuleIndexTest {
 
   @Test
   public void search_name_by_query() {
-    indexRules(newDoc(RuleKey.of("javascript", "S001"))
-      .setName("testing the partial match and matching of rule"));
+    createRule(setName("testing the partial match and matching of rule"));
 
     // substring
     RuleQuery query = new RuleQuery().setQueryText("test");
@@ -178,48 +199,55 @@ public class RuleIndexTest {
   public void search_name_with_protected_chars() {
     String nameWithProtectedChars = "ja#va&sc\"r:ipt";
 
-    indexRules(newDoc(RuleKey.of("javascript", "S001"))
-      .setName(nameWithProtectedChars));
+    RuleDefinitionDto ruleDto = createRule(setName(nameWithProtectedChars));
 
     RuleQuery protectedCharsQuery = new RuleQuery().setQueryText(nameWithProtectedChars);
     List<RuleKey> results = index.search(protectedCharsQuery, new SearchOptions()).getIds();
-    assertThat(results).containsOnly(RuleKey.of("javascript", "S001"));
+    assertThat(results).containsOnly(ruleDto.getKey());
   }
 
   @Test
   public void search_by_any_of_repositories() {
-    indexRules(
-      newDoc(RuleKey.of("findbugs", "S001")),
-      newDoc(RuleKey.of("pmd", "S002")));
+    RuleDefinitionDto findbugs = createRule(
+      setRepositoryKey("findbugs"),
+      setRuleKey("S001"));
+    RuleDefinitionDto pmd = createRule(
+      setRepositoryKey("pmd"),
+      setRuleKey("S002"));
 
-    List<SearchHit> docs = tester.getDocuments(INDEX_TYPE_RULE);
-    for (SearchHit doc : docs) {
-      System.out.println(doc.getSourceAsString());
-    }
     RuleQuery query = new RuleQuery().setRepositories(asList("checkstyle", "pmd"));
     SearchIdResult results = index.search(query, new SearchOptions());
-    assertThat(results.getIds()).containsOnly(RuleKey.of("pmd", "S002"));
+    assertThat(results.getIds()).containsOnly(pmd.getKey());
 
     // no results
     query = new RuleQuery().setRepositories(singletonList("checkstyle"));
     assertThat(index.search(query, new SearchOptions()).getIds()).isEmpty();
 
     // empty list => no filter
-    query = new RuleQuery().setRepositories(Collections.emptyList());
-    assertThat(index.search(query, new SearchOptions()).getIds()).hasSize(2);
+    query = new RuleQuery().setRepositories(emptyList());
+    assertThat(index.search(query, new SearchOptions()).getIds()).containsOnly(findbugs.getKey(), pmd.getKey());
   }
 
   @Test
   public void filter_by_tags() {
-    indexRules(
-      newDoc(RuleKey.of("java", "S001")).setAllTags(singleton("tag1")),
-      newDoc(RuleKey.of("java", "S002")).setAllTags(singleton("tag2")));
+    OrganizationDto organization = dbTester.organizations().insert();
+
+    RuleDefinitionDto rule1 = createRule(setSystemTags("tag1s"));
+    createRuleMetadata(rule1, organization, setTags("tag1"));
+    RuleDefinitionDto rule2 = createRule(setSystemTags("tag2s"));
+    createRuleMetadata(rule2, organization, setTags("tag2"));
 
     // find all
     RuleQuery query = new RuleQuery();
+    assertThat(index.search(query, new SearchOptions()).getIds()).containsOnly(rule1.getKey(), rule2.getKey());
+
+    // tag2s in filter
+    query = new RuleQuery().setOrganizationUuid(organization.getUuid()).setTags(of("tag2s"));
+    assertThat(index.search(query, new SearchOptions()).getIds()).containsOnly(rule2.getKey());
+
     // tag2 in filter
-    query = new RuleQuery().setTags(ImmutableSet.of("tag2"));
-    assertThat(index.search(query, new SearchOptions()).getIds()).containsOnly(RuleKey.of("java", "S002"));
+    query = new RuleQuery().setOrganizationUuid(organization.getUuid()).setTags(of("tag2"));
+    assertThat(index.search(query, new SearchOptions()).getIds()).containsOnly(rule2.getKey());
 
     // empty list => no filter
     query = new RuleQuery().setTags(emptySet());
@@ -227,15 +255,34 @@ public class RuleIndexTest {
 
     // null list => no filter
     query = new RuleQuery().setTags(null);
-    assertThat(index.search(query, new SearchOptions()).getIds()).hasSize(2);
+    assertThat(index.search(query, new SearchOptions()).getIds()).containsOnly(rule1.getKey(), rule2.getKey());
+  }
+
+  @SafeVarargs
+  private final RuleDefinitionDto createRule(Consumer<RuleDefinitionDto>... populaters) {
+    RuleDefinitionDto ruleDto = dbTester.rules().insert(populaters);
+    ruleIndexer.indexRuleDefinition(ruleDto.getKey());
+    return ruleDto;
+  }
+
+  @SafeVarargs
+  private final RuleMetadataDto createRuleMetadata(RuleDefinitionDto rule, OrganizationDto organization, Consumer<RuleMetadataDto>... populaters) {
+    RuleMetadataDto ruleMetadataDto = dbTester.rules().insertOrUpdateMetadata(rule, organization, populaters);
+    ruleIndexer.indexRuleExtension(organization, rule.getKey());
+    return ruleMetadataDto;
   }
 
   @Test
   public void tags_facet_supports_selected_value_with_regexp_special_characters() {
-    indexRules(newDoc(RuleKey.of("java", "S001")).setAllTags(singleton("misra++")));
+    OrganizationDto organization = dbTester.organizations().insert();
 
-    RuleQuery query = new RuleQuery().setTags(singletonList("misra["));
-    SearchOptions options = new SearchOptions().addFacets(RuleIndex.FACET_TAGS);
+    RuleDefinitionDto rule = createRule();
+    createRuleMetadata(rule, organization, setTags("misra++"));
+
+    RuleQuery query = new RuleQuery()
+      .setOrganizationUuid(organization.getUuid())
+      .setTags(singletonList("misra["));
+    SearchOptions options = new SearchOptions().addFacets(FACET_TAGS);
 
     // do not fail
     assertThat(index.search(query, options).getTotal()).isEqualTo(0);
@@ -243,22 +290,21 @@ public class RuleIndexTest {
 
   @Test
   public void search_by_types() {
-    indexRules(
-      newDoc(RULE_KEY_1).setType(CODE_SMELL),
-      newDoc(RULE_KEY_2).setType(VULNERABILITY),
-      newDoc(RULE_KEY_3).setType(BUG),
-      newDoc(RULE_KEY_4).setType(BUG));
+    RuleDefinitionDto codeSmell = createRule(setType(CODE_SMELL));
+    RuleDefinitionDto vulnerability = createRule(setType(VULNERABILITY));
+    RuleDefinitionDto bug1 = createRule(setType(BUG));
+    RuleDefinitionDto bug2 = createRule(setType(BUG));
 
     // find all
     RuleQuery query = new RuleQuery();
     assertThat(index.search(query, new SearchOptions()).getIds()).hasSize(4);
 
     // type3 in filter
-    query = new RuleQuery().setTypes(ImmutableSet.of(VULNERABILITY));
-    assertThat(index.search(query, new SearchOptions()).getIds()).containsOnly(RULE_KEY_2);
+    query = new RuleQuery().setTypes(of(VULNERABILITY));
+    assertThat(index.search(query, new SearchOptions()).getIds()).containsOnly(vulnerability.getKey());
 
-    query = new RuleQuery().setTypes(ImmutableSet.of(BUG));
-    assertThat(index.search(query, new SearchOptions()).getIds()).containsOnly(RULE_KEY_3, RULE_KEY_4);
+    query = new RuleQuery().setTypes(of(BUG));
+    assertThat(index.search(query, new SearchOptions()).getIds()).containsOnly(bug1.getKey(), bug2.getKey());
 
     // types in query => nothing
     query = new RuleQuery().setQueryText("code smell bug vulnerability");
@@ -275,9 +321,8 @@ public class RuleIndexTest {
 
   @Test
   public void search_by_is_template() {
-    indexRules(
-      newDoc(RuleKey.of("java", "S001")).setIsTemplate(false),
-      newDoc(RuleKey.of("java", "S002")).setIsTemplate(true));
+    RuleDefinitionDto ruleNoTemplate = createRule(setIsTemplate(false));
+    RuleDefinitionDto ruleIsTemplate = createRule(setIsTemplate(true));
 
     // find all
     RuleQuery query = new RuleQuery();
@@ -287,23 +332,23 @@ public class RuleIndexTest {
     // Only template
     query = new RuleQuery().setIsTemplate(true);
     results = index.search(query, new SearchOptions());
-    assertThat(results.getIds()).containsOnly(RuleKey.of("java", "S002"));
+    assertThat(results.getIds()).containsOnly(ruleIsTemplate.getKey());
 
     // Only not template
     query = new RuleQuery().setIsTemplate(false);
     results = index.search(query, new SearchOptions());
-    assertThat(results.getIds()).containsOnly(RuleKey.of("java", "S001"));
+    assertThat(results.getIds()).containsOnly(ruleNoTemplate.getKey());
 
     // null => no filter
     query = new RuleQuery().setIsTemplate(null);
-    assertThat(index.search(query, new SearchOptions()).getIds()).hasSize(2);
+    results = index.search(query, new SearchOptions());
+    assertThat(results.getIds()).containsOnly(ruleIsTemplate.getKey(), ruleNoTemplate.getKey());
   }
 
   @Test
   public void search_by_template_key() {
-    indexRules(
-      newDoc(RuleKey.of("java", "S001")).setIsTemplate(true),
-      newDoc(RuleKey.of("java", "S001_MY_CUSTOM")).setTemplateKey("java:S001"));
+    RuleDefinitionDto template = createRule(setIsTemplate(true));
+    RuleDefinitionDto customRule = createRule(setTemplateId(template.getId()));
 
     // find all
     RuleQuery query = new RuleQuery();
@@ -311,9 +356,9 @@ public class RuleIndexTest {
     assertThat(results.getIds()).hasSize(2);
 
     // Only custom rule
-    query = new RuleQuery().setTemplateKey("java:S001");
+    query = new RuleQuery().setTemplateKey(template.getKey().toString());
     results = index.search(query, new SearchOptions());
-    assertThat(results.getIds()).containsOnly(RuleKey.of("java", "S001_MY_CUSTOM"));
+    assertThat(results.getIds()).containsOnly(customRule.getKey());
 
     // null => no filter
     query = new RuleQuery().setTemplateKey(null);
@@ -322,20 +367,19 @@ public class RuleIndexTest {
 
   @Test
   public void search_by_any_of_languages() {
-    indexRules(
-      newDoc(RuleKey.of("java", "S001")).setLanguage("java"),
-      newDoc(RuleKey.of("javascript", "S002")).setLanguage("js"));
+    RuleDefinitionDto java = createRule(setLanguage("java"));
+    RuleDefinitionDto javascript = createRule(setLanguage("js"));
 
     RuleQuery query = new RuleQuery().setLanguages(asList("cobol", "js"));
     SearchIdResult results = index.search(query, new SearchOptions());
-    assertThat(results.getIds()).containsOnly(RuleKey.of("javascript", "S002"));
+    assertThat(results.getIds()).containsOnly(javascript.getKey());
 
     // no results
     query = new RuleQuery().setLanguages(singletonList("cpp"));
     assertThat(index.search(query, new SearchOptions()).getIds()).isEmpty();
 
     // empty list => no filter
-    query = new RuleQuery().setLanguages(Collections.emptyList());
+    query = new RuleQuery().setLanguages(emptyList());
     assertThat(index.search(query, new SearchOptions()).getIds()).hasSize(2);
 
     // null list => no filter
@@ -345,20 +389,19 @@ public class RuleIndexTest {
 
   @Test
   public void search_by_any_of_severities() {
-    indexRules(
-      newDoc(RuleKey.of("java", "S001")).setSeverity(BLOCKER),
-      newDoc(RuleKey.of("java", "S002")).setSeverity(INFO));
+    RuleDefinitionDto blocker = createRule(setSeverity(BLOCKER));
+    RuleDefinitionDto info = createRule(setSeverity(INFO));
 
     RuleQuery query = new RuleQuery().setSeverities(asList(INFO, MINOR));
     SearchIdResult results = index.search(query, new SearchOptions());
-    assertThat(results.getIds()).containsOnly(RuleKey.of("java", "S002"));
+    assertThat(results.getIds()).containsOnly(info.getKey());
 
     // no results
     query = new RuleQuery().setSeverities(singletonList(MINOR));
     assertThat(index.search(query, new SearchOptions()).getIds()).isEmpty();
 
     // empty list => no filter
-    query = new RuleQuery().setSeverities(Collections.emptyList());
+    query = new RuleQuery().setSeverities(emptyList());
     assertThat(index.search(query, new SearchOptions()).getIds()).hasSize(2);
 
     // null list => no filter
@@ -368,20 +411,19 @@ public class RuleIndexTest {
 
   @Test
   public void search_by_any_of_statuses() {
-    indexRules(
-      newDoc(RuleKey.of("java", "S001")).setStatus(RuleStatus.BETA.name()),
-      newDoc(RuleKey.of("java", "S002")).setStatus(RuleStatus.READY.name()));
+    RuleDefinitionDto beta = createRule(setStatus(RuleStatus.BETA));
+    RuleDefinitionDto ready = createRule(setStatus(RuleStatus.READY));
 
     RuleQuery query = new RuleQuery().setStatuses(asList(RuleStatus.DEPRECATED, RuleStatus.READY));
     SearchIdResult<RuleKey> results = index.search(query, new SearchOptions());
-    assertThat(results.getIds()).containsOnly(RuleKey.of("java", "S002"));
+    assertThat(results.getIds()).containsOnly(ready.getKey());
 
     // no results
     query = new RuleQuery().setStatuses(singletonList(RuleStatus.DEPRECATED));
     assertThat(index.search(query, new SearchOptions()).getIds()).isEmpty();
 
     // empty list => no filter
-    query = new RuleQuery().setStatuses(Collections.emptyList());
+    query = new RuleQuery().setStatuses(emptyList());
     assertThat(index.search(query, new SearchOptions()).getIds()).hasSize(2);
 
     // null list => no filter
@@ -391,55 +433,53 @@ public class RuleIndexTest {
 
   @Test
   public void search_by_profile() throws InterruptedException {
-    indexRules(
-      newDoc(RULE_KEY_1),
-      newDoc(RULE_KEY_2),
-      newDoc(RULE_KEY_3));
+    RuleDefinitionDto rule1 = createRule();
+    RuleDefinitionDto rule2 = createRule();
+    RuleDefinitionDto rule3 = createRule();
 
     indexActiveRules(
-      ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY1, RULE_KEY_1)),
-      ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY2, RULE_KEY_1)),
-      ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY1, RULE_KEY_2)));
+      ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY1, rule1.getKey())),
+      ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY2, rule1.getKey())),
+      ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY1, rule2.getKey())));
 
     assertThat(tester.countDocuments(INDEX_TYPE_ACTIVE_RULE)).isEqualTo(3);
 
     // 1. get all active rules.
     assertThat(index.search(new RuleQuery().setActivation(true), new SearchOptions()).getIds())
-      .containsOnly(RULE_KEY_1, RULE_KEY_2);
+      .containsOnly(rule1.getKey(), rule2.getKey());
 
     // 2. get all inactive rules.
     assertThat(index.search(new RuleQuery().setActivation(false), new SearchOptions()).getIds())
-      .containsOnly(RULE_KEY_3);
+      .containsOnly(rule3.getKey());
 
     // 3. get all rules not active on profile
     assertThat(index.search(new RuleQuery().setActivation(false).setQProfileKey(QUALITY_PROFILE_KEY2), new SearchOptions()).getIds())
-      .containsOnly(RULE_KEY_2, RULE_KEY_3);
+      .containsOnly(rule2.getKey(), rule3.getKey());
 
     // 4. get all active rules on profile
     assertThat(index.search(new RuleQuery().setActivation(true).setQProfileKey(QUALITY_PROFILE_KEY2), new SearchOptions()).getIds())
-      .containsOnly(RULE_KEY_1);
+      .containsOnly(rule1.getKey());
   }
 
   @Test
   public void search_by_profile_and_inheritance() {
-    indexRules(
-      newDoc(RULE_KEY_1),
-      newDoc(RULE_KEY_2),
-      newDoc(RULE_KEY_3),
-      newDoc(RULE_KEY_4));
+    RuleDefinitionDto rule1 = createRule();
+    RuleDefinitionDto rule2 = createRule();
+    RuleDefinitionDto rule3 = createRule();
+    RuleDefinitionDto rule4 = createRule();
 
-    ActiveRuleKey activeRuleKey1 = ActiveRuleKey.of(QUALITY_PROFILE_KEY1, RULE_KEY_1);
-    ActiveRuleKey activeRuleKey2 = ActiveRuleKey.of(QUALITY_PROFILE_KEY1, RULE_KEY_2);
-    ActiveRuleKey activeRuleKey3 = ActiveRuleKey.of(QUALITY_PROFILE_KEY1, RULE_KEY_3);
+    ActiveRuleKey activeRuleKey1 = ActiveRuleKey.of(QUALITY_PROFILE_KEY1, rule1.getKey());
+    ActiveRuleKey activeRuleKey2 = ActiveRuleKey.of(QUALITY_PROFILE_KEY1, rule2.getKey());
+    ActiveRuleKey activeRuleKey3 = ActiveRuleKey.of(QUALITY_PROFILE_KEY1, rule3.getKey());
 
     indexActiveRules(
       ActiveRuleDocTesting.newDoc(activeRuleKey1),
       ActiveRuleDocTesting.newDoc(activeRuleKey2),
       ActiveRuleDocTesting.newDoc(activeRuleKey3),
       // Profile 2 is a child a profile 1
-      ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY2, RULE_KEY_1)).setInheritance(INHERITED.name()),
-      ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY2, RULE_KEY_2)).setInheritance(OVERRIDES.name()),
-      ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY2, RULE_KEY_3)).setInheritance(INHERITED.name()));
+      ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY2, rule1.getKey())).setInheritance(INHERITED.name()),
+      ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY2, rule2.getKey())).setInheritance(OVERRIDES.name()),
+      ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY2, rule3.getKey())).setInheritance(INHERITED.name()));
 
     // 0. get all rules
     assertThat(index.search(new RuleQuery(), new SearchOptions()).getIds())
@@ -453,62 +493,61 @@ public class RuleIndexTest {
     // 2. get all inactive rules.
     assertThat(index.search(new RuleQuery()
       .setActivation(false), new SearchOptions()).getIds())
-        .containsOnly(RULE_KEY_4);
+        .containsOnly(rule4.getKey());
 
     // 3. get Inherited Rules on profile1
     assertThat(index.search(new RuleQuery().setActivation(true)
       .setQProfileKey(QUALITY_PROFILE_KEY1)
-      .setInheritance(ImmutableSet.of(INHERITED.name())),
+      .setInheritance(of(INHERITED.name())),
       new SearchOptions()).getIds())
         .isEmpty();
 
     // 4. get Inherited Rules on profile2
     assertThat(index.search(new RuleQuery().setActivation(true)
       .setQProfileKey(QUALITY_PROFILE_KEY2)
-      .setInheritance(ImmutableSet.of(INHERITED.name())),
+      .setInheritance(of(INHERITED.name())),
       new SearchOptions()).getIds())
         .hasSize(2);
 
     // 5. get Overridden Rules on profile1
     assertThat(index.search(new RuleQuery().setActivation(true)
       .setQProfileKey(QUALITY_PROFILE_KEY1)
-      .setInheritance(ImmutableSet.of(OVERRIDES.name())),
+      .setInheritance(of(OVERRIDES.name())),
       new SearchOptions()).getIds())
         .isEmpty();
 
     // 6. get Overridden Rules on profile2
     assertThat(index.search(new RuleQuery().setActivation(true)
       .setQProfileKey(QUALITY_PROFILE_KEY2)
-      .setInheritance(ImmutableSet.of(OVERRIDES.name())),
+      .setInheritance(of(OVERRIDES.name())),
       new SearchOptions()).getIds())
         .hasSize(1);
 
     // 7. get Inherited AND Overridden Rules on profile1
     assertThat(index.search(new RuleQuery().setActivation(true)
       .setQProfileKey(QUALITY_PROFILE_KEY1)
-      .setInheritance(ImmutableSet.of(INHERITED.name(), OVERRIDES.name())),
+      .setInheritance(of(INHERITED.name(), OVERRIDES.name())),
       new SearchOptions()).getIds())
         .isEmpty();
 
     // 8. get Inherited AND Overridden Rules on profile2
     assertThat(index.search(new RuleQuery().setActivation(true)
       .setQProfileKey(QUALITY_PROFILE_KEY2)
-      .setInheritance(ImmutableSet.of(INHERITED.name(), OVERRIDES.name())),
+      .setInheritance(of(INHERITED.name(), OVERRIDES.name())),
       new SearchOptions()).getIds())
         .hasSize(3);
   }
 
   @Test
   public void search_by_profile_and_active_severity() {
-    indexRules(
-      newDoc(RULE_KEY_1).setSeverity(MAJOR),
-      newDoc(RULE_KEY_2).setSeverity(MINOR),
-      newDoc(RULE_KEY_3).setSeverity(INFO));
+    RuleDefinitionDto major = createRule(setSeverity(MAJOR));
+    RuleDefinitionDto minor = createRule(setSeverity(MINOR));
+    RuleDefinitionDto info = createRule(setSeverity(INFO));
 
     indexActiveRules(
-      ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY1, RULE_KEY_1)).setSeverity(BLOCKER),
-      ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY2, RULE_KEY_1)).setSeverity(BLOCKER),
-      ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY1, RULE_KEY_2)).setSeverity(CRITICAL));
+      ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY1, major.getKey())).setSeverity(BLOCKER),
+      ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY2, major.getKey())).setSeverity(BLOCKER),
+      ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY1, minor.getKey())).setSeverity(CRITICAL));
 
     // 1. get all active rules.
     assertThat(index.search(new RuleQuery().setActivation(true).setQProfileKey(QUALITY_PROFILE_KEY1), new SearchOptions()).getIds())
@@ -518,7 +557,7 @@ public class RuleIndexTest {
     SearchIdResult<RuleKey> result = index.search(new RuleQuery().setActivation(true)
       .setQProfileKey(QUALITY_PROFILE_KEY1).setActiveSeverities(singletonList(CRITICAL)),
       new SearchOptions().addFacets(singletonList(RuleIndex.FACET_ACTIVE_SEVERITIES)));
-    assertThat(result.getIds()).containsOnly(RULE_KEY_2);
+    assertThat(result.getIds()).containsOnly(minor.getKey());
 
     // check stickyness of active severity facet
     assertThat(result.getFacets().get(RuleIndex.FACET_ACTIVE_SEVERITIES)).containsOnly(entry(BLOCKER, 1L), entry(CRITICAL, 1L));
@@ -531,115 +570,246 @@ public class RuleIndexTest {
 
   @Test
   public void all_tags() {
-    indexRules(
-      newDoc(RuleKey.of("java", "S001")).setAllTags(asList("tag1", "sys1", "sys2")),
-      newDoc(RuleKey.of("java", "S002")).setAllTags(asList("tag2")));
+    OrganizationDto organization = dbTester.organizations().insert();
+
+    RuleDefinitionDto rule1 = createRule(setSystemTags("sys1", "sys2"));
+    createRuleMetadata(rule1, organization,
+      setOrganizationUuid(organization.getUuid()),
+      setTags("tag1"));
 
-    assertThat(index.terms(RuleIndexDefinition.FIELD_RULE_ALL_TAGS, null, 10)).containsOnly("tag1", "tag2", "sys1", "sys2");
+    RuleDefinitionDto rule2 = createRule(setSystemTags());
+    createRuleMetadata(rule2, organization,
+      setOrganizationUuid(organization.getUuid()),
+      setTags("tag2"));
+
+    assertThat(index.listTags(organization.getUuid(), null, 10)).containsOnly("tag1", "tag2", "sys1", "sys2");
+  }
+
+  @Test
+  public void all_tags_minds_the_oranization() {
+    OrganizationDto organization1 = dbTester.organizations().insert();
+    RuleDefinitionDto rule1 = createRule(setSystemTags("sys1"));
+    createRuleMetadata(rule1, organization1,
+      setOrganizationUuid(organization1.getUuid()),
+      setTags("tag1"));
+
+    OrganizationDto organization2 = dbTester.organizations().insert();
+    RuleDefinitionDto rule2 = createRule(setSystemTags("sys2"));
+    createRuleMetadata(rule2, organization2,
+      setOrganizationUuid(organization2.getUuid()),
+      setTags("tag2"));
+
+    assertThat(index.listTags(organization1.getUuid(), null, 10)).containsOnly("tag1", "sys1", "sys2");
+    assertThat(index.listTags(organization2.getUuid(), null, 10)).containsOnly("tag2", "sys1", "sys2");
   }
 
   @Test
   public void available_since() throws InterruptedException {
-    indexRules(
-      newDoc(RuleKey.of("java", "S001")).setCreatedAt(1000L),
-      newDoc(RuleKey.of("java", "S002")).setCreatedAt(2000L));
+    RuleKey ruleOld = createRule(setCreatedAt(1000L)).getKey();
+    RuleKey ruleOlder = createRule(setCreatedAt(2000L)).getKey();
 
     // 0. find all rules;
-    assertThat(index.search(new RuleQuery(), new SearchOptions()).getIds()).hasSize(2);
+    assertThat(index.search(new RuleQuery(), new SearchOptions()).getIds()).containsOnly(ruleOld, ruleOlder);
 
     // 1. find all rules available since a date;
     RuleQuery availableSinceQuery = new RuleQuery().setAvailableSince(2000L);
-    assertThat(index.search(availableSinceQuery, new SearchOptions()).getIds()).containsOnly(RuleKey.of("java", "S002"));
+    assertThat(index.search(availableSinceQuery, new SearchOptions()).getIds()).containsOnly(ruleOlder);
 
     // 2. find no new rules since tomorrow.
     RuleQuery availableSinceNowQuery = new RuleQuery().setAvailableSince(3000L);
-    assertThat(index.search(availableSinceNowQuery, new SearchOptions()).getIds()).hasSize(0);
+    assertThat(index.search(availableSinceNowQuery, new SearchOptions()).getIds()).containsOnly();
   }
 
   @Test
   public void global_facet_on_repositories_and_tags() {
-    indexRules(
-      newDoc(RuleKey.of("php", "S001")).setAllTags(singletonList("sysTag")),
-      newDoc(RuleKey.of("php", "S002")).setAllTags(singletonList("tag1")),
-      newDoc(RuleKey.of("javascript", "S002")).setAllTags(asList("tag1", "tag2")));
+    OrganizationDto organization = dbTester.organizations().insert();
+
+    createRule(setRepositoryKey("php"), setSystemTags("sysTag"));
+    RuleDefinitionDto rule1 = createRule(setRepositoryKey("php"), setSystemTags());
+    createRuleMetadata(rule1, organization, setTags("tag1"));
+    RuleDefinitionDto rule2 = createRule(setRepositoryKey("javascript"), setSystemTags());
+    createRuleMetadata(rule2, organization, setTags("tag1", "tag2"));
 
     // should not have any facet!
-    RuleQuery query = new RuleQuery();
-    SearchIdResult result = index.search(query, new SearchOptions());
-    assertThat(result.getFacets().getAll()).isEmpty();
+    RuleQuery query = new RuleQuery().setOrganizationUuid(organization.getUuid());
+    SearchIdResult result1 = index.search(query, new SearchOptions());
+    assertThat(result1.getFacets().getAll()).isEmpty();
 
     // should not have any facet on non matching query!
-    result = index.search(new RuleQuery().setQueryText("aeiou"), new SearchOptions().addFacets(singletonList("repositories")));
-    assertThat(result.getFacets().getAll()).hasSize(1);
-    assertThat(result.getFacets().getAll().get("repositories")).isEmpty();
+    SearchIdResult result2 = index.search(new RuleQuery().setQueryText("aeiou"), new SearchOptions().addFacets(singletonList("repositories")));
+    assertThat(result2.getFacets().getAll()).hasSize(1);
+    assertThat(result2.getFacets().getAll().get("repositories")).isEmpty();
 
     // Repositories Facet is preset
-    result = index.search(query, new SearchOptions().addFacets(asList("repositories", "tags")));
-    assertThat(result.getFacets()).isNotNull();
-    assertThat(result.getFacets().getAll()).hasSize(2);
+    SearchIdResult result3 = index.search(query, new SearchOptions().addFacets(asList("repositories", "tags")));
+    assertThat(result3.getFacets()).isNotNull();
 
     // Verify the value of a given facet
-    Map<String, Long> repoFacets = result.getFacets().get("repositories");
+    Map<String, Long> repoFacets = result3.getFacets().get("repositories");
     assertThat(repoFacets).containsOnly(entry("php", 2L), entry("javascript", 1L));
 
     // Check that tag facet has both Tags and SystemTags values
-    Map<String, Long> tagFacets = result.getFacets().get("tags");
+    Map<String, Long> tagFacets = result3.getFacets().get("tags");
     assertThat(tagFacets).containsOnly(entry("tag1", 2L), entry("sysTag", 1L), entry("tag2", 1L));
+
+    // Check that there are no other facets
+    assertThat(result3.getFacets().getAll()).hasSize(2);
+  }
+
+  private void sticky_facet_rule_setup() {
+    insertRuleDefinition(setRepositoryKey("xoo"), setRuleKey("S001"), setLanguage("java"), setSystemTags(), setType(BUG));
+    insertRuleDefinition(setRepositoryKey("xoo"), setRuleKey("S002"), setLanguage("java"), setSystemTags(), setType(CODE_SMELL));
+    insertRuleDefinition(setRepositoryKey("xoo"), setRuleKey("S003"), setLanguage("java"), setSystemTags("T1", "T2"), setType(CODE_SMELL));
+    insertRuleDefinition(setRepositoryKey("xoo"), setRuleKey("S011"), setLanguage("cobol"), setSystemTags(), setType(CODE_SMELL));
+    insertRuleDefinition(setRepositoryKey("xoo"), setRuleKey("S012"), setLanguage("cobol"), setSystemTags(), setType(BUG));
+    insertRuleDefinition(setRepositoryKey("foo"), setRuleKey("S013"), setLanguage("cobol"), setSystemTags("T3", "T4"), setType(VULNERABILITY));
+    insertRuleDefinition(setRepositoryKey("foo"), setRuleKey("S111"), setLanguage("cpp"), setSystemTags(), setType(BUG));
+    insertRuleDefinition(setRepositoryKey("foo"), setRuleKey("S112"), setLanguage("cpp"), setSystemTags(), setType(CODE_SMELL));
+    insertRuleDefinition(setRepositoryKey("foo"), setRuleKey("S113"), setLanguage("cpp"), setSystemTags("T2", "T3"), setType(CODE_SMELL));
   }
 
   @Test
-  public void sticky_facets() {
-    indexRules(
-      newDoc(RuleKey.of("xoo", "S001")).setLanguage("java").setAllTags(Collections.emptyList()).setType(BUG),
-      newDoc(RuleKey.of("xoo", "S002")).setLanguage("java").setAllTags(Collections.emptyList()).setType(CODE_SMELL),
-      newDoc(RuleKey.of("xoo", "S003")).setLanguage("java").setAllTags(asList("T1", "T2")).setType(CODE_SMELL),
-      newDoc(RuleKey.of("xoo", "S011")).setLanguage("cobol").setAllTags(Collections.emptyList()).setType(CODE_SMELL),
-      newDoc(RuleKey.of("xoo", "S012")).setLanguage("cobol").setAllTags(Collections.emptyList()).setType(BUG),
-      newDoc(RuleKey.of("foo", "S013")).setLanguage("cobol").setAllTags(asList("T3", "T4")).setType(VULNERABILITY),
-      newDoc(RuleKey.of("foo", "S111")).setLanguage("cpp").setAllTags(Collections.emptyList()).setType(BUG),
-      newDoc(RuleKey.of("foo", "S112")).setLanguage("cpp").setAllTags(Collections.emptyList()).setType(CODE_SMELL),
-      newDoc(RuleKey.of("foo", "S113")).setLanguage("cpp").setAllTags(asList("T2", "T3")).setType(CODE_SMELL));
+  public void sticky_facets_base() {
+    sticky_facet_rule_setup();
 
-    // 0 assert Base
-    assertThat(index.search(new RuleQuery(), new SearchOptions()).getIds()).hasSize(9);
+    RuleQuery query = new RuleQuery();
+
+    assertThat(index.search(query, new SearchOptions()).getIds()).hasSize(9);
+  }
+
+  /**
+   * Facet with no filters at all
+   */
+  @Test
+  public void sticky_facets_no_filters() {
+    sticky_facet_rule_setup();
+
+    RuleQuery query = new RuleQuery()
+      .setOrganizationUuid("some_uuid");
 
-    // 1 Facet with no filters at all
-    SearchIdResult result = index.search(new RuleQuery(), new SearchOptions().addFacets(asList(FACET_LANGUAGES, FACET_REPOSITORIES, FACET_TAGS, FACET_TYPES)));
+    SearchIdResult result = index.search(query, new SearchOptions().addFacets(asList(FACET_LANGUAGES, FACET_REPOSITORIES,
+      FACET_TAGS, FACET_TYPES)));
     assertThat(result.getFacets().getAll()).hasSize(4);
     assertThat(result.getFacets().getAll().get(FACET_LANGUAGES).keySet()).containsOnly("cpp", "java", "cobol");
     assertThat(result.getFacets().getAll().get(FACET_REPOSITORIES).keySet()).containsExactly("xoo", "foo");
     assertThat(result.getFacets().getAll().get(FACET_TAGS).keySet()).containsOnly("T1", "T2", "T3", "T4");
     assertThat(result.getFacets().getAll().get(FACET_TYPES).keySet()).containsOnly("BUG", "CODE_SMELL", "VULNERABILITY");
+  }
 
-    // 2 Facet with a language filter
-    // -- lang facet should still have all language
-    result = index.search(new RuleQuery().setLanguages(ImmutableList.of("cpp")), new SearchOptions().addFacets(asList(FACET_LANGUAGES, FACET_REPOSITORIES, FACET_TAGS)));
+  /**
+   * Facet with a language filter
+   * -- lang facet should still have all language
+   */
+  @Test
+  public void sticky_facets_with_1_filter() {
+    sticky_facet_rule_setup();
+
+    RuleQuery query = new RuleQuery()
+      .setOrganizationUuid("some_uuid")
+      .setLanguages(ImmutableList.of("cpp"));
+
+    SearchIdResult result = index.search(query, new SearchOptions().addFacets(asList(FACET_LANGUAGES,
+      FACET_REPOSITORIES, FACET_TAGS)));
     assertThat(result.getIds()).hasSize(3);
     assertThat(result.getFacets().getAll()).hasSize(3);
     assertThat(result.getFacets().get(FACET_LANGUAGES).keySet()).containsOnly("cpp", "java", "cobol");
+    assertThat(result.getFacets().get(FACET_REPOSITORIES).keySet()).containsOnly("foo");
+    assertThat(result.getFacets().get(FACET_TAGS).keySet()).containsOnly("T2", "T3");
+  }
+
+  @Test
+  public void tags_facet_should_find_tags_of_specified_organization() {
+    OrganizationDto organization = dbTester.organizations().insert();
+
+    RuleDefinitionDto rule = insertRuleDefinition(setSystemTags());
+    dbTester.rules().insertOrUpdateMetadata(rule, organization, setTags("bla"));
+    ruleIndexer.indexRuleExtension(organization, rule.getKey());
+
+    RuleQuery query = new RuleQuery()
+      .setOrganizationUuid(organization.getUuid());
+    SearchOptions options = new SearchOptions().addFacets(singletonList(FACET_TAGS));
+
+    SearchIdResult result = index.search(query, options);
+    assertThat(result.getFacets().get(FACET_TAGS)).contains(entry("bla", 1L));
+  }
+
+  @Test
+  public void tags_facet_should_not_find_tags_of_any_other_organization() {
+    OrganizationDto organization1 = dbTester.organizations().insert();
+    OrganizationDto organization2 = dbTester.organizations().insert();
+
+    RuleDefinitionDto rule = insertRuleDefinition(setSystemTags());
+    dbTester.rules().insertOrUpdateMetadata(rule, organization1, setTags("bla"));
+
+    RuleQuery query = new RuleQuery()
+      .setOrganizationUuid(organization2.getUuid());
+    SearchOptions options = new SearchOptions().addFacets(singletonList(FACET_TAGS));
+
+    SearchIdResult result = index.search(query, options);
+    assertThat(result.getFacets().get(FACET_TAGS)).contains();
+  }
 
-    // 3 facet with 2 filters
-    // -- lang facet for tag T2
-    // -- tag facet for lang cpp
-    // -- repository for cpp & T2
-    result = index.search(new RuleQuery()
+  @Test
+  public void tags_facet_should_be_available_if_organization_is_speficied() {
+    RuleQuery query = new RuleQuery()
+      .setOrganizationUuid("some_org_id");
+    SearchOptions options = new SearchOptions().addFacets(singletonList(FACET_TAGS));
+
+    SearchIdResult result = index.search(query, options);
+    assertThat(result.getFacets().get(FACET_TAGS)).isNotNull();
+  }
+
+  @Test
+  public void tags_facet_should_be_unavailable_if_no_organization_is_specfified() {
+    RuleQuery query = new RuleQuery();
+    SearchOptions options = new SearchOptions().addFacets(singletonList(FACET_TAGS));
+
+    thrown.expectMessage("Cannot use tags facet, if no organization is specified.");
+    index.search(query, options);
+  }
+
+  /**
+   * Facet with 2 filters
+   * -- lang facet for tag T2
+   * -- tag facet for lang cpp
+   * -- repository for cpp & T2
+   */
+  @Test
+  public void sticky_facets_with_2_filters() {
+    sticky_facet_rule_setup();
+
+    RuleQuery query = new RuleQuery()
+      .setOrganizationUuid("some_uuid")
       .setLanguages(ImmutableList.of("cpp"))
-      .setTags(ImmutableList.of("T2")), new SearchOptions().addFacets(asList(FACET_LANGUAGES, FACET_REPOSITORIES, FACET_TAGS)));
+      .setTags(ImmutableList.of("T2"));
+
+    SearchIdResult result = index.search(query, new SearchOptions().addFacets(asList(FACET_LANGUAGES, FACET_REPOSITORIES, FACET_TAGS)));
     assertThat(result.getIds()).hasSize(1);
     assertThat(result.getFacets().getAll()).hasSize(3);
     assertThat(result.getFacets().get(FACET_LANGUAGES).keySet()).containsOnly("cpp", "java");
     assertThat(result.getFacets().get(FACET_REPOSITORIES).keySet()).containsOnly("foo");
     assertThat(result.getFacets().get(FACET_TAGS).keySet()).containsOnly("T2", "T3");
+  }
 
-    // 4 facet with 3 filters
-    // -- lang facet for tag T2
-    // -- tag facet for lang cpp & java
-    // -- repository for (cpp || java) & T2
-    // -- type
-    result = index.search(new RuleQuery()
+  /**
+   * Facet with 3 filters
+   * -- lang facet for tag T2
+   * -- tag facet for lang cpp & java
+   * -- repository for (cpp || java) & T2
+   * -- type
+   */
+  @Test
+  public void sticky_facets_with_3_filters() {
+    sticky_facet_rule_setup();
+
+    RuleQuery query = new RuleQuery()
+      .setOrganizationUuid("some_uuid")
       .setLanguages(ImmutableList.of("cpp", "java"))
       .setTags(ImmutableList.of("T2"))
-      .setTypes(asList(BUG, CODE_SMELL)), new SearchOptions().addFacets(asList(FACET_LANGUAGES, FACET_REPOSITORIES, FACET_TAGS, FACET_TYPES)));
+      .setTypes(asList(BUG, CODE_SMELL));
+
+    SearchIdResult result = index.search(query, new SearchOptions().addFacets(asList(FACET_LANGUAGES, FACET_REPOSITORIES, FACET_TAGS,
+        FACET_TYPES)));
     assertThat(result.getIds()).hasSize(2);
     assertThat(result.getFacets().getAll()).hasSize(4);
     assertThat(result.getFacets().get(FACET_LANGUAGES).keySet()).containsOnly("cpp", "java");
@@ -650,31 +820,29 @@ public class RuleIndexTest {
 
   @Test
   public void sort_by_name() {
-    indexRules(
-      newDoc(RuleKey.of("java", "S001")).setName("abcd"),
-      newDoc(RuleKey.of("java", "S002")).setName("ABC"),
-      newDoc(RuleKey.of("java", "S003")).setName("FGH"));
+    RuleDefinitionDto abcd = insertRuleDefinition(setName("abcd"));
+    RuleDefinitionDto abc = insertRuleDefinition(setName("ABC"));
+    RuleDefinitionDto fgh = insertRuleDefinition(setName("FGH"));
 
     // ascending
     RuleQuery query = new RuleQuery().setSortField(RuleIndexDefinition.FIELD_RULE_NAME);
     SearchIdResult<RuleKey> results = index.search(query, new SearchOptions());
-    assertThat(results.getIds()).containsExactly(RuleKey.of("java", "S002"), RuleKey.of("java", "S001"), RuleKey.of("java", "S003"));
+    assertThat(results.getIds()).containsExactly(abc.getKey(), abcd.getKey(), fgh.getKey());
 
     // descending
     query = new RuleQuery().setSortField(RuleIndexDefinition.FIELD_RULE_NAME).setAscendingSort(false);
     results = index.search(query, new SearchOptions());
-    assertThat(results.getIds()).containsExactly(RuleKey.of("java", "S003"), RuleKey.of("java", "S001"), RuleKey.of("java", "S002"));
+    assertThat(results.getIds()).containsExactly(fgh.getKey(), abcd.getKey(), abc.getKey());
   }
 
   @Test
   public void default_sort_is_by_updated_at_desc() {
-    indexRules(
-      newDoc(RuleKey.of("java", "S001")).setCreatedAt(1000L).setUpdatedAt(1000L),
-      newDoc(RuleKey.of("java", "S002")).setCreatedAt(1000L).setUpdatedAt(3000L),
-      newDoc(RuleKey.of("java", "S003")).setCreatedAt(1000L).setUpdatedAt(2000L));
+    RuleDefinitionDto old = insertRuleDefinition(setCreatedAt(1000L), setUpdatedAt(1000L));
+    RuleDefinitionDto oldest = insertRuleDefinition(setCreatedAt(1000L), setUpdatedAt(3000L));
+    RuleDefinitionDto older = insertRuleDefinition(setCreatedAt(1000L), setUpdatedAt(2000L));
 
     SearchIdResult<RuleKey> results = index.search(new RuleQuery(), new SearchOptions());
-    assertThat(results.getIds()).containsExactly(RuleKey.of("java", "S002"), RuleKey.of("java", "S003"), RuleKey.of("java", "S001"));
+    assertThat(results.getIds()).containsExactly(oldest.getKey(), older.getKey(), old.getKey());
   }
 
   @Test
@@ -690,10 +858,9 @@ public class RuleIndexTest {
 
   @Test
   public void paging() {
-    indexRules(
-      newDoc(RuleKey.of("java", "S001")),
-      newDoc(RuleKey.of("java", "S002")),
-      newDoc(RuleKey.of("java", "S003")));
+    insertRuleDefinition();
+    insertRuleDefinition();
+    insertRuleDefinition();
 
     // from 0 to 1 included
     SearchOptions options = new SearchOptions();
@@ -723,10 +890,9 @@ public class RuleIndexTest {
 
   @Test
   public void search_all_keys_by_query() {
-    indexRules(
-      newDoc(RuleKey.of("javascript", "X001")),
-      newDoc(RuleKey.of("cobol", "X001")),
-      newDoc(RuleKey.of("php", "S002")));
+    insertRuleDefinition(setRepositoryKey("javascript"), setRuleKey("X001"));
+    insertRuleDefinition(setRepositoryKey("cobol"), setRuleKey("X001"));
+    insertRuleDefinition(setRepositoryKey("php"), setRuleKey("S002"));
 
     // key
     assertThat(index.searchAll(new RuleQuery().setQueryText("X001"))).hasSize(2);
@@ -740,33 +906,39 @@ public class RuleIndexTest {
 
   @Test
   public void search_all_keys_by_profile() {
-    indexRules(
-      newDoc(RULE_KEY_1),
-      newDoc(RULE_KEY_2),
-      newDoc(RULE_KEY_3));
+    RuleDefinitionDto rule1 = insertRuleDefinition();
+    RuleDefinitionDto rule2 = insertRuleDefinition();
+    RuleDefinitionDto rule3 = insertRuleDefinition();
+
+    RuleKey rule1Key = rule1.getKey();
+    RuleKey rule2Key = rule2.getKey();
+    RuleKey rule3Key = rule3.getKey();
 
     indexActiveRules(
-      ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY1, RULE_KEY_1)),
-      ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY2, RULE_KEY_1)),
-      ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY1, RULE_KEY_2)));
+      ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY1, rule1Key)),
+      ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY2, rule1Key)),
+      ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY1, rule2Key)));
 
     assertThat(tester.countDocuments(INDEX_TYPE_ACTIVE_RULE)).isEqualTo(3);
 
     // 1. get all active rules.
-    assertThat(index.searchAll(new RuleQuery().setActivation(true))).containsOnly(RULE_KEY_1, RULE_KEY_2);
+    assertThat(index.searchAll(new RuleQuery().setActivation(true))).containsOnly(rule1Key, rule2Key);
 
     // 2. get all inactive rules.
-    assertThat(index.searchAll(new RuleQuery().setActivation(false))).containsOnly(RULE_KEY_3);
+    assertThat(index.searchAll(new RuleQuery().setActivation(false))).containsOnly(rule3Key);
 
     // 3. get all rules not active on profile
-    assertThat(index.searchAll(new RuleQuery().setActivation(false).setQProfileKey(QUALITY_PROFILE_KEY2))).containsOnly(RULE_KEY_2, RULE_KEY_3);
+    assertThat(index.searchAll(new RuleQuery().setActivation(false).setQProfileKey(QUALITY_PROFILE_KEY2))).containsOnly(rule2Key, rule3Key);
 
     // 4. get all active rules on profile
-    assertThat(index.searchAll(new RuleQuery().setActivation(true).setQProfileKey(QUALITY_PROFILE_KEY2))).containsOnly(RULE_KEY_1);
+    assertThat(index.searchAll(new RuleQuery().setActivation(true).setQProfileKey(QUALITY_PROFILE_KEY2))).containsOnly(rule1Key);
   }
 
-  private void indexRules(RuleDoc... rules) {
-    ruleIndexer.index(asList(rules).iterator());
+  @SafeVarargs
+  private final RuleDefinitionDto insertRuleDefinition(Consumer<RuleDefinitionDto>... populaters) {
+    RuleDefinitionDto ruleDefinitionDto = dbTester.rules().insert(populaters);
+    ruleIndexer.indexRuleDefinition(ruleDefinitionDto.getKey());
+    return ruleDefinitionDto;
   }
 
   private void indexActiveRules(ActiveRuleDoc... docs) {
index f4581fd1e961af833a69a7e1b64a64b235b5d996..43803aa0fea819cd263b75275c1c2e98ffc70fc4 100644 (file)
  */
 package org.sonar.server.rule.index;
 
-import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.sonar.api.config.MapSettings;
 import org.sonar.api.rule.RuleStatus;
 import org.sonar.api.rule.Severity;
 import org.sonar.api.rules.RuleType;
-import org.sonar.api.utils.System2;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.DbTester;
-import org.sonar.db.organization.OrganizationDto;
 import org.sonar.db.rule.RuleDefinitionDto;
 import org.sonar.db.rule.RuleDto;
 import org.sonar.server.es.EsTester;
@@ -41,15 +38,14 @@ import static org.assertj.core.api.Assertions.assertThat;
 
 public class RuleIndexerTest {
 
-  private System2 system2 = System2.INSTANCE;
-
   @Rule
   public EsTester esTester = new EsTester(new RuleIndexDefinition(new MapSettings()));
 
   @Rule
-  public DbTester dbTester = DbTester.create(system2);
+  public DbTester dbTester = DbTester.create();
 
   private DbClient dbClient = dbTester.getDbClient();
+  private final RuleIndexer underTest = new RuleIndexer(esTester.client(), dbClient);
   private DbSession dbSession = dbTester.getSession();
   private RuleDefinitionDto rule = new RuleDefinitionDto()
     .setRuleKey("S001")
@@ -66,17 +62,10 @@ public class RuleIndexerTest {
     .setType(RuleType.BUG)
     .setCreatedAt(1500000000000L)
     .setUpdatedAt(1600000000000L);
-  private OrganizationDto organization;
-
-  @Before
-  public void before() {
-    organization = dbTester.getDefaultOrganization();
-  }
 
   @Test
   public void index_nothing() {
-    RuleIndexer indexer = createIndexer();
-//    indexer.index(Iterators.emptyIterator());
+    // underTest.index(Iterators.emptyIterator());
     assertThat(esTester.countDocuments(RuleIndexDefinition.INDEX_TYPE_RULE)).isEqualTo(0L);
   }
 
@@ -85,44 +74,24 @@ public class RuleIndexerTest {
     dbClient.ruleDao().insert(dbSession, rule);
     dbSession.commit();
 
-    RuleIndexer indexer = createIndexer();
-    indexer.index(organization, rule.getKey());
+    underTest.indexRuleDefinition(rule.getKey());
 
     assertThat(esTester.countDocuments(RuleIndexDefinition.INDEX_TYPE_RULE)).isEqualTo(1);
   }
 
   @Test
   public void removed_rule_is_not_removed_from_index() {
-    RuleIndexer indexer = createIndexer();
-
     // Create and Index rule
     dbClient.ruleDao().insert(dbSession, rule.setStatus(RuleStatus.READY));
     dbSession.commit();
-    indexer.index(organization, rule.getKey());
+    underTest.indexRuleDefinition(rule.getKey());
     assertThat(esTester.countDocuments(RuleIndexDefinition.INDEX_TYPE_RULE)).isEqualTo(1);
 
     // Remove rule
     dbTester.getDbClient().ruleDao().update(dbTester.getSession(), rule.setStatus(RuleStatus.READY).setUpdatedAt(2000000000000L));
     dbTester.getSession().commit();
-    indexer.index(organization, rule.getKey());
-
-    assertThat(esTester.countDocuments(RuleIndexDefinition.INDEX_TYPE_RULE)).isEqualTo(1);
-  }
-
-  @Test
-  public void index_on_startup() {
-    RuleIndexer indexer = createIndexer();
-
-    // Create and Index rule
-    dbClient.ruleDao().insert(dbSession, rule.setStatus(RuleStatus.READY));
-    dbSession.commit();
+    underTest.indexRuleDefinition(rule.getKey());
 
-    indexer.indexOnStartup(indexer.getIndexTypes());
     assertThat(esTester.countDocuments(RuleIndexDefinition.INDEX_TYPE_RULE)).isEqualTo(1);
   }
-
-  private RuleIndexer createIndexer() {
-    return new RuleIndexer(esTester.client(), dbClient, new RuleIteratorFactory(dbTester.getDbClient()), TestDefaultOrganizationProvider.from(dbTester));
-  }
-
 }
index 913301972ef006972d9c852a9b337c3039df25bf..0e78b373b40d57430edc73a446524dd785de1745 100644 (file)
@@ -46,12 +46,12 @@ public class RuleIteratorForSingleChunkTest {
 
   private DbClient dbClient = dbTester.getDbClient();
   private DbSession dbSession = dbTester.getSession();
-  private RuleDto templateRule;
+  private RuleDefinitionDto templateRule;
   private RuleDefinitionDto customRule;
 
   @Before
   public void setUp() throws Exception {
-    templateRule = new RuleDto()
+    templateRule = new RuleDefinitionDto()
         .setRuleKey("S001")
         .setRepositoryKey("xoo")
         .setConfigKey("S1")
@@ -65,9 +65,7 @@ public class RuleIteratorForSingleChunkTest {
         .setSystemTags(newHashSet("cwe"))
         .setType(RuleType.BUG)
         .setCreatedAt(1500000000000L)
-        .setUpdatedAt(1600000000000L)
-        .setOrganizationUuid(dbTester.getDefaultOrganization().getUuid())
-        .setTags(newHashSet("performance"));
+        .setUpdatedAt(1600000000000L);
 
     customRule = new RuleDefinitionDto()
         .setRuleKey("S002")
@@ -87,13 +85,15 @@ public class RuleIteratorForSingleChunkTest {
 
   @Test
   public void iterator_over_one_rule() {
-    dbTester.rules().insertRule(templateRule);
+    dbTester.rules().insert(templateRule);
 
-    List<RuleDoc> results = getResults();
+    List<RuleDocWithSystemScope> results = getResults();
 
     assertThat(results).hasSize(1);
 
-    RuleDoc templateDoc = getRuleDoc(results, templateRule.getRuleKey());
+    RuleDocWithSystemScope ruleDocWithSystemScope = getRuleDoc(results, templateRule.getRuleKey());
+    RuleDoc templateDoc = ruleDocWithSystemScope.getRuleDoc();
+    RuleExtensionDoc templateExtensionDoc = ruleDocWithSystemScope.getRuleExtensionDoc();
     assertThat(templateDoc).isNotNull();
     assertThat(templateDoc.key()).isEqualTo(RuleKey.of("xoo", "S001"));
     assertThat(templateDoc.ruleKey()).isEqualTo("S001");
@@ -105,22 +105,24 @@ public class RuleIteratorForSingleChunkTest {
     assertThat(templateDoc.severity()).isEqualTo(Severity.BLOCKER);
     assertThat(templateDoc.status()).isEqualTo(RuleStatus.READY);
     assertThat(templateDoc.isTemplate()).isTrue();
-    assertThat(templateDoc.allTags()).containsOnly("performance", "cwe");
+    assertThat(templateExtensionDoc.getTags()).containsOnly("cwe");
     assertThat(templateDoc.createdAt()).isEqualTo(1500000000000L);
     assertThat(templateDoc.updatedAt()).isEqualTo(1600000000000L);
   }
 
   @Test
   public void iterator_over_rules() {
-    dbTester.rules().insertRule(templateRule);
+    dbTester.rules().insert(templateRule);
     dbClient.ruleDao().insert(dbSession, customRule);
     dbSession.commit();
 
-    List<RuleDoc> results = getResults();
+    List<RuleDocWithSystemScope> results = getResults();
 
     assertThat(results).hasSize(2);
 
-    RuleDoc templateDoc = getRuleDoc(results, templateRule.getRuleKey());
+    RuleDocWithSystemScope templateDocWithSystemScope = getRuleDoc(results, templateRule.getRuleKey());
+    RuleDoc templateDoc = templateDocWithSystemScope.getRuleDoc();
+    RuleExtensionDoc templateExtensionDoc = templateDocWithSystemScope.getRuleExtensionDoc();
     assertThat(templateDoc.key()).isEqualTo(RuleKey.of("xoo", "S001"));
     assertThat(templateDoc.ruleKey()).isEqualTo("S001");
     assertThat(templateDoc.repository()).isEqualTo("xoo");
@@ -131,11 +133,13 @@ public class RuleIteratorForSingleChunkTest {
     assertThat(templateDoc.severity()).isEqualTo(Severity.BLOCKER);
     assertThat(templateDoc.status()).isEqualTo(RuleStatus.READY);
     assertThat(templateDoc.isTemplate()).isTrue();
-    assertThat(templateDoc.allTags()).containsOnly("performance", "cwe");
+    assertThat(templateExtensionDoc.getTags()).containsOnly("cwe");
     assertThat(templateDoc.createdAt()).isEqualTo(1500000000000L);
     assertThat(templateDoc.updatedAt()).isEqualTo(1600000000000L);
 
-    RuleDoc customDoc = getRuleDoc(results, customRule.getRuleKey());
+    RuleDocWithSystemScope customDocWithSystemScope = getRuleDoc(results, customRule.getRuleKey());
+    RuleDoc customDoc = customDocWithSystemScope.getRuleDoc();
+    RuleExtensionDoc customExtensionDoc = customDocWithSystemScope.getRuleExtensionDoc();
     assertThat(customDoc.key()).isEqualTo(RuleKey.of("xoo", "S002"));
     assertThat(customDoc.ruleKey()).isEqualTo("S002");
     assertThat(customDoc.repository()).isEqualTo("xoo");
@@ -146,48 +150,50 @@ public class RuleIteratorForSingleChunkTest {
     assertThat(customDoc.severity()).isEqualTo(Severity.MAJOR);
     assertThat(customDoc.status()).isEqualTo(RuleStatus.BETA);
     assertThat(customDoc.isTemplate()).isFalse();
-    assertThat(customDoc.allTags()).isEmpty();
+    assertThat(customExtensionDoc.getTags()).isEmpty();
     assertThat(customDoc.createdAt()).isEqualTo(2000000000000L);
     assertThat(customDoc.updatedAt()).isEqualTo(2100000000000L);
   }
 
   @Test
   public void custom_rule() {
-    dbTester.rules().insertRule(templateRule);
+    dbTester.rules().insert(templateRule);
     dbClient.ruleDao().insert(dbSession, customRule.setTemplateId(templateRule.getId()));
     dbSession.commit();
 
-    List<RuleDoc> results = getResults();
+    List<RuleDocWithSystemScope> results = getResults();
 
     assertThat(results).hasSize(2);
 
-    RuleDoc templateDoc = getRuleDoc(results, templateRule.getRuleKey());
+    RuleDocWithSystemScope templateDocWithSystemScope = getRuleDoc(results, templateRule.getRuleKey());
+    RuleDoc templateDoc = templateDocWithSystemScope.getRuleDoc();
     assertThat(templateDoc.isTemplate()).isTrue();
     assertThat(templateDoc.templateKey()).isNull();
 
-    RuleDoc customDoc = getRuleDoc(results, customRule.getRuleKey());
+    RuleDocWithSystemScope customDocWithSystemScope = getRuleDoc(results, customRule.getRuleKey());
+    RuleDoc customDoc = customDocWithSystemScope.getRuleDoc();
     assertThat(customDoc.isTemplate()).isFalse();
     assertThat(customDoc.templateKey()).isEqualTo(RuleKey.of("xoo", "S001"));
   }
 
   @Test
   public void removed_rule_is_returned() {
-    dbTester.rules().insertRule(templateRule.setStatus(RuleStatus.REMOVED));
+    dbTester.rules().insert(templateRule.setStatus(RuleStatus.REMOVED));
     dbSession.commit();
 
-    List<RuleDoc> results = getResults();
+    List<RuleDocWithSystemScope> results = getResults();
 
     assertThat(results).hasSize(1);
   }
 
-  private List<RuleDoc> getResults() {
-    return Lists.newArrayList(new RuleIteratorForSingleChunk(dbTester.getDbClient(), dbTester.getDefaultOrganization(), null));
+  private List<RuleDocWithSystemScope> getResults() {
+    return Lists.newArrayList(new RuleIteratorForSingleChunk(dbTester.getDbClient(), null));
   }
 
-  private RuleDoc getRuleDoc(List<RuleDoc> results, String ruleKey) {
-    RuleDoc rule;
+  private RuleDocWithSystemScope getRuleDoc(List<RuleDocWithSystemScope> results, String ruleKey) {
+    RuleDocWithSystemScope rule;
     rule = results.stream()
-      .filter(r -> ruleKey.equals(r.key().rule()))
+      .filter(r -> ruleKey.equals(r.getRuleDoc().key().rule()))
       .findAny()
       .orElseThrow(() -> new NotFoundException("Rule not found in results"));
     return rule;
index 2682cc8e8301a8e833e68d32da4f4c58ffffcce9..2a9152a5556892261ba3772c8bb2d14ee0f2a48e 100644 (file)
@@ -50,8 +50,8 @@ public class ListActionTest {
 
   @Test
   public void return_rules_in_protobuf() throws Exception {
-    dbTester.rules().insertRule(RuleTesting.newDto(RuleKey.of("java", "S001")).setConfigKey(null).setName(null));
-    dbTester.rules().insertRule(RuleTesting.newDto(RuleKey.of("java", "S002")).setConfigKey("I002").setName("Rule Two"));
+    dbTester.rules().insert(RuleTesting.newRule(RuleKey.of("java", "S001")).setConfigKey(null).setName(null));
+    dbTester.rules().insert(RuleTesting.newRule(RuleKey.of("java", "S002")).setConfigKey("I002").setName("Rule Two"));
     dbTester.getSession().commit();
 
     TestResponse response = tester.newRequest()
index c979889d3738c3372eb5a303edb4cb733acf5bf9..a75bf25c85cb5324f67de69741b1071a3880907a 100644 (file)
  */
 package org.sonar.server.rule.ws;
 
-import com.google.common.collect.ImmutableSet;
-import java.util.Collections;
-import java.util.stream.Stream;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.ClassRule;
 import org.junit.Rule;
 import org.junit.Test;
 import org.sonar.api.server.ws.WebService;
-import org.sonar.core.util.stream.MoreCollectors;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.organization.OrganizationDto;
@@ -120,7 +116,7 @@ public class RulesWsMediumTest {
     tester.get(ActiveRuleDao.class).insert(session, activeRuleDto);
 
     session.commit();
-    ruleIndexer.index(defaultOrganization, rule.getKey());
+    ruleIndexer.indexRuleDefinition(rule.getDefinition().getKey());
     activeRuleIndexer.index();
 
     // 1. With Activation
@@ -136,32 +132,4 @@ public class RulesWsMediumTest {
     result = request.execute();
     result.assertJson(this.getClass(), "show_rule_no_active.json");
   }
-
-  @Test
-  public void get_tags() throws Exception {
-    QualityProfileDto profile = QProfileTesting.newXooP1(defaultOrganization);
-    tester.get(QualityProfileDao.class).insert(session, profile);
-
-    RuleDto rule = RuleTesting.newXooX1(defaultOrganization)
-      .setTags(ImmutableSet.of("hello", "world"))
-      .setSystemTags(Collections.<String>emptySet());
-    ruleDao.insert(session, rule.getDefinition());
-    ruleDao.insertOrUpdate(session, rule.getMetadata().setRuleId(rule.getId()));
-
-    RuleDto rule2 = RuleTesting.newXooX2(defaultOrganization)
-      .setTags(ImmutableSet.of("hello", "java"))
-      .setSystemTags(ImmutableSet.of("sys1"));
-    ruleDao.insert(session, rule2.getDefinition());
-    ruleDao.insertOrUpdate(session, rule2.getMetadata().setRuleId(rule2.getId()));
-
-    session.commit();
-    ruleIndexer.index(defaultOrganization, Stream.of(rule, rule2).map(RuleDto::getKey).collect(MoreCollectors.toList()));
-
-    tester.wsTester().newGetRequest(API_ENDPOINT, API_TAGS_METHOD).execute().assertJson(this.getClass(), "get_tags.json");
-    tester.wsTester().newGetRequest(API_ENDPOINT, API_TAGS_METHOD)
-      .setParam("ps", "1").execute().assertJson(this.getClass(), "get_tags_limited.json");
-    tester.wsTester().newGetRequest(API_ENDPOINT, API_TAGS_METHOD)
-      .setParam("q", "ll").execute().assertJson(this.getClass(), "get_tags_filtered.json");
-  }
-
 }
index de5a39d883ba9d1c3b9be9bd50fc12d8fff1dbfa..d625c25a4383015ebf94469cfb34a2b92771718d 100644 (file)
@@ -24,6 +24,7 @@ import java.util.Date;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.ClassRule;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.sonar.api.rule.RuleKey;
@@ -56,6 +57,7 @@ import org.sonar.server.tester.ServerTester;
 import org.sonar.server.tester.UserSessionRule;
 import org.sonar.server.ws.WsTester;
 
+import static java.util.Arrays.asList;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.sonarqube.ws.client.rule.RulesWsParameters.PARAM_ACTIVATION;
 import static org.sonarqube.ws.client.rule.RulesWsParameters.PARAM_AVAILABLE_SINCE;
@@ -208,7 +210,7 @@ public class SearchActionMediumTest {
 
   @Test
   public void return_lang_key_field_when_language_name_is_not_available() throws Exception {
-    insertRule(RuleTesting.newDto(RuleKey.of("other", "rule")).setLanguage("unknown").getDefinition());
+    insertRule(RuleTesting.newRule(RuleKey.of("other", "rule")).setLanguage("unknown"));
     dbSession.commit();
 
     WsTester.TestRequest request = tester.wsTester().newGetRequest(API_ENDPOINT, API_SEARCH_METHOD).setParam(WebService.Param.FIELDS, "langName");
@@ -496,16 +498,13 @@ public class SearchActionMediumTest {
     result.assertJson(this.getClass(), "get_note_as_markdown_and_html.json");
   }
 
+  @Ignore
   @Test
   public void filter_by_tags() throws Exception {
-    insertRule(RuleTesting.newXooX1()
-      .setSystemTags(ImmutableSet.of("tag1"))
-      .getDefinition());
-    insertRule(RuleTesting.newXooX2()
-      .setSystemTags(ImmutableSet.of("tag2"))
-      .getDefinition());
-
-    dbSession.commit();
+    insertRule(RuleTesting.newRule()
+      .setSystemTags(ImmutableSet.of("tag1")));
+    insertRule(RuleTesting.newRule()
+      .setSystemTags(ImmutableSet.of("tag2")));
 
     activeRuleIndexer.index();
 
@@ -640,6 +639,6 @@ public class SearchActionMediumTest {
   private void insertRule(RuleDefinitionDto definition) {
     ruleDao.insert(dbSession, definition);
     dbSession.commit();
-    ruleIndexer.index(defaultOrganizationDto, definition.getKey());
+    ruleIndexer.indexRuleDefinition(definition.getKey());
   }
 }
index 4d4f348bd8853052d3fbb2dff0d7d458a520d0fd..8d073a246b2d99e199df55cdbbdb08dd9e21a58d 100644 (file)
@@ -192,7 +192,7 @@ public class ShowActionMediumTest {
 
   @Test
   public void show_rule_with_no_default_and_no_overridden_debt() throws Exception {
-    RuleDto ruleDto = RuleTesting.newDto(RuleKey.of("java", "S001"))
+    RuleDefinitionDto ruleDto = RuleTesting.newRule(RuleKey.of("java", "S001"))
       .setName("Rule S001")
       .setDescription("Rule S001 <b>description</b>")
       .setDescriptionFormat(Format.HTML)
@@ -203,7 +203,7 @@ public class ShowActionMediumTest {
       .setDefRemediationFunction(null)
       .setDefRemediationGapMultiplier(null)
       .setDefRemediationBaseEffort(null);
-    ruleDao.insert(session, ruleDto.getDefinition());
+    ruleDao.insert(session, ruleDto);
     session.commit();
     session.clearCache();
 
@@ -259,7 +259,7 @@ public class ShowActionMediumTest {
 
   @Test
   public void show_rule_when_activated() throws Exception {
-    RuleDto ruleDto = RuleTesting.newDto(RuleKey.of("java", "S001"))
+    RuleDefinitionDto ruleDto = RuleTesting.newRule(RuleKey.of("java", "S001"))
       .setName("Rule S001")
       .setDescription("Rule S001 <b>description</b>")
       .setDescriptionFormat(Format.HTML)
@@ -269,12 +269,11 @@ public class ShowActionMediumTest {
       .setType(RuleType.BUG)
       .setCreatedAt(new Date().getTime())
       .setUpdatedAt(new Date().getTime());
-    RuleDefinitionDto definition = ruleDto.getDefinition();
-    ruleDao.insert(session, definition);
+    ruleDao.insert(session, ruleDto);
     session.commit();
-    ruleIndexer.index(defaultOrganization, definition.getKey());
-    RuleParamDto regexParam = RuleParamDto.createFor(definition).setName("regex").setType("STRING").setDescription("Reg *exp*").setDefaultValue(".*");
-    ruleDao.insertRuleParam(session, definition, regexParam);
+    ruleIndexer.indexRuleDefinition(ruleDto.getKey());
+    RuleParamDto regexParam = RuleParamDto.createFor(ruleDto).setName("regex").setType("STRING").setDescription("Reg *exp*").setDefaultValue(".*");
+    ruleDao.insertRuleParam(session, ruleDto, regexParam);
 
     QualityProfileDto profile = QualityProfileDto.createFor("profile")
       .setOrganizationUuid(defaultOrganizationProvider.get().getUuid())
diff --git a/server/sonar-server/src/test/java/org/sonar/server/rule/ws/TagsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/rule/ws/TagsActionTest.java
new file mode 100644 (file)
index 0000000..ad368ae
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.rule.ws;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.config.MapSettings;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbTester;
+import org.sonar.db.organization.OrganizationDto;
+import org.sonar.db.rule.RuleDefinitionDto;
+import org.sonar.server.es.EsClient;
+import org.sonar.server.es.EsTester;
+import org.sonar.server.organization.TestDefaultOrganizationProvider;
+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.WsActionTester;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.db.rule.RuleTesting.setSystemTags;
+import static org.sonar.db.rule.RuleTesting.setTags;
+import static org.sonar.test.JsonAssert.assertJson;
+
+public class TagsActionTest {
+
+  @Rule
+  public UserSessionRule userSession = UserSessionRule.standalone();
+  @Rule
+  public DbTester dbTester = DbTester.create();
+  @Rule
+  public EsTester esTester = new EsTester(new RuleIndexDefinition(new MapSettings()));
+
+  private DbClient dbClient = dbTester.getDbClient();
+  private EsClient esClient = esTester.client();
+  private RuleIndex ruleIndex = new RuleIndex(esClient);
+  private RuleIndexer ruleIndexer = new RuleIndexer(esClient, dbClient);
+
+  private WsActionTester tester = new WsActionTester(new org.sonar.server.rule.ws.TagsAction(ruleIndex, dbClient, TestDefaultOrganizationProvider.from(dbTester)));
+  private OrganizationDto organization;
+
+  @Before
+  public void before() {
+    organization = dbTester.organizations().insert();
+  }
+
+  @Test
+  public void test_definition() {
+    WebService.Action action = tester.getDef();
+    assertThat(action.description()).isNotEmpty();
+    assertThat(action.responseExampleAsString()).isNotEmpty();
+    assertThat(action.isPost()).isFalse();
+    assertThat(action.isInternal()).isFalse();
+    assertThat(action.params()).hasSize(3);
+
+    WebService.Param query = action.param("q");
+    assertThat(query).isNotNull();
+    assertThat(query.isRequired()).isFalse();
+    assertThat(query.description()).isNotEmpty();
+    assertThat(query.exampleValue()).isNotEmpty();
+
+    WebService.Param pageSize = action.param("ps");
+    assertThat(pageSize).isNotNull();
+    assertThat(pageSize.isRequired()).isFalse();
+    assertThat(pageSize.defaultValue()).isEqualTo("0");
+    assertThat(pageSize.description()).isNotEmpty();
+    assertThat(pageSize.exampleValue()).isNotEmpty();
+
+    WebService.Param organization = action.param("organization");
+    assertThat(organization).isNotNull();
+    assertThat(organization.isRequired()).isFalse();
+    assertThat(organization.isInternal()).isTrue();
+    assertThat(organization.description()).isNotEmpty();
+    assertThat(organization.exampleValue()).isNotEmpty();
+    assertThat(organization.since()).isEqualTo("6.4");
+  }
+
+  @Test
+  public void return_system_tag() throws Exception {
+    RuleDefinitionDto r = dbTester.rules().insert(setSystemTags("tag"));
+    ruleIndexer.indexRuleDefinition(r.getKey());
+
+    String result = tester.newRequest().execute().getInput();
+    assertJson(result).isSimilarTo("{\"tags\":[\"tag\"]}");
+  }
+
+  @Test
+  public void return_tag() throws Exception {
+    RuleDefinitionDto r = dbTester.rules().insert(setSystemTags());
+    ruleIndexer.indexRuleDefinition(r.getKey());
+    dbTester.rules().insertOrUpdateMetadata(r, organization, setTags("tag"));
+    ruleIndexer.indexRuleExtension(organization, r.getKey());
+
+    String result = tester.newRequest().setParam("organization", organization.getKey()).execute().getInput();
+    assertJson(result).isSimilarTo("{\"tags\":[\"tag\"]}");
+  }
+}
index a01073d3f16a6b1d041a0499dc74972cf643610a..e48b3fcdd83f77d7e3ec0f22fc35b7e24cd26daa 100644 (file)
@@ -125,7 +125,8 @@ public class ViewIndexerTest {
 
   @Test
   public void clear_views_lookup_cache_on_index_view_uuid() {
-    IssueIndex issueIndex = new IssueIndex(esTester.client(), System2.INSTANCE, userSessionRule, new AuthorizationTypeSupport(userSessionRule));
+    IssueIndex issueIndex = new IssueIndex(esTester.client(), System2.INSTANCE, userSessionRule, new AuthorizationTypeSupport(userSessionRule)
+    );
     IssueIndexer issueIndexer = new IssueIndexer(esTester.client(), new IssueIteratorFactory(dbClient));
 
     String viewUuid = "ABCD";
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/rule/ws/RulesWsMediumTest/get_tags.json b/server/sonar-server/src/test/resources/org/sonar/server/rule/ws/RulesWsMediumTest/get_tags.json
deleted file mode 100644 (file)
index c9a14f3..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-{
-  "tags": [
-    "sys1",
-    "java",
-    "world",
-    "hello"
-  ]
-}