diff options
25 files changed, 306 insertions, 33 deletions
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueLifecycle.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueLifecycle.java index 39bffffc47e..c1f7f2e72cd 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueLifecycle.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueLifecycle.java @@ -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) { diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/util/cache/ProtobufIssueDiskCache.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/util/cache/ProtobufIssueDiskCache.java index 4e015401a3b..39f5c138bef 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/util/cache/ProtobufIssueDiskCache.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/util/cache/ProtobufIssueDiskCache.java @@ -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()); diff --git a/server/sonar-ce-task-projectanalysis/src/main/protobuf/issue_cache.proto b/server/sonar-ce-task-projectanalysis/src/main/protobuf/issue_cache.proto index 77eabd0a214..8b8c05049f8 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/protobuf/issue_cache.proto +++ b/server/sonar-ce-task-projectanalysis/src/main/protobuf/issue_cache.proto @@ -85,6 +85,7 @@ message Issue { optional string assigneeLogin = 47; optional string anticipatedTransitionUuid = 48; repeated Impact impacts = 49; + optional string cleanCodeAttribute = 50; } message Comment { diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IssueLifecycleTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IssueLifecycleTest.java index 8c925bc46fb..1f8758d5533 100644 --- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IssueLifecycleTest.java +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IssueLifecycleTest.java @@ -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 diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/util/cache/ProtobufIssueDiskCacheTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/util/cache/ProtobufIssueDiskCacheTest.java index 340bb259769..f65c91bfbda 100644 --- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/util/cache/ProtobufIssueDiskCacheTest.java +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/util/cache/ProtobufIssueDiskCacheTest.java @@ -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; @@ -68,6 +69,17 @@ public class ProtobufIssueDiskCacheTest { } @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(); defaultIssue.setRuleDescriptionContextKey(TEST_CONTEXT_KEY); @@ -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(); } diff --git a/server/sonar-db-dao/src/it/java/org/sonar/db/issue/IssueDaoIT.java b/server/sonar-db-dao/src/it/java/org/sonar/db/issue/IssueDaoIT.java index f5ecdafdf3c..145c9ad786b 100644 --- a/server/sonar-db-dao/src/it/java/org/sonar/db/issue/IssueDaoIT.java +++ b/server/sonar-db-dao/src/it/java/org/sonar/db/issue/IssueDaoIT.java @@ -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) diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IndexedIssueDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IndexedIssueDto.java index b4f84bfa9f3..ec56e1ef8d9 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IndexedIssueDto.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IndexedIssueDto.java @@ -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; + } } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDto.java index d933e0b45fd..00b1342dbe3 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDto.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDto.java @@ -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; } diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml index a731ea97d42..62b53297cf2 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml @@ -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"> @@ -143,7 +145,8 @@ 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}, @@ -164,7 +167,8 @@ #{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"> @@ -220,7 +224,8 @@ 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> @@ -251,7 +256,8 @@ 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 <= #{selectedAt} </update> @@ -350,7 +356,8 @@ 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, @@ -791,7 +798,6 @@ 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, @@ -804,7 +810,8 @@ 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"> diff --git a/server/sonar-db-dao/src/schema/schema-sq.ddl b/server/sonar-db-dao/src/schema/schema-sq.ddl index 79d702ed087..3c513e7c904 100644 --- a/server/sonar-db-dao/src/schema/schema-sq.ddl +++ b/server/sonar-db-dao/src/schema/schema-sq.ddl @@ -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); diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IndexedIssueDtoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IndexedIssueDtoTest.java index 24ba25aed7f..d4ef1a54658 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IndexedIssueDtoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IndexedIssueDtoTest.java @@ -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"); diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDtoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDtoTest.java index 7530893e97c..d7e64ea110f 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDtoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDtoTest.java @@ -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 index 00000000000..7fbed8dffbf --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v103/AddCleanCodeAttributeColumnInIssuesTable.java @@ -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()); + } + } + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v103/DbVersion103.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v103/DbVersion103.java index e7f216fae9b..82bbeea7384 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v103/DbVersion103.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v103/DbVersion103.java @@ -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 index 00000000000..fc952e6ec47 --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v103/AddCleanCodeAttributeColumnInIssuesTableTest.java @@ -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 index 00000000000..2746ed4bc7b --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v103/AddCleanCodeAttributeColumnInIssuesTableTest/schema.sql @@ -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); diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/issue/IssueFieldsSetter.java b/server/sonar-server-common/src/main/java/org/sonar/server/issue/IssueFieldsSetter.java index d1e640604cf..9d64b033738 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/issue/IssueFieldsSetter.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/issue/IssueFieldsSetter.java @@ -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) { diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/issue/index/IssueIteratorForSingleChunk.java b/server/sonar-server-common/src/main/java/org/sonar/server/issue/index/IssueIteratorForSingleChunk.java index d83383d3557..97222e5315f 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/issue/index/IssueIteratorForSingleChunk.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/issue/index/IssueIteratorForSingleChunk.java @@ -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()); diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/issue/IssueFieldsSetterTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/issue/IssueFieldsSetterTest.java index 3003da52cea..9813c161737 100644 --- a/server/sonar-server-common/src/test/java/org/sonar/server/issue/IssueFieldsSetterTest.java +++ b/server/sonar-server-common/src/test/java/org/sonar/server/issue/IssueFieldsSetterTest.java @@ -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); + } } diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/WebIssueStorageIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/WebIssueStorageIT.java index a856e374b6b..3a227ed644c 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/WebIssueStorageIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/WebIssueStorageIT.java @@ -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(); diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/PullTaintActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/PullTaintActionIT.java index 4e6e21e2851..1ea0306bdba 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/PullTaintActionIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/issue/ws/PullTaintActionIT.java @@ -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) diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchResponseFormat.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchResponseFormat.java index e164e6a50ac..be54e0acf3b 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchResponseFormat.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/SearchResponseFormat.java @@ -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())); diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/pull/PullTaintActionProtobufObjectGenerator.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/pull/PullTaintActionProtobufObjectGenerator.java index 73d6916b69f..ae920de34c2 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/pull/PullTaintActionProtobufObjectGenerator.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/issue/ws/pull/PullTaintActionProtobufObjectGenerator.java @@ -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) { diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/SearchResponseFormatFormatOperationTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/SearchResponseFormatFormatOperationTest.java index 070aec1d96c..b33f55b275a 100644 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/SearchResponseFormatFormatOperationTest.java +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/issue/ws/SearchResponseFormatFormatOperationTest.java @@ -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()); diff --git a/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java b/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java index dfd104b19cb..aa69711c00a 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java @@ -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; } - } |