Skip to content

Commit

Permalink
SONAR-9949 Copy all issue attributes when merging a short in a long l…
Browse files Browse the repository at this point in the history
…iving branch
  • Loading branch information
henryju committed Oct 20, 2017
1 parent 7777015 commit 59c61bf
Show file tree
Hide file tree
Showing 19 changed files with 187 additions and 73 deletions.
Expand Up @@ -27,20 +27,24 @@

@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
Expand Down Expand Up @@ -70,7 +74,24 @@ public String getStatus() {
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);
}

}
Expand Up @@ -48,6 +48,10 @@ public List<IssueChangeDto> selectByTypeAndIssueKeys(DbSession session, Collecti
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));
}
Expand Down
Expand Up @@ -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);
}
Expand Up @@ -19,17 +19,13 @@
*/
package org.sonar.db.issue;

import com.google.common.base.Preconditions;
import java.io.Serializable;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
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 {

Expand All @@ -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;
Expand Down Expand Up @@ -82,16 +77,6 @@ public ShortBranchIssueDto setStatus(@Nullable String s) {
return this;
}

@CheckForNull
public String getResolution() {
return resolution;
}

public ShortBranchIssueDto setResolution(@Nullable String s) {
this.resolution = s;
return this;
}

@CheckForNull
public String getChecksum() {
return checksum;
Expand Down Expand Up @@ -120,6 +105,6 @@ public String toString() {
}

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());
}
}
Expand Up @@ -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
Expand Down
Expand Up @@ -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
Expand Down
Expand Up @@ -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;
Expand Down Expand Up @@ -48,6 +49,14 @@ public void select_issue_changelog_from_issue_key() {
assertThat(changelog.get(0).diffs().get("severity").oldValue()).isEqualTo("MAJOR");
}

@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");
Expand Down
Expand Up @@ -30,7 +30,6 @@
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;
Expand Down Expand Up @@ -212,7 +211,6 @@ public void selectResolvedOrConfirmedByComponentUuid_should_correctly_map_requir
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();
Expand All @@ -221,7 +219,6 @@ public void selectResolvedOrConfirmedByComponentUuid_should_correctly_map_requir
assertThat(fp.getChecksum()).isNotEmpty();
assertThat(fp.getRuleKey()).isNotNull();
assertThat(fp.getStatus()).isNotNull();
assertThat(fp.getResolution()).isNotNull();
}

private static IssueDto newIssueDto(String key) {
Expand Down
Expand Up @@ -67,10 +67,10 @@ public void visitAny(Component component) {
}
}

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);
});
Expand All @@ -92,7 +92,7 @@ private void copyIssues(Component component, Map<DefaultIssue, DefaultIssue> mat
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);
}
}
Expand Down
Expand Up @@ -21,9 +21,10 @@

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;
Expand Down Expand Up @@ -65,7 +66,7 @@ public void initNewOpenIssue(DefaultIssue issue) {
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);
Expand All @@ -77,9 +78,21 @@ public void copyExistingOpenIssue(DefaultIssue raw, DefaultIssue base) {
}
}

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) {
Expand Down
Expand Up @@ -28,28 +28,31 @@
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;

public ShortBranchIssueStatusCopier(ShortBranchIssuesLoader resolvedShortBranchIssuesLoader, IssueLifecycle issueLifecycle) {
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));
}
}
}
Expand Up @@ -21,16 +21,24 @@

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;
Expand All @@ -54,4 +62,37 @@ public Collection<ShortBranchIssue> loadCandidateIssuesForMergingInTargetBranch(
.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());
}
});
}
}

0 comments on commit 59c61bf

Please sign in to comment.