aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorLéo Geoffroy <leo.geoffroy@sonarsource.com>2023-12-18 15:13:35 +0100
committersonartech <sonartech@sonarsource.com>2024-01-17 20:02:43 +0000
commit549dee654c7412853d6f6682928bc50893a28ef3 (patch)
tree82a033c5d7d3c446cf830886353dad76c36e6e1c /server
parent0323b00fad8cce2c66fb1b2a6b015aff5ca8933b (diff)
downloadsonarqube-549dee654c7412853d6f6682928bc50893a28ef3.tar.gz
sonarqube-549dee654c7412853d6f6682928bc50893a28ef3.zip
SONAR-21259 Add tracking and persistence for fixed issues
Diffstat (limited to 'server')
-rw-r--r--server/sonar-ce-task-projectanalysis/src/it/java/org/sonar/ce/task/projectanalysis/step/PersistPullRequestFixedIssueStepIT.java122
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java2
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IntegrateIssuesVisitor.java25
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueVisitor.java8
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueVisitors.java8
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/fixedissues/PullRequestFixedIssueRepository.java30
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/fixedissues/PullRequestFixedIssueRepositoryImpl.java39
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/fixedissues/package-info.java23
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistPullRequestFixedIssueStep.java77
-rw-r--r--server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/ReportComputationSteps.java1
-rw-r--r--server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/fixedissues/PullRequestFixedIssueRepositoryImplTest.java38
-rw-r--r--server/sonar-db-dao/src/it/java/org/sonar/db/issue/IssueFixedDaoIT.java12
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueFixedDao.java4
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueFixedMapper.java2
-rw-r--r--server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueFixedMapper.xml7
15 files changed, 385 insertions, 13 deletions
diff --git a/server/sonar-ce-task-projectanalysis/src/it/java/org/sonar/ce/task/projectanalysis/step/PersistPullRequestFixedIssueStepIT.java b/server/sonar-ce-task-projectanalysis/src/it/java/org/sonar/ce/task/projectanalysis/step/PersistPullRequestFixedIssueStepIT.java
new file mode 100644
index 00000000000..8a6ad9c1145
--- /dev/null
+++ b/server/sonar-ce-task-projectanalysis/src/it/java/org/sonar/ce/task/projectanalysis/step/PersistPullRequestFixedIssueStepIT.java
@@ -0,0 +1,122 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 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.step;
+
+import org.assertj.core.api.Assertions;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder;
+import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule;
+import org.sonar.ce.task.projectanalysis.issue.fixedissues.PullRequestFixedIssueRepositoryImpl;
+import org.sonar.ce.task.step.TestComputationStepContext;
+import org.sonar.core.issue.DefaultIssue;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.ProjectData;
+import org.sonar.db.issue.IssueFixedDto;
+
+import static org.assertj.core.groups.Tuple.tuple;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.ce.task.projectanalysis.component.Component.Type.PROJECT;
+import static org.sonar.ce.task.projectanalysis.component.ReportComponent.builder;
+
+public class PersistPullRequestFixedIssueStepIT {
+ @Rule
+ public DbTester db = DbTester.create(System2.INSTANCE);
+
+ @Rule
+ public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
+ private PullRequestFixedIssueRepositoryImpl fixedIssueRepository;
+ public PersistPullRequestFixedIssueStep underTest;
+
+ private AnalysisMetadataHolder analysisMetadataHolder = mock(AnalysisMetadataHolder.class);
+
+ @Before
+ public void before() {
+ fixedIssueRepository = new PullRequestFixedIssueRepositoryImpl();
+ underTest = new PersistPullRequestFixedIssueStep(analysisMetadataHolder, db.getDbClient(), treeRootHolder, fixedIssueRepository);
+ when(analysisMetadataHolder.isPullRequest()).thenReturn(true);
+ }
+
+ @Test
+ public void execute_shouldPersistFixedIssues() {
+ ProjectData projectData = db.components().insertPublicProject();
+ ComponentDto pullRequest = db.components().insertProjectBranch(projectData.getMainBranchComponent());
+
+ fixedIssueRepository.addFixedIssue(new DefaultIssue().setKey("key1"));
+ fixedIssueRepository.addFixedIssue(new DefaultIssue().setKey("key2"));
+ treeRootHolder.setRoot(builder(PROJECT, 1)
+ .setUuid(pullRequest.uuid())
+ .build());
+
+ underTest.execute(new TestComputationStepContext());
+
+ Assertions.assertThat(db.getDbClient().issueFixedDao().selectByPullRequest(db.getSession(), pullRequest.uuid()))
+ .extracting(IssueFixedDto::issueKey, IssueFixedDto::pullRequestUuid)
+ .containsExactly(tuple("key1", pullRequest.uuid()), tuple("key2", pullRequest.uuid()));
+ }
+
+ @Test
+ public void execute_whenFixedIssuesAlreadyExists_shouldKeepExistingFixedIssues() {
+ ProjectData projectData = db.components().insertPublicProject();
+ ComponentDto pullRequest = db.components().insertProjectBranch(projectData.getMainBranchComponent());
+
+ DbSession session = db.getSession();
+ db.getDbClient().issueFixedDao().insert(session, new IssueFixedDto(pullRequest.uuid(), "key1"));
+ db.getDbClient().issueFixedDao().insert(session, new IssueFixedDto(pullRequest.uuid(), "key2"));
+ session.commit();
+
+ fixedIssueRepository.addFixedIssue(new DefaultIssue().setKey("key1"));
+ treeRootHolder.setRoot(builder(PROJECT, 1)
+ .setUuid(pullRequest.uuid())
+ .build());
+
+ underTest.execute(new TestComputationStepContext());
+
+ Assertions.assertThat(db.getDbClient().issueFixedDao().selectByPullRequest(session, pullRequest.uuid()))
+ .extracting(IssueFixedDto::issueKey, IssueFixedDto::pullRequestUuid)
+ .containsExactly(tuple("key1", pullRequest.uuid()));
+ }
+
+ @Test
+ public void execute_whenRepositoryIsEmpty_shouldNotPersistAnyFixedIssues() {
+ ProjectData projectData = db.components().insertPublicProject();
+ ComponentDto pullRequest = db.components().insertProjectBranch(projectData.getMainBranchComponent());
+
+ DbSession session = db.getSession();
+ db.getDbClient().issueFixedDao().insert(session, new IssueFixedDto(pullRequest.uuid(), "key1"));
+ session.commit();
+
+ treeRootHolder.setRoot(builder(PROJECT, 1)
+ .setUuid(pullRequest.uuid())
+ .build());
+
+ underTest.execute(new TestComputationStepContext());
+
+ Assertions.assertThat(db.getDbClient().issueFixedDao().selectByPullRequest(db.getSession(), pullRequest.uuid()))
+ .isEmpty();
+ }
+
+
+}
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 1f8615159df..476ca2cf47f 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
@@ -100,6 +100,7 @@ import org.sonar.ce.task.projectanalysis.issue.TrackerTargetBranchInputFactory;
import org.sonar.ce.task.projectanalysis.issue.TransitionIssuesToAnticipatedStatesVisitor;
import org.sonar.ce.task.projectanalysis.issue.UpdateConflictResolver;
import org.sonar.ce.task.projectanalysis.issue.filter.IssueFilter;
+import org.sonar.ce.task.projectanalysis.issue.fixedissues.PullRequestFixedIssueRepositoryImpl;
import org.sonar.ce.task.projectanalysis.language.LanguageRepositoryImpl;
import org.sonar.ce.task.projectanalysis.locations.flow.FlowGenerator;
import org.sonar.ce.task.projectanalysis.measure.MeasureComputersHolderImpl;
@@ -240,6 +241,7 @@ public final class ProjectAnalysisTaskContainerPopulator implements ContainerPop
SourceLineReadersFactory.class,
QProfileStatusRepositoryImpl.class,
IssueChangesToDeleteRepository.class,
+ PullRequestFixedIssueRepositoryImpl.class,
// issues
RuleRepositoryImpl.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 339f739c310..55f68ee47bb 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
@@ -86,8 +86,12 @@ public class IntegrateIssuesVisitor extends TypeAwareVisitorAdapter {
public void visitAny(Component component) {
try (CacheAppender<DefaultIssue> cacheAppender = protoIssueCache.newAppender()) {
issueVisitors.beforeComponent(component);
- List<DefaultIssue> issues = getIssues(component);
+ Input<DefaultIssue> rawInput = rawInputFactory.create(component);
+ List<DefaultIssue> issues = getIssues(rawInput, component);
+
+ issueVisitors.onRawIssues(component, rawInput);
processIssues(component, issues);
+
issueVisitors.beforeCaching(component);
appendIssuesToCache(cacheAppender, issues);
issueVisitors.afterComponent(component);
@@ -96,21 +100,21 @@ public class IntegrateIssuesVisitor extends TypeAwareVisitorAdapter {
}
}
- private List<DefaultIssue> getIssues(Component component) {
+ private List<DefaultIssue> getIssues(Input<DefaultIssue> rawInput, Component component) {
if (fileStatuses.isDataUnchanged(component)) {
// we assume there's a previous analysis of the same branch
- return getIssuesForUnchangedFile(component);
+ return getIssuesForUnchangedFile(rawInput, component);
} else {
- return getRawIssues(component);
+ return getRawIssues(rawInput, component);
}
}
- private List<DefaultIssue> getIssuesForUnchangedFile(Component component) {
+ private List<DefaultIssue> getIssuesForUnchangedFile(Input<DefaultIssue> rawInput, Component component) {
Input<DefaultIssue> baseIssues = baseInputFactory.create(component);
Collection<DefaultIssue> issues = baseIssues.getIssues();
//In case of plugin update, issue impacts are potentially updated. We want to avoid incremental analysis in this case.
return hasAnyInvolvedPluginChangedSinceLastAnalysis(issues)
- ? getRawIssues(component)
+ ? getRawIssues(rawInput, component)
: new LinkedList<>(issues);
}
@@ -130,8 +134,7 @@ public class IntegrateIssuesVisitor extends TypeAwareVisitorAdapter {
.toList();
}
- private List<DefaultIssue> getRawIssues(Component component) {
- Input<DefaultIssue> rawInput = rawInputFactory.create(component);
+ private List<DefaultIssue> getRawIssues(Input<DefaultIssue> rawInput, Component component) {
TrackingResult tracking = issueTracking.track(component, rawInput);
var newOpenIssues = fillNewOpenIssues(component, tracking.newIssues(), rawInput);
var existingOpenIssues = fillExistingOpenIssues(tracking.issuesToMerge());
@@ -170,9 +173,9 @@ public class IntegrateIssuesVisitor extends TypeAwareVisitorAdapter {
private static List<DefaultIssue> closeIssues(Stream<DefaultIssue> issues) {
return issues.map(issue ->
- // TODO should replace flag "beingClosed" by express call to transition "automaticClose"
- issue.setBeingClosed(true)
- // TODO manual issues -> was updater.setResolution(newIssue, Issue.RESOLUTION_REMOVED, changeContext);. Is it a problem ?
+ // TODO should replace flag "beingClosed" by express call to transition "automaticClose"
+ issue.setBeingClosed(true)
+ // TODO manual issues -> was updater.setResolution(newIssue, Issue.RESOLUTION_REMOVED, changeContext);. Is it a problem ?
).toList();
}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueVisitor.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueVisitor.java
index d64d5dd0697..d8fc94b4551 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueVisitor.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueVisitor.java
@@ -21,6 +21,7 @@ package org.sonar.ce.task.projectanalysis.issue;
import org.sonar.ce.task.projectanalysis.component.Component;
import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.tracking.Input;
public abstract class IssueVisitor {
@@ -45,6 +46,13 @@ public abstract class IssueVisitor {
}
/**
+ * This method is called for all raw issues of a component before tracking is done.
+ */
+ public void onRawIssues(Component component, Input<DefaultIssue> rawIssues) {
+
+ }
+
+ /**
* This method is called on a component before issues are persisted to cache.
*/
public void beforeCaching(Component component) {
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueVisitors.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueVisitors.java
index 8dcf31b0b15..6fadb057e7d 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueVisitors.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/IssueVisitors.java
@@ -21,6 +21,7 @@ package org.sonar.ce.task.projectanalysis.issue;
import org.sonar.ce.task.projectanalysis.component.Component;
import org.sonar.core.issue.DefaultIssue;
+import org.sonar.core.issue.tracking.Input;
public class IssueVisitors {
@@ -53,4 +54,11 @@ public class IssueVisitors {
visitor.beforeCaching(component);
}
}
+
+ public void onRawIssues(Component component, Input<DefaultIssue> rawIssues) {
+ for (IssueVisitor visitor : visitors) {
+ visitor.onRawIssues(component, rawIssues);
+ }
+ }
+
}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/fixedissues/PullRequestFixedIssueRepository.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/fixedissues/PullRequestFixedIssueRepository.java
new file mode 100644
index 00000000000..5e9426170ec
--- /dev/null
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/fixedissues/PullRequestFixedIssueRepository.java
@@ -0,0 +1,30 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 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.fixedissues;
+
+import java.util.List;
+import org.sonar.core.issue.DefaultIssue;
+
+public interface PullRequestFixedIssueRepository {
+
+ List<DefaultIssue> getFixedIssues();
+
+ void addFixedIssue(DefaultIssue issue);
+}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/fixedissues/PullRequestFixedIssueRepositoryImpl.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/fixedissues/PullRequestFixedIssueRepositoryImpl.java
new file mode 100644
index 00000000000..31a70434f24
--- /dev/null
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/fixedissues/PullRequestFixedIssueRepositoryImpl.java
@@ -0,0 +1,39 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 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.fixedissues;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.sonar.core.issue.DefaultIssue;
+
+public class PullRequestFixedIssueRepositoryImpl implements PullRequestFixedIssueRepository {
+
+ List<DefaultIssue> issues = new ArrayList<>();
+
+ @Override
+ public List<DefaultIssue> getFixedIssues() {
+ return issues;
+ }
+
+ @Override
+ public void addFixedIssue(DefaultIssue issue) {
+ issues.add(issue);
+ }
+}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/fixedissues/package-info.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/fixedissues/package-info.java
new file mode 100644
index 00000000000..1ea710b1e88
--- /dev/null
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/issue/fixedissues/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.ce.task.projectanalysis.issue.fixedissues;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistPullRequestFixedIssueStep.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistPullRequestFixedIssueStep.java
new file mode 100644
index 00000000000..fda9912fc2d
--- /dev/null
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistPullRequestFixedIssueStep.java
@@ -0,0 +1,77 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 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.step;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder;
+import org.sonar.ce.task.projectanalysis.component.TreeRootHolder;
+import org.sonar.ce.task.projectanalysis.issue.fixedissues.PullRequestFixedIssueRepository;
+import org.sonar.ce.task.step.ComputationStep;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.issue.IssueFixedDto;
+
+public class PersistPullRequestFixedIssueStep implements ComputationStep {
+
+ private final AnalysisMetadataHolder analysisMetadataHolder;
+ private final DbClient dbClient;
+ private final TreeRootHolder treeRootHolder;
+ private final PullRequestFixedIssueRepository pullRequestFixedIssueRepository;
+
+ public PersistPullRequestFixedIssueStep(AnalysisMetadataHolder analysisMetadataHolder, DbClient dbClient, TreeRootHolder treeRootHolder,
+ PullRequestFixedIssueRepository pullRequestFixedIssueRepository) {
+ this.analysisMetadataHolder = analysisMetadataHolder;
+
+ this.dbClient = dbClient;
+ this.treeRootHolder = treeRootHolder;
+ this.pullRequestFixedIssueRepository = pullRequestFixedIssueRepository;
+ }
+
+ @Override
+ public void execute(Context context) {
+ if (!analysisMetadataHolder.isPullRequest()) {
+ return;
+ }
+ try (DbSession dbSession = dbClient.openSession(true)) {
+ List<IssueFixedDto> currentIssuesFixed = dbClient.issueFixedDao().selectByPullRequest(dbSession, treeRootHolder.getRoot().getUuid());
+ List<IssueFixedDto> newIssuesFixed = pullRequestFixedIssueRepository.getFixedIssues().stream()
+ .map(i -> new IssueFixedDto(treeRootHolder.getRoot().getUuid(), i.key()))
+ .toList();
+ List<IssueFixedDto> issuesFixedToInsert = difference(newIssuesFixed, currentIssuesFixed);
+ List<IssueFixedDto> issuesFixedToDelete = difference(currentIssuesFixed, newIssuesFixed);
+ issuesFixedToInsert.forEach(i -> dbClient.issueFixedDao().insert(dbSession, i));
+ issuesFixedToDelete.forEach(i -> dbClient.issueFixedDao().delete(dbSession, i));
+ dbSession.commit();
+ }
+ }
+
+ private static List<IssueFixedDto> difference(List<IssueFixedDto> sourceList, List<IssueFixedDto> otherList) {
+ ArrayList<IssueFixedDto> differences = new ArrayList<>(sourceList);
+ differences.removeAll(otherList);
+ return differences;
+ }
+
+
+ @Override
+ public String getDescription() {
+ return "Persist Fixed issues in Pull Request";
+ }
+}
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/ReportComputationSteps.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/ReportComputationSteps.java
index d0783b2708f..2d11a9fa301 100644
--- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/ReportComputationSteps.java
+++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/ReportComputationSteps.java
@@ -111,6 +111,7 @@ public class ReportComputationSteps extends AbstractComputationSteps {
PersistFileSourcesStep.class,
PersistCrossProjectDuplicationIndexStep.class,
EnableAnalysisStep.class,
+ PersistPullRequestFixedIssueStep.class,
UpdateQualityProfilesLastUsedDateStep.class,
PurgeDatastoresStep.class,
diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/fixedissues/PullRequestFixedIssueRepositoryImplTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/fixedissues/PullRequestFixedIssueRepositoryImplTest.java
new file mode 100644
index 00000000000..14e4e10dc9e
--- /dev/null
+++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/issue/fixedissues/PullRequestFixedIssueRepositoryImplTest.java
@@ -0,0 +1,38 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 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.fixedissues;
+
+import org.assertj.core.api.Assertions;
+import org.junit.Test;
+import org.sonar.core.issue.DefaultIssue;
+
+public class PullRequestFixedIssueRepositoryImplTest {
+
+ @Test
+ public void getFixedIssues_shouldReturnListOfIssues() {
+ PullRequestFixedIssueRepositoryImpl fixedIssueRepository = new PullRequestFixedIssueRepositoryImpl();
+ fixedIssueRepository.addFixedIssue(new DefaultIssue().setKey("key1"));
+ fixedIssueRepository.addFixedIssue(new DefaultIssue().setKey("key2"));
+
+ Assertions.assertThat(fixedIssueRepository.getFixedIssues()).extracting(DefaultIssue::key)
+ .containsExactly("key1", "key2");
+ }
+
+}
diff --git a/server/sonar-db-dao/src/it/java/org/sonar/db/issue/IssueFixedDaoIT.java b/server/sonar-db-dao/src/it/java/org/sonar/db/issue/IssueFixedDaoIT.java
index 8a421450d95..753482552be 100644
--- a/server/sonar-db-dao/src/it/java/org/sonar/db/issue/IssueFixedDaoIT.java
+++ b/server/sonar-db-dao/src/it/java/org/sonar/db/issue/IssueFixedDaoIT.java
@@ -63,4 +63,16 @@ public class IssueFixedDaoIT {
assertThat(underTest.selectByPullRequest(db.getSession(), "PR-1")).containsOnly(fixedIssue1, fixedIssue2);
}
+ @Test
+ public void delete_shouldRemovedExpectedIssuesFixed() {
+ IssueFixedDto fixedIssue1 = new IssueFixedDto("PR-1", "ISSUE-1");
+ IssueFixedDto fixedIssue2 = new IssueFixedDto("PR-1", "ISSUE-2");
+ underTest.insert(db.getSession(), fixedIssue1);
+ underTest.insert(db.getSession(), fixedIssue2);
+
+ underTest.delete(db.getSession(), fixedIssue1);
+
+ assertThat(underTest.selectByPullRequest(db.getSession(), "PR-1")).containsOnly(fixedIssue2);
+ }
+
}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueFixedDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueFixedDao.java
index e0cd66256a7..cb6e6f92d1f 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueFixedDao.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueFixedDao.java
@@ -37,4 +37,8 @@ public class IssueFixedDao implements Dao {
public List<IssueFixedDto> selectByPullRequest(DbSession dbSession, @Param("pullRequestUuid") String pullRequestUuid) {
return mapper(dbSession).selectByPullRequest(pullRequestUuid);
}
+
+ public void delete(DbSession dbSession, IssueFixedDto issueFixed) {
+ mapper(dbSession).delete(issueFixed);
+ }
}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueFixedMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueFixedMapper.java
index 1970440b39a..a8e4d8cd166 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueFixedMapper.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/issue/IssueFixedMapper.java
@@ -23,6 +23,6 @@ import java.util.List;
public interface IssueFixedMapper {
void insert(IssueFixedDto dto);
-
List<IssueFixedDto> selectByPullRequest(String pullRequestUuid);
+ void delete(IssueFixedDto issueFixed);
}
diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueFixedMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueFixedMapper.xml
index 54602a4fb44..04088d9c427 100644
--- a/server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueFixedMapper.xml
+++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/issue/IssueFixedMapper.xml
@@ -3,7 +3,7 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "mybatis-3-mapper.dtd">
<mapper namespace="org.sonar.db.issue.IssueFixedMapper">
- <insert id="insert" parameterType="org.sonar.db.issue.IssueFixedDto">
+ <insert id="insert" parameterType="org.sonar.db.issue.IssueFixedDto" useGeneratedKeys="false">
INSERT INTO issues_fixed (
pull_request_uuid,
issue_key
@@ -19,4 +19,9 @@
FROM issues_fixed
WHERE pull_request_uuid = #{pullRequestUuid,jdbcType=VARCHAR}
</select>
+ <delete id="delete" parameterType="org.sonar.db.issue.IssueFixedDto">
+ DELETE FROM issues_fixed
+ WHERE issue_key = #{issueKey,jdbcType=VARCHAR}
+ AND pull_request_uuid = #{pullRequestUuid,jdbcType=VARCHAR}
+ </delete>
</mapper>