diff options
34 files changed, 1789 insertions, 337 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel1.java b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel1.java index 6493e4d5c9c..52c7f87c18f 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel1.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel1.java @@ -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, diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java index 5bb7a911c2e..8462ff54dd2 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java @@ -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, diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevelStartup.java b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevelStartup.java index fcd79320669..6451012791d 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevelStartup.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevelStartup.java @@ -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(); diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ActiveRule.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ActiveRule.java index 7be933e2bd1..4d497c06eb7 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ActiveRule.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ActiveRule.java @@ -20,16 +20,15 @@ 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); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileLoader.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileLoader.java index 5bd527645bb..7d1b475e926 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileLoader.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileLoader.java @@ -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()) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/db/ActiveRuleDao.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/db/ActiveRuleDao.java index 604f077a301..a9b0656fa4d 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/db/ActiveRuleDao.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/db/ActiveRuleDao.java @@ -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)"; diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleDoc.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleDoc.java index 0400bda41df..57fe9150c09 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleDoc.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleDoc.java @@ -20,16 +20,26 @@ 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; } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleIndex.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleIndex.java index b83cba83e3b..1601f33f1ea 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleIndex.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleIndex.java @@ -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 index 00000000000..01dcaaff675 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleIndex2.java @@ -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 index 00000000000..2a3b5f4ec42 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleIndexer.java @@ -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(); + } + } + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleNormalizer.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleNormalizer.java index 397c84ebfd0..d9b09806d84 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleNormalizer.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleNormalizer.java @@ -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 index 00000000000..1b262701eb9 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleResultSetIterator.java @@ -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; + } + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/InheritanceAction.java b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/InheritanceAction.java index d91276a8232..d2c171ae224 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/InheritanceAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/InheritanceAction.java @@ -20,28 +20,27 @@ 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(); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndex2.java b/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndex2.java index 2fbb0fcef1d..498cbc4db39 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndex2.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndex2.java @@ -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()))); diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndexDefinition.java b/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndexDefinition.java index 4096edfcece..158c34afc2f 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndexDefinition.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndexDefinition.java @@ -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); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndexer.java b/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndexer.java index 01ff26443e4..88ce05d3153 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndexer.java +++ b/server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndexer.java @@ -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()); + // } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/search/IndexSynchronizer.java b/server/sonar-server/src/main/java/org/sonar/server/search/IndexSynchronizer.java index 25e4032fd42..657d027a60b 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/search/IndexSynchronizer.java +++ b/server/sonar-server/src/main/java/org/sonar/server/search/IndexSynchronizer.java @@ -19,17 +19,12 @@ */ 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); - } - } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/es/EsTester.java b/server/sonar-server/src/test/java/org/sonar/server/es/EsTester.java index d32c82f7b1b..09970f7b63d 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/es/EsTester.java +++ b/server/sonar-server/src/test/java/org/sonar/server/es/EsTester.java @@ -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 index 00000000000..9bc6811b19e --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/index/ActiveRuleDocTesting.java @@ -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 index 00000000000..28db5d4405e --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/index/ActiveRuleIndex2Test.java @@ -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 index 00000000000..3fbd501a52b --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/index/ActiveRuleIndexerTest.java @@ -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 index 00000000000..6631d172052 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/qualityprofile/index/ActiveRuleResultSetIteratorTest.java @@ -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(); + } + } + +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndex2Test.java b/server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndex2Test.java index 66072c24953..58848982fc3 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndex2Test.java +++ b/server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndex2Test.java @@ -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()); + } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndexDefinitionTest.java b/server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndexDefinitionTest.java index 502ece8cd97..dfbc11f13f8 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndexDefinitionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndexDefinitionTest.java @@ -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 index 00000000000..a2f2f9503f7 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/index/ActiveRuleIndexerTest/index.xml @@ -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 index 00000000000..32aecb5f149 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/index/ActiveRuleResultSetIteratorTest/active_rule_with_inherited_inheritance.xml @@ -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 index 00000000000..a20b455e9ef --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/index/ActiveRuleResultSetIteratorTest/active_rule_with_overrides_inheritance.xml @@ -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 index 00000000000..a2f2f9503f7 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/index/ActiveRuleResultSetIteratorTest/one_active_rule.xml @@ -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 index 00000000000..bda9c4da216 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/index/ActiveRuleResultSetIteratorTest/shared.xml @@ -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 index 00000000000..808b85f593e --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/qualityprofile/ActiveRuleDao.java @@ -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); + } +} diff --git a/sonar-db/src/main/java/org/sonar/db/qualityprofile/ActiveRuleDto.java b/sonar-db/src/main/java/org/sonar/db/qualityprofile/ActiveRuleDto.java index 17aa911ad9d..2f6328e413d 100644 --- a/sonar-db/src/main/java/org/sonar/db/qualityprofile/ActiveRuleDto.java +++ b/sonar-db/src/main/java/org/sonar/db/qualityprofile/ActiveRuleDto.java @@ -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"); diff --git a/sonar-db/src/main/java/org/sonar/db/qualityprofile/QualityProfileDao.java b/sonar-db/src/main/java/org/sonar/db/qualityprofile/QualityProfileDao.java index 70f610d12d2..089adea159e 100644 --- a/sonar-db/src/main/java/org/sonar/db/qualityprofile/QualityProfileDao.java +++ b/sonar-db/src/main/java/org/sonar/db/qualityprofile/QualityProfileDao.java @@ -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); diff --git a/sonar-db/src/main/resources/org/sonar/db/qualityprofile/ActiveRuleMapper.xml b/sonar-db/src/main/resources/org/sonar/db/qualityprofile/ActiveRuleMapper.xml index 00f008aca51..ebeecc1030d 100644 --- a/sonar-db/src/main/resources/org/sonar/db/qualityprofile/ActiveRuleMapper.xml +++ b/sonar-db/src/main/resources/org/sonar/db/qualityprofile/ActiveRuleMapper.xml @@ -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, @@ -29,9 +32,12 @@ 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 index 00000000000..1db32fe9e92 --- /dev/null +++ b/sonar-db/src/test/java/org/sonar/db/qualityprofile/ActiveRuleDaoTest.java @@ -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 { + +} |