diff options
author | Julien HENRY <julien.henry@sonarsource.com> | 2017-10-12 17:53:52 +0200 |
---|---|---|
committer | Julien HENRY <julien.henry@sonarsource.com> | 2017-10-20 18:45:15 +1000 |
commit | 59c61bf59fd3778f723021f5710d27165901760f (patch) | |
tree | 6d6f7528e866839e3a14510787e9f9552fe478d4 /server | |
parent | 777701594f14b0886684d1d81bf32539a7522146 (diff) | |
download | sonarqube-59c61bf59fd3778f723021f5710d27165901760f.tar.gz sonarqube-59c61bf59fd3778f723021f5710d27165901760f.zip |
SONAR-9949 Copy all issue attributes when merging a short in a long living branch
Diffstat (limited to 'server')
16 files changed, 158 insertions, 72 deletions
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 0e2f88f94b9..11095124c97 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 @@ -27,20 +27,24 @@ import org.sonar.core.issue.tracking.Trackable; @Immutable public class ShortBranchIssue implements Trackable { + private final String key; private final Integer line; private final String message; private final String lineHash; private final RuleKey ruleKey; private final String status; - private final String resolution; - public ShortBranchIssue(@Nullable Integer line, String message, @Nullable String lineHash, RuleKey ruleKey, String status, @Nullable String resolution) { + public ShortBranchIssue(String key, @Nullable Integer line, String message, @Nullable String lineHash, RuleKey ruleKey, String status) { + this.key = key; this.line = line; this.message = message; this.lineHash = lineHash; this.ruleKey = ruleKey; this.status = status; - this.resolution = resolution; + } + + public String getKey() { + return key; } @CheckForNull @@ -70,7 +74,24 @@ public class ShortBranchIssue implements Trackable { return status; } - public String getResolution() { - return resolution; + @Override + public int hashCode() { + return key.hashCode(); } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + ShortBranchIssue other = (ShortBranchIssue) obj; + return key.equals(other.key); + } + } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueChangeDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueChangeDao.java index 9a9fa026968..eb22bc65c71 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueChangeDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueChangeDao.java @@ -48,6 +48,10 @@ public class IssueChangeDao implements Dao { return executeLargeInputs(issueKeys, issueKeys1 -> mapper(session).selectByIssuesAndType(issueKeys1, changeType)); } + public List<IssueChangeDto> selectByIssueKeys(DbSession session, Collection<String> issueKeys) { + return executeLargeInputs(issueKeys, issueKeys1 -> mapper(session).selectByIssues(issueKeys1)); + } + public Optional<IssueChangeDto> selectCommentByKey(DbSession session, String commentKey) { return Optional.ofNullable(mapper(session).selectByKeyAndType(commentKey, IssueChangeDto.TYPE_COMMENT)); } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueChangeMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueChangeMapper.java index 1d6db5b2994..3b6aef0cff8 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueChangeMapper.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueChangeMapper.java @@ -40,5 +40,7 @@ public interface IssueChangeMapper { List<IssueChangeDto> selectByIssuesAndType(@Param("issueKeys") List<String> issueKeys, @Param("changeType") String changeType); + List<IssueChangeDto> selectByIssues(@Param("issueKeys") List<String> issueKeys); + List<IssueChangeDto> selectChangelogOfNonClosedIssuesByComponent(@Param("componentUuid") String componentUuid, @Param("changeType") String changeType); } 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 3674cdc7af8..3a68ea03af2 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 @@ -19,7 +19,6 @@ */ package org.sonar.db.issue; -import com.google.common.base.Preconditions; import java.io.Serializable; import javax.annotation.CheckForNull; import javax.annotation.Nullable; @@ -27,9 +26,6 @@ import org.apache.commons.lang.builder.ToStringBuilder; import org.apache.commons.lang.builder.ToStringStyle; import org.sonar.api.rule.RuleKey; import org.sonar.core.issue.ShortBranchIssue; -import org.sonar.db.rule.RuleDefinitionDto; - -import static com.google.common.base.Preconditions.checkArgument; public final class ShortBranchIssueDto implements Serializable { @@ -38,7 +34,6 @@ public final class ShortBranchIssueDto implements Serializable { private Integer line; private String checksum; private String status; - private String resolution; // joins private String ruleKey; @@ -83,16 +78,6 @@ public final class ShortBranchIssueDto implements Serializable { } @CheckForNull - public String getResolution() { - return resolution; - } - - public ShortBranchIssueDto setResolution(@Nullable String s) { - this.resolution = s; - return this; - } - - @CheckForNull public String getChecksum() { return checksum; } @@ -120,6 +105,6 @@ public final class ShortBranchIssueDto implements Serializable { } public static ShortBranchIssue toShortBranchIssue(ShortBranchIssueDto dto) { - return new ShortBranchIssue(dto.getLine(), dto.getMessage(), dto.getChecksum(), dto.getRuleKey(), dto.getStatus(), dto.getResolution()); + return new ShortBranchIssue(dto.getKey(), dto.getLine(), dto.getMessage(), dto.getChecksum(), dto.getRuleKey(), dto.getStatus()); } } diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueChangeMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueChangeMapper.xml index 46a732b0719..8b315466eb7 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueChangeMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueChangeMapper.xml @@ -42,6 +42,16 @@ </foreach> order by c.created_at </select> + + <select id="selectByIssues" parameterType="map" resultType="IssueChange"> + select + <include refid="issueChangeColumns"/> + from issue_changes c + where c.issue_key in + <foreach collection="issueKeys" open="(" close=")" item="key" separator=","> + #{key,jdbcType=VARCHAR} + </foreach> + </select> <select id="selectByKeyAndType" parameterType="map" resultType="IssueChange"> select 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 fa9c34ec2d0..8c365703f9e 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 @@ -219,7 +219,6 @@ i.message as message, i.line as line, i.status as status, - i.resolution as resolution, i.checksum as checksum, r.plugin_rule_key as ruleKey, r.plugin_name as ruleRepo diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueChangeDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueChangeDaoTest.java index a5b46eb3c15..7a0ffca2a48 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueChangeDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueChangeDaoTest.java @@ -19,6 +19,7 @@ */ package org.sonar.db.issue; +import java.util.Arrays; import java.util.List; import java.util.Optional; import org.junit.Rule; @@ -49,6 +50,14 @@ public class IssueChangeDaoTest { } @Test + public void select_issue_changes_from_issues_key() { + db.prepareDbUnit(getClass(), "shared.xml"); + + List<IssueChangeDto> changelog = underTest.selectByIssueKeys(db.getSession(), Arrays.asList("1000", "1001")); + assertThat(changelog).hasSize(5); + } + + @Test public void selectChangelogOfNonClosedIssuesByComponent() { db.prepareDbUnit(getClass(), "selectChangelogOfNonClosedIssuesByComponent.xml"); 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 f44788c174f..8fc59751ee0 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 @@ -30,7 +30,6 @@ import org.junit.Test; import org.junit.rules.ExpectedException; import org.sonar.api.rule.RuleKey; import org.sonar.api.utils.System2; -import org.sonar.core.issue.ShortBranchIssue; import org.sonar.db.DbTester; import org.sonar.db.RowNotFoundException; import org.sonar.db.component.ComponentDto; @@ -212,7 +211,6 @@ public class IssueDaoTest { assertThat(fp.getChecksum()).isEqualTo(fpIssue.getChecksum()); assertThat(fp.getRuleKey()).isEqualTo(fpIssue.getRuleKey()); assertThat(fp.getStatus()).isEqualTo(fpIssue.getStatus()); - assertThat(fp.getResolution()).isEqualTo(fpIssue.getResolution()); assertThat(fp.getLine()).isNotNull(); assertThat(fp.getLine()).isNotZero(); @@ -221,7 +219,6 @@ public class IssueDaoTest { assertThat(fp.getChecksum()).isNotEmpty(); assertThat(fp.getRuleKey()).isNotNull(); assertThat(fp.getStatus()).isNotNull(); - assertThat(fp.getResolution()).isNotNull(); } private static IssueDto newIssueDto(String key) { 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 26db336fcf5..a96d8f2d7e9 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 @@ -67,10 +67,10 @@ public class IntegrateIssuesVisitor extends TypeAwareVisitorAdapter { } } - private void fillNewOpenIssues(Component component, Iterable<DefaultIssue> issues, DiskCache<DefaultIssue>.DiskAppender cacheAppender) { + private void fillNewOpenIssues(Component component, Iterable<DefaultIssue> newIssues, DiskCache<DefaultIssue>.DiskAppender cacheAppender) { List<DefaultIssue> list = new ArrayList<>(); - issues.forEach(issue -> { + newIssues.forEach(issue -> { issueLifecycle.initNewOpenIssue(issue); list.add(issue); }); @@ -92,7 +92,7 @@ public class IntegrateIssuesVisitor extends TypeAwareVisitorAdapter { for (Map.Entry<DefaultIssue, DefaultIssue> entry : matched.entrySet()) { DefaultIssue raw = entry.getKey(); DefaultIssue base = entry.getValue(); - issueLifecycle.copyExistingOpenIssue(raw, base); + issueLifecycle.copyExistingOpenIssueFromLongLivingBranch(raw, base); 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 868e6db0580..7f88d3404e0 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 @@ -21,9 +21,10 @@ package org.sonar.server.computation.task.projectanalysis.issue; import com.google.common.annotations.VisibleForTesting; import java.util.Date; -import javax.annotation.Nullable; import org.sonar.api.issue.Issue; import org.sonar.core.issue.DefaultIssue; +import org.sonar.core.issue.DefaultIssueComment; +import org.sonar.core.issue.FieldDiffs; import org.sonar.core.issue.IssueChangeContext; import org.sonar.core.util.Uuids; import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolder; @@ -65,7 +66,7 @@ public class IssueLifecycle { issue.setEffort(debtCalculator.calculate(issue)); } - public void copyExistingOpenIssue(DefaultIssue raw, DefaultIssue base) { + public void copyExistingOpenIssueFromLongLivingBranch(DefaultIssue raw, DefaultIssue base) { raw.setKey(Uuids.create()); raw.setNew(false); raw.setCopied(true); @@ -77,9 +78,21 @@ public class IssueLifecycle { } } - public void copyResolution(DefaultIssue raw, String status, @Nullable String resolution) { - raw.setStatus(status); - raw.setResolution(resolution); + public void mergeIssueFromShortLivingBranch(DefaultIssue raw, DefaultIssue fromShortLiving) { + raw.setCopied(true); + raw.setType(fromShortLiving.type()); + raw.setResolution(fromShortLiving.resolution()); + raw.setStatus(fromShortLiving.status()); + raw.setAssignee(fromShortLiving.assignee()); + raw.setAuthorLogin(fromShortLiving.authorLogin()); + raw.setTags(fromShortLiving.tags()); + raw.setAttributes(fromShortLiving.attributes()); + if (fromShortLiving.manualSeverity()) { + raw.setManualSeverity(true); + raw.setSeverity(fromShortLiving.severity()); + } + fromShortLiving.comments().forEach(c -> raw.addComment(DefaultIssueComment.copy(raw.key(), c))); + fromShortLiving.changes().forEach(c -> raw.addChange(FieldDiffs.copy(raw.key(), c))); } public void mergeExistingOpenIssue(DefaultIssue raw, DefaultIssue base) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssueStatusCopier.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssueStatusCopier.java index 8b49759aae6..3944af7c101 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssueStatusCopier.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssueStatusCopier.java @@ -28,7 +28,7 @@ import org.sonar.core.issue.tracking.Tracking; import org.sonar.server.computation.task.projectanalysis.component.Component; public class ShortBranchIssueStatusCopier { - private final ShortBranchIssuesLoader resolvedShortBranchIssuesLoader; + private final ShortBranchIssuesLoader shortBranchIssuesLoader; private final SimpleTracker<DefaultIssue, ShortBranchIssue> tracker; private final IssueLifecycle issueLifecycle; @@ -36,20 +36,23 @@ public class ShortBranchIssueStatusCopier { this(resolvedShortBranchIssuesLoader, new SimpleTracker<>(), issueLifecycle); } - public ShortBranchIssueStatusCopier(ShortBranchIssuesLoader resolvedShortBranchIssuesLoader, SimpleTracker<DefaultIssue, ShortBranchIssue> tracker, - IssueLifecycle issueLifecycle) { - this.resolvedShortBranchIssuesLoader = resolvedShortBranchIssuesLoader; + public ShortBranchIssueStatusCopier(ShortBranchIssuesLoader shortBranchIssuesLoader, SimpleTracker<DefaultIssue, ShortBranchIssue> tracker, IssueLifecycle issueLifecycle) { + this.shortBranchIssuesLoader = shortBranchIssuesLoader; this.tracker = tracker; this.issueLifecycle = issueLifecycle; } public void updateStatus(Component component, Collection<DefaultIssue> newIssues) { - Collection<ShortBranchIssue> shortBranchIssues = resolvedShortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component); + Collection<ShortBranchIssue> shortBranchIssues = shortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component); Tracking<DefaultIssue, ShortBranchIssue> tracking = tracker.track(newIssues, shortBranchIssues); - for (Map.Entry<DefaultIssue, ShortBranchIssue> e : tracking.getMatchedRaws().entrySet()) { + Map<DefaultIssue, ShortBranchIssue> matchedRaws = tracking.getMatchedRaws(); + + Map<ShortBranchIssue, DefaultIssue> defaultIssues = shortBranchIssuesLoader.loadDefaultIssuesWithChanges(matchedRaws.values()); + + for (Map.Entry<DefaultIssue, ShortBranchIssue> e : matchedRaws.entrySet()) { ShortBranchIssue issue = e.getValue(); - issueLifecycle.copyResolution(e.getKey(), issue.getStatus(), issue.getResolution()); + issueLifecycle.mergeIssueFromShortLivingBranch(e.getKey(), defaultIssues.get(issue)); } } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssuesLoader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssuesLoader.java index ebac0c2f982..acc598059a6 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssuesLoader.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssuesLoader.java @@ -21,16 +21,24 @@ package org.sonar.server.computation.task.projectanalysis.issue; import java.util.Collection; import java.util.Collections; +import java.util.List; +import java.util.Map; import java.util.Set; import java.util.stream.Collectors; +import org.sonar.core.issue.DefaultIssue; import org.sonar.core.issue.ShortBranchIssue; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.component.ComponentDto; +import org.sonar.db.issue.IssueChangeDto; +import org.sonar.db.issue.IssueDto; import org.sonar.db.issue.ShortBranchIssueDto; import org.sonar.server.computation.task.projectanalysis.component.Component; import org.sonar.server.computation.task.projectanalysis.component.ShortBranchComponentsWithIssues; +import static java.util.stream.Collectors.groupingBy; +import static java.util.stream.Collectors.toMap; + public class ShortBranchIssuesLoader { private final ShortBranchComponentsWithIssues shortBranchComponentsWithIssues; @@ -54,4 +62,37 @@ public class ShortBranchIssuesLoader { .collect(Collectors.toList()); } } + + public Map<ShortBranchIssue, DefaultIssue> loadDefaultIssuesWithChanges(Collection<ShortBranchIssue> lightIssues) { + if (lightIssues.isEmpty()) { + return Collections.emptyMap(); + } + Map<String, ShortBranchIssue> issuesByKey = lightIssues.stream().collect(Collectors.toMap(ShortBranchIssue::getKey, i -> i)); + try (DbSession session = dbClient.openSession(false)) { + + Map<String, List<IssueChangeDto>> changeDtoByIssueKey = dbClient.issueChangeDao() + .selectByIssueKeys(session, issuesByKey.keySet()).stream().collect(groupingBy(IssueChangeDto::getIssueKey)); + + return dbClient.issueDao().selectByKeys(session, issuesByKey.keySet()) + .stream() + .map(IssueDto::toDefaultIssue) + .peek(i -> setChanges(changeDtoByIssueKey, i)) + .collect(toMap(i -> issuesByKey.get(i.key()), i -> i)); + } + } + + private static void setChanges(Map<String, List<IssueChangeDto>> changeDtoByIssueKey, DefaultIssue i) { + changeDtoByIssueKey.get(i.key()).forEach(c -> { + switch (c.getChangeType()) { + case IssueChangeDto.TYPE_FIELD_CHANGE: + i.addChange(c.toFieldDiffs()); + break; + case IssueChangeDto.TYPE_COMMENT: + i.addComment(c.toComment()); + break; + default: + throw new IllegalStateException("Unknow change type: " + c.getChangeType()); + } + }); + } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/PersistIssuesStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/PersistIssuesStep.java index 6276bf44b25..27d5128b5aa 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/PersistIssuesStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/PersistIssuesStep.java @@ -19,15 +19,11 @@ */ package org.sonar.server.computation.task.projectanalysis.step; -import org.sonar.api.issue.IssueComment; import org.sonar.api.utils.System2; import org.sonar.core.issue.DefaultIssue; -import org.sonar.core.issue.DefaultIssueComment; -import org.sonar.core.issue.FieldDiffs; import org.sonar.core.util.CloseableIterator; import org.sonar.db.DbClient; import org.sonar.db.DbSession; -import org.sonar.db.issue.IssueChangeDto; import org.sonar.db.issue.IssueChangeMapper; import org.sonar.db.issue.IssueDto; import org.sonar.db.issue.IssueMapper; @@ -35,6 +31,7 @@ import org.sonar.server.computation.task.projectanalysis.issue.IssueCache; import org.sonar.server.computation.task.projectanalysis.issue.RuleRepository; import org.sonar.server.computation.task.projectanalysis.issue.UpdateConflictResolver; import org.sonar.server.computation.task.step.ComputationStep; +import org.sonar.server.issue.IssueStorage; public class PersistIssuesStep implements ComputationStep { @@ -64,7 +61,7 @@ public class PersistIssuesStep implements ComputationStep { DefaultIssue issue = issues.next(); boolean saved = persistIssueIfRequired(mapper, issue); if (saved) { - insertChanges(changeMapper, issue); + IssueStorage.insertChanges(changeMapper, issue); } } dbSession.flushStatements(); @@ -101,21 +98,6 @@ public class PersistIssuesStep implements ComputationStep { } } - private static void insertChanges(IssueChangeMapper mapper, DefaultIssue issue) { - for (IssueComment comment : issue.comments()) { - DefaultIssueComment c = (DefaultIssueComment) comment; - if (c.isNew()) { - IssueChangeDto changeDto = IssueChangeDto.of(c); - mapper.insert(changeDto); - } - } - FieldDiffs diffs = issue.currentChange(); - if (!issue.isNew() && diffs != null) { - IssueChangeDto changeDto = IssueChangeDto.of(issue.key(), diffs); - mapper.insert(changeDto); - } - } - @Override public String getDescription() { return "Persist issues"; diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueStorage.java b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueStorage.java index 6c02b9c1a0a..6b186a72411 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueStorage.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueStorage.java @@ -152,7 +152,7 @@ public abstract class IssueStorage { protected abstract IssueDto doUpdate(DbSession batchSession, long now, DefaultIssue issue); - private void insertChanges(IssueChangeMapper mapper, DefaultIssue issue) { + public static void insertChanges(IssueChangeMapper mapper, DefaultIssue issue) { for (IssueComment comment : issue.comments()) { DefaultIssueComment c = (DefaultIssueComment) comment; if (c.isNew()) { @@ -164,6 +164,11 @@ public abstract class IssueStorage { if (!issue.isNew() && diffs != null) { IssueChangeDto changeDto = IssueChangeDto.of(issue.key(), diffs); mapper.insert(changeDto); + } else if (issue.isCopied()) { + for (FieldDiffs d : issue.changes()) { + IssueChangeDto changeDto = IssueChangeDto.of(issue.key(), d); + mapper.insert(changeDto); + } } } 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 bdd6a058bff..a588db622b5 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 @@ -76,11 +76,14 @@ public class IssueLifecycleTest { } @Test - public void copyResolution() { - DefaultIssue issue = new DefaultIssue(); - underTest.copyResolution(issue, "status", "resolution"); - assertThat(issue.resolution()).isEqualTo("resolution"); - assertThat(issue.status()).isEqualTo("status"); + public void mergeIssueFromShortLivingBranch() { + DefaultIssue raw = new DefaultIssue(); + DefaultIssue fromShort = new DefaultIssue(); + fromShort.setResolution("resolution"); + fromShort.setStatus("status"); + underTest.mergeIssueFromShortLivingBranch(raw, fromShort); + assertThat(raw.resolution()).isEqualTo("resolution"); + assertThat(raw.status()).isEqualTo("status"); } @Test @@ -120,7 +123,7 @@ public class IssueLifecycleTest { when(debtCalculator.calculate(raw)).thenReturn(DEFAULT_DURATION); - underTest.copyExistingOpenIssue(raw, base); + underTest.copyExistingOpenIssueFromLongLivingBranch(raw, base); assertThat(raw.isNew()).isFalse(); assertThat(raw.isCopied()).isTrue(); diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssueStatusCopierTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssueStatusCopierTest.java index c5948cfc60b..d244d44cb6a 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssueStatusCopierTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssueStatusCopierTest.java @@ -19,11 +19,14 @@ */ package org.sonar.server.computation.task.projectanalysis.issue; +import com.google.common.collect.ImmutableMap; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import javax.annotation.Nullable; import org.junit.Before; import org.junit.Test; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.sonar.api.issue.Issue; @@ -33,6 +36,8 @@ import org.sonar.core.issue.ShortBranchIssue; import org.sonar.core.issue.tracking.SimpleTracker; import org.sonar.server.computation.task.projectanalysis.component.Component; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.anyListOf; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; @@ -76,24 +81,31 @@ public class ShortBranchIssueStatusCopierTest { @Test public void update_status_on_matches() { - ShortBranchIssue shortBranchIssue = newShortBranchIssue(createIssue("issue1", "rule1", Issue.STATUS_CONFIRMED, null)); + DefaultIssue issue1 = createIssue("issue1", "rule1", Issue.STATUS_CONFIRMED, null); + ShortBranchIssue shortBranchIssue = newShortBranchIssue(issue1); DefaultIssue newIssue = createIssue("issue2", "rule1", Issue.STATUS_OPEN, null); when(resolvedShortBranchIssuesLoader.loadCandidateIssuesForMergingInTargetBranch(component)).thenReturn(Collections.singleton(shortBranchIssue)); + when(resolvedShortBranchIssuesLoader.loadDefaultIssuesWithChanges(anyListOf(ShortBranchIssue.class))).thenReturn(ImmutableMap.of(shortBranchIssue, issue1)); copier.updateStatus(component, Collections.singleton(newIssue)); - verify(issueLifecycle).copyResolution(newIssue, shortBranchIssue.getStatus(), shortBranchIssue.getResolution()); + ArgumentCaptor<Collection> captor = ArgumentCaptor.forClass(Collection.class); + verify(resolvedShortBranchIssuesLoader).loadDefaultIssuesWithChanges(captor.capture()); + assertThat(captor.getValue()).containsOnly(shortBranchIssue); + verify(issueLifecycle).mergeIssueFromShortLivingBranch(newIssue, issue1); } @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 shortBranchIssue3 = newShortBranchIssue(createIssue("issue3", "rule1", Issue.STATUS_RESOLVED, Issue.RESOLUTION_FALSE_POSITIVE)); + DefaultIssue issue3 = createIssue("issue3", "rule1", Issue.STATUS_RESOLVED, Issue.RESOLUTION_FALSE_POSITIVE); + ShortBranchIssue shortBranchIssue3 = newShortBranchIssue(issue3); 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.updateStatus(component, Collections.singleton(newIssue)); - verify(issueLifecycle).copyResolution(newIssue, Issue.STATUS_RESOLVED, Issue.RESOLUTION_FALSE_POSITIVE); + verify(issueLifecycle).mergeIssueFromShortLivingBranch(newIssue, issue3); } private static DefaultIssue createIssue(String key, String ruleKey, String status, @Nullable String resolution) { @@ -108,6 +120,6 @@ public class ShortBranchIssueStatusCopierTest { } private ShortBranchIssue newShortBranchIssue(DefaultIssue i) { - return new ShortBranchIssue(i.line(), i.message(), i.getLineHash(), i.ruleKey(), i.status(), i.resolution()); + return new ShortBranchIssue(i.key(), i.line(), i.message(), i.getLineHash(), i.ruleKey(), i.status()); } } |