diff options
author | Jacek Poreda <jacek.poreda@sonarsource.com> | 2023-08-10 10:33:25 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2023-08-18 20:02:49 +0000 |
commit | 177da8d8b082ad4688524d8f3ecea9bee624d09b (patch) | |
tree | 0923e3ac529b086de0f9d42c0e8f3129e5c6e079 | |
parent | 2b456da715b2650a1519a4d273119b01388396df (diff) | |
download | sonarqube-177da8d8b082ad4688524d8f3ecea9bee624d09b.tar.gz sonarqube-177da8d8b082ad4688524d8f3ecea9bee624d09b.zip |
SONAR-20021 Update Issues web apis with Clean Code Taxonomy
37 files changed, 490 insertions, 54 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 >= #{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)) diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/issue/IssuesWsParameters.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/issue/IssuesWsParameters.java index 176b205b349..3adaa21805b 100644 --- a/sonar-ws/src/main/java/org/sonarqube/ws/client/issue/IssuesWsParameters.java +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/issue/IssuesWsParameters.java @@ -54,10 +54,8 @@ public class IssuesWsParameters { public static final String PARAM_TYPE = "type"; public static final String PARAM_ISSUES = "issues"; public static final String PARAM_SEVERITIES = "severities"; - public static final String PARAM_SOFTWARE_QUALITIES = "softwareQualities"; - - //TODO: To be discussed for the naming - public static final String PARAM_SOFTWARE_QUALITIES_SEVERTIIES = "softwareQualitiesSeverities"; + 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 PARAM_STATUSES = "statuses"; public static final String PARAM_RESOLUTIONS = "resolutions"; diff --git a/sonar-ws/src/main/protobuf/ws-commons.proto b/sonar-ws/src/main/protobuf/ws-commons.proto index a8ba062db9e..2c542834c10 100644 --- a/sonar-ws/src/main/protobuf/ws-commons.proto +++ b/sonar-ws/src/main/protobuf/ws-commons.proto @@ -78,6 +78,47 @@ enum RuleScope { ALL = 2; } +enum CleanCodeAttribute { + CONVENTIONAL = 0; + FORMATTED = 1; + IDENTIFIABLE = 2; + CLEAR = 3; + COMPLETE = 4; + EFFICIENT = 5; + LOGICAL = 6; + DISTINCT = 7; + FOCUSED = 8; + MODULAR = 9; + TESTED = 10; + LAWFUL = 11; + RESPECTFUL = 12; + TRUSTWORTHY = 13; +} + +enum CleanCodeAttributeCategory { + ADAPTABLE = 0; + CONSISTENT = 1; + INTENTIONAL = 2; + RESPONSIBLE = 3; +} + +message Impact { + required SoftwareQuality softwareQuality = 1; + required ImpactSeverity severity = 2; +} + +enum SoftwareQuality { + MAINTAINABILITY = 0; + RELIABILITY = 1; + SECURITY = 2; +} + +enum ImpactSeverity { + LOW = 0; + MEDIUM = 1; + HIGH = 2; +} + // Lines start at 1 and line offsets start at 0 message TextRange { // Start line. Should never be absent diff --git a/sonar-ws/src/main/protobuf/ws-issues.proto b/sonar-ws/src/main/protobuf/ws-issues.proto index d545fe7e75d..950853c8156 100644 --- a/sonar-ws/src/main/protobuf/ws-issues.proto +++ b/sonar-ws/src/main/protobuf/ws-issues.proto @@ -163,6 +163,9 @@ message Issue { repeated sonarqube.ws.commons.MessageFormatting messageFormattings = 38; repeated string codeVariants = 39; + optional sonarqube.ws.commons.CleanCodeAttribute cleanCodeAttribute = 40; + optional sonarqube.ws.commons.CleanCodeAttributeCategory cleanCodeAttributeCategory = 41; + repeated sonarqube.ws.commons.Impact impacts = 42; } message Transitions { @@ -289,6 +292,9 @@ message TaintVulnerabilityLite { 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 Flow { |