import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.RuleStatus;
import org.sonar.api.rules.RuleType;
+import org.sonar.db.issue.ImpactDto;
public class RuleForIndexingDto {
private long updatedAt;
private Set<RuleDescriptionSectionDto> ruleDescriptionSectionsDtos = new HashSet<>();
+ private String cleanCodeAttributeCategory;
+ private Set<ImpactDto> impacts = new HashSet<>();
+
@VisibleForTesting
public RuleForIndexingDto() {
// nothing to do here
if (r.getRuleDescriptionSectionDtos() != null) {
ruleForIndexingDto.setRuleDescriptionSectionsDtos(Sets.newHashSet(r.getRuleDescriptionSectionDtos()));
}
+
+ if (r.getCleanCodeAttribute() != null) {
+ ruleForIndexingDto.cleanCodeAttributeCategory = r.getCleanCodeAttribute().getAttributeCategory().name();
+ }
+ ruleForIndexingDto.setImpacts(r.getDefaultImpacts());
+
return ruleForIndexingDto;
}
public void setType(int type) {
this.type = type;
}
+
+ public String getCleanCodeAttributeCategory() {
+ return cleanCodeAttributeCategory;
+ }
+
+ public void setCleanCodeAttributeCategory(String cleanCodeAttributeCategory) {
+ this.cleanCodeAttributeCategory = cleanCodeAttributeCategory;
+ }
+
+ public Set<ImpactDto> getImpacts() {
+ return impacts;
+ }
+
+ public void setImpacts(Set<ImpactDto> impacts) {
+ this.impacts = impacts;
+ }
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.db.rule;
+
+import java.util.List;
+import org.junit.Test;
+import org.sonar.api.issue.impact.Severity;
+import org.sonar.api.issue.impact.SoftwareQuality;
+import org.sonar.api.rules.CleanCodeAttribute;
+import org.sonar.api.rules.CleanCodeAttributeCategory;
+import org.sonar.db.issue.ImpactDto;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class RuleForIndexingDtoTest {
+
+ @Test
+ public void fromRuleDto_whenCleanCodeAttributeSet_setCleanCodeCategory() {
+ RuleDto ruleDto = RuleTesting.newRuleWithoutDescriptionSection();
+ ruleDto.setCleanCodeAttribute(CleanCodeAttribute.FOCUSED);
+ ImpactDto impactDto = new ImpactDto().setSeverity(Severity.HIGH).setSoftwareQuality(SoftwareQuality.SECURITY);
+ ruleDto.replaceAllDefaultImpacts(List.of(impactDto));
+
+ RuleForIndexingDto ruleForIndexingDto = RuleForIndexingDto.fromRuleDto(ruleDto);
+
+ assertThat(ruleForIndexingDto.getCleanCodeAttributeCategory()).isEqualTo(CleanCodeAttributeCategory.ADAPTABLE.name());
+ ImpactDto impact = ruleForIndexingDto.getImpacts().iterator().next();
+
+ assertThat(impact.getSeverity()).isEqualTo(Severity.HIGH);
+ assertThat(impact.getSoftwareQuality()).isEqualTo(SoftwareQuality.SECURITY);
+ }
+
+}
\ No newline at end of file
*/
package org.sonar.db.rule;
+import java.util.Collection;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
return rule -> rule.setTags(copyOf(tags));
}
+ public static Consumer<RuleDto> setCleanCodeAttribute(CleanCodeAttribute cleanCodeAttribute) {
+ return rule -> rule.setCleanCodeAttribute(cleanCodeAttribute);
+ }
+
+ public static Consumer<RuleDto> setImpacts(Collection<ImpactDto> impacts) {
+ return rule -> rule.replaceAllDefaultImpacts(impacts);
+ }
+
}
*/
package org.sonar.server.rule.index;
-import com.google.common.collect.ImmutableMap;
import java.io.IOException;
import java.util.List;
+import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.apache.lucene.search.TotalHits;
import org.elasticsearch.client.RequestOptions;
"quick", "brown", "fox", "jump", "over", "lazi", "dog");
// the following method fails if PUT fails
- tester.putDocuments(TYPE_RULE, new RuleDoc(ImmutableMap.of(
+ tester.putDocuments(TYPE_RULE, new RuleDoc(Map.of(
FIELD_RULE_UUID, "123",
FIELD_RULE_HTML_DESCRIPTION, longText,
FIELD_RULE_REPOSITORY, "java",
import org.junit.Rule;
import org.junit.Test;
import org.sonar.api.impl.utils.AlwaysIncreasingSystem2;
+import org.sonar.api.issue.impact.Severity;
+import org.sonar.api.issue.impact.SoftwareQuality;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.RuleStatus;
+import org.sonar.api.rules.CleanCodeAttribute;
import org.sonar.api.utils.System2;
import org.sonar.core.util.UuidFactory;
import org.sonar.core.util.UuidFactoryFast;
import org.sonar.db.DbTester;
+import org.sonar.db.issue.ImpactDto;
import org.sonar.db.qualityprofile.QProfileDto;
import org.sonar.db.rule.RuleDto;
import org.sonar.server.es.EsTester;
import static org.sonar.api.rules.RuleType.VULNERABILITY;
import static org.sonar.db.rule.RuleDescriptionSectionDto.createDefaultRuleDescriptionSection;
import static org.sonar.db.rule.RuleTesting.newRule;
+import static org.sonar.db.rule.RuleTesting.setCleanCodeAttribute;
import static org.sonar.db.rule.RuleTesting.setCreatedAt;
+import static org.sonar.db.rule.RuleTesting.setImpacts;
import static org.sonar.db.rule.RuleTesting.setIsExternal;
import static org.sonar.db.rule.RuleTesting.setIsTemplate;
import static org.sonar.db.rule.RuleTesting.setLanguage;
verifyEmptySearch(availableSinceNowQuery);
}
+ @Test
+ public void search_by_clean_code_attribute() {
+ RuleDto ruleDto = createRule(setRepositoryKey("php"), setCleanCodeAttribute(CleanCodeAttribute.FOCUSED));
+ index();
+
+ RuleQuery query = new RuleQuery();
+ query.setCleanCodeAttributesCategory(CleanCodeAttribute.LOGICAL.getAttributeCategory().name());
+ SearchIdResult result1 = underTest.search(query, new SearchOptions());
+ assertThat(result1.getUuids()).isEmpty();
+
+
+ query = new RuleQuery();
+ query.setCleanCodeAttributesCategory(CleanCodeAttribute.FOCUSED.getAttributeCategory().name());
+
+ SearchIdResult result2 = underTest.search(query, new SearchOptions());
+
+ assertThat(result2.getUuids()).containsOnly(ruleDto.getUuid());
+ }
+
+ @Test
+ public void search_by_software_quality() {
+ ImpactDto impactDto = new ImpactDto().setSoftwareQuality(SoftwareQuality.SECURITY).setSeverity(Severity.HIGH).setUuid("uuid");
+ RuleDto phpRule = createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto)));
+ index();
+
+ RuleQuery query = new RuleQuery();
+ SearchIdResult result1 = underTest.search(query.setImpactSoftwareQualities(List.of(SoftwareQuality.MAINTAINABILITY.name())), new SearchOptions());
+ assertThat(result1.getUuids()).isEmpty();
+
+
+ query = new RuleQuery();
+ SearchIdResult result2 = underTest.search(query.setImpactSoftwareQualities(List.of(SoftwareQuality.SECURITY.name())), new SearchOptions());
+ assertThat(result2.getUuids()).containsOnly(phpRule.getUuid());
+ }
+
+ @Test
+ public void search_by_severity() {
+ ImpactDto impactDto = new ImpactDto().setSoftwareQuality(SoftwareQuality.SECURITY).setSeverity(Severity.HIGH).setUuid("uuid");
+ RuleDto phpRule = createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto)));
+ index();
+
+ RuleQuery query = new RuleQuery();
+ SearchIdResult result1 = underTest.search(query.setImpactSeverities(List.of(Severity.MEDIUM.name())), new SearchOptions());
+ assertThat(result1.getUuids()).isEmpty();
+
+
+ query = new RuleQuery();
+ SearchIdResult result2 = underTest.search(query.setImpactSeverities(List.of(Severity.HIGH.name())), new SearchOptions());
+ assertThat(result2.getUuids()).containsOnly(phpRule.getUuid());
+ }
+
+ @Test
+ public void search_should_support_clean_code_attribute_category_facet() {
+ createRule(setRepositoryKey("php"), setCleanCodeAttribute(CleanCodeAttribute.FOCUSED));
+ createRule(setRepositoryKey("php"), setCleanCodeAttribute(CleanCodeAttribute.LOGICAL));
+ index();
+
+ RuleQuery query = new RuleQuery();
+
+ SearchIdResult result2 = underTest.search(query, new SearchOptions().addFacets(singletonList("cleanCodeAttributeCategories")));
+
+ assertThat(result2.getFacets().getAll()).hasSize(1);
+ assertThat(result2.getFacets().getAll().get("cleanCodeAttributeCategories")).containsOnly(entry("ADAPTABLE", 1L), entry("INTENTIONAL", 1L));
+ }
+
+ @Test
+ public void search_should_support_software_quality_facet() {
+ ImpactDto impactDto = new ImpactDto().setSoftwareQuality(SoftwareQuality.SECURITY).setSeverity(Severity.HIGH).setUuid("uuid");
+ ImpactDto impactDto2 = new ImpactDto().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(Severity.LOW).setUuid("uuid2");
+ createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto)));
+ createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto2)));
+ index();
+
+ RuleQuery query = new RuleQuery();
+
+ SearchIdResult result2 = underTest.search(query, new SearchOptions().addFacets(singletonList("impacts.softwareQuality")));
+
+ assertThat(result2.getFacets().getAll()).hasSize(1);
+ assertThat(result2.getFacets().getAll().get("impacts.softwareQuality"))
+ .containsOnly(
+ entry("SECURITY", 1L),
+ entry("MAINTAINABILITY", 1L),
+ entry("RELIABILITY", 0L));
+ }
+
+ @Test
+ public void search_should_support_software_quality_facet_with_filtering() {
+ ImpactDto impactDto = new ImpactDto().setSoftwareQuality(SoftwareQuality.SECURITY).setSeverity(Severity.HIGH).setUuid("uuid");
+ ImpactDto impactDto2 = new ImpactDto().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(Severity.LOW).setUuid("uuid2");
+ createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto)));
+ createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto2)));
+ index();
+
+ RuleQuery query = new RuleQuery();
+
+ SearchIdResult result2 = underTest.search(query.setImpactSeverities(Set.of(Severity.HIGH.name())), new SearchOptions().addFacets(singletonList("impacts.softwareQuality")));
+
+ assertThat(result2.getFacets().getAll()).hasSize(1);
+ assertThat(result2.getFacets().getAll().get("impacts.softwareQuality"))
+ .containsOnly(
+ entry("SECURITY", 1L),
+ entry("MAINTAINABILITY", 0L),
+ entry("RELIABILITY", 0L));
+ }
+
+ @Test
+ public void search_should_support_severity_facet() {
+ ImpactDto impactDto = new ImpactDto().setSoftwareQuality(SoftwareQuality.SECURITY).setSeverity(Severity.HIGH).setUuid("uuid");
+ ImpactDto impactDto2 = new ImpactDto().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(Severity.LOW).setUuid("uuid2");
+ createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto)));
+ createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto2)));
+ index();
+
+ RuleQuery query = new RuleQuery();
+
+ SearchIdResult result2 = underTest.search(query, new SearchOptions().addFacets(singletonList("impacts.severity")));
+
+ assertThat(result2.getFacets().getAll()).hasSize(1);
+ assertThat(result2.getFacets().getAll().get("impacts.severity")).containsOnly(entry("LOW", 1L), entry("MEDIUM", 0L), entry("HIGH", 1L));
+ }
+
+ @Test
+ public void search_should_support_severity_facet_with_filters() {
+ ImpactDto impactDto = new ImpactDto().setSoftwareQuality(SoftwareQuality.SECURITY).setSeverity(Severity.HIGH).setUuid("uuid");
+ ImpactDto impactDto2 = new ImpactDto().setSoftwareQuality(SoftwareQuality.MAINTAINABILITY).setSeverity(Severity.LOW).setUuid("uuid2");
+ createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto)));
+ createRule(setRepositoryKey("php"), setImpacts(List.of(impactDto2)));
+ index();
+
+ RuleQuery query = new RuleQuery();
+
+ SearchIdResult result2 = underTest.search(query.setImpactSeverities(Set.of("LOW")), new SearchOptions().addFacets(singletonList("impacts.severity")));
+
+ assertThat(result2.getFacets().getAll()).hasSize(1);
+ assertThat(result2.getFacets().getAll().get("impacts.severity")).containsOnly(entry("LOW", 1L), entry("MEDIUM", 0L), entry("HIGH", 1L));
+ }
+
@Test
public void global_facet_on_repositories_and_tags() {
createRule(setRepositoryKey("php"), setSystemTags("sysTag"), setTags());
.subAggregation(facetTopAggregation);
}
+ public AggregationBuilder buildTopAggregationStickyFacet(String fieldName, String facetName, AggregationBuilder additionalAggregationFilter) {
+ BoolQueryBuilder facetFilter = getStickyFacetFilter(fieldName);
+ return AggregationBuilders
+ .global(facetName)
+ .subAggregation(AggregationBuilders
+ .filter(facetName + "_filter", facetFilter)
+ .subAggregation(additionalAggregationFilter));
+ }
+
public BoolQueryBuilder getStickyFacetFilter(String... fieldNames) {
BoolQueryBuilder facetFilter = boolQuery().must(query);
for (Map.Entry<String, QueryBuilder> filter : filters.entrySet()) {
return this;
}
- public IssueDoc setImpacts(Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> softwareQualities) {
- List<Map<String, String>> convertedMap = softwareQualities
+ public IssueDoc setImpacts(Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> impacts) {
+ List<Map<String, String>> convertedMap = impacts
.entrySet()
.stream()
.map(entry -> Map.of(
import com.google.common.collect.Sets;
import java.util.Collection;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
+import java.util.stream.Collectors;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.apache.commons.lang.builder.ReflectionToStringBuilder;
+import org.sonar.api.issue.impact.SoftwareQuality;
import org.sonar.api.rule.RuleKey;
import org.sonar.api.rule.RuleStatus;
+import org.sonar.api.rules.Rule;
import org.sonar.api.rules.RuleType;
+import org.sonar.db.issue.ImpactDto;
import org.sonar.db.rule.RuleDescriptionSectionDto;
import org.sonar.db.rule.RuleDto;
import org.sonar.db.rule.RuleForIndexingDto;
import org.sonar.server.security.SecurityStandards.SQCategory;
import static java.util.stream.Collectors.joining;
+import static org.sonar.server.rule.index.RuleIndexDefinition.SUB_FIELD_SEVERITY;
+import static org.sonar.server.rule.index.RuleIndexDefinition.SUB_FIELD_SOFTWARE_QUALITY;
import static org.sonar.server.rule.index.RuleIndexDefinition.TYPE_RULE;
/**
return this;
}
+ private RuleDoc setCleanCodeAttributeCategory(String cleanCodeAttributeCategory) {
+ setField(RuleIndexDefinition.FIELD_RULE_CLEAN_CODE_ATTRIBUTE_CATEGORY, cleanCodeAttributeCategory);
+ return this;
+ }
+
+ public RuleDoc setImpacts(Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> impacts) {
+ List<Map<String, String>> convertedMap = impacts
+ .entrySet()
+ .stream()
+ .map(entry -> Map.of(
+ SUB_FIELD_SOFTWARE_QUALITY, entry.getKey().name(),
+ SUB_FIELD_SEVERITY, entry.getValue().name()))
+ .toList();
+ setField(RuleIndexDefinition.FIELD_RULE_IMPACTS, convertedMap);
+ return this;
+ }
+
@Override
public String toString() {
return ReflectionToStringBuilder.toString(this);
.setTags(Sets.union(dto.getTags(), dto.getSystemTags()))
.setUpdatedAt(dto.getUpdatedAt())
.setHtmlDescription(getConcatenatedSectionsInHtml(dto))
- .setTemplateKey(getRuleKey(dto));
+ .setTemplateKey(getRuleKey(dto))
+ .setCleanCodeAttributeCategory(dto.getCleanCodeAttributeCategory())
+ .setImpacts(dto.getImpacts().stream().collect(Collectors.toMap(ImpactDto::getSoftwareQuality, ImpactDto::getSeverity)));
}
@CheckForNull
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.function.Function;
+import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.apache.commons.lang.StringUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.search.aggregations.AggregationBuilder;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.BucketOrder;
+import org.elasticsearch.search.aggregations.bucket.filter.FiltersAggregator;
+import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.terms.IncludeExclude;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
+import org.sonar.api.issue.impact.SoftwareQuality;
import org.sonar.api.rule.RuleStatus;
import org.sonar.api.rule.Severity;
import org.sonar.api.rules.RuleType;
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
import static org.elasticsearch.index.query.QueryBuilders.matchPhraseQuery;
import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
+import static org.elasticsearch.index.query.QueryBuilders.nestedQuery;
import static org.elasticsearch.index.query.QueryBuilders.termsQuery;
+import static org.elasticsearch.search.aggregations.AggregationBuilders.filters;
+import static org.elasticsearch.search.aggregations.AggregationBuilders.reverseNested;
import static org.sonar.api.rules.RuleType.SECURITY_HOTSPOT;
import static org.sonar.api.rules.RuleType.VULNERABILITY;
import static org.sonar.server.es.EsUtils.SCROLL_TIME_IN_MINUTES;
import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_ACTIVE_RULE_INHERITANCE;
import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_ACTIVE_RULE_PROFILE_UUID;
import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_ACTIVE_RULE_SEVERITY;
+import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_RULE_CLEAN_CODE_ATTRIBUTE_CATEGORY;
+import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_RULE_IMPACTS;
+import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_RULE_IMPACT_SEVERITY;
+import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_RULE_IMPACT_SOFTWARE_QUALITY;
import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_RULE_CREATED_AT;
import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_RULE_CWE;
import static org.sonar.server.rule.index.RuleIndexDefinition.FIELD_RULE_HTML_DESCRIPTION;
public static final String FACET_TYPES = "types";
public static final String FACET_OLD_DEFAULT = "true";
public static final String FACET_CWE = "cwe";
+
/**
* @deprecated SansTop25 report is outdated, it has been completely deprecated in version 10.0 and will be removed from version 11.0
*/
BoolQueryBuilder textQuery = boolQuery();
JavaTokenizer.split(queryText)
.stream().map(token -> boolQuery().should(
- matchQuery(
- SEARCH_GRAMS_ANALYZER.subField(FIELD_RULE_NAME),
- StringUtils.left(token, DefaultIndexSettings.MAXIMUM_NGRAM_LENGTH)).boost(20F))
+ matchQuery(
+ SEARCH_GRAMS_ANALYZER.subField(FIELD_RULE_NAME),
+ StringUtils.left(token, DefaultIndexSettings.MAXIMUM_NGRAM_LENGTH)).boost(20F))
.should(
matchPhraseQuery(
ENGLISH_HTML_ANALYZER.subField(FIELD_RULE_HTML_DESCRIPTION),
private static QueryBuilder termQuery(String field, String query, float boost) {
return QueryBuilders.multiMatchQuery(query,
- field, SEARCH_WORDS_ANALYZER.subField(field))
+ field, SEARCH_WORDS_ANALYZER.subField(field))
.operator(Operator.AND)
.boost(boost);
}
}
}
+ if (query.getCleanCodeAttributesCategory() != null) {
+ filters.put(FIELD_RULE_CLEAN_CODE_ATTRIBUTE_CATEGORY, createTermsFilter(FIELD_RULE_CLEAN_CODE_ATTRIBUTE_CATEGORY,
+ Set.of(query.getCleanCodeAttributesCategory())));
+ }
+
+ addImpactFilters(query, filters);
+
return filters;
}
+ private static void addImpactFilters(RuleQuery query, Map<String, QueryBuilder> allFilters) {
+ if (isNotEmpty(query.getImpactSoftwareQualities())) {
+ allFilters.put(
+ FIELD_RULE_IMPACT_SOFTWARE_QUALITY,
+ nestedQuery(
+ FIELD_RULE_IMPACTS,
+ termsQuery(FIELD_RULE_IMPACT_SOFTWARE_QUALITY, query.getImpactSoftwareQualities()),
+ ScoreMode.Avg));
+ }
+
+ if (isNotEmpty(query.getImpactSeverities())) {
+ allFilters.put(
+ FIELD_RULE_IMPACT_SEVERITY,
+ nestedQuery(
+ FIELD_RULE_IMPACTS,
+ termsQuery(FIELD_RULE_IMPACT_SEVERITY, query.getImpactSeverities()),
+ ScoreMode.Avg));
+ }
+ }
+
private static void addSecurityStandardFilter(Map<String, QueryBuilder> filters, String key, Collection<String> values) {
if (isNotEmpty(values)) {
filters.put(key,
return filter;
}
- private static Map<String, AggregationBuilder> getFacets(RuleQuery query, SearchOptions options, QueryBuilder queryBuilder, Map<String, QueryBuilder> filters) {
+ private static Map<String, AggregationBuilder> getFacets(RuleQuery query, SearchOptions options, QueryBuilder queryBuilder,
+ Map<String, QueryBuilder> filters) {
Map<String, AggregationBuilder> aggregations = new HashMap<>();
StickyFacetBuilder stickyFacetBuilder = stickyFacetBuilder(queryBuilder, filters);
return aggregations;
}
- private static void addDefaultFacets(RuleQuery query, SearchOptions options, Map<String, AggregationBuilder> aggregations, StickyFacetBuilder stickyFacetBuilder) {
+ private static 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(FIELD_RULE_REPOSITORY, FACET_REPOSITORIES, MAX_FACET_SIZE,
(repositories == null) ? (new String[0]) : repositories.toArray()));
}
+ if (options.getFacets().contains(FACET_CLEAN_CODE_ATTRIBUTE_CATEGORY)) {
+ Collection<String> tags = query.getTags();
+ aggregations.put(FACET_CLEAN_CODE_ATTRIBUTE_CATEGORY,
+ stickyFacetBuilder.buildStickyFacet(FIELD_RULE_CLEAN_CODE_ATTRIBUTE_CATEGORY, FACET_CLEAN_CODE_ATTRIBUTE_CATEGORY, MAX_FACET_SIZE,
+ (tags == null) ? (new String[0]) : tags.toArray()));
+ }
+
+ addImpactSoftwareQualityFacetIfNeeded(options, query, aggregations, stickyFacetBuilder);
+ addImpactSeverityFacetIfNeeded(options, query, aggregations, stickyFacetBuilder);
addDefaultSecurityFacets(query, options, aggregations, stickyFacetBuilder);
}
+ private static void addImpactSoftwareQualityFacetIfNeeded(SearchOptions options, RuleQuery query, Map<String, AggregationBuilder> aggregations,
+ StickyFacetBuilder stickyFacetBuilder) {
+ if (!options.getFacets().contains(FACET_IMPACT_SOFTWARE_QUALITY)) {
+ return;
+ }
+
+ Function<SoftwareQuality, BoolQueryBuilder> mainQuery = softwareQuality -> boolQuery()
+ .filter(QueryBuilders.termQuery(FIELD_RULE_IMPACT_SOFTWARE_QUALITY, softwareQuality.name()));
+
+ FiltersAggregator.KeyedFilter[] keyedFilters = Arrays.stream(SoftwareQuality.values())
+ .map(softwareQuality -> new FiltersAggregator.KeyedFilter(softwareQuality.name(),
+ isNotEmpty(query.getImpactSeverities()) ? mainQuery.apply(softwareQuality)
+ .filter(termsQuery(FIELD_RULE_IMPACT_SEVERITY, query.getImpactSeverities())) : mainQuery.apply(softwareQuality)))
+ .toArray(FiltersAggregator.KeyedFilter[]::new);
+
+ NestedAggregationBuilder nestedAggregationBuilder = AggregationBuilders.nested("nested_" + FIELD_RULE_IMPACT_SOFTWARE_QUALITY, FIELD_RULE_IMPACTS)
+ .subAggregation(filters(FIELD_RULE_IMPACT_SOFTWARE_QUALITY, keyedFilters));
+
+ AggregationBuilder aggregationBuilder = stickyFacetBuilder.buildTopAggregationStickyFacet(FIELD_RULE_IMPACT_SOFTWARE_QUALITY, FACET_IMPACT_SOFTWARE_QUALITY, nestedAggregationBuilder);
+
+ aggregations.put(FACET_IMPACT_SOFTWARE_QUALITY, aggregationBuilder);
+ }
+
+ private static void addImpactSeverityFacetIfNeeded(SearchOptions options, RuleQuery query, Map<String, AggregationBuilder> aggregations, StickyFacetBuilder stickyFacetBuilder) {
+ if (!options.getFacets().contains(FACET_IMPACT_SEVERITY)) {
+ return;
+ }
+
+ Function<org.sonar.api.issue.impact.Severity, BoolQueryBuilder> mainQuery = softwareQuality -> boolQuery()
+ .filter(QueryBuilders.termQuery(FIELD_RULE_IMPACT_SEVERITY, softwareQuality.name()));
+
+ FiltersAggregator.KeyedFilter[] keyedFilters = Arrays.stream(org.sonar.api.issue.impact.Severity.values())
+ .map(severity -> new FiltersAggregator.KeyedFilter(severity.name(),
+ isNotEmpty(query.getImpactSoftwareQualities()) ?
+ mainQuery.apply(severity).filter(termsQuery(FIELD_RULE_IMPACT_SEVERITY, query.getImpactSoftwareQualities()))
+ : mainQuery.apply(severity)))
+ .toArray(FiltersAggregator.KeyedFilter[]::new);
+
+ NestedAggregationBuilder nestedAggregationBuilder = AggregationBuilders.nested("nested_" + FIELD_RULE_IMPACT_SEVERITY, FIELD_RULE_IMPACTS)
+ .subAggregation(filters(FIELD_RULE_IMPACT_SEVERITY, keyedFilters).subAggregation(reverseNested("reverse_nested_" + FIELD_RULE_IMPACT_SEVERITY)));
+
+ AggregationBuilder aggregationBuilder = stickyFacetBuilder.buildTopAggregationStickyFacet(FIELD_RULE_IMPACT_SEVERITY, FACET_IMPACT_SEVERITY, nestedAggregationBuilder);
+
+ aggregations.put(FACET_IMPACT_SEVERITY, aggregationBuilder);
+ }
+
private static Function<TermsAggregationBuilder, AggregationBuilder> filterSecurityCategories() {
return termsAggregation -> AggregationBuilders.filter(
- "filter_by_rule_types_" + termsAggregation.getName(),
- termsQuery(FIELD_RULE_TYPE,
- VULNERABILITY.name(),
- SECURITY_HOTSPOT.name()))
+ "filter_by_rule_types_" + termsAggregation.getName(),
+ termsQuery(FIELD_RULE_TYPE,
+ VULNERABILITY.name(),
+ SECURITY_HOTSPOT.name()))
.subAggregation(termsAggregation);
}
return EsUtils.termsKeys(esResponse.getAggregations().get(AGGREGATION_NAME_FOR_TAGS));
}
+ @CheckForNull
+ private static QueryBuilder createTermsFilter(String field, Collection<?> values) {
+ return values.isEmpty() ? null : termsQuery(field, values);
+ }
+
private static boolean isNotEmpty(@Nullable Collection<?> list) {
return list != null && !list.isEmpty();
}
*/
package org.sonar.server.rule.index;
-import com.google.common.collect.ImmutableSet;
import java.util.Set;
import javax.inject.Inject;
import org.sonar.api.config.Configuration;
public static final String FIELD_RULE_SONARSOURCE_SECURITY = "sonarsourceSecurity";
public static final String FIELD_RULE_TAGS = "tags";
- public static final Set<String> SORT_FIELDS = ImmutableSet.of(
+ public static final Set<String> SORT_FIELDS = Set.of(
FIELD_RULE_NAME,
FIELD_RULE_UPDATED_AT,
FIELD_RULE_CREATED_AT,
public static final String FIELD_ACTIVE_RULE_PROFILE_UUID = "activeRule_ruleProfile";
public static final String FIELD_ACTIVE_RULE_SEVERITY = "activeRule_severity";
+ public static final String FIELD_RULE_CLEAN_CODE_ATTRIBUTE_CATEGORY = "cleanCodeAttributeCategory";
+ public static final String FIELD_RULE_IMPACTS = "impacts";
+ public static final String SUB_FIELD_SOFTWARE_QUALITY = "softwareQuality";
+ public static final String SUB_FIELD_SEVERITY = "severity";
+ public static final String FIELD_RULE_IMPACT_SOFTWARE_QUALITY = FIELD_RULE_IMPACTS + "." + SUB_FIELD_SOFTWARE_QUALITY;
+ public static final String FIELD_RULE_IMPACT_SEVERITY = FIELD_RULE_IMPACTS + "." + SUB_FIELD_SEVERITY;
+
private final Configuration config;
private final boolean enableSource;
ruleMapping.keywordFieldBuilder(FIELD_RULE_SANS_TOP_25).disableNorms().build();
ruleMapping.keywordFieldBuilder(FIELD_RULE_SONARSOURCE_SECURITY).disableNorms().build();
+ ruleMapping.keywordFieldBuilder(FIELD_RULE_CLEAN_CODE_ATTRIBUTE_CATEGORY).disableNorms().build();
+ ruleMapping.nestedFieldBuilder(FIELD_RULE_IMPACTS)
+ .addKeywordField(SUB_FIELD_SOFTWARE_QUALITY)
+ .addKeywordField(SUB_FIELD_SEVERITY)
+ .build();
+
// Active rule
index.createTypeMapping(TYPE_ACTIVE_RULE)
.keywordFieldBuilder(FIELD_ACTIVE_RULE_UUID).disableNorms().build()
private Collection<String> sansTop25;
private Collection<String> cwe;
private Collection<String> sonarsourceSecurity;
+ private Collection<String> impactSeverities;
+ private Collection<String> impactSoftwareQualities;
+ private String cleanCodeAttributesCategory;
+
@CheckForNull
public QProfileDto getQProfile() {
this.sonarsourceSecurity = sonarsourceSecurity;
return this;
}
+
+ public Collection<String> getImpactSeverities() {
+ return impactSeverities;
+ }
+
+ public RuleQuery setImpactSeverities(Collection<String> impactSeverities) {
+ this.impactSeverities = impactSeverities;
+ return this;
+ }
+
+ public Collection<String> getImpactSoftwareQualities() {
+ return impactSoftwareQualities;
+ }
+
+ public RuleQuery setImpactSoftwareQualities(Collection<String> impactSoftwareQualities) {
+ this.impactSoftwareQualities = impactSoftwareQualities;
+ return this;
+ }
+
+ public String getCleanCodeAttributesCategory() {
+ return cleanCodeAttributesCategory;
+ }
+
+ public RuleQuery setCleanCodeAttributesCategory(String cleanCodeAttributesCategory) {
+ this.cleanCodeAttributesCategory = cleanCodeAttributesCategory;
+ return this;
+ }
+
}
return client.search(requestBuilder);
}
- private void configureTopAggregations(IssueQuery query, SearchOptions options, SearchSourceBuilder esRequest, AllFilters allFilters, RequestFiltersComputer filterComputer) {
+ private void configureTopAggregations(IssueQuery query, SearchOptions options, SearchSourceBuilder esRequest, AllFilters allFilters,
+ RequestFiltersComputer filterComputer) {
TopAggregationHelper aggregationHelper = newAggregationHelper(filterComputer, query);
configureTopAggregations(aggregationHelper, query, options, allFilters, esRequest);
esRequest.aggregation(aggregation);
}
- private static void addImpactSoftwareQualityFacetIfNeeded(SearchOptions options, IssueQuery query, TopAggregationHelper aggregationHelper, SearchSourceBuilder esRequest) {
+ private static void addImpactSoftwareQualityFacetIfNeeded(SearchOptions options, IssueQuery query, TopAggregationHelper aggregationHelper,
+ SearchSourceBuilder esRequest) {
if (!options.getFacets().contains(PARAM_IMPACT_SOFTWARE_QUALITIES)) {
return;
}
public static final String PARAM_TEMPLATE_KEY = "template_key";
public static final String PARAM_COMPARE_TO_PROFILE = "compareToProfile";
+ public static final String PARAM_IMPACT_SOFTWARE_QUALITIES = "impactSoftwareQualities";
+ public static final String PARAM_IMPACT_SEVERITIES = "impactSeverities";
+ public static final String PARAM_CLEAN_CODE_ATTRIBUTE_CATEGORIES = "cleanCodeAttributeCategories";
+
public static final String FIELD_REPO = "repo";
public static final String FIELD_NAME = "name";
public static final String FIELD_CREATED_AT = "createdAt";