aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbelen-pruvost-sonarsource <belen.pruvost@sonarsource.com>2022-01-21 10:43:10 +0100
committersonartech <sonartech@sonarsource.com>2022-01-21 20:03:22 +0000
commite2ee8eeba4e3c7b08cd929c56d0fb136d1cdf372 (patch)
tree08dd8921d0d8714e6593d31832b66e606ade5bfb
parent9d1361c43487f91b3d001a7e1385d35fa05a5115 (diff)
downloadsonarqube-e2ee8eeba4e3c7b08cd929c56d0fb136d1cdf372.tar.gz
sonarqube-e2ee8eeba4e3c7b08cd929c56d0fb136d1cdf372.zip
SONAR-14929 - Handle Issue no longer being new on feature branch
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java6
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IntegrateIssuesVisitor.java2
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueLifecycle.java1
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueOnReferenceBranchVisitor.java51
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/NewIssueClassifier.java18
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistIssuesStep.java22
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/util/cache/ProtobufIssueDiskCache.java2
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/protobuf/issue_cache.proto3
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulatorTest.java2
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IssueLifecycleTest.java4
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IssuesOnReferenceBranchVisitorTest.java110
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/NewIssueClassifierTest.java52
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/PersistIssuesStepTest.java71
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDao.java13
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDto.java14
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueMapper.java6
-rw-r--r--server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueMapper.xml52
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDaoTest.java16
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/issue/IssueDtoTest.java8
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java16
-rw-r--r--server/sonar-db-dao/src/testFixtures/java/org/sonar/db/issue/IssueDbTester.java2
-rw-r--r--sonar-core/src/main/java/org/sonar/core/issue/DefaultIssue.java22
-rw-r--r--sonar-core/src/test/java/org/sonar/core/issue/DefaultIssueTest.java4
23 files changed, 419 insertions, 78 deletions
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java
index ec409d28df6..6928fc4bcd9 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java
@@ -33,7 +33,6 @@ import org.sonar.ce.task.projectanalysis.component.BranchLoader;
import org.sonar.ce.task.projectanalysis.component.BranchPersisterImpl;
import org.sonar.ce.task.projectanalysis.component.ConfigurationRepositoryImpl;
import org.sonar.ce.task.projectanalysis.component.DisabledComponentsHolderImpl;
-import org.sonar.ce.task.projectanalysis.period.NewCodeReferenceBranchComponentUuids;
import org.sonar.ce.task.projectanalysis.component.ProjectPersister;
import org.sonar.ce.task.projectanalysis.component.ReferenceBranchComponentUuids;
import org.sonar.ce.task.projectanalysis.component.ReportModulesPath;
@@ -64,12 +63,14 @@ import org.sonar.ce.task.projectanalysis.issue.IssueAssigner;
import org.sonar.ce.task.projectanalysis.issue.IssueCounter;
import org.sonar.ce.task.projectanalysis.issue.IssueCreationDateCalculator;
import org.sonar.ce.task.projectanalysis.issue.IssueLifecycle;
+import org.sonar.ce.task.projectanalysis.issue.IssueOnReferenceBranchVisitor;
import org.sonar.ce.task.projectanalysis.issue.IssueTrackingDelegator;
import org.sonar.ce.task.projectanalysis.issue.IssueVisitors;
import org.sonar.ce.task.projectanalysis.issue.IssuesRepositoryVisitor;
import org.sonar.ce.task.projectanalysis.issue.LoadComponentUuidsHavingOpenIssuesVisitor;
import org.sonar.ce.task.projectanalysis.issue.MovedIssueVisitor;
import org.sonar.ce.task.projectanalysis.issue.NewEffortAggregator;
+import org.sonar.ce.task.projectanalysis.issue.NewIssueClassifier;
import org.sonar.ce.task.projectanalysis.issue.ProtoIssueCache;
import org.sonar.ce.task.projectanalysis.issue.PullRequestSourceBranchMerger;
import org.sonar.ce.task.projectanalysis.issue.PullRequestTrackerExecution;
@@ -105,8 +106,8 @@ import org.sonar.ce.task.projectanalysis.measure.MeasureRepositoryImpl;
import org.sonar.ce.task.projectanalysis.measure.MeasureToMeasureDto;
import org.sonar.ce.task.projectanalysis.metric.MetricModule;
import org.sonar.ce.task.projectanalysis.notification.NotificationFactory;
-import org.sonar.ce.task.projectanalysis.issue.NewIssueClassifier;
import org.sonar.ce.task.projectanalysis.period.NewCodePeriodResolver;
+import org.sonar.ce.task.projectanalysis.period.NewCodeReferenceBranchComponentUuids;
import org.sonar.ce.task.projectanalysis.period.PeriodHolderImpl;
import org.sonar.ce.task.projectanalysis.qualitygate.EvaluationResultTextConverterImpl;
import org.sonar.ce.task.projectanalysis.qualitygate.QualityGateHolderImpl;
@@ -266,6 +267,7 @@ public final class ProjectAnalysisTaskContainerPopulator implements ContainerPop
MovedIssueVisitor.class,
IssuesRepositoryVisitor.class,
RemoveProcessedComponentsVisitor.class,
+ IssueOnReferenceBranchVisitor.class,
// visitors : order is important, measure computers must be executed at the end in order to access to every measures / issues
LoadComponentUuidsHavingOpenIssuesVisitor.class,
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IntegrateIssuesVisitor.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IntegrateIssuesVisitor.java
index 3739b26c6ed..93a0834b4df 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IntegrateIssuesVisitor.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IntegrateIssuesVisitor.java
@@ -127,7 +127,7 @@ public class IntegrateIssuesVisitor extends TypeAwareVisitorAdapter {
private void process(Component component, DefaultIssue issue, CacheAppender<DefaultIssue> cacheAppender) {
issueLifecycle.doAutomaticTransition(issue);
issueVisitors.onIssue(component, issue);
- if (issue.isNew() || issue.isChanged() || issue.isCopied()) {
+ if (issue.isNew() || issue.isChanged() || issue.isCopied() || issue.isNoLongerNewCodeReferenceIssue()) {
cacheAppender.append(issue);
}
}
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 430a1249d64..96b96d38606 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
@@ -216,5 +216,6 @@ public class IssueLifecycle {
toIssue.setEffort(debtCalculator.calculate(toIssue));
toIssue.setOnDisabledRule(fromIssue.isOnDisabledRule());
toIssue.setSelectedAt(fromIssue.selectedAt());
+ toIssue.setIsNewCodeReferenceIssue(fromIssue.isNewCodeReferenceIssue());
}
}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueOnReferenceBranchVisitor.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueOnReferenceBranchVisitor.java
new file mode 100644
index 00000000000..e99e008253b
--- /dev/null
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueOnReferenceBranchVisitor.java
@@ -0,0 +1,51 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.issue;
+
+import org.sonar.ce.task.projectanalysis.component.Component;
+import org.sonar.core.issue.DefaultIssue;
+
+public class IssueOnReferenceBranchVisitor extends IssueVisitor {
+
+ private final NewIssueClassifier newIssueClassifier;
+
+ public IssueOnReferenceBranchVisitor(NewIssueClassifier newIssueClassifier) {
+ this.newIssueClassifier = newIssueClassifier;
+ }
+
+ @Override
+ public void onIssue(Component component, DefaultIssue issue) {
+ if (!newIssueClassifier.isEnabled()) {
+ return;
+ }
+
+ issue.setIsOnReferencedBranch(newIssueClassifier.isOnBranchUsingReferenceBranch());
+
+ if (issue.isOnReferencedBranch()) {
+ issue.setIsOnChangedLine(newIssueClassifier.hasAtLeastOneLocationOnChangedLines(component, issue));
+
+ if (issue.isNewCodeReferenceIssue() && !issue.isOnChangedLine()) {
+ issue.setIsNoLongerNewCodeReferenceIssue(true);
+ issue.setIsNewCodeReferenceIssue(false);
+ }
+ }
+ }
+
+}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/NewIssueClassifier.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/NewIssueClassifier.java
index 9c5aa13e77e..5b3e8a6cce4 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/NewIssueClassifier.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/NewIssueClassifier.java
@@ -41,7 +41,7 @@ public class NewIssueClassifier {
public boolean isEnabled() {
return analysisMetadataHolder.isPullRequest() || periodHolder.hasPeriodDate() ||
- (periodHolder.hasPeriod() && periodHolder.getPeriod().getMode().equals(NewCodePeriodType.REFERENCE_BRANCH.name()));
+ (periodHolder.hasPeriod() && isOnBranchUsingReferenceBranch());
}
public boolean isNew(Component component, DefaultIssue issue) {
@@ -54,17 +54,21 @@ public class NewIssueClassifier {
return periodHolder.getPeriod().isOnPeriod(issue.creationDate());
}
- if (periodHolder.getPeriod().getMode().equals(NewCodePeriodType.REFERENCE_BRANCH.name())) {
- issue.setIsOnReferencedBranch(true);
- boolean isOnChangedLine = hasAtLeastOneLocationOnChangedLines(component, issue);
- issue.setIsOnChangedLine(isOnChangedLine);
- return isOnChangedLine;
+ if (isOnBranchUsingReferenceBranch()) {
+ return hasAtLeastOneLocationOnChangedLines(component, issue);
}
}
return false;
}
- private boolean hasAtLeastOneLocationOnChangedLines(Component component, DefaultIssue issue) {
+ public boolean isOnBranchUsingReferenceBranch() {
+ if (periodHolder.hasPeriod()) {
+ return periodHolder.getPeriod().getMode().equals(NewCodePeriodType.REFERENCE_BRANCH.name());
+ }
+ return false;
+ }
+
+ public boolean hasAtLeastOneLocationOnChangedLines(Component component, DefaultIssue issue) {
if (component.getType() != Component.Type.FILE) {
return false;
}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistIssuesStep.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistIssuesStep.java
index c744da8bea6..5b8ecfca95a 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistIssuesStep.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistIssuesStep.java
@@ -75,6 +75,7 @@ public class PersistIssuesStep implements ComputationStep {
CloseableIterator<DefaultIssue> issues = protoIssueCache.traverse()) {
List<DefaultIssue> addedIssues = new ArrayList<>(ISSUE_BATCHING_SIZE);
List<DefaultIssue> updatedIssues = new ArrayList<>(ISSUE_BATCHING_SIZE);
+ List<DefaultIssue> noLongerNewIssues = new ArrayList<>(ISSUE_BATCHING_SIZE);
IssueMapper mapper = dbSession.getMapper(IssueMapper.class);
IssueChangeMapper changeMapper = dbSession.getMapper(IssueChangeMapper.class);
@@ -92,10 +93,17 @@ public class PersistIssuesStep implements ComputationStep {
persistUpdatedIssues(statistics, updatedIssues, mapper, changeMapper);
updatedIssues.clear();
}
+ } else if (issue.isNoLongerNewCodeReferenceIssue()) {
+ noLongerNewIssues.add(issue);
+ if (noLongerNewIssues.size() >= ISSUE_BATCHING_SIZE) {
+ persistNoLongerNewIssues(statistics, noLongerNewIssues, mapper);
+ noLongerNewIssues.clear();
+ }
}
}
persistNewIssues(statistics, addedIssues, mapper, changeMapper);
persistUpdatedIssues(statistics, updatedIssues, mapper, changeMapper);
+ persistNoLongerNewIssues(statistics, noLongerNewIssues, mapper);
flushSession(dbSession);
} finally {
statistics.dumpTo(context);
@@ -113,7 +121,7 @@ public class PersistIssuesStep implements ComputationStep {
IssueDto dto = IssueDto.toDtoForComputationInsert(i, ruleUuid, now);
mapper.insert(dto);
if (i.isOnReferencedBranch() && i.isOnChangedLine()) {
- mapper.insertAsNewOnReferenceBranch(NewCodeReferenceIssueDto.fromIssueDto(dto, now, uuidFactory));
+ mapper.insertAsNewCodeOnReferenceBranch(NewCodeReferenceIssueDto.fromIssueDto(dto, now, uuidFactory));
}
statistics.inserts++;
});
@@ -149,6 +157,18 @@ public class PersistIssuesStep implements ComputationStep {
updatedIssues.forEach(i -> issueStorage.insertChanges(changeMapper, i, uuidFactory));
}
+ private static void persistNoLongerNewIssues(IssueStatistics statistics, List<DefaultIssue> noLongerNewIssues, IssueMapper mapper) {
+ if (noLongerNewIssues.isEmpty()) {
+ return;
+ }
+
+ noLongerNewIssues.forEach(i -> {
+ mapper.deleteAsNewCodeOnReferenceBranch(i.key());
+ statistics.updates++;
+ });
+
+ }
+
private static void flushSession(DbSession dbSession) {
dbSession.flushStatements();
dbSession.commit();
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 21c80615e43..40bf25a4101 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
@@ -132,6 +132,7 @@ public class ProtobufIssueDiskCache implements DiskCache<DefaultIssue> {
defaultIssue.setSendNotifications(next.getSendNotifications());
defaultIssue.setSelectedAt(next.hasSelectedAt() ? next.getSelectedAt() : null);
defaultIssue.setQuickFixAvailable(next.getQuickFixAvailable());
+ defaultIssue.setIsNoLongerNewCodeReferenceIssue(next.getIsNoLongerNewCodeReferenceIssue());
for (IssueCache.FieldDiffs protoFieldDiffs : next.getChangesList()) {
defaultIssue.addChange(toDefaultIssueChanges(protoFieldDiffs));
@@ -182,6 +183,7 @@ public class ProtobufIssueDiskCache implements DiskCache<DefaultIssue> {
builder.setSendNotifications(defaultIssue.mustSendNotifications());
ofNullable(defaultIssue.selectedAt()).ifPresent(builder::setSelectedAt);
builder.setQuickFixAvailable(defaultIssue.isQuickFixAvailable());
+ builder.setIsNoLongerNewCodeReferenceIssue(defaultIssue.isNoLongerNewCodeReferenceIssue());
for (FieldDiffs fieldDiffs : defaultIssue.changes()) {
builder.addChanges(toProtoIssueChanges(fieldDiffs));
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 a873fefd52b..29bce80c281 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
@@ -56,7 +56,7 @@ message Issue {
optional bool isFromExternalRuleEngine = 25;
- // FUNCTIONAL DATES
+ // FUNCTIONAL DATES
optional int64 creationDate = 26;
optional int64 updateDate = 27;
optional int64 closeDate = 28;
@@ -78,6 +78,7 @@ message Issue {
optional bool isOnReferencedBranch = 40;
optional bool isOnChangedLine = 41;
+ optional bool isNoLongerNewCodeReferenceIssue = 42;
}
message Comment {
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulatorTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulatorTest.java
index be880239e00..e89a587a834 100644
--- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulatorTest.java
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulatorTest.java
@@ -19,7 +19,6 @@
*/
package org.sonar.ce.task.projectanalysis.container;
-import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
@@ -28,7 +27,6 @@ import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
-import javax.annotation.Nonnull;
import org.junit.Test;
import org.picocontainer.DefaultPicoContainer;
import org.picocontainer.PicoContainer;
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 913dc740343..d96928ad8b7 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
@@ -115,7 +115,8 @@ public class IssueLifecycleTest {
DefaultIssue raw = new DefaultIssue()
.setKey("raw");
DefaultIssue fromShort = new DefaultIssue()
- .setKey("short");
+ .setKey("short")
+ .setIsNewCodeReferenceIssue(true);
fromShort.setResolution("resolution");
fromShort.setStatus("status");
@@ -161,6 +162,7 @@ public class IssueLifecycleTest {
assertThat(raw.changes().get(1).diffs()).containsOnlyKeys(IssueFieldsSetter.FROM_BRANCH);
assertThat(raw.changes().get(1).get(IssueFieldsSetter.FROM_BRANCH).oldValue()).isEqualTo("#2");
assertThat(raw.changes().get(1).get(IssueFieldsSetter.FROM_BRANCH).newValue()).isEqualTo("master");
+ assertThat(raw.isNewCodeReferenceIssue()).isTrue();
}
@Test
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IssuesOnReferenceBranchVisitorTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IssuesOnReferenceBranchVisitorTest.java
new file mode 100644
index 00000000000..37a4aa58394
--- /dev/null
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/IssuesOnReferenceBranchVisitorTest.java
@@ -0,0 +1,110 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.issue;
+
+import org.junit.Test;
+import org.sonar.ce.task.projectanalysis.component.Component;
+import org.sonar.core.issue.DefaultIssue;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+public class IssuesOnReferenceBranchVisitorTest {
+
+ private final NewIssueClassifier newIssueClassifier = mock(NewIssueClassifier.class);
+ private final Component component = mock(Component.class);
+ private final DefaultIssue issue = mock(DefaultIssue.class);
+
+ private final IssueOnReferenceBranchVisitor underTest = new IssueOnReferenceBranchVisitor(newIssueClassifier);
+
+ @Test
+ public void issue_is_not_changed_when_newIssueClassifier_is_not_enabled() {
+ when(newIssueClassifier.isEnabled()).thenReturn(false);
+
+ underTest.onIssue(component, issue);
+ verifyNoInteractions(issue);
+ }
+
+ @Test
+ public void handles_issue_not_on_branch_using_reference_branch() {
+ when(newIssueClassifier.isEnabled()).thenReturn(true);
+ when(newIssueClassifier.isOnBranchUsingReferenceBranch()).thenReturn(false);
+
+ underTest.onIssue(component, issue);
+ verify(issue).setIsOnReferencedBranch(false);
+ verify(issue).isOnReferencedBranch();
+ verifyNoMoreInteractions(issue);
+ }
+
+ @Test
+ public void handles_overall_code_issue_on_branch_using_reference_branch() {
+ when(newIssueClassifier.isEnabled()).thenReturn(true);
+ when(newIssueClassifier.isOnBranchUsingReferenceBranch()).thenReturn(true);
+ when(newIssueClassifier.hasAtLeastOneLocationOnChangedLines(component, issue)).thenReturn(true);
+ when(issue.isOnReferencedBranch()).thenReturn(true);
+ when(issue.isNewCodeReferenceIssue()).thenReturn(false);
+
+ underTest.onIssue(component, issue);
+ verify(issue).setIsOnReferencedBranch(true);
+ verify(issue).isOnReferencedBranch();
+ verify(issue).setIsOnChangedLine(true);
+ verify(issue).isNewCodeReferenceIssue();
+ verifyNoMoreInteractions(issue);
+ }
+
+ @Test
+ public void handles_new_code_issue_on_branch_using_reference_branch_which_is_still_new() {
+ when(newIssueClassifier.isEnabled()).thenReturn(true);
+ when(newIssueClassifier.isOnBranchUsingReferenceBranch()).thenReturn(true);
+ when(newIssueClassifier.hasAtLeastOneLocationOnChangedLines(component, issue)).thenReturn(true);
+ when(issue.isOnReferencedBranch()).thenReturn(true);
+ when(issue.isNewCodeReferenceIssue()).thenReturn(true);
+ when(issue.isOnChangedLine()).thenReturn(true);
+
+ underTest.onIssue(component, issue);
+ verify(issue).setIsOnReferencedBranch(true);
+ verify(issue).isOnReferencedBranch();
+ verify(issue).setIsOnChangedLine(true);
+ verify(issue).isNewCodeReferenceIssue();
+ verify(issue).isOnChangedLine();
+ verifyNoMoreInteractions(issue);
+ }
+
+ @Test
+ public void handles_new_code_issue_on_branch_using_reference_branch_which_is_no_longer_new() {
+ when(newIssueClassifier.isEnabled()).thenReturn(true);
+ when(newIssueClassifier.isOnBranchUsingReferenceBranch()).thenReturn(true);
+ when(newIssueClassifier.hasAtLeastOneLocationOnChangedLines(component, issue)).thenReturn(false);
+ when(issue.isOnReferencedBranch()).thenReturn(true);
+ when(issue.isNewCodeReferenceIssue()).thenReturn(true);
+ when(issue.isOnChangedLine()).thenReturn(false);
+
+ underTest.onIssue(component, issue);
+ verify(issue).setIsOnReferencedBranch(true);
+ verify(issue).isOnReferencedBranch();
+ verify(issue).setIsOnChangedLine(false);
+ verify(issue).isNewCodeReferenceIssue();
+ verify(issue).setIsNoLongerNewCodeReferenceIssue(true);
+ verify(issue).setIsNewCodeReferenceIssue(false);
+ }
+}
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/NewIssueClassifierTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/NewIssueClassifierTest.java
index ed03ba5105d..a7afee4923c 100644
--- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/NewIssueClassifierTest.java
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/NewIssueClassifierTest.java
@@ -109,8 +109,8 @@ public class NewIssueClassifierTest {
.build())
.build());
assertThat(newIssueClassifier.isNew(file, issue)).isTrue();
- verify(issue).setIsOnReferencedBranch(true);
- verify(issue).setIsOnChangedLine(true);
+ assertThat(newIssueClassifier.isOnBranchUsingReferenceBranch()).isTrue();
+ assertThat(newIssueClassifier.hasAtLeastOneLocationOnChangedLines(file, issue)).isTrue();
}
@Test
@@ -130,8 +130,52 @@ public class NewIssueClassifierTest {
.build())
.build());
assertThat(newIssueClassifier.isNew(file, issue)).isFalse();
- verify(issue).setIsOnReferencedBranch(true);
- verify(issue).setIsOnChangedLine(false);
+ assertThat(newIssueClassifier.isOnBranchUsingReferenceBranch()).isTrue();
+ assertThat(newIssueClassifier.hasAtLeastOneLocationOnChangedLines(file, issue)).isFalse();
+ }
+
+ @Test
+ public void isNew_returns_false_for_issue_which_was_new_but_it_is_not_located_on_changed_lines_anymore() {
+ periodHolder.setPeriod(new Period(NewCodePeriodType.REFERENCE_BRANCH.name(), "master", null));
+ Component file = mock(Component.class);
+ DefaultIssue issue = mock(DefaultIssue.class);
+ when(file.getType()).thenReturn(Component.Type.FILE);
+ when(file.getUuid()).thenReturn("fileUuid");
+ when(newLinesRepository.getNewLines(file)).thenReturn(Optional.of(Set.of(2, 3)));
+ when(issue.getLocations()).thenReturn(DbIssues.Locations.newBuilder()
+ .setTextRange(DbCommons.TextRange.newBuilder()
+ .setStartLine(10)
+ .setStartOffset(1)
+ .setEndLine(10)
+ .setEndOffset(2)
+ .build())
+ .build());
+ when(issue.isNewCodeReferenceIssue()).thenReturn(true);
+ assertThat(newIssueClassifier.isNew(file, issue)).isFalse();
+ assertThat(newIssueClassifier.isOnBranchUsingReferenceBranch()).isTrue();
+ assertThat(newIssueClassifier.hasAtLeastOneLocationOnChangedLines(file, issue)).isFalse();
+ }
+
+ @Test
+ public void isNew_returns_true_for_issue_which_was_new_and_is_still_located_on_changed_lines() {
+ periodHolder.setPeriod(new Period(NewCodePeriodType.REFERENCE_BRANCH.name(), "master", null));
+ Component file = mock(Component.class);
+ DefaultIssue issue = mock(DefaultIssue.class);
+ when(file.getType()).thenReturn(Component.Type.FILE);
+ when(file.getUuid()).thenReturn("fileUuid");
+ when(newLinesRepository.getNewLines(file)).thenReturn(Optional.of(Set.of(2, 3)));
+ when(issue.getLocations()).thenReturn(DbIssues.Locations.newBuilder()
+ .setTextRange(DbCommons.TextRange.newBuilder()
+ .setStartLine(2)
+ .setStartOffset(1)
+ .setEndLine(2)
+ .setEndOffset(2)
+ .build())
+ .build());
+ when(issue.isNewCodeReferenceIssue()).thenReturn(true);
+ assertThat(newIssueClassifier.isNew(file, issue)).isTrue();
+ assertThat(newIssueClassifier.isOnBranchUsingReferenceBranch()).isTrue();
+ assertThat(newIssueClassifier.hasAtLeastOneLocationOnChangedLines(file, issue)).isTrue();
}
@Test
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/PersistIssuesStepTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/PersistIssuesStepTest.java
index aeeea14ee82..e53c0fe70d0 100644
--- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/PersistIssuesStepTest.java
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/PersistIssuesStepTest.java
@@ -68,6 +68,7 @@ import static org.sonar.api.issue.Issue.STATUS_CLOSED;
import static org.sonar.api.issue.Issue.STATUS_OPEN;
import static org.sonar.api.rule.Severity.BLOCKER;
import static org.sonar.db.component.ComponentTesting.newFileDto;
+import static org.sonar.db.issue.IssueTesting.newCodeReferenceIssue;
public class PersistIssuesStepTest extends BaseStepTest {
@@ -162,9 +163,7 @@ public class PersistIssuesStepTest extends BaseStepTest {
assertThat(result.getStatus()).isEqualTo(STATUS_OPEN);
assertThat(result.getType()).isEqualTo(RuleType.BUG.getDbConstant());
assertThat(result.getTags()).containsExactlyInAnyOrder("test");
-
- boolean isNewCodeOnReferencedBranch = dbClient.issueDao().isNewCodeOnReferencedBranch(session, result.getKey());
- assertThat(isNewCodeOnReferencedBranch).isFalse();
+ assertThat(result.isNewCodeReferenceIssue()).isFalse();
List<IssueChangeDto> changes = dbClient.issueChangeDao().selectByIssueKeys(session, Arrays.asList(issueKey));
assertThat(changes).extracting(IssueChangeDto::getChangeType).containsExactly(IssueChangeDto.TYPE_COMMENT, IssueChangeDto.TYPE_FIELD_CHANGE);
@@ -211,9 +210,7 @@ public class PersistIssuesStepTest extends BaseStepTest {
assertThat(result.getStatus()).isEqualTo(STATUS_OPEN);
assertThat(result.getType()).isEqualTo(RuleType.BUG.getDbConstant());
assertThat(result.getTags()).isEmpty();
-
- boolean isNewCodeOnReferencedBranch = dbClient.issueDao().isNewCodeOnReferencedBranch(session, result.getKey());
- assertThat(isNewCodeOnReferencedBranch).isFalse();
+ assertThat(result.isNewCodeReferenceIssue()).isFalse();
assertThat(dbClient.issueChangeDao().selectByIssueKeys(session, Arrays.asList(issueKey))).isEmpty();
assertThat(context.getStatistics().getAll()).contains(
@@ -272,9 +269,7 @@ public class PersistIssuesStepTest extends BaseStepTest {
assertThat(result.getSeverity()).isEqualTo(BLOCKER);
assertThat(result.getStatus()).isEqualTo(STATUS_OPEN);
assertThat(result.getType()).isEqualTo(RuleType.BUG.getDbConstant());
-
- boolean isNewCodeOnReferencedBranch = dbClient.issueDao().isNewCodeOnReferencedBranch(session, result.getKey());
- assertThat(isNewCodeOnReferencedBranch).isTrue();
+ assertThat(result.isNewCodeReferenceIssue()).isTrue();
List<IssueChangeDto> changes = dbClient.issueChangeDao().selectByIssueKeys(session, Arrays.asList(issueKey));
assertThat(changes).extracting(IssueChangeDto::getChangeType).containsExactly(IssueChangeDto.TYPE_COMMENT, IssueChangeDto.TYPE_FIELD_CHANGE);
@@ -354,9 +349,7 @@ public class PersistIssuesStepTest extends BaseStepTest {
assertThat(result.getType()).isEqualTo(RuleType.BUG.getDbConstant());
assertThat(context.getStatistics().getAll()).contains(
entry("inserts", "1"), entry("updates", "0"), entry("merged", "0"));
-
- boolean isNewCodeOnReferencedBranch = dbClient.issueDao().isNewCodeOnReferencedBranch(session, result.getKey());
- assertThat(isNewCodeOnReferencedBranch).isTrue();
+ assertThat(result.isNewCodeReferenceIssue()).isTrue();
}
@Test
@@ -391,6 +384,60 @@ public class PersistIssuesStepTest extends BaseStepTest {
}
@Test
+ public void handle_no_longer_new_issue() {
+ RuleDefinitionDto rule = RuleTesting.newRule(RuleKey.of("xoo", "S01"));
+ db.rules().insert(rule);
+ ComponentDto project = db.components().insertPrivateProject();
+ ComponentDto file = db.components().insertComponent(newFileDto(project, null));
+ when(system2.now()).thenReturn(NOW);
+ String issueKey = "ISSUE-5";
+
+ DefaultIssue defaultIssue = new DefaultIssue()
+ .setKey(issueKey)
+ .setType(RuleType.CODE_SMELL)
+ .setRuleKey(rule.getKey())
+ .setComponentUuid(file.uuid())
+ .setComponentKey(file.getKey())
+ .setProjectUuid(project.uuid())
+ .setProjectKey(project.getKey())
+ .setSeverity(BLOCKER)
+ .setStatus(STATUS_OPEN)
+ .setNew(true)
+ .setIsOnReferencedBranch(true)
+ .setIsOnChangedLine(true)
+ .setIsNewCodeReferenceIssue(true)
+ .setIsNoLongerNewCodeReferenceIssue(false)
+ .setCopied(false)
+ .setType(RuleType.BUG)
+ .setCreationDate(new Date(NOW))
+ .setSelectedAt(NOW);
+
+ IssueDto issueDto = IssueDto.toDtoForComputationInsert(defaultIssue, rule.getUuid(), NOW);
+ dbClient.issueDao().insert(session, issueDto);
+ dbClient.issueDao().insertAsNewCodeOnReferenceBranch(session, newCodeReferenceIssue(issueDto));
+ session.commit();
+
+ IssueDto result = dbClient.issueDao().selectOrFailByKey(session, issueKey);
+ assertThat(result.isNewCodeReferenceIssue()).isTrue();
+
+ protoIssueCache.newAppender().append(defaultIssue.setNew(false)
+ .setIsOnReferencedBranch(true)
+ .setIsOnChangedLine(false)
+ .setIsNewCodeReferenceIssue(false)
+ .setIsNoLongerNewCodeReferenceIssue(true))
+ .close();
+
+ TestComputationStepContext context = new TestComputationStepContext();
+ underTest.execute(context);
+
+ assertThat(context.getStatistics().getAll()).contains(
+ entry("inserts", "0"), entry("updates", "1"), entry("merged", "0"));
+
+ result = dbClient.issueDao().selectOrFailByKey(session, issueKey);
+ assertThat(result.isNewCodeReferenceIssue()).isFalse();
+ }
+
+ @Test
public void add_comment() {
ComponentDto project = db.components().insertPrivateProject();
ComponentDto file = db.components().insertComponent(newFileDto(project));
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDao.java
index 0e0de4ed55a..bff947b76fe 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDao.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueDao.java
@@ -58,10 +58,6 @@ public class IssueDao implements Dao {
return executeLargeInputs(keys, mapper(session)::selectByKeys);
}
- public boolean isNewCodeOnReferencedBranch(DbSession session, String issueKey) {
- return mapper(session).isNewCodeOnReferencedBranch(issueKey);
- }
-
public Set<String> selectIssueKeysByComponentUuid(DbSession session, String componentUuid) {
return mapper(session).selectIssueKeysByComponentUuid(componentUuid);
}
@@ -111,9 +107,12 @@ public class IssueDao implements Dao {
mapper(session).update(dto);
}
- public void insertAsNewOnReferenceBranch(DbSession session, NewCodeReferenceIssueDto dto) {
- IssueMapper mapper = mapper(session);
- mapper.insertAsNewOnReferenceBranch(dto);
+ public void insertAsNewCodeOnReferenceBranch(DbSession session, NewCodeReferenceIssueDto dto) {
+ mapper(session).insertAsNewCodeOnReferenceBranch(dto);
+ }
+
+ public void deleteAsNewCodeOnReferenceBranch(DbSession session, String issueKey) {
+ mapper(session).deleteAsNewCodeOnReferenceBranch(issueKey);
}
private static IssueMapper mapper(DbSession session) {
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 f1bc6419665..323a1c80d65 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
@@ -77,6 +77,7 @@ public final class IssueDto implements Serializable {
private long createdAt;
private long updatedAt;
private boolean quickFixAvailable;
+ private boolean isNewCodeReferenceIssue;
// functional dates stored as Long
private Long issueCreationDate;
@@ -141,6 +142,8 @@ public final class IssueDto implements Serializable {
.setIssueUpdateDate(issue.updateDate())
.setSelectedAt(issue.selectedAt())
.setQuickFixAvailable(issue.isQuickFixAvailable())
+ .setIsNewCodeReferenceIssue(issue.isNewCodeReferenceIssue())
+
// technical dates
.setCreatedAt(now)
@@ -188,6 +191,7 @@ public final class IssueDto implements Serializable {
.setIssueUpdateDate(issue.updateDate())
.setSelectedAt(issue.selectedAt())
.setQuickFixAvailable(issue.isQuickFixAvailable())
+ .setIsNewCodeReferenceIssue(issue.isNewCodeReferenceIssue())
// technical date
.setUpdatedAt(now);
@@ -702,6 +706,15 @@ public final class IssueDto implements Serializable {
return this;
}
+ public boolean isNewCodeReferenceIssue() {
+ return isNewCodeReferenceIssue;
+ }
+
+ public IssueDto setIsNewCodeReferenceIssue(boolean isNewCodeReferenceIssue) {
+ this.isNewCodeReferenceIssue = isNewCodeReferenceIssue;
+ return this;
+ }
+
public int getType() {
return type;
}
@@ -758,6 +771,7 @@ public final class IssueDto implements Serializable {
issue.setLocations(parseLocations());
issue.setIsFromExternalRuleEngine(isExternal);
issue.setQuickFixAvailable(quickFixAvailable);
+ issue.setIsNewCodeReferenceIssue(isNewCodeReferenceIssue);
return issue;
}
}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueMapper.java
index 49399d49cf9..bf783317577 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueMapper.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueMapper.java
@@ -37,8 +37,6 @@ public interface IssueMapper {
List<IssueDto> selectByKeys(List<String> keys);
- boolean isNewCodeOnReferencedBranch(@Param("issueKey") String issueKey);
-
Set<String> selectIssueKeysByComponentUuid(@Param("componentUuid") String componentUuid);
List<IssueDto> selectByComponentUuidPaginated(@Param("componentUuid") String componentUuid,
@@ -52,7 +50,9 @@ public interface IssueMapper {
int update(IssueDto issue);
- void insertAsNewOnReferenceBranch(NewCodeReferenceIssueDto issue);
+ void insertAsNewCodeOnReferenceBranch(NewCodeReferenceIssueDto issue);
+
+ void deleteAsNewCodeOnReferenceBranch(String issueKey);
int updateIfBeforeSelectedDate(IssueDto issue);
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 8e84d87f0f7..6f96b1ff854 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
@@ -39,7 +39,8 @@
root.kee as projectKey,
i.project_uuid as projectUuid,
i.issue_type as type,
- i.quick_fix_available as quickFixAvailable
+ i.quick_fix_available as quickFixAvailable,
+ <include refid="isNewCodeReferenceIssue"/>
</sql>
<sql id="issueColumnsInInnerQuery">
@@ -70,6 +71,16 @@
i.quick_fix_available
</sql>
+ <sql id="isNewCodeReferenceIssue" databaseId="mssql">
+ cast(case when n.uuid is null then 0 else 1 end as bit) as isNewCodeReferenceIssue
+ </sql>
+ <sql id="isNewCodeReferenceIssue" databaseId="oracle">
+ case when n.uuid is null then 0 else 1 end as isNewCodeReferenceIssue
+ </sql>
+ <sql id="isNewCodeReferenceIssue">
+ n.uuid is not null as isNewCodeReferenceIssue
+ </sql>
+
<sql id="sortColumn">
<if test="query.sort() != null">,
<choose>
@@ -118,7 +129,7 @@
#{quickFixAvailable, jdbcType=BOOLEAN})
</insert>
- <insert id="insertAsNewOnReferenceBranch" parameterType="NewCodeReferenceIssue" useGeneratedKeys="false">
+ <insert id="insertAsNewCodeOnReferenceBranch" parameterType="NewCodeReferenceIssue" useGeneratedKeys="false">
INSERT INTO new_code_reference_issues (uuid, issue_key, created_at)
VALUES (
#{uuid,jdbcType=VARCHAR},
@@ -126,6 +137,11 @@
#{createdAt,jdbcType=BIGINT})
</insert>
+ <insert id="deleteAsNewCodeOnReferenceBranch" parameterType="String" useGeneratedKeys="false">
+ DELETE FROM new_code_reference_issues
+ where issue_key = #{issueKey, jdbcType=VARCHAR}
+ </insert>
+
<!--
IMPORTANT - invariant columns can't be updated. See IssueDto#toDtoForUpdate()
-->
@@ -190,30 +206,10 @@
inner join rules r on r.uuid=i.rule_uuid
inner join components p on p.uuid=i.component_uuid
inner join components root on root.uuid=i.project_uuid
+ left join new_code_reference_issues n on i.kee = n.issue_key
where i.kee=#{kee,jdbcType=VARCHAR}
</select>
- <sql id="isNewCodeOnReferencedBranchSql">
- select
- case when exists
- (
- select i.uuid from new_code_reference_issues i
- where i.issue_key = #{issueKey,jdbcType=VARCHAR}
- )
- then 1
- else 0
- end
- </sql>
-
- <select id="isNewCodeOnReferencedBranch" parameterType="String" resultType="boolean">
- <include refid="isNewCodeOnReferencedBranchSql"/>
- </select>
-
- <select id="isNewCodeOnReferencedBranch" parameterType="String" resultType="boolean" databaseId="oracle">
- <include refid="isNewCodeOnReferencedBranchSql"/>
- from dual
- </select>
-
<select id="scrollNonClosedByComponentUuid" parameterType="String" resultType="Issue" fetchSize="${_scrollFetchSize}"
resultSetType="FORWARD_ONLY">
select
@@ -222,6 +218,7 @@
inner join rules r on r.uuid=i.rule_uuid
inner join components p on p.uuid=i.component_uuid
inner join components root on root.uuid=i.project_uuid
+ left join new_code_reference_issues n on i.kee = n.issue_key
where
i.component_uuid = #{componentUuid,jdbcType=VARCHAR} and
i.status &lt;&gt; 'CLOSED'
@@ -234,6 +231,7 @@
inner join rules r on r.uuid=i.rule_uuid
inner join components p on p.uuid=i.component_uuid
inner join components root on root.uuid=i.project_uuid
+ left join new_code_reference_issues n on i.kee = n.issue_key
where
(r.is_external is NULL or r.is_external = ${_false}) and
i.component_uuid = #{componentUuid,jdbcType=VARCHAR} and
@@ -256,6 +254,8 @@
ic.issue_key = i.kee
and ic.change_type = 'diff'
and ic.change_data like '%status=%|CLOSED%'
+ left join new_code_reference_issues n on
+ i.kee = n.issue_key
where
i.component_uuid = #{componentUuid,jdbcType=VARCHAR}
and i.status = 'CLOSED'
@@ -291,6 +291,7 @@
inner join rules r on r.uuid=i.rule_uuid
inner join components p on p.uuid=i.component_uuid
inner join components root on root.uuid=i.project_uuid
+ left join new_code_reference_issues n on i.kee = n.issue_key
where i.kee in
<foreach collection="list" open="(" close=")" item="key" separator=",">
#{key,jdbcType=VARCHAR}
@@ -304,6 +305,7 @@
inner join rules r on r.uuid=i.rule_uuid
inner join components p on p.uuid=i.component_uuid
inner join components root on root.uuid=i.project_uuid
+ left join new_code_reference_issues n on i.kee = n.issue_key
where
i.kee in
<foreach collection="keys" open="(" close=")" item="key" separator=",">
@@ -341,6 +343,7 @@
inner join rules r on r.uuid = i.rule_uuid
inner join components p on p.uuid = i.component_uuid
inner join components root on root.uuid = i.project_uuid
+ left join new_code_reference_issues n on i.kee = n.issue_key
where
(r.is_external is NULL or r.is_external = ${_false}) and
i.project_uuid = #{projectUuid, jdbcType=VARCHAR} and
@@ -402,6 +405,7 @@
inner join components p on p.uuid=i.component_uuid
inner join components root on root.uuid=i.project_uuid
left join users u on i.assignee = u.uuid
+ left join new_code_reference_issues n on i.kee = n.issue_key
where i.project_uuid=#{componentUuid,jdbcType=VARCHAR}
order by i.issue_creation_date ASC
limit #{pagination.pageSize,jdbcType=INTEGER} offset #{pagination.offset,jdbcType=INTEGER}
@@ -424,6 +428,7 @@
inner join components p on p.uuid=i.component_uuid
inner join components root on root.uuid=i.project_uuid
left join users u on i.assignee = u.uuid
+ left join new_code_reference_issues n on i.kee = n.issue_key
</select>
<select id="selectByComponentUuidPaginated" parameterType="map" resultType="Issue" databaseId="oracle">
@@ -447,6 +452,7 @@
inner join components p on p.uuid=i.component_uuid
inner join components root on root.uuid=i.project_uuid
left join users u on i.assignee = u.uuid
+ left join new_code_reference_issues n on i.kee = n.issue_key
</select>
</mapper>
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 afa5e7b28de..1621667f538 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
@@ -444,7 +444,7 @@ public class IssueDaoTest {
}
@Test
- public void selectByKey_givenOneIssueWithQuickFix_selectOneIssueWithQuickFix2() {
+ public void selectByKey_givenOneIssueNewOnReferenceBranch_selectOneIssueWithNewOnReferenceBranch() {
prepareIssuesComponent();
underTest.insert(db.getSession(), newIssueDto(ISSUE_KEY1)
.setMessage("the message")
@@ -459,12 +459,18 @@ public class IssueDaoTest {
.setProjectUuid(PROJECT_UUID)
.setQuickFixAvailable(true));
IssueDto issue1 = underTest.selectOrFailByKey(db.getSession(), ISSUE_KEY1);
+ IssueDto issue2 = underTest.selectOrFailByKey(db.getSession(), ISSUE_KEY2);
- underTest.insertAsNewOnReferenceBranch(db.getSession(),
- newCodeReferenceIssue(issue1));
+ assertThat(issue1.isNewCodeReferenceIssue()).isFalse();
+ assertThat(issue2.isNewCodeReferenceIssue()).isFalse();
- assertThat(underTest.isNewCodeOnReferencedBranch(db.getSession(), ISSUE_KEY1)).isTrue();
- assertThat(underTest.isNewCodeOnReferencedBranch(db.getSession(), ISSUE_KEY2)).isFalse();
+ underTest.insertAsNewCodeOnReferenceBranch(db.getSession(), newCodeReferenceIssue(issue1));
+
+ assertThat(underTest.selectOrFailByKey(db.getSession(), ISSUE_KEY1).isNewCodeReferenceIssue()).isTrue();
+ assertThat(underTest.selectOrFailByKey(db.getSession(), ISSUE_KEY2).isNewCodeReferenceIssue()).isFalse();
+
+ underTest.deleteAsNewCodeOnReferenceBranch(db.getSession(), ISSUE_KEY1);
+ assertThat(underTest.selectOrFailByKey(db.getSession(), ISSUE_KEY1).isNewCodeReferenceIssue()).isFalse();
}
private static IssueDto newIssueDto(String key) {
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 8f1b5dabeb7..b8c221c6360 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
@@ -112,6 +112,7 @@ public class IssueDtoTest {
assertThat(issue.updateDate()).isEqualTo(DateUtils.truncate(updatedAt, Calendar.SECOND));
assertThat(issue.closeDate()).isEqualTo(DateUtils.truncate(closedAt, Calendar.SECOND));
assertThat(issue.isNew()).isFalse();
+ assertThat(issue.isNewCodeReferenceIssue()).isFalse();
}
@Test
@@ -182,7 +183,7 @@ public class IssueDtoTest {
"path/to/module/uuid", "123", "projectKey", "key=value", "ruleUuid");
assertThat(issueDto.isQuickFixAvailable()).isTrue();
-
+ assertThat(issueDto.isNewCodeReferenceIssue()).isTrue();
}
@Test
@@ -214,7 +215,7 @@ public class IssueDtoTest {
"path/to/module/uuid", "123", "projectKey", "key=value");
assertThat(issueDto.isQuickFixAvailable()).isTrue();
-
+ assertThat(issueDto.isNewCodeReferenceIssue()).isTrue();
}
private DefaultIssue createExampleDefaultIssue(Date dateNow) {
@@ -246,7 +247,8 @@ public class IssueDtoTest {
.setCloseDate(dateNow)
.setUpdateDate(dateNow)
.setSelectedAt(dateNow.getTime())
- .setQuickFixAvailable(true);
+ .setQuickFixAvailable(true)
+ .setIsNewCodeReferenceIssue(true);
return defaultIssue;
}
}
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java
index 19b84c9db2c..a159c50505f 100644
--- a/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java
@@ -1126,11 +1126,17 @@ public class PurgeDaoTest {
// others remain
assertThat(db.countRowsOfTable("issues")).isEqualTo(2);
- assertThat(db.getDbClient().issueDao().selectByKey(dbSession, notOldEnoughClosed.getKey())).isNotEmpty();
- assertThat(db.getDbClient().issueDao().isNewCodeOnReferencedBranch(dbSession, notOldEnoughClosed.getKey())).isTrue();
- assertThat(db.getDbClient().issueDao().selectByKey(dbSession, notClosed.getKey())).isNotEmpty();
- assertThat(db.getDbClient().issueDao().isNewCodeOnReferencedBranch(dbSession, notClosed.getKey())).isTrue();
- assertThat(db.getDbClient().issueDao().isNewCodeOnReferencedBranch(dbSession, oldClosed.getKey())).isFalse();
+
+ Optional<IssueDto> notOldEnoughClosedFromQuery = db.getDbClient().issueDao().selectByKey(dbSession, notOldEnoughClosed.getKey());
+ assertThat(notOldEnoughClosedFromQuery).isNotEmpty();
+ assertThat(notOldEnoughClosedFromQuery.get().isNewCodeReferenceIssue()).isTrue();
+
+ Optional<IssueDto> notClosedFromQuery = db.getDbClient().issueDao().selectByKey(dbSession, notClosed.getKey());
+ assertThat(notClosedFromQuery).isNotEmpty();
+ assertThat(notClosedFromQuery.get().isNewCodeReferenceIssue()).isTrue();
+
+ Optional<IssueDto> oldClosedFromQuery = db.getDbClient().issueDao().selectByKey(dbSession, oldClosed.getKey());
+ assertThat(oldClosedFromQuery).isEmpty();
}
@Test
diff --git a/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/issue/IssueDbTester.java b/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/issue/IssueDbTester.java
index 10ce0985873..632368599a0 100644
--- a/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/issue/IssueDbTester.java
+++ b/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/issue/IssueDbTester.java
@@ -222,7 +222,7 @@ public class IssueDbTester {
* Inserts an issue as new code in a branch using reference branch for new code
*/
public void insertNewCodeReferenceIssue(NewCodeReferenceIssueDto dto) {
- db.getDbClient().issueDao().insertAsNewOnReferenceBranch(db.getSession(), dto);
+ db.getDbClient().issueDao().insertAsNewCodeOnReferenceBranch(db.getSession(), dto);
db.commit();
}
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 89aa9a7fa3b..7d8a8874896 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
@@ -125,6 +125,10 @@ public class DefaultIssue implements Issue, Trackable, org.sonar.api.ce.measure.
private Long selectedAt;
private boolean quickFixAvailable;
+ private boolean isNewCodeReferenceIssue;
+
+ // true if the issue is no longer new in its branch
+ private boolean isNoLongerNewCodeReferenceIssue = false;
@Override
public String key() {
@@ -548,6 +552,24 @@ public class DefaultIssue implements Issue, Trackable, org.sonar.api.ce.measure.
return this;
}
+ public boolean isNewCodeReferenceIssue() {
+ return isNewCodeReferenceIssue;
+ }
+
+ public DefaultIssue setIsNewCodeReferenceIssue(boolean isNewCodeReferenceIssue) {
+ this.isNewCodeReferenceIssue = isNewCodeReferenceIssue;
+ return this;
+ }
+
+ public boolean isNoLongerNewCodeReferenceIssue() {
+ return isNoLongerNewCodeReferenceIssue;
+ }
+
+ public DefaultIssue setIsNoLongerNewCodeReferenceIssue(boolean isNoLongerNewCodeReferenceIssue) {
+ this.isNoLongerNewCodeReferenceIssue = isNoLongerNewCodeReferenceIssue;
+ return this;
+ }
+
@CheckForNull
public FieldDiffs currentChange() {
return currentChange;
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 874272d2d40..423dc536b00 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
@@ -58,6 +58,8 @@ public class DefaultIssueTest {
.setNew(true)
.setIsOnReferencedBranch(true)
.setIsOnChangedLine(true)
+ .setIsNewCodeReferenceIssue(true)
+ .setIsNoLongerNewCodeReferenceIssue(true)
.setBeingClosed(true)
.setOnDisabledRule(true)
.setCopied(true)
@@ -87,6 +89,8 @@ public class DefaultIssueTest {
assertThat(issue.isNew()).isTrue();
assertThat(issue.isOnReferencedBranch()).isTrue();
assertThat(issue.isOnChangedLine()).isTrue();
+ assertThat(issue.isNewCodeReferenceIssue()).isTrue();
+ assertThat(issue.isNoLongerNewCodeReferenceIssue()).isTrue();
assertThat(issue.isCopied()).isTrue();
assertThat(issue.isBeingClosed()).isTrue();
assertThat(issue.isOnDisabledRule()).isTrue();