From 26fde2bdeb8ca2d5b9b76ce1ebc11706c9471cd0 Mon Sep 17 00:00:00 2001 From: Aurelien Poscia Date: Wed, 6 Jul 2022 10:39:01 +0200 Subject: [PATCH] SONAR-16614 Read ruleDescriptionContextKey from scanner reports and persist to DB --- .../projectanalysis/issue/IssueLifecycle.java | 1 + .../issue/TrackerRawInputFactory.java | 1 + .../util/cache/ProtobufIssueDiskCache.java | 11 ++- .../src/main/protobuf/issue_cache.proto | 1 + .../issue/IssueLifecycleTest.java | 48 +++++++++- .../issue/TrackerRawInputFactoryTest.java | 27 ++++++ .../cache/ProtobufIssueDiskCacheTest.java | 96 +++++++++++++++++++ .../java/org/sonar/db/issue/IssueDto.java | 3 + .../java/org/sonar/db/issue/IssueDtoTest.java | 11 ++- .../sonar/server/issue/IssueFieldsSetter.java | 12 ++- .../server/issue/IssueFieldsSetterTest.java | 37 +++++++ .../org/sonar/core/issue/DefaultIssue.java | 2 +- .../sonar/core/issue/DefaultIssueTest.java | 5 +- 13 files changed, 246 insertions(+), 9 deletions(-) create mode 100644 server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/util/cache/ProtobufIssueDiskCacheTest.java 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 373f1dc51fe..b488efe2ffb 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 @@ -194,6 +194,7 @@ public class IssueLifecycle { // fields coming from raw updater.setPastLine(raw, base.getLine()); updater.setPastLocations(raw, base.getLocations()); + updater.setRuleDescriptionContextKey(raw, base.getRuleDescriptionContextKey().orElse(null)); updater.setPastMessage(raw, base.getMessage(), changeContext); updater.setPastGap(raw, base.gap(), changeContext); updater.setPastEffort(raw, base.effort(), changeContext); diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/TrackerRawInputFactory.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/TrackerRawInputFactory.java index d77b4e64bb1..577c15076a2 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/TrackerRawInputFactory.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/TrackerRawInputFactory.java @@ -209,6 +209,7 @@ public class TrackerRawInputFactory { issue.setIsFromExternalRuleEngine(false); issue.setLocations(dbLocationsBuilder.build()); issue.setQuickFixAvailable(reportIssue.getQuickFixAvailable()); + issue.setRuleDescriptionContextKey(reportIssue.hasRuleDescriptionContextKey() ? reportIssue.getRuleDescriptionContextKey() : null); return 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 75f8ecc3a84..d7cc18c9076 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 @@ -19,6 +19,7 @@ */ package org.sonar.ce.task.projectanalysis.util.cache; +import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableSet; import java.io.BufferedOutputStream; @@ -73,7 +74,7 @@ public class ProtobufIssueDiskCache implements DiskCache { @Override public CloseableIterator traverse() { CloseableIterator protoIterator = Protobuf.readStream(file, IssueCache.Issue.parser()); - return new CloseableIterator() { + return new CloseableIterator<>() { @CheckForNull @Override protected DefaultIssue doNext() { @@ -90,7 +91,8 @@ public class ProtobufIssueDiskCache implements DiskCache { }; } - private static DefaultIssue toDefaultIssue(IssueCache.Issue next) { + @VisibleForTesting + static DefaultIssue toDefaultIssue(IssueCache.Issue next) { DefaultIssue defaultIssue = new DefaultIssue(); defaultIssue.setKey(next.getKey()); defaultIssue.setType(RuleType.valueOf(next.getRuleType())); @@ -114,6 +116,7 @@ public class ProtobufIssueDiskCache implements DiskCache { defaultIssue.setAuthorLogin(next.hasAuthorLogin() ? next.getAuthorLogin() : null); next.getCommentsList().forEach(c -> defaultIssue.addComment(toDefaultIssueComment(c))); defaultIssue.setTags(ImmutableSet.copyOf(TAGS_SPLITTER.split(next.getTags()))); + defaultIssue.setRuleDescriptionContextKey(next.hasRuleDescriptionContextKey() ? next.getRuleDescriptionContextKey() : null); defaultIssue.setLocations(next.hasLocations() ? next.getLocations() : null); defaultIssue.setIsFromExternalRuleEngine(next.getIsFromExternalRuleEngine()); defaultIssue.setCreationDate(new Date(next.getCreationDate())); @@ -139,7 +142,8 @@ public class ProtobufIssueDiskCache implements DiskCache { return defaultIssue; } - private static IssueCache.Issue toProto(IssueCache.Issue.Builder builder, DefaultIssue defaultIssue) { + @VisibleForTesting + static IssueCache.Issue toProto(IssueCache.Issue.Builder builder, DefaultIssue defaultIssue) { builder.clear(); builder.setKey(defaultIssue.key()); builder.setRuleType(defaultIssue.type().getDbConstant()); @@ -164,6 +168,7 @@ public class ProtobufIssueDiskCache implements DiskCache { defaultIssue.defaultIssueComments().forEach(c -> builder.addComments(toProtoComment(c))); ofNullable(defaultIssue.tags()).ifPresent(t -> builder.setTags(String.join(TAGS_SEPARATOR, t))); ofNullable(defaultIssue.getLocations()).ifPresent(l -> builder.setLocations((DbIssues.Locations) l)); + defaultIssue.getRuleDescriptionContextKey().ifPresent(builder::setRuleDescriptionContextKey); builder.setIsFromExternalRuleEngine(defaultIssue.isFromExternalRuleEngine()); builder.setCreationDate(defaultIssue.creationDate().getTime()); ofNullable(defaultIssue.updateDate()).map(Date::getTime).ifPresent(builder::setUpdateDate); 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 dd913b0ea50..f4135f33a39 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 @@ -79,6 +79,7 @@ message Issue { optional bool isOnChangedLine = 41; optional bool isNewCodeReferenceIssue = 42; optional bool isNoLongerNewCodeReferenceIssue = 43; + optional string ruleDescriptionContextKey = 44; } 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 c1f1e06bfff..e5dc9f067b8 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 @@ -59,6 +59,7 @@ import static org.sonar.db.rule.RuleTesting.XOO_X1; public class IssueLifecycleTest { private static final Date DEFAULT_DATE = new Date(); private static final Duration DEFAULT_DURATION = Duration.create(10); + private static final String TEST_CONTEXT_KEY = "test_context_key"; private final DumbRule rule = new DumbRule(XOO_X1); @@ -244,7 +245,8 @@ public class IssueLifecycleTest { .setKey("RAW_KEY") .setCreationDate(parseDate("2015-10-01")) .setUpdateDate(parseDate("2015-10-02")) - .setCloseDate(parseDate("2015-10-03")); + .setCloseDate(parseDate("2015-10-03")) + .setRuleDescriptionContextKey(TEST_CONTEXT_KEY); DbIssues.Locations issueLocations = DbIssues.Locations.newBuilder() .setTextRange(DbCommons.TextRange.newBuilder() @@ -297,6 +299,7 @@ public class IssueLifecycleTest { assertThat(raw.selectedAt()).isEqualTo(1000L); assertThat(raw.changes().get(0).get(IssueFieldsSetter.FROM_BRANCH).oldValue()).isEqualTo("master"); assertThat(raw.changes().get(0).get(IssueFieldsSetter.FROM_BRANCH).newValue()).isEqualTo("release-2.x"); + assertThat(raw.getRuleDescriptionContextKey()).contains(TEST_CONTEXT_KEY); verifyNoInteractions(updater); } @@ -316,6 +319,7 @@ public class IssueLifecycleTest { .setNew(true) .setKey("RAW_KEY") .setRuleKey(XOO_X1) + .setRuleDescriptionContextKey("spring") .setCreationDate(parseDate("2015-10-01")) .setUpdateDate(parseDate("2015-10-02")) .setCloseDate(parseDate("2015-10-03")); @@ -341,6 +345,7 @@ public class IssueLifecycleTest { .setLine(10) .setMessage("message") .setGap(15d) + .setRuleDescriptionContextKey("hibernate") .setEffort(Duration.create(15L)) .setManualSeverity(false) .setLocations(issueLocations) @@ -372,6 +377,7 @@ public class IssueLifecycleTest { verify(updater).setPastSeverity(raw, BLOCKER, issueChangeContext); verify(updater).setPastLine(raw, 10); + verify(updater).setRuleDescriptionContextKey(raw, "hibernate"); verify(updater).setPastMessage(raw, "message", issueChangeContext); verify(updater).setPastEffort(raw, Duration.create(15L), issueChangeContext); verify(updater).setPastLocations(raw, issueLocations); @@ -414,4 +420,44 @@ public class IssueLifecycleTest { assertThat(raw.isChanged()).isTrue(); } + + @Test + public void mergeExistingOpenIssue_with_rule_description_context_key_added() { + DefaultIssue raw = new DefaultIssue() + .setNew(true) + .setKey("RAW_KEY") + .setRuleKey(XOO_X1) + .setRuleDescriptionContextKey(TEST_CONTEXT_KEY); + DefaultIssue base = new DefaultIssue() + .setChanged(true) + .setKey("RAW_KEY") + .setResolution(RESOLUTION_FALSE_POSITIVE) + .setStatus(STATUS_RESOLVED) + .setRuleDescriptionContextKey(null); + + underTest.mergeExistingOpenIssue(raw, base); + + assertThat(raw.isChanged()).isTrue(); + assertThat(raw.getRuleDescriptionContextKey()).isEqualTo(raw.getRuleDescriptionContextKey()); + } + + @Test + public void mergeExistingOpenIssue_with_rule_description_context_key_removed() { + DefaultIssue raw = new DefaultIssue() + .setNew(true) + .setKey("RAW_KEY") + .setRuleKey(XOO_X1) + .setRuleDescriptionContextKey(null); + DefaultIssue base = new DefaultIssue() + .setChanged(true) + .setKey("RAW_KEY") + .setResolution(RESOLUTION_FALSE_POSITIVE) + .setStatus(STATUS_RESOLVED) + .setRuleDescriptionContextKey(TEST_CONTEXT_KEY); + + underTest.mergeExistingOpenIssue(raw, base); + + assertThat(raw.isChanged()).isTrue(); + assertThat(raw.getRuleDescriptionContextKey()).isEmpty(); + } } diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/TrackerRawInputFactoryTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/TrackerRawInputFactoryTest.java index f823bebd098..1392fd64f86 100644 --- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/TrackerRawInputFactoryTest.java +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/TrackerRawInputFactoryTest.java @@ -27,6 +27,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; +import java.util.Optional; import java.util.stream.IntStream; import org.apache.commons.codec.digest.DigestUtils; import org.junit.Before; @@ -79,6 +80,7 @@ public class TrackerRawInputFactoryTest { private static final int FILE_REF = 2; private static final int NOT_IN_REPORT_FILE_REF = 3; private static final int ANOTHER_FILE_REF = 4; + private static final String TEST_CONTEXT_KEY = "test_context_key"; @Rule public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule().setRoot(PROJECT); @@ -165,10 +167,35 @@ public class TrackerRawInputFactoryTest { assertThat(issue.tags()).isEmpty(); assertInitializedIssue(issue); assertThat(issue.effort()).isNull(); + assertThat(issue.getRuleDescriptionContextKey()).isEmpty(); assertLocationHashIsMadeOf(input, "intexample=line+of+code+2;"); } + @Test + public void load_issues_from_report_with_rule_description_context_key() { + RuleKey ruleKey = RuleKey.of("java", "S001"); + markRuleAsActive(ruleKey); + when(issueFilter.accept(any(), eq(FILE))).thenReturn(true); + + when(sourceLinesHash.getLineHashesMatchingDBVersion(FILE)).thenReturn(Collections.singletonList("line")); + ScannerReport.Issue reportIssue = ScannerReport.Issue.newBuilder() + .setTextRange(newTextRange(2)) + .setMsg("the message") + .setRuleRepository(ruleKey.repository()) + .setRuleKey(ruleKey.rule()) + .setRuleDescriptionContextKey(TEST_CONTEXT_KEY) + .build(); + reportReader.putIssues(FILE.getReportAttributes().getRef(), singletonList(reportIssue)); + Input input = underTest.create(FILE); + + Collection issues = input.getIssues(); + assertThat(issues) + .hasSize(1) + .extracting(DefaultIssue::getRuleDescriptionContextKey) + .containsOnly(Optional.of(TEST_CONTEXT_KEY)); + } + @Test public void calculateLocationHash_givenIssueOn3Lines_calculateHashOn3Lines() { RuleKey ruleKey = RuleKey.of("java", "S001"); 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 new file mode 100644 index 00000000000..e04a954d580 --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/util/cache/ProtobufIssueDiskCacheTest.java @@ -0,0 +1,96 @@ +/* + * SonarQube + * Copyright (C) 2009-2022 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.ce.task.projectanalysis.util.cache; + +import java.util.Date; +import org.junit.Test; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rules.RuleType; +import org.sonar.core.issue.DefaultIssue; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ProtobufIssueDiskCacheTest { + + private static final String TEST_CONTEXT_KEY = "test_context_key"; + + @Test + public void toDefaultIssue_whenRuleDescriptionContextKeyPresent_shouldSetItInDefaultIssue() { + IssueCache.Issue issue = prepareIssueWithCompulsoryFields() + .setRuleDescriptionContextKey(TEST_CONTEXT_KEY) + .build(); + + DefaultIssue defaultIssue = ProtobufIssueDiskCache.toDefaultIssue(issue); + + assertThat(defaultIssue.getRuleDescriptionContextKey()).contains(TEST_CONTEXT_KEY); + } + + @Test + public void toDefaultIssue_whenRuleDescriptionContextKeyAbsent_shouldNotSetItInDefaultIssue() { + IssueCache.Issue issue = prepareIssueWithCompulsoryFields() + .build(); + + DefaultIssue defaultIssue = ProtobufIssueDiskCache.toDefaultIssue(issue); + + assertThat(defaultIssue.getRuleDescriptionContextKey()).isEmpty(); + } + + @Test + public void toProto_whenRuleDescriptionContextKeySet_shouldCopyToIssueProto() { + DefaultIssue defaultIssue = createDefaultIssueWithMandatoryFields(); + defaultIssue.setRuleDescriptionContextKey(TEST_CONTEXT_KEY); + + IssueCache.Issue issue = ProtobufIssueDiskCache.toProto(IssueCache.Issue.newBuilder(), defaultIssue); + + assertThat(issue.hasRuleDescriptionContextKey()).isTrue(); + assertThat(issue.getRuleDescriptionContextKey()).isEqualTo(TEST_CONTEXT_KEY); + } + + @Test + public void toProto_whenRuleDescriptionContextKeyNotSet_shouldCopyToIssueProto() { + DefaultIssue defaultIssue = createDefaultIssueWithMandatoryFields(); + defaultIssue.setRuleDescriptionContextKey(null); + + IssueCache.Issue issue = ProtobufIssueDiskCache.toProto(IssueCache.Issue.newBuilder(), defaultIssue); + + assertThat(issue.hasRuleDescriptionContextKey()).isFalse(); + } + + private static DefaultIssue createDefaultIssueWithMandatoryFields() { + DefaultIssue defaultIssue = new DefaultIssue(); + defaultIssue.setKey("test_issue:key"); + defaultIssue.setType(RuleType.CODE_SMELL); + defaultIssue.setComponentKey("component_key"); + defaultIssue.setProjectUuid("project_uuid"); + defaultIssue.setProjectKey("project_key"); + defaultIssue.setRuleKey(RuleKey.of("ruleRepo", "rule1")); + defaultIssue.setStatus("open"); + defaultIssue.setCreationDate(new Date()); + return defaultIssue; + } + + private static IssueCache.Issue.Builder prepareIssueWithCompulsoryFields() { + return IssueCache.Issue.newBuilder() + .setRuleType(RuleType.CODE_SMELL.getDbConstant()) + .setRuleKey("test_issue:key") + .setStatus("open"); + } + +} 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 8e6ad9f5a36..7611cff2f3c 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 @@ -126,6 +126,7 @@ public final class IssueDto implements Serializable { .setRuleKey(issue.ruleKey().repository(), issue.ruleKey().rule()) .setExternal(issue.isFromExternalRuleEngine()) .setTags(issue.tags()) + .setRuleDescriptionContextKey(issue.getRuleDescriptionContextKey().orElse(null)) .setComponentUuid(issue.componentUuid()) .setComponentKey(issue.componentKey()) .setModuleUuidPath(issue.moduleUuidPath()) @@ -174,6 +175,7 @@ public final class IssueDto implements Serializable { .setRuleKey(issue.ruleKey().repository(), issue.ruleKey().rule()) .setExternal(issue.isFromExternalRuleEngine()) .setTags(issue.tags()) + .setRuleDescriptionContextKey(issue.getRuleDescriptionContextKey().orElse(null)) .setComponentUuid(issue.componentUuid()) .setComponentKey(issue.componentKey()) .setModuleUuidPath(issue.moduleUuidPath()) @@ -734,6 +736,7 @@ public final class IssueDto implements Serializable { issue.setManualSeverity(manualSeverity); issue.setRuleKey(getRuleKey()); issue.setTags(getTags()); + issue.setRuleDescriptionContextKey(ruleDescriptionContextKey); issue.setLanguage(language); issue.setAuthorLogin(authorLogin); issue.setNew(false); 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 cacc799d7a2..53c2eb6e3e8 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 @@ -38,6 +38,8 @@ import static org.assertj.core.api.Assertions.assertThat; public class IssueDtoTest { + private static final String TEST_CONTEXT_KEY = "test_context_key"; + @Test public void set_issue_fields() { Date createdAt = DateUtils.addDays(new Date(), -5); @@ -67,7 +69,8 @@ public class IssueDtoTest { .setAuthorLogin("pierre") .setIssueCreationDate(createdAt) .setIssueUpdateDate(updatedAt) - .setIssueCloseDate(closedAt); + .setIssueCloseDate(closedAt) + .setRuleDescriptionContextKey(TEST_CONTEXT_KEY); DefaultIssue issue = dto.toDefaultIssue(); assertThat(issue.key()).isEqualTo("100"); @@ -94,6 +97,7 @@ public class IssueDtoTest { assertThat(issue.closeDate()).isEqualTo(DateUtils.truncate(closedAt, Calendar.SECOND)); assertThat(issue.isNew()).isFalse(); assertThat(issue.isNewCodeReferenceIssue()).isFalse(); + assertThat(issue.getRuleDescriptionContextKey()).contains(TEST_CONTEXT_KEY); } @Test @@ -165,6 +169,7 @@ public class IssueDtoTest { assertThat(issueDto.isQuickFixAvailable()).isTrue(); assertThat(issueDto.isNewCodeReferenceIssue()).isTrue(); + assertThat(issueDto.getOptionalRuleDescriptionContextKey()).contains(TEST_CONTEXT_KEY); } @Test @@ -197,6 +202,7 @@ public class IssueDtoTest { assertThat(issueDto.isQuickFixAvailable()).isTrue(); assertThat(issueDto.isNewCodeReferenceIssue()).isTrue(); + assertThat(issueDto.getOptionalRuleDescriptionContextKey()).contains(TEST_CONTEXT_KEY); } private DefaultIssue createExampleDefaultIssue(Date dateNow) { @@ -227,7 +233,8 @@ public class IssueDtoTest { .setUpdateDate(dateNow) .setSelectedAt(dateNow.getTime()) .setQuickFixAvailable(true) - .setIsNewCodeReferenceIssue(true); + .setIsNewCodeReferenceIssue(true) + .setRuleDescriptionContextKey(TEST_CONTEXT_KEY); return defaultIssue; } } 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 2d85d30ab16..bdcc5837f13 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 @@ -160,6 +160,17 @@ public class IssueFieldsSetter { return false; } + public boolean setRuleDescriptionContextKey(DefaultIssue issue, @Nullable String previousContextKey) { + String currentContextKey = issue.getRuleDescriptionContextKey().orElse(null); + issue.setRuleDescriptionContextKey(previousContextKey); + if (!Objects.equals(currentContextKey, previousContextKey)) { + issue.setRuleDescriptionContextKey(currentContextKey); + issue.setChanged(true); + return true; + } + return false; + } + public boolean setLocations(DefaultIssue issue, @Nullable Object locations) { if (!Objects.equals(locations, issue.getLocations())) { issue.setLocations(locations); @@ -173,7 +184,6 @@ public class IssueFieldsSetter { Object currentLocations = issue.getLocations(); issue.setLocations(previousLocations); return setLocations(issue, currentLocations); - } public boolean setResolution(DefaultIssue issue, @Nullable String resolution, IssueChangeContext context) { 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 2ab5b815ed0..b70ea011845 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 @@ -43,6 +43,7 @@ import static org.sonar.server.issue.IssueFieldsSetter.UNUSED; public class IssueFieldsSetterTest { + private final String DEFAULT_RULE_DESCRIPTION_CONTEXT_KEY = "spring"; private DefaultIssue issue = new DefaultIssue(); private IssueChangeContext context = IssueChangeContext.createUser(new Date(), "user_uuid"); @@ -511,4 +512,40 @@ public class IssueFieldsSetterTest { assertThat(issue.isChanged()).isTrue(); assertThat(issue.updateDate()).isEqualTo(DateUtils.truncate(context.date(), Calendar.SECOND)); } + + @Test + public void setRuleDescriptionContextKey_setContextKeyIfPreviousValueIsNull() { + issue.setRuleDescriptionContextKey(DEFAULT_RULE_DESCRIPTION_CONTEXT_KEY); + boolean updated = underTest.setRuleDescriptionContextKey(issue, null); + + assertThat(updated).isTrue(); + assertThat(issue.getRuleDescriptionContextKey()).contains(DEFAULT_RULE_DESCRIPTION_CONTEXT_KEY); + } + + @Test + public void setRuleDescriptionContextKey_dontSetContextKeyIfPreviousValueIsTheSame() { + issue.setRuleDescriptionContextKey(DEFAULT_RULE_DESCRIPTION_CONTEXT_KEY); + boolean updated = underTest.setRuleDescriptionContextKey(issue, DEFAULT_RULE_DESCRIPTION_CONTEXT_KEY); + + assertThat(updated).isFalse(); + assertThat(issue.getRuleDescriptionContextKey()).contains(DEFAULT_RULE_DESCRIPTION_CONTEXT_KEY); + } + + @Test + public void setRuleDescriptionContextKey_dontSetContextKeyIfBothValuesAreNull() { + issue.setRuleDescriptionContextKey(null); + boolean updated = underTest.setRuleDescriptionContextKey(issue, null); + + assertThat(updated).isFalse(); + assertThat(issue.getRuleDescriptionContextKey()).isEmpty(); + } + + @Test + public void setRuleDescriptionContextKey_setContextKeyIfValuesAreDifferent() { + issue.setRuleDescriptionContextKey(DEFAULT_RULE_DESCRIPTION_CONTEXT_KEY); + boolean updated = underTest.setRuleDescriptionContextKey(issue, "hibernate"); + + assertThat(updated).isTrue(); + assertThat(issue.getRuleDescriptionContextKey()).contains(DEFAULT_RULE_DESCRIPTION_CONTEXT_KEY); + } } 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 dde978b82c3..4f92924e0c4 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 @@ -33,8 +33,8 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Optional; import java.util.Set; +import java.util.Optional; import javax.annotation.CheckForNull; import javax.annotation.Nullable; import org.apache.commons.lang.StringUtils; diff --git a/sonar-core/src/test/java/org/sonar/core/issue/DefaultIssueTest.java b/sonar-core/src/test/java/org/sonar/core/issue/DefaultIssueTest.java index 3c8ca77a091..e63651e7a43 100644 --- a/sonar-core/src/test/java/org/sonar/core/issue/DefaultIssueTest.java +++ b/sonar-core/src/test/java/org/sonar/core/issue/DefaultIssueTest.java @@ -33,6 +33,7 @@ import static org.mockito.Mockito.mock; public class DefaultIssueTest { + private static final String TEST_CONTEXT_KEY = "test_context_key"; private DefaultIssue issue = new DefaultIssue(); @Test @@ -65,7 +66,8 @@ public class DefaultIssueTest { .setCreationDate(new SimpleDateFormat("yyyy-MM-dd").parse("2013-08-19")) .setUpdateDate(new SimpleDateFormat("yyyy-MM-dd").parse("2013-08-20")) .setCloseDate(new SimpleDateFormat("yyyy-MM-dd").parse("2013-08-21")) - .setSelectedAt(1400000000000L); + .setSelectedAt(1400000000000L) + .setRuleDescriptionContextKey(TEST_CONTEXT_KEY); assertThat(issue.key()).isEqualTo("ABCD"); assertThat(issue.componentKey()).isEqualTo("org.sample.Sample"); @@ -97,6 +99,7 @@ public class DefaultIssueTest { assertThat(issue.updateDate()).isEqualTo(new SimpleDateFormat("yyyy-MM-dd").parse("2013-08-20")); assertThat(issue.closeDate()).isEqualTo(new SimpleDateFormat("yyyy-MM-dd").parse("2013-08-21")); assertThat(issue.selectedAt()).isEqualTo(1400000000000L); + assertThat(issue.getRuleDescriptionContextKey()).contains(TEST_CONTEXT_KEY); } @Test -- 2.39.5