package org.sonar.server.computation.issue;
import com.google.common.base.Optional;
+import java.util.Date;
import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.IssueChangeContext;
+import org.sonar.server.computation.analysis.AnalysisMetadataHolder;
import org.sonar.server.computation.component.Component;
import org.sonar.server.computation.filemove.MovedFilesRepository;
import org.sonar.server.computation.filemove.MovedFilesRepository.OriginalFile;
+import org.sonar.server.issue.IssueUpdater;
import static com.google.common.base.Preconditions.checkState;
public class MovedIssueVisitor extends IssueVisitor {
+ private final AnalysisMetadataHolder analysisMetadataHolder;
private final MovedFilesRepository movedFilesRepository;
+ private final IssueUpdater issueUpdater;
- public MovedIssueVisitor(MovedFilesRepository movedFilesRepository) {
+ public MovedIssueVisitor(AnalysisMetadataHolder analysisMetadataHolder, MovedFilesRepository movedFilesRepository, IssueUpdater issueUpdater) {
+ this.analysisMetadataHolder = analysisMetadataHolder;
this.movedFilesRepository = movedFilesRepository;
+ this.issueUpdater = issueUpdater;
}
@Override
issue, component);
OriginalFile originalFile = originalFileOptional.get();
checkState(originalFile.getUuid().equals(issue.componentUuid()),
- "Issue %s doesn't belong to file %s registered as original file of current file %s",
- issue, originalFile.getUuid(), component);
+ "Issue %s doesn't belong to file %s registered as original file of current file %s",
+ issue, originalFile.getUuid(), component);
- // it's enough to change component uuid, only this field is written to table ISSUES
- // other fields (such as module, modulePath, componentKey) are read-only and result of a join with other tables
- issue.setComponentUuid(component.getUuid());
+ // changes the issue's component uuid, add a change and set issue as changed to enforce it is persisted to DB
+ issueUpdater.setIssueMoved(issue, component.getUuid(), IssueChangeContext.createUser(new Date(analysisMetadataHolder.getAnalysisDate()), null));
+ // other fields (such as module, modulePath, componentKey) are read-only and set/reset for consistency only
issue.setComponentKey(component.getKey());
issue.setModuleUuid(null);
issue.setModuleUuidPath(null);
-
- // ensure issue is updated in DB
- issue.setChanged(true);
}
}
public static final String STATUS = "status";
public static final String AUTHOR = "author";
public static final String ACTION_PLAN = "actionPlan";
+ public static final String FILE = "file";
/**
* It should be renamed to 'effort', but it hasn't been done to prevent a massive update in database
return false;
}
+ public boolean setIssueMoved(DefaultIssue issue, String newComponentUuid, IssueChangeContext context) {
+ if (!Objects.equals(newComponentUuid, issue.componentUuid())) {
+ issue.setFieldChange(context, FILE, issue.componentUuid(), newComponentUuid);
+ issue.setComponentUuid(newComponentUuid);
+ issue.setUpdateDate(context.date());
+ issue.setChanged(true);
+ return true;
+ }
+ return false;
+ }
+
}
package org.sonar.server.computation.issue;
import com.google.common.base.Optional;
+import java.util.Date;
import org.junit.Before;
import org.junit.Test;
import org.junit.rules.ExpectedException;
+import org.mockito.ArgumentCaptor;
import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.IssueChangeContext;
+import org.sonar.server.computation.analysis.AnalysisMetadataHolderRule;
import org.sonar.server.computation.component.Component;
import org.sonar.server.computation.component.ReportComponent;
import org.sonar.server.computation.filemove.MovedFilesRepository;
+import org.sonar.server.issue.IssueUpdater;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
public class MovedIssueVisitorTest {
+ private static final long ANALYSIS_DATE = 894521;
private static final String FILE_UUID = "file uuid";
private static final Component FILE = ReportComponent.builder(Component.Type.FILE, 1).setUuid(FILE_UUID).build();
@org.junit.Rule
public ExpectedException expectedException = ExpectedException.none();
+ @org.junit.Rule
+ public AnalysisMetadataHolderRule analysisMetadataHolder = new AnalysisMetadataHolderRule();
private MovedFilesRepository movedFilesRepository = mock(MovedFilesRepository.class);
- private MovedIssueVisitor underTest = new MovedIssueVisitor(movedFilesRepository);
+ private MovedIssueVisitor underTest = new MovedIssueVisitor(analysisMetadataHolder, movedFilesRepository, new IssueUpdater());
@Before
public void setUp() throws Exception {
+ analysisMetadataHolder.setAnalysisDate(ANALYSIS_DATE);
when(movedFilesRepository.getOriginalFile(any(Component.class)))
.thenReturn(Optional.<MovedFilesRepository.OriginalFile>absent());
}
verify(issue).setComponentKey(FILE.getKey());
verify(issue).setModuleUuid(null);
verify(issue).setModuleUuidPath(null);
-
verify(issue).setChanged(true);
+ ArgumentCaptor<IssueChangeContext> issueChangeContextCaptor = ArgumentCaptor.forClass(IssueChangeContext.class);
+ verify(issue).setFieldChange(issueChangeContextCaptor.capture(), eq("file"), eq(originalFile.getUuid()), eq(FILE.getUuid()));
+ assertThat(issueChangeContextCaptor.getValue().date()).isEqualTo(new Date(ANALYSIS_DATE));
+ assertThat(issueChangeContextCaptor.getValue().login()).isNull();
+ assertThat(issueChangeContextCaptor.getValue().scan()).isFalse();
}
private DefaultIssue mockIssue(String fileUuid) {
*/
package org.sonar.server.issue;
+import java.util.Calendar;
import java.util.Date;
+import java.util.Map;
+import org.apache.commons.lang.time.DateUtils;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
updater.setNewAuthor(issue, "julien", context);
}
+ @Test
+ public void setIssueMoved_has_no_effect_if_component_uuid_is_not_changed() {
+ String componentUuid = "a";
+ issue.setComponentUuid(componentUuid);
+
+ updater.setIssueMoved(issue, componentUuid, context);
+
+ assertThat(issue.changes()).isEmpty();
+ assertThat(issue.componentUuid()).isEqualTo(componentUuid);
+ assertThat(issue.isChanged()).isFalse();
+ assertThat(issue.updateDate()).isNull();
+ assertThat(issue.mustSendNotifications()).isFalse();
+ }
+
+ @Test
+ public void setIssueMoved_changes_componentUuid_adds_a_change() {
+ String oldComponentUuid = "a";
+ String newComponentUuid = "b";
+ issue.setComponentUuid(oldComponentUuid);
+
+ updater.setIssueMoved(issue, newComponentUuid, context);
+
+ assertThat(issue.changes()).hasSize(1);
+ FieldDiffs fieldDiffs = issue.changes().get(0);
+ assertThat(fieldDiffs.creationDate()).isEqualTo(context.date());
+ assertThat(fieldDiffs.diffs()).hasSize(1);
+ Map.Entry<String, FieldDiffs.Diff> entry = fieldDiffs.diffs().entrySet().iterator().next();
+ assertThat(entry.getKey()).isEqualTo("file");
+ assertThat(entry.getValue().oldValue()).isEqualTo(oldComponentUuid);
+ assertThat(entry.getValue().newValue()).isEqualTo(newComponentUuid);
+ assertThat(issue.componentUuid()).isEqualTo(newComponentUuid);
+ assertThat(issue.isChanged()).isTrue();
+ assertThat(issue.updateDate()).isEqualTo(DateUtils.truncate(context.date(), Calendar.SECOND));
+ }
}
issue.changelog.field.status=Status
issue.changelog.field.tags=Tags
issue.changelog.field.type=Type
+issue.changelog.field.file=File
issue.changelog.removed={0} removed
issue.changelog.was=was {0}
issue.comment.delete_confirm_button=Delete