@@ -23,6 +23,11 @@ import org.sonar.api.server.ServerSide; | |||
@ServerSide | |||
public abstract class BaseIndex { | |||
public static final String SORT_SUFFIX = "sort"; | |||
public static final String SEARCH_WORDS_SUFFIX = "words"; | |||
public static final String SEARCH_PARTIAL_SUFFIX = "grams"; | |||
private final EsClient client; | |||
public BaseIndex(EsClient client) { |
@@ -21,17 +21,6 @@ package org.sonar.server.es; | |||
import com.google.common.base.Function; | |||
import com.google.common.collect.Lists; | |||
import org.elasticsearch.action.search.SearchScrollRequestBuilder; | |||
import org.elasticsearch.common.joda.time.format.ISODateTimeFormat; | |||
import org.elasticsearch.common.unit.TimeValue; | |||
import org.elasticsearch.search.SearchHit; | |||
import org.elasticsearch.search.SearchHits; | |||
import org.elasticsearch.search.aggregations.bucket.terms.Terms; | |||
import org.sonar.server.search.BaseDoc; | |||
import javax.annotation.CheckForNull; | |||
import javax.annotation.Nullable; | |||
import java.util.ArrayDeque; | |||
import java.util.ArrayList; | |||
import java.util.Collections; | |||
@@ -42,6 +31,15 @@ import java.util.List; | |||
import java.util.Map; | |||
import java.util.NoSuchElementException; | |||
import java.util.Queue; | |||
import javax.annotation.CheckForNull; | |||
import javax.annotation.Nullable; | |||
import org.elasticsearch.action.search.SearchScrollRequestBuilder; | |||
import org.elasticsearch.common.joda.time.format.ISODateTimeFormat; | |||
import org.elasticsearch.common.unit.TimeValue; | |||
import org.elasticsearch.search.SearchHit; | |||
import org.elasticsearch.search.SearchHits; | |||
import org.elasticsearch.search.aggregations.bucket.terms.Terms; | |||
import org.sonar.server.search.BaseDoc; | |||
public class EsUtils { | |||
@@ -121,4 +119,33 @@ public class EsUtils { | |||
} | |||
}; | |||
} | |||
public static <ID> Iterator<ID> scrollIds(final EsClient esClient, final String scrollId, final Function<String, ID> idConverter) { | |||
return new Iterator<ID>() { | |||
private final Queue<SearchHit> hits = new ArrayDeque<>(); | |||
@Override | |||
public boolean hasNext() { | |||
if (hits.isEmpty()) { | |||
SearchScrollRequestBuilder esRequest = esClient.prepareSearchScroll(scrollId) | |||
.setScroll(TimeValue.timeValueMinutes(SCROLL_TIME_IN_MINUTES)); | |||
Collections.addAll(hits, esRequest.get().getHits().getHits()); | |||
} | |||
return !hits.isEmpty(); | |||
} | |||
@Override | |||
public ID next() { | |||
if (!hasNext()) { | |||
throw new NoSuchElementException(); | |||
} | |||
return idConverter.apply(hits.poll().getId()); | |||
} | |||
@Override | |||
public void remove() { | |||
throw new UnsupportedOperationException("Cannot remove item when scrolling"); | |||
} | |||
}; | |||
} | |||
} |
@@ -54,7 +54,7 @@ public class NewIndex { | |||
// defaults | |||
attributes.put("dynamic", false); | |||
attributes.put("_all", ImmutableSortedMap.of("enabled", false)); | |||
attributes.put("_source", ImmutableSortedMap.of("enabled", true)); | |||
attributes.put("properties", properties); | |||
} | |||
@@ -78,6 +78,11 @@ public class NewIndex { | |||
return this; | |||
} | |||
public NewIndexType setEnableSource(boolean enableSource) { | |||
attributes.put("_source", ImmutableSortedMap.of("enabled", enableSource)); | |||
return this; | |||
} | |||
public StringFieldBuilder stringFieldBuilder(String fieldName) { | |||
return new StringFieldBuilder(this, fieldName); | |||
} | |||
@@ -250,7 +255,13 @@ public class NewIndex { | |||
public void build() { | |||
validate(); | |||
Map<String, Object> hash = new TreeMap<>(); | |||
if (!subFields.isEmpty()) { | |||
if (subFields.isEmpty()) { | |||
hash.putAll(ImmutableMap.of( | |||
"type", "string", | |||
"index", disableSearch ? "no" : "not_analyzed", | |||
"omit_norms", "true", | |||
"doc_values", docValues)); | |||
} else { | |||
hash.put("type", "multi_field"); | |||
Map<String, Object> multiFields = new TreeMap<>(subFields); | |||
multiFields.put(fieldName, ImmutableMap.of( | |||
@@ -259,12 +270,6 @@ public class NewIndex { | |||
"omit_norms", "true", | |||
"doc_values", docValues)); | |||
hash.put("fields", multiFields); | |||
} else { | |||
hash.putAll(ImmutableMap.of( | |||
"type", "string", | |||
"index", disableSearch ? "no" : "not_analyzed", | |||
"omit_norms", "true", | |||
"doc_values", docValues)); | |||
} | |||
indexType.setProperty(fieldName, hash); |
@@ -0,0 +1,66 @@ | |||
/* | |||
* 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.es; | |||
import com.google.common.base.Function; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import org.apache.commons.lang.builder.ReflectionToStringBuilder; | |||
import org.elasticsearch.action.search.SearchResponse; | |||
import org.elasticsearch.search.SearchHit; | |||
import org.elasticsearch.search.SearchHits; | |||
public class SearchIdResult<ID> { | |||
private final List<ID> ids; | |||
private final Facets facets; | |||
private final long total; | |||
public SearchIdResult(SearchResponse response, Function<String, ID> converter) { | |||
this.facets = new Facets(response); | |||
this.total = response.getHits().totalHits(); | |||
this.ids = convertToIds(response.getHits(), converter); | |||
} | |||
public List<ID> getIds() { | |||
return ids; | |||
} | |||
public long getTotal() { | |||
return total; | |||
} | |||
public Facets getFacets() { | |||
return this.facets; | |||
} | |||
@Override | |||
public String toString() { | |||
return ReflectionToStringBuilder.toString(this); | |||
} | |||
public static <ID> List<ID> convertToIds(SearchHits hits, Function<String, ID> converter) { | |||
List<ID> docs = new ArrayList<>(); | |||
for (SearchHit hit : hits.getHits()) { | |||
docs.add(converter.apply(hit.getId())); | |||
} | |||
return docs; | |||
} | |||
} |
@@ -76,12 +76,12 @@ import org.sonar.server.es.SearchResult; | |||
import org.sonar.server.es.Sorting; | |||
import org.sonar.server.exceptions.NotFoundException; | |||
import org.sonar.server.issue.IssueQuery; | |||
import org.sonarqube.ws.client.issue.IssueFilterParameters; | |||
import org.sonar.server.rule.index.RuleNormalizer; | |||
import org.sonar.server.rule.index.RuleIndexDefinition; | |||
import org.sonar.server.search.IndexDefinition; | |||
import org.sonar.server.search.StickyFacetBuilder; | |||
import org.sonar.server.user.UserSession; | |||
import org.sonar.server.view.index.ViewIndexDefinition; | |||
import org.sonarqube.ws.client.issue.IssueFilterParameters; | |||
import static com.google.common.collect.Lists.newArrayList; | |||
@@ -652,7 +652,7 @@ public class IssueIndex extends BaseIndex { | |||
issueTags.include(String.format(SUBSTRING_MATCH_REGEXP, textQuery)); | |||
} | |||
TermsBuilder ruleTags = AggregationBuilders.terms(tagsOnRulesSubAggregation) | |||
.field(RuleNormalizer.RuleField.ALL_TAGS.field()) | |||
.field(RuleIndexDefinition.FIELD_RULE_ALL_TAGS) | |||
.size(maxNumberOfTags) | |||
.order(Terms.Order.term(true)) | |||
.minDocCount(1L); |
@@ -106,7 +106,7 @@ public class IssueIndexer extends BaseIndexer { | |||
bulk.stop(); | |||
} | |||
BulkIndexer createBulkIndexer(boolean large) { | |||
private BulkIndexer createBulkIndexer(boolean large) { | |||
BulkIndexer bulk = new BulkIndexer(esClient, IssueIndexDefinition.INDEX); | |||
bulk.setLarge(large); | |||
return bulk; |
@@ -48,6 +48,7 @@ import org.sonar.server.qualityprofile.index.ActiveRuleNormalizer; | |||
import org.sonar.server.ruby.PlatformRackBridge; | |||
import org.sonar.server.rule.db.RuleDao; | |||
import org.sonar.server.rule.index.RuleIndex; | |||
import org.sonar.server.rule.index.RuleIndex2; | |||
import org.sonar.server.rule.index.RuleNormalizer; | |||
import org.sonar.server.search.EsSearchModule; | |||
import org.sonar.server.search.IndexQueue; | |||
@@ -108,6 +109,7 @@ public class PlatformLevel1 extends PlatformLevel { | |||
ActiveRuleDao.class, | |||
// rules/qprofiles | |||
RuleIndex2.class, | |||
RuleNormalizer.class, | |||
ActiveRuleNormalizer.class, | |||
RuleIndex.class, |
@@ -256,6 +256,8 @@ import org.sonar.server.rule.RuleOperations; | |||
import org.sonar.server.rule.RuleRepositories; | |||
import org.sonar.server.rule.RuleService; | |||
import org.sonar.server.rule.RuleUpdater; | |||
import org.sonar.server.rule.index.RuleIndexDefinition; | |||
import org.sonar.server.rule.index.RuleIndexer; | |||
import org.sonar.server.rule.ws.ActiveRuleCompleter; | |||
import org.sonar.server.rule.ws.RepositoriesAction; | |||
import org.sonar.server.rule.ws.RuleMapper; | |||
@@ -430,6 +432,8 @@ public class PlatformLevel4 extends PlatformLevel { | |||
RubyQProfileActivityService.class, | |||
// rule | |||
RuleIndexDefinition.class, | |||
RuleIndexer.class, | |||
AnnotationRuleParser.class, | |||
XMLRuleParser.class, | |||
DefaultRuleFinder.class, |
@@ -21,6 +21,11 @@ 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; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import java.util.Map; | |||
import org.elasticsearch.action.search.SearchRequestBuilder; | |||
import org.elasticsearch.action.search.SearchResponse; | |||
import org.elasticsearch.action.search.SearchType; | |||
@@ -36,19 +41,13 @@ import org.sonar.api.rule.RuleStatus; | |||
import org.sonar.db.qualityprofile.ActiveRuleDto; | |||
import org.sonar.db.qualityprofile.ActiveRuleKey; | |||
import org.sonar.server.qualityprofile.ActiveRule; | |||
import org.sonar.server.rule.index.RuleNormalizer; | |||
import org.sonar.server.rule.index.RuleIndexDefinition; | |||
import org.sonar.server.search.BaseIndex; | |||
import org.sonar.server.search.FacetValue; | |||
import org.sonar.server.search.IndexDefinition; | |||
import org.sonar.server.search.IndexField; | |||
import org.sonar.server.search.SearchClient; | |||
import java.util.ArrayList; | |||
import java.util.HashMap; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import java.util.Map; | |||
public class ActiveRuleIndex extends BaseIndex<ActiveRule, ActiveRuleDto, ActiveRuleKey> { | |||
public static final String COUNT_ACTIVE_RULES = "countActiveRules"; | |||
@@ -139,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(RuleNormalizer.RuleField.STATUS.field(), RuleStatus.REMOVED.name()))))) | |||
FilterBuilders.termFilter(RuleIndexDefinition.FIELD_STATUS, RuleStatus.REMOVED.name()))))) | |||
.setRouting(key); | |||
SearchResponse response = request.get(); | |||
@@ -154,14 +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(RuleNormalizer.RuleField.STATUS.field(), "REMOVED")))).get(key); | |||
FilterBuilders.termFilter(RuleIndexDefinition.FIELD_STATUS, "REMOVED")))).get(key); | |||
} | |||
public Map<String, Long> countAllByQualityProfileKey() { | |||
return countByField(ActiveRuleNormalizer.ActiveRuleField.PROFILE_KEY, | |||
FilterBuilders.hasParentFilter(IndexDefinition.RULE.getIndexType(), | |||
FilterBuilders.notFilter( | |||
FilterBuilders.termFilter(RuleNormalizer.RuleField.STATUS.field(), "REMOVED")))); | |||
FilterBuilders.termFilter(RuleIndexDefinition.FIELD_STATUS, "REMOVED")))); | |||
} | |||
public Multimap<String, FacetValue> getStatsByProfileKey(String key) { | |||
@@ -174,7 +173,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(RuleNormalizer.RuleField.STATUS.field(), RuleStatus.REMOVED.name()))))) | |||
FilterBuilders.termFilter(RuleIndexDefinition.FIELD_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()) |
@@ -27,9 +27,8 @@ import javax.annotation.Nullable; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.server.ServerSide; | |||
import org.sonar.core.permission.GlobalPermissions; | |||
import org.sonar.server.exceptions.NotFoundException; | |||
import org.sonar.server.rule.index.RuleIndex; | |||
import org.sonar.server.rule.index.RuleNormalizer; | |||
import org.sonar.server.rule.index.RuleIndex2; | |||
import org.sonar.server.rule.index.RuleIndexDefinition; | |||
import org.sonar.server.rule.index.RuleQuery; | |||
import org.sonar.server.search.QueryContext; | |||
import org.sonar.server.search.Result; | |||
@@ -41,13 +40,13 @@ import org.sonar.server.user.UserSession; | |||
@ServerSide | |||
public class RuleService { | |||
private final RuleIndex index; | |||
private final RuleIndex2 index; | |||
private final RuleUpdater ruleUpdater; | |||
private final RuleCreator ruleCreator; | |||
private final RuleDeleter ruleDeleter; | |||
private final UserSession userSession; | |||
public RuleService(RuleIndex index, RuleUpdater ruleUpdater, RuleCreator ruleCreator, RuleDeleter ruleDeleter, UserSession userSession) { | |||
public RuleService(RuleIndex2 index, RuleUpdater ruleUpdater, RuleCreator ruleCreator, RuleDeleter ruleDeleter, UserSession userSession) { | |||
this.index = index; | |||
this.ruleUpdater = ruleUpdater; | |||
this.ruleCreator = ruleCreator; | |||
@@ -56,20 +55,19 @@ public class RuleService { | |||
} | |||
@CheckForNull | |||
@Deprecated | |||
public Rule getByKey(RuleKey key) { | |||
return index.getNullableByKey(key); | |||
throw new UnsupportedOperationException("Please use RuleDao"); | |||
} | |||
@Deprecated | |||
public List<Rule> getByKeys(Collection<RuleKey> keys) { | |||
return index.getByKeys(keys); | |||
throw new UnsupportedOperationException("Please use RuleDao"); | |||
} | |||
@Deprecated | |||
public Rule getNonNullByKey(RuleKey key) { | |||
Rule rule = index.getNullableByKey(key); | |||
if (rule == null) { | |||
throw new NotFoundException("Rule not found: " + key); | |||
} | |||
return rule; | |||
throw new UnsupportedOperationException("Please use RuleDao"); | |||
} | |||
public RuleQuery newRuleQuery() { | |||
@@ -85,7 +83,7 @@ public class RuleService { | |||
*/ | |||
public Set<String> listTags() { | |||
/** using combined ALL_TAGS field of ES until ES update that has multiTerms aggregation */ | |||
return index.terms(RuleNormalizer.RuleField.ALL_TAGS.field()); | |||
return index.terms(RuleIndexDefinition.FIELD_RULE_ALL_TAGS); | |||
} | |||
/** | |||
@@ -93,7 +91,7 @@ public class RuleService { | |||
*/ | |||
public Set<String> listTags(@Nullable String query, int size) { | |||
/** using combined ALL_TAGS field of ES until ES update that has multiTerms aggregation */ | |||
return index.terms(RuleNormalizer.RuleField.ALL_TAGS.field(), query, size); | |||
return index.terms(RuleIndexDefinition.FIELD_RULE_ALL_TAGS, query, size); | |||
} | |||
public RuleKey create(NewRule newRule) { |
@@ -19,9 +19,11 @@ | |||
*/ | |||
package org.sonar.server.rule.index; | |||
import com.google.common.annotations.VisibleForTesting; | |||
import com.google.common.base.Predicate; | |||
import com.google.common.collect.Iterables; | |||
import java.util.ArrayList; | |||
import java.util.Collection; | |||
import java.util.Date; | |||
import java.util.List; | |||
import java.util.Map; | |||
@@ -49,11 +51,6 @@ public class RuleDoc extends BaseDoc implements Rule { | |||
super(fields); | |||
} | |||
@Override | |||
public RuleKey key() { | |||
return RuleKey.parse(this.<String>getField(RuleNormalizer.RuleField.KEY.field())); | |||
} | |||
/** | |||
* @deprecated Only use for sqale backward compat. Use key() instead. | |||
*/ | |||
@@ -62,34 +59,87 @@ public class RuleDoc extends BaseDoc implements Rule { | |||
return getField(RuleNormalizer.RuleField.ID.field()); | |||
} | |||
/** | |||
* Alias for backward-compatibility with SQALE | |||
*/ | |||
public RuleKey ruleKey() { | |||
return key(); | |||
@Override | |||
public RuleKey key() { | |||
return RuleKey.parse(this.<String>getField(RuleIndexDefinition.FIELD_RULE_KEY)); | |||
} | |||
public RuleDoc setKey(@Nullable String s) { | |||
setField(RuleIndexDefinition.FIELD_RULE_KEY, s); | |||
return this; | |||
} | |||
@VisibleForTesting | |||
List<String> keyAsList() { | |||
return (List<String>) getField(RuleIndexDefinition.FIELD_RULE_KEY_AS_LIST); | |||
} | |||
public RuleDoc setKeyAsList(@Nullable List<String> s) { | |||
setField(RuleIndexDefinition.FIELD_RULE_KEY_AS_LIST, s); | |||
return this; | |||
} | |||
@CheckForNull | |||
public String ruleKey() { | |||
return getNullableField(RuleIndexDefinition.FIELD_RULE_RULE_KEY); | |||
} | |||
public RuleDoc setRuleKey(@Nullable String s) { | |||
setField(RuleIndexDefinition.FIELD_RULE_RULE_KEY, s); | |||
return this; | |||
} | |||
@CheckForNull | |||
public String repository() { | |||
return getNullableField(RuleIndexDefinition.FIELD_RULE_REPOSITORY); | |||
} | |||
public RuleDoc setRepository(@Nullable String s) { | |||
setField(RuleIndexDefinition.FIELD_RULE_REPOSITORY, s); | |||
return this; | |||
} | |||
@Override | |||
@CheckForNull | |||
public String internalKey() { | |||
return getNullableField(RuleNormalizer.RuleField.INTERNAL_KEY.field()); | |||
return getNullableField(RuleIndexDefinition.FIELD_RULE_INTERNAL_KEY); | |||
} | |||
public RuleDoc setInternalKey(@Nullable String s) { | |||
setField(RuleIndexDefinition.FIELD_RULE_INTERNAL_KEY, s); | |||
return this; | |||
} | |||
@Override | |||
@CheckForNull | |||
public String language() { | |||
return getNullableField(RuleNormalizer.RuleField.LANGUAGE.field()); | |||
return getNullableField(RuleIndexDefinition.FIELD_RULE_LANGUAGE); | |||
} | |||
public RuleDoc setLanguage(@Nullable String s) { | |||
setField(RuleIndexDefinition.FIELD_RULE_LANGUAGE, s); | |||
return this; | |||
} | |||
@Override | |||
public String name() { | |||
return getField(RuleNormalizer.RuleField.NAME.field()); | |||
return getField(RuleIndexDefinition.FIELD_RULE_NAME); | |||
} | |||
public RuleDoc setName(@Nullable String s) { | |||
setField(RuleIndexDefinition.FIELD_RULE_NAME, s); | |||
return this; | |||
} | |||
@Override | |||
@CheckForNull | |||
public String htmlDescription() { | |||
return getNullableField(RuleNormalizer.RuleField.HTML_DESCRIPTION.field()); | |||
return getNullableField(RuleIndexDefinition.FIELD_RULE_HTML_DESCRIPTION); | |||
} | |||
public RuleDoc setHtmlDescription(@Nullable String s) { | |||
setField(RuleIndexDefinition.FIELD_RULE_HTML_DESCRIPTION, s); | |||
return this; | |||
} | |||
@Override | |||
@@ -107,18 +157,33 @@ public class RuleDoc extends BaseDoc implements Rule { | |||
@Override | |||
@CheckForNull | |||
public String severity() { | |||
return (String) getNullableField(RuleNormalizer.RuleField.SEVERITY.field()); | |||
return (String) getNullableField(RuleIndexDefinition.FIELD_RULE_SEVERITY); | |||
} | |||
public RuleDoc setSeverity(@Nullable String s) { | |||
setField(RuleIndexDefinition.FIELD_RULE_SEVERITY, s); | |||
return this; | |||
} | |||
@Override | |||
@CheckForNull | |||
public RuleStatus status() { | |||
return RuleStatus.valueOf((String) getField(RuleNormalizer.RuleField.STATUS.field())); | |||
return RuleStatus.valueOf((String) getField(RuleIndexDefinition.FIELD_RULE_STATUS)); | |||
} | |||
public RuleDoc setStatus(@Nullable String s) { | |||
setField(RuleIndexDefinition.FIELD_RULE_STATUS, s); | |||
return this; | |||
} | |||
@Override | |||
public boolean isTemplate() { | |||
return (Boolean) getField(RuleNormalizer.RuleField.IS_TEMPLATE.field()); | |||
return (Boolean) getField(RuleIndexDefinition.FIELD_RULE_IS_TEMPLATE); | |||
} | |||
public RuleDoc setIsTemplate(@Nullable Boolean b) { | |||
setField(RuleIndexDefinition.FIELD_RULE_IS_TEMPLATE, b); | |||
return this; | |||
} | |||
@Override | |||
@@ -128,6 +193,11 @@ public class RuleDoc extends BaseDoc implements Rule { | |||
return templateKey != null ? RuleKey.parse(templateKey) : null; | |||
} | |||
public RuleDoc setTemplateKey(@Nullable String s) { | |||
setField(RuleIndexDefinition.FIELD_RULE_TEMPLATE_KEY, s); | |||
return this; | |||
} | |||
@Override | |||
public List<String> tags() { | |||
return (List<String>) getField(RuleNormalizer.RuleField.TAGS.field()); | |||
@@ -138,6 +208,15 @@ public class RuleDoc extends BaseDoc implements Rule { | |||
return (List<String>) getField(RuleNormalizer.RuleField.SYSTEM_TAGS.field()); | |||
} | |||
public Collection<String> allTags() { | |||
return (Collection<String>) getField(RuleIndexDefinition.FIELD_RULE_ALL_TAGS); | |||
} | |||
public RuleDoc setAllTags(@Nullable Collection<String> l) { | |||
setField(RuleIndexDefinition.FIELD_RULE_ALL_TAGS, l); | |||
return this; | |||
} | |||
@Override | |||
public List<RuleParam> params() { | |||
List<RuleParam> params = new ArrayList<>(); | |||
@@ -267,11 +346,31 @@ public class RuleDoc extends BaseDoc implements Rule { | |||
return IndexUtils.parseDateTime((String) getNullableField(RuleNormalizer.RuleField.CREATED_AT.field())); | |||
} | |||
@CheckForNull | |||
public Long createdAtAsLong() { | |||
return (Long) getField(RuleIndexDefinition.FIELD_RULE_CREATED_AT); | |||
} | |||
public RuleDoc setCreatedAt(@Nullable Long l) { | |||
setField(RuleIndexDefinition.FIELD_RULE_CREATED_AT, l); | |||
return this; | |||
} | |||
@Override | |||
public Date updatedAt() { | |||
return IndexUtils.parseDateTime((String) getNullableField(RuleNormalizer.RuleField.UPDATED_AT.field())); | |||
} | |||
@CheckForNull | |||
public Long updatedAtAtAsLong() { | |||
return (Long) getField(RuleIndexDefinition.FIELD_RULE_UPDATED_AT); | |||
} | |||
public RuleDoc setUpdatedAt(@Nullable Long l) { | |||
setField(RuleIndexDefinition.FIELD_RULE_UPDATED_AT, l); | |||
return this; | |||
} | |||
@Override | |||
public boolean isManual() { | |||
return getField(RuleNormalizer.RuleField.REPOSITORY.field()).equals(MANUAL_REPOSITORY); |
@@ -151,7 +151,7 @@ public class RuleIndex extends BaseIndex<Rule, RuleDto, RuleKey> { | |||
/* integrate Query Sort */ | |||
String queryText = query.getQueryText(); | |||
if (query.getSortField() != null) { | |||
FieldSortBuilder sort = SortBuilders.fieldSort(query.getSortField().sortField()); | |||
FieldSortBuilder sort = SortBuilders.fieldSort(query.getSortField()); | |||
if (query.isAscendingSort()) { | |||
sort.order(SortOrder.ASC); | |||
} else { |
@@ -0,0 +1,493 @@ | |||
/* | |||
* 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.rule.index; | |||
import com.google.common.base.Function; | |||
import com.google.common.base.Joiner; | |||
import com.google.common.base.Predicate; | |||
import com.google.common.collect.Collections2; | |||
import com.google.common.collect.ImmutableList; | |||
import java.util.ArrayList; | |||
import java.util.Arrays; | |||
import java.util.Collection; | |||
import java.util.HashMap; | |||
import java.util.HashSet; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import javax.annotation.Nonnull; | |||
import javax.annotation.Nullable; | |||
import org.apache.commons.collections.CollectionUtils; | |||
import org.apache.commons.lang.StringUtils; | |||
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.BoolFilterBuilder; | |||
import org.elasticsearch.index.query.BoolQueryBuilder; | |||
import org.elasticsearch.index.query.FilterBuilder; | |||
import org.elasticsearch.index.query.FilterBuilders; | |||
import org.elasticsearch.index.query.HasParentFilterBuilder; | |||
import org.elasticsearch.index.query.MatchQueryBuilder; | |||
import org.elasticsearch.index.query.QueryBuilder; | |||
import org.elasticsearch.index.query.QueryBuilders; | |||
import org.elasticsearch.index.query.SimpleQueryStringBuilder; | |||
import org.elasticsearch.search.aggregations.AggregationBuilder; | |||
import org.elasticsearch.search.aggregations.AggregationBuilders; | |||
import org.elasticsearch.search.aggregations.bucket.terms.Terms; | |||
import org.elasticsearch.search.aggregations.bucket.terms.TermsBuilder; | |||
import org.elasticsearch.search.sort.FieldSortBuilder; | |||
import org.elasticsearch.search.sort.SortBuilders; | |||
import org.elasticsearch.search.sort.SortOrder; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.rule.RuleStatus; | |||
import org.sonar.api.rule.Severity; | |||
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; | |||
import static org.sonar.server.es.EsUtils.SCROLL_TIME_IN_MINUTES; | |||
import static org.sonar.server.es.EsUtils.scrollIds; | |||
/** | |||
* The unique entry-point to interact with Elasticsearch index "rules". | |||
* All the requests are listed here. | |||
*/ | |||
public class RuleIndex2 extends BaseIndex { | |||
public static final String FACET_LANGUAGES = "languages"; | |||
public static final String FACET_TAGS = "tags"; | |||
public static final String FACET_REPOSITORIES = "repositories"; | |||
public static final String FACET_SEVERITIES = "severities"; | |||
public static final String FACET_ACTIVE_SEVERITIES = "active_severities"; | |||
public static final String FACET_STATUSES = "statuses"; | |||
public static final String FACET_OLD_DEFAULT = "true"; | |||
public static final List<String> ALL_STATUSES_EXCEPT_REMOVED = ImmutableList.copyOf( | |||
Collections2.filter( | |||
Collections2.transform( | |||
Arrays.asList(RuleStatus.values()), | |||
new Function<RuleStatus, String>() { | |||
@Override | |||
public String apply(@Nonnull RuleStatus input) { | |||
return input.toString(); | |||
} | |||
}), | |||
new Predicate<String>() { | |||
@Override | |||
public boolean apply(@Nonnull String input) { | |||
return !RuleStatus.REMOVED.toString().equals(input); | |||
} | |||
})); | |||
public RuleIndex2(EsClient client) { | |||
super(client); | |||
} | |||
public SearchIdResult<RuleKey> search(RuleQuery query, SearchOptions options) { | |||
SearchRequestBuilder esSearch = getClient() | |||
.prepareSearch(RuleIndexDefinition.INDEX) | |||
.setTypes(RuleIndexDefinition.TYPE_RULE); | |||
QueryBuilder qb = buildQuery(query); | |||
Map<String, FilterBuilder> filters = buildFilters(query); | |||
if (!options.getFacets().isEmpty()) { | |||
for (AggregationBuilder aggregation : getFacets(query, options, qb, filters).values()) { | |||
esSearch.addAggregation(aggregation); | |||
} | |||
} | |||
setSorting(query, esSearch); | |||
setPagination(options, esSearch); | |||
BoolFilterBuilder fb = FilterBuilders.boolFilter(); | |||
for (FilterBuilder filterBuilder : filters.values()) { | |||
fb.must(filterBuilder); | |||
} | |||
esSearch.setQuery(QueryBuilders.filteredQuery(qb, fb)); | |||
return new SearchIdResult<>(esSearch.get(), ToRuleKey.INSTANCE); | |||
} | |||
/** | |||
* Return all keys matching the search query, without pagination nor facets | |||
*/ | |||
public Iterator<RuleKey> searchAll(RuleQuery query) { | |||
SearchRequestBuilder esSearch = getClient() | |||
.prepareSearch(RuleIndexDefinition.INDEX) | |||
.setTypes(RuleIndexDefinition.TYPE_RULE) | |||
.setSearchType(SearchType.SCAN) | |||
.setScroll(TimeValue.timeValueMinutes(SCROLL_TIME_IN_MINUTES)); | |||
QueryBuilder qb = buildQuery(query); | |||
Map<String, FilterBuilder> filters = buildFilters(query); | |||
setSorting(query, esSearch); | |||
BoolFilterBuilder fb = FilterBuilders.boolFilter(); | |||
for (FilterBuilder filterBuilder : filters.values()) { | |||
fb.must(filterBuilder); | |||
} | |||
esSearch.setQuery(QueryBuilders.filteredQuery(qb, fb)); | |||
SearchResponse response = esSearch.get(); | |||
return scrollIds(getClient(), response.getScrollId(), ToRuleKey.INSTANCE); | |||
} | |||
/* Build main query (search based) */ | |||
private QueryBuilder buildQuery(RuleQuery query) { | |||
// No contextual query case | |||
String queryText = query.getQueryText(); | |||
if (queryText == null || queryText.isEmpty()) { | |||
return QueryBuilders.matchAllQuery(); | |||
} | |||
// Build RuleBased contextual query | |||
BoolQueryBuilder qb = QueryBuilders.boolQuery(); | |||
String queryString = query.getQueryText(); | |||
// Human readable type of querying | |||
qb.should(QueryBuilders.simpleQueryStringQuery(query.getQueryText()) | |||
.field(RuleIndexDefinition.FIELD_RULE_NAME + "." + BaseIndex.SEARCH_WORDS_SUFFIX, 20f) | |||
.field(RuleIndexDefinition.FIELD_RULE_HTML_DESCRIPTION + "." + BaseIndex.SEARCH_WORDS_SUFFIX, 3f) | |||
.defaultOperator(SimpleQueryStringBuilder.Operator.AND) | |||
).boost(20f); | |||
// Match and partial Match queries | |||
qb.should(this.termQuery(RuleIndexDefinition.FIELD_RULE_KEY, queryString, 15f)); | |||
qb.should(this.termQuery(RuleIndexDefinition.FIELD_RULE_KEY_AS_LIST, queryString, 35f)); | |||
qb.should(this.termQuery(RuleIndexDefinition.FIELD_RULE_LANGUAGE, queryString, 3f)); | |||
qb.should(this.termQuery(RuleIndexDefinition.FIELD_RULE_ALL_TAGS, queryString, 10f)); | |||
qb.should(this.termAnyQuery(RuleIndexDefinition.FIELD_RULE_ALL_TAGS, queryString, 1f)); | |||
return qb; | |||
} | |||
private QueryBuilder termQuery(String field, String query, float boost) { | |||
return QueryBuilders.multiMatchQuery(query, | |||
field, field + "." + IndexField.SEARCH_PARTIAL_SUFFIX) | |||
.operator(MatchQueryBuilder.Operator.AND) | |||
.boost(boost); | |||
} | |||
private QueryBuilder termAnyQuery(String field, String query, float boost) { | |||
return QueryBuilders.multiMatchQuery(query, | |||
field, field + "." + IndexField.SEARCH_PARTIAL_SUFFIX) | |||
.operator(MatchQueryBuilder.Operator.OR) | |||
.boost(boost); | |||
} | |||
/* Build main filter (match based) */ | |||
private Map<String, FilterBuilder> buildFilters(RuleQuery query) { | |||
Map<String, FilterBuilder> filters = new HashMap<>(); | |||
/* Add enforced filter on rules that are REMOVED */ | |||
filters.put(RuleIndexDefinition.FIELD_RULE_STATUS, | |||
FilterBuilders.boolFilter().mustNot( | |||
FilterBuilders.termFilter(RuleIndexDefinition.FIELD_RULE_STATUS, | |||
RuleStatus.REMOVED.toString()))); | |||
if (!StringUtils.isEmpty(query.getInternalKey())) { | |||
filters.put(RuleIndexDefinition.FIELD_RULE_INTERNAL_KEY, | |||
FilterBuilders.termFilter(RuleIndexDefinition.FIELD_RULE_INTERNAL_KEY, query.getInternalKey())); | |||
} | |||
if (!StringUtils.isEmpty(query.getRuleKey())) { | |||
filters.put(RuleIndexDefinition.FIELD_RULE_RULE_KEY, | |||
FilterBuilders.termFilter(RuleIndexDefinition.FIELD_RULE_RULE_KEY, query.getRuleKey())); | |||
} | |||
if (!CollectionUtils.isEmpty(query.getLanguages())) { | |||
filters.put(RuleIndexDefinition.FIELD_RULE_LANGUAGE, | |||
FilterBuilders.termsFilter(RuleIndexDefinition.FIELD_RULE_LANGUAGE, query.getLanguages())); | |||
} | |||
if (!CollectionUtils.isEmpty(query.getRepositories())) { | |||
filters.put(RuleIndexDefinition.FIELD_RULE_REPOSITORY, | |||
FilterBuilders.termsFilter(RuleIndexDefinition.FIELD_RULE_REPOSITORY, query.getRepositories())); | |||
} | |||
if (!CollectionUtils.isEmpty(query.getSeverities())) { | |||
filters.put(RuleIndexDefinition.FIELD_RULE_SEVERITY, | |||
FilterBuilders.termsFilter(RuleIndexDefinition.FIELD_RULE_SEVERITY, query.getSeverities())); | |||
} | |||
if (!StringUtils.isEmpty(query.getKey())) { | |||
filters.put(RuleIndexDefinition.FIELD_RULE_KEY, | |||
FilterBuilders.termFilter(RuleIndexDefinition.FIELD_RULE_KEY, query.getKey())); | |||
} | |||
if (!CollectionUtils.isEmpty(query.getTags())) { | |||
filters.put(RuleIndexDefinition.FIELD_RULE_ALL_TAGS, | |||
FilterBuilders.termsFilter(RuleIndexDefinition.FIELD_RULE_ALL_TAGS, query.getTags())); | |||
} | |||
if (query.getAvailableSinceLong() != null) { | |||
filters.put("availableSince", FilterBuilders.rangeFilter(RuleIndexDefinition.FIELD_RULE_CREATED_AT) | |||
.gte(query.getAvailableSinceLong())); | |||
} | |||
Collection<RuleStatus> statusValues = query.getStatuses(); | |||
if (statusValues != null && !statusValues.isEmpty()) { | |||
Collection<String> stringStatus = new ArrayList<>(); | |||
for (RuleStatus status : statusValues) { | |||
stringStatus.add(status.name()); | |||
} | |||
filters.put(RuleIndexDefinition.FIELD_RULE_STATUS, | |||
FilterBuilders.termsFilter(RuleIndexDefinition.FIELD_RULE_STATUS, stringStatus)); | |||
} | |||
Boolean isTemplate = query.isTemplate(); | |||
if (isTemplate != null) { | |||
filters.put(RuleIndexDefinition.FIELD_RULE_IS_TEMPLATE, | |||
FilterBuilders.termFilter(RuleIndexDefinition.FIELD_RULE_IS_TEMPLATE, Boolean.toString(isTemplate))); | |||
} | |||
String template = query.templateKey(); | |||
if (template != null) { | |||
filters.put(RuleIndexDefinition.FIELD_RULE_TEMPLATE_KEY, | |||
FilterBuilders.termFilter(RuleIndexDefinition.FIELD_RULE_TEMPLATE_KEY, template)); | |||
} | |||
// 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()); | |||
// ChildQuery | |||
FilterBuilder childQuery; | |||
if (childrenFilter.hasClauses()) { | |||
childQuery = childrenFilter; | |||
} else { | |||
childQuery = FilterBuilders.matchAllFilter(); | |||
} | |||
/** Implementation of activation query */ | |||
if (Boolean.TRUE.equals(query.getActivation())) { | |||
filters.put("activation", | |||
FilterBuilders.hasChildFilter(IndexDefinition.ACTIVE_RULE.getIndexType(), | |||
childQuery)); | |||
} else if (Boolean.FALSE.equals(query.getActivation())) { | |||
filters.put("activation", | |||
FilterBuilders.boolFilter().mustNot( | |||
FilterBuilders.hasChildFilter(IndexDefinition.ACTIVE_RULE.getIndexType(), | |||
childQuery))); | |||
} | |||
return filters; | |||
} | |||
private BoolFilterBuilder addTermFilter(BoolFilterBuilder filter, String field, @Nullable Collection<String> values) { | |||
if (values != null && !values.isEmpty()) { | |||
BoolFilterBuilder valuesFilter = FilterBuilders.boolFilter(); | |||
for (String value : values) { | |||
FilterBuilder valueFilter = FilterBuilders.termFilter(field, value); | |||
valuesFilter.should(valueFilter); | |||
} | |||
filter.must(valuesFilter); | |||
} | |||
return filter; | |||
} | |||
private BoolFilterBuilder addTermFilter(BoolFilterBuilder filter, String field, @Nullable String value) { | |||
if (value != null && !value.isEmpty()) { | |||
filter.must(FilterBuilders.termFilter(field, value)); | |||
} | |||
return filter; | |||
} | |||
private Map<String, AggregationBuilder> getFacets(RuleQuery query, SearchOptions options, QueryBuilder queryBuilder, Map<String, FilterBuilder> filters) { | |||
Map<String, AggregationBuilder> aggregations = new HashMap<>(); | |||
StickyFacetBuilder stickyFacetBuilder = stickyFacetBuilder(queryBuilder, filters); | |||
addDefaultFacets(query, options, aggregations, stickyFacetBuilder); | |||
addStatusFacetIfNeeded(options, aggregations, stickyFacetBuilder); | |||
if (options.getFacets().contains(FACET_SEVERITIES)) { | |||
aggregations.put(FACET_SEVERITIES, | |||
stickyFacetBuilder.buildStickyFacet(RuleIndexDefinition.FIELD_RULE_SEVERITY, FACET_SEVERITIES, Severity.ALL.toArray())); | |||
} | |||
addActiveSeverityFacetIfNeeded(query, options, aggregations, stickyFacetBuilder); | |||
return aggregations; | |||
} | |||
private void addDefaultFacets(RuleQuery query, SearchOptions options, Map<String, AggregationBuilder> aggregations, StickyFacetBuilder stickyFacetBuilder) { | |||
if (options.getFacets().contains(FACET_LANGUAGES) || options.getFacets().contains(FACET_OLD_DEFAULT)) { | |||
Collection<String> languages = query.getLanguages(); | |||
aggregations.put(FACET_LANGUAGES, | |||
stickyFacetBuilder.buildStickyFacet(RuleIndexDefinition.FIELD_RULE_LANGUAGE, FACET_LANGUAGES, | |||
languages == null ? new String[0] : languages.toArray())); | |||
} | |||
if (options.getFacets().contains(FACET_TAGS) || options.getFacets().contains(FACET_OLD_DEFAULT)) { | |||
Collection<String> tags = query.getTags(); | |||
aggregations.put(FACET_TAGS, | |||
stickyFacetBuilder.buildStickyFacet(RuleIndexDefinition.FIELD_RULE_ALL_TAGS, FACET_TAGS, | |||
tags == null ? new String[0] : tags.toArray())); | |||
} | |||
if (options.getFacets().contains("repositories") || options.getFacets().contains(FACET_OLD_DEFAULT)) { | |||
Collection<String> repositories = query.getRepositories(); | |||
aggregations.put(FACET_REPOSITORIES, | |||
stickyFacetBuilder.buildStickyFacet(RuleIndexDefinition.FIELD_RULE_REPOSITORY, FACET_REPOSITORIES, | |||
repositories == null ? new String[0] : repositories.toArray())); | |||
} | |||
} | |||
private void addStatusFacetIfNeeded(SearchOptions options, Map<String, AggregationBuilder> aggregations, StickyFacetBuilder stickyFacetBuilder) { | |||
if (options.getFacets().contains(FACET_STATUSES)) { | |||
BoolFilterBuilder facetFilter = stickyFacetBuilder.getStickyFacetFilter(RuleIndexDefinition.FIELD_RULE_STATUS); | |||
AggregationBuilder statuses = AggregationBuilders.filter(FACET_STATUSES + "_filter") | |||
.filter(facetFilter) | |||
.subAggregation( | |||
AggregationBuilders | |||
.terms(FACET_STATUSES) | |||
.field(RuleIndexDefinition.FIELD_RULE_STATUS) | |||
.include(Joiner.on('|').join(ALL_STATUSES_EXCEPT_REMOVED)) | |||
.exclude(RuleStatus.REMOVED.toString()) | |||
.size(ALL_STATUSES_EXCEPT_REMOVED.size())); | |||
aggregations.put(FACET_STATUSES, AggregationBuilders.global(FACET_STATUSES).subAggregation(statuses)); | |||
} | |||
} | |||
private void addActiveSeverityFacetIfNeeded(RuleQuery query, SearchOptions options, Map<String, AggregationBuilder> aggregations, StickyFacetBuilder stickyFacetBuilder) { | |||
if (options.getFacets().contains(FACET_ACTIVE_SEVERITIES)) { | |||
// We are building a children aggregation on active rules | |||
// 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(), | |||
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()); | |||
FilterBuilder activeRuleFilter; | |||
if (childrenFilter.hasClauses()) { | |||
activeRuleFilter = childrenFilter.must(ruleFilter); | |||
} else { | |||
activeRuleFilter = ruleFilter; | |||
} | |||
AggregationBuilder activeSeverities = AggregationBuilders.children(FACET_ACTIVE_SEVERITIES + "_children") | |||
.childType(IndexDefinition.ACTIVE_RULE.getIndexType()) | |||
.subAggregation(AggregationBuilders.filter(FACET_ACTIVE_SEVERITIES + "_filter") | |||
.filter(activeRuleFilter) | |||
.subAggregation( | |||
AggregationBuilders | |||
.terms(FACET_ACTIVE_SEVERITIES) | |||
.field(ActiveRuleNormalizer.ActiveRuleField.SEVERITY.field()) | |||
.include(Joiner.on('|').join(Severity.ALL)) | |||
.size(Severity.ALL.size()))); | |||
aggregations.put(FACET_ACTIVE_SEVERITIES, AggregationBuilders.global(FACET_ACTIVE_SEVERITIES).subAggregation(activeSeverities)); | |||
} | |||
} | |||
private StickyFacetBuilder stickyFacetBuilder(QueryBuilder query, Map<String, FilterBuilder> filters) { | |||
return new StickyFacetBuilder(query, filters); | |||
} | |||
private void setSorting(RuleQuery query, SearchRequestBuilder esSearch) { | |||
/* integrate Query Sort */ | |||
String queryText = query.getQueryText(); | |||
if (query.getSortField() != null) { | |||
FieldSortBuilder sort = SortBuilders.fieldSort(appendSortSuffixIfNeeded(query.getSortField())); | |||
if (query.isAscendingSort()) { | |||
sort.order(SortOrder.ASC); | |||
} else { | |||
sort.order(SortOrder.DESC); | |||
} | |||
esSearch.addSort(sort); | |||
} else if (queryText != null && !queryText.isEmpty()) { | |||
esSearch.addSort(SortBuilders.scoreSort()); | |||
} else { | |||
esSearch.addSort(appendSortSuffixIfNeeded(RuleIndexDefinition.FIELD_RULE_UPDATED_AT), SortOrder.DESC); | |||
// deterministic sort when exactly the same updated_at (same millisecond) | |||
esSearch.addSort(appendSortSuffixIfNeeded(RuleIndexDefinition.FIELD_RULE_KEY), SortOrder.ASC); | |||
} | |||
} | |||
public static String appendSortSuffixIfNeeded(String field) { | |||
return field + | |||
((field.equals(RuleIndexDefinition.FIELD_RULE_NAME) | |||
|| field.equals(RuleIndexDefinition.FIELD_RULE_KEY)) | |||
? "." + BaseIndex.SORT_SUFFIX | |||
: ""); | |||
} | |||
private void setPagination(SearchOptions options, SearchRequestBuilder esSearch) { | |||
esSearch.setFrom(options.getOffset()); | |||
esSearch.setSize(options.getLimit()); | |||
} | |||
public Set<String> terms(String fields) { | |||
return terms(fields, null, Integer.MAX_VALUE); | |||
} | |||
public Set<String> terms(String fields, @Nullable String query, int size) { | |||
Set<String> tags = new HashSet<>(); | |||
String key = "_ref"; | |||
TermsBuilder terms = AggregationBuilders.terms(key) | |||
.field(fields) | |||
.size(size) | |||
.minDocCount(1); | |||
if (query != null) { | |||
terms.include(".*" + query + ".*"); | |||
} | |||
SearchRequestBuilder request = this.getClient() | |||
.prepareSearch(RuleIndexDefinition.INDEX) | |||
.setQuery(QueryBuilders.matchAllQuery()) | |||
.addAggregation(terms); | |||
SearchResponse esResponse = request.get(); | |||
Terms aggregation = esResponse.getAggregations().get(key); | |||
if (aggregation != null) { | |||
for (Terms.Bucket value : aggregation.getBuckets()) { | |||
tags.add(value.getKey()); | |||
} | |||
} | |||
return tags; | |||
} | |||
private enum ToRuleKey implements Function<String, RuleKey> { | |||
INSTANCE; | |||
@Override | |||
public RuleKey apply(@Nonnull String input) { | |||
return RuleKey.parse(input); | |||
} | |||
} | |||
} |
@@ -0,0 +1,100 @@ | |||
/* | |||
* 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.rule.index; | |||
import com.google.common.collect.ImmutableMap; | |||
import com.google.common.collect.ImmutableSet; | |||
import java.util.Set; | |||
import org.sonar.api.config.Settings; | |||
import org.sonar.server.es.IndexDefinition; | |||
import org.sonar.server.es.NewIndex; | |||
/** | |||
* Definition of ES index "rules", including settings and fields. | |||
*/ | |||
public class RuleIndexDefinition implements IndexDefinition { | |||
public static final String INDEX = "rules"; | |||
public static final String TYPE_RULE = "rule"; | |||
public static final String FIELD_RULE_KEY = "key"; | |||
// TODO find at what this field is useful ? | |||
public static final String FIELD_RULE_KEY_AS_LIST = "_key"; | |||
public static final String FIELD_RULE_REPOSITORY = "repo"; | |||
public static final String FIELD_RULE_RULE_KEY = "ruleKey"; | |||
public static final String FIELD_RULE_INTERNAL_KEY = "internalKey"; | |||
public static final String FIELD_RULE_NAME = "name"; | |||
public static final String FIELD_RULE_HTML_DESCRIPTION = "htmlDesc"; | |||
public static final String FIELD_RULE_SEVERITY = "severity"; | |||
public static final String FIELD_RULE_STATUS = "status"; | |||
public static final String FIELD_RULE_LANGUAGE = "lang"; | |||
public static final String FIELD_RULE_IS_TEMPLATE = "isTemplate"; | |||
public static final String FIELD_RULE_TEMPLATE_KEY = "templateKey"; | |||
public static final String FIELD_RULE_ALL_TAGS = "allTags"; | |||
public static final String FIELD_RULE_CREATED_AT = "createdAt"; | |||
public static final String FIELD_RULE_UPDATED_AT = "updatedAt"; | |||
public static final Set<String> SORT_FIELDS = ImmutableSet.of( | |||
RuleIndexDefinition.FIELD_RULE_NAME, | |||
RuleIndexDefinition.FIELD_RULE_UPDATED_AT, | |||
RuleIndexDefinition.FIELD_RULE_CREATED_AT, | |||
RuleIndexDefinition.FIELD_RULE_KEY | |||
); | |||
private final Settings settings; | |||
public RuleIndexDefinition(Settings settings) { | |||
this.settings = settings; | |||
} | |||
@Override | |||
public void define(IndexDefinitionContext context) { | |||
NewIndex index = context.create(INDEX); | |||
index.refreshHandledByIndexer(); | |||
index.setShards(settings); | |||
// Rule type | |||
NewIndex.NewIndexType ruleMapping = index.createType(TYPE_RULE); | |||
ruleMapping.setAttribute("_id", ImmutableMap.of("path", FIELD_RULE_KEY)); | |||
ruleMapping.setAttribute("_routing", ImmutableMap.of("required", true, "path", RuleIndexDefinition.FIELD_RULE_REPOSITORY)); | |||
ruleMapping.setEnableSource(false); | |||
ruleMapping.stringFieldBuilder(FIELD_RULE_KEY).enableSorting().enableGramSearch().build(); | |||
ruleMapping.stringFieldBuilder(FIELD_RULE_KEY_AS_LIST).enableGramSearch().build(); | |||
ruleMapping.stringFieldBuilder(FIELD_RULE_RULE_KEY).disableSearch().docValues().build(); | |||
ruleMapping.stringFieldBuilder(FIELD_RULE_REPOSITORY).docValues().build(); | |||
ruleMapping.stringFieldBuilder(FIELD_RULE_INTERNAL_KEY).disableSearch().docValues().build(); | |||
ruleMapping.stringFieldBuilder(FIELD_RULE_NAME).enableSorting().enableWordSearch().build(); | |||
ruleMapping.stringFieldBuilder(FIELD_RULE_HTML_DESCRIPTION).enableWordSearch().build(); | |||
ruleMapping.stringFieldBuilder(FIELD_RULE_SEVERITY).docValues().build(); | |||
ruleMapping.stringFieldBuilder(FIELD_RULE_STATUS).docValues().build(); | |||
ruleMapping.stringFieldBuilder(FIELD_RULE_LANGUAGE).enableGramSearch().build(); | |||
ruleMapping.createBooleanField(FIELD_RULE_IS_TEMPLATE); | |||
ruleMapping.stringFieldBuilder(FIELD_RULE_TEMPLATE_KEY).docValues().build(); | |||
ruleMapping.stringFieldBuilder(FIELD_RULE_ALL_TAGS).enableGramSearch().build(); | |||
ruleMapping.createLongField(FIELD_RULE_CREATED_AT); | |||
ruleMapping.createLongField(FIELD_RULE_UPDATED_AT); | |||
} | |||
} |
@@ -0,0 +1,101 @@ | |||
/* | |||
* 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.rule.index; | |||
import java.util.Iterator; | |||
import org.elasticsearch.action.index.IndexRequest; | |||
import org.sonar.db.DbClient; | |||
import org.sonar.db.DbSession; | |||
import org.sonar.server.es.BaseIndexer; | |||
import org.sonar.server.es.BulkIndexer; | |||
import org.sonar.server.es.EsClient; | |||
import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_RULE_UPDATED_AT; | |||
import static org.sonar.server.rule.index.RuleIndexDefinition.INDEX; | |||
import static org.sonar.server.rule.index.RuleIndexDefinition.TYPE_RULE; | |||
public class RuleIndexer extends BaseIndexer { | |||
private final DbClient dbClient; | |||
public RuleIndexer(DbClient dbClient, EsClient esClient) { | |||
super(esClient, 300, INDEX, TYPE_RULE, FIELD_RULE_UPDATED_AT); | |||
this.dbClient = dbClient; | |||
} | |||
@Override | |||
protected long doIndex(long lastUpdatedAt) { | |||
return doIndex(createBulkIndexer(false), lastUpdatedAt); | |||
} | |||
public void index(Iterator<RuleDoc> rules) { | |||
doIndex(createBulkIndexer(false), rules); | |||
} | |||
private long doIndex(BulkIndexer bulk, long lastUpdatedAt) { | |||
DbSession dbSession = dbClient.openSession(false); | |||
long maxDate; | |||
try { | |||
RuleResultSetIterator rowIt = RuleResultSetIterator.create(dbClient, dbSession, lastUpdatedAt); | |||
maxDate = doIndex(bulk, rowIt); | |||
rowIt.close(); | |||
return maxDate; | |||
} finally { | |||
dbSession.close(); | |||
} | |||
} | |||
private long doIndex(BulkIndexer bulk, Iterator<RuleDoc> rules) { | |||
bulk.start(); | |||
long maxDate = 0L; | |||
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)); | |||
// it's more efficient to sort programmatically than in SQL on some databases (MySQL for instance) | |||
maxDate = Math.max(maxDate, rule.updatedAtAtAsLong()); | |||
} | |||
bulk.stop(); | |||
return maxDate; | |||
} | |||
private BulkIndexer createBulkIndexer(boolean large) { | |||
BulkIndexer bulk = new BulkIndexer(esClient, INDEX); | |||
bulk.setLarge(large); | |||
return bulk; | |||
} | |||
private IndexRequest newUpsertRequest(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()); | |||
// } | |||
} |
@@ -20,15 +20,12 @@ | |||
package org.sonar.server.rule.index; | |||
import com.google.common.base.Preconditions; | |||
import org.sonar.api.rule.RuleStatus; | |||
import org.sonar.api.rule.Severity; | |||
import org.sonar.server.search.IndexField; | |||
import javax.annotation.CheckForNull; | |||
import javax.annotation.Nullable; | |||
import java.util.Collection; | |||
import java.util.Date; | |||
import javax.annotation.CheckForNull; | |||
import javax.annotation.Nullable; | |||
import org.sonar.api.rule.RuleStatus; | |||
import org.sonar.api.rule.Severity; | |||
public class RuleQuery { | |||
@@ -45,8 +42,8 @@ public class RuleQuery { | |||
private Collection<String> activeSeverities; | |||
private String templateKey; | |||
private Boolean isTemplate; | |||
private Date availableSince; | |||
private IndexField sortField; | |||
private Long availableSince; | |||
private String sortField; | |||
private boolean ascendingSort = true; | |||
private String internalKey; | |||
private String ruleKey; | |||
@@ -203,15 +200,15 @@ public class RuleQuery { | |||
return this; | |||
} | |||
public IndexField getSortField() { | |||
public String getSortField() { | |||
return this.sortField; | |||
} | |||
public RuleQuery setSortField(@Nullable IndexField sf) { | |||
if (sf != null && !sf.isSortable()) { | |||
throw new IllegalStateException(String.format("Field '%s' is not sortable", sf.field())); | |||
public RuleQuery setSortField(@Nullable String field) { | |||
if (field != null && !RuleIndexDefinition.SORT_FIELDS.contains(field)) { | |||
throw new IllegalStateException(String.format("Field '%s' is not sortable", field)); | |||
} | |||
this.sortField = sf; | |||
this.sortField = field; | |||
return this; | |||
} | |||
@@ -224,12 +221,23 @@ public class RuleQuery { | |||
return this; | |||
} | |||
@Deprecated | |||
public RuleQuery setAvailableSince(@Nullable Date d) { | |||
this.availableSince = d; | |||
this.availableSince = d.getTime(); | |||
return this; | |||
} | |||
@Deprecated | |||
public Date getAvailableSince() { | |||
return new Date(this.availableSince); | |||
} | |||
public RuleQuery setAvailableSince(@Nullable Long l) { | |||
this.availableSince = l; | |||
return this; | |||
} | |||
public Long getAvailableSinceLong() { | |||
return this.availableSince; | |||
} | |||
@@ -0,0 +1,141 @@ | |||
/* | |||
* 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.rule.index; | |||
import com.google.common.base.Splitter; | |||
import com.google.common.collect.ImmutableList; | |||
import com.google.common.collect.ImmutableSet; | |||
import com.google.common.collect.Maps; | |||
import com.google.common.collect.Sets; | |||
import java.sql.PreparedStatement; | |||
import java.sql.ResultSet; | |||
import java.sql.SQLException; | |||
import java.util.Set; | |||
import javax.annotation.Nullable; | |||
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.rule.RuleDto; | |||
import org.sonar.db.rule.SeverityUtil; | |||
import org.sonar.markdown.Markdown; | |||
/** | |||
* Scrolls over table RULES and reads documents to populate the rules index | |||
*/ | |||
public class RuleResultSetIterator extends ResultSetIterator<RuleDoc> { | |||
private static final String[] FIELDS = { | |||
// column 1 | |||
"r.plugin_rule_key", | |||
"r.plugin_name", | |||
"r.name", | |||
"r.description", | |||
"r.description_format", | |||
"r.priority", | |||
"r.status", | |||
"r.is_template", | |||
"r.tags", | |||
"r.system_tags", | |||
// column 11 | |||
"t.plugin_rule_key", | |||
"t.plugin_name", | |||
"r.plugin_config_key", | |||
"r.language", | |||
"r.created_at_ms", | |||
"r.updated_at_ms", | |||
}; | |||
private static final String SQL_ALL = "SELECT " + StringUtils.join(FIELDS, ",") + " FROM rules r " + | |||
"LEFT OUTER JOIN rules t ON t.id=r.template_id"; | |||
private static final String SQL_AFTER_DATE = SQL_ALL + " WHERE r.updated_at_ms>?"; | |||
private static final Splitter TAGS_SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings(); | |||
private RuleResultSetIterator(PreparedStatement stmt) throws SQLException { | |||
super(stmt); | |||
} | |||
static RuleResultSetIterator 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 RuleResultSetIterator(stmt); | |||
} catch (SQLException e) { | |||
throw new IllegalStateException("Fail to prepare SQL request to select all rules", e); | |||
} | |||
} | |||
@Override | |||
protected RuleDoc read(ResultSet rs) throws SQLException { | |||
RuleDoc doc = new RuleDoc(Maps.<String, Object>newHashMapWithExpectedSize(16)); | |||
String ruleKey = rs.getString(1); | |||
String repositoryKey = rs.getString(2); | |||
RuleKey key = RuleKey.of(repositoryKey, ruleKey); | |||
// all the fields must be present, even if value is null | |||
doc.setKey(key.toString()); | |||
doc.setKeyAsList(ImmutableList.of(repositoryKey, ruleKey)); | |||
doc.setRuleKey(ruleKey); | |||
doc.setRepository(repositoryKey); | |||
doc.setName(rs.getString(3)); | |||
String description = rs.getString(4); | |||
String descriptionFormat = rs.getString(5); | |||
if (descriptionFormat != null) { | |||
if (RuleDto.Format.HTML.equals(RuleDto.Format.valueOf(descriptionFormat))) { | |||
doc.setHtmlDescription(description); | |||
} else { | |||
doc.setHtmlDescription(description == null ? null : Markdown.convertToHtml(description)); | |||
} | |||
} | |||
doc.setSeverity(SeverityUtil.getSeverityFromOrdinal(rs.getInt(6))); | |||
doc.setStatus(rs.getString(7)); | |||
doc.setIsTemplate(rs.getBoolean(8)); | |||
doc.setAllTags(Sets.union(stringTagsToSet(rs.getString(9)), stringTagsToSet(rs.getString(10)))); | |||
String templateRuleKey = rs.getString(11); | |||
String templateRepoKey = rs.getString(12); | |||
if (templateRepoKey != null && templateRuleKey != null) { | |||
doc.setTemplateKey(RuleKey.of(templateRepoKey, templateRuleKey).toString()); | |||
} else { | |||
doc.setTemplateKey(null); | |||
} | |||
doc.setInternalKey(rs.getString(13)); | |||
doc.setLanguage(rs.getString(14)); | |||
doc.setCreatedAt(rs.getLong(15)); | |||
doc.setUpdatedAt(rs.getLong(16)); | |||
return doc; | |||
} | |||
private static Set<String> stringTagsToSet(@Nullable String tags) { | |||
return ImmutableSet.copyOf(TAGS_SPLITTER.split(tags == null ? "" : tags)); | |||
} | |||
} |
@@ -55,8 +55,8 @@ import org.sonar.db.rule.RuleDto; | |||
import org.sonar.db.rule.RuleParamDto; | |||
import org.sonar.server.qualityprofile.ActiveRule; | |||
import org.sonar.server.rule.Rule; | |||
import org.sonar.server.rule.index.RuleIndex; | |||
import org.sonar.server.rule.index.RuleNormalizer; | |||
import org.sonar.server.rule.index.RuleIndex2; | |||
import org.sonar.server.rule.index.RuleIndexDefinition; | |||
import org.sonar.server.rule.index.RuleQuery; | |||
import org.sonar.server.search.FacetValue; | |||
import org.sonar.server.search.Facets; | |||
@@ -71,6 +71,15 @@ import static com.google.common.collect.FluentIterable.from; | |||
import static org.sonar.server.search.QueryContext.MAX_LIMIT; | |||
import static org.sonar.server.ws.WsUtils.writeProtobuf; | |||
import static org.sonar.server.rule.index.RuleIndex2.ALL_STATUSES_EXCEPT_REMOVED; | |||
import static org.sonar.server.rule.index.RuleIndex2.FACET_ACTIVE_SEVERITIES; | |||
import static org.sonar.server.rule.index.RuleIndex2.FACET_LANGUAGES; | |||
import static org.sonar.server.rule.index.RuleIndex2.FACET_OLD_DEFAULT; | |||
import static org.sonar.server.rule.index.RuleIndex2.FACET_REPOSITORIES; | |||
import static org.sonar.server.rule.index.RuleIndex2.FACET_SEVERITIES; | |||
import static org.sonar.server.rule.index.RuleIndex2.FACET_STATUSES; | |||
import static org.sonar.server.rule.index.RuleIndex2.FACET_TAGS; | |||
/** | |||
* @since 4.4 | |||
*/ | |||
@@ -97,12 +106,12 @@ public class SearchAction implements RulesWsAction { | |||
private final UserSession userSession; | |||
private final DbClient dbClient; | |||
private final RuleIndex ruleIndex; | |||
private final RuleIndex2 ruleIndex; | |||
private final ActiveRuleCompleter activeRuleCompleter; | |||
private final RuleMapping mapping; | |||
private final RuleMapper mapper; | |||
public SearchAction(RuleIndex ruleIndex, ActiveRuleCompleter activeRuleCompleter, RuleMapping mapping, UserSession userSession, DbClient dbClient, RuleMapper mapper) { | |||
public SearchAction(RuleIndex2 ruleIndex, ActiveRuleCompleter activeRuleCompleter, RuleMapping mapping, UserSession userSession, DbClient dbClient, RuleMapper mapper) { | |||
this.userSession = userSession; | |||
this.ruleIndex = ruleIndex; | |||
this.activeRuleCompleter = activeRuleCompleter; | |||
@@ -181,13 +190,14 @@ public class SearchAction implements RulesWsAction { | |||
@CheckForNull | |||
protected Collection<String> possibleFacets() { | |||
return Arrays.asList( | |||
RuleIndex.FACET_LANGUAGES, | |||
RuleIndex.FACET_REPOSITORIES, | |||
RuleIndex.FACET_TAGS, | |||
RuleIndex.FACET_SEVERITIES, | |||
RuleIndex.FACET_ACTIVE_SEVERITIES, | |||
RuleIndex.FACET_STATUSES, | |||
RuleIndex.FACET_OLD_DEFAULT); | |||
FACET_LANGUAGES, | |||
FACET_REPOSITORIES, | |||
FACET_TAGS, | |||
FACET_SEVERITIES, | |||
FACET_ACTIVE_SEVERITIES, | |||
FACET_STATUSES, | |||
FACET_OLD_DEFAULT | |||
); | |||
} | |||
/** | |||
@@ -287,11 +297,9 @@ public class SearchAction implements RulesWsAction { | |||
action | |||
.createParam(Param.SORT) | |||
.setDescription("Sort field") | |||
.setPossibleValues(RuleNormalizer.RuleField.NAME.field(), | |||
RuleNormalizer.RuleField.UPDATED_AT.field(), | |||
RuleNormalizer.RuleField.CREATED_AT.field(), | |||
RuleNormalizer.RuleField.KEY.field()) | |||
.setExampleValue(RuleNormalizer.RuleField.NAME.field()); | |||
.setPossibleValues(RuleIndexDefinition.SORT_FIELDS) | |||
.setExampleValue(RuleIndexDefinition.SORT_FIELDS.iterator().next()); | |||
action | |||
.createParam(Param.ASCENDING) | |||
@@ -318,7 +326,7 @@ public class SearchAction implements RulesWsAction { | |||
String sortParam = request.param(Param.SORT); | |||
if (sortParam != null) { | |||
query.setSortField(RuleNormalizer.RuleField.of(sortParam)); | |||
query.setSortField(sortParam); | |||
query.setAscendingSort(request.mandatoryParamAsBoolean(Param.ASCENDING)); | |||
} | |||
return query; | |||
@@ -337,7 +345,7 @@ public class SearchAction implements RulesWsAction { | |||
.setLimit(context.getLimit()) | |||
.setOffset(context.getOffset()) | |||
.setScroll(context.isScroll()); | |||
if (context.facets().contains(RuleIndex.FACET_OLD_DEFAULT)) { | |||
if (context.facets().contains(RuleIndex2.FACET_OLD_DEFAULT)) { | |||
searchQueryContext.addFacets(DEFAULT_FACETS); | |||
} else { | |||
searchQueryContext.addFacets(context.facets()); | |||
@@ -424,12 +432,12 @@ public class SearchAction implements RulesWsAction { | |||
} | |||
protected void writeFacets(SearchResponse.Builder response, Request request, QueryContext context, SearchResult results) { | |||
addMandatoryFacetValues(results, RuleIndex.FACET_LANGUAGES, request.paramAsStrings(PARAM_LANGUAGES)); | |||
addMandatoryFacetValues(results, RuleIndex.FACET_REPOSITORIES, request.paramAsStrings(PARAM_REPOSITORIES)); | |||
addMandatoryFacetValues(results, RuleIndex.FACET_STATUSES, RuleIndex.ALL_STATUSES_EXCEPT_REMOVED); | |||
addMandatoryFacetValues(results, RuleIndex.FACET_SEVERITIES, Severity.ALL); | |||
addMandatoryFacetValues(results, RuleIndex.FACET_ACTIVE_SEVERITIES, Severity.ALL); | |||
addMandatoryFacetValues(results, RuleIndex.FACET_TAGS, request.paramAsStrings(PARAM_TAGS)); | |||
addMandatoryFacetValues(results, FACET_LANGUAGES, request.paramAsStrings(PARAM_LANGUAGES)); | |||
addMandatoryFacetValues(results, FACET_REPOSITORIES, request.paramAsStrings(PARAM_REPOSITORIES)); | |||
addMandatoryFacetValues(results, FACET_STATUSES, ALL_STATUSES_EXCEPT_REMOVED); | |||
addMandatoryFacetValues(results, FACET_SEVERITIES, Severity.ALL); | |||
addMandatoryFacetValues(results, FACET_ACTIVE_SEVERITIES, Severity.ALL); | |||
addMandatoryFacetValues(results, FACET_TAGS, request.paramAsStrings(PARAM_TAGS)); | |||
Common.Facet.Builder facet = Common.Facet.newBuilder(); | |||
Common.FacetValue.Builder value = Common.FacetValue.newBuilder(); |
@@ -30,7 +30,6 @@ 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.rule.index.RuleIndex; | |||
import org.sonar.server.test.index.TestIndexer; | |||
import org.sonar.server.user.index.UserIndexer; | |||
import org.sonar.server.view.index.ViewIndexer; | |||
@@ -56,7 +55,8 @@ public class IndexSynchronizer { | |||
*/ | |||
public IndexSynchronizer(DbClient db, IndexClient index, | |||
TestIndexer testIndexer, IssueAuthorizationIndexer issueAuthorizationIndexer, IssueIndexer issueIndexer, | |||
UserIndexer userIndexer, ViewIndexer viewIndexer, ActivityIndexer activityIndexer, Settings settings) { | |||
UserIndexer userIndexer, ViewIndexer viewIndexer, ActivityIndexer activityIndexer, | |||
Settings settings) { | |||
this.db = db; | |||
this.index = index; | |||
this.testIndexer = testIndexer; | |||
@@ -71,7 +71,7 @@ public class IndexSynchronizer { | |||
public void executeDeprecated() { | |||
DbSession session = db.openSession(false); | |||
try { | |||
synchronize(session, db.deprecatedRuleDao(), index.get(RuleIndex.class)); | |||
// synchronize(session, db.deprecatedRuleDao(), index.get(RuleIndex.class)); | |||
synchronize(session, db.activeRuleDao(), index.get(ActiveRuleIndex.class)); | |||
session.commit(); | |||
} finally { |
@@ -23,6 +23,11 @@ import com.google.common.base.Function; | |||
import com.google.common.base.Throwables; | |||
import com.google.common.collect.Collections2; | |||
import com.google.common.collect.Iterables; | |||
import java.io.File; | |||
import java.io.FileInputStream; | |||
import java.util.Collections; | |||
import java.util.List; | |||
import java.util.Map; | |||
import org.apache.commons.io.IOUtils; | |||
import org.apache.commons.lang.math.RandomUtils; | |||
import org.apache.commons.lang.reflect.ConstructorUtils; | |||
@@ -47,12 +52,6 @@ import org.sonar.server.search.BaseDoc; | |||
import org.sonar.server.search.SearchClient; | |||
import org.sonar.test.TestUtils; | |||
import java.io.File; | |||
import java.io.FileInputStream; | |||
import java.util.Collections; | |||
import java.util.List; | |||
import java.util.Map; | |||
import static com.google.common.collect.Lists.newArrayList; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
@@ -21,6 +21,7 @@ package org.sonar.server.es; | |||
import com.google.common.collect.ImmutableMap; | |||
import java.util.Map; | |||
import org.assertj.core.data.MapEntry; | |||
import org.elasticsearch.cluster.metadata.IndexMetaData; | |||
import org.elasticsearch.common.settings.Settings; | |||
import org.junit.Test; | |||
@@ -66,8 +67,8 @@ public class NewIndexTest { | |||
mapping.createUuidPathField("uuid_path_field"); | |||
mapping = index.getTypes().get("issue"); | |||
assertThat(mapping.getAttributes().get("dynamic")).isEqualTo("true"); | |||
assertThat(mapping).isNotNull(); | |||
assertThat(mapping.getAttributes().get("dynamic")).isEqualTo("true"); | |||
assertThat(mapping.getProperty("foo_field")).isInstanceOf(Map.class); | |||
assertThat((Map) mapping.getProperty("foo_field")).containsEntry("type", "string"); | |||
assertThat((Map) mapping.getProperty("byte_field")).isNotEmpty(); | |||
@@ -184,4 +185,26 @@ public class NewIndexTest { | |||
assertThat(index.getSettings().get(IndexMetaData.SETTING_NUMBER_OF_SHARDS)).isEqualTo("3"); | |||
assertThat(index.getSettings().get(IndexMetaData.SETTING_NUMBER_OF_REPLICAS)).isEqualTo("1"); | |||
} | |||
@Test | |||
public void index_with_source() { | |||
NewIndex index = new NewIndex("issues"); | |||
NewIndex.NewIndexType mapping = index.createType("issue"); | |||
mapping.setEnableSource(true); | |||
mapping = index.getTypes().get("issue"); | |||
assertThat(mapping).isNotNull(); | |||
assertThat((Map<String, Object>)mapping.getAttributes().get("_source")).containsExactly(MapEntry.entry("enabled", true)); | |||
} | |||
@Test | |||
public void index_without_source() { | |||
NewIndex index = new NewIndex("issues"); | |||
NewIndex.NewIndexType mapping = index.createType("issue"); | |||
mapping.setEnableSource(false); | |||
mapping = index.getTypes().get("issue"); | |||
assertThat(mapping).isNotNull(); | |||
assertThat((Map<String, Object>)mapping.getAttributes().get("_source")).containsExactly(MapEntry.entry("enabled", false)); | |||
} | |||
} |
@@ -1,418 +0,0 @@ | |||
/* | |||
* 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.rule; | |||
import com.google.common.collect.ImmutableSet; | |||
import com.google.common.collect.Iterables; | |||
import java.util.Collection; | |||
import java.util.List; | |||
import org.junit.After; | |||
import org.junit.Before; | |||
import org.junit.ClassRule; | |||
import org.junit.Test; | |||
import org.sonar.api.rule.RuleStatus; | |||
import org.sonar.api.server.rule.RuleParamType; | |||
import org.sonar.db.DbSession; | |||
import org.sonar.db.rule.RuleDto; | |||
import org.sonar.db.rule.RuleParamDto; | |||
import org.sonar.db.rule.RuleTesting; | |||
import org.sonar.server.db.DbClient; | |||
import org.sonar.server.platform.Platform; | |||
import org.sonar.server.rule.db.RuleDao; | |||
import org.sonar.server.rule.index.RuleDoc; | |||
import org.sonar.server.rule.index.RuleIndex; | |||
import org.sonar.server.rule.index.RuleQuery; | |||
import org.sonar.server.search.QueryContext; | |||
import org.sonar.server.tester.ServerTester; | |||
import org.sonar.server.tester.UserSessionRule; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
/** | |||
* Test persistence in db and indexation in es (--> integration of DAOs and Indexes) | |||
*/ | |||
public class RuleBackendMediumTest { | |||
@ClassRule | |||
public static ServerTester tester = new ServerTester(); | |||
@org.junit.Rule | |||
public UserSessionRule userSessionRule = UserSessionRule.forServerTester(tester); | |||
RuleDao dao = tester.get(RuleDao.class); | |||
RuleIndex index = tester.get(RuleIndex.class); | |||
DbClient db; | |||
DbSession dbSession; | |||
@Before | |||
public void before() { | |||
tester.clearDbAndIndexes(); | |||
db = tester.get(DbClient.class); | |||
dbSession = tester.get(DbClient.class).openSession(false); | |||
} | |||
@After | |||
public void after() { | |||
dbSession.close(); | |||
} | |||
@Test | |||
public void insert_in_db_and_multiget_in_es() { | |||
// insert db | |||
RuleDto ruleDto = RuleTesting.newXooX1(); | |||
RuleDto ruleDto2 = RuleTesting.newXooX2(); | |||
dao.insert(dbSession, ruleDto, ruleDto2); | |||
dbSession.commit(); | |||
// check that we get two rules | |||
Collection<Rule> hits = index.getByKeys(RuleTesting.XOO_X1, RuleTesting.XOO_X2); | |||
assertThat(hits).hasSize(2); | |||
} | |||
@Test | |||
public void insert_in_db_and_index_in_es() { | |||
// insert db | |||
RuleDto ruleDto = RuleTesting.newXooX1(); | |||
dao.insert(dbSession, ruleDto); | |||
dbSession.commit(); | |||
// verify that rule is persisted in db | |||
RuleDto persistedDto = dao.getNullableByKey(dbSession, RuleTesting.XOO_X1); | |||
assertThat(persistedDto).isNotNull(); | |||
assertThat(persistedDto.getId()).isGreaterThanOrEqualTo(0); | |||
assertThat(persistedDto.getRuleKey()).isEqualTo(ruleDto.getRuleKey()); | |||
assertThat(persistedDto.getLanguage()).isEqualTo(ruleDto.getLanguage()); | |||
assertThat(persistedDto.getCreatedAt()).isNotNull(); | |||
assertThat(persistedDto.getUpdatedAt()).isNotNull(); | |||
// verify that rule is indexed in es | |||
Rule hit = index.getByKey(RuleTesting.XOO_X1); | |||
assertRuleEquivalent(ruleDto, hit); | |||
// Verify Multi-get | |||
Collection<Rule> hits = index.getByKeys(RuleTesting.XOO_X1); | |||
assertThat(hits).hasSize(1); | |||
assertRuleEquivalent(ruleDto, Iterables.getFirst(hits, null)); | |||
} | |||
private void assertRuleEquivalent(RuleDto ruleDto, Rule hit) { | |||
assertThat(hit).isNotNull(); | |||
assertThat(hit.key().repository()).isEqualTo(ruleDto.getRepositoryKey()); | |||
assertThat(hit.key().rule()).isEqualTo(ruleDto.getRuleKey()); | |||
assertThat(hit.language()).isEqualTo(ruleDto.getLanguage()); | |||
assertThat(hit.name()).isEqualTo(ruleDto.getName()); | |||
assertThat(hit.htmlDescription()).isEqualTo(ruleDto.getDescription()); | |||
assertThat(hit.status()).isEqualTo(RuleStatus.READY); | |||
assertThat(hit.createdAt()).isNotNull(); | |||
assertThat(hit.updatedAt()).isNotNull(); | |||
assertThat(hit.internalKey()).isEqualTo(ruleDto.getConfigKey()); | |||
assertThat(hit.severity()).isEqualTo(ruleDto.getSeverityString()); | |||
assertThat(hit.isTemplate()).isFalse(); | |||
assertThat(hit.effortToFixDescription()).isEqualTo(ruleDto.getEffortToFixDescription()); | |||
} | |||
@Test | |||
public void insert_rule_tags_in_db_and_index_in_es() { | |||
// insert db | |||
RuleDto ruleDto = RuleTesting.newXooX1(); | |||
dao.insert(dbSession, ruleDto); | |||
dbSession.commit(); | |||
RuleDto persistedDto = dao.getNullableByKey(dbSession, RuleTesting.XOO_X1); | |||
assertThat(persistedDto.getTags().containsAll(ruleDto.getTags())).isTrue(); | |||
assertThat(persistedDto.getSystemTags().containsAll(ruleDto.getSystemTags())).isTrue(); | |||
Rule hit = index.getByKey(RuleTesting.XOO_X1); | |||
assertThat(hit.tags().containsAll(ruleDto.getTags())).isTrue(); | |||
assertThat(hit.systemTags().containsAll(ruleDto.getSystemTags())).isTrue(); | |||
} | |||
@Test | |||
public void insert_and_index_rule_parameters() { | |||
// insert db | |||
RuleDto ruleDto = RuleTesting.newXooX1(); | |||
dao.insert(dbSession, ruleDto); | |||
dbSession.commit(); | |||
RuleParamDto minParamDto = new RuleParamDto() | |||
.setName("min") | |||
.setType(RuleParamType.INTEGER.type()) | |||
.setDefaultValue("2") | |||
.setDescription("Minimum"); | |||
dao.insertRuleParam(dbSession, ruleDto, minParamDto); | |||
RuleParamDto maxParamDto = new RuleParamDto() | |||
.setName("max") | |||
.setType(RuleParamType.INTEGER.type()) | |||
.setDefaultValue("10") | |||
.setDescription("Maximum"); | |||
dao.insertRuleParam(dbSession, ruleDto, maxParamDto); | |||
dbSession.commit(); | |||
// Verify that RuleDto has date from insertion | |||
RuleDto theRule = dao.getNullableByKey(dbSession, RuleTesting.XOO_X1); | |||
assertThat(theRule.getCreatedAt()).isNotNull(); | |||
assertThat(theRule.getUpdatedAt()).isNotNull(); | |||
// verify that parameters are persisted in db | |||
List<RuleParamDto> persistedDtos = dao.selectRuleParamsByRuleKey(dbSession, theRule.getKey()); | |||
assertThat(persistedDtos).hasSize(2); | |||
// verify that parameters are indexed in es | |||
Rule hit = index.getByKey(RuleTesting.XOO_X1); | |||
assertThat(hit).isNotNull(); | |||
assertThat(hit.key()).isNotNull(); | |||
RuleService service = tester.get(RuleService.class); | |||
Rule rule = service.getByKey(RuleTesting.XOO_X1); | |||
assertThat(rule.params()).hasSize(2); | |||
assertThat(Iterables.getLast(rule.params(), null).key()).isEqualTo("max"); | |||
} | |||
@Test | |||
public void insert_and_delete_rule_parameters() { | |||
// insert db | |||
RuleDto ruleDto = RuleTesting.newXooX1(); | |||
dao.insert(dbSession, ruleDto); | |||
dbSession.commit(); | |||
RuleParamDto minParamDto = new RuleParamDto() | |||
.setName("min") | |||
.setType(RuleParamType.INTEGER.type()) | |||
.setDefaultValue("2") | |||
.setDescription("Minimum"); | |||
dao.insertRuleParam(dbSession, ruleDto, minParamDto); | |||
RuleParamDto maxParamDto = new RuleParamDto() | |||
.setName("max") | |||
.setType(RuleParamType.INTEGER.type()) | |||
.setDefaultValue("10") | |||
.setDescription("Maximum"); | |||
dao.insertRuleParam(dbSession, ruleDto, maxParamDto); | |||
dbSession.commit(); | |||
// 0. Verify that RuleDto has date from insertion | |||
assertThat(dao.selectRuleParamsByRuleKey(dbSession, RuleTesting.XOO_X1)).hasSize(2); | |||
assertThat(index.getByKey(RuleTesting.XOO_X1).params()).hasSize(2); | |||
// 1. Delete parameter | |||
dao.deleteRuleParam(dbSession, ruleDto, maxParamDto); | |||
dbSession.commit(); | |||
// 2. assert only one param left | |||
assertThat(dao.selectRuleParamsByRuleKey(dbSession, RuleTesting.XOO_X1)).hasSize(1); | |||
assertThat(index.getByKey(RuleTesting.XOO_X1).params()).hasSize(1); | |||
} | |||
@Test | |||
public void insert_and_update_rule() { | |||
// insert db | |||
RuleDto ruleDto = RuleTesting.newXooX1() | |||
.setTags(ImmutableSet.of("hello")) | |||
.setName("first name"); | |||
dao.insert(dbSession, ruleDto); | |||
dbSession.commit(); | |||
// verify that parameters are indexed in es | |||
Rule hit = index.getByKey(RuleTesting.XOO_X1); | |||
assertThat(hit.tags()).containsExactly("hello"); | |||
assertThat(hit.name()).isEqualTo("first name"); | |||
// Update in DB | |||
ruleDto.setTags(ImmutableSet.of("world")) | |||
.setName("second name"); | |||
dao.update(dbSession, ruleDto); | |||
dbSession.commit(); | |||
// verify that parameters are updated in es | |||
hit = index.getByKey(RuleTesting.XOO_X1); | |||
assertThat(hit.tags()).containsExactly("world"); | |||
assertThat(hit.name()).isEqualTo("second name"); | |||
} | |||
@Test | |||
public void insert_and_update_rule_param() { | |||
// insert db | |||
RuleDto ruleDto = RuleTesting.newXooX1(); | |||
dao.insert(dbSession, ruleDto); | |||
dbSession.commit(); | |||
RuleParamDto minParamDto = new RuleParamDto() | |||
.setName("min") | |||
.setType(RuleParamType.INTEGER.type()) | |||
.setDefaultValue("2") | |||
.setDescription("Minimum"); | |||
dao.insertRuleParam(dbSession, ruleDto, minParamDto); | |||
RuleParamDto maxParamDto = new RuleParamDto() | |||
.setName("max") | |||
.setType(RuleParamType.INTEGER.type()) | |||
.setDefaultValue("10") | |||
.setDescription("Maximum"); | |||
dao.insertRuleParam(dbSession, ruleDto, maxParamDto); | |||
dbSession.commit(); | |||
// verify that parameters are indexed in es | |||
Rule hit = index.getByKey(RuleTesting.XOO_X1); | |||
assertThat(hit.params()).hasSize(2); | |||
RuleParam param = hit.params().get(0); | |||
assertThat(param.key()).isEqualTo("min"); | |||
assertThat(param.defaultValue()).isEqualTo("2"); | |||
assertThat(param.description()).isEqualTo("Minimum"); | |||
// Update in DB | |||
minParamDto | |||
.setDefaultValue("0.5") | |||
.setDescription("new description"); | |||
dao.updateRuleParam(dbSession, ruleDto, minParamDto); | |||
dbSession.commit(); | |||
// verify that parameters are updated in es | |||
hit = index.getByKey(RuleTesting.XOO_X1); | |||
assertThat(hit.params()).hasSize(2); | |||
param = null; | |||
for (RuleParam pparam : hit.params()) { | |||
if (pparam.key().equals("min")) { | |||
param = pparam; | |||
} | |||
} | |||
assertThat(param).isNotNull(); | |||
assertThat(param.key()).isEqualTo("min"); | |||
assertThat(param.defaultValue()).isEqualTo("0.5"); | |||
assertThat(param.description()).isEqualTo("new description"); | |||
} | |||
@Test | |||
@Deprecated | |||
public void has_id() { | |||
RuleDto ruleDto = RuleTesting.newXooX1(); | |||
dao.insert(dbSession, ruleDto); | |||
dbSession.commit(); | |||
assertThat(((RuleDoc) index.getByKey(RuleTesting.XOO_X1)).id()).isEqualTo(ruleDto.getId()); | |||
} | |||
@Test | |||
public void insert_update_debt_overload() { | |||
RuleDto ruleDto = RuleTesting.newXooX1() | |||
.setRemediationFunction(null) | |||
.setRemediationCoefficient(null) | |||
.setRemediationOffset(null); | |||
RuleDto overloadedRuleDto = RuleTesting.newXooX2(); | |||
dao.insert(dbSession, ruleDto, overloadedRuleDto); | |||
dbSession.commit(); | |||
// Assert is overloaded or not | |||
assertThat(index.getByKey(RuleTesting.XOO_X1).debtOverloaded()).isFalse(); | |||
assertThat(index.getByKey(RuleTesting.XOO_X2).debtOverloaded()).isTrue(); | |||
// Assert overloaded value | |||
Rule base = index.getByKey(RuleTesting.XOO_X1); | |||
Rule overloaded = index.getByKey(RuleTesting.XOO_X2); | |||
assertThat(base.debtRemediationFunction().type().toString()) | |||
.isEqualTo(ruleDto.getDefaultRemediationFunction()); | |||
assertThat(base.debtRemediationFunction().coefficient()) | |||
.isEqualTo(ruleDto.getDefaultRemediationCoefficient()); | |||
assertThat(base.debtRemediationFunction().offset()) | |||
.isEqualTo(ruleDto.getDefaultRemediationOffset()); | |||
assertThat(overloaded.debtRemediationFunction().type().toString()) | |||
.isEqualTo(overloadedRuleDto.getRemediationFunction()); | |||
assertThat(overloaded.debtRemediationFunction().coefficient()) | |||
.isEqualTo(overloadedRuleDto.getRemediationCoefficient()); | |||
assertThat(overloaded.debtRemediationFunction().offset()) | |||
.isEqualTo(overloadedRuleDto.getRemediationOffset()); | |||
} | |||
@Test | |||
public void should_not_find_removed() { | |||
// insert db | |||
dao.insert(dbSession, | |||
RuleTesting.newXooX1(), | |||
RuleTesting.newXooX2().setStatus(RuleStatus.REMOVED)); | |||
dbSession.commit(); | |||
// 0. Assert rules are in DB | |||
assertThat(dao.selectAll(dbSession)).hasSize(2); | |||
// 1. assert getBy for removed | |||
assertThat(index.getByKey(RuleTesting.XOO_X2)).isNotNull(); | |||
// 2. assert find does not get REMOVED | |||
List<Rule> rules = index.search(new RuleQuery(), new QueryContext(userSessionRule)).getHits(); | |||
assertThat(rules).hasSize(1); | |||
assertThat(rules.get(0).key()).isEqualTo(RuleTesting.XOO_X1); | |||
} | |||
@Test | |||
public void synchronize_after() { | |||
// insert db | |||
dao.insert(dbSession, | |||
RuleTesting.newXooX1()); | |||
dbSession.commit(); | |||
// 0. Assert rules are in DB | |||
assertThat(dao.selectAll(dbSession)).hasSize(1); | |||
assertThat(index.countAll()).isEqualTo(1); | |||
tester.clearIndexes(); | |||
assertThat(index.countAll()).isEqualTo(0); | |||
tester.get(Platform.class).executeStartupTasks(); | |||
assertThat(index.countAll()).isEqualTo(1); | |||
} | |||
@Test | |||
public void synchronize_after_with_nested() { | |||
RuleDto rule = RuleTesting.newXooX1(); | |||
// insert db | |||
dao.insert(dbSession, rule); | |||
dao.insertRuleParam(dbSession, rule, RuleParamDto.createFor(rule).setName("MyParam").setType("STRING").setDefaultValue("test")); | |||
dbSession.commit(); | |||
// 0. Assert rules are in DB | |||
assertThat(dao.selectAll(dbSession)).hasSize(1); | |||
assertThat(index.countAll()).isEqualTo(1); | |||
tester.clearIndexes(); | |||
assertThat(index.countAll()).isEqualTo(0); | |||
tester.get(Platform.class).executeStartupTasks(); | |||
assertThat(index.countAll()).isEqualTo(1); | |||
assertThat(index.getByKey(rule.getKey()).param("MyParam").defaultValue()).isEqualTo("test"); | |||
} | |||
} |
@@ -0,0 +1,50 @@ | |||
/* | |||
* 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.rule.index; | |||
import com.google.common.collect.Maps; | |||
import java.util.Arrays; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.rule.RuleStatus; | |||
import org.sonar.api.rule.Severity; | |||
import org.sonar.db.rule.RuleTesting; | |||
public class RuleDocTesting { | |||
public static RuleDoc newDoc() { | |||
return newDoc(RuleTesting.XOO_X1); | |||
} | |||
public static RuleDoc newDoc(RuleKey ruleKey) { | |||
return new RuleDoc(Maps.<String, Object>newHashMap()) | |||
.setKey(ruleKey.toString()) | |||
.setRepository(ruleKey.repository()) | |||
.setRuleKey(ruleKey.rule()) | |||
.setName("Name " + ruleKey.toString()) | |||
.setHtmlDescription("Description " + ruleKey.rule()) | |||
.setSeverity(Severity.CRITICAL) | |||
.setStatus(RuleStatus.READY.name()) | |||
.setLanguage("xoo") | |||
.setIsTemplate(false) | |||
.setAllTags(Arrays.asList("bug", "performance")) | |||
.setCreatedAt(150000000L) | |||
.setUpdatedAt(160000000L); | |||
} | |||
} |
@@ -0,0 +1,732 @@ | |||
/* | |||
* 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.rule.index; | |||
import com.google.common.collect.ImmutableList; | |||
import com.google.common.collect.ImmutableSet; | |||
import java.util.Collections; | |||
import java.util.List; | |||
import java.util.Map; | |||
import org.junit.Before; | |||
import org.junit.ClassRule; | |||
import org.junit.Ignore; | |||
import org.junit.Rule; | |||
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.server.es.EsTester; | |||
import org.sonar.server.es.SearchIdResult; | |||
import org.sonar.server.es.SearchOptions; | |||
import org.sonar.server.tester.UserSessionRule; | |||
import static java.util.Arrays.asList; | |||
import static java.util.Collections.singleton; | |||
import static java.util.Collections.singletonList; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.assertj.core.data.MapEntry.entry; | |||
import static org.junit.Assert.fail; | |||
import static org.sonar.api.rule.Severity.BLOCKER; | |||
import static org.sonar.api.rule.Severity.INFO; | |||
import static org.sonar.api.rule.Severity.MINOR; | |||
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; | |||
public class RuleIndex2Test { | |||
@ClassRule | |||
public static EsTester tester = new EsTester().addDefinitions(new RuleIndexDefinition(new Settings())); | |||
@Rule | |||
public UserSessionRule userSessionRule = UserSessionRule.standalone(); | |||
RuleIndex2 index; | |||
RuleIndexer ruleIndexer; | |||
@Before | |||
public void setUp() { | |||
tester.truncateIndices(); | |||
ruleIndexer = new RuleIndexer(null, tester.client()); | |||
index = new RuleIndex2(tester.client()); | |||
} | |||
@Test | |||
public void search_all_rules() { | |||
indexRules( | |||
newDoc(RuleKey.of("javascript", "S001")), | |||
newDoc(RuleKey.of("java", "S002"))); | |||
SearchIdResult results = index.search(new RuleQuery(), new SearchOptions()); | |||
assertThat(results.getTotal()).isEqualTo(2); | |||
assertThat(results.getIds()).hasSize(2); | |||
} | |||
@Test | |||
public void search_key_by_query() { | |||
indexRules( | |||
newDoc(RuleKey.of("javascript", "X001")), | |||
newDoc(RuleKey.of("cobol", "X001")), | |||
newDoc(RuleKey.of("php", "S002"))); | |||
// key | |||
RuleQuery query = new RuleQuery().setQueryText("X001"); | |||
assertThat(index.search(query, new SearchOptions()).getIds()).hasSize(2); | |||
// partial key does not match | |||
query = new RuleQuery().setQueryText("X00"); | |||
assertThat(index.search(query, new SearchOptions()).getIds()).isEmpty(); | |||
// repo:key -> nice-to-have ! | |||
query = new RuleQuery().setQueryText("javascript:X001"); | |||
assertThat(index.search(query, new SearchOptions()).getIds()).hasSize(1); | |||
} | |||
@Test | |||
public void filter_by_key() { | |||
indexRules( | |||
newDoc(RuleKey.of("javascript", "X001")), | |||
newDoc(RuleKey.of("cobol", "X001")), | |||
newDoc(RuleKey.of("php", "S002"))); | |||
// key | |||
RuleQuery query = new RuleQuery().setKey(RuleKey.of("javascript", "X001").toString()); | |||
assertThat(index.search(query, new SearchOptions()).getIds()).hasSize(1); | |||
// partial key does not match | |||
query = new RuleQuery().setKey("X001"); | |||
assertThat(index.search(query, new SearchOptions()).getIds()).isEmpty(); | |||
} | |||
@Test | |||
public void search_name_by_query() { | |||
indexRules(newDoc(RuleKey.of("javascript", "S001")) | |||
.setName("testing the partial match and matching of rule")); | |||
// substring | |||
RuleQuery query = new RuleQuery().setQueryText("test"); | |||
assertThat(index.search(query, new SearchOptions()).getIds()).hasSize(1); | |||
// substring | |||
query = new RuleQuery().setQueryText("partial match"); | |||
assertThat(index.search(query, new SearchOptions()).getIds()).hasSize(1); | |||
// case-insensitive | |||
query = new RuleQuery().setQueryText("TESTING"); | |||
assertThat(index.search(query, new SearchOptions()).getIds()).hasSize(1); | |||
// not found | |||
query = new RuleQuery().setQueryText("not present"); | |||
assertThat(index.search(query, new SearchOptions()).getIds()).isEmpty(); | |||
} | |||
@Test | |||
public void search_name_with_protected_chars() { | |||
String nameWithProtectedChars = "ja#va&sc\"r:ipt"; | |||
indexRules(newDoc(RuleKey.of("javascript", "S001")) | |||
.setName(nameWithProtectedChars)); | |||
RuleQuery protectedCharsQuery = new RuleQuery().setQueryText(nameWithProtectedChars); | |||
List<RuleKey> results = index.search(protectedCharsQuery, new SearchOptions()).getIds(); | |||
assertThat(results).containsOnly(RuleKey.of("javascript", "S001")); | |||
} | |||
@Test | |||
public void search_by_any_of_repositories() { | |||
indexRules( | |||
newDoc(RuleKey.of("findbugs", "S001")), | |||
newDoc(RuleKey.of("pmd", "S002"))); | |||
RuleQuery query = new RuleQuery().setRepositories(asList("checkstyle", "pmd")); | |||
SearchIdResult results = index.search(query, new SearchOptions()); | |||
assertThat(results.getIds()).containsOnly(RuleKey.of("pmd", "S002")); | |||
// no results | |||
query = new RuleQuery().setRepositories(singletonList("checkstyle")); | |||
assertThat(index.search(query, new SearchOptions()).getIds()).isEmpty(); | |||
// empty list => no filter | |||
query = new RuleQuery().setRepositories(Collections.<String>emptyList()); | |||
assertThat(index.search(query, new SearchOptions()).getIds()).hasSize(2); | |||
} | |||
@Test | |||
public void search_by_tag() { | |||
indexRules( | |||
newDoc(RuleKey.of("java", "S001")).setAllTags(singleton("tag1")), | |||
newDoc(RuleKey.of("java", "S002")).setAllTags(singleton("tag2"))); | |||
// find all | |||
RuleQuery query = new RuleQuery(); | |||
assertThat(index.search(query, new SearchOptions()).getIds()).hasSize(2); | |||
// tag1 in query | |||
query = new RuleQuery().setQueryText("tag1"); | |||
assertThat(index.search(query, new SearchOptions()).getIds()).containsOnly(RuleKey.of("java", "S001")); | |||
// tag1 and tag2 in query | |||
query = new RuleQuery().setQueryText("tag1 tag2"); | |||
assertThat(index.search(query, new SearchOptions()).getIds()).hasSize(2); | |||
// tag2 in filter | |||
query = new RuleQuery().setTags(ImmutableSet.of("tag2")); | |||
assertThat(index.search(query, new SearchOptions()).getIds()).containsOnly(RuleKey.of("java", "S002")); | |||
// tag2 in filter and tag1 tag2 in query | |||
query = new RuleQuery().setTags(ImmutableSet.of("tag2")).setQueryText("tag1"); | |||
assertThat(index.search(query, new SearchOptions()).getIds()).hasSize(0); | |||
// tag2 in filter and tag1 in query | |||
query = new RuleQuery().setTags(ImmutableSet.of("tag2")).setQueryText("tag1 tag2"); | |||
assertThat(index.search(query, new SearchOptions()).getIds()).containsOnly(RuleKey.of("java", "S002")); | |||
// null list => no filter | |||
query = new RuleQuery().setTags(Collections.<String>emptySet()); | |||
assertThat(index.search(query, new SearchOptions()).getIds()).hasSize(2); | |||
// null list => no filter | |||
query = new RuleQuery().setTags(null); | |||
assertThat(index.search(query, new SearchOptions()).getIds()).hasSize(2); | |||
} | |||
@Test | |||
public void search_by_is_template() { | |||
indexRules( | |||
newDoc(RuleKey.of("java", "S001")).setIsTemplate(false), | |||
newDoc(RuleKey.of("java", "S002")).setIsTemplate(true)); | |||
// find all | |||
RuleQuery query = new RuleQuery(); | |||
SearchIdResult results = index.search(query, new SearchOptions()); | |||
assertThat(results.getIds()).hasSize(2); | |||
// Only template | |||
query = new RuleQuery().setIsTemplate(true); | |||
results = index.search(query, new SearchOptions()); | |||
assertThat(results.getIds()).containsOnly(RuleKey.of("java", "S002")); | |||
// Only not template | |||
query = new RuleQuery().setIsTemplate(false); | |||
results = index.search(query, new SearchOptions()); | |||
assertThat(results.getIds()).containsOnly(RuleKey.of("java", "S001")); | |||
// null => no filter | |||
query = new RuleQuery().setIsTemplate(null); | |||
assertThat(index.search(query, new SearchOptions()).getIds()).hasSize(2); | |||
} | |||
@Test | |||
public void search_by_template_key() { | |||
indexRules( | |||
newDoc(RuleKey.of("java", "S001")).setIsTemplate(true), | |||
newDoc(RuleKey.of("java", "S001_MY_CUSTOM")).setTemplateKey("java:S001")); | |||
// find all | |||
RuleQuery query = new RuleQuery(); | |||
SearchIdResult results = index.search(query, new SearchOptions()); | |||
assertThat(results.getIds()).hasSize(2); | |||
// Only custom rule | |||
query = new RuleQuery().setTemplateKey("java:S001"); | |||
results = index.search(query, new SearchOptions()); | |||
assertThat(results.getIds()).containsOnly(RuleKey.of("java", "S001_MY_CUSTOM")); | |||
// null => no filter | |||
query = new RuleQuery().setTemplateKey(null); | |||
assertThat(index.search(query, new SearchOptions()).getIds()).hasSize(2); | |||
} | |||
@Test | |||
public void search_by_any_of_languages() { | |||
indexRules( | |||
newDoc(RuleKey.of("java", "S001")).setLanguage("java"), | |||
newDoc(RuleKey.of("javascript", "S002")).setLanguage("js")); | |||
RuleQuery query = new RuleQuery().setLanguages(asList("cobol", "js")); | |||
SearchIdResult results = index.search(query, new SearchOptions()); | |||
assertThat(results.getIds()).containsOnly(RuleKey.of("javascript", "S002")); | |||
// no results | |||
query = new RuleQuery().setLanguages(singletonList("cpp")); | |||
assertThat(index.search(query, new SearchOptions()).getIds()).isEmpty(); | |||
// empty list => no filter | |||
query = new RuleQuery().setLanguages(Collections.<String>emptyList()); | |||
assertThat(index.search(query, new SearchOptions()).getIds()).hasSize(2); | |||
// null list => no filter | |||
query = new RuleQuery().setLanguages(null); | |||
assertThat(index.search(query, new SearchOptions()).getIds()).hasSize(2); | |||
} | |||
@Test | |||
public void search_by_any_of_severities() { | |||
indexRules( | |||
newDoc(RuleKey.of("java", "S001")).setSeverity(BLOCKER), | |||
newDoc(RuleKey.of("java", "S002")).setSeverity(INFO)); | |||
RuleQuery query = new RuleQuery().setSeverities(asList(INFO, MINOR)); | |||
SearchIdResult results = index.search(query, new SearchOptions()); | |||
assertThat(results.getIds()).containsOnly(RuleKey.of("java", "S002")); | |||
// no results | |||
query = new RuleQuery().setSeverities(singletonList(MINOR)); | |||
assertThat(index.search(query, new SearchOptions()).getIds()).isEmpty(); | |||
// empty list => no filter | |||
query = new RuleQuery().setSeverities(Collections.<String>emptyList()); | |||
assertThat(index.search(query, new SearchOptions()).getIds()).hasSize(2); | |||
// null list => no filter | |||
query = new RuleQuery().setSeverities(null); | |||
assertThat(index.search(query, new SearchOptions()).getIds()).hasSize(2); | |||
} | |||
@Test | |||
public void search_by_any_of_statuses() { | |||
indexRules( | |||
newDoc(RuleKey.of("java", "S001")).setStatus(RuleStatus.BETA.name()), | |||
newDoc(RuleKey.of("java", "S002")).setStatus(RuleStatus.READY.name())); | |||
RuleQuery query = new RuleQuery().setStatuses(asList(RuleStatus.DEPRECATED, RuleStatus.READY)); | |||
SearchIdResult results = index.search(query, new SearchOptions()); | |||
assertThat(results.getIds()).containsOnly(RuleKey.of("java", "S002")); | |||
// no results | |||
query = new RuleQuery().setStatuses(singletonList(RuleStatus.DEPRECATED)); | |||
assertThat(index.search(query, new SearchOptions()).getIds()).isEmpty(); | |||
// empty list => no filter | |||
query = new RuleQuery().setStatuses(Collections.<RuleStatus>emptyList()); | |||
assertThat(index.search(query, new SearchOptions()).getIds()).hasSize(2); | |||
// null list => no filter | |||
query = new RuleQuery().setStatuses(null); | |||
assertThat(index.search(query, new SearchOptions()).getIds()).hasSize(2); | |||
} | |||
@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()); | |||
} | |||
@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); | |||
} | |||
@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)); | |||
} | |||
@Test | |||
public void all_tags() { | |||
indexRules( | |||
newDoc(RuleKey.of("java", "S001")).setAllTags(asList("tag1", "sys1", "sys2")), | |||
newDoc(RuleKey.of("java", "S002")).setAllTags(asList("tag2"))); | |||
assertThat(index.terms(RuleIndexDefinition.FIELD_RULE_ALL_TAGS, null, 10)).containsOnly("tag1", "tag2", "sys1", "sys2"); | |||
} | |||
@Test | |||
public void available_since() throws InterruptedException { | |||
indexRules( | |||
newDoc(RuleKey.of("java", "S001")).setCreatedAt(1000L), | |||
newDoc(RuleKey.of("java", "S002")).setCreatedAt(2000L)); | |||
// 0. find all rules; | |||
assertThat(index.search(new RuleQuery(), new SearchOptions()).getIds()).hasSize(2); | |||
// 1. find all rules available since a date; | |||
RuleQuery availableSinceQuery = new RuleQuery().setAvailableSince(2000L); | |||
assertThat(index.search(availableSinceQuery, new SearchOptions()).getIds()).containsOnly(RuleKey.of("java", "S002")); | |||
// 2. find no new rules since tomorrow. | |||
RuleQuery availableSinceNowQuery = new RuleQuery().setAvailableSince(3000L); | |||
assertThat(index.search(availableSinceNowQuery, new SearchOptions()).getIds()).hasSize(0); | |||
} | |||
@Test | |||
public void global_facet_on_repositories_and_tags() { | |||
indexRules( | |||
newDoc(RuleKey.of("php", "S001")).setAllTags(singletonList("sysTag")), | |||
newDoc(RuleKey.of("php", "S002")).setAllTags(singletonList("tag1")), | |||
newDoc(RuleKey.of("javascript", "S002")).setAllTags(asList("tag1", "tag2"))); | |||
// should not have any facet! | |||
RuleQuery query = new RuleQuery(); | |||
SearchIdResult result = index.search(query, new SearchOptions()); | |||
assertThat(result.getFacets().getAll()).isEmpty(); | |||
// should not have any facet on non matching query! | |||
result = index.search(new RuleQuery().setQueryText("aeiou"), new SearchOptions().addFacets(singletonList("repositories"))); | |||
assertThat(result.getFacets().getAll()).hasSize(1); | |||
assertThat(result.getFacets().getAll().get("repositories")).isEmpty(); | |||
// Repositories Facet is preset | |||
result = index.search(query, new SearchOptions().addFacets(asList("repositories", "tags"))); | |||
assertThat(result.getFacets()).isNotNull(); | |||
assertThat(result.getFacets().getAll()).hasSize(2); | |||
// Verify the value of a given facet | |||
Map<String, Long> repoFacets = result.getFacets().get("repositories"); | |||
assertThat(repoFacets).containsOnly(entry("php", 2L), entry("javascript", 1L)); | |||
// Check that tag facet has both Tags and SystemTags values | |||
Map<String, Long> tagFacets = result.getFacets().get("tags"); | |||
assertThat(tagFacets).containsOnly(entry("tag1", 2L), entry("sysTag", 1L), entry("tag2", 1L)); | |||
} | |||
@Test | |||
public void sticky_facets() { | |||
indexRules( | |||
newDoc(RuleKey.of("xoo", "S001")).setLanguage("java").setAllTags(Collections.<String>emptyList()), | |||
newDoc(RuleKey.of("xoo", "S002")).setLanguage("java").setAllTags(Collections.<String>emptyList()), | |||
newDoc(RuleKey.of("xoo", "S003")).setLanguage("java").setAllTags(asList("T1", "T2")), | |||
newDoc(RuleKey.of("xoo", "S011")).setLanguage("cobol").setAllTags(Collections.<String>emptyList()), | |||
newDoc(RuleKey.of("xoo", "S012")).setLanguage("cobol").setAllTags(Collections.<String>emptyList()), | |||
newDoc(RuleKey.of("foo", "S013")).setLanguage("cobol").setAllTags(asList("T3", "T4")), | |||
newDoc(RuleKey.of("foo", "S111")).setLanguage("cpp").setAllTags(Collections.<String>emptyList()), | |||
newDoc(RuleKey.of("foo", "S112")).setLanguage("cpp").setAllTags(Collections.<String>emptyList()), | |||
newDoc(RuleKey.of("foo", "S113")).setLanguage("cpp").setAllTags(asList("T2", "T3"))); | |||
// 0 assert Base | |||
assertThat(index.search(new RuleQuery(), new SearchOptions()).getIds()).hasSize(9); | |||
// 1 Facet with no filters at all | |||
SearchIdResult result = index.search(new RuleQuery(), new SearchOptions().addFacets(asList("languages", "repositories", "tags"))); | |||
assertThat(result.getFacets().getAll()).hasSize(3); | |||
assertThat(result.getFacets().getAll().get(FACET_LANGUAGES).keySet()).containsOnly("cpp", "java", "cobol"); | |||
assertThat(result.getFacets().getAll().get(FACET_REPOSITORIES).keySet()).containsOnly("xoo", "foo"); | |||
assertThat(result.getFacets().getAll().get(FACET_TAGS).keySet()).containsOnly("T1", "T2", "T3", "T4"); | |||
// 2 Facet with a language filter | |||
// -- lang facet should still have all language | |||
result = index.search(new RuleQuery().setLanguages(ImmutableList.of("cpp")) | |||
, new SearchOptions().addFacets(asList("languages", "repositories", "tags"))); | |||
assertThat(result.getIds()).hasSize(3); | |||
assertThat(result.getFacets().getAll()).hasSize(3); | |||
assertThat(result.getFacets().get(FACET_LANGUAGES).keySet()).containsOnly("cpp", "java", "cobol"); | |||
// 3 facet with 2 filters | |||
// -- lang facet for tag T2 | |||
// -- tag facet for lang cpp | |||
// -- repository for cpp & T2 | |||
result = index.search(new RuleQuery() | |||
.setLanguages(ImmutableList.of("cpp")) | |||
.setTags(ImmutableList.of("T2")) | |||
, new SearchOptions().addFacets(asList("languages", "repositories", "tags"))); | |||
assertThat(result.getIds()).hasSize(1); | |||
assertThat(result.getFacets().getAll()).hasSize(3); | |||
assertThat(result.getFacets().get(FACET_LANGUAGES).keySet()).containsOnly("cpp", "java"); | |||
assertThat(result.getFacets().get(FACET_REPOSITORIES).keySet()).containsOnly("foo"); | |||
assertThat(result.getFacets().get(FACET_TAGS).keySet()).containsOnly("T2", "T3"); | |||
// 4 facet with 2 filters | |||
// -- lang facet for tag T2 | |||
// -- tag facet for lang cpp & java | |||
// -- repository for (cpp || java) & T2 | |||
result = index.search(new RuleQuery() | |||
.setLanguages(ImmutableList.of("cpp", "java")) | |||
.setTags(ImmutableList.of("T2")) | |||
, new SearchOptions().addFacets(asList("languages", "repositories", "tags"))); | |||
assertThat(result.getIds()).hasSize(2); | |||
assertThat(result.getFacets().getAll()).hasSize(3); | |||
assertThat(result.getFacets().get(FACET_LANGUAGES).keySet()).containsOnly("cpp", "java"); | |||
assertThat(result.getFacets().get(FACET_REPOSITORIES).keySet()).containsOnly("foo", "xoo"); | |||
assertThat(result.getFacets().get(FACET_TAGS).keySet()).containsOnly("T1", "T2", "T3"); | |||
} | |||
@Test | |||
public void sort_by_name() { | |||
indexRules( | |||
newDoc(RuleKey.of("java", "S001")).setName("abcd"), | |||
newDoc(RuleKey.of("java", "S002")).setName("ABC"), | |||
newDoc(RuleKey.of("java", "S003")).setName("FGH")); | |||
// ascending | |||
RuleQuery query = new RuleQuery().setSortField(RuleIndexDefinition.FIELD_RULE_NAME); | |||
SearchIdResult<RuleKey> results = index.search(query, new SearchOptions()); | |||
assertThat(results.getIds()).containsExactly(RuleKey.of("java", "S002"), RuleKey.of("java", "S001"), RuleKey.of("java", "S003")); | |||
// descending | |||
query = new RuleQuery().setSortField(RuleIndexDefinition.FIELD_RULE_NAME).setAscendingSort(false); | |||
results = index.search(query, new SearchOptions()); | |||
assertThat(results.getIds()).containsExactly(RuleKey.of("java", "S003"),RuleKey.of("java", "S001"), RuleKey.of("java", "S002")); | |||
} | |||
@Test | |||
public void default_sort_is_by_updated_at_desc() { | |||
indexRules( | |||
newDoc(RuleKey.of("java", "S001")).setCreatedAt(1000L).setUpdatedAt(1000L), | |||
newDoc(RuleKey.of("java", "S002")).setCreatedAt(1000L).setUpdatedAt(3000L), | |||
newDoc(RuleKey.of("java", "S003")).setCreatedAt(1000L).setUpdatedAt(2000L)); | |||
SearchIdResult<RuleKey> results = index.search(new RuleQuery(), new SearchOptions()); | |||
assertThat(results.getIds()).containsExactly(RuleKey.of("java", "S002"), RuleKey.of("java", "S003"), RuleKey.of("java", "S001")); | |||
} | |||
@Test | |||
public void fail_sort_by_language() { | |||
try { | |||
// Sorting on a field not tagged as sortable | |||
new RuleQuery().setSortField(RuleIndexDefinition.FIELD_RULE_LANGUAGE); | |||
fail(); | |||
} catch (IllegalStateException e) { | |||
assertThat(e).hasMessage("Field 'lang' is not sortable"); | |||
} | |||
} | |||
@Test | |||
public void paging() { | |||
indexRules( | |||
newDoc(RuleKey.of("java", "S001")), | |||
newDoc(RuleKey.of("java", "S002")), | |||
newDoc(RuleKey.of("java", "S003"))); | |||
// from 0 to 1 included | |||
SearchOptions options = new SearchOptions(); | |||
options.setOffset(0).setLimit(2); | |||
SearchIdResult results = index.search(new RuleQuery(), options); | |||
assertThat(results.getTotal()).isEqualTo(3); | |||
assertThat(results.getIds()).hasSize(2); | |||
// from 0 to 9 included | |||
options.setOffset(0).setLimit(10); | |||
results = index.search(new RuleQuery(), options); | |||
assertThat(results.getTotal()).isEqualTo(3); | |||
assertThat(results.getIds()).hasSize(3); | |||
// from 2 to 11 included | |||
options.setOffset(2).setLimit(10); | |||
results = index.search(new RuleQuery(), options); | |||
assertThat(results.getTotal()).isEqualTo(3); | |||
assertThat(results.getIds()).hasSize(1); | |||
// from 2 to 11 included | |||
options.setOffset(2).setLimit(0); | |||
results = index.search(new RuleQuery(), options); | |||
assertThat(results.getTotal()).isEqualTo(3); | |||
assertThat(results.getIds()).hasSize(1); | |||
} | |||
@Test | |||
public void search_all_keys_by_query() { | |||
indexRules( | |||
newDoc(RuleKey.of("javascript", "X001")), | |||
newDoc(RuleKey.of("cobol", "X001")), | |||
newDoc(RuleKey.of("php", "S002"))); | |||
// key | |||
assertThat(index.searchAll(new RuleQuery().setQueryText("X001"))).hasSize(2); | |||
// partial key does not match | |||
assertThat(index.searchAll(new RuleQuery().setQueryText("X00"))).isEmpty(); | |||
// repo:key -> nice-to-have ! | |||
assertThat(index.searchAll(new RuleQuery().setQueryText("javascript:X001"))).hasSize(1); | |||
} | |||
private void indexRules(RuleDoc... rules) { | |||
ruleIndexer.index(asList(rules).iterator()); | |||
} | |||
} |
@@ -0,0 +1,48 @@ | |||
/* | |||
* 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.rule.index; | |||
import org.junit.Test; | |||
import org.sonar.api.config.Settings; | |||
import org.sonar.server.es.IndexDefinition; | |||
import org.sonar.server.es.NewIndex; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
public class RuleIndexDefinitionTest { | |||
IndexDefinition.IndexDefinitionContext underTest = new IndexDefinition.IndexDefinitionContext(); | |||
@Test | |||
public void define() { | |||
RuleIndexDefinition def = new RuleIndexDefinition(new Settings()); | |||
def.define(underTest); | |||
assertThat(underTest.getIndices()).hasSize(1); | |||
NewIndex ruleIndex = underTest.getIndices().get("rules"); | |||
assertThat(ruleIndex).isNotNull(); | |||
assertThat(ruleIndex.getTypes().keySet()).containsOnly("rule"); | |||
// no cluster by default | |||
assertThat(ruleIndex.getSettings().get("index.number_of_shards")).isEqualTo(String.valueOf(NewIndex.DEFAULT_NUMBER_OF_SHARDS)); | |||
assertThat(ruleIndex.getSettings().get("index.number_of_replicas")).isEqualTo("0"); | |||
} | |||
} |
@@ -1,935 +0,0 @@ | |||
/* | |||
* 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.rule.index; | |||
import com.google.common.base.Function; | |||
import com.google.common.collect.ImmutableList; | |||
import com.google.common.collect.ImmutableSet; | |||
import com.google.common.collect.Iterables; | |||
import com.google.common.collect.Sets; | |||
import java.util.Arrays; | |||
import java.util.Collection; | |||
import java.util.Collections; | |||
import java.util.Date; | |||
import java.util.HashSet; | |||
import java.util.Iterator; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import javax.annotation.Nullable; | |||
import org.apache.commons.lang.time.DateUtils; | |||
import org.junit.After; | |||
import org.junit.Before; | |||
import org.junit.ClassRule; | |||
import org.junit.Test; | |||
import org.sonar.api.rule.RuleKey; | |||
import org.sonar.api.rule.RuleStatus; | |||
import org.sonar.api.rule.Severity; | |||
import org.sonar.db.DbSession; | |||
import org.sonar.db.qualityprofile.ActiveRuleDto; | |||
import org.sonar.db.qualityprofile.QualityProfileDto; | |||
import org.sonar.db.rule.RuleDto; | |||
import org.sonar.db.rule.RuleParamDto; | |||
import org.sonar.db.rule.RuleTesting; | |||
import org.sonar.server.db.DbClient; | |||
import org.sonar.server.qualityprofile.ActiveRule; | |||
import org.sonar.server.qualityprofile.QProfileTesting; | |||
import org.sonar.server.rule.Rule; | |||
import org.sonar.server.rule.db.RuleDao; | |||
import org.sonar.server.search.FacetValue; | |||
import org.sonar.server.search.QueryContext; | |||
import org.sonar.server.search.Result; | |||
import org.sonar.server.tester.ServerTester; | |||
import org.sonar.server.tester.UserSessionRule; | |||
import static com.google.common.collect.Lists.newArrayList; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
import static org.junit.Assert.fail; | |||
public class RuleIndexMediumTest { | |||
@ClassRule | |||
public static ServerTester tester = new ServerTester(); | |||
@org.junit.Rule | |||
public UserSessionRule userSessionRule = UserSessionRule.forServerTester(tester); | |||
protected DbClient db; | |||
RuleDao dao; | |||
RuleIndex index; | |||
// IndexClient index; | |||
DbSession dbSession; | |||
@Before | |||
public void before() { | |||
dao = tester.get(RuleDao.class); | |||
index = tester.get(RuleIndex.class); | |||
tester.clearDbAndIndexes(); | |||
db = tester.get(DbClient.class); | |||
// index = tester.get(IndexClient.class); | |||
dbSession = tester.get(DbClient.class).openSession(false); | |||
} | |||
@After | |||
public void after() { | |||
if (dbSession != null) { | |||
dbSession.close(); | |||
} | |||
} | |||
@Test | |||
public void getByKey() { | |||
RuleDto ruleDto = RuleTesting.newDto(RuleKey.of("javascript", "S001")); | |||
dao.insert(dbSession, ruleDto); | |||
dbSession.commit(); | |||
Rule rule = index.getByKey(RuleKey.of("javascript", "S001")); | |||
assertThat(rule.htmlDescription()).isEqualTo(ruleDto.getDescription()); | |||
assertThat(rule.key()).isEqualTo(ruleDto.getKey()); | |||
assertThat(rule.debtRemediationFunction().type().name()) | |||
.isEqualTo(ruleDto.getRemediationFunction()); | |||
assertThat(Sets.newHashSet(rule.tags())).isEqualTo(ruleDto.getTags()); | |||
assertThat(Sets.newHashSet(rule.systemTags())).isEqualTo(ruleDto.getSystemTags()); | |||
} | |||
@Test | |||
public void getByKey_null_if_not_found() { | |||
Rule rule = index.getNullableByKey(RuleKey.of("javascript", "unknown")); | |||
assertThat(rule).isNull(); | |||
} | |||
@Test | |||
public void global_facet_on_repositories_and_tags() { | |||
dao.insert(dbSession, RuleTesting.newDto(RuleKey.of("php", "S001")) | |||
.setSystemTags(ImmutableSet.of("sysTag"))) | |||
.setTags(ImmutableSet.<String>of()); | |||
dao.insert(dbSession, RuleTesting.newDto(RuleKey.of("php", "S002")) | |||
.setSystemTags(ImmutableSet.<String>of())) | |||
.setTags(ImmutableSet.of("tag1")); | |||
dao.insert(dbSession, RuleTesting.newDto(RuleKey.of("javascript", "S002")) | |||
.setTags(ImmutableSet.of("tag1", "tag2"))) | |||
.setSystemTags(ImmutableSet.<String>of()); | |||
dbSession.commit(); | |||
// should not have any facet! | |||
RuleQuery query = new RuleQuery(); | |||
Result result = index.search(query, new QueryContext(userSessionRule)); | |||
assertThat(result.getFacets()).isEmpty(); | |||
// should not have any facet on non matching query! | |||
result = index.search(new RuleQuery().setQueryText("aeiou"), new QueryContext(userSessionRule).addFacets(Arrays.asList("repositories"))); | |||
assertThat(result.getFacets()).isEmpty(); | |||
// Repositories Facet is preset | |||
result = index.search(query, new QueryContext(userSessionRule).addFacets(Arrays.asList("repositories", "tags"))); | |||
assertThat(result.getFacets()).isNotNull(); | |||
assertThat(result.getFacets()).hasSize(2); | |||
// Verify the value of a given facet | |||
Collection<FacetValue> repoFacets = result.getFacetValues("repositories"); | |||
assertThat(repoFacets).hasSize(2); | |||
assertThat(Iterables.get(repoFacets, 0).getKey()).isEqualTo("php"); | |||
assertThat(Iterables.get(repoFacets, 0).getValue()).isEqualTo(2); | |||
assertThat(Iterables.get(repoFacets, 1).getKey()).isEqualTo("javascript"); | |||
assertThat(Iterables.get(repoFacets, 1).getValue()).isEqualTo(1); | |||
// Check that tag facet has both Tags and SystemTags values | |||
Collection<FacetValue> tagFacet = result.getFacetValues("tags"); | |||
assertThat(tagFacet).hasSize(3); | |||
assertThat(Iterables.get(tagFacet, 0).getKey()).isEqualTo("tag1"); | |||
assertThat(Iterables.get(tagFacet, 0).getValue()).isEqualTo(2); | |||
} | |||
@Test | |||
public void return_all_doc_fields_by_default() { | |||
dao.insert(dbSession, RuleTesting.newDto(RuleKey.of("javascript", "S001"))); | |||
dbSession.commit(); | |||
QueryContext options = new QueryContext(userSessionRule).setFieldsToReturn(null); | |||
Result<Rule> results = index.search(new RuleQuery(), options); | |||
assertThat(results.getHits()).hasSize(1); | |||
Rule hit = Iterables.getFirst(results.getHits(), null); | |||
assertThat(hit.key()).isNotNull(); | |||
assertThat(hit.htmlDescription()).isNotNull(); | |||
assertThat(hit.name()).isNotNull(); | |||
options = new QueryContext(userSessionRule).setFieldsToReturn(Collections.<String>emptyList()); | |||
results = index.search(new RuleQuery(), options); | |||
assertThat(results.getHits()).hasSize(1); | |||
hit = Iterables.getFirst(results.getHits(), null); | |||
assertThat(hit.key()).isNotNull(); | |||
assertThat(hit.htmlDescription()).isNotNull(); | |||
assertThat(hit.name()).isNotNull(); | |||
} | |||
@Test | |||
public void select_doc_fields_to_return() { | |||
dao.insert(dbSession, RuleTesting.newDto(RuleKey.of("javascript", "S001"))); | |||
dbSession.commit(); | |||
QueryContext options = new QueryContext(userSessionRule); | |||
options.addFieldsToReturn(RuleNormalizer.RuleField.LANGUAGE.field(), RuleNormalizer.RuleField.STATUS.field()); | |||
Result<Rule> results = index.search(new RuleQuery(), options); | |||
assertThat(results.getHits()).hasSize(1); | |||
Rule hit = Iterables.getFirst(results.getHits(), null); | |||
assertThat(hit.language()).isEqualTo("js"); | |||
assertThat(hit.status()).isEqualTo(RuleStatus.READY); | |||
try { | |||
hit.htmlDescription(); | |||
fail(); | |||
} catch (IllegalStateException e) { | |||
assertThat(e).hasMessage("Field htmlDesc not specified in query options"); | |||
} | |||
} | |||
@Test | |||
public void search_name_by_query() { | |||
dao.insert(dbSession, RuleTesting.newDto(RuleKey.of("javascript", "S001")) | |||
.setName("testing the partial match and matching of rule")); | |||
dbSession.commit(); | |||
// substring | |||
RuleQuery query = new RuleQuery().setQueryText("test"); | |||
assertThat(index.search(query, new QueryContext(userSessionRule)).getHits()).hasSize(1); | |||
// substring | |||
query = new RuleQuery().setQueryText("partial match"); | |||
assertThat(index.search(query, new QueryContext(userSessionRule)).getHits()).hasSize(1); | |||
// case-insensitive | |||
query = new RuleQuery().setQueryText("TESTING"); | |||
assertThat(index.search(query, new QueryContext(userSessionRule)).getHits()).hasSize(1); | |||
// not found | |||
query = new RuleQuery().setQueryText("not present"); | |||
assertThat(index.search(query, new QueryContext(userSessionRule)).getHits()).isEmpty(); | |||
} | |||
@Test | |||
public void search_key_by_query() { | |||
dao.insert(dbSession, RuleTesting.newDto(RuleKey.of("javascript", "X001"))); | |||
dao.insert(dbSession, RuleTesting.newDto(RuleKey.of("cobol", "X001"))); | |||
dao.insert(dbSession, RuleTesting.newDto(RuleKey.of("php", "S002"))); | |||
dbSession.commit(); | |||
// key | |||
RuleQuery query = new RuleQuery().setQueryText("X001"); | |||
assertThat(index.search(query, new QueryContext(userSessionRule)).getHits()).hasSize(2); | |||
// partial key does not match | |||
query = new RuleQuery().setQueryText("X00"); | |||
// TODO fix non-partial match for Key search | |||
assertThat(index.search(query, new QueryContext(userSessionRule)).getHits()).isEmpty(); | |||
// repo:key -> nice-to-have ! | |||
query = new RuleQuery().setQueryText("javascript:X001"); | |||
assertThat(index.search(query, new QueryContext(userSessionRule)).getHits()).hasSize(1); | |||
} | |||
@Test | |||
public void filter_by_key() { | |||
dao.insert(dbSession, RuleTesting.newDto(RuleKey.of("javascript", "X001"))); | |||
dao.insert(dbSession, RuleTesting.newDto(RuleKey.of("cobol", "X001"))); | |||
dao.insert(dbSession, RuleTesting.newDto(RuleKey.of("php", "S002"))); | |||
dbSession.commit(); | |||
// key | |||
RuleQuery query = new RuleQuery().setKey(RuleKey.of("javascript", "X001").toString()); | |||
assertThat(index.search(query, new QueryContext(userSessionRule)).getHits()).hasSize(1); | |||
// partial key does not match | |||
query = new RuleQuery().setKey("X001"); | |||
// TODO fix non-partial match for Key search | |||
assertThat(index.search(query, new QueryContext(userSessionRule)).getHits()).isEmpty(); | |||
} | |||
@Test | |||
public void search_all_rules() { | |||
dao.insert(dbSession, RuleTesting.newDto(RuleKey.of("javascript", "S001"))); | |||
dao.insert(dbSession, RuleTesting.newDto(RuleKey.of("java", "S002"))); | |||
dbSession.commit(); | |||
Result results = index.search(new RuleQuery(), new QueryContext(userSessionRule)); | |||
assertThat(results.getTotal()).isEqualTo(2); | |||
assertThat(results.getHits()).hasSize(2); | |||
} | |||
@Test | |||
public void scroll_all_rules() { | |||
int max = 100; | |||
for (int i = 0; i < max; i++) { | |||
dao.insert(dbSession, RuleTesting.newDto(RuleKey.of("java", "scroll_" + i))); | |||
} | |||
dbSession.commit(); | |||
Result results = index.search(new RuleQuery(), new QueryContext(userSessionRule).setScroll(true)); | |||
assertThat(results.getTotal()).isEqualTo(max); | |||
assertThat(results.getHits()).hasSize(0); | |||
Iterator<Rule> it = results.scroll(); | |||
int count = 0; | |||
while (it.hasNext()) { | |||
count++; | |||
it.next(); | |||
} | |||
assertThat(count).isEqualTo(max); | |||
} | |||
@Test | |||
public void search_by_any_of_repositories() { | |||
dao.insert(dbSession, RuleTesting.newDto(RuleKey.of("findbugs", "S001"))); | |||
dao.insert(dbSession, RuleTesting.newDto(RuleKey.of("pmd", "S002"))); | |||
dbSession.commit(); | |||
RuleQuery query = new RuleQuery().setRepositories(Arrays.asList("checkstyle", "pmd")); | |||
Result<Rule> results = index.search(query, new QueryContext(userSessionRule)); | |||
assertThat(results.getHits()).hasSize(1); | |||
assertThat(Iterables.getFirst(results.getHits(), null).key().rule()).isEqualTo("S002"); | |||
// no results | |||
query = new RuleQuery().setRepositories(Arrays.asList("checkstyle")); | |||
assertThat(index.search(query, new QueryContext(userSessionRule)).getHits()).isEmpty(); | |||
// empty list => no filter | |||
query = new RuleQuery().setRepositories(Collections.<String>emptyList()); | |||
assertThat(index.search(query, new QueryContext(userSessionRule)).getHits()).hasSize(2); | |||
} | |||
@Test | |||
public void search_by_any_of_languages() { | |||
dao.insert(dbSession, | |||
RuleTesting.newDto(RuleKey.of("java", "S001")).setLanguage("java"), | |||
RuleTesting.newDto(RuleKey.of("javascript", "S002")).setLanguage("js")); | |||
dbSession.commit(); | |||
RuleQuery query = new RuleQuery().setLanguages(Arrays.asList("cobol", "js")); | |||
Result<Rule> results = index.search(query, new QueryContext(userSessionRule)); | |||
assertThat(results.getHits()).hasSize(1); | |||
assertThat(Iterables.getFirst(results.getHits(), null).key().rule()).isEqualTo("S002"); | |||
// no results | |||
query = new RuleQuery().setLanguages(Arrays.asList("cpp")); | |||
assertThat(index.search(query, new QueryContext(userSessionRule)).getHits()).isEmpty(); | |||
// empty list => no filter | |||
query = new RuleQuery().setLanguages(Collections.<String>emptyList()); | |||
assertThat(index.search(query, new QueryContext(userSessionRule)).getHits()).hasSize(2); | |||
// null list => no filter | |||
query = new RuleQuery().setLanguages(null); | |||
assertThat(index.search(query, new QueryContext(userSessionRule)).getHits()).hasSize(2); | |||
} | |||
@Test | |||
public void search_by_any_of_severities() { | |||
dao.insert(dbSession, RuleTesting.newDto(RuleKey.of("java", "S001")).setSeverity(Severity.BLOCKER)); | |||
dao.insert(dbSession, RuleTesting.newDto(RuleKey.of("java", "S002")).setSeverity(Severity.INFO)); | |||
dbSession.commit(); | |||
RuleQuery query = new RuleQuery().setSeverities(Arrays.asList(Severity.INFO, Severity.MINOR)); | |||
Result<Rule> results = index.search(query, new QueryContext(userSessionRule)); | |||
assertThat(results.getHits()).hasSize(1); | |||
assertThat(Iterables.getFirst(results.getHits(), null).key().rule()).isEqualTo("S002"); | |||
// no results | |||
query = new RuleQuery().setSeverities(Arrays.asList(Severity.MINOR)); | |||
assertThat(index.search(query, new QueryContext(userSessionRule)).getHits()).isEmpty(); | |||
// empty list => no filter | |||
query = new RuleQuery().setSeverities(Collections.<String>emptyList()); | |||
assertThat(index.search(query, new QueryContext(userSessionRule)).getHits()).hasSize(2); | |||
// null list => no filter | |||
query = new RuleQuery().setSeverities(null); | |||
assertThat(index.search(query, new QueryContext(userSessionRule)).getHits()).hasSize(2); | |||
} | |||
@Test | |||
public void search_by_any_of_statuses() { | |||
dao.insert(dbSession, RuleTesting.newDto(RuleKey.of("java", "S001")).setStatus(RuleStatus.BETA)); | |||
dao.insert(dbSession, RuleTesting.newDto(RuleKey.of("java", "S002")).setStatus(RuleStatus.READY)); | |||
dbSession.commit(); | |||
RuleQuery query = new RuleQuery().setStatuses(Arrays.asList(RuleStatus.DEPRECATED, RuleStatus.READY)); | |||
Result<Rule> results = index.search(query, new QueryContext(userSessionRule)); | |||
assertThat(results.getHits()).hasSize(1); | |||
assertThat(Iterables.getFirst(results.getHits(), null).key().rule()).isEqualTo("S002"); | |||
// no results | |||
query = new RuleQuery().setStatuses(Arrays.asList(RuleStatus.DEPRECATED)); | |||
assertThat(index.search(query, new QueryContext(userSessionRule)).getHits()).isEmpty(); | |||
// empty list => no filter | |||
query = new RuleQuery().setStatuses(Collections.<RuleStatus>emptyList()); | |||
assertThat(index.search(query, new QueryContext(userSessionRule)).getHits()).hasSize(2); | |||
// null list => no filter | |||
query = new RuleQuery().setStatuses(null); | |||
assertThat(index.search(query, new QueryContext(userSessionRule)).getHits()).hasSize(2); | |||
} | |||
@Test | |||
public void sort_by_name() { | |||
dao.insert(dbSession, RuleTesting.newDto(RuleKey.of("java", "S001")).setName("abcd")); | |||
dao.insert(dbSession, RuleTesting.newDto(RuleKey.of("java", "S002")).setName("ABC")); | |||
dao.insert(dbSession, RuleTesting.newDto(RuleKey.of("java", "S003")).setName("FGH")); | |||
dbSession.commit(); | |||
// ascending | |||
RuleQuery query = new RuleQuery().setSortField(RuleNormalizer.RuleField.NAME); | |||
Result<Rule> results = index.search(query, new QueryContext(userSessionRule)); | |||
assertThat(results.getHits()).hasSize(3); | |||
assertThat(Iterables.getFirst(results.getHits(), null).key().rule()).isEqualTo("S002"); | |||
assertThat(Iterables.getLast(results.getHits(), null).key().rule()).isEqualTo("S003"); | |||
// descending | |||
query = new RuleQuery().setSortField(RuleNormalizer.RuleField.NAME).setAscendingSort(false); | |||
results = index.search(query, new QueryContext(userSessionRule)); | |||
assertThat(results.getHits()).hasSize(3); | |||
assertThat(Iterables.getFirst(results.getHits(), null).key().rule()).isEqualTo("S003"); | |||
assertThat(Iterables.getLast(results.getHits(), null).key().rule()).isEqualTo("S002"); | |||
} | |||
@Test | |||
public void fail_sort_by_language() { | |||
try { | |||
// Sorting on a field not tagged as sortable | |||
new RuleQuery().setSortField(RuleNormalizer.RuleField.LANGUAGE); | |||
fail(); | |||
} catch (IllegalStateException e) { | |||
assertThat(e).hasMessage("Field 'lang' is not sortable"); | |||
} | |||
} | |||
@Test | |||
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<Rule> result = index.search(new RuleQuery().setActivation(true), | |||
new QueryContext(userSessionRule)); | |||
assertThat(result.getHits()).hasSize(2); | |||
// 2. get all inactive rules. | |||
result = index.search(new RuleQuery().setActivation(false), | |||
new QueryContext(userSessionRule)); | |||
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 QueryContext(userSessionRule)); | |||
// TODO | |||
assertThat(result.getHits()).hasSize(1); | |||
// 4. get all active rules on profile | |||
result = index.search(new RuleQuery().setActivation(true) | |||
.setQProfileKey(qualityProfileDto2.getKey()), | |||
new QueryContext(userSessionRule)); | |||
assertThat(result.getHits()).hasSize(1); | |||
assertThat(result.getHits().get(0).name()).isEqualTo(rule1.getName()); | |||
} | |||
@Test | |||
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<Rule> result = index.search(new RuleQuery(), | |||
new QueryContext(userSessionRule)); | |||
assertThat(result.getHits()).hasSize(4); | |||
// 1. get all active rules | |||
result = index.search(new RuleQuery().setActivation(true), | |||
new QueryContext(userSessionRule)); | |||
assertThat(result.getHits()).hasSize(3); | |||
// 2. get all inactive rules. | |||
result = index.search(new RuleQuery().setActivation(false), | |||
new QueryContext(userSessionRule)); | |||
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 QueryContext(userSessionRule) | |||
); | |||
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 QueryContext(userSessionRule) | |||
); | |||
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 QueryContext(userSessionRule) | |||
); | |||
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 QueryContext(userSessionRule) | |||
); | |||
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 QueryContext(userSessionRule) | |||
); | |||
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 QueryContext(userSessionRule) | |||
); | |||
assertThat(result.getHits()).hasSize(3); | |||
} | |||
@Test | |||
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<Rule> result = index.search(new RuleQuery().setActivation(true).setQProfileKey(qualityProfileDto1.getKey()), | |||
new QueryContext(userSessionRule)); | |||
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 QueryContext(userSessionRule).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 QueryContext(userSessionRule).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)); | |||
} | |||
@Test | |||
public void complex_param_value() { | |||
String value = "//expression[primary/qualifiedIdentifier[count(IDENTIFIER) = 2]/IDENTIFIER[2]/@tokenValue = 'firstOf' and primary/identifierSuffix/arguments/expression[not(primary) or primary[not(qualifiedIdentifier) or identifierSuffix]]]"; | |||
QualityProfileDto profile = QProfileTesting.newXooP1(); | |||
db.qualityProfileDao().insert(dbSession, profile); | |||
RuleDto rule = RuleTesting.newXooX1(); | |||
dao.insert(dbSession, rule); | |||
RuleParamDto param = RuleParamDto.createFor(rule) | |||
.setName("testing") | |||
.setType("STRING") | |||
.setDefaultValue(value); | |||
dao.insertRuleParam(dbSession, rule, param); | |||
dbSession.commit(); | |||
assertThat(index.getByKey(rule.getKey()).params()).hasSize(1); | |||
assertThat(index.getByKey(rule.getKey()).params().get(0).defaultValue()).isEqualTo(value); | |||
} | |||
@Test | |||
public void search_by_tag() { | |||
dao.insert(dbSession, RuleTesting.newDto(RuleKey.of("java", "S001")).setTags(ImmutableSet.of("tag1"))); | |||
dao.insert(dbSession, RuleTesting.newDto(RuleKey.of("java", "S002")).setTags(ImmutableSet.of("tag2"))); | |||
dbSession.commit(); | |||
// find all | |||
RuleQuery query = new RuleQuery(); | |||
assertThat(index.search(query, new QueryContext(userSessionRule)).getHits()).hasSize(2); | |||
// tag1 in query | |||
query = new RuleQuery().setQueryText("tag1"); | |||
assertThat(index.search(query, new QueryContext(userSessionRule)).getHits()).hasSize(1); | |||
assertThat(Iterables.getFirst(index.search(query, new QueryContext(userSessionRule)).getHits(), null).tags()).containsExactly("tag1"); | |||
// tag1 and tag2 in query | |||
query = new RuleQuery().setQueryText("tag1 tag2"); | |||
assertThat(index.search(query, new QueryContext(userSessionRule)).getHits()).hasSize(2); | |||
// tag2 in filter | |||
query = new RuleQuery().setTags(ImmutableSet.of("tag2")); | |||
assertThat(index.search(query, new QueryContext(userSessionRule)).getHits()).hasSize(1); | |||
assertThat(Iterables.getFirst(index.search(query, new QueryContext(userSessionRule)).getHits(), null).tags()).containsExactly("tag2"); | |||
// tag2 in filter and tag1 tag2 in query | |||
query = new RuleQuery().setTags(ImmutableSet.of("tag2")).setQueryText("tag1"); | |||
assertThat(index.search(query, new QueryContext(userSessionRule)).getHits()).hasSize(0); | |||
// tag2 in filter and tag1 in query | |||
query = new RuleQuery().setTags(ImmutableSet.of("tag2")).setQueryText("tag1 tag2"); | |||
assertThat(index.search(query, new QueryContext(userSessionRule)).getHits()).hasSize(1); | |||
assertThat(Iterables.getFirst(index.search(query, new QueryContext(userSessionRule)).getHits(), null).tags()).containsExactly("tag2"); | |||
// null list => no filter | |||
query = new RuleQuery().setTags(Collections.<String>emptySet()); | |||
assertThat(index.search(query, new QueryContext(userSessionRule)).getHits()).hasSize(2); | |||
// null list => no filter | |||
query = new RuleQuery().setTags(null); | |||
assertThat(index.search(query, new QueryContext(userSessionRule)).getHits()).hasSize(2); | |||
} | |||
@Test | |||
public void search_by_is_template() { | |||
dao.insert(dbSession, RuleTesting.newDto(RuleKey.of("java", "S001")).setIsTemplate(false)); | |||
dao.insert(dbSession, RuleTesting.newDto(RuleKey.of("java", "S002")).setIsTemplate(true)); | |||
dbSession.commit(); | |||
// find all | |||
RuleQuery query = new RuleQuery(); | |||
Result<Rule> results = index.search(query, new QueryContext(userSessionRule)); | |||
assertThat(results.getHits()).hasSize(2); | |||
// Only template | |||
query = new RuleQuery().setIsTemplate(true); | |||
results = index.search(query, new QueryContext(userSessionRule)); | |||
assertThat(results.getHits()).hasSize(1); | |||
assertThat(Iterables.getFirst(results.getHits(), null).key().rule()).isEqualTo("S002"); | |||
assertThat(Iterables.getFirst(results.getHits(), null).isTemplate()).isTrue(); | |||
// Only not template | |||
query = new RuleQuery().setIsTemplate(false); | |||
results = index.search(query, new QueryContext(userSessionRule)); | |||
assertThat(results.getHits()).hasSize(1); | |||
assertThat(Iterables.getFirst(results.getHits(), null).isTemplate()).isFalse(); | |||
assertThat(Iterables.getFirst(results.getHits(), null).key().rule()).isEqualTo("S001"); | |||
// null => no filter | |||
query = new RuleQuery().setIsTemplate(null); | |||
assertThat(index.search(query, new QueryContext(userSessionRule)).getHits()).hasSize(2); | |||
} | |||
@Test | |||
public void search_by_template_key() { | |||
RuleDto templateRule = RuleTesting.newDto(RuleKey.of("java", "S001")).setIsTemplate(true); | |||
dao.insert(dbSession, templateRule); | |||
dao.insert(dbSession, RuleTesting.newDto(RuleKey.of("java", "S001_MY_CUSTOM")).setTemplateId(templateRule.getId())); | |||
dbSession.commit(); | |||
// find all | |||
RuleQuery query = new RuleQuery(); | |||
Result<Rule> results = index.search(query, new QueryContext(userSessionRule)); | |||
assertThat(results.getHits()).hasSize(2); | |||
// Only custom rule | |||
query = new RuleQuery().setTemplateKey("java:S001"); | |||
results = index.search(query, new QueryContext(userSessionRule)); | |||
assertThat(results.getHits()).hasSize(1); | |||
assertThat(Iterables.getFirst(results.getHits(), null).key().rule()).isEqualTo("S001_MY_CUSTOM"); | |||
assertThat(Iterables.getFirst(results.getHits(), null).templateKey()).isEqualTo(RuleKey.of("java", "S001")); | |||
// null => no filter | |||
query = new RuleQuery().setTemplateKey(null); | |||
assertThat(index.search(query, new QueryContext(userSessionRule)).getHits()).hasSize(2); | |||
} | |||
@Test | |||
public void search_by_template_key_with_params() { | |||
RuleDto templateRule = RuleTesting.newDto(RuleKey.of("java", "S001")).setIsTemplate(true); | |||
RuleParamDto ruleParamDto = RuleParamDto.createFor(templateRule).setName("regex").setType("STRING").setDescription("Reg ex").setDefaultValue(".*"); | |||
dao.insert(dbSession, templateRule); | |||
dao.insertRuleParam(dbSession, templateRule, ruleParamDto); | |||
RuleDto customRule = RuleTesting.newDto(RuleKey.of("java", "S001_MY_CUSTOM")).setTemplateId(templateRule.getId()); | |||
RuleParamDto customRuleParam = RuleParamDto.createFor(customRule).setName("regex").setType("STRING").setDescription("Reg ex").setDefaultValue("a.*"); | |||
dao.insert(dbSession, customRule); | |||
dao.insertRuleParam(dbSession, customRule, customRuleParam); | |||
dbSession.commit(); | |||
// find all | |||
RuleQuery query = new RuleQuery(); | |||
Result<Rule> results = index.search(query, new QueryContext(userSessionRule)); | |||
assertThat(results.getHits()).hasSize(2); | |||
// get params | |||
assertThat(index.getByKey(templateRule.getKey()).params()).hasSize(1); | |||
assertThat(index.getByKey(customRule.getKey()).params()).hasSize(1); | |||
} | |||
@Test | |||
public void show_custom_rule() { | |||
RuleDto templateRule = RuleTesting.newDto(RuleKey.of("java", "S001")).setIsTemplate(true); | |||
dao.insert(dbSession, templateRule); | |||
dao.insert(dbSession, RuleTesting.newDto(RuleKey.of("java", "S001_MY_CUSTOM")).setTemplateId(templateRule.getId())); | |||
dbSession.commit(); | |||
// find all | |||
RuleQuery query = new RuleQuery(); | |||
Result<Rule> results = index.search(query, new QueryContext(userSessionRule)); | |||
assertThat(results.getHits()).hasSize(2); | |||
// find custom rule | |||
assertThat(index.getByKey(RuleKey.of("java", "S001_MY_CUSTOM")).templateKey()).isEqualTo(RuleKey.of("java", "S001")); | |||
} | |||
@Test | |||
public void paging() { | |||
dao.insert(dbSession, RuleTesting.newDto(RuleKey.of("java", "S001"))); | |||
dao.insert(dbSession, RuleTesting.newDto(RuleKey.of("java", "S002"))); | |||
dao.insert(dbSession, RuleTesting.newDto(RuleKey.of("java", "S003"))); | |||
dbSession.commit(); | |||
// from 0 to 1 included | |||
QueryContext options = new QueryContext(userSessionRule); | |||
options.setOffset(0).setLimit(2); | |||
Result results = index.search(new RuleQuery(), options); | |||
assertThat(results.getTotal()).isEqualTo(3); | |||
assertThat(results.getHits()).hasSize(2); | |||
// from 0 to 9 included | |||
options.setOffset(0).setLimit(10); | |||
results = index.search(new RuleQuery(), options); | |||
assertThat(results.getTotal()).isEqualTo(3); | |||
assertThat(results.getHits()).hasSize(3); | |||
// from 2 to 11 included | |||
options.setOffset(2).setLimit(10); | |||
results = index.search(new RuleQuery(), options); | |||
assertThat(results.getTotal()).isEqualTo(3); | |||
assertThat(results.getHits()).hasSize(1); | |||
// from 2 to 11 included | |||
options.setOffset(2).setLimit(0); | |||
results = index.search(new RuleQuery(), options); | |||
assertThat(results.getTotal()).isEqualTo(3); | |||
assertThat(results.getHits()).hasSize(0); | |||
} | |||
@Test | |||
public void available_since() throws InterruptedException { | |||
dao.insert(dbSession, RuleTesting.newDto(RuleKey.of("java", "S001"))); | |||
dbSession.commit(); | |||
Date since = new Date(); | |||
dao.insert(dbSession, RuleTesting.newDto(RuleKey.of("java", "S002"))); | |||
dbSession.commit(); | |||
// 0. find all rules; | |||
assertThat(index.search(new RuleQuery(), new QueryContext(userSessionRule)).getHits()).hasSize(2); | |||
// 1. find all rules available since a date; | |||
RuleQuery availableSinceQuery = new RuleQuery() | |||
.setAvailableSince(since); | |||
List<Rule> hits = index.search(availableSinceQuery, new QueryContext(userSessionRule)).getHits(); | |||
assertThat(hits).hasSize(1); | |||
assertThat(hits.get(0).key()).isEqualTo(RuleKey.of("java", "S002")); | |||
// 2. find no new rules since tomorrow. | |||
RuleQuery availableSinceNowQuery = new RuleQuery() | |||
.setAvailableSince(DateUtils.addDays(since, 1)); | |||
assertThat(index.search(availableSinceNowQuery, new QueryContext(userSessionRule)).getHits()).hasSize(0); | |||
} | |||
@Test | |||
public void scroll_byIds() { | |||
Set<Integer> ids = new HashSet<>(); | |||
for (int i = 0; i < 150; i++) { | |||
RuleDto rule = RuleTesting.newDto(RuleKey.of("scroll", "r_" + i)); | |||
dao.insert(dbSession, rule); | |||
dbSession.commit(); | |||
ids.add(rule.getId()); | |||
} | |||
List<Rule> rules = index.getByIds(ids); | |||
assertThat(rules).hasSize(ids.size()); | |||
} | |||
@Test | |||
public void search_protected_chars() { | |||
String nameWithProtectedChars = "ja#va&sc\"r:ipt"; | |||
RuleDto ruleDto = RuleTesting.newXooX1().setName(nameWithProtectedChars); | |||
dao.insert(dbSession, ruleDto); | |||
dbSession.commit(); | |||
Rule rule = index.getByKey(RuleTesting.XOO_X1); | |||
assertThat(rule.name()).isEqualTo(nameWithProtectedChars); | |||
RuleQuery protectedCharsQuery = new RuleQuery().setQueryText(nameWithProtectedChars); | |||
List<Rule> results = index.search(protectedCharsQuery, new QueryContext(userSessionRule)).getHits(); | |||
assertThat(results).hasSize(1); | |||
assertThat(results.get(0).key()).isEqualTo(RuleTesting.XOO_X1); | |||
} | |||
@Test | |||
public void sticky_facets() { | |||
dao.insert(dbSession, | |||
RuleTesting.newDto(RuleKey.of("xoo", "S001")).setLanguage("java").setTags(ImmutableSet.<String>of()), | |||
RuleTesting.newDto(RuleKey.of("xoo", "S002")).setLanguage("java").setTags(ImmutableSet.<String>of()), | |||
RuleTesting.newDto(RuleKey.of("xoo", "S003")).setLanguage("java").setTags(ImmutableSet.<String>of("T1", "T2")), | |||
RuleTesting.newDto(RuleKey.of("xoo", "S011")).setLanguage("cobol").setTags(ImmutableSet.<String>of()), | |||
RuleTesting.newDto(RuleKey.of("xoo", "S012")).setLanguage("cobol").setTags(ImmutableSet.<String>of()), | |||
RuleTesting.newDto(RuleKey.of("foo", "S013")).setLanguage("cobol").setTags(ImmutableSet.<String>of("T3", "T4")), | |||
RuleTesting.newDto(RuleKey.of("foo", "S111")).setLanguage("cpp").setTags(ImmutableSet.<String>of()), | |||
RuleTesting.newDto(RuleKey.of("foo", "S112")).setLanguage("cpp").setTags(ImmutableSet.<String>of()), | |||
RuleTesting.newDto(RuleKey.of("foo", "S113")).setLanguage("cpp").setTags(ImmutableSet.<String>of("T2", "T3"))); | |||
dbSession.commit(); | |||
// 0 assert Base | |||
assertThat(index.countAll()).isEqualTo(9); | |||
assertThat(index.search(new RuleQuery(), new QueryContext(userSessionRule)).getHits()).hasSize(9); | |||
// 1 Facet with no filters at all | |||
Map<String, Collection<FacetValue>> facets = index.search(new RuleQuery(), new QueryContext(userSessionRule).addFacets(Arrays.asList("languages", "repositories", "tags"))) | |||
.getFacets(); | |||
assertThat(facets.keySet()).hasSize(3); | |||
assertThat(facets.get(RuleIndex.FACET_LANGUAGES)).extracting("key").containsOnly("cpp", "java", "cobol"); | |||
assertThat(facets.get(RuleIndex.FACET_REPOSITORIES)).extracting("key").containsOnly("xoo", "foo"); | |||
assertThat(facets.get(RuleIndex.FACET_TAGS)).extracting("key").containsOnly("systag1", "systag2", "T1", "T2", "T3", "T4"); | |||
// 2 Facet with a language filter | |||
// -- lang facet should still have all language | |||
Result<Rule> result = index.search(new RuleQuery() | |||
.setLanguages(ImmutableList.<String>of("cpp")) | |||
, new QueryContext(userSessionRule).addFacets(Arrays.asList("languages", "repositories", "tags"))); | |||
assertThat(result.getHits()).hasSize(3); | |||
assertThat(result.getFacets()).hasSize(3); | |||
assertThat(result.getFacets().get(RuleIndex.FACET_LANGUAGES)).extracting("key").containsOnly("cpp", "java", "cobol"); | |||
// 3 facet with 2 filters | |||
// -- lang facet for tag T2 | |||
// -- tag facet for lang cpp | |||
// -- repository for cpp & T2 | |||
result = index.search(new RuleQuery() | |||
.setLanguages(ImmutableList.<String>of("cpp")) | |||
.setTags(ImmutableList.<String>of("T2")) | |||
, new QueryContext(userSessionRule).addFacets(Arrays.asList("languages", "repositories", "tags"))); | |||
assertThat(result.getHits()).hasSize(1); | |||
assertThat(result.getFacets().keySet()).hasSize(3); | |||
assertThat(result.getFacets().get(RuleIndex.FACET_LANGUAGES)).extracting("key").containsOnly("cpp", "java"); | |||
assertThat(result.getFacets().get(RuleIndex.FACET_REPOSITORIES)).extracting("key").containsOnly("foo"); | |||
assertThat(result.getFacets().get(RuleIndex.FACET_TAGS)).extracting("key").containsOnly("systag1", "systag2", "T2", "T3"); | |||
// 4 facet with 2 filters | |||
// -- lang facet for tag T2 | |||
// -- tag facet for lang cpp & java | |||
// -- repository for (cpp || java) & T2 | |||
result = index.search(new RuleQuery() | |||
.setLanguages(ImmutableList.<String>of("cpp", "java")) | |||
.setTags(ImmutableList.<String>of("T2")) | |||
, new QueryContext(userSessionRule).addFacets(Arrays.asList("languages", "repositories", "tags"))); | |||
assertThat(result.getHits()).hasSize(2); | |||
assertThat(result.getFacets().keySet()).hasSize(3); | |||
assertThat(result.getFacets().get(RuleIndex.FACET_LANGUAGES)).extracting("key").containsOnly("cpp", "java"); | |||
assertThat(result.getFacets().get(RuleIndex.FACET_REPOSITORIES)).extracting("key").containsOnly("foo", "xoo"); | |||
assertThat(result.getFacets().get(RuleIndex.FACET_TAGS)).extracting("key").containsOnly("systag1", "systag2", "T1", "T2", "T3"); | |||
} | |||
private static List<String> ruleKeys(List<Rule> rules) { | |||
return newArrayList(Iterables.transform(rules, new Function<Rule, String>() { | |||
@Override | |||
public String apply(@Nullable Rule input) { | |||
return input != null ? input.key().rule() : null; | |||
} | |||
})); | |||
} | |||
} |
@@ -0,0 +1,109 @@ | |||
/* | |||
* 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.rule.index; | |||
import com.google.common.collect.Iterators; | |||
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.RuleStatus; | |||
import org.sonar.api.utils.System2; | |||
import org.sonar.db.DbClient; | |||
import org.sonar.db.DbTester; | |||
import org.sonar.db.rule.RuleDto; | |||
import org.sonar.db.rule.RuleTesting; | |||
import org.sonar.server.es.EsTester; | |||
import org.sonar.test.DbTests; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
@Category(DbTests.class) | |||
public class RuleIndexerTest { | |||
@ClassRule | |||
public static EsTester esTester = new EsTester().addDefinitions(new RuleIndexDefinition(new Settings())); | |||
@Rule | |||
public DbTester dbTester = DbTester.create(System2.INSTANCE); | |||
@Before | |||
public void setUp() { | |||
esTester.truncateIndices(); | |||
} | |||
@Test | |||
public void index_nothing() { | |||
RuleIndexer indexer = createIndexer(); | |||
indexer.index(Iterators.<RuleDoc>emptyIterator()); | |||
assertThat(esTester.countDocuments(RuleIndexDefinition.INDEX, RuleIndexDefinition.TYPE_RULE)).isEqualTo(0L); | |||
} | |||
@Test | |||
public void index_nothing_if_disabled() { | |||
dbTester.prepareDbUnit(getClass(), "index.xml"); | |||
createIndexer().setEnabled(false).index(); | |||
assertThat(esTester.countDocuments(RuleIndexDefinition.INDEX, RuleIndexDefinition.TYPE_RULE)).isZero(); | |||
} | |||
@Test | |||
public void index() { | |||
dbTester.prepareDbUnit(getClass(), "index.xml"); | |||
RuleIndexer indexer = createIndexer(); | |||
indexer.index(); | |||
assertThat(esTester.countDocuments(RuleIndexDefinition.INDEX, RuleIndexDefinition.TYPE_RULE)).isEqualTo(1); | |||
} | |||
@Test | |||
public void removed_rule_is_removed_from_index() { | |||
RuleIndexer indexer = createIndexer(); | |||
// Create and Index rule | |||
RuleDto ruleDto = RuleTesting.newDto(RuleKey.of("xoo", "S001")) | |||
.setStatus(RuleStatus.READY) | |||
.setUpdatedAtInMs(1000L); | |||
dbTester.getDbClient().ruleDao().insert(dbTester.getSession(), ruleDto); | |||
dbTester.getSession().commit(); | |||
indexer.index(); | |||
// Remove rule | |||
ruleDto.setStatus(RuleStatus.REMOVED); | |||
ruleDto.setUpdatedAtInMs(2000L); | |||
dbTester.getDbClient().ruleDao().update(dbTester.getSession(), ruleDto); | |||
dbTester.getSession().commit(); | |||
indexer.index(); | |||
assertThat(esTester.countDocuments(RuleIndexDefinition.INDEX, RuleIndexDefinition.TYPE_RULE)).isZero(); | |||
} | |||
private RuleIndexer createIndexer() { | |||
RuleIndexer indexer = new RuleIndexer(new DbClient(dbTester.database(), dbTester.myBatis()), esTester.client()); | |||
indexer.setEnabled(true); | |||
return indexer; | |||
} | |||
} |
@@ -0,0 +1,169 @@ | |||
/* | |||
* 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.rule.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.rule.RuleStatus; | |||
import org.sonar.api.rule.Severity; | |||
import org.sonar.api.utils.System2; | |||
import org.sonar.db.DbTester; | |||
import org.sonar.test.DbTests; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
@Category(DbTests.class) | |||
public class RuleResultSetIteratorTest { | |||
@Rule | |||
public DbTester dbTester = DbTester.create(System2.INSTANCE); | |||
@Before | |||
public void setUp() { | |||
dbTester.truncateTables(); | |||
} | |||
@Test | |||
public void iterator_over_one_rule() { | |||
dbTester.prepareDbUnit(getClass(), "one_rule.xml"); | |||
RuleResultSetIterator it = RuleResultSetIterator.create(dbTester.getDbClient(), dbTester.getSession(), 0L); | |||
Map<String, RuleDoc> rulesByKey = rulesByKey(it); | |||
it.close(); | |||
assertThat(rulesByKey).hasSize(1); | |||
RuleDoc rule = rulesByKey.get("S001"); | |||
assertThat(rule.key()).isEqualTo(RuleKey.of("xoo", "S001")); | |||
assertThat(rule.keyAsList()).containsOnly("xoo", "S001"); | |||
assertThat(rule.ruleKey()).isEqualTo("S001"); | |||
assertThat(rule.repository()).isEqualTo("xoo"); | |||
assertThat(rule.internalKey()).isEqualTo("S1"); | |||
assertThat(rule.name()).isEqualTo("Null Pointer"); | |||
assertThat(rule.htmlDescription()).isEqualTo("S001 desc"); | |||
assertThat(rule.language()).isEqualTo("xoo"); | |||
assertThat(rule.severity()).isEqualTo(Severity.BLOCKER); | |||
assertThat(rule.status()).isEqualTo(RuleStatus.READY); | |||
assertThat(rule.isTemplate()).isFalse(); | |||
assertThat(rule.allTags()).containsOnly("bug", "performance", "cwe"); | |||
assertThat(rule.createdAtAsLong()).isEqualTo(1500000000000L); | |||
assertThat(rule.updatedAtAtAsLong()).isEqualTo(1600000000000L); | |||
} | |||
@Test | |||
public void select_after_date() { | |||
dbTester.prepareDbUnit(getClass(), "shared.xml"); | |||
RuleResultSetIterator it = RuleResultSetIterator.create(dbTester.getDbClient(), dbTester.getSession(), 1_900_000_000_000L); | |||
assertThat(it.hasNext()).isTrue(); | |||
RuleDoc issue = it.next(); | |||
assertThat(issue.key()).isEqualTo(RuleKey.of("xoo", "S002")); | |||
assertThat(it.hasNext()).isFalse(); | |||
it.close(); | |||
} | |||
@Test | |||
public void iterator_over_rules() { | |||
dbTester.prepareDbUnit(getClass(), "shared.xml"); | |||
RuleResultSetIterator it = RuleResultSetIterator.create(dbTester.getDbClient(), dbTester.getSession(), 0L); | |||
Map<String, RuleDoc> rulesByKey = rulesByKey(it); | |||
it.close(); | |||
assertThat(rulesByKey).hasSize(2); | |||
RuleDoc rule = rulesByKey.get("S001"); | |||
assertThat(rule.key()).isEqualTo(RuleKey.of("xoo", "S001")); | |||
assertThat(rule.keyAsList()).containsOnly("xoo", "S001"); | |||
assertThat(rule.ruleKey()).isEqualTo("S001"); | |||
assertThat(rule.repository()).isEqualTo("xoo"); | |||
assertThat(rule.internalKey()).isEqualTo("S1"); | |||
assertThat(rule.name()).isEqualTo("Null Pointer"); | |||
assertThat(rule.htmlDescription()).isEqualTo("S001 desc"); | |||
assertThat(rule.language()).isEqualTo("xoo"); | |||
assertThat(rule.severity()).isEqualTo(Severity.BLOCKER); | |||
assertThat(rule.status()).isEqualTo(RuleStatus.READY); | |||
assertThat(rule.isTemplate()).isFalse(); | |||
assertThat(rule.allTags()).containsOnly("bug", "performance", "cwe"); | |||
assertThat(rule.createdAtAsLong()).isEqualTo(1500000000000L); | |||
assertThat(rule.updatedAtAtAsLong()).isEqualTo(1600000000000L); | |||
rule = rulesByKey.get("S002"); | |||
assertThat(rule.key()).isEqualTo(RuleKey.of("xoo", "S002")); | |||
assertThat(rule.keyAsList()).containsOnly("xoo", "S002"); | |||
assertThat(rule.ruleKey()).isEqualTo("S002"); | |||
assertThat(rule.repository()).isEqualTo("xoo"); | |||
assertThat(rule.internalKey()).isEqualTo("S2"); | |||
assertThat(rule.name()).isEqualTo("Slow"); | |||
assertThat(rule.htmlDescription()).isEqualTo("<strong>S002 desc</strong>"); | |||
assertThat(rule.language()).isEqualTo("xoo"); | |||
assertThat(rule.severity()).isEqualTo(Severity.CRITICAL); | |||
assertThat(rule.status()).isEqualTo(RuleStatus.BETA); | |||
assertThat(rule.isTemplate()).isTrue(); | |||
assertThat(rule.allTags()).isEmpty(); | |||
assertThat(rule.createdAtAsLong()).isEqualTo(2000000000000L); | |||
assertThat(rule.updatedAtAtAsLong()).isEqualTo(2100000000000L); | |||
} | |||
@Test | |||
public void custom_rule() { | |||
dbTester.prepareDbUnit(getClass(), "custom_rule.xml"); | |||
RuleResultSetIterator it = RuleResultSetIterator.create(dbTester.getDbClient(), dbTester.getSession(), 0L); | |||
Map<String, RuleDoc> rulesByKey = rulesByKey(it); | |||
it.close(); | |||
assertThat(rulesByKey).hasSize(2); | |||
RuleDoc rule = rulesByKey.get("S001"); | |||
assertThat(rule.isTemplate()).isTrue(); | |||
assertThat(rule.templateKey()).isNull(); | |||
rule = rulesByKey.get("S002"); | |||
assertThat(rule.isTemplate()).isFalse(); | |||
assertThat(rule.templateKey()).isEqualTo(RuleKey.of("xoo", "S001")); | |||
} | |||
@Test | |||
public void removed_rule_is_returned() { | |||
dbTester.prepareDbUnit(getClass(), "removed_rule.xml"); | |||
RuleResultSetIterator it = RuleResultSetIterator.create(dbTester.getDbClient(), dbTester.getSession(), 0L); | |||
Map<String, RuleDoc> rulesByKey = rulesByKey(it); | |||
it.close(); | |||
assertThat(rulesByKey).hasSize(1); | |||
} | |||
private static Map<String, RuleDoc> rulesByKey(RuleResultSetIterator it) { | |||
return Maps.uniqueIndex(it, new Function<RuleDoc, String>() { | |||
@Override | |||
public String apply(@Nonnull RuleDoc rule) { | |||
return rule.key().rule(); | |||
} | |||
}); | |||
} | |||
} |
@@ -9,7 +9,7 @@ | |||
remediation_offset="5min" default_remediation_offset="10h" | |||
effort_to_fix_description="squid.S115.effortToFix" description_format="MARKDOWN" | |||
created_at="2013-12-16" updated_at="2013-12-16" | |||
created_at_ms="[null]" updated_at_ms="[null]" | |||
created_at_ms="0" updated_at_ms="0" | |||
/> | |||
</dataset> |
@@ -8,7 +8,7 @@ | |||
remediation_coeff="1h" default_remediation_coeff="5d" | |||
remediation_offset="5min" default_remediation_offset="10h" | |||
effort_to_fix_description="squid.S115.effortToFix" description_format="HTML" | |||
created_at_ms="[null]" updated_at_ms="[null]" | |||
created_at_ms="0" updated_at_ms="0" | |||
/> | |||
<rules tags="[null]" system_tags="[null]" id="2" plugin_rule_key="NewRuleKey2" plugin_name="plugin2" name="new name2" description="new description2" status="BETA" | |||
@@ -19,7 +19,7 @@ | |||
remediation_coeff="5d" default_remediation_coeff="1h" | |||
remediation_offset="10h" default_remediation_offset="5min" | |||
effort_to_fix_description="squid.S115.effortToFix2" description_format="MARKDOWN" | |||
created_at_ms="[null]" updated_at_ms="[null]" | |||
created_at_ms="0" updated_at_ms="0" | |||
/> | |||
</dataset> |
@@ -10,7 +10,7 @@ | |||
remediation_coeff="1h" default_remediation_coeff="5d" | |||
remediation_offset="5min" default_remediation_offset="10h" | |||
effort_to_fix_description="squid.S115.effortToFix" description_format="MARKDOWN" | |||
created_at_ms="1500000000000" updated_at_ms="1600000000000" | |||
created_at_ms="1500000000000" updated_at_ms="0" | |||
/> | |||
<rules tags="[null]" system_tags="[null]" id="2" plugin_rule_key="Parent1" plugin_name="checkstyle" |
@@ -0,0 +1,13 @@ | |||
<dataset> | |||
<rules id="1" 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" | |||
/> | |||
</dataset> |
@@ -0,0 +1,13 @@ | |||
<dataset> | |||
<rules id="1" 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="REMOVED" | |||
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" | |||
/> | |||
</dataset> |
@@ -0,0 +1,25 @@ | |||
<dataset> | |||
<!-- Template rule --> | |||
<rules id="1" 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="[true]" 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" | |||
/> | |||
<!-- Custom rule --> | |||
<rules id="2" 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="[false]" template_id="1" | |||
tags="[null]" system_tags="[null]" | |||
created_at="2014-05-10" updated_at="2014-05-11" | |||
created_at_ms="2000000000000" updated_at_ms="2100000000000" | |||
/> | |||
</dataset> |
@@ -0,0 +1,13 @@ | |||
<dataset> | |||
<rules id="1" 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" | |||
/> | |||
</dataset> |
@@ -0,0 +1,13 @@ | |||
<dataset> | |||
<rules id="1" 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="REMOVED" | |||
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" | |||
/> | |||
</dataset> |
@@ -0,0 +1,22 @@ | |||
<dataset> | |||
<rules id="1" 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="2" 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" | |||
/> | |||
</dataset> |
@@ -8,7 +8,7 @@ | |||
created_at_ms="[null]" updated_at_ms="[null]" | |||
/> | |||
<!-- re-entrant migration - ignore the issues that are already fed with new dates --> | |||
<!-- re-entrant migration - ignore the rules that are already fed with new dates --> | |||
<rules id="2" name="Slow" plugin_rule_key="S002" | |||
plugin_config_key="S2" plugin_name="java" description="[null]" priority="4" status="BETA" | |||
is_template="[false]" template_id="[null]" |