]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-20424 Store and populate within issue `clean_code_attribute`
authorJacek <jacek.poreda@sonarsource.com>
Thu, 14 Sep 2023 12:07:05 +0000 (14:07 +0200)
committersonartech <sonartech@sonarsource.com>
Thu, 21 Sep 2023 20:02:44 +0000 (20:02 +0000)
25 files changed:
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueLifecycle.java
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/util/cache/ProtobufIssueDiskCache.java
server/sonar-ce-task-projectanalysis/src/main/protobuf/issue_cache.proto
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IssueLifecycleTest.java
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/util/cache/ProtobufIssueDiskCacheTest.java
server/sonar-db-dao/src/it/java/org/sonar/db/issue/IssueDaoIT.java
server/sonar-db-dao/src/main/java/org/sonar/db/issue/IndexedIssueDto.java
server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDto.java
server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml
server/sonar-db-dao/src/schema/schema-sq.ddl
server/sonar-db-dao/src/test/java/org/sonar/db/issue/IndexedIssueDtoTest.java
server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDtoTest.java
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v103/AddCleanCodeAttributeColumnInIssuesTable.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v103/DbVersion103.java
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v103/AddCleanCodeAttributeColumnInIssuesTableTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v103/AddCleanCodeAttributeColumnInIssuesTableTest/schema.sql [new file with mode: 0644]
server/sonar-server-common/src/main/java/org/sonar/server/issue/IssueFieldsSetter.java
server/sonar-server-common/src/main/java/org/sonar/server/issue/index/IssueIteratorForSingleChunk.java
server/sonar-server-common/src/test/java/org/sonar/server/issue/IssueFieldsSetterTest.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/WebIssueStorageIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/PullTaintActionIT.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/pull/PullTaintActionProtobufObjectGenerator.java
server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/SearchResponseFormatFormatOperationTest.java
sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java

index 39bffffc47e364dce18b6f34080ca6a347e7946c..c1f7f2e72cd5c732a061540d7b72b3098e599e11 100644 (file)
@@ -26,6 +26,7 @@ import java.util.Optional;
 import javax.inject.Inject;
 import org.jetbrains.annotations.NotNull;
 import org.sonar.api.issue.Issue;
+import org.sonar.api.rules.CleanCodeAttribute;
 import org.sonar.api.rules.RuleType;
 import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder;
 import org.sonar.core.issue.DefaultIssue;
@@ -38,6 +39,7 @@ import org.sonar.server.issue.IssueFieldsSetter;
 import org.sonar.server.issue.workflow.IssueWorkflow;
 
 import static java.util.Objects.requireNonNull;
+import static java.util.Optional.ofNullable;
 import static org.sonar.core.issue.IssueChangeContext.issueChangeContextByScanBuilder;
 
 /**
@@ -83,6 +85,11 @@ public class IssueLifecycle {
     issue.setEffort(debtCalculator.calculate(issue));
     setType(issue, rule);
     setStatus(issue, rule);
+    setCleanCodeAttribute(issue, rule);
+  }
+
+  private static void setCleanCodeAttribute(DefaultIssue issue, Rule rule) {
+    issue.setCleanCodeAttribute(ofNullable(rule.cleanCodeAttribute()).orElse(CleanCodeAttribute.defaultCleanCodeAttribute()));
   }
 
   private static void setType(DefaultIssue issue, Rule rule) {
@@ -129,6 +136,7 @@ public class IssueLifecycle {
       to.setManualSeverity(true);
       to.setSeverity(from.severity());
     }
+    to.setCleanCodeAttribute(from.getCleanCodeAttribute());
     copyChangesOfIssueFromOtherBranch(to, from);
   }
 
@@ -181,6 +189,7 @@ public class IssueLifecycle {
       raw.setChanged(true);
     }
     setType(raw, rule);
+    setCleanCodeAttribute(raw, rule);
     copyFields(raw, base);
     base.changes().forEach(raw::addChange);
 
@@ -204,6 +213,7 @@ public class IssueLifecycle {
     updater.setPastEffort(raw, base.effort(), changeContext);
     updater.setCodeVariants(raw, requireNonNull(base.codeVariants()), changeContext);
     updater.setImpacts(raw, base.impacts(), changeContext);
+    updater.setCleanCodeAttribute(raw, base.getCleanCodeAttribute(), changeContext);
   }
 
   public void doAutomaticTransition(DefaultIssue issue) {
index 4e015401a3b5555524a7dfb0512bef1c4b688c2a..39f5c138bef5a8b4271764d51874ad010a7c6161 100644 (file)
@@ -35,6 +35,7 @@ import javax.annotation.CheckForNull;
 import org.sonar.api.issue.impact.Severity;
 import org.sonar.api.issue.impact.SoftwareQuality;
 import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rules.CleanCodeAttribute;
 import org.sonar.api.rules.RuleType;
 import org.sonar.api.utils.Duration;
 import org.sonar.api.utils.System2;
@@ -138,6 +139,7 @@ public class ProtobufIssueDiskCache implements DiskCache<DefaultIssue> {
     defaultIssue.setSelectedAt(next.hasSelectedAt() ? next.getSelectedAt() : null);
     defaultIssue.setQuickFixAvailable(next.getQuickFixAvailable());
     defaultIssue.setIsNoLongerNewCodeReferenceIssue(next.getIsNoLongerNewCodeReferenceIssue());
+    defaultIssue.setCleanCodeAttribute(next.hasCleanCodeAttribute() ? CleanCodeAttribute.valueOf(next.getCleanCodeAttribute()) : null);
     if (next.hasAnticipatedTransitionUuid()) {
       defaultIssue.setAnticipatedTransitionUuid(next.getAnticipatedTransitionUuid());
     }
@@ -156,6 +158,7 @@ public class ProtobufIssueDiskCache implements DiskCache<DefaultIssue> {
     builder.clear();
     builder.setKey(defaultIssue.key());
     builder.setRuleType(defaultIssue.type().getDbConstant());
+    ofNullable(defaultIssue.getCleanCodeAttribute()).ifPresent(value -> builder.setCleanCodeAttribute(value.name()));
     ofNullable(defaultIssue.componentUuid()).ifPresent(builder::setComponentUuid);
     builder.setComponentKey(defaultIssue.componentKey());
     builder.setProjectUuid(defaultIssue.projectUuid());
index 77eabd0a214c57d718f935941192285d16fbcbcd..8b8c05049f8245a24d2345e12f64ad46931bf9df 100644 (file)
@@ -85,6 +85,7 @@ message Issue {
   optional string assigneeLogin = 47;
   optional string anticipatedTransitionUuid = 48;
   repeated Impact impacts = 49;
+  optional string cleanCodeAttribute = 50;
 }
 
 message Comment {
index 8c925bc46fb140bafa30fcd8819dfefe32ed7ac2..1f8758d5533e29b50216fa632923bbd853e899dd 100644 (file)
@@ -25,6 +25,7 @@ import org.junit.Rule;
 import org.junit.Test;
 import org.sonar.api.issue.impact.Severity;
 import org.sonar.api.issue.impact.SoftwareQuality;
+import org.sonar.api.rules.CleanCodeAttribute;
 import org.sonar.api.rules.RuleType;
 import org.sonar.api.utils.Duration;
 import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolderRule;
@@ -93,6 +94,7 @@ public class IssueLifecycleTest {
     assertThat(issue.effort()).isEqualTo(DEFAULT_DURATION);
     assertThat(issue.isNew()).isTrue();
     assertThat(issue.isCopied()).isFalse();
+    assertThat(issue.getCleanCodeAttribute()).isEqualTo(CleanCodeAttribute.CONVENTIONAL);
   }
 
   @Test
@@ -123,6 +125,7 @@ public class IssueLifecycleTest {
       .setIsNewCodeReferenceIssue(true);
     fromShort.setResolution("resolution");
     fromShort.setStatus("status");
+    fromShort.setCleanCodeAttribute(CleanCodeAttribute.COMPLETE);
 
     Date commentDate = new Date();
     fromShort.addComment(new DefaultIssueComment()
@@ -154,6 +157,7 @@ public class IssueLifecycleTest {
 
     assertThat(raw.resolution()).isEqualTo("resolution");
     assertThat(raw.status()).isEqualTo("status");
+    assertThat(raw.getCleanCodeAttribute()).isEqualTo(CleanCodeAttribute.COMPLETE);
     assertThat(raw.defaultIssueComments())
       .extracting(DefaultIssueComment::issueKey, DefaultIssueComment::createdAt, DefaultIssueComment::userUuid, DefaultIssueComment::markdownText)
       .containsOnly(tuple("raw", commentDate, "user_uuid", "A comment"));
@@ -184,6 +188,7 @@ public class IssueLifecycleTest {
       .setKey("short");
     fromShort.setResolution("resolution");
     fromShort.setStatus("status");
+    fromShort.setCleanCodeAttribute(CleanCodeAttribute.DISTINCT);
 
     Date commentDate = new Date();
     fromShort.addComment(new DefaultIssueComment()
@@ -211,6 +216,7 @@ public class IssueLifecycleTest {
 
     assertThat(raw.resolution()).isEqualTo("resolution");
     assertThat(raw.status()).isEqualTo("status");
+    assertThat(raw.getCleanCodeAttribute()).isEqualTo(CleanCodeAttribute.DISTINCT);
     assertThat(raw.defaultIssueComments())
       .extracting(DefaultIssueComment::issueKey, DefaultIssueComment::createdAt, DefaultIssueComment::userUuid, DefaultIssueComment::markdownText)
       .containsOnly(tuple("raw", commentDate, "user_uuid", "A comment"));
@@ -295,6 +301,7 @@ public class IssueLifecycleTest {
       .setCreationDate(parseDate("2015-01-01"))
       .setUpdateDate(parseDate("2015-01-02"))
       .setCloseDate(parseDate("2015-01-03"))
+      .setCleanCodeAttribute(CleanCodeAttribute.FOCUSED)
       .setResolution(RESOLUTION_FIXED)
       .setStatus(STATUS_CLOSED)
       .setSeverity(BLOCKER)
@@ -321,6 +328,7 @@ public class IssueLifecycleTest {
 
     assertThat(raw.isNew()).isFalse();
     assertThat(raw.isCopied()).isTrue();
+    assertThat(raw.getCleanCodeAttribute()).isEqualTo(CleanCodeAttribute.FOCUSED);
     assertThat(raw.key()).isNotNull();
     assertThat(raw.key()).isNotEqualTo(base.key());
     assertThat(raw.creationDate()).isEqualTo(base.creationDate());
@@ -387,6 +395,7 @@ public class IssueLifecycleTest {
       .setKey("RAW_KEY")
       .setRuleKey(XOO_X1)
       .setRuleDescriptionContextKey("spring")
+      .setCleanCodeAttribute(CleanCodeAttribute.IDENTIFIABLE)
       .setCodeVariants(Set.of("foo", "bar"))
       .addImpact(SoftwareQuality.MAINTAINABILITY, Severity.HIGH)
       .setCreationDate(parseDate("2015-10-01"))
@@ -413,6 +422,7 @@ public class IssueLifecycleTest {
       .setKey("BASE_KEY")
       .setCreationDate(parseDate("2015-01-01"))
       .setUpdateDate(parseDate("2015-01-02"))
+      .setCleanCodeAttribute(CleanCodeAttribute.FOCUSED)
       .setResolution(RESOLUTION_FALSE_POSITIVE)
       .setStatus(STATUS_RESOLVED)
       .setSeverity(BLOCKER)
@@ -468,6 +478,7 @@ public class IssueLifecycleTest {
     verify(updater).setPastMessage(raw, "message with code", messageFormattings, issueChangeContext);
     verify(updater).setPastEffort(raw, Duration.create(15L), issueChangeContext);
     verify(updater).setPastLocations(raw, issueLocations);
+    verify(updater).setCleanCodeAttribute(raw, CleanCodeAttribute.FOCUSED, issueChangeContext);
   }
 
   @Test
index 340bb259769246d2f53c204679cddcf942cbf540..f65c91bfbda720e4e7433bb00dc40146553af025 100644 (file)
@@ -25,6 +25,7 @@ import org.junit.Test;
 import org.sonar.api.issue.impact.Severity;
 import org.sonar.api.issue.impact.SoftwareQuality;
 import org.sonar.api.rule.RuleKey;
+import org.sonar.api.rules.CleanCodeAttribute;
 import org.sonar.api.rules.RuleType;
 import org.sonar.core.issue.DefaultIssue;
 
@@ -67,6 +68,17 @@ public class ProtobufIssueDiskCacheTest {
     assertThat(defaultIssue.impacts()).containsExactlyInAnyOrderEntriesOf(Map.of(SoftwareQuality.MAINTAINABILITY, Severity.HIGH, SoftwareQuality.RELIABILITY, Severity.LOW));
   }
 
+  @Test
+  public void toDefaultIssue_whenCleanCodeAttributeIsSet_shouldSetItInDefaultIssue() {
+    IssueCache.Issue issue = prepareIssueWithCompulsoryFields()
+      .setCleanCodeAttribute(CleanCodeAttribute.FOCUSED.name())
+      .build();
+
+    DefaultIssue defaultIssue = ProtobufIssueDiskCache.toDefaultIssue(issue);
+
+    assertThat(defaultIssue.getCleanCodeAttribute()).isEqualTo(CleanCodeAttribute.FOCUSED);
+  }
+
   @Test
   public void toProto_whenRuleDescriptionContextKeySet_shouldCopyToIssueProto() {
     DefaultIssue defaultIssue = createDefaultIssueWithMandatoryFields();
@@ -102,6 +114,16 @@ public class ProtobufIssueDiskCacheTest {
     );
   }
 
+  @Test
+  public void toProto_whenCleanCodeAttributeIsSet_shouldCopyToIssueProto() {
+    DefaultIssue defaultIssue = createDefaultIssueWithMandatoryFields();
+    defaultIssue.setCleanCodeAttribute(CleanCodeAttribute.FOCUSED);
+
+    IssueCache.Issue issue = ProtobufIssueDiskCache.toProto(IssueCache.Issue.newBuilder(), defaultIssue);
+
+    assertThat(issue.getCleanCodeAttribute()).isEqualTo(CleanCodeAttribute.FOCUSED.name());
+  }
+
   private IssueCache.Impact toImpact(SoftwareQuality softwareQuality, Severity severity) {
     return IssueCache.Impact.newBuilder().setSoftwareQuality(softwareQuality.name()).setSeverity(severity.name()).build();
   }
index f5ecdafdf3c8a5e991dff5fcf082388d54f1dc88..145c9ad786b4bec43f9b518e96059f0535043de0 100644 (file)
@@ -134,6 +134,7 @@ public class IssueDaoIT {
       .setManualSeverity(false)
       .setMessage("the message")
       .setRuleDescriptionContextKey(TEST_CONTEXT_KEY)
+      .setRuleCleanCodeAttribute(RULE.getCleanCodeAttribute())
       .setLine(500)
       .setEffort(10L)
       .setGap(3.14)
@@ -164,7 +165,7 @@ public class IssueDaoIT {
     assertThat(issue.getIssueCloseDate()).isNotNull();
     assertThat(issue.getRuleRepo()).isEqualTo(RULE.getRepositoryKey());
     assertThat(issue.getRule()).isEqualTo(RULE.getRuleKey());
-    assertThat(issue.getCleanCodeAttribute()).isEqualTo(RULE.getCleanCodeAttribute());
+    assertThat(issue.getEffectiveCleanCodeAttribute()).isEqualTo(RULE.getCleanCodeAttribute());
     assertThat(issue.parseLocations()).isNull();
     assertThat(issue.getImpacts())
       .extracting(ImpactDto::getSeverity, ImpactDto::getSoftwareQuality)
index b4f84bfa9f3816c7756ef5afef57e09f2e68256c..ec56e1ef8d9938f743e166bb056d259f94896f51 100644 (file)
@@ -33,6 +33,7 @@ public final class IndexedIssueDto {
   private Integer line = null;
   private String resolution = null;
   private String cleanCodeAttribute = null;
+  private String ruleCleanCodeAttribute = null;
   private String severity = null;
   private String status = null;
   private Long effort = null;
@@ -312,11 +313,23 @@ public final class IndexedIssueDto {
   }
 
   public String getCleanCodeAttribute() {
-    return cleanCodeAttribute;
+    if (cleanCodeAttribute != null) {
+      return cleanCodeAttribute;
+    }
+    return ruleCleanCodeAttribute;
   }
 
   public IndexedIssueDto setCleanCodeAttribute(String cleanCodeAttribute) {
     this.cleanCodeAttribute = cleanCodeAttribute;
     return this;
   }
+
+  public String getRuleCleanCodeAttribute() {
+    return ruleCleanCodeAttribute;
+  }
+
+  public IndexedIssueDto setRuleCleanCodeAttribute(String ruleCleanCodeAttribute) {
+    this.ruleCleanCodeAttribute = ruleCleanCodeAttribute;
+    return this;
+  }
 }
index d933e0b45fd09b2ac65f2c7e278a493fe9c1e4ca..00b1342dbe33060d4ee8d05e73120661796295cb 100644 (file)
@@ -116,6 +116,7 @@ public final class IssueDto implements Serializable {
   //non-persisted fields
   private Set<ImpactDto> ruleDefaultImpacts = new HashSet<>();
   private CleanCodeAttribute cleanCodeAttribute;
+  private CleanCodeAttribute ruleCleanCodeAttribute;
 
   public IssueDto() {
     // nothing to do
@@ -158,6 +159,7 @@ public final class IssueDto implements Serializable {
       .setIsNewCodeReferenceIssue(issue.isNewCodeReferenceIssue())
       .setCodeVariants(issue.codeVariants())
       .replaceAllImpacts(mapToImpactDto(issue.impacts()))
+      .setCleanCodeAttribute(issue.getCleanCodeAttribute())
       // technical dates
       .setCreatedAt(now)
       .setUpdatedAt(now);
@@ -215,6 +217,7 @@ public final class IssueDto implements Serializable {
       .setIsNewCodeReferenceIssue(issue.isNewCodeReferenceIssue())
       .setCodeVariants(issue.codeVariants())
       .replaceAllImpacts(mapToImpactDto(issue.impacts()))
+      .setCleanCodeAttribute(issue.getCleanCodeAttribute())
       // technical date
       .setUpdatedAt(now);
   }
@@ -771,8 +774,11 @@ public final class IssueDto implements Serializable {
   }
 
   @CheckForNull
-  public CleanCodeAttribute getCleanCodeAttribute() {
-    return cleanCodeAttribute;
+  public CleanCodeAttribute getEffectiveCleanCodeAttribute() {
+    if (cleanCodeAttribute != null) {
+      return cleanCodeAttribute;
+    }
+    return ruleCleanCodeAttribute;
   }
 
   public IssueDto setCleanCodeAttribute(CleanCodeAttribute cleanCodeAttribute) {
@@ -780,6 +786,11 @@ public final class IssueDto implements Serializable {
     return this;
   }
 
+  public IssueDto setRuleCleanCodeAttribute(CleanCodeAttribute ruleCleanCodeAttribute) {
+    this.ruleCleanCodeAttribute = ruleCleanCodeAttribute;
+    return this;
+  }
+
   public Optional<String> getClosedChangeData() {
     return Optional.ofNullable(closedChangeData);
   }
@@ -887,6 +898,7 @@ public final class IssueDto implements Serializable {
     issue.setQuickFixAvailable(quickFixAvailable);
     issue.setIsNewCodeReferenceIssue(isNewCodeReferenceIssue);
     issue.setCodeVariants(getCodeVariants());
+    issue.setCleanCodeAttribute(cleanCodeAttribute);
     impacts.forEach(i -> issue.addImpact(i.getSoftwareQuality(), i.getSeverity()));
     return issue;
   }
index a731ea97d422ffd5efa7d8fcd8add99383427051..62b53297cf2ac5615496ab66ffe43a2750b478f9 100644 (file)
@@ -32,7 +32,8 @@
     r.plugin_name as ruleRepo,
     r.language as language,
     r.security_standards as securityStandards,
-    r.clean_code_attribute as cleanCodeAttribute,
+    i.clean_code_attribute as cleanCodeAttribute,
+    r.clean_code_attribute as ruleCleanCodeAttribute,
     p.kee as componentKey,
     i.component_uuid as componentUuid,
     p.path as filePath,
@@ -73,7 +74,8 @@
     i.project_uuid,
     i.issue_type,
     i.quick_fix_available,
-    i.code_variants
+    i.code_variants,
+    i.clean_code_attribute
   </sql>
 
   <sql id="isNewCodeReferenceIssue" databaseId="mssql">
     INSERT INTO issues (kee, rule_uuid, severity, manual_severity,
     message, message_formattings, line, locations, gap, effort, status, tags, rule_description_context_key,
     resolution, checksum, assignee, author_login, issue_creation_date, issue_update_date,
-    issue_close_date, created_at, updated_at, component_uuid, project_uuid, issue_type, quick_fix_available, code_variants)
+    issue_close_date, created_at, updated_at, component_uuid, project_uuid, issue_type, quick_fix_available, code_variants,
+    clean_code_attribute)
     VALUES (
     #{kee,jdbcType=VARCHAR},
     #{ruleUuid,jdbcType=VARCHAR},
     #{createdAt,jdbcType=BIGINT}, #{updatedAt,jdbcType=BIGINT},
     #{componentUuid,jdbcType=VARCHAR}, #{projectUuid,jdbcType=VARCHAR}, #{type,jdbcType=INTEGER},
     #{quickFixAvailable, jdbcType=BOOLEAN},
-    #{codeVariantsString,jdbcType=VARCHAR})
+    #{codeVariantsString,jdbcType=VARCHAR},
+    #{effectiveCleanCodeAttribute,jdbcType=VARCHAR})
   </insert>
 
   <insert id="insertAsNewCodeOnReferenceBranch" parameterType="NewCodeReferenceIssue" useGeneratedKeys="false">
     issue_close_date=#{issueCloseTime,jdbcType=BIGINT},
     updated_at=#{updatedAt,jdbcType=BIGINT},
     issue_type=#{type,jdbcType=INTEGER},
-    code_variants=#{codeVariantsString,jdbcType=VARCHAR}
+    code_variants=#{codeVariantsString,jdbcType=VARCHAR},
+    clean_code_attribute=#{effectiveCleanCodeAttribute,jdbcType=VARCHAR}
     where kee = #{kee}
   </update>
 
     issue_close_date=#{issueCloseTime,jdbcType=BIGINT},
     updated_at=#{updatedAt,jdbcType=BIGINT},
     issue_type=#{type,jdbcType=INTEGER},
-    code_variants=#{codeVariantsString,jdbcType=VARCHAR}
+    code_variants=#{codeVariantsString,jdbcType=VARCHAR},
+    clean_code_attribute=#{effectiveCleanCodeAttribute,jdbcType=VARCHAR}
     where kee = #{kee} and updated_at &lt;= #{selectedAt}
   </update>
 
       i.issue_update_date as issueUpdateDate,
       r.uuid as ruleUuid,
       r.language as language,
-      r.clean_code_attribute as cleanCodeAttribute,
+      i.clean_code_attribute as cleanCodeAttribute,
+      r.clean_code_attribute as ruleCleanCodeAttribute,
       c.uuid as componentUuid,
       c.path,
       c.scope,
     r.rule_type as ruleType,
     r.plugin_name as ruleRepo,
     r.plugin_rule_key as ruleKey,
-    r.clean_code_attribute as cleanCodeAttribute,
     i.message as message,
     i.message_formattings as messageFormattings,
     i.severity as severity,
     i.rule_description_context_key as ruleDescriptionContextKey,
     <include refid="issueImpactsColumns"/>
     <include refid="ruleDefaultImpactsColumns"/>
-    r.clean_code_attribute as cleanCodeAttribute
+    i.clean_code_attribute as cleanCodeAttribute,
+    r.clean_code_attribute as ruleCleanCodeAttribute
   </sql>
 
   <select id="selectByBranch" parameterType="map" resultMap="issueResultMap" resultOrdered="true">
index 79d702ed08723d8b0ff41998f3325a4424ac587e..3c513e7c9041ece26bad3ef972e85db059306591 100644 (file)
@@ -459,7 +459,8 @@ CREATE TABLE "ISSUES"(
     "QUICK_FIX_AVAILABLE" BOOLEAN,
     "RULE_DESCRIPTION_CONTEXT_KEY" CHARACTER VARYING(50),
     "MESSAGE_FORMATTINGS" BINARY LARGE OBJECT,
-    "CODE_VARIANTS" CHARACTER VARYING(4000)
+    "CODE_VARIANTS" CHARACTER VARYING(4000),
+    "CLEAN_CODE_ATTRIBUTE" CHARACTER VARYING(40)
 );
 ALTER TABLE "ISSUES" ADD CONSTRAINT "PK_ISSUES" PRIMARY KEY("KEE");
 CREATE INDEX "ISSUES_ASSIGNEE" ON "ISSUES"("ASSIGNEE" NULLS FIRST);
index 24ba25aed7f9fd62cfb5a56823d4d8400a734040..d4ef1a54658fd1ea6a4ff04d3fe0b2a10137df62 100644 (file)
@@ -37,6 +37,7 @@ public class IndexedIssueDtoTest {
       .setStatus("status")
       .setNewCodeReferenceIssue(true)
       .setCleanCodeAttribute("cleanCodeAttribute")
+      .setRuleCleanCodeAttribute("ruleCleanCodeAttribute")
       .setCodeVariants("codeVariants")
       .setSecurityStandards("securityStandards")
       .setComponentUuid("componentUuid")
@@ -63,13 +64,13 @@ public class IndexedIssueDtoTest {
 
     assertThat(indexedIssueDto)
       .extracting(IndexedIssueDto::getIssueKey, IndexedIssueDto::getAssignee, IndexedIssueDto::getAuthorLogin, IndexedIssueDto::getStatus,
-        IndexedIssueDto::isNewCodeReferenceIssue, IndexedIssueDto::getCleanCodeAttribute, IndexedIssueDto::getCodeVariants,
+        IndexedIssueDto::isNewCodeReferenceIssue, IndexedIssueDto::getCleanCodeAttribute, IndexedIssueDto::getRuleCleanCodeAttribute, IndexedIssueDto::getCodeVariants,
         IndexedIssueDto::getSecurityStandards, IndexedIssueDto::getComponentUuid, IndexedIssueDto::getIssueCloseDate, IndexedIssueDto::getIssueCreationDate,
         IndexedIssueDto::getIssueUpdateDate, IndexedIssueDto::getEffort, IndexedIssueDto::isMain, IndexedIssueDto::getLanguage, IndexedIssueDto::getLine,
         IndexedIssueDto::getPath, IndexedIssueDto::getProjectUuid, IndexedIssueDto::getQualifier, IndexedIssueDto::getResolution,
         IndexedIssueDto::getRuleUuid, IndexedIssueDto::getScope, IndexedIssueDto::getSeverity, IndexedIssueDto::getTags, IndexedIssueDto::getIssueType,
         IndexedIssueDto::getBranchUuid)
-      .containsExactly("issueKey", "assignee", "authorLogin", "status", true, "cleanCodeAttribute", "codeVariants", "securityStandards",
+      .containsExactly("issueKey", "assignee", "authorLogin", "status", true, "cleanCodeAttribute", "ruleCleanCodeAttribute", "codeVariants", "securityStandards",
         "componentUuid", 1L, 2L, 3L, 4L, true, "language", 5, "path", "projectUuid", "qualifier", "resolution", "ruleUuid",
         "scope", "severity", "tags", 6, "branchUuid");
 
index 7530893e97c2140442d97f3b0282d7f6ff50b9e7..d7e64ea110fd717907312a961a3922a4bc7402eb 100644 (file)
@@ -138,7 +138,7 @@ public class IssueDtoTest {
     assertThat(dto.getRuleRepo()).isEqualTo("java");
     assertThat(dto.getRule()).isEqualTo("AvoidCycle");
     assertThat(dto.getRuleKey()).hasToString("java:AvoidCycle");
-    assertThat(dto.getCleanCodeAttribute()).isEqualTo(CleanCodeAttribute.CLEAR);
+    assertThat(dto.getEffectiveCleanCodeAttribute()).isEqualTo(CleanCodeAttribute.CLEAR);
     assertThat(dto.getLanguage()).isEqualTo("xoo");
     assertThat(dto.isExternal()).isTrue();
   }
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v103/AddCleanCodeAttributeColumnInIssuesTable.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v103/AddCleanCodeAttributeColumnInIssuesTable.java
new file mode 100644 (file)
index 0000000..7fbed8d
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.platform.db.migration.version.v103;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.db.DatabaseUtils;
+import org.sonar.server.platform.db.migration.def.ColumnDef;
+import org.sonar.server.platform.db.migration.def.VarcharColumnDef;
+import org.sonar.server.platform.db.migration.sql.AddColumnsBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+public class AddCleanCodeAttributeColumnInIssuesTable extends DdlChange {
+  private static final String TABLE_NAME = "issues";
+  private static final String COLUMN_NAME = "clean_code_attribute";
+  private static final int NEW_COLUMN_SIZE = 40;
+
+
+  public AddCleanCodeAttributeColumnInIssuesTable(Database db) {
+    super(db);
+  }
+
+  @Override
+  public void execute(Context context) throws SQLException {
+    try (Connection connection = getDatabase().getDataSource().getConnection()) {
+      if (!DatabaseUtils.tableColumnExists(connection, TABLE_NAME, COLUMN_NAME)) {
+        ColumnDef columnDef = VarcharColumnDef.newVarcharColumnDefBuilder()
+          .setColumnName(COLUMN_NAME)
+          .setLimit(NEW_COLUMN_SIZE)
+          .setIsNullable(true)
+          .build();
+        context.execute(new AddColumnsBuilder(getDialect(), TABLE_NAME).addColumn(columnDef).build());
+      }
+    }
+  }
+}
index e7f216fae9bf81457cf770c6317f0469c8b90b59..82bbeea7384db31958bfd01fb158574e9a8c2294 100644 (file)
@@ -45,6 +45,7 @@ public class DbVersion103 implements DbVersion {
       .add(10_3_001, "Add table 'github_perms_mapping'", CreateGithubPermissionsMappingTable.class)
       .add(10_3_002, "Create unique index on 'github_perms_mapping'", CreateUniqueIndexForGithubPermissionsMappingTable.class)
       .add(10_3_003, "Add default mappings to 'github_perms_mapping'", PopulateGithubPermissionsMapping.class)
+      .add(10_3_004, "Add 'clean_code_attribute' column in 'issues' table", AddCleanCodeAttributeColumnInIssuesTable.class);
     ;
   }
 }
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v103/AddCleanCodeAttributeColumnInIssuesTableTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v103/AddCleanCodeAttributeColumnInIssuesTableTest.java
new file mode 100644 (file)
index 0000000..fc952e6
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.platform.db.migration.version.v103;
+
+import java.sql.SQLException;
+import java.sql.Types;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.db.CoreDbTester;
+
+import static org.assertj.core.api.Assertions.assertThatCode;
+
+public class AddCleanCodeAttributeColumnInIssuesTableTest {
+  private static final String TABLE_NAME = "issues";
+  private static final String COLUMN_NAME = "clean_code_attribute";
+
+  @Rule
+  public final CoreDbTester db = CoreDbTester.createForSchema(AddCleanCodeAttributeColumnInIssuesTableTest.class, "schema.sql");
+
+  private final AddCleanCodeAttributeColumnInIssuesTable underTest = new AddCleanCodeAttributeColumnInIssuesTable(db.database());
+
+  @Test
+  public void execute_whenColumnDoesNotExist_shouldCreateColumn() throws SQLException {
+    db.assertColumnDoesNotExist(TABLE_NAME, COLUMN_NAME);
+    underTest.execute();
+    db.assertColumnDefinition(TABLE_NAME, COLUMN_NAME, Types.VARCHAR, 40, true);
+  }
+
+  @Test
+  public void execute_whenColumnsAlreadyExists_shouldNotFail() throws SQLException {
+    underTest.execute();
+    assertThatCode(underTest::execute).doesNotThrowAnyException();
+  }
+}
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v103/AddCleanCodeAttributeColumnInIssuesTableTest/schema.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v103/AddCleanCodeAttributeColumnInIssuesTableTest/schema.sql
new file mode 100644 (file)
index 0000000..2746ed4
--- /dev/null
@@ -0,0 +1,38 @@
+CREATE TABLE "ISSUES"(
+    "KEE" CHARACTER VARYING(50) NOT NULL,
+    "RULE_UUID" CHARACTER VARYING(40),
+    "SEVERITY" CHARACTER VARYING(10),
+    "MANUAL_SEVERITY" BOOLEAN NOT NULL,
+    "MESSAGE" CHARACTER VARYING(4000),
+    "LINE" INTEGER,
+    "GAP" DOUBLE PRECISION,
+    "STATUS" CHARACTER VARYING(20),
+    "RESOLUTION" CHARACTER VARYING(20),
+    "CHECKSUM" CHARACTER VARYING(1000),
+    "ASSIGNEE" CHARACTER VARYING(255),
+    "AUTHOR_LOGIN" CHARACTER VARYING(255),
+    "EFFORT" INTEGER,
+    "CREATED_AT" BIGINT,
+    "UPDATED_AT" BIGINT,
+    "ISSUE_CREATION_DATE" BIGINT,
+    "ISSUE_UPDATE_DATE" BIGINT,
+    "ISSUE_CLOSE_DATE" BIGINT,
+    "TAGS" CHARACTER VARYING(4000),
+    "COMPONENT_UUID" CHARACTER VARYING(50),
+    "PROJECT_UUID" CHARACTER VARYING(50),
+    "LOCATIONS" BINARY LARGE OBJECT,
+    "ISSUE_TYPE" TINYINT,
+    "FROM_HOTSPOT" BOOLEAN,
+    "QUICK_FIX_AVAILABLE" BOOLEAN,
+    "RULE_DESCRIPTION_CONTEXT_KEY" CHARACTER VARYING(50),
+    "MESSAGE_FORMATTINGS" BINARY LARGE OBJECT,
+    "CODE_VARIANTS" CHARACTER VARYING(4000)
+);
+ALTER TABLE "ISSUES" ADD CONSTRAINT "PK_ISSUES" PRIMARY KEY("KEE");
+CREATE INDEX "ISSUES_ASSIGNEE" ON "ISSUES"("ASSIGNEE" NULLS FIRST);
+CREATE INDEX "ISSUES_COMPONENT_UUID" ON "ISSUES"("COMPONENT_UUID" NULLS FIRST);
+CREATE INDEX "ISSUES_CREATION_DATE" ON "ISSUES"("ISSUE_CREATION_DATE" NULLS FIRST);
+CREATE INDEX "ISSUES_PROJECT_UUID" ON "ISSUES"("PROJECT_UUID" NULLS FIRST);
+CREATE INDEX "ISSUES_RESOLUTION" ON "ISSUES"("RESOLUTION" NULLS FIRST);
+CREATE INDEX "ISSUES_UPDATED_AT" ON "ISSUES"("UPDATED_AT" NULLS FIRST);
+CREATE INDEX "ISSUES_RULE_UUID" ON "ISSUES"("RULE_UUID" NULLS FIRST);
index d1e640604cf4976cd1dde9ca8e56bea5765a2abe..9d64b033738aec41c766b7333077930fa8a0ee51 100644 (file)
@@ -33,6 +33,7 @@ import javax.annotation.Nullable;
 import org.sonar.api.ce.ComputeEngineSide;
 import org.sonar.api.issue.impact.Severity;
 import org.sonar.api.issue.impact.SoftwareQuality;
+import org.sonar.api.rules.CleanCodeAttribute;
 import org.sonar.api.rules.RuleType;
 import org.sonar.api.server.ServerSide;
 import org.sonar.api.server.rule.RuleTagFormat;
@@ -46,6 +47,7 @@ import org.sonar.db.user.UserIdDto;
 
 import static com.google.common.base.Preconditions.checkState;
 import static com.google.common.base.Strings.isNullOrEmpty;
+import static java.util.Objects.requireNonNull;
 
 /**
  * Updates issue fields and chooses if changes must be kept in history.
@@ -57,6 +59,7 @@ public class IssueFieldsSetter {
   public static final String UNUSED = "";
   public static final String SEVERITY = "severity";
   public static final String TYPE = "type";
+  public static final String CLEAN_CODE_ATTRIBUTE = "cleanCodeAttribute";
   public static final String ASSIGNEE = "assignee";
   public static final String RESOLUTION = "resolution";
   public static final String STATUS = "status";
@@ -323,8 +326,8 @@ public class IssueFieldsSetter {
 
     for (int i = 0; i < l1c.getMessageFormattingCount(); i++) {
       if (l1c.getMessageFormatting(i).getStart() != l2.getMessageFormatting(i).getStart()
-          || l1c.getMessageFormatting(i).getEnd() != l2.getMessageFormatting(i).getEnd()
-          || l1c.getMessageFormatting(i).getType() != l2.getMessageFormatting(i).getType()) {
+        || l1c.getMessageFormatting(i).getEnd() != l2.getMessageFormatting(i).getEnd()
+        || l1c.getMessageFormatting(i).getType() != l2.getMessageFormatting(i).getType()) {
         return false;
       }
     }
@@ -441,6 +444,19 @@ public class IssueFieldsSetter {
     return false;
   }
 
+  public boolean setCleanCodeAttribute(DefaultIssue raw, @Nullable CleanCodeAttribute previousCleanCodeAttribute, IssueChangeContext changeContext) {
+    CleanCodeAttribute newCleanCodeAttribute = requireNonNull(raw.getCleanCodeAttribute());
+    if (Objects.equals(previousCleanCodeAttribute, newCleanCodeAttribute)) {
+      return false;
+    }
+    raw.setFieldChange(changeContext, CLEAN_CODE_ATTRIBUTE, previousCleanCodeAttribute, newCleanCodeAttribute.name());
+    raw.setCleanCodeAttribute(newCleanCodeAttribute);
+    raw.setUpdateDate(changeContext.date());
+    raw.setChanged(true);
+    return true;
+
+  }
+
   private static Set<String> getNewCodeVariants(DefaultIssue issue) {
     Set<String> issueCodeVariants = issue.codeVariants();
     if (issueCodeVariants == null) {
index d83383d355797d87944251c53bb72b547e60c4ad..97222e5315ff5aee7fce0790e0f2b889e0b8120f 100644 (file)
@@ -91,10 +91,9 @@ class IssueIteratorForSingleChunk implements IssueIterator {
     doc.setSeverity(indexedIssueDto.getSeverity());
     String cleanCodeAttributeCategory = Optional.ofNullable(indexedIssueDto.getCleanCodeAttribute())
       .map(CleanCodeAttribute::valueOf)
-      .map(cleanCodeAttribute -> cleanCodeAttribute.getAttributeCategory().name())
+      .map(CleanCodeAttribute::getAttributeCategory)
+      .map(Enum::name)
       .orElse(null);
-    //TODO SONAR-20073 uncomment once clean code attribute is set to not-null
-    //.orElseThrow(() -> new IllegalStateException("Clean Code Attribute is missing for issue " + key));
     doc.setCleanCodeAttributeCategory(cleanCodeAttributeCategory);
     doc.setStatus(indexedIssueDto.getStatus());
     doc.setEffort(indexedIssueDto.getEffort());
index 3003da52cea304b1e389996d740bc60054569eae..9813c161737a73259dab99ad0fc1b8f12a5feaaf 100644 (file)
@@ -31,6 +31,7 @@ import org.apache.commons.lang.time.DateUtils;
 import org.junit.Test;
 import org.sonar.api.issue.impact.Severity;
 import org.sonar.api.issue.impact.SoftwareQuality;
+import org.sonar.api.rules.CleanCodeAttribute;
 import org.sonar.api.rules.RuleType;
 import org.sonar.api.utils.Duration;
 import org.sonar.core.issue.DefaultIssue;
@@ -750,4 +751,25 @@ public class IssueFieldsSetterTest {
     assertThat(updated).isTrue();
     assertThat(issue.getRuleDescriptionContextKey()).contains(DEFAULT_RULE_DESCRIPTION_CONTEXT_KEY);
   }
+
+  @Test
+  public void setCleanCodeAttribute_whenCleanCodeAttributeChanged_shouldUpdateIssue() {
+    issue.setCleanCodeAttribute(CleanCodeAttribute.CLEAR);
+    boolean updated = underTest.setCleanCodeAttribute(issue, CleanCodeAttribute.COMPLETE, context);
+
+    assertThat(updated).isTrue();
+    assertThat(issue.getCleanCodeAttribute()).isEqualTo(CleanCodeAttribute.CLEAR);
+    assertThat(issue.currentChange().get("cleanCodeAttribute"))
+      .extracting(FieldDiffs.Diff::oldValue, FieldDiffs.Diff::newValue)
+      .containsExactly(CleanCodeAttribute.COMPLETE, CleanCodeAttribute.CLEAR.name());
+  }
+
+  @Test
+  public void setCleanCodeAttribute_whenCleanCodeAttributeNotChanged_shouldNotUpdateIssue() {
+    issue.setCleanCodeAttribute(CleanCodeAttribute.CLEAR);
+    boolean updated = underTest.setCleanCodeAttribute(issue, CleanCodeAttribute.CLEAR, context);
+
+    assertThat(updated).isFalse();
+    assertThat(issue.getCleanCodeAttribute()).isEqualTo(CleanCodeAttribute.CLEAR);
+  }
 }
index a856e374b6be6f82000ac31e3da35ea2cd7d4423..3a227ed644caf1c37b0eef976d90f9bc68007b13 100644 (file)
@@ -119,7 +119,7 @@ public class WebIssueStorageIT {
 
     assertThat(createdIssues).hasSize(1);
 
-    assertThat(createdIssues.iterator().next().getCleanCodeAttribute()).isEqualTo(CleanCodeAttribute.CLEAR);
+    assertThat(createdIssues.iterator().next().getEffectiveCleanCodeAttribute()).isEqualTo(CleanCodeAttribute.CLEAR);
     assertThat(createdIssues.iterator().next().getEffectiveImpacts()).isEqualTo(Map.of(SoftwareQuality.MAINTAINABILITY, Severity.HIGH));
 
     assertThat(db.countRowsOfTable("issues")).isOne();
@@ -166,7 +166,7 @@ public class WebIssueStorageIT {
     Collection<IssueDto> updatedIssues = underTest.save(db.getSession(), singletonList(issue));
     assertThat(updatedIssues).hasSize(1);
 
-    assertThat(updatedIssues.iterator().next().getCleanCodeAttribute()).isEqualTo(CleanCodeAttribute.CLEAR);
+    assertThat(updatedIssues.iterator().next().getEffectiveCleanCodeAttribute()).isEqualTo(CleanCodeAttribute.CLEAR);
     assertThat(updatedIssues.iterator().next().getEffectiveImpacts()).isEqualTo(Map.of(SoftwareQuality.MAINTAINABILITY, Severity.HIGH));
 
     assertThat(db.countRowsOfTable("issues")).isOne();
index 4e6e21e2851d21fe4f9ae6900193386c202d3b52..1ea0306bdba2bb0c62c39fb86fb70dd644f17646 100644 (file)
@@ -256,9 +256,9 @@ public class PullTaintActionIT {
     assertThat(taintLite.getType()).isEqualTo(Common.RuleType.forNumber(issueDto.getType()));
     assertThat(taintLite.getAssignedToSubscribedUser()).isTrue();
     assertThat(taintLite.getCleanCodeAttribute())
-      .isEqualTo(Common.CleanCodeAttribute.valueOf(issueDto.getCleanCodeAttribute().name()));
+      .isEqualTo(Common.CleanCodeAttribute.valueOf(issueDto.getEffectiveCleanCodeAttribute().name()));
     assertThat(taintLite.getCleanCodeAttributeCategory())
-      .isEqualTo(Common.CleanCodeAttributeCategory.valueOf(issueDto.getCleanCodeAttribute().getAttributeCategory().name()));
+      .isEqualTo(Common.CleanCodeAttributeCategory.valueOf(issueDto.getEffectiveCleanCodeAttribute().getAttributeCategory().name()));
 
     assertThat(taintLite.getImpactsList())
       .extracting(Common.Impact::getSoftwareQuality, Common.Impact::getSeverity)
index e164e6a50ac672348c0c89f169248db5d2478fa4..be54e0acf3b2d4a40d44640ae2eac4577705cf53 100644 (file)
@@ -174,7 +174,7 @@ public class SearchResponseFormat {
     issueBuilder.setKey(dto.getKey());
     issueBuilder.setType(Common.RuleType.forNumber(dto.getType()));
 
-    CleanCodeAttribute cleanCodeAttribute = dto.getCleanCodeAttribute();
+    CleanCodeAttribute cleanCodeAttribute = dto.getEffectiveCleanCodeAttribute();
     if (cleanCodeAttribute != null) {
       issueBuilder.setCleanCodeAttribute(Common.CleanCodeAttribute.valueOf(cleanCodeAttribute.name()));
       issueBuilder.setCleanCodeAttributeCategory(Common.CleanCodeAttributeCategory.valueOf(cleanCodeAttribute.getAttributeCategory().name()));
index 73d6916b69fdf3c93b51c5a5115bb75c61b749c4..ae920de34c2342ee9d23eb41ac414c2d8ff04ea3 100644 (file)
@@ -96,7 +96,7 @@ public class PullTaintActionProtobufObjectGenerator implements ProtobufObjectGen
       taintBuilder.setSeverity(Common.Severity.valueOf(issueDto.getSeverity()));
     }
     taintBuilder.setType(Common.RuleType.forNumber(issueDto.getType()));
-    CleanCodeAttribute cleanCodeAttribute = issueDto.getCleanCodeAttribute();
+    CleanCodeAttribute cleanCodeAttribute = issueDto.getEffectiveCleanCodeAttribute();
     String cleanCodeAttributeString = cleanCodeAttribute != null ? cleanCodeAttribute.name() : null;
     String cleanCodeAttributeCategoryString = cleanCodeAttribute != null ? cleanCodeAttribute.getAttributeCategory().name() : null;
     if (cleanCodeAttributeString != null) {
index 070aec1d96c252348ee7f993b15ef1f839127bb1..b33f55b275ac727775ca92cdc9e1b2a994f13940 100644 (file)
@@ -124,8 +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.getCleanCodeAttribute()).isEqualTo(Common.CleanCodeAttribute.valueOf(issueDto.getEffectiveCleanCodeAttribute().name()));
+    assertThat(issue.getCleanCodeAttributeCategory()).isEqualTo(Common.CleanCodeAttributeCategory.valueOf(issueDto.getEffectiveCleanCodeAttribute().getAttributeCategory().name()));
     assertThat(issue.getType().getNumber()).isEqualTo(issueDto.getType());
     assertThat(issue.getComponent()).isEqualTo(issueDto.getComponentKey());
     assertThat(issue.getRule()).isEqualTo(issueDto.getRuleKey().toString());
index dfd104b19cb96222760e39bbb4c4b0fb55f83802..aa69711c00a12a4d0f0b9de29f9edea4bd8f153c 100644 (file)
@@ -45,6 +45,7 @@ import org.sonar.api.issue.Issue;
 import org.sonar.api.issue.impact.SoftwareQuality;
 import org.sonar.api.rule.RuleKey;
 import org.sonar.api.rule.Severity;
+import org.sonar.api.rules.CleanCodeAttribute;
 import org.sonar.api.rules.RuleType;
 import org.sonar.api.utils.Duration;
 import org.sonar.core.issue.tracking.Trackable;
@@ -94,7 +95,7 @@ public class DefaultIssue implements Issue, Trackable, org.sonar.api.ce.measure.
 
   // all changes
   // -- contains only current change (if any) on CE side unless reopening a closed issue or copying issue from base branch
-  //    when analyzing a branch from the first time
+  // when analyzing a branch from the first time
   private List<FieldDiffs> changes = null;
 
   // true if the issue did not exist in the previous scan.
@@ -135,6 +136,7 @@ public class DefaultIssue implements Issue, Trackable, org.sonar.api.ce.measure.
   private String anticipatedTransitionUuid = null;
 
   private Map<SoftwareQuality, org.sonar.api.issue.impact.Severity> impacts = new EnumMap<>(SoftwareQuality.class);
+  private CleanCodeAttribute cleanCodeAttribute = null;
 
   @Override
   public String key() {
@@ -443,7 +445,6 @@ public class DefaultIssue implements Issue, Trackable, org.sonar.api.ce.measure.
     return this;
   }
 
-
   public DefaultIssue setNew(boolean b) {
     isNew = b;
     return this;
@@ -713,6 +714,16 @@ public class DefaultIssue implements Issue, Trackable, org.sonar.api.ce.measure.
     return this;
   }
 
+  public DefaultIssue setCleanCodeAttribute(@Nullable CleanCodeAttribute cleanCodeAttribute) {
+    this.cleanCodeAttribute = cleanCodeAttribute;
+    return this;
+  }
+
+  @Nullable
+  public CleanCodeAttribute getCleanCodeAttribute() {
+    return cleanCodeAttribute;
+  }
+
   @Override
   public Integer getLine() {
     return line;
@@ -742,5 +753,4 @@ public class DefaultIssue implements Issue, Trackable, org.sonar.api.ce.measure.
   public Date getUpdateDate() {
     return updateDate;
   }
-
 }