]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-7330 Add active rule index definition
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Tue, 23 Feb 2016 15:01:56 +0000 (16:01 +0100)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Mon, 29 Feb 2016 12:26:54 +0000 (13:26 +0100)
34 files changed:
server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel1.java
server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevelStartup.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ActiveRule.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileLoader.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/db/ActiveRuleDao.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleDoc.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleIndex.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleIndex2.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleIndexer.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleNormalizer.java
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleResultSetIterator.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/InheritanceAction.java
server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndex2.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/search/IndexSynchronizer.java
server/sonar-server/src/test/java/org/sonar/server/es/EsTester.java
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/index/ActiveRuleDocTesting.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/index/ActiveRuleIndex2Test.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/index/ActiveRuleIndexerTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/index/ActiveRuleResultSetIteratorTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndex2Test.java
server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndexDefinitionTest.java
server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/index/ActiveRuleIndexerTest/index.xml [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/index/ActiveRuleResultSetIteratorTest/active_rule_with_inherited_inheritance.xml [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/index/ActiveRuleResultSetIteratorTest/active_rule_with_overrides_inheritance.xml [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/index/ActiveRuleResultSetIteratorTest/one_active_rule.xml [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/index/ActiveRuleResultSetIteratorTest/shared.xml [new file with mode: 0644]
sonar-db/src/main/java/org/sonar/db/qualityprofile/ActiveRuleDao.java [new file with mode: 0644]
sonar-db/src/main/java/org/sonar/db/qualityprofile/ActiveRuleDto.java
sonar-db/src/main/java/org/sonar/db/qualityprofile/QualityProfileDao.java
sonar-db/src/main/resources/org/sonar/db/qualityprofile/ActiveRuleMapper.xml
sonar-db/src/test/java/org/sonar/db/qualityprofile/ActiveRuleDaoTest.java [new file with mode: 0644]

index 6493e4d5c9cf435d81cdd3c03a7fbc7ebe8fd98c..52c7f87c18f12da194b8c4154f0d88d68a0fa5f9 100644 (file)
@@ -44,6 +44,7 @@ import org.sonar.server.platform.ServerSettings;
 import org.sonar.server.platform.TempFolderProvider;
 import org.sonar.server.qualityprofile.db.ActiveRuleDao;
 import org.sonar.server.qualityprofile.index.ActiveRuleIndex;
+import org.sonar.server.qualityprofile.index.ActiveRuleIndex2;
 import org.sonar.server.qualityprofile.index.ActiveRuleNormalizer;
 import org.sonar.server.ruby.PlatformRackBridge;
 import org.sonar.server.rule.db.RuleDao;
@@ -110,6 +111,7 @@ public class PlatformLevel1 extends PlatformLevel {
 
       // rules/qprofiles
       RuleIndex2.class,
+      ActiveRuleIndex2.class,
       RuleNormalizer.class,
       ActiveRuleNormalizer.class,
       RuleIndex.class,
index 5bb7a911c2e09ac122884a517b4d1eafba3891c6..8462ff54dd2df2188debe3e8cd40209623516f53 100644 (file)
@@ -223,6 +223,7 @@ import org.sonar.server.qualityprofile.QProfileService;
 import org.sonar.server.qualityprofile.QProfiles;
 import org.sonar.server.qualityprofile.RuleActivator;
 import org.sonar.server.qualityprofile.RuleActivatorContextFactory;
+import org.sonar.server.qualityprofile.index.ActiveRuleIndexer;
 import org.sonar.server.qualityprofile.ws.BackupAction;
 import org.sonar.server.qualityprofile.ws.BulkRuleActivationActions;
 import org.sonar.server.qualityprofile.ws.ChangeParentAction;
@@ -387,6 +388,7 @@ public class PlatformLevel4 extends PlatformLevel {
       UpdateCenterWs.class,
 
       // quality profile
+      ActiveRuleIndexer.class,
       XMLProfileParser.class,
       XMLProfileSerializer.class,
       AnnotationProfileParser.class,
index fcd793206697ae25473d8b8740839815b1bde353..6451012791d4f0b9a8f65ca0b9ce66d0048ea487 100644 (file)
@@ -74,7 +74,6 @@ public class PlatformLevelStartup extends PlatformLevel {
     DoPrivileged.execute(new DoPrivileged.Task(getComponentByType(ThreadLocalUserSession.class)) {
       @Override
       protected void doPrivileged() {
-        getComponentByType(IndexSynchronizer.class).executeDeprecated();
         PlatformLevelStartup.super.start();
         getComponentByType(IndexSynchronizer.class).execute();
         getComponentByType(ServerLifecycleNotifier.class).notifyStart();
index 7be933e2bd138795e07b36fa7216008ff0b12f83..4d497c06eb7a591d1e1eef79c0cee4b5d66f7be6 100644 (file)
 package org.sonar.server.qualityprofile;
 
 import com.google.common.collect.ImmutableList;
-import org.sonar.db.qualityprofile.ActiveRuleKey;
-
-import javax.annotation.CheckForNull;
 import java.util.Date;
 import java.util.List;
 import java.util.Map;
+import javax.annotation.CheckForNull;
+import org.sonar.db.qualityprofile.ActiveRuleKey;
 
 public interface ActiveRule {
 
-  public enum Inheritance {
+  enum Inheritance {
     NONE, OVERRIDES, INHERITED;
     public static final List<Inheritance> ALL = ImmutableList.of(NONE, OVERRIDES, INHERITED);
   }
index 5bd527645bbb665e98d987ffd4787d7b3ae61e0e..7d1b475e92616fa0100e3fc0730f2c7c30c1b0db 100644 (file)
@@ -21,26 +21,24 @@ package org.sonar.server.qualityprofile;
 
 import com.google.common.collect.Lists;
 import com.google.common.collect.Multimap;
-import org.sonar.api.server.ServerSide;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import javax.annotation.CheckForNull;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rule.RuleStatus;
+import org.sonar.api.server.ServerSide;
+import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.qualityprofile.ActiveRuleKey;
 import org.sonar.db.qualityprofile.QualityProfileDto;
-import org.sonar.db.DbClient;
 import org.sonar.server.qualityprofile.index.ActiveRuleIndex;
 import org.sonar.server.rule.index.RuleIndex;
 import org.sonar.server.rule.index.RuleQuery;
 import org.sonar.server.search.FacetValue;
 import org.sonar.server.search.IndexClient;
 import org.sonar.server.search.QueryContext;
-
-import javax.annotation.CheckForNull;
-
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
 import org.sonar.server.user.UserSession;
 
 @ServerSide
@@ -102,10 +100,6 @@ public class QProfileLoader {
     return index.get(ActiveRuleIndex.class).findByProfile(key);
   }
 
-  public long countActiveRulesByProfile(String key) {
-    return index.get(ActiveRuleIndex.class).countByQualityProfileKey(key);
-  }
-
   public Map<String, Long> countAllActiveRules() {
     Map<String, Long> counts = new HashMap<>();
     for (Map.Entry<String, Long> entry : index.get(ActiveRuleIndex.class).countAllByQualityProfileKey().entrySet()) {
@@ -114,10 +108,6 @@ public class QProfileLoader {
     return counts;
   }
 
-  public Multimap<String, FacetValue> getStatsByProfile(String key) {
-    return index.get(ActiveRuleIndex.class).getStatsByProfileKey(key);
-  }
-
   public Map<String, Multimap<String, FacetValue>> getAllProfileStats() {
     List<String> keys = Lists.newArrayList();
     for (QualityProfileDto profile : this.findAll()) {
index 604f077a3013e2b99cfd6f9465433cafd6932218..a9b0656fa4dcbc76aa65774af5d4bf2b401c6afb 100644 (file)
@@ -22,6 +22,8 @@ package org.sonar.server.qualityprofile.db;
 import com.google.common.base.Function;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
+import java.util.List;
+import javax.annotation.CheckForNull;
 import java.util.ArrayList;
 import java.util.List;
 import javax.annotation.CheckForNull;
@@ -44,6 +46,7 @@ import org.sonar.server.search.IndexDefinition;
 
 import static java.util.Collections.emptyList;
 
+@Deprecated
 public class ActiveRuleDao extends BaseDao<ActiveRuleMapper, ActiveRuleDto, ActiveRuleKey> {
 
   private static final String QUALITY_PROFILE_IS_NOT_PERSISTED = "Quality profile is not persisted (missing id)";
index 0400bda41df9174c14b6a21bc1c55c245a4659ee..57fe9150c099ddea42aa17650c01bdb98d1026de 100644 (file)
 package org.sonar.server.qualityprofile.index;
 
 import com.google.common.base.Preconditions;
+import com.google.common.collect.Maps;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Map;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
 import org.sonar.db.qualityprofile.ActiveRuleKey;
 import org.sonar.server.qualityprofile.ActiveRule;
 import org.sonar.server.search.BaseDoc;
 import org.sonar.server.search.IndexUtils;
 
-import javax.annotation.CheckForNull;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+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;
+import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_ACTIVE_RULE_PARENT_KEY;
+import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_ACTIVE_RULE_PROFILE_KEY;
+import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_ACTIVE_RULE_REPOSITORY;
+import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_ACTIVE_RULE_RULE_KEY;
+import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_ACTIVE_RULE_SEVERITY;
+import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_ACTIVE_RULE_UPDATED_AT;
 
 public class ActiveRuleDoc extends BaseDoc implements ActiveRule {
 
@@ -37,33 +47,38 @@ public class ActiveRuleDoc extends BaseDoc implements ActiveRule {
 
   public ActiveRuleDoc(Map<String, Object> fields) {
     super(fields);
-    this.key = ActiveRuleKey.parse((String) getField(ActiveRuleNormalizer.ActiveRuleField.KEY.field()));
-    Preconditions.checkArgument(key!=null, "Invalid ActiveRuleKey!");
+    this.key = ActiveRuleKey.parse((String) getField(FIELD_ACTIVE_RULE_KEY));
+    Preconditions.checkArgument(key != null, "ActiveRuleKey cannot be null");
   }
 
-  @Override
-  public Date createdAt() {
-    return IndexUtils.parseDateTime((String) getNullableField(ActiveRuleNormalizer.ActiveRuleField.CREATED_AT.field()));
-  }
-
-  @Override
-  public Date updatedAt() {
-    return IndexUtils.parseDateTime((String) getNullableField(ActiveRuleNormalizer.ActiveRuleField.UPDATED_AT.field()));
+  public ActiveRuleDoc(ActiveRuleKey key) {
+    super(Maps.<String, Object>newHashMapWithExpectedSize(8));
+    Preconditions.checkNotNull(key, "ActiveRuleKey cannot be null");
+    this.key = key;
+    setField(FIELD_ACTIVE_RULE_KEY, key.toString());
+    setField(FIELD_ACTIVE_RULE_PROFILE_KEY, key.qProfile());
+    setField(FIELD_ACTIVE_RULE_RULE_KEY, key.ruleKey().toString());
+    setField(FIELD_ACTIVE_RULE_REPOSITORY, key.ruleKey().repository());
   }
 
   @Override
   public ActiveRuleKey key() {
-    return this.key;
+    return key;
   }
 
   @Override
   public String severity() {
-    return (String) getNullableField(ActiveRuleNormalizer.ActiveRuleField.SEVERITY.field());
+    return (String) getNullableField(FIELD_ACTIVE_RULE_SEVERITY);
+  }
+
+  public ActiveRuleDoc setSeverity(@Nullable String s) {
+    setField(FIELD_ACTIVE_RULE_SEVERITY, s);
+    return this;
   }
 
   @Override
   public ActiveRule.Inheritance inheritance() {
-    String inheritance = getNullableField(ActiveRuleNormalizer.ActiveRuleField.INHERITANCE.field());
+    String inheritance = getNullableField(FIELD_ACTIVE_RULE_INHERITANCE);
     if (inheritance == null || inheritance.isEmpty() ||
       inheritance.toLowerCase().contains("none")) {
       return Inheritance.NONE;
@@ -76,26 +91,61 @@ public class ActiveRuleDoc extends BaseDoc implements ActiveRule {
     }
   }
 
+  public ActiveRuleDoc setInheritance(@Nullable String s) {
+    setField(FIELD_ACTIVE_RULE_INHERITANCE, s);
+    return this;
+  }
+
   @Override
   @CheckForNull
   public ActiveRuleKey parentKey() {
-    String data = getNullableField(ActiveRuleNormalizer.ActiveRuleField.PARENT_KEY.field());
+    String data = getNullableField(FIELD_ACTIVE_RULE_PARENT_KEY);
     if (data != null && !data.isEmpty()) {
       return ActiveRuleKey.parse(data);
     }
     return null;
   }
 
+  public ActiveRuleDoc setParentKey(@Nullable String s) {
+    setField(FIELD_ACTIVE_RULE_PARENT_KEY, s);
+    return this;
+  }
+
   @Override
+  @Deprecated
   public Map<String, String> params() {
-    Map<String, String> params = new HashMap<>();
-    List<Map<String, String>> allParams = getNullableField(ActiveRuleNormalizer.ActiveRuleField.PARAMS.field());
-    if (allParams != null) {
-      for (Map<String, String> param : allParams) {
-        params.put(param.get(ActiveRuleNormalizer.ActiveRuleParamField.NAME.field()),
-          param.get(ActiveRuleNormalizer.ActiveRuleParamField.VALUE.field()));
-      }
-    }
-    return params;
+    return Collections.emptyMap();
+  }
+
+  @Override
+  @Deprecated
+  public Date createdAt() {
+    return IndexUtils.parseDateTime((String) getNullableField(FIELD_ACTIVE_RULE_CREATED_AT));
+  }
+
+  @CheckForNull
+  public Long createdAtAsLong() {
+    return (Long) getField(FIELD_ACTIVE_RULE_CREATED_AT);
+  }
+
+  public ActiveRuleDoc setCreatedAt(@Nullable Long l) {
+    setField(FIELD_ACTIVE_RULE_CREATED_AT, l);
+    return this;
+  }
+
+  @Override
+  @Deprecated
+  public Date updatedAt() {
+    return IndexUtils.parseDateTime((String) getNullableField(FIELD_ACTIVE_RULE_UPDATED_AT));
+  }
+
+  @CheckForNull
+  public Long updatedAtAsLong() {
+    return (Long) getField(FIELD_ACTIVE_RULE_UPDATED_AT);
+  }
+
+  public ActiveRuleDoc setUpdatedAt(@Nullable Long l) {
+    setField(FIELD_ACTIVE_RULE_UPDATED_AT, l);
+    return this;
   }
 }
index b83cba83e3b40cc8660114178e24a4c433b449c6..1601f33f1eaba4600ae67895f5d82d8c7c55753c 100644 (file)
@@ -19,7 +19,6 @@
  */
 package org.sonar.server.qualityprofile.index;
 
-import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Multimap;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -48,6 +47,7 @@ import org.sonar.server.search.IndexDefinition;
 import org.sonar.server.search.IndexField;
 import org.sonar.server.search.SearchClient;
 
+@Deprecated
 public class ActiveRuleIndex extends BaseIndex<ActiveRule, ActiveRuleDto, ActiveRuleKey> {
 
   public static final String COUNT_ACTIVE_RULES = "countActiveRules";
@@ -138,7 +138,7 @@ public class ActiveRuleIndex extends BaseIndex<ActiveRule, ActiveRuleDto, Active
       .setQuery(QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(), FilterBuilders.boolFilter()
         .must(FilterBuilders.termFilter(ActiveRuleNormalizer.ActiveRuleField.PROFILE_KEY.field(), key))
         .mustNot(FilterBuilders.hasParentFilter(this.getParentType(),
-          FilterBuilders.termFilter(RuleIndexDefinition.FIELD_STATUS, RuleStatus.REMOVED.name())))))
+          FilterBuilders.termFilter(RuleIndexDefinition.FIELD_RULE_STATUS, RuleStatus.REMOVED.name())))))
       .setRouting(key);
 
     SearchResponse response = request.get();
@@ -153,18 +153,14 @@ public class ActiveRuleIndex extends BaseIndex<ActiveRule, ActiveRuleDto, Active
     return countByField(ActiveRuleNormalizer.ActiveRuleField.PROFILE_KEY,
       FilterBuilders.hasParentFilter(IndexDefinition.RULE.getIndexType(),
         FilterBuilders.notFilter(
-          FilterBuilders.termFilter(RuleIndexDefinition.FIELD_STATUS, "REMOVED")))).get(key);
+          FilterBuilders.termFilter(RuleIndexDefinition.FIELD_RULE_STATUS, "REMOVED")))).get(key);
   }
 
   public Map<String, Long> countAllByQualityProfileKey() {
     return countByField(ActiveRuleNormalizer.ActiveRuleField.PROFILE_KEY,
       FilterBuilders.hasParentFilter(IndexDefinition.RULE.getIndexType(),
         FilterBuilders.notFilter(
-          FilterBuilders.termFilter(RuleIndexDefinition.FIELD_STATUS, "REMOVED"))));
-  }
-
-  public Multimap<String, FacetValue> getStatsByProfileKey(String key) {
-    return getStatsByProfileKeys(ImmutableList.of(key)).get(key);
+          FilterBuilders.termFilter(RuleIndexDefinition.FIELD_RULE_STATUS, "REMOVED"))));
   }
 
   public Map<String, Multimap<String, FacetValue>> getStatsByProfileKeys(List<String> keys) {
@@ -173,7 +169,7 @@ public class ActiveRuleIndex extends BaseIndex<ActiveRule, ActiveRuleDto, Active
         QueryBuilders.termsQuery(ActiveRuleNormalizer.ActiveRuleField.PROFILE_KEY.field(), keys),
         FilterBuilders.boolFilter()
           .mustNot(FilterBuilders.hasParentFilter(this.getParentType(),
-            FilterBuilders.termFilter(RuleIndexDefinition.FIELD_STATUS, RuleStatus.REMOVED.name())))))
+            FilterBuilders.termFilter(RuleIndexDefinition.FIELD_RULE_STATUS, RuleStatus.REMOVED.name())))))
       .addAggregation(AggregationBuilders.terms(ActiveRuleNormalizer.ActiveRuleField.PROFILE_KEY.field())
         .field(ActiveRuleNormalizer.ActiveRuleField.PROFILE_KEY.field()).size(0)
         .subAggregation(AggregationBuilders.terms(ActiveRuleNormalizer.ActiveRuleField.INHERITANCE.field())
diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleIndex2.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleIndex2.java
new file mode 100644 (file)
index 0000000..01dcaaf
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.qualityprofile.index;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Multimap;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+import org.elasticsearch.action.get.GetRequestBuilder;
+import org.elasticsearch.action.get.GetResponse;
+import org.elasticsearch.action.search.SearchRequestBuilder;
+import org.elasticsearch.action.search.SearchResponse;
+import org.elasticsearch.action.search.SearchType;
+import org.elasticsearch.common.unit.TimeValue;
+import org.elasticsearch.index.query.FilterBuilder;
+import org.elasticsearch.index.query.FilterBuilders;
+import org.elasticsearch.index.query.QueryBuilders;
+import org.elasticsearch.search.SearchHit;
+import org.elasticsearch.search.aggregations.Aggregation;
+import org.elasticsearch.search.aggregations.AggregationBuilders;
+import org.elasticsearch.search.aggregations.Aggregations;
+import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
+import org.elasticsearch.search.aggregations.bucket.terms.Terms;
+import org.elasticsearch.search.aggregations.metrics.valuecount.InternalValueCount;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rule.RuleStatus;
+import org.sonar.db.qualityprofile.ActiveRuleKey;
+import org.sonar.server.es.BaseIndex;
+import org.sonar.server.es.EsClient;
+import org.sonar.server.qualityprofile.ActiveRule;
+import org.sonar.server.rule.index.RuleIndexDefinition;
+import org.sonar.server.search.FacetValue;
+
+import static org.sonar.server.es.EsUtils.SCROLL_TIME_IN_MINUTES;
+import static org.sonar.server.es.EsUtils.scroll;
+import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_ACTIVE_RULE_INHERITANCE;
+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_STATUS;
+import static org.sonar.server.rule.index.RuleIndexDefinition.INDEX;
+import static org.sonar.server.rule.index.RuleIndexDefinition.TYPE_ACTIVE_RULE;
+import static org.sonar.server.rule.index.RuleIndexDefinition.TYPE_RULE;
+
+/**
+ * The unique entry-point to interact with Elasticsearch index "active rules".
+ * All the requests are listed here.
+ */
+public class ActiveRuleIndex2 extends BaseIndex {
+
+  public static final String COUNT_ACTIVE_RULES = "countActiveRules";
+
+  public ActiveRuleIndex2(EsClient client) {
+    super(client);
+  }
+
+  /**
+   * @deprecated since 5.5, use {@link org.sonar.db.qualityprofile.ActiveRuleDao instead}
+   */
+  @Deprecated
+  @CheckForNull
+  public ActiveRule getNullableByKey(ActiveRuleKey key) {
+    GetRequestBuilder request = getClient().prepareGet()
+      .setIndex(RuleIndexDefinition.INDEX)
+      .setType(RuleIndexDefinition.TYPE_ACTIVE_RULE)
+      .setId(key.toString())
+      .setFetchSource(true)
+      .setRouting(key.ruleKey().repository());
+
+    GetResponse response = request.get();
+    if (response.isExists()) {
+      return new ActiveRuleDoc(response.getSource());
+    }
+    return null;
+  }
+
+  /**
+   * @deprecated since 5.5, use {@link org.sonar.db.qualityprofile.ActiveRuleDao instead}
+   */
+  @Deprecated
+  public List<ActiveRule> findByRule(RuleKey key) {
+    SearchRequestBuilder request = getClient().prepareSearch(RuleIndexDefinition.INDEX)
+      .setQuery(QueryBuilders
+        .hasParentQuery(RuleIndexDefinition.TYPE_RULE,
+          QueryBuilders.idsQuery(RuleIndexDefinition.TYPE_RULE)
+            .addIds(key.toString())
+        ))
+      .setRouting(key.repository())
+      .setSize(Integer.MAX_VALUE);
+
+    SearchResponse response = request.get();
+
+    List<ActiveRule> activeRules = new ArrayList<>();
+    for (SearchHit hit : response.getHits()) {
+      activeRules.add(new ActiveRuleDoc(hit.getSource()));
+    }
+    return activeRules;
+  }
+
+  /**
+   * @deprecated since 5.5, use {@link org.sonar.db.qualityprofile.ActiveRuleDao instead}
+   */
+  @Deprecated
+  public Iterator<ActiveRuleDoc> findByProfile(String key) {
+    SearchRequestBuilder request = getClient().prepareSearch(RuleIndexDefinition.INDEX)
+      .setTypes(RuleIndexDefinition.TYPE_ACTIVE_RULE)
+      .setSearchType(SearchType.SCAN)
+      .setScroll(TimeValue.timeValueMinutes(SCROLL_TIME_IN_MINUTES))
+      .setSize(100)
+      .setQuery(QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(), FilterBuilders.boolFilter()
+        .must(FilterBuilders.termFilter(RuleIndexDefinition.FIELD_ACTIVE_RULE_PROFILE_KEY, key))
+        .mustNot(FilterBuilders.hasParentFilter(RuleIndexDefinition.TYPE_RULE,
+          FilterBuilders.termFilter(RuleIndexDefinition.FIELD_RULE_STATUS, RuleStatus.REMOVED.name())))));
+
+    SearchResponse response = request.get();
+    return scroll(getClient(), response.getScrollId(), ToDoc.INSTANCE);
+  }
+
+  public Map<String, Long> countAllByQualityProfileKey() {
+    return countByField(FIELD_ACTIVE_RULE_PROFILE_KEY,
+      FilterBuilders.hasParentFilter(TYPE_RULE,
+        FilterBuilders.notFilter(
+          FilterBuilders.termFilter(FIELD_RULE_STATUS, "REMOVED"))));
+  }
+
+  private Map<String, Long> countByField(String indexField, FilterBuilder filter) {
+    Map<String, Long> counts = new HashMap<>();
+
+    SearchRequestBuilder request = getClient().prepareSearch(INDEX)
+      .setTypes(TYPE_ACTIVE_RULE)
+      .setQuery(QueryBuilders.filteredQuery(
+        QueryBuilders.matchAllQuery(),
+        filter))
+      .setSize(0)
+      .addAggregation(AggregationBuilders
+        .terms(indexField)
+        .field(indexField)
+        .order(Terms.Order.count(false))
+        .size(Integer.MAX_VALUE)
+        .minDocCount(0));
+
+    SearchResponse response = request.get();
+
+    Terms values =
+      response.getAggregations().get(indexField);
+
+    for (Terms.Bucket value : values.getBuckets()) {
+      counts.put(value.getKey(), value.getDocCount());
+    }
+    return counts;
+  }
+
+  public Map<String, Multimap<String, FacetValue>> getStatsByProfileKeys(List<String> keys) {
+    SearchRequestBuilder request = getClient().prepareSearch(INDEX)
+      .setQuery(QueryBuilders.filteredQuery(
+        QueryBuilders.termsQuery(FIELD_ACTIVE_RULE_PROFILE_KEY, keys),
+        FilterBuilders.boolFilter()
+          .mustNot(FilterBuilders.hasParentFilter(TYPE_RULE,
+            FilterBuilders.termFilter(FIELD_RULE_STATUS, RuleStatus.REMOVED.name())))))
+      .addAggregation(AggregationBuilders.terms(FIELD_ACTIVE_RULE_PROFILE_KEY)
+        .field(RuleIndexDefinition.FIELD_ACTIVE_RULE_PROFILE_KEY).size(0)
+        .subAggregation(AggregationBuilders.terms(FIELD_ACTIVE_RULE_INHERITANCE)
+          .field(RuleIndexDefinition.FIELD_ACTIVE_RULE_INHERITANCE))
+        .subAggregation(AggregationBuilders.terms(FIELD_ACTIVE_RULE_SEVERITY)
+          .field(RuleIndexDefinition.FIELD_ACTIVE_RULE_SEVERITY))
+        .subAggregation(AggregationBuilders.count(COUNT_ACTIVE_RULES)))
+      .setSize(0)
+      .setTypes(TYPE_ACTIVE_RULE);
+    SearchResponse response = request.get();
+    Map<String, Multimap<String, FacetValue>> stats = new HashMap<>();
+    Aggregation aggregation = response.getAggregations().get(FIELD_ACTIVE_RULE_PROFILE_KEY);
+    for (Terms.Bucket value : ((Terms) aggregation).getBuckets()) {
+      stats.put(value.getKey(), processAggregations(value.getAggregations()));
+    }
+
+    return stats;
+  }
+
+  private Multimap<String, FacetValue> processAggregations(Aggregations aggregations) {
+    Multimap<String, FacetValue> stats = ArrayListMultimap.create();
+    if (aggregations != null) {
+      for (Aggregation aggregation : aggregations.asList()) {
+        if (aggregation instanceof StringTerms) {
+          for (Terms.Bucket value : ((Terms) aggregation).getBuckets()) {
+            FacetValue facetValue = new FacetValue(value.getKey(), value.getDocCount());
+            stats.put(aggregation.getName(), facetValue);
+          }
+        } else if (aggregation instanceof InternalValueCount) {
+          InternalValueCount count = (InternalValueCount) aggregation;
+          FacetValue facetValue = new FacetValue(count.getName(), count.getValue());
+          stats.put(count.getName(), facetValue);
+        }
+      }
+    }
+    return stats;
+  }
+
+  private enum ToDoc implements Function<Map<String, Object>, ActiveRuleDoc> {
+    INSTANCE;
+
+    @Override
+    public ActiveRuleDoc apply(@Nonnull Map<String, Object> input) {
+      return new ActiveRuleDoc(input);
+    }
+  }
+
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleIndexer.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleIndexer.java
new file mode 100644 (file)
index 0000000..2a3b5f4
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.qualityprofile.index;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.FluentIterable;
+import java.util.Iterator;
+import java.util.List;
+import javax.annotation.Nonnull;
+import org.elasticsearch.action.index.IndexRequest;
+import org.elasticsearch.action.search.SearchRequestBuilder;
+import org.elasticsearch.index.query.FilterBuilders;
+import org.elasticsearch.index.query.QueryBuilders;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.qualityprofile.ActiveRuleKey;
+import org.sonar.server.es.BaseIndexer;
+import org.sonar.server.es.BulkIndexer;
+import org.sonar.server.es.EsClient;
+import org.sonar.server.qualityprofile.ActiveRuleChange;
+import org.sonar.server.rule.index.RuleIndexDefinition;
+
+import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_ACTIVE_RULE_UPDATED_AT;
+import static org.sonar.server.rule.index.RuleIndexDefinition.INDEX;
+import static org.sonar.server.rule.index.RuleIndexDefinition.TYPE_ACTIVE_RULE;
+
+public class ActiveRuleIndexer extends BaseIndexer {
+
+  private final DbClient dbClient;
+
+  public ActiveRuleIndexer(DbClient dbClient, EsClient esClient) {
+    super(esClient, 300, INDEX, TYPE_ACTIVE_RULE, FIELD_ACTIVE_RULE_UPDATED_AT);
+    this.dbClient = dbClient;
+  }
+
+  @Override
+  protected long doIndex(long lastUpdatedAt) {
+    return doIndex(createBulkIndexer(false), lastUpdatedAt);
+  }
+
+  public void index(Iterator<ActiveRuleDoc> rules) {
+    doIndex(createBulkIndexer(false), rules);
+  }
+
+  private long doIndex(BulkIndexer bulk, long lastUpdatedAt) {
+    DbSession dbSession = dbClient.openSession(false);
+    long maxDate;
+    try {
+      ActiveRuleResultSetIterator rowIt = ActiveRuleResultSetIterator.create(dbClient, dbSession, lastUpdatedAt);
+      maxDate = doIndex(bulk, rowIt);
+      rowIt.close();
+      return maxDate;
+    } finally {
+      dbSession.close();
+    }
+  }
+
+  private long doIndex(BulkIndexer bulk, Iterator<ActiveRuleDoc> activeRules) {
+    bulk.start();
+    long maxDate = 0L;
+    while (activeRules.hasNext()) {
+      ActiveRuleDoc activeRule = activeRules.next();
+      bulk.add(newIndexRequest(activeRule));
+
+      // it's more efficient to sort programmatically than in SQL on some databases (MySQL for instance)
+      maxDate = Math.max(maxDate, activeRule.updatedAtAsLong());
+    }
+    bulk.stop();
+    return maxDate;
+  }
+
+  public void index(List<ActiveRuleChange> changes) {
+    deleteKeys(FluentIterable.from(changes)
+      .filter(MatchDeactivatedRule.INSTANCE)
+      .transform(ActiveRuleChangeToKey.INSTANCE)
+      .toList());
+    index();
+  }
+
+  public void deleteProfile(String qualityProfileKey) {
+    BulkIndexer bulk = new BulkIndexer(esClient, INDEX);
+    bulk.start();
+    SearchRequestBuilder search = esClient.prepareSearch(INDEX)
+      .setTypes(TYPE_ACTIVE_RULE)
+      .setQuery(QueryBuilders.filteredQuery(
+        QueryBuilders.matchAllQuery(),
+        FilterBuilders.boolFilter().must(FilterBuilders.termsFilter(RuleIndexDefinition.FIELD_ACTIVE_RULE_PROFILE_KEY, qualityProfileKey))
+        ));
+    bulk.addDeletion(search);
+    bulk.stop();
+  }
+
+  private void deleteKeys(List<ActiveRuleKey> keys) {
+    BulkIndexer bulk = new BulkIndexer(esClient, INDEX);
+    bulk.start();
+    SearchRequestBuilder search = esClient.prepareSearch(INDEX)
+      .setTypes(TYPE_ACTIVE_RULE)
+      .setQuery(QueryBuilders.filteredQuery(
+        QueryBuilders.matchAllQuery(),
+        FilterBuilders.boolFilter().must(FilterBuilders.termsFilter(RuleIndexDefinition.FIELD_ACTIVE_RULE_KEY, keys))
+        ));
+    bulk.addDeletion(search);
+    bulk.stop();
+  }
+
+  private BulkIndexer createBulkIndexer(boolean large) {
+    BulkIndexer bulk = new BulkIndexer(esClient, INDEX);
+    bulk.setLarge(large);
+    return bulk;
+  }
+
+  private IndexRequest newIndexRequest(ActiveRuleDoc doc) {
+    return new IndexRequest(INDEX, TYPE_ACTIVE_RULE, doc.key().toString())
+      .parent(doc.key().ruleKey().toString())
+      .routing(doc.key().ruleKey().repository())
+      .source(doc.getFields());
+  }
+
+  private enum MatchDeactivatedRule implements Predicate<ActiveRuleChange> {
+    INSTANCE;
+
+    @Override
+    public boolean apply(@Nonnull ActiveRuleChange input) {
+      return input.getType().equals(ActiveRuleChange.Type.DEACTIVATED);
+    }
+  }
+
+  private enum ActiveRuleChangeToKey implements Function<ActiveRuleChange, ActiveRuleKey> {
+    INSTANCE;
+
+    @Override
+    public ActiveRuleKey apply(@Nonnull ActiveRuleChange input) {
+      return input.getKey();
+    }
+  }
+
+}
index 397c84ebfd0ae9d423e434c722c212a355e94f50..d9b09806d84017e863ce75313e15f50df9ef84a7 100644 (file)
@@ -112,9 +112,9 @@ public class ActiveRuleNormalizer extends BaseNormalizer<ActiveRuleDto, ActiveRu
 
       /* Creating updateRequest */
       requests.add(new UpdateRequest()
+        .id(key.toString())
         .routing(key.ruleKey().toString())
-        .id(activeRuleDto.getKey().toString())
-        .parent(activeRuleDto.getKey().ruleKey().toString())
+        .parent(key.ruleKey().toString())
         .doc(newRule)
         .upsert(getUpsertFor(ActiveRuleField.ALL_FIELDS, newRule)));
 
diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleResultSetIterator.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleResultSetIterator.java
new file mode 100644 (file)
index 0000000..1b26270
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.qualityprofile.index;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.ResultSetIterator;
+import org.sonar.db.qualityprofile.ActiveRuleKey;
+import org.sonar.db.rule.SeverityUtil;
+import org.sonar.server.qualityprofile.ActiveRule;
+
+/**
+ * Scrolls over table ACTIVE_RULES and reads documents to populate the active rules index
+ */
+public class ActiveRuleResultSetIterator extends ResultSetIterator<ActiveRuleDoc> {
+
+  private static final String[] FIELDS = {
+    // column 1
+    "a.failure_level",
+    "a.inheritance",
+    "r.plugin_rule_key",
+    "r.plugin_name",
+    "qp.kee",
+    "profile_parent.kee",
+    "a.created_at_ms",
+    "a.updated_at_ms"
+  };
+
+  private static final String SQL_ALL = "SELECT " + StringUtils.join(FIELDS, ",") + " FROM active_rules a " +
+    "INNER JOIN rules_profiles qp ON qp.id=a.profile_id " +
+    "INNER JOIN rules r ON r.id = a.rule_id " +
+    "LEFT JOIN rules_profiles profile_parent ON profile_parent.kee=qp.parent_kee ";
+
+  private static final String SQL_AFTER_DATE = SQL_ALL + " WHERE a.updated_at_ms>?";
+
+  private ActiveRuleResultSetIterator(PreparedStatement stmt) throws SQLException {
+    super(stmt);
+  }
+
+  static ActiveRuleResultSetIterator create(DbClient dbClient, DbSession session, long afterDate) {
+    try {
+      String sql = afterDate > 0L ? SQL_AFTER_DATE : SQL_ALL;
+      PreparedStatement stmt = dbClient.getMyBatis().newScrollingSelectStatement(session, sql);
+      if (afterDate > 0L) {
+        stmt.setLong(1, afterDate);
+      }
+      return new ActiveRuleResultSetIterator(stmt);
+    } catch (SQLException e) {
+      throw new IllegalStateException("Fail to prepare SQL request to select all active rules", e);
+    }
+  }
+
+  @Override
+  protected ActiveRuleDoc read(ResultSet rs) throws SQLException {
+    RuleKey ruleKey = RuleKey.of(rs.getString(4), rs.getString(3));
+    ActiveRuleKey activeRuleKey = ActiveRuleKey.of(rs.getString(5), ruleKey);
+
+    ActiveRuleDoc doc = new ActiveRuleDoc(activeRuleKey);
+
+    // all the fields must be present, even if value is null
+    doc.setSeverity(SeverityUtil.getSeverityFromOrdinal(rs.getInt(1)));
+
+    String inheritance = rs.getString(2);
+    if (inheritance != null) {
+      doc.setInheritance(inheritance);
+    } else {
+      doc.setInheritance(ActiveRule.Inheritance.NONE.name());
+    }
+
+    String parentProfileKey = rs.getString(6);
+    if (parentProfileKey != null) {
+      doc.setParentKey(ActiveRuleKey.of(parentProfileKey, ruleKey).toString());
+    } else {
+      doc.setParentKey(null);
+    }
+
+    doc.setCreatedAt(rs.getLong(7));
+    doc.setUpdatedAt(rs.getLong(8));
+
+    return doc;
+  }
+
+}
index d91276a8232c6c7f03e64485e1b9fe0946bf7c3f..d2c171ae224f040ad6f4316adbbe8b934096f72e 100644 (file)
 package org.sonar.server.qualityprofile.ws;
 
 import com.google.common.collect.Multimap;
+import java.util.List;
+import java.util.Map;
+import javax.annotation.Nullable;
 import org.sonar.api.resources.Languages;
 import org.sonar.api.server.ws.Request;
 import org.sonar.api.server.ws.Response;
 import org.sonar.api.server.ws.WebService.NewAction;
 import org.sonar.api.server.ws.WebService.NewController;
 import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.qualityprofile.QualityProfileDto;
-import org.sonar.db.DbClient;
 import org.sonar.server.exceptions.NotFoundException;
 import org.sonar.server.qualityprofile.QProfile;
 import org.sonar.server.qualityprofile.QProfileFactory;
 import org.sonar.server.qualityprofile.QProfileLoader;
 import org.sonar.server.qualityprofile.QProfileLookup;
-import org.sonar.server.qualityprofile.index.ActiveRuleIndex;
-import org.sonar.server.qualityprofile.index.ActiveRuleNormalizer;
+import org.sonar.server.rule.index.RuleIndexDefinition;
 import org.sonar.server.search.FacetValue;
 
-import javax.annotation.Nullable;
-
-import java.util.List;
-import java.util.Map;
+import static org.sonar.server.qualityprofile.index.ActiveRuleIndex2.COUNT_ACTIVE_RULES;
 
 public class InheritanceAction implements QProfileWsAction {
 
@@ -148,16 +147,16 @@ public class InheritanceAction implements QProfileWsAction {
 
   private Long getActiveRuleCount(Multimap<String, FacetValue> profileStats) {
     Long result = null;
-    if (profileStats.containsKey(ActiveRuleIndex.COUNT_ACTIVE_RULES)) {
-      result = profileStats.get(ActiveRuleIndex.COUNT_ACTIVE_RULES).iterator().next().getValue();
+    if (profileStats.containsKey(COUNT_ACTIVE_RULES)) {
+      result = profileStats.get(COUNT_ACTIVE_RULES).iterator().next().getValue();
     }
     return result;
   }
 
   private Long getOverridingRuleCount(Multimap<String, FacetValue> profileStats) {
     Long result = null;
-    if (profileStats.containsKey(ActiveRuleNormalizer.ActiveRuleField.INHERITANCE.field())) {
-      for (FacetValue value : profileStats.get(ActiveRuleNormalizer.ActiveRuleField.INHERITANCE.field())) {
+    if (profileStats.containsKey(RuleIndexDefinition.FIELD_ACTIVE_RULE_INHERITANCE)) {
+      for (FacetValue value : profileStats.get(RuleIndexDefinition.FIELD_ACTIVE_RULE_INHERITANCE)) {
         if ("OVERRIDES".equals(value.getKey())) {
           result = value.getValue();
         }
index 2fbb0fcef1da7d40b585283c648e395cdec00c21..498cbc4db398466d102941176bc14f8be1756d07 100644 (file)
@@ -64,8 +64,6 @@ import org.sonar.server.es.BaseIndex;
 import org.sonar.server.es.EsClient;
 import org.sonar.server.es.SearchIdResult;
 import org.sonar.server.es.SearchOptions;
-import org.sonar.server.qualityprofile.index.ActiveRuleNormalizer;
-import org.sonar.server.search.IndexDefinition;
 import org.sonar.server.search.IndexField;
 import org.sonar.server.search.StickyFacetBuilder;
 
@@ -276,9 +274,9 @@ public class RuleIndex2 extends BaseIndex {
 
     // ActiveRule Filter (profile and inheritance)
     BoolFilterBuilder childrenFilter = FilterBuilders.boolFilter();
-    this.addTermFilter(childrenFilter, ActiveRuleNormalizer.ActiveRuleField.PROFILE_KEY.field(), query.getQProfileKey());
-    this.addTermFilter(childrenFilter, ActiveRuleNormalizer.ActiveRuleField.INHERITANCE.field(), query.getInheritance());
-    this.addTermFilter(childrenFilter, ActiveRuleNormalizer.ActiveRuleField.SEVERITY.field(), query.getActiveSeverities());
+    addTermFilter(childrenFilter, RuleIndexDefinition.FIELD_ACTIVE_RULE_PROFILE_KEY, query.getQProfileKey());
+    addTermFilter(childrenFilter, RuleIndexDefinition.FIELD_ACTIVE_RULE_INHERITANCE, query.getInheritance());
+    addTermFilter(childrenFilter, RuleIndexDefinition.FIELD_ACTIVE_RULE_SEVERITY, query.getActiveSeverities());
 
     // ChildQuery
     FilterBuilder childQuery;
@@ -291,12 +289,12 @@ public class RuleIndex2 extends BaseIndex {
     /** Implementation of activation query */
     if (Boolean.TRUE.equals(query.getActivation())) {
       filters.put("activation",
-        FilterBuilders.hasChildFilter(IndexDefinition.ACTIVE_RULE.getIndexType(),
+        FilterBuilders.hasChildFilter(RuleIndexDefinition.TYPE_ACTIVE_RULE,
           childQuery));
     } else if (Boolean.FALSE.equals(query.getActivation())) {
       filters.put("activation",
         FilterBuilders.boolFilter().mustNot(
-          FilterBuilders.hasChildFilter(IndexDefinition.ACTIVE_RULE.getIndexType(),
+          FilterBuilders.hasChildFilter(RuleIndexDefinition.TYPE_ACTIVE_RULE,
             childQuery)));
     }
 
@@ -383,13 +381,13 @@ public class RuleIndex2 extends BaseIndex {
       // so the rule filter has to be used as parent filter for active rules
       // from which we remove filters that concern active rules ("activation")
       HasParentFilterBuilder ruleFilter = FilterBuilders.hasParentFilter(
-        IndexDefinition.RULE.getIndexType(),
+        RuleIndexDefinition.TYPE_RULE,
         stickyFacetBuilder.getStickyFacetFilter("activation"));
 
       // Rebuilding the active rule filter without severities
       BoolFilterBuilder childrenFilter = FilterBuilders.boolFilter();
-      this.addTermFilter(childrenFilter, ActiveRuleNormalizer.ActiveRuleField.PROFILE_KEY.field(), query.getQProfileKey());
-      this.addTermFilter(childrenFilter, ActiveRuleNormalizer.ActiveRuleField.INHERITANCE.field(), query.getInheritance());
+      this.addTermFilter(childrenFilter, RuleIndexDefinition.FIELD_ACTIVE_RULE_PROFILE_KEY, query.getQProfileKey());
+      this.addTermFilter(childrenFilter, RuleIndexDefinition.FIELD_ACTIVE_RULE_INHERITANCE, query.getInheritance());
       FilterBuilder activeRuleFilter;
       if (childrenFilter.hasClauses()) {
         activeRuleFilter = childrenFilter.must(ruleFilter);
@@ -398,13 +396,13 @@ public class RuleIndex2 extends BaseIndex {
       }
 
       AggregationBuilder activeSeverities = AggregationBuilders.children(FACET_ACTIVE_SEVERITIES + "_children")
-        .childType(IndexDefinition.ACTIVE_RULE.getIndexType())
+        .childType(RuleIndexDefinition.TYPE_ACTIVE_RULE)
         .subAggregation(AggregationBuilders.filter(FACET_ACTIVE_SEVERITIES + "_filter")
           .filter(activeRuleFilter)
           .subAggregation(
             AggregationBuilders
               .terms(FACET_ACTIVE_SEVERITIES)
-              .field(ActiveRuleNormalizer.ActiveRuleField.SEVERITY.field())
+              .field(RuleIndexDefinition.FIELD_ACTIVE_RULE_SEVERITY)
               .include(Joiner.on('|').join(Severity.ALL))
               .size(Severity.ALL.size())));
 
index 4096edfcece19a3eeedea5695ff11076d522f324..158c34afc2f9abbff5426bc6849554b2f5d670e6 100644 (file)
@@ -58,6 +58,19 @@ public class RuleIndexDefinition implements IndexDefinition {
     RuleIndexDefinition.FIELD_RULE_KEY
   );
 
+  // Active rule fields
+
+  public static final String TYPE_ACTIVE_RULE = "activeRule";
+  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";
+  public static final String FIELD_ACTIVE_RULE_PROFILE_KEY = "profile";
+  public static final String FIELD_ACTIVE_RULE_SEVERITY = "severity";
+  public static final String FIELD_ACTIVE_RULE_PARENT_KEY = "parentKey";
+  public static final String FIELD_ACTIVE_RULE_RULE_KEY = "ruleKey";
+  public static final String FIELD_ACTIVE_RULE_CREATED_AT = "createdAt";
+  public static final String FIELD_ACTIVE_RULE_UPDATED_AT = "updatedAt";
+
   private final Settings settings;
 
   public RuleIndexDefinition(Settings settings) {
@@ -96,5 +109,22 @@ public class RuleIndexDefinition implements IndexDefinition {
 
     ruleMapping.createLongField(FIELD_RULE_CREATED_AT);
     ruleMapping.createLongField(FIELD_RULE_UPDATED_AT);
+
+    // Active rule type
+    NewIndex.NewIndexType activeRuleMapping = index.createType(RuleIndexDefinition.TYPE_ACTIVE_RULE);
+    activeRuleMapping.setAttribute("_id", ImmutableMap.of("path", RuleIndexDefinition.FIELD_ACTIVE_RULE_KEY));
+    activeRuleMapping.setAttribute("_parent", ImmutableMap.of("type", RuleIndexDefinition.TYPE_RULE));
+    activeRuleMapping.setAttribute("_routing", ImmutableMap.of("required", true, "path", RuleIndexDefinition.FIELD_ACTIVE_RULE_REPOSITORY));
+
+    activeRuleMapping.stringFieldBuilder(RuleIndexDefinition.FIELD_ACTIVE_RULE_KEY).enableSorting().enableGramSearch().build();
+    activeRuleMapping.stringFieldBuilder(RuleIndexDefinition.FIELD_ACTIVE_RULE_RULE_KEY).disableSearch().docValues().build();
+    activeRuleMapping.stringFieldBuilder(RuleIndexDefinition.FIELD_ACTIVE_RULE_REPOSITORY).disableSearch().docValues().build();
+    activeRuleMapping.stringFieldBuilder(RuleIndexDefinition.FIELD_ACTIVE_RULE_PROFILE_KEY).docValues().build();
+    activeRuleMapping.stringFieldBuilder(RuleIndexDefinition.FIELD_ACTIVE_RULE_INHERITANCE).docValues().build();
+    activeRuleMapping.stringFieldBuilder(RuleIndexDefinition.FIELD_ACTIVE_RULE_SEVERITY).docValues().build();
+    activeRuleMapping.stringFieldBuilder(RuleIndexDefinition.FIELD_ACTIVE_RULE_PARENT_KEY).disableSearch().docValues().build();
+
+    activeRuleMapping.createLongField(RuleIndexDefinition.FIELD_ACTIVE_RULE_CREATED_AT);
+    activeRuleMapping.createLongField(RuleIndexDefinition.FIELD_ACTIVE_RULE_UPDATED_AT);
   }
 }
index 01ff26443e4557e1d32bb6b50809302a89ad39ac..88ce05d3153709967cf3db66f717957f1891e8c1 100644 (file)
@@ -19,7 +19,6 @@
  */
 package org.sonar.server.rule.index;
 
-import com.google.common.annotations.VisibleForTesting;
 import java.util.Iterator;
 import org.elasticsearch.action.index.IndexRequest;
 import org.sonar.db.DbClient;
@@ -46,8 +45,7 @@ public class RuleIndexer extends BaseIndexer {
     return doIndex(createBulkIndexer(false), lastUpdatedAt);
   }
 
-  @VisibleForTesting
-  void index(Iterator<RuleDoc> rules) {
+  public void index(Iterator<RuleDoc> rules) {
     doIndex(createBulkIndexer(false), rules);
   }
 
@@ -70,11 +68,11 @@ public class RuleIndexer extends BaseIndexer {
     while (rules.hasNext()) {
       RuleDoc rule = rules.next();
       // TODO when active rule is not more DAO v2, restore deleting of REMOVED rules and also remove active rules linked to this rule
-//      if (rule.status() == RuleStatus.REMOVED) {
-//        bulk.add(newDeleteRequest(rule));
-//      } else {
-//      }
-      bulk.add(newUpsertRequest(rule));
+      // if (rule.status() == RuleStatus.REMOVED) {
+      // bulk.add(newDeleteRequest(rule));
+      // } else {
+      // }
+      bulk.add(newIndexRequest(rule));
 
       // it's more efficient to sort programmatically than in SQL on some databases (MySQL for instance)
       maxDate = Math.max(maxDate, rule.updatedAtAtAsLong());
@@ -89,15 +87,15 @@ public class RuleIndexer extends BaseIndexer {
     return bulk;
   }
 
-  private IndexRequest newUpsertRequest(RuleDoc rule) {
+  private IndexRequest newIndexRequest(RuleDoc rule) {
     return new IndexRequest(INDEX, TYPE_RULE, rule.key().toString())
       .routing(rule.repository())
       .source(rule.getFields());
   }
 
-//  private DeleteRequest newDeleteRequest(RuleDoc rule) {
-//    return new DeleteRequest(INDEX, TYPE_RULE, rule.key().toString())
-//      .routing(rule.repository());
-//  }
+  // private DeleteRequest newDeleteRequest(RuleDoc rule) {
+  // return new DeleteRequest(INDEX, TYPE_RULE, rule.key().toString())
+  // .routing(rule.repository());
+  // }
 
 }
index 25e4032fd4249129fec47a690f4e6b47060c73be..657d027a60b4fd5a7de58acdf62ed83aa96a3ed4 100644 (file)
  */
 package org.sonar.server.search;
 
-import java.util.Date;
 import org.sonar.api.config.Settings;
 import org.sonar.api.utils.log.Logger;
 import org.sonar.api.utils.log.Loggers;
-import org.sonar.db.DbSession;
 import org.sonar.server.activity.index.ActivityIndexer;
-import org.sonar.server.db.DbClient;
-import org.sonar.server.db.DeprecatedDao;
 import org.sonar.server.issue.index.IssueAuthorizationIndexer;
 import org.sonar.server.issue.index.IssueIndexer;
-import org.sonar.server.qualityprofile.index.ActiveRuleIndex;
 import org.sonar.server.test.index.TestIndexer;
 import org.sonar.server.user.index.UserIndexer;
 import org.sonar.server.view.index.ViewIndexer;
@@ -38,8 +33,6 @@ public class IndexSynchronizer {
 
   private static final Logger LOG = Loggers.get(IndexSynchronizer.class);
 
-  private final DbClient db;
-  private final IndexClient index;
   private final TestIndexer testIndexer;
   private final IssueAuthorizationIndexer issueAuthorizationIndexer;
   private final IssueIndexer issueIndexer;
@@ -53,12 +46,9 @@ public class IndexSynchronizer {
    * because we need {@link org.sonar.server.issue.index.IssueAuthorizationIndexer} to be executed before
    * {@link org.sonar.server.issue.index.IssueIndexer}
    */
-  public IndexSynchronizer(DbClient db, IndexClient index,
-    TestIndexer testIndexer, IssueAuthorizationIndexer issueAuthorizationIndexer, IssueIndexer issueIndexer,
+  public IndexSynchronizer(TestIndexer testIndexer, IssueAuthorizationIndexer issueAuthorizationIndexer, IssueIndexer issueIndexer,
     UserIndexer userIndexer, ViewIndexer viewIndexer, ActivityIndexer activityIndexer,
     Settings settings) {
-    this.db = db;
-    this.index = index;
     this.testIndexer = testIndexer;
     this.issueAuthorizationIndexer = issueAuthorizationIndexer;
     this.issueIndexer = issueIndexer;
@@ -68,17 +58,6 @@ public class IndexSynchronizer {
     this.settings = settings;
   }
 
-  public void executeDeprecated() {
-    DbSession session = db.openSession(false);
-    try {
-      // synchronize(session, db.deprecatedRuleDao(), index.get(RuleIndex.class));
-      synchronize(session, db.activeRuleDao(), index.get(ActiveRuleIndex.class));
-      session.commit();
-    } finally {
-      session.close();
-    }
-  }
-
   public void execute() {
     if (!settings.getBoolean("sonar.internal.es.disableIndexes")) {
       LOG.info("Index activities");
@@ -99,14 +78,4 @@ public class IndexSynchronizer {
     }
   }
 
-  void synchronize(DbSession session, DeprecatedDao dao, Index index) {
-    long count = index.getIndexStat().getDocumentCount();
-    Date lastSynch = index.getLastSynchronization();
-    LOG.info("Index {}s", index.getIndexType());
-    if (count <= 0) {
-      dao.synchronizeAfter(session);
-    } else {
-      dao.synchronizeAfter(session, lastSynch);
-    }
-  }
 }
index d32c82f7b1b60b0acc7e8e87bddefbb6ec932e8d..09970f7b63d43a42c4591ef3fb0e8be92dd14b97 100644 (file)
@@ -222,7 +222,7 @@ public class EsTester extends ExternalResource {
     }));
   }
 
-  public List<String> getIds(String indexName, String typeName){
+  public List<String> getIds(String indexName, String typeName) {
     return FluentIterable.from(getDocuments(indexName, typeName)).transform(SearchHitToId.INSTANCE).toList();
   }
 
@@ -234,7 +234,7 @@ public class EsTester extends ExternalResource {
     return client;
   }
 
-  private enum SearchHitToId implements Function<SearchHit, String>{
+  private enum SearchHitToId implements Function<SearchHit, String> {
     INSTANCE;
 
     @Override
diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/index/ActiveRuleDocTesting.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/index/ActiveRuleDocTesting.java
new file mode 100644 (file)
index 0000000..9bc6811
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.qualityprofile.index;
+
+import org.sonar.api.rule.Severity;
+import org.sonar.db.qualityprofile.ActiveRuleKey;
+import org.sonar.db.rule.RuleTesting;
+
+public class ActiveRuleDocTesting {
+
+  public static ActiveRuleDoc newDoc() {
+    return newDoc(ActiveRuleKey.of("sonar-way", RuleTesting.XOO_X1));
+  }
+
+  public static ActiveRuleDoc newDoc(ActiveRuleKey key) {
+    return new ActiveRuleDoc(key)
+      .setSeverity(Severity.CRITICAL)
+      .setParentKey(null)
+      .setInheritance(null).setCreatedAt(150000000L)
+      .setUpdatedAt(160000000L);
+  }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/index/ActiveRuleIndex2Test.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/index/ActiveRuleIndex2Test.java
new file mode 100644 (file)
index 0000000..28db5d4
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.qualityprofile.index;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.sonar.api.config.Settings;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rule.RuleStatus;
+import org.sonar.db.qualityprofile.ActiveRuleKey;
+import org.sonar.db.rule.RuleTesting;
+import org.sonar.server.es.EsTester;
+import org.sonar.server.qualityprofile.ActiveRule;
+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.search.FacetValue;
+
+import static java.util.Arrays.asList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.entry;
+import static org.sonar.api.rule.Severity.BLOCKER;
+import static org.sonar.api.rule.Severity.MAJOR;
+import static org.sonar.api.rule.Severity.MINOR;
+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.RuleIndexDefinition.INDEX;
+import static org.sonar.server.rule.index.RuleIndexDefinition.TYPE_ACTIVE_RULE;
+
+public class ActiveRuleIndex2Test {
+
+  static final RuleKey RULE_KEY_1 = RuleTesting.XOO_X1;
+  static final RuleKey RULE_KEY_2 = RuleTesting.XOO_X2;
+
+  static final String QUALITY_PROFILE_KEY1 = "qp1";
+  static final String QUALITY_PROFILE_KEY2 = "qp2";
+
+  @ClassRule
+  public static EsTester tester = new EsTester().addDefinitions(new RuleIndexDefinition(new Settings()));
+
+  ActiveRuleIndex2 index;
+
+  ActiveRuleIndexer activeRuleIndexer;
+  RuleIndexer ruleIndexer;
+
+  @Before
+  public void setUp() {
+    tester.truncateIndices();
+    activeRuleIndexer = new ActiveRuleIndexer(null, tester.client());
+    ruleIndexer = new RuleIndexer(null, tester.client());
+    index = new ActiveRuleIndex2(tester.client());
+  }
+
+  @Test
+  public void count_all_by_quality_profile_key() {
+    indexRules(RuleDocTesting.newDoc(RULE_KEY_1));
+
+    indexActiveRules(
+      ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY1, RULE_KEY_1)),
+      ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY2, RULE_KEY_1)));
+
+    // 0. Test base case
+    assertThat(tester.countDocuments(INDEX, TYPE_ACTIVE_RULE)).isEqualTo(2);
+
+    // 1. Assert by term aggregation;
+    assertThat(index.countAllByQualityProfileKey()).containsOnly(entry(QUALITY_PROFILE_KEY1, 1L), entry(QUALITY_PROFILE_KEY2, 1L));
+  }
+
+  @Test
+  public void stats_for_all() {
+    indexRules(
+      newDoc(RULE_KEY_1),
+      newDoc(RULE_KEY_2));
+
+    ActiveRuleKey activeRuleKey1 = ActiveRuleKey.of(QUALITY_PROFILE_KEY1, RULE_KEY_1);
+    ActiveRuleKey activeRuleKey2 = ActiveRuleKey.of(QUALITY_PROFILE_KEY1, RULE_KEY_2);
+
+    indexActiveRules(
+      ActiveRuleDocTesting.newDoc(activeRuleKey1).setSeverity(BLOCKER),
+      ActiveRuleDocTesting.newDoc(activeRuleKey2).setSeverity(MINOR),
+      // Profile 2 is a child a profile 1
+      ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY2, RULE_KEY_1)).setSeverity(MAJOR)
+        .setParentKey(activeRuleKey1.toString()).setInheritance(INHERITED.name()),
+      ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY2, RULE_KEY_2)).setSeverity(BLOCKER)
+        .setParentKey(activeRuleKey2.toString()).setInheritance(OVERRIDES.name()));
+
+    // 0. Test base case
+    assertThat(tester.countDocuments(INDEX, TYPE_ACTIVE_RULE)).isEqualTo(4);
+
+    // 1. Assert by term aggregation;
+    Map<String, Multimap<String, FacetValue>> stats = index.getStatsByProfileKeys(ImmutableList.of(QUALITY_PROFILE_KEY1, QUALITY_PROFILE_KEY2));
+    assertThat(stats).hasSize(2);
+  }
+
+  /**
+   * SONAR-5844
+   */
+  @Test
+  public void stats_for_all_with_lof_of_profiles() {
+    indexRules(RuleDocTesting.newDoc(RULE_KEY_1), RuleDocTesting.newDoc(RULE_KEY_2));
+
+    indexActiveRules(
+      ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY1, RULE_KEY_1)),
+      ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY2, RULE_KEY_1)));
+
+    List<String> profileKeys = new ArrayList<>();
+    List<ActiveRuleDoc> docs = new ArrayList<>();
+    for (int i = 0; i < 30; i++) {
+      String profileKey = "profile-" + i;
+      profileKeys.add(profileKey);
+      docs.add(ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(profileKey, RULE_KEY_1)).setSeverity(BLOCKER));
+      docs.add(ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(profileKey, RULE_KEY_2)).setSeverity(MAJOR));
+    }
+    indexActiveRules(docs.toArray(new ActiveRuleDoc[]{}));
+
+    Map<String, Multimap<String, FacetValue>> stats = index.getStatsByProfileKeys(profileKeys);
+    assertThat(stats).hasSize(30);
+  }
+
+  @Test
+  public void get_by_key() {
+    indexRules(RuleDocTesting.newDoc(RULE_KEY_1));
+    ActiveRuleKey key = ActiveRuleKey.of(QUALITY_PROFILE_KEY1, RULE_KEY_1);
+    indexActiveRules(ActiveRuleDocTesting.newDoc(key));
+
+    assertThat(index.getNullableByKey(key)).isNotNull();
+    assertThat(index.getNullableByKey(ActiveRuleKey.of(QUALITY_PROFILE_KEY1, RULE_KEY_2))).isNull();
+  }
+
+  @Test
+  public void find_active_rules() {
+    indexRules(
+      RuleDocTesting.newDoc(RULE_KEY_1),
+      RuleDocTesting.newDoc(RULE_KEY_2),
+      RuleDocTesting.newDoc(RuleKey.of("xoo", "removed")).setStatus(RuleStatus.REMOVED.name())
+    );
+
+    indexActiveRules(
+      ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY1, RULE_KEY_1)),
+      ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY1, RULE_KEY_2)),
+      ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY2, RULE_KEY_2)),
+      // Removed rule can still be activated for instance when removing the checkstyle plugin, active rules related on checkstyle are not
+      // removed
+      // because if the plugin is re-install, quality profiles using these rule are not changed.
+      ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY2, RuleKey.of("xoo", "removed")))
+    );
+
+    // 1. find by rule key
+
+    // in es
+    List<ActiveRule> activeRules = index.findByRule(RULE_KEY_1);
+    assertThat(activeRules).hasSize(1);
+    assertThat(activeRules.get(0).key().ruleKey()).isEqualTo(RULE_KEY_1);
+
+    activeRules = index.findByRule(RULE_KEY_2);
+    assertThat(activeRules).hasSize(2);
+    assertThat(activeRules.get(0).key().ruleKey()).isEqualTo(RULE_KEY_2);
+
+    activeRules = index.findByRule(RuleKey.of("unknown", "unknown"));
+    assertThat(activeRules).isEmpty();
+
+    // 2. find by profile
+    List<ActiveRuleDoc> activeRuleDocs = Lists.newArrayList(index.findByProfile(QUALITY_PROFILE_KEY1));
+    assertThat(activeRuleDocs).hasSize(2);
+    assertThat(activeRuleDocs.get(0).key().qProfile()).isEqualTo(QUALITY_PROFILE_KEY1);
+    assertThat(activeRuleDocs.get(1).key().qProfile()).isEqualTo(QUALITY_PROFILE_KEY1);
+
+    activeRuleDocs = Lists.newArrayList(index.findByProfile(QUALITY_PROFILE_KEY2));
+    assertThat(activeRuleDocs).hasSize(1);
+    assertThat(activeRuleDocs.get(0).key().qProfile()).isEqualTo(QUALITY_PROFILE_KEY2);
+
+    activeRuleDocs = Lists.newArrayList(index.findByProfile("unknown"));
+    assertThat(activeRuleDocs).isEmpty();
+  }
+
+  @Test
+  public void find_many_active_rules_by_profile() {
+    int nb = 150;
+    RuleDoc[] ruleDocs = new RuleDoc[nb];
+    ActiveRuleDoc[] activeRuleDocs = new ActiveRuleDoc[nb];
+    for (int i = 0; i < nb; i++) {
+      RuleKey ruleKey = RuleKey.of("xoo", "S00" + i);
+      ruleDocs[i] = RuleDocTesting.newDoc(ruleKey);
+      activeRuleDocs[i] = ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY1, ruleKey));
+    }
+    indexRules(ruleDocs);
+    indexActiveRules(activeRuleDocs);
+
+    // verify index
+    assertThat(index.findByProfile(QUALITY_PROFILE_KEY1)).hasSize(nb);
+  }
+
+  @Test
+  public void find_many_active_rules_by_rule() {
+    indexRules(RuleDocTesting.newDoc(RULE_KEY_1));
+
+    int nb = 150;
+    ActiveRuleDoc[] activeRuleDocs = new ActiveRuleDoc[nb];
+    for (int i = 0; i < nb; i++) {
+      activeRuleDocs[i] = ActiveRuleDocTesting.newDoc(ActiveRuleKey.of("qp" + i, RULE_KEY_1));
+    }
+    indexActiveRules(activeRuleDocs);
+
+    // verify index
+    assertThat(index.findByRule(RULE_KEY_1)).hasSize(nb);
+  }
+
+  private void indexActiveRules(ActiveRuleDoc... docs) {
+    activeRuleIndexer.index(asList(docs).iterator());
+  }
+
+  private void indexRules(RuleDoc... rules) {
+    ruleIndexer.index(asList(rules).iterator());
+  }
+
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/index/ActiveRuleIndexerTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/index/ActiveRuleIndexerTest.java
new file mode 100644 (file)
index 0000000..3fbd501
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.qualityprofile.index;
+
+import com.google.common.collect.Iterators;
+import java.util.Arrays;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.sonar.api.config.Settings;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rule.Severity;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+import org.sonar.db.qualityprofile.ActiveRuleDto;
+import org.sonar.db.qualityprofile.ActiveRuleKey;
+import org.sonar.db.qualityprofile.QualityProfileDto;
+import org.sonar.db.rule.RuleDto;
+import org.sonar.db.rule.RuleTesting;
+import org.sonar.server.es.EsTester;
+import org.sonar.server.qualityprofile.ActiveRuleChange;
+import org.sonar.server.rule.index.RuleIndexDefinition;
+import org.sonar.test.DbTests;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.singletonList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.server.qualityprofile.ActiveRuleChange.Type.ACTIVATED;
+import static org.sonar.server.qualityprofile.ActiveRuleChange.Type.DEACTIVATED;
+import static org.sonar.server.qualityprofile.index.ActiveRuleDocTesting.newDoc;
+import static org.sonar.server.rule.index.RuleIndexDefinition.INDEX;
+import static org.sonar.server.rule.index.RuleIndexDefinition.TYPE_ACTIVE_RULE;
+
+@Category(DbTests.class)
+public class ActiveRuleIndexerTest {
+
+  static final RuleKey RULE_KEY_1 = RuleTesting.XOO_X1;
+  static final RuleKey RULE_KEY_2 = RuleTesting.XOO_X2;
+  static final RuleKey RULE_KEY_3 = RuleTesting.XOO_X3;
+
+  static final String QUALITY_PROFILE_KEY1 = "qp1";
+  static final String QUALITY_PROFILE_KEY2 = "qp2";
+
+  @ClassRule
+  public static EsTester esTester = new EsTester().addDefinitions(new RuleIndexDefinition(new Settings()));
+
+  @Rule
+  public DbTester dbTester = DbTester.create(System2.INSTANCE);
+
+  ActiveRuleIndexer indexer;
+
+  @Before
+  public void setUp() {
+    esTester.truncateIndices();
+    indexer = new ActiveRuleIndexer(dbTester.getDbClient(), esTester.client());
+    indexer.setEnabled(true);
+  }
+
+  @Test
+  public void index_nothing() {
+    indexer.index(Iterators.<ActiveRuleDoc>emptyIterator());
+    assertThat(esTester.countDocuments(INDEX, TYPE_ACTIVE_RULE)).isZero();
+  }
+
+  @Test
+  public void index_nothing_if_disabled() {
+    dbTester.prepareDbUnit(getClass(), "index.xml");
+
+    ActiveRuleIndexer indexer = new ActiveRuleIndexer(dbTester.getDbClient(), esTester.client());
+    indexer.setEnabled(false);
+    indexer.index();
+
+    assertThat(esTester.countDocuments(INDEX, TYPE_ACTIVE_RULE)).isZero();
+  }
+
+  @Test
+  public void index() {
+    dbTester.prepareDbUnit(getClass(), "index.xml");
+
+    indexer.index();
+
+    assertThat(esTester.countDocuments(INDEX, TYPE_ACTIVE_RULE)).isEqualTo(1);
+  }
+
+  @Test
+  public void delete_profile() throws Exception {
+    indexActiveRules(
+      newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY1, RULE_KEY_1)),
+      newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY1, RULE_KEY_2)),
+      newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY2, RULE_KEY_2)),
+      newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY2, RULE_KEY_3)));
+
+    assertThat(esTester.getIds(INDEX, TYPE_ACTIVE_RULE)).hasSize(4);
+
+    indexer.deleteProfile(QUALITY_PROFILE_KEY1);
+
+    assertThat(esTester.getIds(INDEX, TYPE_ACTIVE_RULE)).containsOnly(
+      ActiveRuleKey.of(QUALITY_PROFILE_KEY2, RULE_KEY_2).toString(),
+      ActiveRuleKey.of(QUALITY_PROFILE_KEY2, RULE_KEY_3).toString()
+      );
+  }
+
+  @Test
+  public void index_from_changes_remove_deactivated_rules() throws Exception {
+    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_KEY2, RULE_KEY_2);
+    ActiveRuleKey activeRuleKey4 = ActiveRuleKey.of(QUALITY_PROFILE_KEY2, RULE_KEY_3);
+
+    indexActiveRules(
+      newDoc(activeRuleKey1),
+      newDoc(activeRuleKey2),
+      newDoc(activeRuleKey3),
+      newDoc(activeRuleKey4));
+
+    assertThat(esTester.getIds(INDEX, TYPE_ACTIVE_RULE)).hasSize(4);
+
+    indexer.index(Arrays.asList(
+      ActiveRuleChange.createFor(ACTIVATED, activeRuleKey1),
+      ActiveRuleChange.createFor(DEACTIVATED, activeRuleKey2),
+      ActiveRuleChange.createFor(DEACTIVATED, activeRuleKey3)
+      ));
+
+    assertThat(esTester.getIds(INDEX, TYPE_ACTIVE_RULE)).containsOnly(
+      activeRuleKey1.toString(),
+      activeRuleKey4.toString()
+      );
+  }
+
+  @Test
+  public void index_from_changes_index_new_active_rule() throws Exception {
+    long yesterday = 1000000L;
+    long now = 2000000L;
+
+    // Index one active rule
+    RuleDto rule = RuleTesting.newDto(RULE_KEY_1);
+    dbTester.getDbClient().ruleDao().insert(dbTester.getSession(), rule);
+    QualityProfileDto profile = QualityProfileDto.createFor("qp").setLanguage("xoo").setName("profile");
+    dbTester.getDbClient().qualityProfileDao().insert(dbTester.getSession(), profile);
+    ActiveRuleDto activeRule = ActiveRuleDto.createFor(profile, rule).setSeverity(Severity.BLOCKER)
+      .setCreatedAtInMs(yesterday).setUpdatedAtInMs(yesterday);
+//    dbTester.getDbClient().activeRuleDao().insert(dbTester.getSession(), activeRule);
+    dbTester.getSession().commit();
+
+    indexer.index();
+
+    assertThat(esTester.getIds(INDEX, TYPE_ACTIVE_RULE)).containsOnly(activeRule.getKey().toString());
+
+    // Index another active rule
+    RuleDto rule2 = RuleTesting.newDto(RULE_KEY_2);
+    dbTester.getDbClient().ruleDao().insert(dbTester.getSession(), rule2);
+    ActiveRuleDto activeRule2 = ActiveRuleDto.createFor(profile, rule2).setSeverity(Severity.CRITICAL)
+      .setCreatedAtInMs(now).setUpdatedAtInMs(now);
+//    dbTester.getDbClient().activeRuleDao().insert(dbTester.getSession(), activeRule2);
+    dbTester.getSession().commit();
+
+    indexer.index(singletonList(ActiveRuleChange.createFor(ACTIVATED, activeRule2.getKey())));
+
+    assertThat(esTester.getIds(INDEX, TYPE_ACTIVE_RULE)).containsOnly(
+      activeRule.getKey().toString(),
+      activeRule2.getKey().toString()
+    );
+  }
+
+  private void indexActiveRules(ActiveRuleDoc... docs) {
+    indexer.index(asList(docs).iterator());
+  }
+
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/index/ActiveRuleResultSetIteratorTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/index/ActiveRuleResultSetIteratorTest.java
new file mode 100644 (file)
index 0000000..6631d17
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.qualityprofile.index;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Maps;
+import java.util.Map;
+import javax.annotation.Nonnull;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.sonar.api.rule.RuleKey;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbTester;
+import org.sonar.db.qualityprofile.ActiveRuleKey;
+import org.sonar.server.qualityprofile.ActiveRule;
+import org.sonar.test.DbTests;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.api.rule.Severity.BLOCKER;
+import static org.sonar.api.rule.Severity.CRITICAL;
+import static org.sonar.api.rule.Severity.INFO;
+import static org.sonar.server.qualityprofile.ActiveRule.Inheritance.INHERITED;
+
+@Category(DbTests.class)
+public class ActiveRuleResultSetIteratorTest {
+
+  @Rule
+  public DbTester dbTester = DbTester.create(System2.INSTANCE);
+
+  @Before
+  public void setUp() {
+    dbTester.truncateTables();
+  }
+
+  @Test
+  public void iterator_over_one_active_rule() {
+    dbTester.prepareDbUnit(getClass(), "one_active_rule.xml");
+    ActiveRuleResultSetIterator it = ActiveRuleResultSetIterator.create(dbTester.getDbClient(), dbTester.getSession(), 0L);
+    Map<ActiveRuleKey, ActiveRuleDoc> activeRulesByKey = activeRulesByKey(it);
+    it.close();
+
+    assertThat(activeRulesByKey).hasSize(1);
+
+    ActiveRuleKey key = ActiveRuleKey.of("sonar-way", RuleKey.of("xoo", "S001"));
+    ActiveRuleDoc activeRule = activeRulesByKey.get(key);
+    assertThat(activeRule.key()).isEqualTo(key);
+    assertThat(activeRule.severity()).isEqualTo(CRITICAL);
+    assertThat(activeRule.inheritance()).isEqualTo(ActiveRule.Inheritance.NONE);
+    assertThat(activeRule.parentKey()).isNull();
+    assertThat(activeRule.createdAtAsLong()).isEqualTo(1500000000000L);
+    assertThat(activeRule.updatedAtAsLong()).isEqualTo(1600000000000L);
+  }
+
+  @Test
+  public void iterator_over_active_rules() {
+    dbTester.prepareDbUnit(getClass(), "shared.xml");
+    ActiveRuleResultSetIterator it = ActiveRuleResultSetIterator.create(dbTester.getDbClient(), dbTester.getSession(), 0L);
+    Map<ActiveRuleKey, ActiveRuleDoc> activeRulesByKey = activeRulesByKey(it);
+    it.close();
+
+    assertThat(activeRulesByKey).hasSize(3);
+
+    ActiveRuleKey key = ActiveRuleKey.of("sonar-way", RuleKey.of("xoo", "S002"));
+    ActiveRuleDoc activeRule = activeRulesByKey.get(key);
+    assertThat(activeRule.key()).isEqualTo(key);
+    assertThat(activeRule.severity()).isEqualTo(CRITICAL);
+    assertThat(activeRule.inheritance()).isEqualTo(ActiveRule.Inheritance.NONE);
+    assertThat(activeRule.parentKey()).isNull();
+    assertThat(activeRule.createdAtAsLong()).isEqualTo(2000000000000L);
+    assertThat(activeRule.updatedAtAsLong()).isEqualTo(2100000000000L);
+
+    key = ActiveRuleKey.of("parent", RuleKey.of("xoo", "S001"));
+    activeRule = activeRulesByKey.get(key);
+    assertThat(activeRule.key()).isEqualTo(key);
+    assertThat(activeRule.severity()).isEqualTo(INFO);
+    assertThat(activeRule.inheritance()).isEqualTo(ActiveRule.Inheritance.NONE);
+    assertThat(activeRule.parentKey()).isNull();
+    assertThat(activeRule.createdAtAsLong()).isEqualTo(1700000000000L);
+    assertThat(activeRule.updatedAtAsLong()).isEqualTo(1800000000000L);
+
+    key = ActiveRuleKey.of("child", RuleKey.of("xoo", "S001"));
+    activeRule = activeRulesByKey.get(key);
+    assertThat(activeRule.key()).isEqualTo(key);
+    assertThat(activeRule.severity()).isEqualTo(BLOCKER);
+    assertThat(activeRule.inheritance()).isEqualTo(INHERITED);
+    assertThat(activeRule.parentKey()).isEqualTo(ActiveRuleKey.of("parent", RuleKey.of("xoo", "S001")));
+    assertThat(activeRule.createdAtAsLong()).isEqualTo(1500000000000L);
+    assertThat(activeRule.updatedAtAsLong()).isEqualTo(1600000000000L);
+  }
+
+  @Test
+  public void active_rule_with_inherited_inheritance() {
+    dbTester.prepareDbUnit(getClass(), "active_rule_with_inherited_inheritance.xml");
+    ActiveRuleResultSetIterator it = ActiveRuleResultSetIterator.create(dbTester.getDbClient(), dbTester.getSession(), 0L);
+    Map<ActiveRuleKey, ActiveRuleDoc> activeRulesByKey = activeRulesByKey(it);
+    it.close();
+
+    assertThat(activeRulesByKey).hasSize(2);
+
+    ActiveRuleKey key = ActiveRuleKey.of("child", RuleKey.of("xoo", "S001"));
+    ActiveRuleDoc activeRule = activeRulesByKey.get(key);
+    assertThat(activeRule.inheritance()).isEqualTo(INHERITED);
+    assertThat(activeRule.parentKey()).isEqualTo(ActiveRuleKey.of("parent", RuleKey.of("xoo", "S001")));
+  }
+
+  @Test
+  public void active_rule_with_overrides_inheritance() {
+    dbTester.prepareDbUnit(getClass(), "active_rule_with_overrides_inheritance.xml");
+    ActiveRuleResultSetIterator it = ActiveRuleResultSetIterator.create(dbTester.getDbClient(), dbTester.getSession(), 0L);
+    Map<ActiveRuleKey, ActiveRuleDoc> activeRulesByKey = activeRulesByKey(it);
+    it.close();
+
+    assertThat(activeRulesByKey).hasSize(2);
+
+    ActiveRuleKey key = ActiveRuleKey.of("child", RuleKey.of("xoo", "S001"));
+    ActiveRuleDoc activeRule = activeRulesByKey.get(key);
+    assertThat(activeRule.inheritance()).isEqualTo(ActiveRule.Inheritance.OVERRIDES);
+    assertThat(activeRule.parentKey()).isEqualTo(ActiveRuleKey.of("parent", RuleKey.of("xoo", "S001")));
+  }
+
+  @Test
+  public void select_after_date() {
+    dbTester.prepareDbUnit(getClass(), "shared.xml");
+    ActiveRuleResultSetIterator it = ActiveRuleResultSetIterator.create(dbTester.getDbClient(), dbTester.getSession(), 1_900_000_000_000L);
+
+    assertThat(it.hasNext()).isTrue();
+    ActiveRuleDoc doc = it.next();
+    assertThat(doc.key()).isEqualTo(ActiveRuleKey.of("sonar-way", RuleKey.of("xoo", "S002")));
+
+    assertThat(it.hasNext()).isFalse();
+    it.close();
+  }
+
+  private static Map<ActiveRuleKey, ActiveRuleDoc> activeRulesByKey(ActiveRuleResultSetIterator it) {
+    return Maps.uniqueIndex(it, DocToKey.INSTANCE);
+  }
+
+  private enum DocToKey implements Function<ActiveRuleDoc, ActiveRuleKey> {
+    INSTANCE;
+
+    @Override
+    public ActiveRuleKey apply(@Nonnull ActiveRuleDoc doc) {
+      return doc.key();
+    }
+  }
+
+}
index 66072c24953abd330b9b7e7e6c05654e24af34af..58848982fc327dead75202b3ff55079243d41a73 100644 (file)
@@ -26,14 +26,18 @@ import java.util.List;
 import java.util.Map;
 import org.junit.Before;
 import org.junit.ClassRule;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.sonar.api.config.Settings;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rule.RuleStatus;
+import org.sonar.db.qualityprofile.ActiveRuleKey;
+import org.sonar.db.rule.RuleTesting;
 import org.sonar.server.es.EsTester;
 import org.sonar.server.es.SearchIdResult;
 import org.sonar.server.es.SearchOptions;
+import org.sonar.server.qualityprofile.index.ActiveRuleDoc;
+import org.sonar.server.qualityprofile.index.ActiveRuleDocTesting;
+import org.sonar.server.qualityprofile.index.ActiveRuleIndexer;
 
 import static java.util.Arrays.asList;
 import static java.util.Collections.singleton;
@@ -42,26 +46,42 @@ import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.data.MapEntry.entry;
 import static org.junit.Assert.fail;
 import static org.sonar.api.rule.Severity.BLOCKER;
+import static org.sonar.api.rule.Severity.CRITICAL;
 import static org.sonar.api.rule.Severity.INFO;
+import static org.sonar.api.rule.Severity.MAJOR;
 import static org.sonar.api.rule.Severity.MINOR;
+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.RuleIndex2.FACET_LANGUAGES;
 import static org.sonar.server.rule.index.RuleIndex2.FACET_REPOSITORIES;
 import static org.sonar.server.rule.index.RuleIndex2.FACET_TAGS;
+import static org.sonar.server.rule.index.RuleIndexDefinition.INDEX;
+import static org.sonar.server.rule.index.RuleIndexDefinition.TYPE_ACTIVE_RULE;
 
 public class RuleIndex2Test {
 
+  static final RuleKey RULE_KEY_1 = RuleTesting.XOO_X1;
+  static final RuleKey RULE_KEY_2 = RuleTesting.XOO_X2;
+  static final RuleKey RULE_KEY_3 = RuleTesting.XOO_X3;
+  static final RuleKey RULE_KEY_4 = RuleKey.of("xoo", "x4");
+
+  static final String QUALITY_PROFILE_KEY1 = "qp1";
+  static final String QUALITY_PROFILE_KEY2 = "qp2";
+
   @ClassRule
   public static EsTester tester = new EsTester().addDefinitions(new RuleIndexDefinition(new Settings()));
 
   RuleIndex2 index;
 
   RuleIndexer ruleIndexer;
+  ActiveRuleIndexer activeRuleIndexer;
 
   @Before
   public void setUp() {
     tester.truncateIndices();
     ruleIndexer = new RuleIndexer(null, tester.client());
+    activeRuleIndexer = new ActiveRuleIndexer(null, tester.client());
     index = new RuleIndex2(tester.client());
   }
 
@@ -306,7 +326,7 @@ public class RuleIndex2Test {
       newDoc(RuleKey.of("java", "S002")).setStatus(RuleStatus.READY.name()));
 
     RuleQuery query = new RuleQuery().setStatuses(asList(RuleStatus.DEPRECATED, RuleStatus.READY));
-    SearchIdResult results = index.search(query, new SearchOptions());
+    SearchIdResult<RuleKey> results = index.search(query, new SearchOptions());
     assertThat(results.getIds()).containsOnly(RuleKey.of("java", "S002"));
 
     // no results
@@ -323,194 +343,146 @@ public class RuleIndex2Test {
   }
 
   @Test
-  @Ignore
-  public void search_by_profile() {
-    // QualityProfileDto qualityProfileDto1 = QProfileTesting.newXooP1();
-    // QualityProfileDto qualityProfileDto2 = QProfileTesting.newXooP2();
-    // db.qualityProfileDao().insert(dbSession, qualityProfileDto1, qualityProfileDto2);
-    //
-    // RuleDto rule1 = RuleTesting.newXooX1();
-    // RuleDto rule2 = RuleTesting.newXooX2();
-    // RuleDto rule3 = RuleTesting.newXooX3();
-    // dao.insert(dbSession, rule1, rule2, rule3);
-    //
-    // db.activeRuleDao().insert(
-    // dbSession,
-    // ActiveRuleDto.createFor(qualityProfileDto1, rule1).setSeverity("BLOCKER"),
-    // ActiveRuleDto.createFor(qualityProfileDto2, rule1).setSeverity("BLOCKER"),
-    // ActiveRuleDto.createFor(qualityProfileDto1, rule2).setSeverity("BLOCKER"));
-    // dbSession.commit();
-    // dbSession.clearCache();
-    //
-    // // 1. get all active rules.
-    // Result<org.sonar.server.rule.Rule> result = index.search(new RuleQuery().setActivation(true),
-    // new SearchOptions());
-    // assertThat(result.getHits()).hasSize(2);
-    //
-    // // 2. get all inactive rules.
-    // result = index.search(new RuleQuery().setActivation(false),
-    // new SearchOptions());
-    // assertThat(result.getHits()).hasSize(1);
-    // assertThat(result.getHits().get(0).name()).isEqualTo(rule3.getName());
-    //
-    // // 3. get all rules not active on profile
-    // index.search(new RuleQuery().setActivation(false).setQProfileKey(qualityProfileDto2.getKey()),
-    // new SearchOptions());
-    // // TODO
-    // assertThat(result.getHits()).hasSize(1);
-    //
-    // // 4. get all active rules on profile
-    // result = index.search(new RuleQuery().setActivation(true)
-    // .setQProfileKey(qualityProfileDto2.getKey()),
-    // new SearchOptions());
-    // assertThat(result.getHits()).hasSize(1);
-    // assertThat(result.getHits().get(0).name()).isEqualTo(rule1.getName());
+  public void search_by_profile() throws InterruptedException {
+    indexRules(
+      newDoc(RULE_KEY_1),
+      newDoc(RULE_KEY_2),
+      newDoc(RULE_KEY_3));
+
+    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)));
+
+    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);
+
+    // 2. get all inactive rules.
+    assertThat(index.search(new RuleQuery().setActivation(false), new SearchOptions()).getIds())
+      .containsOnly(RULE_KEY_3);
+
+    // 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);
+
+    // 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);
   }
 
   @Test
-  @Ignore
   public void search_by_profile_and_inheritance() {
-    // QualityProfileDto qualityProfileDto1 = QProfileTesting.newXooP1();
-    // QualityProfileDto qualityProfileDto2 = QProfileTesting.newXooP2().setParentKee(QProfileTesting.XOO_P1_KEY);
-    // db.qualityProfileDao().insert(dbSession, qualityProfileDto1, qualityProfileDto2);
-    //
-    // RuleDto rule1 = RuleTesting.newDto(RuleKey.of("xoo", "S001"));
-    // RuleDto rule2 = RuleTesting.newDto(RuleKey.of("xoo", "S002"));
-    // RuleDto rule3 = RuleTesting.newDto(RuleKey.of("xoo", "S003"));
-    // RuleDto rule4 = RuleTesting.newDto(RuleKey.of("xoo", "S004"));
-    // dao.insert(dbSession, rule1, rule2, rule3, rule4);
-    //
-    // db.activeRuleDao().insert(
-    // dbSession,
-    // ActiveRuleDto.createFor(qualityProfileDto1, rule1)
-    // .setSeverity("BLOCKER"),
-    // ActiveRuleDto.createFor(qualityProfileDto1, rule2)
-    // .setSeverity("BLOCKER"),
-    // ActiveRuleDto.createFor(qualityProfileDto1, rule3)
-    // .setSeverity("BLOCKER"),
-    //
-    // ActiveRuleDto.createFor(qualityProfileDto2, rule1)
-    // .setSeverity("MINOR")
-    // .setInheritance(ActiveRule.Inheritance.INHERITED.name()),
-    // ActiveRuleDto.createFor(qualityProfileDto2, rule2)
-    // .setSeverity("BLOCKER")
-    // .setInheritance(ActiveRule.Inheritance.OVERRIDES.name()),
-    // ActiveRuleDto.createFor(qualityProfileDto2, rule3)
-    // .setSeverity("BLOCKER")
-    // .setInheritance(ActiveRule.Inheritance.INHERITED.name())
-    // );
-    //
-    // dbSession.commit();
-    //
-    // // 0. get all rules
-    // Result<org.sonar.server.rule.Rule> result = index.search(new RuleQuery(),
-    // new SearchOptions());
-    // assertThat(result.getHits()).hasSize(4);
-    //
-    // // 1. get all active rules
-    // result = index.search(new RuleQuery().setActivation(true),
-    // new SearchOptions());
-    // assertThat(result.getHits()).hasSize(3);
-    //
-    // // 2. get all inactive rules.
-    // result = index.search(new RuleQuery().setActivation(false),
-    // new SearchOptions());
-    // assertThat(result.getHits()).hasSize(1);
-    // assertThat(result.getHits().get(0).name()).isEqualTo(rule4.getName());
-    //
-    // // 3. get Inherited Rules on profile1
-    // result = index.search(new RuleQuery().setActivation(true)
-    // .setQProfileKey(qualityProfileDto1.getKey())
-    // .setInheritance(ImmutableSet.of(ActiveRule.Inheritance.INHERITED.name())),
-    // new SearchOptions()
-    // );
-    // assertThat(result.getHits()).hasSize(0);
-    //
-    // // 4. get Inherited Rules on profile2
-    // result = index.search(new RuleQuery().setActivation(true)
-    // .setQProfileKey(qualityProfileDto2.getKey())
-    // .setInheritance(ImmutableSet.of(ActiveRule.Inheritance.INHERITED.name())),
-    // new SearchOptions()
-    // );
-    // assertThat(result.getHits()).hasSize(2);
-    //
-    // // 5. get Overridden Rules on profile1
-    // result = index.search(new RuleQuery().setActivation(true)
-    // .setQProfileKey(qualityProfileDto1.getKey())
-    // .setInheritance(ImmutableSet.of(ActiveRule.Inheritance.OVERRIDES.name())),
-    // new SearchOptions()
-    // );
-    // assertThat(result.getHits()).hasSize(0);
-    //
-    // // 6. get Overridden Rules on profile2
-    // result = index.search(new RuleQuery().setActivation(true)
-    // .setQProfileKey(qualityProfileDto2.getKey())
-    // .setInheritance(ImmutableSet.of(ActiveRule.Inheritance.OVERRIDES.name())),
-    // new SearchOptions()
-    // );
-    // assertThat(result.getHits()).hasSize(1);
-    //
-    // // 7. get Inherited AND Overridden Rules on profile1
-    // result = index.search(new RuleQuery().setActivation(true)
-    // .setQProfileKey(qualityProfileDto1.getKey())
-    // .setInheritance(ImmutableSet.of(
-    // ActiveRule.Inheritance.INHERITED.name(), ActiveRule.Inheritance.OVERRIDES.name())),
-    // new SearchOptions()
-    // );
-    // assertThat(result.getHits()).hasSize(0);
-    //
-    // // 8. get Inherited AND Overridden Rules on profile2
-    // result = index.search(new RuleQuery().setActivation(true)
-    // .setQProfileKey(qualityProfileDto2.getKey())
-    // .setInheritance(ImmutableSet.of(
-    // ActiveRule.Inheritance.INHERITED.name(), ActiveRule.Inheritance.OVERRIDES.name())),
-    // new SearchOptions()
-    // );
-    // assertThat(result.getHits()).hasSize(3);
+    indexRules(
+      newDoc(RULE_KEY_1),
+      newDoc(RULE_KEY_2),
+      newDoc(RULE_KEY_3),
+      newDoc(RULE_KEY_4));
+
+    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);
+
+    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))
+        .setParentKey(activeRuleKey1.toString()).setInheritance(INHERITED.name()),
+      ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY2, RULE_KEY_2))
+        .setParentKey(activeRuleKey2.toString()).setInheritance(OVERRIDES.name()),
+      ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY2, RULE_KEY_3))
+        .setParentKey(activeRuleKey3.toString()).setInheritance(INHERITED.name()));
+
+    // 0. get all rules
+    assertThat(index.search(new RuleQuery(), new SearchOptions()).getIds())
+      .hasSize(4);
+
+    // 1. get all active rules
+    assertThat(index.search(new RuleQuery()
+      .setActivation(true), new SearchOptions()).getIds())
+      .hasSize(3);
+
+    // 2. get all inactive rules.
+    assertThat(index.search(new RuleQuery()
+      .setActivation(false), new SearchOptions()).getIds())
+      .containsOnly(RULE_KEY_4);
+
+    // 3. get Inherited Rules on profile1
+    assertThat(index.search(new RuleQuery().setActivation(true)
+      .setQProfileKey(QUALITY_PROFILE_KEY1)
+      .setInheritance(ImmutableSet.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())),
+      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())),
+      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())),
+      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())),
+      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())),
+      new SearchOptions()).getIds())
+      .hasSize(3);
   }
 
   @Test
-  @Ignore
   public void search_by_profile_and_active_severity() {
-    // QualityProfileDto qualityProfileDto1 = QProfileTesting.newXooP1();
-    // QualityProfileDto qualityProfileDto2 = QProfileTesting.newXooP2();
-    // db.qualityProfileDao().insert(dbSession, qualityProfileDto1, qualityProfileDto2);
-    //
-    // RuleDto rule1 = RuleTesting.newXooX1().setSeverity("MAJOR");
-    // RuleDto rule2 = RuleTesting.newXooX2().setSeverity("MINOR");
-    // RuleDto rule3 = RuleTesting.newXooX3().setSeverity("INFO");
-    // dao.insert(dbSession, rule1, rule2, rule3);
-    //
-    // db.activeRuleDao().insert(
-    // dbSession,
-    // ActiveRuleDto.createFor(qualityProfileDto1, rule1).setSeverity("BLOCKER"),
-    // ActiveRuleDto.createFor(qualityProfileDto2, rule1).setSeverity("BLOCKER"),
-    // ActiveRuleDto.createFor(qualityProfileDto1, rule2).setSeverity("CRITICAL"));
-    // dbSession.commit();
-    // dbSession.clearCache();
-    //
-    // // 1. get all active rules.
-    // Result<org.sonar.server.rule.Rule> result = index.search(new
-    // RuleQuery().setActivation(true).setQProfileKey(qualityProfileDto1.getKey()),
-    // new SearchOptions());
-    // assertThat(result.getHits()).hasSize(2);
-    //
-    // // 2. get rules with active severity critical.
-    // result = index.search(new
-    // RuleQuery().setActivation(true).setQProfileKey(qualityProfileDto1.getKey()).setActiveSeverities(Arrays.asList("CRITICAL")),
-    // new SearchOptions().addFacets(Arrays.asList(RuleIndex.FACET_ACTIVE_SEVERITIES)));
-    // assertThat(result.getHits()).hasSize(1);
-    // assertThat(result.getHits().get(0).name()).isEqualTo(rule2.getName());
-    // // check stickyness of active severity facet
-    // assertThat(result.getFacetValues(RuleIndex.FACET_ACTIVE_SEVERITIES)).containsOnly(new FacetValue("BLOCKER", 1), new
-    // FacetValue("CRITICAL", 1));
-    //
-    // // 3. count activation severities of all active rules
-    // result = index.search(new RuleQuery(),
-    // new SearchOptions().addFacets(Arrays.asList(RuleIndex.FACET_ACTIVE_SEVERITIES)));
-    // assertThat(result.getHits()).hasSize(3);
-    // assertThat(result.getFacetValues(RuleIndex.FACET_ACTIVE_SEVERITIES)).containsOnly(new FacetValue("BLOCKER", 2), new
-    // FacetValue("CRITICAL", 1));
+    indexRules(
+      newDoc(RULE_KEY_1).setSeverity(MAJOR),
+      newDoc(RULE_KEY_2).setSeverity(MINOR),
+      newDoc(RULE_KEY_3).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));
+
+    // 1. get all active rules.
+    assertThat(index.search(new RuleQuery().setActivation(true).setQProfileKey(QUALITY_PROFILE_KEY1), new SearchOptions()).getIds())
+      .hasSize(2);
+
+    // 2. get rules with active severity critical.
+    SearchIdResult<RuleKey> result = index.search(new RuleQuery().setActivation(true)
+      .setQProfileKey(QUALITY_PROFILE_KEY1).setActiveSeverities(singletonList(CRITICAL)),
+      new SearchOptions().addFacets(singletonList(RuleIndex2.FACET_ACTIVE_SEVERITIES)));
+    assertThat(result.getIds()).containsOnly(RULE_KEY_2);
+
+    // check stickyness of active severity facet
+    assertThat(result.getFacets().get(RuleIndex2.FACET_ACTIVE_SEVERITIES)).containsOnly(entry(BLOCKER, 1L), entry(CRITICAL, 1L));
+
+    // 3. count activation severities of all active rules
+    result = index.search(new RuleQuery(), new SearchOptions().addFacets(singletonList(RuleIndex2.FACET_ACTIVE_SEVERITIES)));
+    assertThat(result.getIds()).hasSize(3);
+    assertThat(result.getFacets().get(RuleIndex2.FACET_ACTIVE_SEVERITIES)).containsOnly(entry(BLOCKER, 2L), entry(CRITICAL, 1L));
   }
 
   @Test
@@ -724,4 +696,8 @@ public class RuleIndex2Test {
   private void indexRules(RuleDoc... rules) {
     ruleIndexer.index(asList(rules).iterator());
   }
+
+  private void indexActiveRules(ActiveRuleDoc... docs) {
+    activeRuleIndexer.index(asList(docs).iterator());
+  }
 }
index 502ece8cd97c2456ee161a51aed3ec7071da20a9..dfbc11f13f873913d0cc6cb8bee759b06c5a36c3 100644 (file)
@@ -38,7 +38,7 @@ public class RuleIndexDefinitionTest {
     assertThat(underTest.getIndices()).hasSize(1);
     NewIndex ruleIndex = underTest.getIndices().get("rules");
     assertThat(ruleIndex).isNotNull();
-    assertThat(ruleIndex.getTypes().keySet()).containsOnly("rule");
+    assertThat(ruleIndex.getTypes().keySet()).containsOnly("rule", "activeRule");
 
     // no cluster by default
     assertThat(ruleIndex.getSettings().get("index.number_of_shards")).isEqualTo(String.valueOf(NewIndex.DEFAULT_NUMBER_OF_SHARDS));
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/index/ActiveRuleIndexerTest/index.xml b/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/index/ActiveRuleIndexerTest/index.xml
new file mode 100644 (file)
index 0000000..a2f2f95
--- /dev/null
@@ -0,0 +1,13 @@
+<dataset>
+
+  <rules id="10" name="Null Pointer" plugin_name="xoo" plugin_rule_key="S001"
+         plugin_config_key="S1" description_format="HTML" description="S001 desc" language="xoo"
+         priority="4" status="READY" is_template="[false]" template_id="[null]"
+         tags="bug,performance" system_tags="cwe"
+         created_at_ms="1500000000000" updated_at_ms="1600000000000"/>
+
+  <rules_profiles id="100" name="Sonar Way" kee="sonar-way" language="xoo" parent_kee="[null]" is_default="[false]"/>
+
+  <active_rules id="1" profile_id="100" rule_id="10" failure_level="3" inheritance="[null]"
+                created_at_ms="1500000000000" updated_at_ms="1600000000000"/>
+</dataset>
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/index/ActiveRuleResultSetIteratorTest/active_rule_with_inherited_inheritance.xml b/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/index/ActiveRuleResultSetIteratorTest/active_rule_with_inherited_inheritance.xml
new file mode 100644 (file)
index 0000000..32aecb5
--- /dev/null
@@ -0,0 +1,20 @@
+<dataset>
+
+  <rules id="10" name="Null Pointer" plugin_rule_key="S001" plugin_name="xoo"
+         plugin_config_key="S1" description_format="HTML" description="S001 desc" language="xoo"
+         priority="4" status="READY" is_template="[false]" template_id="[null]"
+         tags="bug,performance" system_tags="cwe"
+         created_at_ms="1500000000000" updated_at_ms="1600000000000"/>
+
+  <rules_profiles id="1" name="Parent" kee="parent" language="xoo" parent_kee="[null]" is_default="[false]"/>
+
+  <rules_profiles id="2" name="Child" kee="child" language="xoo" parent_kee="parent" is_default="[false]"/>
+
+  <active_rules id="1" profile_id="2" rule_id="10" failure_level="4" inheritance="INHERITED"
+                created_at_ms="1500000000000" updated_at_ms="1600000000000"/>
+
+  <!-- Parent of Active rule 1 -->
+  <active_rules id="2" profile_id="1" rule_id="10" failure_level="0" inheritance="[null]"
+                created_at_ms="1500000000000" updated_at_ms="1600000000000"/>
+
+</dataset>
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/index/ActiveRuleResultSetIteratorTest/active_rule_with_overrides_inheritance.xml b/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/index/ActiveRuleResultSetIteratorTest/active_rule_with_overrides_inheritance.xml
new file mode 100644 (file)
index 0000000..a20b455
--- /dev/null
@@ -0,0 +1,20 @@
+<dataset>
+
+  <rules id="10" name="Null Pointer" plugin_rule_key="S001" plugin_name="xoo"
+         plugin_config_key="S1" description_format="HTML" description="S001 desc" language="xoo"
+         priority="4" status="READY" is_template="[false]" template_id="[null]"
+         tags="bug,performance" system_tags="cwe"
+         created_at_ms="1500000000000" updated_at_ms="1600000000000"/>
+
+  <rules_profiles id="1" name="Parent" kee="parent" language="xoo" parent_kee="[null]" is_default="[false]"/>
+
+  <rules_profiles id="2" name="Child" kee="child" language="xoo" parent_kee="parent" is_default="[false]"/>
+
+  <active_rules id="1" profile_id="2" rule_id="10" failure_level="2" inheritance="OVERRIDES"
+                created_at_ms="1500000000000" updated_at_ms="1600000000000"/>
+
+  <!-- Parent of Active rule 1 -->
+  <active_rules id="2" profile_id="1" rule_id="10" failure_level="0" inheritance="[null]"
+                created_at_ms="1500000000000" updated_at_ms="1600000000000"/>
+
+</dataset>
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/index/ActiveRuleResultSetIteratorTest/one_active_rule.xml b/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/index/ActiveRuleResultSetIteratorTest/one_active_rule.xml
new file mode 100644 (file)
index 0000000..a2f2f95
--- /dev/null
@@ -0,0 +1,13 @@
+<dataset>
+
+  <rules id="10" name="Null Pointer" plugin_name="xoo" plugin_rule_key="S001"
+         plugin_config_key="S1" description_format="HTML" description="S001 desc" language="xoo"
+         priority="4" status="READY" is_template="[false]" template_id="[null]"
+         tags="bug,performance" system_tags="cwe"
+         created_at_ms="1500000000000" updated_at_ms="1600000000000"/>
+
+  <rules_profiles id="100" name="Sonar Way" kee="sonar-way" language="xoo" parent_kee="[null]" is_default="[false]"/>
+
+  <active_rules id="1" profile_id="100" rule_id="10" failure_level="3" inheritance="[null]"
+                created_at_ms="1500000000000" updated_at_ms="1600000000000"/>
+</dataset>
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/index/ActiveRuleResultSetIteratorTest/shared.xml b/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/index/ActiveRuleResultSetIteratorTest/shared.xml
new file mode 100644 (file)
index 0000000..bda9c4d
--- /dev/null
@@ -0,0 +1,37 @@
+<dataset>
+
+  <rules id="10" name="Null Pointer" plugin_rule_key="S001"
+         plugin_config_key="S1" plugin_name="xoo"
+         description_format="HTML" description="S001 desc" language="xoo"
+         priority="4" status="READY"
+         is_template="[false]" template_id="[null]"
+         tags="bug,performance" system_tags="cwe"
+         created_at="2014-05-10" updated_at="2014-05-11"
+         created_at_ms="1500000000000" updated_at_ms="1600000000000"/>
+
+  <rules id="11" name="Slow" plugin_rule_key="S002"
+         plugin_config_key="S2" plugin_name="xoo"
+         description_format="MARKDOWN" description="*S002 desc*" language="xoo"
+         priority="3" status="BETA"
+         is_template="[true]" template_id="[null]"
+         tags="[null]" system_tags="[null]"
+         created_at="2014-05-10" updated_at="2014-05-11"
+         created_at_ms="2000000000000" updated_at_ms="2100000000000"/>
+
+  <rules_profiles id="1" name="Parent" kee="parent" language="xoo" parent_kee="[null]" is_default="[false]"/>
+
+  <rules_profiles id="2" name="Child" kee="child" language="xoo" parent_kee="parent" is_default="[false]"/>
+
+  <active_rules id="1" profile_id="2" rule_id="10" failure_level="4" inheritance="INHERITED"
+                created_at_ms="1500000000000" updated_at_ms="1600000000000"/>
+
+  <!-- Parent of Active rule 1 -->
+  <active_rules id="2" profile_id="1" rule_id="10" failure_level="0" inheritance="[null]"
+                created_at_ms="1700000000000" updated_at_ms="1800000000000"/>
+
+  <rules_profiles id="3" name="Sonar Way" kee="sonar-way" language="xoo" parent_kee="[null]" is_default="[false]"/>
+
+  <active_rules id="3" profile_id="3" rule_id="11" failure_level="3" inheritance="[null]"
+                created_at_ms="2000000000000" updated_at_ms="2100000000000"/>
+
+</dataset>
diff --git a/sonar-db/src/main/java/org/sonar/db/qualityprofile/ActiveRuleDao.java b/sonar-db/src/main/java/org/sonar/db/qualityprofile/ActiveRuleDao.java
new file mode 100644 (file)
index 0000000..808b85f
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.db.qualityprofile;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import java.util.List;
+import javax.annotation.CheckForNull;
+import org.sonar.db.Dao;
+import org.sonar.db.DbSession;
+import org.sonar.db.RowNotFoundException;
+import org.sonar.db.rule.RuleDto;
+
+public class ActiveRuleDao implements Dao {
+
+  private static final String QUALITY_PROFILE_IS_NOT_PERSISTED = "Quality profile is not persisted (missing id)";
+  private static final String RULE_IS_NOT_PERSISTED = "Rule is not persisted";
+  private static final String RULE_PARAM_IS_NOT_PERSISTED = "Rule param is not persisted";
+  private static final String ACTIVE_RULE_KEY_CANNOT_BE_NULL = "ActiveRuleKey cannot be null";
+  private static final String ACTIVE_RULE_IS_NOT_PERSISTED = "ActiveRule is not persisted";
+  private static final String ACTIVE_RULE_IS_ALREADY_PERSISTED = "ActiveRule is already persisted";
+  private static final String ACTIVE_RULE_PARAM_IS_NOT_PERSISTED = "ActiveRuleParam is not persisted";
+  private static final String ACTIVE_RULE_PARAM_IS_ALREADY_PERSISTED = "ActiveRuleParam is already persisted";
+  private static final String PARAMETER_NAME_CANNOT_BE_NULL = "ParameterName cannot be null";
+
+  public Optional<ActiveRuleDto> selectByKey(DbSession session, ActiveRuleKey key) {
+    return Optional.fromNullable(mapper(session).selectByKey(key.qProfile(), key.ruleKey().repository(), key.ruleKey().rule()));
+  }
+
+  public ActiveRuleDto selectOrFailByKey(DbSession session, ActiveRuleKey key) {
+    Optional<ActiveRuleDto> activeRule = selectByKey(session, key);
+    if (activeRule.isPresent()) {
+      return activeRule.get();
+    }
+    throw new RowNotFoundException(String.format("Active rule with key '%s' does not exist", key));
+  }
+
+  public List<ActiveRuleDto> selectByRule(DbSession dbSession, RuleDto rule) {
+    Preconditions.checkNotNull(rule.getId(), RULE_IS_NOT_PERSISTED);
+    return mapper(dbSession).selectByRuleId(rule.getId());
+  }
+
+  public List<ActiveRuleParamDto> selectAllParams(DbSession dbSession) {
+    return mapper(dbSession).selectAllParams();
+  }
+
+  public ActiveRuleDto insert(DbSession session, ActiveRuleDto item) {
+    Preconditions.checkArgument(item.getProfileId() != null, QUALITY_PROFILE_IS_NOT_PERSISTED);
+    Preconditions.checkArgument(item.getRuleId() != null, RULE_IS_NOT_PERSISTED);
+    Preconditions.checkArgument(item.getId() == null, ACTIVE_RULE_IS_ALREADY_PERSISTED);
+    mapper(session).insert(item);
+    return item;
+  }
+
+  public ActiveRuleDto update(DbSession session, ActiveRuleDto item) {
+    Preconditions.checkArgument(item.getProfileId() != null, QUALITY_PROFILE_IS_NOT_PERSISTED);
+    Preconditions.checkArgument(item.getRuleId() != null, ActiveRuleDao.RULE_IS_NOT_PERSISTED);
+    Preconditions.checkArgument(item.getId() != null, ACTIVE_RULE_IS_NOT_PERSISTED);
+    mapper(session).update(item);
+    return item;
+  }
+
+  public void delete(DbSession session, ActiveRuleKey key) {
+    Optional<ActiveRuleDto> activeRule = selectByKey(session, key);
+    if (activeRule.isPresent()) {
+      mapper(session).deleteParameters(activeRule.get().getId());
+      mapper(session).delete(activeRule.get().getId());
+    }
+  }
+
+  /**
+   * Nested DTO ActiveRuleParams
+   */
+
+  public ActiveRuleParamDto insertParam(DbSession session, ActiveRuleDto activeRule, ActiveRuleParamDto activeRuleParam) {
+    Preconditions.checkArgument(activeRule.getId() != null, ACTIVE_RULE_IS_NOT_PERSISTED);
+    Preconditions.checkArgument(activeRuleParam.getId() == null, ACTIVE_RULE_PARAM_IS_ALREADY_PERSISTED);
+    Preconditions.checkNotNull(activeRuleParam.getRulesParameterId(), RULE_PARAM_IS_NOT_PERSISTED);
+
+    activeRuleParam.setActiveRuleId(activeRule.getId());
+    mapper(session).insertParameter(activeRuleParam);
+    return activeRuleParam;
+  }
+
+  public void deleteParamByKeyAndName(DbSession session, ActiveRuleKey key, String param) {
+    // TODO SQL rewrite to delete by key
+    Optional<ActiveRuleDto> activeRule = selectByKey(session, key);
+    if (activeRule.isPresent()) {
+      ActiveRuleParamDto activeRuleParam = mapper(session).selectParamByActiveRuleAndKey(activeRule.get().getId(), param);
+      if (activeRuleParam != null) {
+        mapper(session).deleteParameter(activeRuleParam.getId());
+      }
+    }
+  }
+
+  public void updateParam(DbSession session, ActiveRuleDto activeRule, ActiveRuleParamDto activeRuleParam) {
+    Preconditions.checkNotNull(activeRule.getId(), ACTIVE_RULE_IS_NOT_PERSISTED);
+    Preconditions.checkNotNull(activeRuleParam.getId(), ACTIVE_RULE_PARAM_IS_NOT_PERSISTED);
+    mapper(session).updateParameter(activeRuleParam);
+  }
+
+  public void deleteParam(DbSession session, ActiveRuleDto activeRule, ActiveRuleParamDto activeRuleParam) {
+    Preconditions.checkNotNull(activeRule.getId(), ACTIVE_RULE_IS_NOT_PERSISTED);
+    Preconditions.checkNotNull(activeRuleParam.getId(), ACTIVE_RULE_PARAM_IS_NOT_PERSISTED);
+    mapper(session).deleteParameter(activeRuleParam.getId());
+  }
+
+  public void deleteByProfileKey(DbSession session, String profileKey) {
+    /** Functional cascade for params */
+    for (ActiveRuleDto activeRule : selectByProfileKey(session, profileKey)) {
+      delete(session, activeRule.getKey());
+    }
+  }
+
+  public List<ActiveRuleDto> selectByProfileKey(DbSession session, String profileKey) {
+    return mapper(session).selectByProfileKey(profileKey);
+  }
+
+  /**
+   * Finder methods for ActiveRuleParams
+   */
+
+  public List<ActiveRuleParamDto> selectParamsByActiveRuleKey(DbSession session, ActiveRuleKey key) {
+    Preconditions.checkNotNull(key, ACTIVE_RULE_KEY_CANNOT_BE_NULL);
+    ActiveRuleDto activeRule = selectOrFailByKey(session, key);
+    return mapper(session).selectParamsByActiveRuleId(activeRule.getId());
+  }
+
+  @CheckForNull
+  public ActiveRuleParamDto selectParamByKeyAndName(ActiveRuleKey key, String name, DbSession session) {
+    Preconditions.checkNotNull(key, ACTIVE_RULE_KEY_CANNOT_BE_NULL);
+    Preconditions.checkNotNull(name, PARAMETER_NAME_CANNOT_BE_NULL);
+    Optional<ActiveRuleDto> activeRule = selectByKey(session, key);
+    if (activeRule.isPresent()) {
+      return mapper(session).selectParamByActiveRuleAndKey(activeRule.get().getId(), name);
+    }
+    return null;
+  }
+
+  public void deleteParamsByRuleParam(DbSession dbSession, RuleDto rule, String paramKey) {
+    List<ActiveRuleDto> activeRules = selectByRule(dbSession, rule);
+    for (ActiveRuleDto activeRule : activeRules) {
+      for (ActiveRuleParamDto activeParam : selectParamsByActiveRuleKey(dbSession, activeRule.getKey())) {
+        if (activeParam.getKey().equals(paramKey)) {
+          deleteParam(dbSession, activeRule, activeParam);
+        }
+      }
+    }
+  }
+
+  private ActiveRuleMapper mapper(DbSession session) {
+    return session.getMapper(ActiveRuleMapper.class);
+  }
+}
index 17aa911ad9d04ef0450a4dbfcee4cc3b73aaf3a1..2f6328e413da5c2502273e52b9b17ab18a45a8c3 100644 (file)
@@ -36,17 +36,20 @@ public class ActiveRuleDto extends Dto<ActiveRuleKey> {
   public static final String INHERITED = ActiveRule.INHERITED;
   public static final String OVERRIDES = ActiveRule.OVERRIDES;
 
-  private String repository;
-  private String ruleField;
-  private String profileKey;
-
   private Integer id;
   private Integer profileId;
   private Integer ruleId;
   private Integer severity;
   private String inheritance;
-  //This field do not exists in db, it's only retrieve by joins
+
+  private long createdAtInMs;
+  private long updatedAtInMs;
+
+  //These fields do not exists in db, it's only retrieve by joins
   private Integer parentId;
+  private String repository;
+  private String ruleField;
+  private String profileKey;
 
   /**
    * @deprecated for internal use, should be private
@@ -77,7 +80,6 @@ public class ActiveRuleDto extends Dto<ActiveRuleKey> {
     return profileId;
   }
 
-  // TODO mark as private
   public ActiveRuleDto setProfileId(Integer profileId) {
     this.profileId = profileId;
     return this;
@@ -87,7 +89,6 @@ public class ActiveRuleDto extends Dto<ActiveRuleKey> {
     return ruleId;
   }
 
-  // TODO mark as private
   public ActiveRuleDto setRuleId(Integer ruleId) {
     this.ruleId = ruleId;
     return this;
@@ -122,10 +123,12 @@ public class ActiveRuleDto extends Dto<ActiveRuleKey> {
   }
 
   @CheckForNull
+  @Deprecated
   public Integer getParentId() {
     return parentId;
   }
 
+  @Deprecated
   public ActiveRuleDto setParentId(@Nullable Integer parentId) {
     this.parentId = parentId;
     return this;
@@ -139,6 +142,24 @@ public class ActiveRuleDto extends Dto<ActiveRuleKey> {
     return StringUtils.equals(OVERRIDES, inheritance);
   }
 
+  public long getUpdatedAtInMs() {
+    return updatedAtInMs;
+  }
+
+  public ActiveRuleDto setUpdatedAtInMs(long updatedAtInMs) {
+    this.updatedAtInMs = updatedAtInMs;
+    return this;
+  }
+
+  public long getCreatedAtInMs() {
+    return createdAtInMs;
+  }
+
+  public ActiveRuleDto setCreatedAtInMs(long createdAtInMs) {
+    this.createdAtInMs = createdAtInMs;
+    return this;
+  }
+
   public static ActiveRuleDto createFor(QualityProfileDto profileDto, RuleDto ruleDto) {
     Preconditions.checkNotNull(profileDto.getId(), "Profile is not persisted");
     Preconditions.checkNotNull(ruleDto.getId(), "Rule is not persisted");
index 70f610d12d2545c0ee42c5faf3e74453369c5c97..089adea159e9d8fbee421669944449364f2c1213 100644 (file)
@@ -32,8 +32,8 @@ import org.sonar.api.utils.System2;
 import org.sonar.db.Dao;
 import org.sonar.db.DbSession;
 import org.sonar.db.MyBatis;
-import org.sonar.db.component.ComponentDto;
 import org.sonar.db.RowNotFoundException;
+import org.sonar.db.component.ComponentDto;
 
 @ServerSide
 public class QualityProfileDao implements Dao {
@@ -209,21 +209,11 @@ public class QualityProfileDao implements Dao {
     }
   }
 
-  /**
-   * @deprecated Replaced by
-   *    {@link #selectByKey(DbSession, String)}
-   */
-  @Deprecated
   @CheckForNull
   public QualityProfileDto selectById(DbSession session, int id) {
     return mapper(session).selectById(id);
   }
 
-  /**
-   * @deprecated Replaced by
-   *    {@link #selectByKey(DbSession, String)}
-   */
-  @Deprecated
   @CheckForNull
   public QualityProfileDto selectById(int id) {
     DbSession session = mybatis.openSession(false);
index 00f008aca51099ec6f9563b603b6ef307875acde..ebeecc1030dcf562a142a167184036630aabba82 100644 (file)
@@ -13,7 +13,9 @@
     r.plugin_name as "repository",
     qp.kee as "profileKey",
     a.created_at as "createdAt",
-    a.updated_at as "updatedAt"
+    a.updated_at as "updatedAt",
+    a.created_at_ms as "createdAtInMs",
+    a.updated_at_ms as "updatedAtInMs"
   </sql>
 
   <sql id="activeRuleKeyJoin">
@@ -21,6 +23,7 @@
     INNER JOIN rules r ON r.id = a.rule_id
   </sql>
 
+  <!-- Should be removed when ActiveRuleDao v2 will be removed -->
   <sql id="activeRuleColumns">
     a.id,
     a.profile_id as profileId,
     a.inheritance as inheritance,
     active_rule_parent.id as parentId,
     a.created_at as "createdAt",
-    a.updated_at as "updatedAt"
+    a.updated_at as "updatedAt",
+    a.created_at_ms as "createdAtInMs",
+    a.updated_at_ms as "updatedAtInMs"
   </sql>
 
+  <!-- Should be removed when ActiveRuleDao v2 will be removed -->
   <sql id="activeRuleJoin">
     INNER JOIN rules_profiles qp ON qp.id=a.profile_id
     LEFT JOIN rules_profiles profile_parent ON profile_parent.kee=qp.parent_kee
@@ -52,8 +58,8 @@
   </select>
 
   <insert id="insert" parameterType="ActiveRule" keyColumn="id" useGeneratedKeys="true" keyProperty="id">
-    INSERT INTO active_rules (profile_id, rule_id, failure_level, inheritance, created_at, updated_at)
-    VALUES (#{profileId}, #{ruleId}, #{severity}, #{inheritance}, #{createdAt}, #{updatedAt})
+    INSERT INTO active_rules (profile_id, rule_id, failure_level, inheritance, created_at, updated_at, created_at_ms, updated_at_ms)
+    VALUES (#{profileId}, #{ruleId}, #{severity}, #{inheritance}, #{createdAt}, #{updatedAt}, #{createdAtInMs}, #{updatedAtInMs})
   </insert>
 
   <update id="update" parameterType="ActiveRule">
@@ -62,7 +68,8 @@
     rule_id=#{ruleId},
     failure_level=#{severity},
     inheritance=#{inheritance},
-    updated_at=#{updatedAt}
+    updated_at=#{updatedAt},
+    updated_at_ms=#{updatedAtInMs}
     WHERE id=#{id}
   </update>
 
diff --git a/sonar-db/src/test/java/org/sonar/db/qualityprofile/ActiveRuleDaoTest.java b/sonar-db/src/test/java/org/sonar/db/qualityprofile/ActiveRuleDaoTest.java
new file mode 100644 (file)
index 0000000..1db32fe
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.db.qualityprofile;
+
+// TODO
+public class ActiveRuleDaoTest {
+
+}