Browse Source

SONAR-7330 Add active rule index definition

tags/5.5-M6
Julien Lancelot 8 years ago
parent
commit
5d148ea07d
34 changed files with 1789 additions and 337 deletions
  1. 2
    0
      server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel1.java
  2. 2
    0
      server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
  3. 0
    1
      server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevelStartup.java
  4. 3
    4
      server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ActiveRule.java
  5. 7
    17
      server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileLoader.java
  6. 3
    0
      server/sonar-server/src/main/java/org/sonar/server/qualityprofile/db/ActiveRuleDao.java
  7. 78
    28
      server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleDoc.java
  8. 5
    9
      server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleIndex.java
  9. 229
    0
      server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleIndex2.java
  10. 155
    0
      server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleIndexer.java
  11. 2
    2
      server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleNormalizer.java
  12. 105
    0
      server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleResultSetIterator.java
  13. 10
    11
      server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/InheritanceAction.java
  14. 10
    12
      server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndex2.java
  15. 30
    0
      server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndexDefinition.java
  16. 11
    13
      server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndexer.java
  17. 1
    32
      server/sonar-server/src/main/java/org/sonar/server/search/IndexSynchronizer.java
  18. 2
    2
      server/sonar-server/src/test/java/org/sonar/server/es/EsTester.java
  19. 39
    0
      server/sonar-server/src/test/java/org/sonar/server/qualityprofile/index/ActiveRuleDocTesting.java
  20. 242
    0
      server/sonar-server/src/test/java/org/sonar/server/qualityprofile/index/ActiveRuleIndex2Test.java
  21. 188
    0
      server/sonar-server/src/test/java/org/sonar/server/qualityprofile/index/ActiveRuleIndexerTest.java
  22. 166
    0
      server/sonar-server/src/test/java/org/sonar/server/qualityprofile/index/ActiveRuleResultSetIteratorTest.java
  23. 158
    182
      server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndex2Test.java
  24. 1
    1
      server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndexDefinitionTest.java
  25. 13
    0
      server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/index/ActiveRuleIndexerTest/index.xml
  26. 20
    0
      server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/index/ActiveRuleResultSetIteratorTest/active_rule_with_inherited_inheritance.xml
  27. 20
    0
      server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/index/ActiveRuleResultSetIteratorTest/active_rule_with_overrides_inheritance.xml
  28. 13
    0
      server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/index/ActiveRuleResultSetIteratorTest/one_active_rule.xml
  29. 37
    0
      server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/index/ActiveRuleResultSetIteratorTest/shared.xml
  30. 171
    0
      sonar-db/src/main/java/org/sonar/db/qualityprofile/ActiveRuleDao.java
  31. 28
    7
      sonar-db/src/main/java/org/sonar/db/qualityprofile/ActiveRuleDto.java
  32. 1
    11
      sonar-db/src/main/java/org/sonar/db/qualityprofile/QualityProfileDao.java
  33. 12
    5
      sonar-db/src/main/resources/org/sonar/db/qualityprofile/ActiveRuleMapper.xml
  34. 25
    0
      sonar-db/src/test/java/org/sonar/db/qualityprofile/ActiveRuleDaoTest.java

+ 2
- 0
server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel1.java View File

@@ -44,6 +44,7 @@ import org.sonar.server.platform.ServerSettings;
import org.sonar.server.platform.TempFolderProvider;
import org.sonar.server.qualityprofile.db.ActiveRuleDao;
import org.sonar.server.qualityprofile.index.ActiveRuleIndex;
import org.sonar.server.qualityprofile.index.ActiveRuleIndex2;
import org.sonar.server.qualityprofile.index.ActiveRuleNormalizer;
import org.sonar.server.ruby.PlatformRackBridge;
import org.sonar.server.rule.db.RuleDao;
@@ -110,6 +111,7 @@ public class PlatformLevel1 extends PlatformLevel {

// rules/qprofiles
RuleIndex2.class,
ActiveRuleIndex2.class,
RuleNormalizer.class,
ActiveRuleNormalizer.class,
RuleIndex.class,

+ 2
- 0
server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java View File

@@ -223,6 +223,7 @@ import org.sonar.server.qualityprofile.QProfileService;
import org.sonar.server.qualityprofile.QProfiles;
import org.sonar.server.qualityprofile.RuleActivator;
import org.sonar.server.qualityprofile.RuleActivatorContextFactory;
import org.sonar.server.qualityprofile.index.ActiveRuleIndexer;
import org.sonar.server.qualityprofile.ws.BackupAction;
import org.sonar.server.qualityprofile.ws.BulkRuleActivationActions;
import org.sonar.server.qualityprofile.ws.ChangeParentAction;
@@ -387,6 +388,7 @@ public class PlatformLevel4 extends PlatformLevel {
UpdateCenterWs.class,

// quality profile
ActiveRuleIndexer.class,
XMLProfileParser.class,
XMLProfileSerializer.class,
AnnotationProfileParser.class,

+ 0
- 1
server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevelStartup.java View File

@@ -74,7 +74,6 @@ public class PlatformLevelStartup extends PlatformLevel {
DoPrivileged.execute(new DoPrivileged.Task(getComponentByType(ThreadLocalUserSession.class)) {
@Override
protected void doPrivileged() {
getComponentByType(IndexSynchronizer.class).executeDeprecated();
PlatformLevelStartup.super.start();
getComponentByType(IndexSynchronizer.class).execute();
getComponentByType(ServerLifecycleNotifier.class).notifyStart();

+ 3
- 4
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ActiveRule.java View File

@@ -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);
}

+ 7
- 17
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/QProfileLoader.java View File

@@ -21,26 +21,24 @@ package org.sonar.server.qualityprofile;

import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import org.sonar.api.server.ServerSide;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.annotation.CheckForNull;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.RuleStatus;
import org.sonar.api.server.ServerSide;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.qualityprofile.ActiveRuleKey;
import org.sonar.db.qualityprofile.QualityProfileDto;
import org.sonar.db.DbClient;
import org.sonar.server.qualityprofile.index.ActiveRuleIndex;
import org.sonar.server.rule.index.RuleIndex;
import org.sonar.server.rule.index.RuleQuery;
import org.sonar.server.search.FacetValue;
import org.sonar.server.search.IndexClient;
import org.sonar.server.search.QueryContext;

import javax.annotation.CheckForNull;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.sonar.server.user.UserSession;

@ServerSide
@@ -102,10 +100,6 @@ public class QProfileLoader {
return index.get(ActiveRuleIndex.class).findByProfile(key);
}

public long countActiveRulesByProfile(String key) {
return index.get(ActiveRuleIndex.class).countByQualityProfileKey(key);
}

public Map<String, Long> countAllActiveRules() {
Map<String, Long> counts = new HashMap<>();
for (Map.Entry<String, Long> entry : index.get(ActiveRuleIndex.class).countAllByQualityProfileKey().entrySet()) {
@@ -114,10 +108,6 @@ public class QProfileLoader {
return counts;
}

public Multimap<String, FacetValue> getStatsByProfile(String key) {
return index.get(ActiveRuleIndex.class).getStatsByProfileKey(key);
}

public Map<String, Multimap<String, FacetValue>> getAllProfileStats() {
List<String> keys = Lists.newArrayList();
for (QualityProfileDto profile : this.findAll()) {

+ 3
- 0
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/db/ActiveRuleDao.java View File

@@ -22,6 +22,8 @@ package org.sonar.server.qualityprofile.db;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import java.util.List;
import javax.annotation.CheckForNull;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.CheckForNull;
@@ -44,6 +46,7 @@ import org.sonar.server.search.IndexDefinition;

import static java.util.Collections.emptyList;

@Deprecated
public class ActiveRuleDao extends BaseDao<ActiveRuleMapper, ActiveRuleDto, ActiveRuleKey> {

private static final String QUALITY_PROFILE_IS_NOT_PERSISTED = "Quality profile is not persisted (missing id)";

+ 78
- 28
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleDoc.java View File

@@ -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;
}
}

+ 5
- 9
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleIndex.java View File

@@ -19,7 +19,6 @@
*/
package org.sonar.server.qualityprofile.index;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Multimap;
import java.util.ArrayList;
import java.util.HashMap;
@@ -48,6 +47,7 @@ import org.sonar.server.search.IndexDefinition;
import org.sonar.server.search.IndexField;
import org.sonar.server.search.SearchClient;

@Deprecated
public class ActiveRuleIndex extends BaseIndex<ActiveRule, ActiveRuleDto, ActiveRuleKey> {

public static final String COUNT_ACTIVE_RULES = "countActiveRules";
@@ -138,7 +138,7 @@ public class ActiveRuleIndex extends BaseIndex<ActiveRule, ActiveRuleDto, Active
.setQuery(QueryBuilders.filteredQuery(QueryBuilders.matchAllQuery(), FilterBuilders.boolFilter()
.must(FilterBuilders.termFilter(ActiveRuleNormalizer.ActiveRuleField.PROFILE_KEY.field(), key))
.mustNot(FilterBuilders.hasParentFilter(this.getParentType(),
FilterBuilders.termFilter(RuleIndexDefinition.FIELD_STATUS, RuleStatus.REMOVED.name())))))
FilterBuilders.termFilter(RuleIndexDefinition.FIELD_RULE_STATUS, RuleStatus.REMOVED.name())))))
.setRouting(key);

SearchResponse response = request.get();
@@ -153,18 +153,14 @@ public class ActiveRuleIndex extends BaseIndex<ActiveRule, ActiveRuleDto, Active
return countByField(ActiveRuleNormalizer.ActiveRuleField.PROFILE_KEY,
FilterBuilders.hasParentFilter(IndexDefinition.RULE.getIndexType(),
FilterBuilders.notFilter(
FilterBuilders.termFilter(RuleIndexDefinition.FIELD_STATUS, "REMOVED")))).get(key);
FilterBuilders.termFilter(RuleIndexDefinition.FIELD_RULE_STATUS, "REMOVED")))).get(key);
}

public Map<String, Long> countAllByQualityProfileKey() {
return countByField(ActiveRuleNormalizer.ActiveRuleField.PROFILE_KEY,
FilterBuilders.hasParentFilter(IndexDefinition.RULE.getIndexType(),
FilterBuilders.notFilter(
FilterBuilders.termFilter(RuleIndexDefinition.FIELD_STATUS, "REMOVED"))));
}

public Multimap<String, FacetValue> getStatsByProfileKey(String key) {
return getStatsByProfileKeys(ImmutableList.of(key)).get(key);
FilterBuilders.termFilter(RuleIndexDefinition.FIELD_RULE_STATUS, "REMOVED"))));
}

public Map<String, Multimap<String, FacetValue>> getStatsByProfileKeys(List<String> keys) {
@@ -173,7 +169,7 @@ public class ActiveRuleIndex extends BaseIndex<ActiveRule, ActiveRuleDto, Active
QueryBuilders.termsQuery(ActiveRuleNormalizer.ActiveRuleField.PROFILE_KEY.field(), keys),
FilterBuilders.boolFilter()
.mustNot(FilterBuilders.hasParentFilter(this.getParentType(),
FilterBuilders.termFilter(RuleIndexDefinition.FIELD_STATUS, RuleStatus.REMOVED.name())))))
FilterBuilders.termFilter(RuleIndexDefinition.FIELD_RULE_STATUS, RuleStatus.REMOVED.name())))))
.addAggregation(AggregationBuilders.terms(ActiveRuleNormalizer.ActiveRuleField.PROFILE_KEY.field())
.field(ActiveRuleNormalizer.ActiveRuleField.PROFILE_KEY.field()).size(0)
.subAggregation(AggregationBuilders.terms(ActiveRuleNormalizer.ActiveRuleField.INHERITANCE.field())

+ 229
- 0
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleIndex2.java View File

@@ -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);
}
}

}

+ 155
- 0
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleIndexer.java View File

@@ -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();
}
}

}

+ 2
- 2
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleNormalizer.java View File

@@ -112,9 +112,9 @@ public class ActiveRuleNormalizer extends BaseNormalizer<ActiveRuleDto, ActiveRu

/* Creating updateRequest */
requests.add(new UpdateRequest()
.id(key.toString())
.routing(key.ruleKey().toString())
.id(activeRuleDto.getKey().toString())
.parent(activeRuleDto.getKey().ruleKey().toString())
.parent(key.ruleKey().toString())
.doc(newRule)
.upsert(getUpsertFor(ActiveRuleField.ALL_FIELDS, newRule)));


+ 105
- 0
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/index/ActiveRuleResultSetIterator.java View File

@@ -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;
}

}

+ 10
- 11
server/sonar-server/src/main/java/org/sonar/server/qualityprofile/ws/InheritanceAction.java View File

@@ -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();
}

+ 10
- 12
server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndex2.java View File

@@ -64,8 +64,6 @@ import org.sonar.server.es.BaseIndex;
import org.sonar.server.es.EsClient;
import org.sonar.server.es.SearchIdResult;
import org.sonar.server.es.SearchOptions;
import org.sonar.server.qualityprofile.index.ActiveRuleNormalizer;
import org.sonar.server.search.IndexDefinition;
import org.sonar.server.search.IndexField;
import org.sonar.server.search.StickyFacetBuilder;

@@ -276,9 +274,9 @@ public class RuleIndex2 extends BaseIndex {

// ActiveRule Filter (profile and inheritance)
BoolFilterBuilder childrenFilter = FilterBuilders.boolFilter();
this.addTermFilter(childrenFilter, ActiveRuleNormalizer.ActiveRuleField.PROFILE_KEY.field(), query.getQProfileKey());
this.addTermFilter(childrenFilter, ActiveRuleNormalizer.ActiveRuleField.INHERITANCE.field(), query.getInheritance());
this.addTermFilter(childrenFilter, ActiveRuleNormalizer.ActiveRuleField.SEVERITY.field(), query.getActiveSeverities());
addTermFilter(childrenFilter, RuleIndexDefinition.FIELD_ACTIVE_RULE_PROFILE_KEY, query.getQProfileKey());
addTermFilter(childrenFilter, RuleIndexDefinition.FIELD_ACTIVE_RULE_INHERITANCE, query.getInheritance());
addTermFilter(childrenFilter, RuleIndexDefinition.FIELD_ACTIVE_RULE_SEVERITY, query.getActiveSeverities());

// ChildQuery
FilterBuilder childQuery;
@@ -291,12 +289,12 @@ public class RuleIndex2 extends BaseIndex {
/** Implementation of activation query */
if (Boolean.TRUE.equals(query.getActivation())) {
filters.put("activation",
FilterBuilders.hasChildFilter(IndexDefinition.ACTIVE_RULE.getIndexType(),
FilterBuilders.hasChildFilter(RuleIndexDefinition.TYPE_ACTIVE_RULE,
childQuery));
} else if (Boolean.FALSE.equals(query.getActivation())) {
filters.put("activation",
FilterBuilders.boolFilter().mustNot(
FilterBuilders.hasChildFilter(IndexDefinition.ACTIVE_RULE.getIndexType(),
FilterBuilders.hasChildFilter(RuleIndexDefinition.TYPE_ACTIVE_RULE,
childQuery)));
}

@@ -383,13 +381,13 @@ public class RuleIndex2 extends BaseIndex {
// so the rule filter has to be used as parent filter for active rules
// from which we remove filters that concern active rules ("activation")
HasParentFilterBuilder ruleFilter = FilterBuilders.hasParentFilter(
IndexDefinition.RULE.getIndexType(),
RuleIndexDefinition.TYPE_RULE,
stickyFacetBuilder.getStickyFacetFilter("activation"));

// Rebuilding the active rule filter without severities
BoolFilterBuilder childrenFilter = FilterBuilders.boolFilter();
this.addTermFilter(childrenFilter, ActiveRuleNormalizer.ActiveRuleField.PROFILE_KEY.field(), query.getQProfileKey());
this.addTermFilter(childrenFilter, ActiveRuleNormalizer.ActiveRuleField.INHERITANCE.field(), query.getInheritance());
this.addTermFilter(childrenFilter, RuleIndexDefinition.FIELD_ACTIVE_RULE_PROFILE_KEY, query.getQProfileKey());
this.addTermFilter(childrenFilter, RuleIndexDefinition.FIELD_ACTIVE_RULE_INHERITANCE, query.getInheritance());
FilterBuilder activeRuleFilter;
if (childrenFilter.hasClauses()) {
activeRuleFilter = childrenFilter.must(ruleFilter);
@@ -398,13 +396,13 @@ public class RuleIndex2 extends BaseIndex {
}

AggregationBuilder activeSeverities = AggregationBuilders.children(FACET_ACTIVE_SEVERITIES + "_children")
.childType(IndexDefinition.ACTIVE_RULE.getIndexType())
.childType(RuleIndexDefinition.TYPE_ACTIVE_RULE)
.subAggregation(AggregationBuilders.filter(FACET_ACTIVE_SEVERITIES + "_filter")
.filter(activeRuleFilter)
.subAggregation(
AggregationBuilders
.terms(FACET_ACTIVE_SEVERITIES)
.field(ActiveRuleNormalizer.ActiveRuleField.SEVERITY.field())
.field(RuleIndexDefinition.FIELD_ACTIVE_RULE_SEVERITY)
.include(Joiner.on('|').join(Severity.ALL))
.size(Severity.ALL.size())));


+ 30
- 0
server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndexDefinition.java View File

@@ -58,6 +58,19 @@ public class RuleIndexDefinition implements IndexDefinition {
RuleIndexDefinition.FIELD_RULE_KEY
);

// Active rule fields

public static final String TYPE_ACTIVE_RULE = "activeRule";
public static final String FIELD_ACTIVE_RULE_KEY = "key";
public static final String FIELD_ACTIVE_RULE_REPOSITORY = "repo";
public static final String FIELD_ACTIVE_RULE_INHERITANCE = "inheritance";
public static final String FIELD_ACTIVE_RULE_PROFILE_KEY = "profile";
public static final String FIELD_ACTIVE_RULE_SEVERITY = "severity";
public static final String FIELD_ACTIVE_RULE_PARENT_KEY = "parentKey";
public static final String FIELD_ACTIVE_RULE_RULE_KEY = "ruleKey";
public static final String FIELD_ACTIVE_RULE_CREATED_AT = "createdAt";
public static final String FIELD_ACTIVE_RULE_UPDATED_AT = "updatedAt";

private final Settings settings;

public RuleIndexDefinition(Settings settings) {
@@ -96,5 +109,22 @@ public class RuleIndexDefinition implements IndexDefinition {

ruleMapping.createLongField(FIELD_RULE_CREATED_AT);
ruleMapping.createLongField(FIELD_RULE_UPDATED_AT);

// Active rule type
NewIndex.NewIndexType activeRuleMapping = index.createType(RuleIndexDefinition.TYPE_ACTIVE_RULE);
activeRuleMapping.setAttribute("_id", ImmutableMap.of("path", RuleIndexDefinition.FIELD_ACTIVE_RULE_KEY));
activeRuleMapping.setAttribute("_parent", ImmutableMap.of("type", RuleIndexDefinition.TYPE_RULE));
activeRuleMapping.setAttribute("_routing", ImmutableMap.of("required", true, "path", RuleIndexDefinition.FIELD_ACTIVE_RULE_REPOSITORY));

activeRuleMapping.stringFieldBuilder(RuleIndexDefinition.FIELD_ACTIVE_RULE_KEY).enableSorting().enableGramSearch().build();
activeRuleMapping.stringFieldBuilder(RuleIndexDefinition.FIELD_ACTIVE_RULE_RULE_KEY).disableSearch().docValues().build();
activeRuleMapping.stringFieldBuilder(RuleIndexDefinition.FIELD_ACTIVE_RULE_REPOSITORY).disableSearch().docValues().build();
activeRuleMapping.stringFieldBuilder(RuleIndexDefinition.FIELD_ACTIVE_RULE_PROFILE_KEY).docValues().build();
activeRuleMapping.stringFieldBuilder(RuleIndexDefinition.FIELD_ACTIVE_RULE_INHERITANCE).docValues().build();
activeRuleMapping.stringFieldBuilder(RuleIndexDefinition.FIELD_ACTIVE_RULE_SEVERITY).docValues().build();
activeRuleMapping.stringFieldBuilder(RuleIndexDefinition.FIELD_ACTIVE_RULE_PARENT_KEY).disableSearch().docValues().build();

activeRuleMapping.createLongField(RuleIndexDefinition.FIELD_ACTIVE_RULE_CREATED_AT);
activeRuleMapping.createLongField(RuleIndexDefinition.FIELD_ACTIVE_RULE_UPDATED_AT);
}
}

+ 11
- 13
server/sonar-server/src/main/java/org/sonar/server/rule/index/RuleIndexer.java View File

@@ -19,7 +19,6 @@
*/
package org.sonar.server.rule.index;

import com.google.common.annotations.VisibleForTesting;
import java.util.Iterator;
import org.elasticsearch.action.index.IndexRequest;
import org.sonar.db.DbClient;
@@ -46,8 +45,7 @@ public class RuleIndexer extends BaseIndexer {
return doIndex(createBulkIndexer(false), lastUpdatedAt);
}

@VisibleForTesting
void index(Iterator<RuleDoc> rules) {
public void index(Iterator<RuleDoc> rules) {
doIndex(createBulkIndexer(false), rules);
}

@@ -70,11 +68,11 @@ public class RuleIndexer extends BaseIndexer {
while (rules.hasNext()) {
RuleDoc rule = rules.next();
// TODO when active rule is not more DAO v2, restore deleting of REMOVED rules and also remove active rules linked to this rule
// if (rule.status() == RuleStatus.REMOVED) {
// bulk.add(newDeleteRequest(rule));
// } else {
// }
bulk.add(newUpsertRequest(rule));
// if (rule.status() == RuleStatus.REMOVED) {
// bulk.add(newDeleteRequest(rule));
// } else {
// }
bulk.add(newIndexRequest(rule));

// it's more efficient to sort programmatically than in SQL on some databases (MySQL for instance)
maxDate = Math.max(maxDate, rule.updatedAtAtAsLong());
@@ -89,15 +87,15 @@ public class RuleIndexer extends BaseIndexer {
return bulk;
}

private IndexRequest newUpsertRequest(RuleDoc rule) {
private IndexRequest newIndexRequest(RuleDoc rule) {
return new IndexRequest(INDEX, TYPE_RULE, rule.key().toString())
.routing(rule.repository())
.source(rule.getFields());
}

// private DeleteRequest newDeleteRequest(RuleDoc rule) {
// return new DeleteRequest(INDEX, TYPE_RULE, rule.key().toString())
// .routing(rule.repository());
// }
// private DeleteRequest newDeleteRequest(RuleDoc rule) {
// return new DeleteRequest(INDEX, TYPE_RULE, rule.key().toString())
// .routing(rule.repository());
// }

}

+ 1
- 32
server/sonar-server/src/main/java/org/sonar/server/search/IndexSynchronizer.java View File

@@ -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);
}
}
}

+ 2
- 2
server/sonar-server/src/test/java/org/sonar/server/es/EsTester.java View File

@@ -222,7 +222,7 @@ public class EsTester extends ExternalResource {
}));
}

public List<String> getIds(String indexName, String typeName){
public List<String> getIds(String indexName, String typeName) {
return FluentIterable.from(getDocuments(indexName, typeName)).transform(SearchHitToId.INSTANCE).toList();
}

@@ -234,7 +234,7 @@ public class EsTester extends ExternalResource {
return client;
}

private enum SearchHitToId implements Function<SearchHit, String>{
private enum SearchHitToId implements Function<SearchHit, String> {
INSTANCE;

@Override

+ 39
- 0
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/index/ActiveRuleDocTesting.java View File

@@ -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);
}
}

+ 242
- 0
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/index/ActiveRuleIndex2Test.java View File

@@ -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());
}

}

+ 188
- 0
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/index/ActiveRuleIndexerTest.java View File

@@ -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());
}

}

+ 166
- 0
server/sonar-server/src/test/java/org/sonar/server/qualityprofile/index/ActiveRuleResultSetIteratorTest.java View File

@@ -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();
}
}

}

+ 158
- 182
server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndex2Test.java View File

@@ -26,14 +26,18 @@ import java.util.List;
import java.util.Map;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Ignore;
import org.junit.Test;
import org.sonar.api.config.Settings;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.RuleStatus;
import org.sonar.db.qualityprofile.ActiveRuleKey;
import org.sonar.db.rule.RuleTesting;
import org.sonar.server.es.EsTester;
import org.sonar.server.es.SearchIdResult;
import org.sonar.server.es.SearchOptions;
import org.sonar.server.qualityprofile.index.ActiveRuleDoc;
import org.sonar.server.qualityprofile.index.ActiveRuleDocTesting;
import org.sonar.server.qualityprofile.index.ActiveRuleIndexer;

import static java.util.Arrays.asList;
import static java.util.Collections.singleton;
@@ -42,26 +46,42 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.data.MapEntry.entry;
import static org.junit.Assert.fail;
import static org.sonar.api.rule.Severity.BLOCKER;
import static org.sonar.api.rule.Severity.CRITICAL;
import static org.sonar.api.rule.Severity.INFO;
import static org.sonar.api.rule.Severity.MAJOR;
import static org.sonar.api.rule.Severity.MINOR;
import static org.sonar.server.qualityprofile.ActiveRule.Inheritance.INHERITED;
import static org.sonar.server.qualityprofile.ActiveRule.Inheritance.OVERRIDES;
import static org.sonar.server.rule.index.RuleDocTesting.newDoc;
import static org.sonar.server.rule.index.RuleIndex2.FACET_LANGUAGES;
import static org.sonar.server.rule.index.RuleIndex2.FACET_REPOSITORIES;
import static org.sonar.server.rule.index.RuleIndex2.FACET_TAGS;
import static org.sonar.server.rule.index.RuleIndexDefinition.INDEX;
import static org.sonar.server.rule.index.RuleIndexDefinition.TYPE_ACTIVE_RULE;

public class RuleIndex2Test {

static final RuleKey RULE_KEY_1 = RuleTesting.XOO_X1;
static final RuleKey RULE_KEY_2 = RuleTesting.XOO_X2;
static final RuleKey RULE_KEY_3 = RuleTesting.XOO_X3;
static final RuleKey RULE_KEY_4 = RuleKey.of("xoo", "x4");

static final String QUALITY_PROFILE_KEY1 = "qp1";
static final String QUALITY_PROFILE_KEY2 = "qp2";

@ClassRule
public static EsTester tester = new EsTester().addDefinitions(new RuleIndexDefinition(new Settings()));

RuleIndex2 index;

RuleIndexer ruleIndexer;
ActiveRuleIndexer activeRuleIndexer;

@Before
public void setUp() {
tester.truncateIndices();
ruleIndexer = new RuleIndexer(null, tester.client());
activeRuleIndexer = new ActiveRuleIndexer(null, tester.client());
index = new RuleIndex2(tester.client());
}

@@ -306,7 +326,7 @@ public class RuleIndex2Test {
newDoc(RuleKey.of("java", "S002")).setStatus(RuleStatus.READY.name()));

RuleQuery query = new RuleQuery().setStatuses(asList(RuleStatus.DEPRECATED, RuleStatus.READY));
SearchIdResult results = index.search(query, new SearchOptions());
SearchIdResult<RuleKey> results = index.search(query, new SearchOptions());
assertThat(results.getIds()).containsOnly(RuleKey.of("java", "S002"));

// no results
@@ -323,194 +343,146 @@ public class RuleIndex2Test {
}

@Test
@Ignore
public void search_by_profile() {
// QualityProfileDto qualityProfileDto1 = QProfileTesting.newXooP1();
// QualityProfileDto qualityProfileDto2 = QProfileTesting.newXooP2();
// db.qualityProfileDao().insert(dbSession, qualityProfileDto1, qualityProfileDto2);
//
// RuleDto rule1 = RuleTesting.newXooX1();
// RuleDto rule2 = RuleTesting.newXooX2();
// RuleDto rule3 = RuleTesting.newXooX3();
// dao.insert(dbSession, rule1, rule2, rule3);
//
// db.activeRuleDao().insert(
// dbSession,
// ActiveRuleDto.createFor(qualityProfileDto1, rule1).setSeverity("BLOCKER"),
// ActiveRuleDto.createFor(qualityProfileDto2, rule1).setSeverity("BLOCKER"),
// ActiveRuleDto.createFor(qualityProfileDto1, rule2).setSeverity("BLOCKER"));
// dbSession.commit();
// dbSession.clearCache();
//
// // 1. get all active rules.
// Result<org.sonar.server.rule.Rule> result = index.search(new RuleQuery().setActivation(true),
// new SearchOptions());
// assertThat(result.getHits()).hasSize(2);
//
// // 2. get all inactive rules.
// result = index.search(new RuleQuery().setActivation(false),
// new SearchOptions());
// assertThat(result.getHits()).hasSize(1);
// assertThat(result.getHits().get(0).name()).isEqualTo(rule3.getName());
//
// // 3. get all rules not active on profile
// index.search(new RuleQuery().setActivation(false).setQProfileKey(qualityProfileDto2.getKey()),
// new SearchOptions());
// // TODO
// assertThat(result.getHits()).hasSize(1);
//
// // 4. get all active rules on profile
// result = index.search(new RuleQuery().setActivation(true)
// .setQProfileKey(qualityProfileDto2.getKey()),
// new SearchOptions());
// assertThat(result.getHits()).hasSize(1);
// assertThat(result.getHits().get(0).name()).isEqualTo(rule1.getName());
public void search_by_profile() throws InterruptedException {
indexRules(
newDoc(RULE_KEY_1),
newDoc(RULE_KEY_2),
newDoc(RULE_KEY_3));

indexActiveRules(
ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY1, RULE_KEY_1)),
ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY2, RULE_KEY_1)),
ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY1, RULE_KEY_2)));

assertThat(tester.countDocuments(INDEX, TYPE_ACTIVE_RULE)).isEqualTo(3);

// 1. get all active rules.
assertThat(index.search(new RuleQuery().setActivation(true), new SearchOptions()).getIds())
.containsOnly(RULE_KEY_1, RULE_KEY_2);

// 2. get all inactive rules.
assertThat(index.search(new RuleQuery().setActivation(false), new SearchOptions()).getIds())
.containsOnly(RULE_KEY_3);

// 3. get all rules not active on profile
assertThat(index.search(new RuleQuery().setActivation(false).setQProfileKey(QUALITY_PROFILE_KEY2), new SearchOptions()).getIds())
.containsOnly(RULE_KEY_2, RULE_KEY_3);

// 4. get all active rules on profile
assertThat(index.search(new RuleQuery().setActivation(true).setQProfileKey(QUALITY_PROFILE_KEY2), new SearchOptions()).getIds())
.containsOnly(RULE_KEY_1);
}

@Test
@Ignore
public void search_by_profile_and_inheritance() {
// QualityProfileDto qualityProfileDto1 = QProfileTesting.newXooP1();
// QualityProfileDto qualityProfileDto2 = QProfileTesting.newXooP2().setParentKee(QProfileTesting.XOO_P1_KEY);
// db.qualityProfileDao().insert(dbSession, qualityProfileDto1, qualityProfileDto2);
//
// RuleDto rule1 = RuleTesting.newDto(RuleKey.of("xoo", "S001"));
// RuleDto rule2 = RuleTesting.newDto(RuleKey.of("xoo", "S002"));
// RuleDto rule3 = RuleTesting.newDto(RuleKey.of("xoo", "S003"));
// RuleDto rule4 = RuleTesting.newDto(RuleKey.of("xoo", "S004"));
// dao.insert(dbSession, rule1, rule2, rule3, rule4);
//
// db.activeRuleDao().insert(
// dbSession,
// ActiveRuleDto.createFor(qualityProfileDto1, rule1)
// .setSeverity("BLOCKER"),
// ActiveRuleDto.createFor(qualityProfileDto1, rule2)
// .setSeverity("BLOCKER"),
// ActiveRuleDto.createFor(qualityProfileDto1, rule3)
// .setSeverity("BLOCKER"),
//
// ActiveRuleDto.createFor(qualityProfileDto2, rule1)
// .setSeverity("MINOR")
// .setInheritance(ActiveRule.Inheritance.INHERITED.name()),
// ActiveRuleDto.createFor(qualityProfileDto2, rule2)
// .setSeverity("BLOCKER")
// .setInheritance(ActiveRule.Inheritance.OVERRIDES.name()),
// ActiveRuleDto.createFor(qualityProfileDto2, rule3)
// .setSeverity("BLOCKER")
// .setInheritance(ActiveRule.Inheritance.INHERITED.name())
// );
//
// dbSession.commit();
//
// // 0. get all rules
// Result<org.sonar.server.rule.Rule> result = index.search(new RuleQuery(),
// new SearchOptions());
// assertThat(result.getHits()).hasSize(4);
//
// // 1. get all active rules
// result = index.search(new RuleQuery().setActivation(true),
// new SearchOptions());
// assertThat(result.getHits()).hasSize(3);
//
// // 2. get all inactive rules.
// result = index.search(new RuleQuery().setActivation(false),
// new SearchOptions());
// assertThat(result.getHits()).hasSize(1);
// assertThat(result.getHits().get(0).name()).isEqualTo(rule4.getName());
//
// // 3. get Inherited Rules on profile1
// result = index.search(new RuleQuery().setActivation(true)
// .setQProfileKey(qualityProfileDto1.getKey())
// .setInheritance(ImmutableSet.of(ActiveRule.Inheritance.INHERITED.name())),
// new SearchOptions()
// );
// assertThat(result.getHits()).hasSize(0);
//
// // 4. get Inherited Rules on profile2
// result = index.search(new RuleQuery().setActivation(true)
// .setQProfileKey(qualityProfileDto2.getKey())
// .setInheritance(ImmutableSet.of(ActiveRule.Inheritance.INHERITED.name())),
// new SearchOptions()
// );
// assertThat(result.getHits()).hasSize(2);
//
// // 5. get Overridden Rules on profile1
// result = index.search(new RuleQuery().setActivation(true)
// .setQProfileKey(qualityProfileDto1.getKey())
// .setInheritance(ImmutableSet.of(ActiveRule.Inheritance.OVERRIDES.name())),
// new SearchOptions()
// );
// assertThat(result.getHits()).hasSize(0);
//
// // 6. get Overridden Rules on profile2
// result = index.search(new RuleQuery().setActivation(true)
// .setQProfileKey(qualityProfileDto2.getKey())
// .setInheritance(ImmutableSet.of(ActiveRule.Inheritance.OVERRIDES.name())),
// new SearchOptions()
// );
// assertThat(result.getHits()).hasSize(1);
//
// // 7. get Inherited AND Overridden Rules on profile1
// result = index.search(new RuleQuery().setActivation(true)
// .setQProfileKey(qualityProfileDto1.getKey())
// .setInheritance(ImmutableSet.of(
// ActiveRule.Inheritance.INHERITED.name(), ActiveRule.Inheritance.OVERRIDES.name())),
// new SearchOptions()
// );
// assertThat(result.getHits()).hasSize(0);
//
// // 8. get Inherited AND Overridden Rules on profile2
// result = index.search(new RuleQuery().setActivation(true)
// .setQProfileKey(qualityProfileDto2.getKey())
// .setInheritance(ImmutableSet.of(
// ActiveRule.Inheritance.INHERITED.name(), ActiveRule.Inheritance.OVERRIDES.name())),
// new SearchOptions()
// );
// assertThat(result.getHits()).hasSize(3);
indexRules(
newDoc(RULE_KEY_1),
newDoc(RULE_KEY_2),
newDoc(RULE_KEY_3),
newDoc(RULE_KEY_4));

ActiveRuleKey activeRuleKey1 = ActiveRuleKey.of(QUALITY_PROFILE_KEY1, RULE_KEY_1);
ActiveRuleKey activeRuleKey2 = ActiveRuleKey.of(QUALITY_PROFILE_KEY1, RULE_KEY_2);
ActiveRuleKey activeRuleKey3 = ActiveRuleKey.of(QUALITY_PROFILE_KEY1, RULE_KEY_3);

indexActiveRules(
ActiveRuleDocTesting.newDoc(activeRuleKey1),
ActiveRuleDocTesting.newDoc(activeRuleKey2),
ActiveRuleDocTesting.newDoc(activeRuleKey3),
// Profile 2 is a child a profile 1
ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY2, RULE_KEY_1))
.setParentKey(activeRuleKey1.toString()).setInheritance(INHERITED.name()),
ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY2, RULE_KEY_2))
.setParentKey(activeRuleKey2.toString()).setInheritance(OVERRIDES.name()),
ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY2, RULE_KEY_3))
.setParentKey(activeRuleKey3.toString()).setInheritance(INHERITED.name()));

// 0. get all rules
assertThat(index.search(new RuleQuery(), new SearchOptions()).getIds())
.hasSize(4);

// 1. get all active rules
assertThat(index.search(new RuleQuery()
.setActivation(true), new SearchOptions()).getIds())
.hasSize(3);

// 2. get all inactive rules.
assertThat(index.search(new RuleQuery()
.setActivation(false), new SearchOptions()).getIds())
.containsOnly(RULE_KEY_4);

// 3. get Inherited Rules on profile1
assertThat(index.search(new RuleQuery().setActivation(true)
.setQProfileKey(QUALITY_PROFILE_KEY1)
.setInheritance(ImmutableSet.of(INHERITED.name())),
new SearchOptions()).getIds())
.isEmpty();

// 4. get Inherited Rules on profile2
assertThat(index.search(new RuleQuery().setActivation(true)
.setQProfileKey(QUALITY_PROFILE_KEY2)
.setInheritance(ImmutableSet.of(INHERITED.name())),
new SearchOptions()).getIds())
.hasSize(2);

// 5. get Overridden Rules on profile1
assertThat(index.search(new RuleQuery().setActivation(true)
.setQProfileKey(QUALITY_PROFILE_KEY1)
.setInheritance(ImmutableSet.of(OVERRIDES.name())),
new SearchOptions()).getIds())
.isEmpty();

// 6. get Overridden Rules on profile2
assertThat(index.search(new RuleQuery().setActivation(true)
.setQProfileKey(QUALITY_PROFILE_KEY2)
.setInheritance(ImmutableSet.of(OVERRIDES.name())),
new SearchOptions()).getIds())
.hasSize(1);

// 7. get Inherited AND Overridden Rules on profile1
assertThat(index.search(new RuleQuery().setActivation(true)
.setQProfileKey(QUALITY_PROFILE_KEY1)
.setInheritance(ImmutableSet.of(INHERITED.name(), OVERRIDES.name())),
new SearchOptions()).getIds())
.isEmpty();

// 8. get Inherited AND Overridden Rules on profile2
assertThat(index.search(new RuleQuery().setActivation(true)
.setQProfileKey(QUALITY_PROFILE_KEY2)
.setInheritance(ImmutableSet.of(INHERITED.name(), OVERRIDES.name())),
new SearchOptions()).getIds())
.hasSize(3);
}

@Test
@Ignore
public void search_by_profile_and_active_severity() {
// QualityProfileDto qualityProfileDto1 = QProfileTesting.newXooP1();
// QualityProfileDto qualityProfileDto2 = QProfileTesting.newXooP2();
// db.qualityProfileDao().insert(dbSession, qualityProfileDto1, qualityProfileDto2);
//
// RuleDto rule1 = RuleTesting.newXooX1().setSeverity("MAJOR");
// RuleDto rule2 = RuleTesting.newXooX2().setSeverity("MINOR");
// RuleDto rule3 = RuleTesting.newXooX3().setSeverity("INFO");
// dao.insert(dbSession, rule1, rule2, rule3);
//
// db.activeRuleDao().insert(
// dbSession,
// ActiveRuleDto.createFor(qualityProfileDto1, rule1).setSeverity("BLOCKER"),
// ActiveRuleDto.createFor(qualityProfileDto2, rule1).setSeverity("BLOCKER"),
// ActiveRuleDto.createFor(qualityProfileDto1, rule2).setSeverity("CRITICAL"));
// dbSession.commit();
// dbSession.clearCache();
//
// // 1. get all active rules.
// Result<org.sonar.server.rule.Rule> result = index.search(new
// RuleQuery().setActivation(true).setQProfileKey(qualityProfileDto1.getKey()),
// new SearchOptions());
// assertThat(result.getHits()).hasSize(2);
//
// // 2. get rules with active severity critical.
// result = index.search(new
// RuleQuery().setActivation(true).setQProfileKey(qualityProfileDto1.getKey()).setActiveSeverities(Arrays.asList("CRITICAL")),
// new SearchOptions().addFacets(Arrays.asList(RuleIndex.FACET_ACTIVE_SEVERITIES)));
// assertThat(result.getHits()).hasSize(1);
// assertThat(result.getHits().get(0).name()).isEqualTo(rule2.getName());
// // check stickyness of active severity facet
// assertThat(result.getFacetValues(RuleIndex.FACET_ACTIVE_SEVERITIES)).containsOnly(new FacetValue("BLOCKER", 1), new
// FacetValue("CRITICAL", 1));
//
// // 3. count activation severities of all active rules
// result = index.search(new RuleQuery(),
// new SearchOptions().addFacets(Arrays.asList(RuleIndex.FACET_ACTIVE_SEVERITIES)));
// assertThat(result.getHits()).hasSize(3);
// assertThat(result.getFacetValues(RuleIndex.FACET_ACTIVE_SEVERITIES)).containsOnly(new FacetValue("BLOCKER", 2), new
// FacetValue("CRITICAL", 1));
indexRules(
newDoc(RULE_KEY_1).setSeverity(MAJOR),
newDoc(RULE_KEY_2).setSeverity(MINOR),
newDoc(RULE_KEY_3).setSeverity(INFO));

indexActiveRules(
ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY1, RULE_KEY_1)).setSeverity(BLOCKER),
ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY2, RULE_KEY_1)).setSeverity(BLOCKER),
ActiveRuleDocTesting.newDoc(ActiveRuleKey.of(QUALITY_PROFILE_KEY1, RULE_KEY_2)).setSeverity(CRITICAL));

// 1. get all active rules.
assertThat(index.search(new RuleQuery().setActivation(true).setQProfileKey(QUALITY_PROFILE_KEY1), new SearchOptions()).getIds())
.hasSize(2);

// 2. get rules with active severity critical.
SearchIdResult<RuleKey> result = index.search(new RuleQuery().setActivation(true)
.setQProfileKey(QUALITY_PROFILE_KEY1).setActiveSeverities(singletonList(CRITICAL)),
new SearchOptions().addFacets(singletonList(RuleIndex2.FACET_ACTIVE_SEVERITIES)));
assertThat(result.getIds()).containsOnly(RULE_KEY_2);

// check stickyness of active severity facet
assertThat(result.getFacets().get(RuleIndex2.FACET_ACTIVE_SEVERITIES)).containsOnly(entry(BLOCKER, 1L), entry(CRITICAL, 1L));

// 3. count activation severities of all active rules
result = index.search(new RuleQuery(), new SearchOptions().addFacets(singletonList(RuleIndex2.FACET_ACTIVE_SEVERITIES)));
assertThat(result.getIds()).hasSize(3);
assertThat(result.getFacets().get(RuleIndex2.FACET_ACTIVE_SEVERITIES)).containsOnly(entry(BLOCKER, 2L), entry(CRITICAL, 1L));
}

@Test
@@ -724,4 +696,8 @@ public class RuleIndex2Test {
private void indexRules(RuleDoc... rules) {
ruleIndexer.index(asList(rules).iterator());
}

private void indexActiveRules(ActiveRuleDoc... docs) {
activeRuleIndexer.index(asList(docs).iterator());
}
}

+ 1
- 1
server/sonar-server/src/test/java/org/sonar/server/rule/index/RuleIndexDefinitionTest.java View File

@@ -38,7 +38,7 @@ public class RuleIndexDefinitionTest {
assertThat(underTest.getIndices()).hasSize(1);
NewIndex ruleIndex = underTest.getIndices().get("rules");
assertThat(ruleIndex).isNotNull();
assertThat(ruleIndex.getTypes().keySet()).containsOnly("rule");
assertThat(ruleIndex.getTypes().keySet()).containsOnly("rule", "activeRule");

// no cluster by default
assertThat(ruleIndex.getSettings().get("index.number_of_shards")).isEqualTo(String.valueOf(NewIndex.DEFAULT_NUMBER_OF_SHARDS));

+ 13
- 0
server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/index/ActiveRuleIndexerTest/index.xml View File

@@ -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>

+ 20
- 0
server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/index/ActiveRuleResultSetIteratorTest/active_rule_with_inherited_inheritance.xml View File

@@ -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>

+ 20
- 0
server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/index/ActiveRuleResultSetIteratorTest/active_rule_with_overrides_inheritance.xml View File

@@ -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>

+ 13
- 0
server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/index/ActiveRuleResultSetIteratorTest/one_active_rule.xml View File

@@ -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>

+ 37
- 0
server/sonar-server/src/test/resources/org/sonar/server/qualityprofile/index/ActiveRuleResultSetIteratorTest/shared.xml View File

@@ -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>

+ 171
- 0
sonar-db/src/main/java/org/sonar/db/qualityprofile/ActiveRuleDao.java View File

@@ -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);
}
}

+ 28
- 7
sonar-db/src/main/java/org/sonar/db/qualityprofile/ActiveRuleDto.java View File

@@ -36,17 +36,20 @@ public class ActiveRuleDto extends Dto<ActiveRuleKey> {
public static final String INHERITED = ActiveRule.INHERITED;
public static final String OVERRIDES = ActiveRule.OVERRIDES;

private String repository;
private String ruleField;
private String profileKey;

private Integer id;
private Integer profileId;
private Integer ruleId;
private Integer severity;
private String inheritance;
//This field do not exists in db, it's only retrieve by joins

private long createdAtInMs;
private long updatedAtInMs;

//These fields do not exists in db, it's only retrieve by joins
private Integer parentId;
private String repository;
private String ruleField;
private String profileKey;

/**
* @deprecated for internal use, should be private
@@ -77,7 +80,6 @@ public class ActiveRuleDto extends Dto<ActiveRuleKey> {
return profileId;
}

// TODO mark as private
public ActiveRuleDto setProfileId(Integer profileId) {
this.profileId = profileId;
return this;
@@ -87,7 +89,6 @@ public class ActiveRuleDto extends Dto<ActiveRuleKey> {
return ruleId;
}

// TODO mark as private
public ActiveRuleDto setRuleId(Integer ruleId) {
this.ruleId = ruleId;
return this;
@@ -122,10 +123,12 @@ public class ActiveRuleDto extends Dto<ActiveRuleKey> {
}

@CheckForNull
@Deprecated
public Integer getParentId() {
return parentId;
}

@Deprecated
public ActiveRuleDto setParentId(@Nullable Integer parentId) {
this.parentId = parentId;
return this;
@@ -139,6 +142,24 @@ public class ActiveRuleDto extends Dto<ActiveRuleKey> {
return StringUtils.equals(OVERRIDES, inheritance);
}

public long getUpdatedAtInMs() {
return updatedAtInMs;
}

public ActiveRuleDto setUpdatedAtInMs(long updatedAtInMs) {
this.updatedAtInMs = updatedAtInMs;
return this;
}

public long getCreatedAtInMs() {
return createdAtInMs;
}

public ActiveRuleDto setCreatedAtInMs(long createdAtInMs) {
this.createdAtInMs = createdAtInMs;
return this;
}

public static ActiveRuleDto createFor(QualityProfileDto profileDto, RuleDto ruleDto) {
Preconditions.checkNotNull(profileDto.getId(), "Profile is not persisted");
Preconditions.checkNotNull(ruleDto.getId(), "Rule is not persisted");

+ 1
- 11
sonar-db/src/main/java/org/sonar/db/qualityprofile/QualityProfileDao.java View File

@@ -32,8 +32,8 @@ import org.sonar.api.utils.System2;
import org.sonar.db.Dao;
import org.sonar.db.DbSession;
import org.sonar.db.MyBatis;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.RowNotFoundException;
import org.sonar.db.component.ComponentDto;

@ServerSide
public class QualityProfileDao implements Dao {
@@ -209,21 +209,11 @@ public class QualityProfileDao implements Dao {
}
}

/**
* @deprecated Replaced by
* {@link #selectByKey(DbSession, String)}
*/
@Deprecated
@CheckForNull
public QualityProfileDto selectById(DbSession session, int id) {
return mapper(session).selectById(id);
}

/**
* @deprecated Replaced by
* {@link #selectByKey(DbSession, String)}
*/
@Deprecated
@CheckForNull
public QualityProfileDto selectById(int id) {
DbSession session = mybatis.openSession(false);

+ 12
- 5
sonar-db/src/main/resources/org/sonar/db/qualityprofile/ActiveRuleMapper.xml View File

@@ -13,7 +13,9 @@
r.plugin_name as "repository",
qp.kee as "profileKey",
a.created_at as "createdAt",
a.updated_at as "updatedAt"
a.updated_at as "updatedAt",
a.created_at_ms as "createdAtInMs",
a.updated_at_ms as "updatedAtInMs"
</sql>

<sql id="activeRuleKeyJoin">
@@ -21,6 +23,7 @@
INNER JOIN rules r ON r.id = a.rule_id
</sql>

<!-- Should be removed when ActiveRuleDao v2 will be removed -->
<sql id="activeRuleColumns">
a.id,
a.profile_id as profileId,
@@ -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>


+ 25
- 0
sonar-db/src/test/java/org/sonar/db/qualityprofile/ActiveRuleDaoTest.java View File

@@ -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 {

}

Loading…
Cancel
Save