]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-20021 Update Issues web apis with Clean Code Taxonomy
authorJacek Poreda <jacek.poreda@sonarsource.com>
Thu, 10 Aug 2023 08:33:25 +0000 (10:33 +0200)
committersonartech <sonartech@sonarsource.com>
Fri, 18 Aug 2023 20:02:49 +0000 (20:02 +0000)
37 files changed:
server/sonar-db-dao/src/main/java/org/sonar/db/issue/ImpactDto.java
server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml
server/sonar-db-dao/src/test/java/org/sonar/db/rule/RuleDtoTest.java
server/sonar-server-common/src/main/java/org/sonar/server/es/Facets.java
server/sonar-server-common/src/main/java/org/sonar/server/issue/SearchRequest.java
server/sonar-server-common/src/test/java/org/sonar/server/issue/SearchRequestTest.java
server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueIndex.java
server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQuery.java
server/sonar-webserver-es/src/main/java/org/sonar/server/issue/index/IssueQueryFactory.java
server/sonar-webserver-es/src/test/java/org/sonar/server/issue/index/IssueIndexFacetsTest.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/PullTaintActionIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/SearchActionIT.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/AddCommentAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/AssignAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/DeleteCommentAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/DoTransitionAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/EditCommentAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchResponseFormat.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SetSeverityAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SetTagsAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SetTypeAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/pull/PullTaintActionProtobufObjectGenerator.java
server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/add_comment-example.json
server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/assign-example.json
server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/delete_comment-example.json
server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/do_transition-example.json
server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/edit_comment-example.json
server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/pull-taint-example.proto
server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/search-example.json
server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/set_severity-example.json
server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/set_tags-example.json
server/sonar-webserver-webapi/src/main/resources/org/sonar/server/issue/ws/set_type-example.json
server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/SearchResponseFormatFormatOperationTest.java
sonar-ws/src/main/java/org/sonarqube/ws/client/issue/IssuesWsParameters.java
sonar-ws/src/main/protobuf/ws-commons.proto
sonar-ws/src/main/protobuf/ws-issues.proto

index b62469b68dbab6c2c18272af370c5119aa6e4d61..82da4d3b0b453a6dfb5c667ed022478918a1f9db 100644 (file)
@@ -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;
   }
index 17889d58ee31313fe22a81de15c2d507f01d4ce9..a731ea97d422ffd5efa7d8fcd8add99383427051 100644 (file)
     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
         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
        <if test="changedSince != null">
         AND i.issue_update_date &gt;= #{changedSince,jdbcType=BIGINT}
        </if>
+    order by i.kee
   </select>
 
   <select id="selectRecentlyClosedIssues" resultType="string">
index d0ee6dcd3dc7ca4bdf75ceedda44e652d1947341..a821a58b64500ffda8786a23e7a18b7d09ab05d9 100644 (file)
@@ -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);
   }
-
 }
index 23e5dbb95c69b2b3bbd6ed8ecceaf28bd94598dc..435977d7ab83a2dd802ac69859fdd65dd5a8ba4e 100644 (file)
@@ -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) {
index d22ab314330aaf16818db61012e425de4658e1aa..3eb488a5951970af06d67cbb3c79d8a08b99ae34 100644 (file)
@@ -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;
+  }
 }
index f5f7f0620c1891e4649f8e2a4895ad55077beab7..63812baa036a6394ad3e377c42524727b4c0e3f1 100644 (file)
@@ -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
index a8991a87577e4b5ccd9cffb5f419c49820e4f8b9..f6336ef6eda1f04f0453d47c9c7d01a725e64cb9 100644 (file)
@@ -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);
   }
 
index 11cd3614c1f26c6ef4901803db778340a88b4aa0..33eefc1e188eff5e850a6b3caa72195b99cc7322 100644 (file)
@@ -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);
index fb2ce21a14ac1fbdbdb22f1d564338df45923a98..5386e7c302ce731e0a18443c6fff2de9d78bdbd2 100644 (file)
@@ -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())
index cc9ede60838681122bf94e4ba0e22c1e59a9bc6a..1171923de9757ad3096d0abfef89a4bd6d617e13 100644 (file)
@@ -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));
index 6cff8f8be4789679c149612127144c48418d3534..4e6e21e2851d21fe4f9ae6900193386c202d3b52 100644 (file)
@@ -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());
index ad598866ccbc7518fe16f4601b51060056be44be..87e055fa282a7a37db5fca2e27768f7e5f4a3440 100644 (file)
@@ -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
@@ -582,6 +589,152 @@ public class SearchActionIT {
       .assertJson(this.getClass(), "search_by_variants_with_facets.json");
   }
 
+  @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();
@@ -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();
index 18d576a1032c077dd3225853225b55a48e8709bd..3be770a150fc5b67b9e74a1e806a507b21482cad 100644 (file)
@@ -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"),
index 9e1c9ca3231c0e7a2aef6160899650df3e294b55..4b10e0c3344a29a26d37eced5f907bb28852e698 100644 (file)
@@ -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"),
index a6efef345bf4eec559fc16df80a7a6a9eee0564d..14d2ac2af140cd1182cad72ffd03b8542d0da289 100644 (file)
@@ -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."),
index 134e68f7e21e758fb6d3196e24a61393e11bcf1d..c92802304992f7d582cb73a0107965f34877fce0 100644 (file)
@@ -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)),
index b5b7fe2df241c974a61e1831f76a90db47dcb754..603f8914777ad936e2b45b501557e4c82a1c7dd1 100644 (file)
@@ -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"),
index 674c74cca2e549fac635434621a562572c92c499..f16f97a6d961800c2d1986e79aeb3ed156224157 100644 (file)
@@ -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)))
index 3622343ea1bba97c548c89b11b8f119f97f13f3c..1852d6dcf9c4f276ba17c81fde43032525e27bbd 100644 (file)
@@ -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);
index 485d500773acc8ef70cd97f604790ad844d4e0f2..f5d81fd87c13f100b93a1bf1b73a9059eb26c439 100644 (file)
@@ -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"),
index 7bbc682406239b22bcdc950b0758c80892b3fb36..d3e8fa064fe59a2cbc01a6cc45d27fcaee81e66e 100644 (file)
@@ -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"),
index bf49aa4d89558db8da7b2e9f95faeb20de69eaf6..ea22f193d3bf2d746561e0ceb1b2c3e64e009aa7 100644 (file)
@@ -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"),
index 4bf5de02faaf916c18044bcf0ca41a16ba63f5bc..73d6916b69fdf3c93b51c5a5115bb75c61b749c4 100644 (file)
@@ -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);
index 38de0633acf76fc2110c597b35de3b18e5f29f0a..f4fa2b2f815bd8b92a738f7c0ed22efb4c85872d 100644 (file)
@@ -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",
index f591d112dbbe4587744276b4ff852a8f7c3edbb4..79b0025dadf24100b474ec06d64f2799e2771402 100644 (file)
@@ -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,
index f591d112dbbe4587744276b4ff852a8f7c3edbb4..f6322816f8687a6f4cbe0888099b1d410c77c539 100644 (file)
@@ -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",
index f591d112dbbe4587744276b4ff852a8f7c3edbb4..f6322816f8687a6f4cbe0888099b1d410c77c539 100644 (file)
@@ -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",
index f591d112dbbe4587744276b4ff852a8f7c3edbb4..79b0025dadf24100b474ec06d64f2799e2771402 100644 (file)
@@ -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,
index 05fb3bf1677846e977df1a808329f0bcc4628bfc..c5c802ebda484c52d34506ab95ba272d1bac4cda 100644 (file)
@@ -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 {
index 24d91b687615106a5316affc0647e05d1d1154ff..0a664cad9dd4ce061ea10507dc08c9eab3464f5c 100644 (file)
       "status": "RESOLVED",
       "resolution": "WONTFIX",
       "severity": "MAJOR",
+      "cleanCodeAttribute": "CLEAR",
+      "cleanCodeAttributeCategory": "INTENTIONAL",
+      "impacts": [
+        {
+          "softwareQuality": "SECURITY",
+          "severity": "HIGH"
+        }
+      ],
       "message": "Remove this unused private \"getKee\" method.",
       "messageFormattings": [
         {
index f591d112dbbe4587744276b4ff852a8f7c3edbb4..79b0025dadf24100b474ec06d64f2799e2771402 100644 (file)
@@ -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,
index d690eb9b06a826808e100bab1d58e36f0b245924..ac3bf2c81a9c35052a9da20af462e818833a4b09 100644 (file)
@@ -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,
index f591d112dbbe4587744276b4ff852a8f7c3edbb4..79b0025dadf24100b474ec06d64f2799e2771402 100644 (file)
@@ -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,
index cebac2d567c6cef6d9a7a072a402d6331531d38c..070aec1d96c252348ee7f993b15ef1f839127bb1 100644 (file)
@@ -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))
index 176b205b34922c1440198f11a7471af3a386b6b5..3adaa21805b1562b43236980650c5eea1a7cc304 100644 (file)
@@ -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";
index a8ba062db9e18676015c529d1091215232bfa39c..2c542834c1080348fe4c69f476d4a86cac3d1169 100644 (file)
@@ -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
index d545fe7e75d71422ac9622c29809a9afc69ba867..950853c81565567d0d99ea7426c2dec5cf473374 100644 (file)
@@ -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 {