From: Jacek Date: Thu, 14 Sep 2023 12:07:05 +0000 (+0200) Subject: SONAR-20424 Store and populate within issue `clean_code_attribute` X-Git-Tag: 10.3.0.82913~367 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=e01e97bcbcd8716eca619fdec233de5b2f5e7be2;p=sonarqube.git SONAR-20424 Store and populate within issue `clean_code_attribute` --- 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.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 { 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; @@ -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(); } 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 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 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 @@ -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}) @@ -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} @@ -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} @@ -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, - r.clean_code_attribute as cleanCodeAttribute + i.clean_code_attribute as cleanCodeAttribute, + r.clean_code_attribute as ruleCleanCodeAttribute