aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorJacek Poreda <jacek.poreda@sonarsource.com>2023-08-10 10:33:25 +0200
committersonartech <sonartech@sonarsource.com>2023-08-18 20:02:49 +0000
commit177da8d8b082ad4688524d8f3ecea9bee624d09b (patch)
tree0923e3ac529b086de0f9d42c0e8f3129e5c6e079 /server
parent2b456da715b2650a1519a4d273119b01388396df (diff)
downloadsonarqube-177da8d8b082ad4688524d8f3ecea9bee624d09b.tar.gz
sonarqube-177da8d8b082ad4688524d8f3ecea9bee624d09b.zip
SONAR-20021 Update Issues web apis with Clean Code Taxonomy
Diffstat (limited to 'server')
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/issue/ImpactDto.java6
-rw-r--r--server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml10
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleDtoTest.java12
-rw-r--r--server/sonar-server-common/src/main/java/org/sonar/server/es/Facets.java12
-rw-r--r--server/sonar-server-common/src/main/java/org/sonar/server/issue/SearchRequest.java30
-rw-r--r--server/sonar-server-common/src/test/java/org/sonar/server/issue/SearchRequestTest.java9
-rw-r--r--server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java28
-rw-r--r--server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQuery.java2
-rw-r--r--server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQueryFactory.java3
-rw-r--r--server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexFacetsTest.java22
-rw-r--r--server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/PullTaintActionIT.java14
-rw-r--r--server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/SearchActionIT.java161
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/AddCommentAction.java1
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/AssignAction.java1
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/DeleteCommentAction.java1
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/DoTransitionAction.java1
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/EditCommentAction.java1
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchAction.java46
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchResponseFormat.java16
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SetSeverityAction.java1
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SetTagsAction.java1
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SetTypeAction.java1
-rw-r--r--server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/pull/PullTaintActionProtobufObjectGenerator.java15
-rw-r--r--server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/add_comment-example.json8
-rw-r--r--server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/assign-example.json8
-rw-r--r--server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/delete_comment-example.json8
-rw-r--r--server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/do_transition-example.json8
-rw-r--r--server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/edit_comment-example.json8
-rw-r--r--server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/pull-taint-example.proto13
-rw-r--r--server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/search-example.json8
-rw-r--r--server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/set_severity-example.json8
-rw-r--r--server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/set_tags-example.json8
-rw-r--r--server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/set_type-example.json8
-rw-r--r--server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/SearchResponseFormatFormatOperationTest.java12
34 files changed, 441 insertions, 50 deletions
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/ImpactDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/ImpactDto.java
index b62469b68db..82da4d3b0b4 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/ImpactDto.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/ImpactDto.java
@@ -33,6 +33,12 @@ public class ImpactDto implements Serializable {
// nothing to do
}
+ public ImpactDto(String uuid, SoftwareQuality softwareQuality, Severity severity) {
+ this.uuid = uuid;
+ this.softwareQuality = softwareQuality;
+ this.severity = severity;
+ }
+
public String getUuid() {
return uuid;
}
diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml
index 17889d58ee3..a731ea97d42 100644
--- a/server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml
+++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml
@@ -801,10 +801,13 @@
i.component_uuid as component_uuid,
i.assignee as assigneeUuid,
u.login as assigneeLogin,
- i.rule_description_context_key as ruleDescriptionContextKey
+ i.rule_description_context_key as ruleDescriptionContextKey,
+ <include refid="issueImpactsColumns"/>
+ <include refid="ruleDefaultImpactsColumns"/>
+ r.clean_code_attribute as cleanCodeAttribute
</sql>
- <select id="selectByBranch" parameterType="map" resultType="Issue">
+ <select id="selectByBranch" parameterType="map" resultMap="issueResultMap" resultOrdered="true">
select
<include refid="selectByBranchColumns"/>
, p.path as filePath
@@ -812,6 +815,8 @@
inner join rules r on r.uuid = i.rule_uuid
inner join components p on p.uuid=i.component_uuid
left join users u on i.assignee = u.uuid
+ left outer join issues_impacts ii on i.kee = ii.issue_key
+ left outer join rules_default_impacts rdi on r.uuid = rdi.rule_uuid
where
<if test="keys.size() > 0">
i.kee IN
@@ -822,6 +827,7 @@
<if test="changedSince != null">
AND i.issue_update_date &gt;= #{changedSince,jdbcType=BIGINT}
</if>
+ order by i.kee
</select>
<select id="selectRecentlyClosedIssues" resultType="string">
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleDtoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleDtoTest.java
index d0ee6dcd3dc..a821a58b645 100644
--- a/server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleDtoTest.java
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleDtoTest.java
@@ -24,15 +24,11 @@ import java.util.Collections;
import java.util.Set;
import java.util.TreeSet;
import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
import org.junit.Test;
import org.sonar.api.issue.impact.Severity;
import org.sonar.api.issue.impact.SoftwareQuality;
import org.sonar.core.util.UuidFactoryFast;
-import org.sonar.api.rule.RuleScope;
-import org.sonar.api.rules.CleanCodeAttribute;
import org.sonar.api.rules.RuleType;
-import org.sonar.api.server.rule.RulesDefinition;
import org.sonar.core.util.Uuids;
import org.sonar.db.issue.ImpactDto;
@@ -41,8 +37,6 @@ import static org.apache.commons.lang.StringUtils.repeat;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.Assertions.tuple;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
import static org.sonar.db.rule.RuleDto.ERROR_MESSAGE_SECTION_ALREADY_EXISTS;
import static org.sonar.db.rule.RuleTesting.newRule;
@@ -248,10 +242,6 @@ public class RuleDtoTest {
}
public static ImpactDto newImpactDto(SoftwareQuality softwareQuality, Severity severity) {
- return new ImpactDto()
- .setUuid(UuidFactoryFast.getInstance().create())
- .setSoftwareQuality(softwareQuality)
- .setSeverity(severity);
+ return new ImpactDto(UuidFactoryFast.getInstance().create(), softwareQuality, severity);
}
-
}
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/es/Facets.java b/server/sonar-server-common/src/main/java/org/sonar/server/es/Facets.java
index 23e5dbb95c6..435977d7ab8 100644
--- a/server/sonar-server-common/src/main/java/org/sonar/server/es/Facets.java
+++ b/server/sonar-server-common/src/main/java/org/sonar/server/es/Facets.java
@@ -38,6 +38,7 @@ import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation;
import org.elasticsearch.search.aggregations.bucket.filter.Filter;
import org.elasticsearch.search.aggregations.bucket.histogram.Histogram;
import org.elasticsearch.search.aggregations.bucket.missing.Missing;
+import org.elasticsearch.search.aggregations.bucket.nested.ReverseNested;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.metrics.Sum;
@@ -176,7 +177,16 @@ public class Facets {
private void processMultiBucketAggregation(MultiBucketsAggregation aggregation) {
LinkedHashMap<String, Long> facet = getOrCreateFacet(aggregation.getName());
- aggregation.getBuckets().forEach(bucket -> facet.put(bucket.getKeyAsString(), bucket.getDocCount()));
+ aggregation.getBuckets().forEach(bucket -> {
+ if (!bucket.getAggregations().asList().isEmpty()) {
+ Aggregation next = bucket.getAggregations().iterator().next();
+ if (next instanceof ReverseNested reverseNestedBucket) {
+ facet.put(bucket.getKeyAsString(), reverseNestedBucket.getDocCount());
+ }
+ } else {
+ facet.put(bucket.getKeyAsString(), bucket.getDocCount());
+ }
+ });
}
public boolean contains(String facetName) {
diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/issue/SearchRequest.java b/server/sonar-server-common/src/main/java/org/sonar/server/issue/SearchRequest.java
index d22ab314330..3eb488a5951 100644
--- a/server/sonar-server-common/src/main/java/org/sonar/server/issue/SearchRequest.java
+++ b/server/sonar-server-common/src/main/java/org/sonar/server/issue/SearchRequest.java
@@ -57,6 +57,9 @@ public class SearchRequest {
private List<String> rules;
private String sort;
private List<String> severities;
+ private List<String> impactSeverities;
+ private List<String> impactSoftwareQualities;
+ private List<String> cleanCodeAttributesCategories;
private List<String> statuses;
private List<String> tags;
private Set<String> types;
@@ -512,4 +515,31 @@ public class SearchRequest {
this.codeVariants = codeVariants;
return this;
}
+
+ public List<String> getImpactSeverities() {
+ return impactSeverities;
+ }
+
+ public SearchRequest setImpactSeverities(@Nullable List<String> impactSeverities) {
+ this.impactSeverities = impactSeverities;
+ return this;
+ }
+
+ public List<String> getImpactSoftwareQualities() {
+ return impactSoftwareQualities;
+ }
+
+ public SearchRequest setImpactSoftwareQualities(@Nullable List<String> impactSoftwareQualities) {
+ this.impactSoftwareQualities = impactSoftwareQualities;
+ return this;
+ }
+
+ public List<String> getCleanCodeAttributesCategories() {
+ return cleanCodeAttributesCategories;
+ }
+
+ public SearchRequest setCleanCodeAttributesCategories(@Nullable List<String> cleanCodeAttributesCategories) {
+ this.cleanCodeAttributesCategories = cleanCodeAttributesCategories;
+ return this;
+ }
}
diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/issue/SearchRequestTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/issue/SearchRequestTest.java
index f5f7f0620c1..63812baa036 100644
--- a/server/sonar-server-common/src/test/java/org/sonar/server/issue/SearchRequestTest.java
+++ b/server/sonar-server-common/src/test/java/org/sonar/server/issue/SearchRequestTest.java
@@ -19,6 +19,7 @@
*/
package org.sonar.server.issue;
+import java.util.List;
import org.junit.Test;
import static java.util.Arrays.asList;
@@ -54,7 +55,10 @@ public class SearchRequestTest {
.setOwaspAsvsLevel(2)
.setPciDss32(asList("1", "4"))
.setPciDss40(asList("3", "5"))
- .setCodeVariants(asList("variant1", "variant2"));
+ .setCodeVariants(asList("variant1", "variant2"))
+ .setCleanCodeAttributesCategories(singletonList("ADAPTABLE"))
+ .setImpactSeverities(List.of("HIGH", "LOW"))
+ .setImpactSoftwareQualities(List.of("RELIABILITY", "SECURITY"));
assertThat(underTest.getIssues()).containsOnlyOnce("anIssueKey");
assertThat(underTest.getSeverities()).containsExactly("MAJOR", "MINOR");
@@ -81,6 +85,9 @@ public class SearchRequestTest {
assertThat(underTest.getPciDss32()).containsExactly("1", "4");
assertThat(underTest.getPciDss40()).containsExactly("3", "5");
assertThat(underTest.getCodeVariants()).containsExactly("variant1", "variant2");
+ assertThat(underTest.getCleanCodeAttributesCategories()).containsExactly("ADAPTABLE");
+ assertThat(underTest.getImpactSeverities()).containsExactly("HIGH", "LOW");
+ assertThat(underTest.getImpactSoftwareQualities()).containsExactly("RELIABILITY", "SECURITY");
}
@Test
diff --git a/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java b/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java
index a8991a87577..f6336ef6eda 100644
--- a/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java
+++ b/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java
@@ -111,6 +111,7 @@ import static org.elasticsearch.index.query.QueryBuilders.rangeQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
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.escapeSpecialRegexChars;
@@ -128,6 +129,8 @@ import static org.sonar.server.issue.index.IssueIndex.Facet.CREATED_AT;
import static org.sonar.server.issue.index.IssueIndex.Facet.CWE;
import static org.sonar.server.issue.index.IssueIndex.Facet.DIRECTORIES;
import static org.sonar.server.issue.index.IssueIndex.Facet.FILES;
+import static org.sonar.server.issue.index.IssueIndex.Facet.IMPACT_SEVERITY;
+import static org.sonar.server.issue.index.IssueIndex.Facet.IMPACT_SOFTWARE_QUALITY;
import static org.sonar.server.issue.index.IssueIndex.Facet.LANGUAGES;
import static org.sonar.server.issue.index.IssueIndex.Facet.OWASP_ASVS_40;
import static org.sonar.server.issue.index.IssueIndex.Facet.OWASP_TOP_10;
@@ -140,8 +143,6 @@ import static org.sonar.server.issue.index.IssueIndex.Facet.RULES;
import static org.sonar.server.issue.index.IssueIndex.Facet.SANS_TOP_25;
import static org.sonar.server.issue.index.IssueIndex.Facet.SCOPES;
import static org.sonar.server.issue.index.IssueIndex.Facet.SEVERITIES;
-import static org.sonar.server.issue.index.IssueIndex.Facet.IMPACT_SOFTWARE_QUALITY;
-import static org.sonar.server.issue.index.IssueIndex.Facet.IMPACT_SEVERITY;
import static org.sonar.server.issue.index.IssueIndex.Facet.SONARSOURCE_SECURITY;
import static org.sonar.server.issue.index.IssueIndex.Facet.STATUSES;
import static org.sonar.server.issue.index.IssueIndex.Facet.TAGS;
@@ -159,6 +160,9 @@ import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_FILE
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_FUNC_CLOSED_AT;
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_FUNC_CREATED_AT;
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_FUNC_UPDATED_AT;
+import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_IMPACTS;
+import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_IMPACT_SEVERITY;
+import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_IMPACT_SOFTWARE_QUALITY;
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_IS_MAIN_BRANCH;
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_KEY;
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_LANGUAGE;
@@ -176,9 +180,6 @@ import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_SANS
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_SCOPE;
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_SEVERITY;
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_SEVERITY_VALUE;
-import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_IMPACTS;
-import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_IMPACT_SOFTWARE_QUALITY;
-import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_IMPACT_SEVERITY;
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_SQ_SECURITY_CATEGORY;
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_STATUS;
import static org.sonar.server.issue.index.IssueIndexDefinition.FIELD_ISSUE_TAGS;
@@ -199,6 +200,8 @@ import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_CREATED_AT;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_CWE;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_DIRECTORIES;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_FILES;
+import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_IMPACT_SEVERITIES;
+import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_IMPACT_SOFTWARE_QUALITIES;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_LANGUAGES;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_OWASP_ASVS_40;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_OWASP_TOP_10;
@@ -210,8 +213,6 @@ import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_RULES;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SANS_TOP_25;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SCOPES;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SEVERITIES;
-import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SOFTWARE_QUALITIES;
-import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SOFTWARE_QUALITIES_SEVERTIIES;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SONARSOURCE_SECURITY;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_STATUSES;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_TAGS;
@@ -255,8 +256,8 @@ public class IssueIndex {
public enum Facet {
SEVERITIES(PARAM_SEVERITIES, FIELD_ISSUE_SEVERITY, STICKY, Severity.ALL.size()),
- IMPACT_SOFTWARE_QUALITY(PARAM_SOFTWARE_QUALITIES, FIELD_ISSUE_IMPACT_SOFTWARE_QUALITY, STICKY, SoftwareQuality.values().length),
- IMPACT_SEVERITY(PARAM_SOFTWARE_QUALITIES_SEVERTIIES, FIELD_ISSUE_IMPACT_SEVERITY, STICKY,
+ IMPACT_SOFTWARE_QUALITY(PARAM_IMPACT_SOFTWARE_QUALITIES, FIELD_ISSUE_IMPACT_SOFTWARE_QUALITY, STICKY, SoftwareQuality.values().length),
+ IMPACT_SEVERITY(PARAM_IMPACT_SEVERITIES, FIELD_ISSUE_IMPACT_SEVERITY, STICKY,
org.sonar.api.issue.impact.Severity.values().length),
CLEAN_CODE_ATTRIBUTE_CATEGORY(PARAM_CLEAN_CODE_ATTRIBUTE_CATEGORIES, FIELD_ISSUE_CLEAN_CODE_ATTRIBUTE_CATEGORY, STICKY, CleanCodeAttributeCategory.values().length),
STATUSES(PARAM_STATUSES, FIELD_ISSUE_STATUS, STICKY, Issue.STATUSES.size()),
@@ -900,7 +901,7 @@ public class IssueIndex {
}
private static void addImpactSoftwareQualityFacetIfNeeded(SearchOptions options, IssueQuery query, TopAggregationHelper aggregationHelper, SearchSourceBuilder esRequest) {
- if (!options.getFacets().contains(PARAM_SOFTWARE_QUALITIES)) {
+ if (!options.getFacets().contains(PARAM_IMPACT_SOFTWARE_QUALITIES)) {
return;
}
@@ -924,7 +925,7 @@ public class IssueIndex {
}
private static void addImpactSeverityFacetIfNeeded(SearchOptions options, IssueQuery query, TopAggregationHelper aggregationHelper, SearchSourceBuilder esRequest) {
- if (!options.getFacets().contains(PARAM_SOFTWARE_QUALITIES_SEVERTIIES)) {
+ if (!options.getFacets().contains(PARAM_IMPACT_SEVERITIES)) {
return;
}
@@ -942,8 +943,9 @@ public class IssueIndex {
IMPACT_SEVERITY.getName(), IMPACT_SEVERITY.getTopAggregationDef(),
NO_EXTRA_FILTER,
t -> t.subAggregation(AggregationBuilders.nested("nested_" + IMPACT_SEVERITY.getName(), FIELD_ISSUE_IMPACTS)
- .subAggregation(filters(IMPACT_SEVERITY.getName(),
- keyedFilters))));
+ .subAggregation(filters(IMPACT_SEVERITY.getName(), keyedFilters)
+ // we want to count the number of issues for each severity, so we need to reverse the nested aggregation
+ .subAggregation(reverseNested("reverse_nested_" + IMPACT_SOFTWARE_QUALITY.getName())))));
esRequest.aggregation(aggregation);
}
diff --git a/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQuery.java b/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQuery.java
index 11cd3614c1f..33eefc1e188 100644
--- a/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQuery.java
+++ b/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQuery.java
@@ -101,7 +101,7 @@ public class IssueQuery {
private final Boolean newCodeOnReference;
private final Collection<String> newCodeOnReferenceByProjectUuids;
private final Collection<String> codeVariants;
- private Collection<String> cleanCodeAttributesCategories;
+ private final Collection<String> cleanCodeAttributesCategories;
private IssueQuery(Builder builder) {
this.issueKeys = defaultCollection(builder.issueKeys);
diff --git a/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQueryFactory.java b/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQueryFactory.java
index fb2ce21a14a..5386e7c302c 100644
--- a/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQueryFactory.java
+++ b/server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQueryFactory.java
@@ -122,6 +122,9 @@ public class IssueQueryFactory {
IssueQuery.Builder builder = IssueQuery.builder()
.issueKeys(request.getIssues())
.severities(request.getSeverities())
+ .cleanCodeAttributesCategories(request.getCleanCodeAttributesCategories())
+ .impactSoftwareQualities(request.getImpactSoftwareQualities())
+ .impactSeverities(request.getImpactSeverities())
.statuses(request.getStatuses())
.resolutions(request.getResolutions())
.resolved(request.getResolved())
diff --git a/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexFacetsTest.java b/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexFacetsTest.java
index cc9ede60838..1171923de97 100644
--- a/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexFacetsTest.java
+++ b/server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexFacetsTest.java
@@ -671,7 +671,7 @@ public class IssueIndexFacetsTest extends IssueIndexTestCommon {
}
@Test
- public void search_shouldReturnSoftwareQualityFacet() {
+ public void search_shouldReturnImpactSoftwareQualitiesFacet() {
ComponentDto project = newPrivateProjectDto();
ComponentDto file = newFileDto(project);
@@ -686,14 +686,14 @@ public class IssueIndexFacetsTest extends IssueIndexTestCommon {
newDoc("I4", project.uuid(), file).setImpacts(Map.of(
MAINTAINABILITY, org.sonar.api.issue.impact.Severity.LOW)));
- assertThatFacetHasOnly(IssueQuery.builder(), "softwareQualities",
+ assertThatFacetHasOnly(IssueQuery.builder(), "impactSoftwareQualities",
entry("MAINTAINABILITY", 3L),
entry("RELIABILITY", 2L),
entry("SECURITY", 0L));
}
@Test
- public void search_whenFilteredOnSeverity_shouldReturnSoftwareQualityFacet() {
+ public void search_whenFilteredOnSeverity_shouldReturnImpactSoftwareQualitiesFacet() {
ComponentDto project = newPrivateProjectDto();
ComponentDto file = newFileDto(project);
@@ -710,31 +710,31 @@ public class IssueIndexFacetsTest extends IssueIndexTestCommon {
MAINTAINABILITY, org.sonar.api.issue.impact.Severity.LOW)));
assertThatFacetHasOnly(IssueQuery.builder().impactSoftwareQualities(Set.of(MAINTAINABILITY.name())).impactSeverities(Set.of(org.sonar.api.issue.impact.Severity.LOW.name())),
- "softwareQualities",
+ "impactSoftwareQualities",
entry("MAINTAINABILITY", 2L),
entry("RELIABILITY", 0L),
entry("SECURITY", 0L));
- assertThatFacetHasOnly(IssueQuery.builder().impactSeverities(Set.of(Severity.MEDIUM.name())), "softwareQualities",
+ assertThatFacetHasOnly(IssueQuery.builder().impactSeverities(Set.of(Severity.MEDIUM.name())), "impactSoftwareQualities",
entry("MAINTAINABILITY", 0L),
entry("RELIABILITY", 1L),
entry("SECURITY", 0L));
- assertThatFacetHasOnly(IssueQuery.builder().impactSeverities(Set.of(org.sonar.api.issue.impact.Severity.HIGH.name())), "softwareQualities",
+ assertThatFacetHasOnly(IssueQuery.builder().impactSeverities(Set.of(org.sonar.api.issue.impact.Severity.HIGH.name())), "impactSoftwareQualities",
entry("MAINTAINABILITY", 1L),
entry("RELIABILITY", 1L),
entry("SECURITY", 0L));
assertThatFacetHasOnly(IssueQuery.builder()
.tags(singletonList("my-tag"))
- .impactSeverities(Set.of(org.sonar.api.issue.impact.Severity.HIGH.name())), "softwareQualities",
+ .impactSeverities(Set.of(org.sonar.api.issue.impact.Severity.HIGH.name())), "impactSoftwareQualities",
entry("MAINTAINABILITY", 1L),
entry("RELIABILITY", 0L),
entry("SECURITY", 0L));
}
@Test
- public void search_shouldReturnSoftwareQualitySeverityFacet() {
+ public void search_shouldReturnImpactSeverityFacet() {
ComponentDto project = newPrivateProjectDto();
ComponentDto file = newFileDto(project);
@@ -749,14 +749,14 @@ public class IssueIndexFacetsTest extends IssueIndexTestCommon {
newDoc("I4", project.uuid(), file).setImpacts(Map.of(
MAINTAINABILITY, org.sonar.api.issue.impact.Severity.LOW)));
- assertThatFacetHasOnly(IssueQuery.builder(), "softwareQualitiesSeverities",
+ assertThatFacetHasOnly(IssueQuery.builder(), "impactSeverities",
entry("HIGH", 2L),
entry("MEDIUM", 1L),
entry("LOW", 2L));
}
@Test
- public void search_whenFilteredOnSoftwareQuality_shouldReturnSoftwareQualitySeverityFacet() {
+ public void search_whenFilteredOnSoftwareQuality_shouldReturnImpactSeverityFacet() {
ComponentDto project = newPrivateProjectDto();
ComponentDto file = newFileDto(project);
@@ -771,7 +771,7 @@ public class IssueIndexFacetsTest extends IssueIndexTestCommon {
newDoc("I4", project.uuid(), file).setImpacts(Map.of(
MAINTAINABILITY, org.sonar.api.issue.impact.Severity.LOW)));
- assertThatFacetHasOnly(IssueQuery.builder().impactSoftwareQualities(Set.of(MAINTAINABILITY.name())), "softwareQualitiesSeverities",
+ assertThatFacetHasOnly(IssueQuery.builder().impactSoftwareQualities(Set.of(MAINTAINABILITY.name())), "impactSeverities",
entry("HIGH", 1L),
entry("MEDIUM", 0L),
entry("LOW", 2L));
diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/PullTaintActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/PullTaintActionIT.java
index 6cff8f8be47..4e6e21e2851 100644
--- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/PullTaintActionIT.java
+++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/PullTaintActionIT.java
@@ -56,9 +56,11 @@ import org.sonarqube.ws.Common;
import org.sonarqube.ws.Issues;
import static java.lang.String.format;
+import static java.util.stream.Collectors.toList;
import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.assertj.core.groups.Tuple.tuple;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.sonar.api.web.UserRole.USER;
@@ -253,6 +255,18 @@ public class PullTaintActionIT {
assertThat(taintLite.getRuleKey()).isEqualTo("javasecurity:S1000");
assertThat(taintLite.getType()).isEqualTo(Common.RuleType.forNumber(issueDto.getType()));
assertThat(taintLite.getAssignedToSubscribedUser()).isTrue();
+ assertThat(taintLite.getCleanCodeAttribute())
+ .isEqualTo(Common.CleanCodeAttribute.valueOf(issueDto.getCleanCodeAttribute().name()));
+ assertThat(taintLite.getCleanCodeAttributeCategory())
+ .isEqualTo(Common.CleanCodeAttributeCategory.valueOf(issueDto.getCleanCodeAttribute().getAttributeCategory().name()));
+
+ assertThat(taintLite.getImpactsList())
+ .extracting(Common.Impact::getSoftwareQuality, Common.Impact::getSeverity)
+ .containsExactlyInAnyOrderElementsOf(issueDto.getEffectiveImpacts()
+ .entrySet()
+ .stream()
+ .map(entry -> tuple(Common.SoftwareQuality.valueOf(entry.getKey().name()), Common.ImpactSeverity.valueOf(entry.getValue().name())))
+ .collect(toList()));
Issues.Location location = taintLite.getMainLocation();
assertThat(location.getMessage()).isEqualTo(issueDto.getMessage());
diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/SearchActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/SearchActionIT.java
index ad598866ccb..87e055fa282 100644
--- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/SearchActionIT.java
+++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/SearchActionIT.java
@@ -26,6 +26,7 @@ import java.time.Clock;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Optional;
import java.util.Random;
import java.util.Set;
import java.util.function.Consumer;
@@ -35,6 +36,7 @@ import java.util.stream.Stream;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.sonar.api.issue.impact.SoftwareQuality;
import org.sonar.api.resources.Languages;
import org.sonar.api.rule.RuleStatus;
import org.sonar.api.rules.RuleType;
@@ -51,6 +53,7 @@ import org.sonar.db.component.BranchType;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.ProjectData;
import org.sonar.db.component.SnapshotDto;
+import org.sonar.db.issue.ImpactDto;
import org.sonar.db.issue.IssueChangeDto;
import org.sonar.db.issue.IssueDto;
import org.sonar.db.permission.GroupPermissionDto;
@@ -123,6 +126,8 @@ import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_CODE_VARIAN
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_COMPONENTS;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_CREATED_AFTER;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_HIDE_COMMENTS;
+import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_IMPACT_SEVERITIES;
+import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_IMPACT_SOFTWARE_QUALITIES;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_IN_NEW_CODE_PERIOD;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_PULL_REQUEST;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_RULES;
@@ -227,7 +232,8 @@ public class SearchActionIT {
.getIssuesList()
.get(0)
.getActions()
- .getActionsList()).isEqualTo(asList(ACTION_SET_TAGS, COMMENT_KEY, ACTION_ASSIGN));
+ .getActionsList())
+ .isEqualTo(asList(ACTION_SET_TAGS, COMMENT_KEY, ACTION_ASSIGN));
response = ws.newRequest()
.setParam(PARAM_ADDITIONAL_FIELDS, "actions")
@@ -239,7 +245,8 @@ public class SearchActionIT {
.getIssuesList()
.get(0)
.getActions()
- .getActionsList()).isEqualTo(asList(ACTION_SET_TAGS, COMMENT_KEY));
+ .getActionsList())
+ .isEqualTo(asList(ACTION_SET_TAGS, COMMENT_KEY));
}
@Test
@@ -583,6 +590,152 @@ public class SearchActionIT {
}
@Test
+ public void search_whenImpactSoftwareQualitiesFacetRequested_shouldReturnFacet() {
+ RuleDto rule = newIssueRule();
+ ComponentDto project = db.components().insertPublicProject("PROJECT_ID",
+ c -> c.setKey("PROJECT_KEY").setName("NAME_PROJECT_ID").setLongName("LONG_NAME_PROJECT_ID").setLanguage("java")).getMainBranchComponent();
+ ComponentDto file = db.components().insertComponent(newFileDto(project, null, "FILE_ID").setKey("FILE_KEY").setLanguage("java"));
+ IssueDto issue1 = db.issues().insertIssue(rule, project, file, i -> i
+ .addImpact(new ImpactDto(uuidFactory.create(), SoftwareQuality.SECURITY, org.sonar.api.issue.impact.Severity.HIGH))
+ .addImpact(new ImpactDto(uuidFactory.create(), SoftwareQuality.RELIABILITY, org.sonar.api.issue.impact.Severity.HIGH)));
+ IssueDto issue2 = db.issues().insertIssue(rule, project, file, i -> i
+ .addImpact(new ImpactDto(uuidFactory.create(), SoftwareQuality.RELIABILITY, org.sonar.api.issue.impact.Severity.HIGH)));
+ IssueDto issue3 = db.issues().insertIssue(rule, project, file, i -> i
+ .addImpact(new ImpactDto(uuidFactory.create(), SoftwareQuality.SECURITY, org.sonar.api.issue.impact.Severity.MEDIUM))
+ .addImpact(new ImpactDto(uuidFactory.create(), SoftwareQuality.RELIABILITY, org.sonar.api.issue.impact.Severity.LOW)));
+ indexPermissionsAndIssues();
+
+ SearchWsResponse response = ws.newRequest()
+ .setParam(FACETS, PARAM_IMPACT_SOFTWARE_QUALITIES)
+ .executeProtobuf(SearchWsResponse.class);
+
+ assertThat(response.getIssuesList())
+ .extracting(Issue::getKey)
+ .containsExactlyInAnyOrder(issue1.getKey(), issue2.getKey(), issue3.getKey());
+
+ Optional<Common.Facet> first = response.getFacets().getFacetsList()
+ .stream().filter(facet -> facet.getProperty().equals(PARAM_IMPACT_SOFTWARE_QUALITIES))
+ .findFirst();
+ assertThat(first.get().getValuesList())
+ .extracting(Common.FacetValue::getVal, Common.FacetValue::getCount)
+ .containsExactlyInAnyOrder(
+ tuple("MAINTAINABILITY", 3L),
+ tuple("RELIABILITY", 3L),
+ tuple("SECURITY", 2L));
+ }
+
+ @Test
+ public void search_whenFilteredByImpactSeverities_shouldReturnImpactSoftwareQualitiesFacet() {
+ RuleDto rule = newIssueRule();
+ ComponentDto project = db.components().insertPublicProject("PROJECT_ID",
+ c -> c.setKey("PROJECT_KEY").setName("NAME_PROJECT_ID").setLongName("LONG_NAME_PROJECT_ID").setLanguage("java")).getMainBranchComponent();
+ ComponentDto file = db.components().insertComponent(newFileDto(project, null, "FILE_ID").setKey("FILE_KEY").setLanguage("java"));
+ IssueDto issue1 = db.issues().insertIssue(rule, project, file, i -> i
+ .addImpact(new ImpactDto(uuidFactory.create(), SoftwareQuality.SECURITY, org.sonar.api.issue.impact.Severity.HIGH))
+ .addImpact(new ImpactDto(uuidFactory.create(), SoftwareQuality.RELIABILITY, org.sonar.api.issue.impact.Severity.HIGH)));
+ IssueDto issue2 = db.issues().insertIssue(rule, project, file, i -> i
+ .addImpact(new ImpactDto(uuidFactory.create(), SoftwareQuality.RELIABILITY, org.sonar.api.issue.impact.Severity.HIGH)));
+ IssueDto issue3 = db.issues().insertIssue(rule, project, file, i -> i
+ .addImpact(new ImpactDto(uuidFactory.create(), SoftwareQuality.SECURITY, org.sonar.api.issue.impact.Severity.MEDIUM))
+ .addImpact(new ImpactDto(uuidFactory.create(), SoftwareQuality.RELIABILITY, org.sonar.api.issue.impact.Severity.LOW)));
+ indexPermissionsAndIssues();
+
+ SearchWsResponse response = ws.newRequest()
+ .setParam(PARAM_IMPACT_SEVERITIES, org.sonar.api.issue.impact.Severity.LOW.name())
+ .setParam(FACETS, PARAM_IMPACT_SOFTWARE_QUALITIES)
+ .executeProtobuf(SearchWsResponse.class);
+
+ assertThat(response.getIssuesList())
+ .extracting(Issue::getKey)
+ .containsExactlyInAnyOrder(issue3.getKey())
+ .doesNotContain(issue1.getKey(), issue2.getKey());
+
+ Optional<Common.Facet> first = response.getFacets().getFacetsList()
+ .stream().filter(facet -> facet.getProperty().equals(PARAM_IMPACT_SOFTWARE_QUALITIES))
+ .findFirst();
+ assertThat(first.get().getValuesList())
+ .extracting(Common.FacetValue::getVal, Common.FacetValue::getCount)
+ .containsExactlyInAnyOrder(
+ tuple("MAINTAINABILITY", 0L),
+ tuple("RELIABILITY", 1L),
+ tuple("SECURITY", 0L));
+ }
+
+ @Test
+ public void search_whenImpactSeveritiesFacetRequested_shouldReturnFacet() {
+ RuleDto rule = newIssueRule();
+ ComponentDto project = db.components().insertPublicProject("PROJECT_ID",
+ c -> c.setKey("PROJECT_KEY").setName("NAME_PROJECT_ID").setLongName("LONG_NAME_PROJECT_ID").setLanguage("java")).getMainBranchComponent();
+ ComponentDto file = db.components().insertComponent(newFileDto(project, null, "FILE_ID").setKey("FILE_KEY").setLanguage("java"));
+ IssueDto issue1 = db.issues().insertIssue(rule, project, file, i -> i.replaceAllImpacts(List.of(
+ new ImpactDto(uuidFactory.create(), SoftwareQuality.SECURITY, org.sonar.api.issue.impact.Severity.HIGH),
+ new ImpactDto(uuidFactory.create(), SoftwareQuality.RELIABILITY, org.sonar.api.issue.impact.Severity.HIGH))));
+ IssueDto issue2 = db.issues().insertIssue(rule, project, file, i -> i.replaceAllImpacts(List.of(
+ new ImpactDto(uuidFactory.create(), SoftwareQuality.RELIABILITY, org.sonar.api.issue.impact.Severity.HIGH))));
+ IssueDto issue3 = db.issues().insertIssue(rule, project, file, i -> i.replaceAllImpacts(List.of(
+ new ImpactDto(uuidFactory.create(), SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.LOW),
+ new ImpactDto(uuidFactory.create(), SoftwareQuality.SECURITY, org.sonar.api.issue.impact.Severity.MEDIUM),
+ new ImpactDto(uuidFactory.create(), SoftwareQuality.RELIABILITY, org.sonar.api.issue.impact.Severity.LOW))));
+ indexPermissionsAndIssues();
+
+ SearchWsResponse response = ws.newRequest()
+ .setParam(FACETS, PARAM_IMPACT_SEVERITIES)
+ .executeProtobuf(SearchWsResponse.class);
+
+ assertThat(response.getIssuesList())
+ .extracting(Issue::getKey)
+ .containsExactlyInAnyOrder(issue1.getKey(), issue2.getKey(), issue3.getKey());
+
+ Optional<Common.Facet> first = response.getFacets().getFacetsList()
+ .stream().filter(facet -> facet.getProperty().equals(PARAM_IMPACT_SEVERITIES))
+ .findFirst();
+ assertThat(first.get().getValuesList())
+ .extracting(Common.FacetValue::getVal, Common.FacetValue::getCount)
+ .containsExactlyInAnyOrder(
+ tuple("HIGH", 2L),
+ tuple("MEDIUM", 1L),
+ tuple("LOW", 1L));
+ }
+
+ @Test
+ public void search_whenFilteredByImpactSoftwareQualities_shouldReturnFacet() {
+ RuleDto rule = newIssueRule();
+ ComponentDto project = db.components().insertPublicProject("PROJECT_ID",
+ c -> c.setKey("PROJECT_KEY").setName("NAME_PROJECT_ID").setLongName("LONG_NAME_PROJECT_ID").setLanguage("java")).getMainBranchComponent();
+ ComponentDto file = db.components().insertComponent(newFileDto(project, null, "FILE_ID").setKey("FILE_KEY").setLanguage("java"));
+ IssueDto issue1 = db.issues().insertIssue(rule, project, file, i -> i.replaceAllImpacts(List.of(
+ new ImpactDto(uuidFactory.create(), SoftwareQuality.SECURITY, org.sonar.api.issue.impact.Severity.HIGH),
+ new ImpactDto(uuidFactory.create(), SoftwareQuality.RELIABILITY, org.sonar.api.issue.impact.Severity.HIGH))));
+ IssueDto issue2 = db.issues().insertIssue(rule, project, file, i -> i.replaceAllImpacts(List.of(
+ new ImpactDto(uuidFactory.create(), SoftwareQuality.RELIABILITY, org.sonar.api.issue.impact.Severity.HIGH))));
+ IssueDto issue3 = db.issues().insertIssue(rule, project, file, i -> i.replaceAllImpacts(List.of(
+ new ImpactDto(uuidFactory.create(), SoftwareQuality.MAINTAINABILITY, org.sonar.api.issue.impact.Severity.LOW),
+ new ImpactDto(uuidFactory.create(), SoftwareQuality.SECURITY, org.sonar.api.issue.impact.Severity.MEDIUM),
+ new ImpactDto(uuidFactory.create(), SoftwareQuality.RELIABILITY, org.sonar.api.issue.impact.Severity.LOW))));
+ indexPermissionsAndIssues();
+
+ SearchWsResponse response = ws.newRequest()
+ .setParam(PARAM_IMPACT_SOFTWARE_QUALITIES, SoftwareQuality.SECURITY.name())
+ .setParam(FACETS, PARAM_IMPACT_SEVERITIES)
+ .executeProtobuf(SearchWsResponse.class);
+
+ assertThat(response.getIssuesList())
+ .extracting(Issue::getKey)
+ .containsExactlyInAnyOrder(issue1.getKey(), issue3.getKey())
+ .doesNotContain(issue2.getKey());
+
+ Optional<Common.Facet> first = response.getFacets().getFacetsList()
+ .stream().filter(facet -> facet.getProperty().equals(PARAM_IMPACT_SEVERITIES))
+ .findFirst();
+ assertThat(first.get().getValuesList())
+ .extracting(Common.FacetValue::getVal, Common.FacetValue::getCount)
+ .containsExactlyInAnyOrder(
+ tuple("HIGH", 1L),
+ tuple("MEDIUM", 1L),
+ tuple("LOW", 0L));
+ }
+
+ @Test
public void issue_on_removed_file() {
RuleDto rule = newIssueRule();
ComponentDto project = db.components().insertPublicProject("PROJECT_ID", c -> c.setKey("PROJECT_KEY").setKey("PROJECT_KEY")).getMainBranchComponent();
@@ -1790,8 +1943,8 @@ public class SearchActionIT {
"additionalFields", "asc", "assigned", "assignees", "author", "components", "branch", "pullRequest", "createdAfter", "createdAt",
"createdBefore", "createdInLast", "directories", "facets", "files", "issues", "scopes", "languages", "onComponentOnly",
"p", "projects", "ps", "resolutions", "resolved", "rules", "s", "severities", "statuses", "tags", "types", "pciDss-3.2", "pciDss-4.0", "owaspAsvs-4.0",
- "owaspAsvsLevel", "owaspTop10",
- "owaspTop10-2021", "sansTop25", "cwe", "sonarsourceSecurity", "timeZone", "inNewCodePeriod", "codeVariants");
+ "owaspAsvsLevel", "owaspTop10", "owaspTop10-2021", "sansTop25", "cwe", "sonarsourceSecurity", "timeZone", "inNewCodePeriod", "codeVariants",
+ "cleanCodeAttributeCategories", "impactSeverities", "impactSoftwareQualities");
WebService.Param branch = def.param(PARAM_BRANCH);
assertThat(branch.isInternal()).isFalse();
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/AddCommentAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/AddCommentAction.java
index 18d576a1032..3be770a150f 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/AddCommentAction.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/AddCommentAction.java
@@ -72,6 +72,7 @@ public class AddCommentAction implements IssuesWsAction {
"Requires authentication and the following permission: 'Browse' on the project of the specified issue.")
.setSince("3.6")
.setChangelog(
+ new Change("10.2", "Add 'impacts', 'cleanCodeAttribute', 'cleanCodeAttributeCategory' fields to the response"),
new Change("9.6", "Response field 'ruleDescriptionContextKey' added"),
new Change("8.8", "The response field components.uuid is removed"),
new Change("6.3", "the response returns the issue with all its details"),
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/AssignAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/AssignAction.java
index 9e1c9ca3231..4b10e0c3344 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/AssignAction.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/AssignAction.java
@@ -75,6 +75,7 @@ public class AssignAction implements IssuesWsAction {
.setDescription("Assign/Unassign an issue. Requires authentication and Browse permission on project")
.setSince("3.6")
.setChangelog(
+ new Change("10.2", "Add 'impacts', 'cleanCodeAttribute', 'cleanCodeAttributeCategory' fields to the response"),
new Change("9.6", "Response field 'ruleDescriptionContextKey' added"),
new Change("8.8", "The response field components.uuid is removed"),
new Change("6.5", "the database ids of the components are removed from the response"),
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/DeleteCommentAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/DeleteCommentAction.java
index a6efef345bf..14d2ac2af14 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/DeleteCommentAction.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/DeleteCommentAction.java
@@ -60,6 +60,7 @@ public class DeleteCommentAction implements IssuesWsAction {
"Requires authentication and the following permission: 'Browse' on the project of the specified issue.")
.setSince("3.6")
.setChangelog(
+ new Change("10.2", "Add 'impacts', 'cleanCodeAttribute', 'cleanCodeAttributeCategory' fields to the response"),
new Change("9.6", "Response field 'ruleDescriptionContextKey' added"),
new Change("8.8", "The response field components.uuid is removed"),
new Change("6.5", "the response field components.uuid is deprecated. Use components.key instead."),
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/DoTransitionAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/DoTransitionAction.java
index 134e68f7e21..c9280230499 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/DoTransitionAction.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/DoTransitionAction.java
@@ -82,6 +82,7 @@ public class DoTransitionAction implements IssuesWsAction {
"The transitions involving security hotspots require the permission 'Administer Security Hotspot'.")
.setSince("3.6")
.setChangelog(
+ new Change("10.2", "Add 'impacts', 'cleanCodeAttribute', 'cleanCodeAttributeCategory' fields to the response"),
new Change("9.6", "Response field 'ruleDescriptionContextKey' added"),
new Change("8.8", "The response field components.uuid is removed"),
new Change("8.1", format("transitions '%s' and '%s' are no more supported", SET_AS_IN_REVIEW, OPEN_AS_VULNERABILITY)),
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/EditCommentAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/EditCommentAction.java
index b5b7fe2df24..603f8914777 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/EditCommentAction.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/EditCommentAction.java
@@ -66,6 +66,7 @@ public class EditCommentAction implements IssuesWsAction {
"Requires authentication and the following permission: 'Browse' on the project of the specified issue.")
.setSince("3.6")
.setChangelog(
+ new Change("10.2", "Add 'impacts', 'cleanCodeAttribute', 'cleanCodeAttributeCategory' fields to the response"),
new Change("9.6", "Response field 'ruleDescriptionContextKey' added"),
new Change("8.8", "The response field components.uuid is removed"),
new Change("6.3", "the response returns the issue with all its details"),
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchAction.java
index 674c74cca2e..f16f97a6d96 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchAction.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchAction.java
@@ -22,6 +22,7 @@ package org.sonar.server.issue.ws;
import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
@@ -32,7 +33,9 @@ import javax.annotation.Nullable;
import org.apache.lucene.search.TotalHits;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.search.SearchHit;
+import org.sonar.api.issue.impact.SoftwareQuality;
import org.sonar.api.rule.Severity;
+import org.sonar.api.rules.CleanCodeAttributeCategory;
import org.sonar.api.rules.RuleType;
import org.sonar.api.server.ws.Change;
import org.sonar.api.server.ws.Request;
@@ -94,6 +97,7 @@ import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_ASSIGNED;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_ASSIGNEES;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_AUTHOR;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_BRANCH;
+import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_CLEAN_CODE_ATTRIBUTE_CATEGORIES;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_CODE_VARIANTS;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_COMPONENTS;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_COMPONENT_KEYS;
@@ -122,6 +126,8 @@ import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_RULES;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SANS_TOP_25;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SCOPES;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SEVERITIES;
+import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_IMPACT_SOFTWARE_QUALITIES;
+import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_IMPACT_SEVERITIES;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SONARSOURCE_SECURITY;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_STATUSES;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_TAGS;
@@ -157,10 +163,15 @@ public class SearchAction implements IssuesWsAction {
PARAM_CWE,
PARAM_CREATED_AT,
PARAM_SONARSOURCE_SECURITY,
- PARAM_CODE_VARIANTS
+ PARAM_CODE_VARIANTS,
+ PARAM_CLEAN_CODE_ATTRIBUTE_CATEGORIES,
+ PARAM_IMPACT_SOFTWARE_QUALITIES,
+ PARAM_IMPACT_SEVERITIES
);
private static final String INTERNAL_PARAMETER_DISCLAIMER = "This parameter is mostly used by the Issues page, please prefer usage of the componentKeys parameter. ";
+ private static final String NEW_FACET_ADDED_MESSAGE = "Facet '%s' has been added";
+ private static final String NEW_PARAM_ADDED_MESSAGE = "Param '%s' has been added";
private static final Set<String> FACETS_REQUIRING_PROJECT = newHashSet(PARAM_FILES, PARAM_DIRECTORIES);
private final UserSession userSession;
@@ -194,6 +205,13 @@ public class SearchAction implements IssuesWsAction {
+ "<br/>When issue indexation is in progress returns 503 service unavailable HTTP code.")
.setSince("3.6")
.setChangelog(
+ new Change("10.2", "Add 'impacts', 'cleanCodeAttribute', 'cleanCodeAttributeCategory' fields to the response"),
+ new Change("10.2", format(NEW_PARAM_ADDED_MESSAGE, PARAM_IMPACT_SOFTWARE_QUALITIES)),
+ new Change("10.2", format(NEW_PARAM_ADDED_MESSAGE, PARAM_IMPACT_SEVERITIES)),
+ new Change("10.2", format(NEW_PARAM_ADDED_MESSAGE, PARAM_CLEAN_CODE_ATTRIBUTE_CATEGORIES)),
+ new Change("10.2", format(NEW_FACET_ADDED_MESSAGE, PARAM_IMPACT_SOFTWARE_QUALITIES)),
+ new Change("10.2", format(NEW_FACET_ADDED_MESSAGE, PARAM_IMPACT_SEVERITIES)),
+ new Change("10.2", format(NEW_FACET_ADDED_MESSAGE, PARAM_CLEAN_CODE_ATTRIBUTE_CATEGORIES)),
new Change("10.2", format("Parameter '%s' renamed to '%s'", PARAM_COMPONENT_KEYS, PARAM_COMPONENTS)),
new Change("10.1", "Add the 'codeVariants' parameter, facet and response field"),
new Change("10.0", "Parameter 'sansTop25' is deprecated"),
@@ -256,6 +274,21 @@ public class SearchAction implements IssuesWsAction {
.setDescription("Comma-separated list of severities")
.setExampleValue(Severity.BLOCKER + "," + Severity.CRITICAL)
.setPossibleValues(Severity.ALL);
+ action.createParam(PARAM_IMPACT_SOFTWARE_QUALITIES)
+ .setSince("10.2")
+ .setDescription("Comma-separated list of Software Qualities")
+ .setExampleValue(SoftwareQuality.MAINTAINABILITY + "," + SoftwareQuality.RELIABILITY)
+ .setPossibleValues(SoftwareQuality.values());
+ action.createParam(PARAM_IMPACT_SEVERITIES)
+ .setSince("10.2")
+ .setDescription("Comma-separated list of Software Quality Severities")
+ .setExampleValue(org.sonar.api.issue.impact.Severity.HIGH + "," + org.sonar.api.issue.impact.Severity.MEDIUM)
+ .setPossibleValues(org.sonar.api.issue.impact.Severity.values());
+ action.createParam(PARAM_CLEAN_CODE_ATTRIBUTE_CATEGORIES)
+ .setSince("10.2")
+ .setDescription("Comma-separated list of Clean Code Attribute Categories")
+ .setExampleValue(CleanCodeAttributeCategory.ADAPTABLE + "," + CleanCodeAttributeCategory.INTENTIONAL)
+ .setPossibleValues(CleanCodeAttributeCategory.values());
action.createParam(PARAM_STATUSES)
.setDescription("Comma-separated list of statuses")
.setExampleValue(STATUS_OPEN + "," + STATUS_REOPENED)
@@ -482,6 +515,10 @@ public class SearchAction implements IssuesWsAction {
private void completeFacets(Facets facets, SearchRequest request, IssueQuery query) {
addMandatoryValuesToFacet(facets, PARAM_SEVERITIES, Severity.ALL);
addMandatoryValuesToFacet(facets, PARAM_STATUSES, ISSUE_STATUSES);
+ addMandatoryValuesToFacet(facets, PARAM_IMPACT_SOFTWARE_QUALITIES, enumToStringCollection(SoftwareQuality.values()));
+ addMandatoryValuesToFacet(facets, PARAM_IMPACT_SEVERITIES, enumToStringCollection(org.sonar.api.issue.impact.Severity.values()));
+ addMandatoryValuesToFacet(facets, PARAM_CLEAN_CODE_ATTRIBUTE_CATEGORIES, enumToStringCollection(CleanCodeAttributeCategory.values()));
+
addMandatoryValuesToFacet(facets, PARAM_RESOLUTIONS, concat(singletonList(""), RESOLUTIONS));
addMandatoryValuesToFacet(facets, FACET_PROJECTS, query.projectUuids());
addMandatoryValuesToFacet(facets, PARAM_FILES, query.files());
@@ -512,6 +549,10 @@ public class SearchAction implements IssuesWsAction {
addMandatoryValuesToFacet(facets, PARAM_CODE_VARIANTS, request.getCodeVariants());
}
+ private static Collection<String> enumToStringCollection(Enum<?>... enumValues) {
+ return Arrays.stream(enumValues).map(Enum::name).toList();
+ }
+
private static void setTypesFacet(Facets facets) {
Map<String, Long> typeFacet = facets.get(PARAM_TYPES);
if (typeFacet != null) {
@@ -575,6 +616,9 @@ public class SearchAction implements IssuesWsAction {
.setRules(request.paramAsStrings(PARAM_RULES))
.setSort(request.param(Param.SORT))
.setSeverities(request.paramAsStrings(PARAM_SEVERITIES))
+ .setImpactSeverities(request.paramAsStrings(PARAM_IMPACT_SEVERITIES))
+ .setImpactSoftwareQualities(request.paramAsStrings(PARAM_IMPACT_SOFTWARE_QUALITIES))
+ .setCleanCodeAttributesCategories(request.paramAsStrings(PARAM_CLEAN_CODE_ATTRIBUTE_CATEGORIES))
.setStatuses(request.paramAsStrings(PARAM_STATUSES))
.setTags(request.paramAsStrings(PARAM_TAGS))
.setTypes(allRuleTypesExceptHotspotsIfEmpty(request.paramAsStrings(PARAM_TYPES)))
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchResponseFormat.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchResponseFormat.java
index 3622343ea1b..1852d6dcf9c 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchResponseFormat.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchResponseFormat.java
@@ -31,6 +31,7 @@ import java.util.stream.Collectors;
import org.sonar.api.resources.Language;
import org.sonar.api.resources.Languages;
import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rules.CleanCodeAttribute;
import org.sonar.api.rules.RuleType;
import org.sonar.api.utils.DateUtils;
import org.sonar.api.utils.Duration;
@@ -173,6 +174,21 @@ public class SearchResponseFormat {
issueBuilder.setKey(dto.getKey());
issueBuilder.setType(Common.RuleType.forNumber(dto.getType()));
+ CleanCodeAttribute cleanCodeAttribute = dto.getCleanCodeAttribute();
+ String cleanCodeAttributeString = cleanCodeAttribute != null ? cleanCodeAttribute.name() : null;
+ String cleanCodeAttributeCategoryString = cleanCodeAttribute != null ? cleanCodeAttribute.getAttributeCategory().name() : null;
+ if (cleanCodeAttributeString != null) {
+ issueBuilder.setCleanCodeAttribute(Common.CleanCodeAttribute.valueOf(cleanCodeAttributeString));
+ issueBuilder.setCleanCodeAttributeCategory(Common.CleanCodeAttributeCategory.valueOf(cleanCodeAttributeCategoryString));
+ }
+ issueBuilder.addAllImpacts(dto.getEffectiveImpacts().entrySet()
+ .stream()
+ .map(entry -> Common.Impact.newBuilder()
+ .setSoftwareQuality(Common.SoftwareQuality.valueOf(entry.getKey().name()))
+ .setSeverity(Common.ImpactSeverity.valueOf(entry.getValue().name()))
+ .build())
+ .toList());
+
ComponentDto component = data.getComponentByUuid(dto.getComponentUuid());
issueBuilder.setComponent(component.getKey());
setBranchOrPr(component, issueBuilder, data);
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SetSeverityAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SetSeverityAction.java
index 485d500773a..f5d81fd87c1 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SetSeverityAction.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SetSeverityAction.java
@@ -80,6 +80,7 @@ public class SetSeverityAction implements IssuesWsAction {
.setSince("3.6")
.setChangelog(
new Change("10.2", "This endpoint is now deprecated."),
+ new Change("10.2", "Add 'impacts', 'cleanCodeAttribute', 'cleanCodeAttributeCategory' fields to the response"),
new Change("9.6", "Response field 'ruleDescriptionContextKey' added"),
new Change("8.8", "The response field components.uuid is removed"),
new Change("6.5", "the database ids of the components are removed from the response"),
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SetTagsAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SetTagsAction.java
index 7bbc6824062..d3e8fa064fe 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SetTagsAction.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SetTagsAction.java
@@ -74,6 +74,7 @@ public class SetTagsAction implements IssuesWsAction {
.setDescription("Set tags on an issue. <br/>" +
"Requires authentication and Browse permission on project")
.setChangelog(
+ new Change("10.2", "Add 'impacts', 'cleanCodeAttribute', 'cleanCodeAttributeCategory' fields to the response"),
new Change("9.6", "Response field 'ruleDescriptionContextKey' added"),
new Change("8.8", "The response field components.uuid is removed"),
new Change("6.5", "the database ids of the components are removed from the response"),
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SetTypeAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SetTypeAction.java
index bf49aa4d895..ea22f193d3b 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SetTypeAction.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SetTypeAction.java
@@ -83,6 +83,7 @@ public class SetTypeAction implements IssuesWsAction {
"</ul>")
.setSince("5.5")
.setChangelog(
+ new Change("10.2", "Add 'impacts', 'cleanCodeAttribute', 'cleanCodeAttributeCategory' fields to the response"),
new Change("10.2", "This endpoint is now deprecated."),
new Change("9.6", "Response field 'ruleDescriptionContextKey' added"),
new Change("8.8", "The response field components.uuid is removed"),
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/pull/PullTaintActionProtobufObjectGenerator.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/pull/PullTaintActionProtobufObjectGenerator.java
index 4bf5de02faa..73d6916b69f 100644
--- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/pull/PullTaintActionProtobufObjectGenerator.java
+++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/pull/PullTaintActionProtobufObjectGenerator.java
@@ -26,6 +26,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
+import org.sonar.api.rules.CleanCodeAttribute;
import org.sonar.api.server.ServerSide;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
@@ -95,6 +96,20 @@ public class PullTaintActionProtobufObjectGenerator implements ProtobufObjectGen
taintBuilder.setSeverity(Common.Severity.valueOf(issueDto.getSeverity()));
}
taintBuilder.setType(Common.RuleType.forNumber(issueDto.getType()));
+ CleanCodeAttribute cleanCodeAttribute = issueDto.getCleanCodeAttribute();
+ String cleanCodeAttributeString = cleanCodeAttribute != null ? cleanCodeAttribute.name() : null;
+ String cleanCodeAttributeCategoryString = cleanCodeAttribute != null ? cleanCodeAttribute.getAttributeCategory().name() : null;
+ if (cleanCodeAttributeString != null) {
+ taintBuilder.setCleanCodeAttribute(Common.CleanCodeAttribute.valueOf(cleanCodeAttributeString));
+ taintBuilder.setCleanCodeAttributeCategory(Common.CleanCodeAttributeCategory.valueOf(cleanCodeAttributeCategoryString));
+ }
+ taintBuilder.addAllImpacts(issueDto.getEffectiveImpacts().entrySet()
+ .stream().map(entry -> Common.Impact.newBuilder()
+ .setSoftwareQuality(Common.SoftwareQuality.valueOf(entry.getKey().name()))
+ .setSeverity(Common.ImpactSeverity.valueOf(entry.getValue().name()))
+ .build())
+ .toList());
+
taintBuilder.setClosed(false);
taintBuilder.setMainLocation(locationBuilder.build());
issueDto.getOptionalRuleDescriptionContextKey().ifPresent(taintBuilder::setRuleDescriptionContextKey);
diff --git a/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/add_comment-example.json b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/add_comment-example.json
index 38de0633acf..f4fa2b2f815 100644
--- a/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/add_comment-example.json
+++ b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/add_comment-example.json
@@ -2,6 +2,14 @@
"issue": {
"key": "AVibidgv1LF0E-ru2DVv",
"rule": "squid:S2301",
+ "cleanCodeAttribute": "CLEAR",
+ "cleanCodeAttributeCategory": "INTENTIONAL",
+ "impacts": [
+ {
+ "softwareQuality": "SECURITY",
+ "severity": "HIGH"
+ }
+ ],
"severity": "MAJOR",
"component": "org.sonarsource.sonarlint.intellij:sonarlint-intellij:src/main/java/org/sonarlint/intellij/core/ServerIssueUpdater.java",
"project": "org.sonarsource.sonarlint.intellij:sonarlint-intellij",
diff --git a/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/assign-example.json b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/assign-example.json
index f591d112dbb..79b0025dadf 100644
--- a/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/assign-example.json
+++ b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/assign-example.json
@@ -3,6 +3,14 @@
"key": "AVibidgv1LF0E-ru2DVv",
"rule": "squid:S2301",
"severity": "MAJOR",
+ "cleanCodeAttribute": "CLEAR",
+ "cleanCodeAttributeCategory": "INTENTIONAL",
+ "impacts": [
+ {
+ "softwareQuality": "SECURITY",
+ "severity": "HIGH"
+ }
+ ],
"component": "org.sonarsource.sonarlint.intellij:sonarlint-intellij:src/main/java/org/sonarlint/intellij/core/ServerIssueUpdater.java",
"project": "org.sonarsource.sonarlint.intellij:sonarlint-intellij",
"line": 78,
diff --git a/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/delete_comment-example.json b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/delete_comment-example.json
index f591d112dbb..f6322816f86 100644
--- a/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/delete_comment-example.json
+++ b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/delete_comment-example.json
@@ -2,6 +2,14 @@
"issue": {
"key": "AVibidgv1LF0E-ru2DVv",
"rule": "squid:S2301",
+ "cleanCodeAttribute": "CLEAR",
+ "cleanCodeAttributeCategory": "INTENTIONAL",
+ "impacts": [
+ {
+ "softwareQuality": "SECURITY",
+ "severity": "HIGH"
+ }
+ ],
"severity": "MAJOR",
"component": "org.sonarsource.sonarlint.intellij:sonarlint-intellij:src/main/java/org/sonarlint/intellij/core/ServerIssueUpdater.java",
"project": "org.sonarsource.sonarlint.intellij:sonarlint-intellij",
diff --git a/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/do_transition-example.json b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/do_transition-example.json
index f591d112dbb..f6322816f86 100644
--- a/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/do_transition-example.json
+++ b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/do_transition-example.json
@@ -2,6 +2,14 @@
"issue": {
"key": "AVibidgv1LF0E-ru2DVv",
"rule": "squid:S2301",
+ "cleanCodeAttribute": "CLEAR",
+ "cleanCodeAttributeCategory": "INTENTIONAL",
+ "impacts": [
+ {
+ "softwareQuality": "SECURITY",
+ "severity": "HIGH"
+ }
+ ],
"severity": "MAJOR",
"component": "org.sonarsource.sonarlint.intellij:sonarlint-intellij:src/main/java/org/sonarlint/intellij/core/ServerIssueUpdater.java",
"project": "org.sonarsource.sonarlint.intellij:sonarlint-intellij",
diff --git a/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/edit_comment-example.json b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/edit_comment-example.json
index f591d112dbb..79b0025dadf 100644
--- a/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/edit_comment-example.json
+++ b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/edit_comment-example.json
@@ -3,6 +3,14 @@
"key": "AVibidgv1LF0E-ru2DVv",
"rule": "squid:S2301",
"severity": "MAJOR",
+ "cleanCodeAttribute": "CLEAR",
+ "cleanCodeAttributeCategory": "INTENTIONAL",
+ "impacts": [
+ {
+ "softwareQuality": "SECURITY",
+ "severity": "HIGH"
+ }
+ ],
"component": "org.sonarsource.sonarlint.intellij:sonarlint-intellij:src/main/java/org/sonarlint/intellij/core/ServerIssueUpdater.java",
"project": "org.sonarsource.sonarlint.intellij:sonarlint-intellij",
"line": 78,
diff --git a/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/pull-taint-example.proto b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/pull-taint-example.proto
index 05fb3bf1677..c5c802ebda4 100644
--- a/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/pull-taint-example.proto
+++ b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/pull-taint-example.proto
@@ -1,20 +1,23 @@
-# The response contains a single protocol buffer message: TaintVulnerabilityPullQueryTimestamp followed by 0..n number of TaintLite protocol buffer messages.
+# The response contains a single protocol buffer message: TaintVulnerabilityPullQueryTimestamp followed by 0..n number of TaintVulnerabilityLite protocol buffer messages.
message TaintVulnerabilityPullQueryTimestamp {
required int64 queryTimestamp = 1;
}
-message TaintLite {
+message TaintVulnerabilityLite {
required string key = 1;
optional int64 creationDate = 2;
optional bool resolved = 3;
optional string ruleKey = 4;
- optional string severity = 5;
- optional string type = 6;
+ optional sonarqube.ws.commons.Severity severity = 5;
+ optional sonarqube.ws.commons.RuleType type = 6;
optional Location mainLocation = 7;
optional bool closed = 8;
- optional Flow flows = 9;
+ repeated Flow flows = 9;
optional bool assignedToSubscribedUser = 10;
optional string ruleDescriptionContextKey = 11;
+ optional sonarqube.ws.commons.CleanCodeAttribute cleanCodeAttribute = 12;
+ optional sonarqube.ws.commons.CleanCodeAttributeCategory cleanCodeAttributeCategory = 13;
+ repeated sonarqube.ws.commons.Impact impacts = 14;
}
message Location {
diff --git a/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/search-example.json b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/search-example.json
index 24d91b68761..0a664cad9dd 100644
--- a/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/search-example.json
+++ b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/search-example.json
@@ -13,6 +13,14 @@
"status": "RESOLVED",
"resolution": "WONTFIX",
"severity": "MAJOR",
+ "cleanCodeAttribute": "CLEAR",
+ "cleanCodeAttributeCategory": "INTENTIONAL",
+ "impacts": [
+ {
+ "softwareQuality": "SECURITY",
+ "severity": "HIGH"
+ }
+ ],
"message": "Remove this unused private \"getKee\" method.",
"messageFormattings": [
{
diff --git a/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/set_severity-example.json b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/set_severity-example.json
index f591d112dbb..79b0025dadf 100644
--- a/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/set_severity-example.json
+++ b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/set_severity-example.json
@@ -3,6 +3,14 @@
"key": "AVibidgv1LF0E-ru2DVv",
"rule": "squid:S2301",
"severity": "MAJOR",
+ "cleanCodeAttribute": "CLEAR",
+ "cleanCodeAttributeCategory": "INTENTIONAL",
+ "impacts": [
+ {
+ "softwareQuality": "SECURITY",
+ "severity": "HIGH"
+ }
+ ],
"component": "org.sonarsource.sonarlint.intellij:sonarlint-intellij:src/main/java/org/sonarlint/intellij/core/ServerIssueUpdater.java",
"project": "org.sonarsource.sonarlint.intellij:sonarlint-intellij",
"line": 78,
diff --git a/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/set_tags-example.json b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/set_tags-example.json
index d690eb9b06a..ac3bf2c81a9 100644
--- a/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/set_tags-example.json
+++ b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/set_tags-example.json
@@ -3,6 +3,14 @@
"key": "AVibidgv1LF0E-ru2DVv",
"rule": "squid:S2301",
"severity": "MAJOR",
+ "cleanCodeAttribute": "CLEAR",
+ "cleanCodeAttributeCategory": "INTENTIONAL",
+ "impacts": [
+ {
+ "softwareQuality": "SECURITY",
+ "severity": "HIGH"
+ }
+ ],
"component": "org.sonarsource.sonarlint.intellij:sonarlint-intellij:src/main/java/org/sonarlint/intellij/core/ServerIssueUpdater.java",
"project": "org.sonarsource.sonarlint.intellij:sonarlint-intellij",
"line": 78,
diff --git a/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/set_type-example.json b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/set_type-example.json
index f591d112dbb..79b0025dadf 100644
--- a/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/set_type-example.json
+++ b/server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/set_type-example.json
@@ -3,6 +3,14 @@
"key": "AVibidgv1LF0E-ru2DVv",
"rule": "squid:S2301",
"severity": "MAJOR",
+ "cleanCodeAttribute": "CLEAR",
+ "cleanCodeAttributeCategory": "INTENTIONAL",
+ "impacts": [
+ {
+ "softwareQuality": "SECURITY",
+ "severity": "HIGH"
+ }
+ ],
"component": "org.sonarsource.sonarlint.intellij:sonarlint-intellij:src/main/java/org/sonarlint/intellij/core/ServerIssueUpdater.java",
"project": "org.sonarsource.sonarlint.intellij:sonarlint-intellij",
"line": 78,
diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/SearchResponseFormatFormatOperationTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/SearchResponseFormatFormatOperationTest.java
index cebac2d567c..070aec1d96c 100644
--- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/SearchResponseFormatFormatOperationTest.java
+++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/SearchResponseFormatFormatOperationTest.java
@@ -30,6 +30,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnitRunner;
import org.sonar.api.resources.Languages;
+import org.sonar.api.rules.CleanCodeAttribute;
import org.sonar.api.utils.Duration;
import org.sonar.api.utils.Durations;
import org.sonar.db.DbTester;
@@ -51,6 +52,7 @@ import static java.lang.System.currentTimeMillis;
import static java.util.stream.Collectors.toList;
import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.groups.Tuple.tuple;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
@@ -122,6 +124,8 @@ public class SearchResponseFormatFormatOperationTest {
private void assertIssueEqualsIssueDto(Issue issue, IssueDto issueDto) {
assertThat(issue.getKey()).isEqualTo(issueDto.getKey());
+ assertThat(issue.getCleanCodeAttribute()).isEqualTo(Common.CleanCodeAttribute.valueOf(issueDto.getCleanCodeAttribute().name()));
+ assertThat(issue.getCleanCodeAttributeCategory()).isEqualTo(Common.CleanCodeAttributeCategory.valueOf(issueDto.getCleanCodeAttribute().getAttributeCategory().name()));
assertThat(issue.getType().getNumber()).isEqualTo(issueDto.getType());
assertThat(issue.getComponent()).isEqualTo(issueDto.getComponentKey());
assertThat(issue.getRule()).isEqualTo(issueDto.getRuleKey().toString());
@@ -140,6 +144,13 @@ public class SearchResponseFormatFormatOperationTest {
assertThat(issue.getQuickFixAvailable()).isEqualTo(issueDto.isQuickFixAvailable());
assertThat(issue.getRuleDescriptionContextKey()).isEqualTo(issueDto.getOptionalRuleDescriptionContextKey().orElse(null));
assertThat(new ArrayList<>(issue.getCodeVariantsList())).containsExactlyInAnyOrderElementsOf(issueDto.getCodeVariants());
+ assertThat(issue.getImpactsList())
+ .extracting(Common.Impact::getSoftwareQuality, Common.Impact::getSeverity)
+ .containsExactlyInAnyOrderElementsOf(issueDto.getEffectiveImpacts()
+ .entrySet()
+ .stream()
+ .map(entry -> tuple(Common.SoftwareQuality.valueOf(entry.getKey().name()), Common.ImpactSeverity.valueOf(entry.getValue().name())))
+ .collect(toList()));
}
@Test
@@ -295,6 +306,7 @@ public class SearchResponseFormatFormatOperationTest {
componentDto = component;
issueDto = newIssue(ruleDto, component.branchUuid(), component.getKey(), component)
.setType(CODE_SMELL)
+ .setCleanCodeAttribute(CleanCodeAttribute.CLEAR)
.setRuleDescriptionContextKey("context_key_" + randomAlphanumeric(5))
.setAssigneeUuid(userDto.getUuid())
.setResolution("resolution_" + randomAlphanumeric(5))