From: Julien HENRY Date: Mon, 16 Oct 2017 14:25:09 +0000 (+0200) Subject: SONAR-9987 Add a changelog entry when copying/merging issues X-Git-Tag: 6.7-RC1~151 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=a45212f715dbf525d0686523b76fcb46f62f101a;p=sonarqube.git SONAR-9987 Add a changelog entry when copying/merging issues --- diff --git a/server/sonar-db-dao/src/main/java/org/sonar/core/issue/ShortBranchIssue.java b/server/sonar-db-dao/src/main/java/org/sonar/core/issue/ShortBranchIssue.java index 11095124c97..f6c6bcd977b 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/core/issue/ShortBranchIssue.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/core/issue/ShortBranchIssue.java @@ -33,14 +33,16 @@ public class ShortBranchIssue implements Trackable { private final String lineHash; private final RuleKey ruleKey; private final String status; + private final String branchName; - public ShortBranchIssue(String key, @Nullable Integer line, String message, @Nullable String lineHash, RuleKey ruleKey, String status) { + public ShortBranchIssue(String key, @Nullable Integer line, String message, @Nullable String lineHash, RuleKey ruleKey, String status, String branchName) { this.key = key; this.line = line; this.message = message; this.lineHash = lineHash; this.ruleKey = ruleKey; this.status = status; + this.branchName = branchName; } public String getKey() { @@ -74,6 +76,10 @@ public class ShortBranchIssue implements Trackable { return status; } + public String getBranchName() { + return branchName; + } + @Override public int hashCode() { return key.hashCode(); diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/ShortBranchIssueDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/ShortBranchIssueDto.java index 3a68ea03af2..3c1901041e4 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/ShortBranchIssueDto.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/ShortBranchIssueDto.java @@ -38,6 +38,7 @@ public final class ShortBranchIssueDto implements Serializable { // joins private String ruleKey; private String ruleRepo; + private String branchName; public String getKey() { return kee; @@ -68,6 +69,15 @@ public final class ShortBranchIssueDto implements Serializable { return this; } + public String getBranchName() { + return branchName; + } + + public ShortBranchIssueDto setBranchName(String s) { + this.branchName = s; + return this; + } + public String getStatus() { return status; } @@ -105,6 +115,6 @@ public final class ShortBranchIssueDto implements Serializable { } public static ShortBranchIssue toShortBranchIssue(ShortBranchIssueDto dto) { - return new ShortBranchIssue(dto.getKey(), dto.getLine(), dto.getMessage(), dto.getChecksum(), dto.getRuleKey(), dto.getStatus()); + return new ShortBranchIssue(dto.getKey(), dto.getLine(), dto.getMessage(), dto.getChecksum(), dto.getRuleKey(), dto.getStatus(), dto.getBranchName()); } } 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 8c365703f9e..09d43435502 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 @@ -221,9 +221,11 @@ i.status as status, i.checksum as checksum, r.plugin_rule_key as ruleKey, - r.plugin_name as ruleRepo + r.plugin_name as ruleRepo, + b.kee as branchName from issues i inner join rules r on r.id = i.rule_id + inner join project_branches b on i.project_uuid = b.uuid where i.component_uuid in #{key,jdbcType=VARCHAR} diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDaoTest.java index 5201db84197..8c4682215c1 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDaoTest.java @@ -33,6 +33,7 @@ import org.sonar.api.rule.RuleKey; import org.sonar.api.utils.System2; import org.sonar.db.DbTester; import org.sonar.db.RowNotFoundException; +import org.sonar.db.component.BranchType; import org.sonar.db.component.ComponentDto; import org.sonar.db.component.ComponentTesting; import org.sonar.db.organization.OrganizationDto; @@ -185,14 +186,19 @@ public class IssueDaoTest { @Test public void selectResolvedOrConfirmedByComponentUuid() { RuleDefinitionDto rule = db.rules().insert(); - ComponentDto project = db.components().insertPrivateProject(); - ComponentDto file = db.components().insertComponent(newFileDto(project)); - IssueDto openIssue = db.issues().insert(rule, project, file, i -> i.setStatus(Issue.STATUS_OPEN).setResolution(null)); - IssueDto closedIssue = db.issues().insert(rule, project, file, i -> i.setStatus(Issue.STATUS_CLOSED).setResolution(Issue.RESOLUTION_FIXED)); - IssueDto reopenedIssue = db.issues().insert(rule, project, file, i -> i.setStatus(Issue.STATUS_REOPENED).setResolution(null)); - IssueDto confirmedIssue = db.issues().insert(rule, project, file, i -> i.setStatus(Issue.STATUS_CONFIRMED).setResolution(null)); - IssueDto wontfixIssue = db.issues().insert(rule, project, file, i -> i.setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_WONT_FIX)); - IssueDto fpIssue = db.issues().insert(rule, project, file, i -> i.setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_FALSE_POSITIVE)); + ComponentDto project = db.components().insertMainBranch(); + ComponentDto projectBranch = db.components().insertProjectBranch(project, + b -> b.setKey("feature/foo") + .setBranchType(BranchType.SHORT)); + + ComponentDto file = db.components().insertComponent(newFileDto(projectBranch)); + + IssueDto openIssue = db.issues().insert(rule, projectBranch, file, i -> i.setStatus(Issue.STATUS_OPEN).setResolution(null)); + IssueDto closedIssue = db.issues().insert(rule, projectBranch, file, i -> i.setStatus(Issue.STATUS_CLOSED).setResolution(Issue.RESOLUTION_FIXED)); + IssueDto reopenedIssue = db.issues().insert(rule, projectBranch, file, i -> i.setStatus(Issue.STATUS_REOPENED).setResolution(null)); + IssueDto confirmedIssue = db.issues().insert(rule, projectBranch, file, i -> i.setStatus(Issue.STATUS_CONFIRMED).setResolution(null)); + IssueDto wontfixIssue = db.issues().insert(rule, projectBranch, file, i -> i.setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_WONT_FIX)); + IssueDto fpIssue = db.issues().insert(rule, projectBranch, file, i -> i.setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_FALSE_POSITIVE)); assertThat(underTest.selectResolvedOrConfirmedByComponentUuids(db.getSession(), Collections.singletonList(file.uuid()))) .extracting("kee") @@ -202,9 +208,13 @@ public class IssueDaoTest { @Test public void selectResolvedOrConfirmedByComponentUuid_should_correctly_map_required_fields() { RuleDefinitionDto rule = db.rules().insert(); - ComponentDto project = db.components().insertPrivateProject(); - ComponentDto file = db.components().insertComponent(newFileDto(project)); - IssueDto fpIssue = db.issues().insert(rule, project, file, i -> i.setStatus("RESOLVED").setResolution("FALSE-POSITIVE")); + ComponentDto project = db.components().insertMainBranch(); + ComponentDto projectBranch = db.components().insertProjectBranch(project, + b -> b.setKey("feature/foo") + .setBranchType(BranchType.SHORT)); + + ComponentDto file = db.components().insertComponent(newFileDto(projectBranch)); + IssueDto fpIssue = db.issues().insert(rule, projectBranch, file, i -> i.setStatus("RESOLVED").setResolution("FALSE-POSITIVE")); ShortBranchIssueDto fp = underTest.selectResolvedOrConfirmedByComponentUuids(db.getSession(), Collections.singletonList(file.uuid())).get(0); assertThat(fp.getLine()).isEqualTo(fpIssue.getLine()); diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/MergeBranchComponentUuids.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/MergeBranchComponentUuids.java index 9817dadbfdf..e91d1fc8c4a 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/MergeBranchComponentUuids.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/MergeBranchComponentUuids.java @@ -37,31 +37,38 @@ public class MergeBranchComponentUuids { private final AnalysisMetadataHolder analysisMetadataHolder; private final DbClient dbClient; private Map uuidsByKey; + private String mergeBranchName; public MergeBranchComponentUuids(AnalysisMetadataHolder analysisMetadataHolder, DbClient dbClient) { this.analysisMetadataHolder = analysisMetadataHolder; this.dbClient = dbClient; } - private void loadMergeBranchComponents() { - String mergeBranchUuid = analysisMetadataHolder.getBranch().get().getMergeBranchUuid().get(); + private void lazyInit() { + if (uuidsByKey == null) { + String mergeBranchUuid = analysisMetadataHolder.getBranch().get().getMergeBranchUuid().get(); + + uuidsByKey = new HashMap<>(); + try (DbSession dbSession = dbClient.openSession(false)) { - uuidsByKey = new HashMap<>(); - try (DbSession dbSession = dbClient.openSession(false)) { + List components = dbClient.componentDao().selectByProjectUuid(mergeBranchUuid, dbSession); + for (ComponentDto dto : components) { + uuidsByKey.put(dto.getKey(), dto.uuid()); + } - List components = dbClient.componentDao().selectByProjectUuid(mergeBranchUuid, dbSession); - for (ComponentDto dto : components) { - uuidsByKey.put(dto.getKey(), dto.uuid()); + mergeBranchName = dbClient.branchDao().selectByUuid(dbSession, mergeBranchUuid).get().getKey(); } } } + public String getMergeBranchName() { + lazyInit(); + return mergeBranchName; + } + @CheckForNull public String getUuid(String dbKey) { - if (uuidsByKey == null) { - loadMergeBranchComponents(); - } - + lazyInit(); String cleanComponentKey = removeBranchFromKey(dbKey); return uuidsByKey.get(cleanComponentKey); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IntegrateIssuesVisitor.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IntegrateIssuesVisitor.java index e0c9bd92fc1..d381db8fa36 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IntegrateIssuesVisitor.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IntegrateIssuesVisitor.java @@ -26,6 +26,7 @@ import org.sonar.core.issue.DefaultIssue; import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolder; import org.sonar.server.computation.task.projectanalysis.component.Component; import org.sonar.server.computation.task.projectanalysis.component.CrawlerDepthLimit; +import org.sonar.server.computation.task.projectanalysis.component.MergeBranchComponentUuids; import org.sonar.server.computation.task.projectanalysis.component.TypeAwareVisitorAdapter; import org.sonar.server.util.cache.DiskCache; @@ -39,9 +40,11 @@ public class IntegrateIssuesVisitor extends TypeAwareVisitorAdapter { private final IssueTrackingDelegator issueTracking; private final ShortBranchIssueMerger issueStatusCopier; private final AnalysisMetadataHolder analysisMetadataHolder; + private final MergeBranchComponentUuids mergeBranchComponentUuids; public IntegrateIssuesVisitor(IssueCache issueCache, IssueLifecycle issueLifecycle, IssueVisitors issueVisitors, - AnalysisMetadataHolder analysisMetadataHolder, IssueTrackingDelegator issueTracking, ShortBranchIssueMerger issueStatusCopier) { + AnalysisMetadataHolder analysisMetadataHolder, IssueTrackingDelegator issueTracking, ShortBranchIssueMerger issueStatusCopier, + MergeBranchComponentUuids mergeBranchComponentUuids) { super(CrawlerDepthLimit.FILE, POST_ORDER); this.issueCache = issueCache; this.issueLifecycle = issueLifecycle; @@ -49,6 +52,7 @@ public class IntegrateIssuesVisitor extends TypeAwareVisitorAdapter { this.analysisMetadataHolder = analysisMetadataHolder; this.issueTracking = issueTracking; this.issueStatusCopier = issueStatusCopier; + this.mergeBranchComponentUuids = mergeBranchComponentUuids; } @Override @@ -92,7 +96,7 @@ public class IntegrateIssuesVisitor extends TypeAwareVisitorAdapter { for (Map.Entry entry : matched.entrySet()) { DefaultIssue raw = entry.getKey(); DefaultIssue base = entry.getValue(); - issueLifecycle.copyExistingOpenIssueFromLongLivingBranch(raw, base); + issueLifecycle.copyExistingOpenIssueFromLongLivingBranch(raw, base, mergeBranchComponentUuids.getMergeBranchName()); process(component, raw, cacheAppender); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IssueLifecycle.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IssueLifecycle.java index 6b40c04feb4..00322067935 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IssueLifecycle.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IssueLifecycle.java @@ -45,13 +45,16 @@ public class IssueLifecycle { private final IssueChangeContext changeContext; private final IssueFieldsSetter updater; private final DebtCalculator debtCalculator; + private final AnalysisMetadataHolder analysisMetadataHolder; public IssueLifecycle(AnalysisMetadataHolder analysisMetadataHolder, IssueWorkflow workflow, IssueFieldsSetter updater, DebtCalculator debtCalculator) { - this(IssueChangeContext.createScan(new Date(analysisMetadataHolder.getAnalysisDate())), workflow, updater, debtCalculator); + this(analysisMetadataHolder, IssueChangeContext.createScan(new Date(analysisMetadataHolder.getAnalysisDate())), workflow, updater, debtCalculator); } @VisibleForTesting - IssueLifecycle(IssueChangeContext changeContext, IssueWorkflow workflow, IssueFieldsSetter updater, DebtCalculator debtCalculator) { + IssueLifecycle(AnalysisMetadataHolder analysisMetadataHolder, IssueChangeContext changeContext, IssueWorkflow workflow, IssueFieldsSetter updater, + DebtCalculator debtCalculator) { + this.analysisMetadataHolder = analysisMetadataHolder; this.workflow = workflow; this.updater = updater; this.debtCalculator = debtCalculator; @@ -66,13 +69,19 @@ public class IssueLifecycle { issue.setEffort(debtCalculator.calculate(issue)); } - public void copyExistingOpenIssueFromLongLivingBranch(DefaultIssue raw, DefaultIssue base) { + public void copyExistingOpenIssueFromLongLivingBranch(DefaultIssue raw, DefaultIssue base, String fromLongBranchName) { raw.setKey(Uuids.create()); raw.setNew(false); copyIssueAttributes(raw, base); + raw.setFieldChange(changeContext, IssueFieldsSetter.FROM_LONG_BRANCH, fromLongBranchName, analysisMetadataHolder.getBranch().get().getName()); } - public void copyIssueAttributes(DefaultIssue to, DefaultIssue from) { + public void mergeConfirmedOrResolvedFromShortLivingBranch(DefaultIssue raw, DefaultIssue base, String fromShortBranchName) { + copyIssueAttributes(raw, base); + raw.setFieldChange(changeContext, IssueFieldsSetter.FROM_SHORT_BRANCH, fromShortBranchName, analysisMetadataHolder.getBranch().get().getName()); + } + + private void copyIssueAttributes(DefaultIssue to, DefaultIssue from) { to.setCopied(true); copyFields(to, from); if (from.manualSeverity()) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssueMerger.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssueMerger.java index 79d5e5c2a77..41d55e85704 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssueMerger.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssueMerger.java @@ -56,7 +56,7 @@ public class ShortBranchIssueMerger { for (Map.Entry e : matchedRaws.entrySet()) { ShortBranchIssue issue = e.getValue(); - issueLifecycle.copyIssueAttributes(e.getKey(), defaultIssues.get(issue)); + issueLifecycle.mergeConfirmedOrResolvedFromShortLivingBranch(e.getKey(), defaultIssues.get(issue), issue.getBranchName()); } } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueFieldsSetter.java b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueFieldsSetter.java index a1de892c458..1bfdd848ce8 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueFieldsSetter.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueFieldsSetter.java @@ -58,6 +58,8 @@ public class IssueFieldsSetter { public static final String STATUS = "status"; public static final String AUTHOR = "author"; public static final String FILE = "file"; + public static final String FROM_LONG_BRANCH = "from_long_branch"; + public static final String FROM_SHORT_BRANCH = "from_short_branch"; /** * It should be renamed to 'effort', but it hasn't been done to prevent a massive update in database diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/ChangelogAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/ChangelogAction.java index 35542267b9b..b1eebdecdd6 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/ChangelogAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/ChangelogAction.java @@ -175,5 +175,6 @@ public class ChangelogAction implements IssuesWsAction { ComponentDto file = files.get(fileUuid); return file == null ? null : file.longName(); } + } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IntegrateIssuesVisitorTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IntegrateIssuesVisitorTest.java index 1427302612c..0f695c19d65 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IntegrateIssuesVisitorTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IntegrateIssuesVisitorTest.java @@ -119,6 +119,8 @@ public class IntegrateIssuesVisitorTest { private MergeBranchComponentUuids mergeBranchComponentsUuids; @Mock private ShortBranchIssueMerger issueStatusCopier; + @Mock + private MergeBranchComponentUuids mergeBranchComponentUuids; ArgumentCaptor defaultIssueCaptor; @@ -150,7 +152,7 @@ public class IntegrateIssuesVisitorTest { treeRootHolder.setRoot(PROJECT); issueCache = new IssueCache(temp.newFile(), System2.INSTANCE); when(issueFilter.accept(any(DefaultIssue.class), eq(FILE))).thenReturn(true); - underTest = new IntegrateIssuesVisitor(issueCache, issueLifecycle, issueVisitors, analysisMetadataHolder, trackingDelegator, issueStatusCopier); + underTest = new IntegrateIssuesVisitor(issueCache, issueLifecycle, issueVisitors, analysisMetadataHolder, trackingDelegator, issueStatusCopier, mergeBranchComponentUuids); } @Test @@ -258,6 +260,7 @@ public class IntegrateIssuesVisitorTest { public void copy_issues_when_creating_new_long_living_branch() throws Exception { when(mergeBranchComponentsUuids.getUuid(FILE_KEY)).thenReturn(FILE_UUID_ON_BRANCH); + when(mergeBranchComponentUuids.getMergeBranchName()).thenReturn("master"); when(analysisMetadataHolder.isLongLivingBranch()).thenReturn(true); when(analysisMetadataHolder.isFirstAnalysis()).thenReturn(true); @@ -284,7 +287,7 @@ public class IntegrateIssuesVisitorTest { ArgumentCaptor rawIssueCaptor = ArgumentCaptor.forClass(DefaultIssue.class); ArgumentCaptor baseIssueCaptor = ArgumentCaptor.forClass(DefaultIssue.class); - verify(issueLifecycle).copyExistingOpenIssueFromLongLivingBranch(rawIssueCaptor.capture(), baseIssueCaptor.capture()); + verify(issueLifecycle).copyExistingOpenIssueFromLongLivingBranch(rawIssueCaptor.capture(), baseIssueCaptor.capture(), eq("master")); assertThat(rawIssueCaptor.getValue().severity()).isEqualTo(Severity.BLOCKER); assertThat(baseIssueCaptor.getValue().severity()).isEqualTo(Severity.MAJOR); diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IssueLifecycleTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IssueLifecycleTest.java index ecd79be5617..5b13b2ea7f8 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IssueLifecycleTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IssueLifecycleTest.java @@ -21,12 +21,16 @@ package org.sonar.server.computation.task.projectanalysis.issue; import com.google.common.collect.ImmutableMap; import java.util.Date; +import org.junit.Rule; import org.junit.Test; import org.sonar.api.utils.Duration; import org.sonar.core.issue.DefaultIssue; +import org.sonar.core.issue.DefaultIssueComment; import org.sonar.core.issue.IssueChangeContext; import org.sonar.db.protobuf.DbCommons; import org.sonar.db.protobuf.DbIssues; +import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolderRule; +import org.sonar.server.computation.task.projectanalysis.analysis.Branch; import org.sonar.server.issue.IssueFieldsSetter; import org.sonar.server.issue.workflow.IssueWorkflow; @@ -57,7 +61,10 @@ public class IssueLifecycleTest { DebtCalculator debtCalculator = mock(DebtCalculator.class); - IssueLifecycle underTest = new IssueLifecycle(issueChangeContext, workflow, updater, debtCalculator); + @Rule + public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule(); + + IssueLifecycle underTest = new IssueLifecycle(analysisMetadataHolder, issueChangeContext, workflow, updater, debtCalculator); @Test public void initNewOpenIssue() throws Exception { @@ -81,9 +88,23 @@ public class IssueLifecycleTest { DefaultIssue fromShort = new DefaultIssue(); fromShort.setResolution("resolution"); fromShort.setStatus("status"); - underTest.copyIssueAttributes(raw, fromShort); + + Date commentDate = new Date(); + fromShort.addComment(new DefaultIssueComment() + .setIssueKey("short") + .setCreatedAt(commentDate) + .setUserLogin("user") + .setMarkdownText("A comment")); + + Branch branch = mock(Branch.class); + when(branch.getName()).thenReturn("master"); + analysisMetadataHolder.setBranch(branch); + + underTest.mergeConfirmedOrResolvedFromShortLivingBranch(raw, fromShort, "feature/foo"); assertThat(raw.resolution()).isEqualTo("resolution"); assertThat(raw.status()).isEqualTo("status"); + assertThat(raw.changes().get(0).get(IssueFieldsSetter.FROM_SHORT_BRANCH).oldValue()).isEqualTo("feature/foo"); + assertThat(raw.changes().get(0).get(IssueFieldsSetter.FROM_SHORT_BRANCH).newValue()).isEqualTo("master"); } @Test @@ -123,7 +144,11 @@ public class IssueLifecycleTest { when(debtCalculator.calculate(raw)).thenReturn(DEFAULT_DURATION); - underTest.copyExistingOpenIssueFromLongLivingBranch(raw, base); + Branch branch = mock(Branch.class); + when(branch.getName()).thenReturn("release-2.x"); + analysisMetadataHolder.setBranch(branch); + + underTest.copyExistingOpenIssueFromLongLivingBranch(raw, base, "master"); assertThat(raw.isNew()).isFalse(); assertThat(raw.isCopied()).isTrue(); @@ -140,6 +165,8 @@ public class IssueLifecycleTest { assertThat(raw.debt()).isEqualTo(DEFAULT_DURATION); assertThat(raw.isOnDisabledRule()).isTrue(); assertThat(raw.selectedAt()).isEqualTo(1000L); + assertThat(raw.changes().get(0).get(IssueFieldsSetter.FROM_LONG_BRANCH).oldValue()).isEqualTo("master"); + assertThat(raw.changes().get(0).get(IssueFieldsSetter.FROM_LONG_BRANCH).newValue()).isEqualTo("release-2.x"); verifyZeroInteractions(updater); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssueMergerTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssueMergerTest.java index fcda03e282c..134f83b9d4e 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssueMergerTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssueMergerTest.java @@ -72,7 +72,7 @@ public class ShortBranchIssueMergerTest { @Test public void do_nothing_if_no_new_issue() { DefaultIssue i = createIssue("issue1", "rule1", Issue.STATUS_CONFIRMED, null); - when(resolvedShortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component)).thenReturn(Collections.singleton(newShortBranchIssue(i))); + when(resolvedShortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component)).thenReturn(Collections.singleton(newShortBranchIssue(i, "myBranch"))); copier.tryMerge(component, Collections.emptyList()); verify(resolvedShortBranchIssuesLoader).loadCandidateIssuesForMergingInTargetBranch(component); @@ -82,7 +82,7 @@ public class ShortBranchIssueMergerTest { @Test public void update_status_on_matches() { DefaultIssue issue1 = createIssue("issue1", "rule1", Issue.STATUS_CONFIRMED, null); - ShortBranchIssue shortBranchIssue = newShortBranchIssue(issue1); + ShortBranchIssue shortBranchIssue = newShortBranchIssue(issue1, "myBranch"); DefaultIssue newIssue = createIssue("issue2", "rule1", Issue.STATUS_OPEN, null); when(resolvedShortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component)).thenReturn(Collections.singleton(shortBranchIssue)); @@ -91,21 +91,21 @@ public class ShortBranchIssueMergerTest { ArgumentCaptor captor = ArgumentCaptor.forClass(Collection.class); verify(resolvedShortBranchIssuesLoader).loadDefaultIssuesWithChanges(captor.capture()); assertThat(captor.getValue()).containsOnly(shortBranchIssue); - verify(issueLifecycle).copyIssueAttributes(newIssue, issue1); + verify(issueLifecycle).mergeConfirmedOrResolvedFromShortLivingBranch(newIssue, issue1, "myBranch"); } @Test public void prefer_resolved_issues() { - ShortBranchIssue shortBranchIssue1 = newShortBranchIssue(createIssue("issue1", "rule1", Issue.STATUS_CONFIRMED, null)); - ShortBranchIssue shortBranchIssue2 = newShortBranchIssue(createIssue("issue2", "rule1", Issue.STATUS_CONFIRMED, null)); + ShortBranchIssue shortBranchIssue1 = newShortBranchIssue(createIssue("issue1", "rule1", Issue.STATUS_CONFIRMED, null), "myBranch1"); + ShortBranchIssue shortBranchIssue2 = newShortBranchIssue(createIssue("issue2", "rule1", Issue.STATUS_CONFIRMED, null), "myBranch2"); DefaultIssue issue3 = createIssue("issue3", "rule1", Issue.STATUS_RESOLVED, Issue.RESOLUTION_FALSE_POSITIVE); - ShortBranchIssue shortBranchIssue3 = newShortBranchIssue(issue3); + ShortBranchIssue shortBranchIssue3 = newShortBranchIssue(issue3, "myBranch3"); DefaultIssue newIssue = createIssue("newIssue", "rule1", Issue.STATUS_OPEN, null); when(resolvedShortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component)).thenReturn(Arrays.asList(shortBranchIssue1, shortBranchIssue2, shortBranchIssue3)); when(resolvedShortBranchIssuesLoader.loadDefaultIssuesWithChanges(anyListOf(ShortBranchIssue.class))).thenReturn(ImmutableMap.of(shortBranchIssue3, issue3)); copier.tryMerge(component, Collections.singleton(newIssue)); - verify(issueLifecycle).copyIssueAttributes(newIssue, issue3); + verify(issueLifecycle).mergeConfirmedOrResolvedFromShortLivingBranch(newIssue, issue3, "myBranch3"); } private static DefaultIssue createIssue(String key, String ruleKey, String status, @Nullable String resolution) { @@ -119,7 +119,7 @@ public class ShortBranchIssueMergerTest { return issue; } - private ShortBranchIssue newShortBranchIssue(DefaultIssue i) { - return new ShortBranchIssue(i.key(), i.line(), i.message(), i.getLineHash(), i.ruleKey(), i.status()); + private ShortBranchIssue newShortBranchIssue(DefaultIssue i, String originBranch) { + return new ShortBranchIssue(i.key(), i.line(), i.message(), i.getLineHash(), i.ruleKey(), i.status(), originBranch); } } diff --git a/server/sonar-web/src/main/js/components/issue/components/IssueChangelogDiff.js b/server/sonar-web/src/main/js/components/issue/components/IssueChangelogDiff.js index e0b1a93556b..f858a13810a 100644 --- a/server/sonar-web/src/main/js/components/issue/components/IssueChangelogDiff.js +++ b/server/sonar-web/src/main/js/components/issue/components/IssueChangelogDiff.js @@ -43,6 +43,28 @@ export default function IssueChangelogDiff(props /*: { diff: ChangelogDiff } */)

); } + if (diff.key === 'from_long_branch') { + return ( +

+ {translateWithParameters( + 'issue.change.from_long_branch', + diff.oldValue || '', + diff.newValue || '' + )} +

+ ); + } + if (diff.key === 'from_short_branch') { + return ( +

+ {translateWithParameters( + 'issue.change.from_short_branch', + diff.oldValue || '', + diff.newValue || '' + )} +

+ ); + } let message; if (diff.newValue != null) { diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index 970c8424246..06b017e85f6 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -607,6 +607,8 @@ issues.my_issues=My Issues issue.changelog.changed_to={0} changed to {1} issue.changelog.was=was {0} issue.change.file_move=The file has been moved from {0} to {1} +issue.change.from_long_branch=The issue has been copied from branch '{0}' to branch '{1}' +issue.change.from_short_branch=The issue has been merged from branch '{0}' into branch '{1}' issue.changelog.removed={0} removed issue.changelog.field.severity=Severity issue.changelog.field.actionPlan=Action Plan