diff options
author | Léo Geoffroy <leo.geoffroy@sonarsource.com> | 2023-06-29 11:14:18 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2023-06-29 20:05:13 +0000 |
commit | f133851d79feb169a6e2e445aff9008d26ad3bae (patch) | |
tree | 3135fb3e6533b5cde1bc951ec9731819ef892507 /server | |
parent | d4def4920788a250a674cf2c89915e46da8467b6 (diff) | |
download | sonarqube-f133851d79feb169a6e2e445aff9008d26ad3bae.tar.gz sonarqube-f133851d79feb169a6e2e445aff9008d26ad3bae.zip |
SONAR-19558 Update PurgeDao to not rely on component uuid
Diffstat (limited to 'server')
15 files changed, 367 insertions, 322 deletions
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistProjectLinksStep.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistProjectLinksStep.java index a0f9c71c3cb..051c9ac0c8c 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistProjectLinksStep.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/PersistProjectLinksStep.java @@ -65,7 +65,6 @@ public class PersistProjectLinksStep implements ComputationStep { if (!analysisMetadataHolder.getBranch().isMain()) { return; } - try (DbSession session = dbClient.openSession(false)) { Component rootComponent = treeRootHolder.getRoot(); ScannerReport.Component batchComponent = reportReader.readComponent(rootComponent.getReportAttributes().getRef()); diff --git a/server/sonar-db-dao/src/it/java/org/sonar/db/purge/PurgeDaoIT.java b/server/sonar-db-dao/src/it/java/org/sonar/db/purge/PurgeDaoIT.java index ed9bfdc1289..5739021ecfd 100644 --- a/server/sonar-db-dao/src/it/java/org/sonar/db/purge/PurgeDaoIT.java +++ b/server/sonar-db-dao/src/it/java/org/sonar/db/purge/PurgeDaoIT.java @@ -121,7 +121,7 @@ public class PurgeDaoIT { private final System2 system2 = mock(System2.class); @Rule - public DbTester db = DbTester.create(system2); + public DbTester db = DbTester.create(system2, true); private final DbClient dbClient = db.getDbClient(); private final DbSession dbSession = db.getSession(); @@ -134,7 +134,7 @@ public class PurgeDaoIT { db.components().insertSnapshot(project.getMainBranchComponent(), t -> t.setStatus(STATUS_UNPROCESSED).setLast(false)); SnapshotDto lastAnalysis = db.components().insertSnapshot(project.getMainBranchComponent(), t -> t.setStatus(STATUS_PROCESSED).setLast(true)); - underTest.purge(dbSession, newConfigurationWith30Days(project.getMainBranchComponent().uuid()), PurgeListener.EMPTY, new PurgeProfiler()); + underTest.purge(dbSession, newConfigurationWith30Days(System2.INSTANCE, project.getMainBranchComponent().uuid(), project.projectUuid()), PurgeListener.EMPTY, new PurgeProfiler()); dbSession.commit(); assertThat(uuidsOfAnalysesOfRoot(project.getMainBranchComponent())).containsOnly(pastAnalysis.getUuid(), lastAnalysis.getUuid()); @@ -272,21 +272,22 @@ public class PurgeDaoIT { @Test public void close_issues_clean_index_and_file_sources_of_disabled_components_specified_by_uuid_in_configuration() { RuleDto rule = db.rules().insert(); - ComponentDto project = db.components().insertPublicProject().getMainBranchComponent(); - db.components().insertSnapshot(project); - db.components().insertSnapshot(project); - db.components().insertSnapshot(project, s -> s.setLast(false)); - - ComponentDto dir = db.components().insertComponent(newDirectory(project, "sub").setEnabled(false)); - ComponentDto srcFile = db.components().insertComponent(newFileDto(project, dir).setEnabled(false)); - ComponentDto testFile = db.components().insertComponent(newFileDto(project, dir).setEnabled(false)); - ComponentDto enabledFile = db.components().insertComponent(newFileDto(project, dir).setEnabled(true)); - IssueDto openOnFile = db.issues().insert(rule, project, srcFile, issue -> issue.setStatus("OPEN")); - IssueDto confirmOnFile = db.issues().insert(rule, project, srcFile, issue -> issue.setStatus("CONFIRM")); - IssueDto openOnDir = db.issues().insert(rule, project, dir, issue -> issue.setStatus("OPEN")); - IssueDto confirmOnDir = db.issues().insert(rule, project, dir, issue -> issue.setStatus("CONFIRM")); - IssueDto openOnEnabledComponent = db.issues().insert(rule, project, enabledFile, issue -> issue.setStatus("OPEN")); - IssueDto confirmOnEnabledComponent = db.issues().insert(rule, project, enabledFile, issue -> issue.setStatus("CONFIRM")); + ProjectData projectData = db.components().insertPublicProject(); + ComponentDto mainBranch = projectData.getMainBranchComponent(); + db.components().insertSnapshot(mainBranch); + db.components().insertSnapshot(mainBranch); + db.components().insertSnapshot(mainBranch, s -> s.setLast(false)); + + ComponentDto dir = db.components().insertComponent(newDirectory(mainBranch, "sub").setEnabled(false)); + ComponentDto srcFile = db.components().insertComponent(newFileDto(mainBranch, dir).setEnabled(false)); + ComponentDto testFile = db.components().insertComponent(newFileDto(mainBranch, dir).setEnabled(false)); + ComponentDto enabledFile = db.components().insertComponent(newFileDto(mainBranch, dir).setEnabled(true)); + IssueDto openOnFile = db.issues().insert(rule, mainBranch, srcFile, issue -> issue.setStatus("OPEN")); + IssueDto confirmOnFile = db.issues().insert(rule, mainBranch, srcFile, issue -> issue.setStatus("CONFIRM")); + IssueDto openOnDir = db.issues().insert(rule, mainBranch, dir, issue -> issue.setStatus("OPEN")); + IssueDto confirmOnDir = db.issues().insert(rule, mainBranch, dir, issue -> issue.setStatus("CONFIRM")); + IssueDto openOnEnabledComponent = db.issues().insert(rule, mainBranch, enabledFile, issue -> issue.setStatus("OPEN")); + IssueDto confirmOnEnabledComponent = db.issues().insert(rule, mainBranch, enabledFile, issue -> issue.setStatus("CONFIRM")); assertThat(db.countSql("select count(*) from snapshots where purge_status = 1")).isZero(); @@ -303,8 +304,8 @@ public class PurgeDaoIT { LiveMeasureDto liveMeasureMetric2OnFile = db.measures().insertLiveMeasure(srcFile, metric2); LiveMeasureDto liveMeasureMetric1OnDir = db.measures().insertLiveMeasure(dir, metric1); LiveMeasureDto liveMeasureMetric2OnDir = db.measures().insertLiveMeasure(dir, metric2); - LiveMeasureDto liveMeasureMetric1OnProject = db.measures().insertLiveMeasure(project, metric1); - LiveMeasureDto liveMeasureMetric2OnProject = db.measures().insertLiveMeasure(project, metric2); + LiveMeasureDto liveMeasureMetric1OnProject = db.measures().insertLiveMeasure(mainBranch, metric1); + LiveMeasureDto liveMeasureMetric2OnProject = db.measures().insertLiveMeasure(mainBranch, metric2); LiveMeasureDto liveMeasureMetric1OnNonSelected = db.measures().insertLiveMeasure(enabledFile, metric1); LiveMeasureDto liveMeasureMetric2OnNonSelected = db.measures().insertLiveMeasure(enabledFile, metric2); assertThat(db.countRowsOfTable("live_measures")).isEqualTo(8); @@ -312,12 +313,12 @@ public class PurgeDaoIT { // back to present Set<String> selectedComponentUuids = ImmutableSet.of(srcFile.uuid(), testFile.uuid()); - underTest.purge(dbSession, newConfigurationWith30Days(system2, project.uuid(), project.uuid(), selectedComponentUuids), + underTest.purge(dbSession, newConfigurationWith30Days(system2, mainBranch.uuid(), projectData.projectUuid(), selectedComponentUuids), purgeListener, new PurgeProfiler()); dbSession.commit(); - verify(purgeListener).onComponentsDisabling(project.uuid(), selectedComponentUuids); - verify(purgeListener).onComponentsDisabling(project.uuid(), ImmutableSet.of(dir.uuid())); + verify(purgeListener).onComponentsDisabling(mainBranch.uuid(), selectedComponentUuids); + verify(purgeListener).onComponentsDisabling(mainBranch.uuid(), ImmutableSet.of(dir.uuid())); // set purge_status=1 for non-last snapshot assertThat(db.countSql("select count(*) from snapshots where purge_status = 1")).isOne(); @@ -342,11 +343,11 @@ public class PurgeDaoIT { // deletes live measure of selected assertThat(db.countRowsOfTable("live_measures")).isEqualTo(4); List<LiveMeasureDto> liveMeasureDtos = db.getDbClient().liveMeasureDao() - .selectByComponentUuidsAndMetricUuids(dbSession, ImmutableSet.of(srcFile.uuid(), dir.uuid(), project.uuid(), enabledFile.uuid()), + .selectByComponentUuidsAndMetricUuids(dbSession, ImmutableSet.of(srcFile.uuid(), dir.uuid(), mainBranch.uuid(), enabledFile.uuid()), ImmutableSet.of(metric1.getUuid(), metric2.getUuid())); assertThat(liveMeasureDtos) .extracting(LiveMeasureDto::getComponentUuid) - .containsOnly(enabledFile.uuid(), project.uuid()); + .containsOnly(enabledFile.uuid(), mainBranch.uuid()); assertThat(liveMeasureDtos) .extracting(LiveMeasureDto::getMetricUuid) .containsOnly(metric1.getUuid(), metric2.getUuid()); @@ -739,31 +740,30 @@ public class PurgeDaoIT { @Test public void delete_row_in_ce_task_input_referring_to_a_row_in_ce_activity_when_deleting_project() { - ComponentDto project = ComponentTesting.newPrivateProjectDto(); - ComponentDto branch = ComponentTesting.newBranchComponent(project, newBranchDto(project)); - ComponentDto anotherBranch = ComponentTesting.newBranchComponent(project, newBranchDto(project)); - ComponentDto anotherProject = ComponentTesting.newPrivateProjectDto(); + ProjectData project = db.components().insertPrivateProject(); + ComponentDto branch = db.components().insertProjectBranch(project.getMainBranchComponent()); + ComponentDto anotherBranch = db.components().insertProjectBranch(project.getMainBranchComponent()); + ProjectData anotherProject = db.components().insertPrivateProject(); - insertComponents(List.of(project, anotherProject), List.of(branch, anotherBranch)); - CeActivityDto projectTask = insertCeActivity(project, project.uuid()); + CeActivityDto projectTask = insertCeActivity(project.getMainBranchComponent(), project.getProjectDto().getUuid()); insertCeTaskInput(projectTask.getUuid()); - CeActivityDto branchTask = insertCeActivity(branch, project.uuid()); + CeActivityDto branchTask = insertCeActivity(branch, project.getProjectDto().getUuid()); insertCeTaskInput(branchTask.getUuid()); - CeActivityDto anotherBranchTask = insertCeActivity(anotherBranch, project.uuid()); + CeActivityDto anotherBranchTask = insertCeActivity(anotherBranch, project.getProjectDto().getUuid()); insertCeTaskInput(anotherBranchTask.getUuid()); - CeActivityDto anotherProjectTask = insertCeActivity(anotherProject, anotherProject.uuid()); + CeActivityDto anotherProjectTask = insertCeActivity(anotherProject.getMainBranchComponent(), anotherProject.getProjectDto().getUuid()); insertCeTaskInput(anotherProjectTask.getUuid()); insertCeTaskInput("non existing task"); dbSession.commit(); - underTest.deleteProject(dbSession, branch.uuid(), branch.qualifier(), project.name(), project.getKey()); + underTest.deleteProject(dbSession, branch.uuid(), branch.qualifier(), project.getProjectDto().getName(), project.getProjectDto().getKey()); dbSession.commit(); assertThat(uuidsIn("ce_activity")).containsOnly(projectTask.getUuid(), anotherBranchTask.getUuid(), anotherProjectTask.getUuid()); assertThat(taskUuidsIn("ce_task_input")).containsOnly(projectTask.getUuid(), anotherBranchTask.getUuid(), anotherProjectTask.getUuid(), "non existing task"); - underTest.deleteProject(dbSession, project.uuid(), project.qualifier(), project.name(), project.getKey()); + underTest.deleteProject(dbSession, project.getProjectDto().getUuid(), project.getProjectDto().getQualifier(), project.getProjectDto().getName(), project.getProjectDto().getKey()); dbSession.commit(); assertThat(uuidsIn("ce_activity")).containsOnly(anotherProjectTask.getUuid()); @@ -772,31 +772,29 @@ public class PurgeDaoIT { @Test public void delete_row_in_ce_scanner_context_referring_to_a_row_in_ce_activity_when_deleting_project() { - ComponentDto project = ComponentTesting.newPrivateProjectDto(); - ComponentDto branch = ComponentTesting.newBranchComponent(project, newBranchDto(project)); - ComponentDto anotherBranch = ComponentTesting.newBranchComponent(project, newBranchDto(project)); - ComponentDto anotherProject = ComponentTesting.newPrivateProjectDto(); - - insertComponents(List.of(project, anotherProject), List.of(branch, anotherBranch)); + ProjectData project = db.components().insertPrivateProject(); + ComponentDto branch = db.components().insertProjectBranch(project.getMainBranchComponent()); + ComponentDto anotherBranch = db.components().insertProjectBranch(project.getMainBranchComponent()); + ProjectData anotherProject = db.components().insertPrivateProject(); - CeActivityDto projectTask = insertCeActivity(project, project.uuid()); + CeActivityDto projectTask = insertCeActivity(project.getMainBranchComponent(), project.getProjectDto().getUuid()); insertCeScannerContext(projectTask.getUuid()); - CeActivityDto branchTask = insertCeActivity(branch, project.uuid()); + CeActivityDto branchTask = insertCeActivity(branch, project.getProjectDto().getUuid()); insertCeScannerContext(branchTask.getUuid()); - CeActivityDto anotherBranchTask = insertCeActivity(anotherBranch, project.uuid()); + CeActivityDto anotherBranchTask = insertCeActivity(anotherBranch, project.getProjectDto().getUuid()); insertCeScannerContext(anotherBranchTask.getUuid()); - CeActivityDto anotherProjectTask = insertCeActivity(anotherProject, anotherProject.uuid()); + CeActivityDto anotherProjectTask = insertCeActivity(anotherProject.getMainBranchComponent(), anotherProject.getProjectDto().getUuid()); insertCeScannerContext(anotherProjectTask.getUuid()); insertCeScannerContext("non existing task"); dbSession.commit(); - underTest.deleteProject(dbSession, branch.uuid(), branch.qualifier(), project.name(), project.getKey()); + underTest.deleteProject(dbSession, branch.uuid(), branch.qualifier(), project.getProjectDto().getName(), project.getProjectDto().getKey()); dbSession.commit(); assertThat(uuidsIn("ce_activity")).containsOnly(projectTask.getUuid(), anotherBranchTask.getUuid(), anotherProjectTask.getUuid()); assertThat(taskUuidsIn("ce_scanner_context")).containsOnly(projectTask.getUuid(), anotherBranchTask.getUuid(), anotherProjectTask.getUuid(), "non existing task"); - underTest.deleteProject(dbSession, project.uuid(), project.qualifier(), project.name(), project.getKey()); + underTest.deleteProject(dbSession, project.getProjectDto().getUuid(), project.getProjectDto().getQualifier(), project.getProjectDto().getName(), project.getProjectDto().getKey()); dbSession.commit(); assertThat(uuidsIn("ce_activity")).containsOnly(anotherProjectTask.getUuid()); @@ -805,31 +803,30 @@ public class PurgeDaoIT { @Test public void delete_row_in_ce_task_characteristics_referring_to_a_row_in_ce_activity_when_deleting_project() { - ComponentDto project = ComponentTesting.newPrivateProjectDto(); - ComponentDto branch = ComponentTesting.newBranchComponent(project, newBranchDto(project)); - ComponentDto anotherBranch = ComponentTesting.newBranchComponent(project, newBranchDto(project)); - ComponentDto anotherProject = ComponentTesting.newPrivateProjectDto(); + ProjectData project = db.components().insertPrivateProject(); + ComponentDto branch = db.components().insertProjectBranch(project.getMainBranchComponent()); + ComponentDto anotherBranch = db.components().insertProjectBranch(project.getMainBranchComponent()); + ProjectData anotherProject = db.components().insertPrivateProject(); - insertComponents(List.of(project, anotherProject), List.of(branch, anotherBranch)); - CeActivityDto projectTask = insertCeActivity(project, project.uuid()); + CeActivityDto projectTask = insertCeActivity(project.getMainBranchComponent(), project.projectUuid()); insertCeTaskCharacteristics(projectTask.getUuid(), 3); - CeActivityDto branchTask = insertCeActivity(branch, project.uuid()); + CeActivityDto branchTask = insertCeActivity(branch, project.projectUuid()); insertCeTaskCharacteristics(branchTask.getUuid(), 2); - CeActivityDto anotherBranchTask = insertCeActivity(anotherBranch, project.uuid()); + CeActivityDto anotherBranchTask = insertCeActivity(anotherBranch, project.projectUuid()); insertCeTaskCharacteristics(anotherBranchTask.getUuid(), 6); - CeActivityDto anotherProjectTask = insertCeActivity(anotherProject, anotherProject.uuid()); + CeActivityDto anotherProjectTask = insertCeActivity(anotherProject.getMainBranchComponent(), anotherProject.projectUuid()); insertCeTaskCharacteristics(anotherProjectTask.getUuid(), 2); insertCeTaskCharacteristics("non existing task", 5); dbSession.commit(); - underTest.deleteProject(dbSession, branch.uuid(), branch.qualifier(), project.name(), project.getKey()); + underTest.deleteProject(dbSession, branch.uuid(), branch.qualifier(), project.getProjectDto().getName(), project.getProjectDto().getKey()); dbSession.commit(); assertThat(uuidsIn("ce_activity")).containsOnly(projectTask.getUuid(), anotherBranchTask.getUuid(), anotherProjectTask.getUuid()); assertThat(taskUuidsIn("ce_task_characteristics")).containsOnly(projectTask.getUuid(), anotherBranchTask.getUuid(), anotherProjectTask.getUuid(), "non existing task"); - underTest.deleteProject(dbSession, project.uuid(), project.qualifier(), project.name(), project.getKey()); + underTest.deleteProject(dbSession, project.getProjectDto().getUuid(), project.getProjectDto().getQualifier(), project.getProjectDto().getName(), project.getProjectDto().getKey()); dbSession.commit(); assertThat(uuidsIn("ce_activity")).containsOnly(anotherProjectTask.getUuid()); @@ -838,31 +835,29 @@ public class PurgeDaoIT { @Test public void delete_row_in_ce_task_message_referring_to_a_row_in_ce_activity_when_deleting_project() { - ComponentDto project = ComponentTesting.newPrivateProjectDto(); - ComponentDto branch = ComponentTesting.newBranchComponent(project, newBranchDto(project)); - ComponentDto anotherBranch = ComponentTesting.newBranchComponent(project, newBranchDto(project)); - ComponentDto anotherProject = ComponentTesting.newPrivateProjectDto(); - - insertComponents(List.of(project, anotherProject), List.of(branch, anotherBranch)); + ProjectData project = db.components().insertPrivateProject(); + ComponentDto branch = db.components().insertProjectBranch(project.getMainBranchComponent()); + ComponentDto anotherBranch = db.components().insertProjectBranch(project.getMainBranchComponent()); + ProjectData anotherProject = db.components().insertPrivateProject(); - CeActivityDto projectTask = insertCeActivity(project, project.uuid()); + CeActivityDto projectTask = insertCeActivity(project.getMainBranchComponent(), project.getProjectDto().getUuid()); insertCeTaskMessages(projectTask.getUuid(), 3); - CeActivityDto branchTask = insertCeActivity(branch, project.uuid()); + CeActivityDto branchTask = insertCeActivity(branch, project.getProjectDto().getUuid()); insertCeTaskMessages(branchTask.getUuid(), 2); - CeActivityDto anotherBranchTask = insertCeActivity(anotherBranch, project.uuid()); + CeActivityDto anotherBranchTask = insertCeActivity(anotherBranch, project.getProjectDto().getUuid()); insertCeTaskMessages(anotherBranchTask.getUuid(), 6); - CeActivityDto anotherProjectTask = insertCeActivity(anotherProject, anotherProject.uuid()); + CeActivityDto anotherProjectTask = insertCeActivity(anotherProject.getMainBranchComponent(), anotherProject.getProjectDto().getUuid()); insertCeTaskMessages(anotherProjectTask.getUuid(), 2); insertCeTaskMessages("non existing task", 5); dbSession.commit(); - underTest.deleteProject(dbSession, branch.uuid(), branch.qualifier(), project.name(), project.getKey()); + underTest.deleteProject(dbSession, branch.uuid(), branch.qualifier(), project.getProjectDto().getName(), project.getProjectDto().getKey()); dbSession.commit(); assertThat(uuidsIn("ce_activity")).containsOnly(projectTask.getUuid(), anotherBranchTask.getUuid(), anotherProjectTask.getUuid()); assertThat(taskUuidsIn("ce_task_message")).containsOnly(projectTask.getUuid(), anotherBranchTask.getUuid(), anotherProjectTask.getUuid(), "non existing task"); - underTest.deleteProject(dbSession, project.uuid(), project.qualifier(), project.name(), project.getKey()); + underTest.deleteProject(dbSession, project.getProjectDto().getUuid(), project.getProjectDto().getQualifier(), project.getProjectDto().getName(), project.getProjectDto().getKey()); dbSession.commit(); assertThat(uuidsIn("ce_activity")).containsOnly(anotherProjectTask.getUuid()); @@ -890,21 +885,23 @@ public class PurgeDaoIT { @Test public void delete_tasks_in_ce_queue_when_deleting_project() { - ComponentDto projectToBeDeleted = db.components().insertPrivateProject().getMainBranchComponent(); - ComponentDto anotherLivingProject = db.components().insertPrivateProject().getMainBranchComponent(); + ProjectData projectToBeDeleted = db.components().insertPrivateProject(); + ProjectData anotherLivingProject = db.components().insertPrivateProject(); // Insert 3 rows in CE_QUEUE: two for the project that will be deleted (in order to check that status // is not involved in deletion), and one on another project - dbClient.ceQueueDao().insert(dbSession, createCeQueue(projectToBeDeleted, projectToBeDeleted.uuid(), Status.PENDING)); - dbClient.ceQueueDao().insert(dbSession, createCeQueue(projectToBeDeleted, projectToBeDeleted.uuid(), Status.IN_PROGRESS)); - dbClient.ceQueueDao().insert(dbSession, createCeQueue(anotherLivingProject, anotherLivingProject.uuid(), Status.PENDING)); + dbClient.ceQueueDao().insert(dbSession, createCeQueue(projectToBeDeleted.getMainBranchComponent(), projectToBeDeleted.projectUuid(), Status.PENDING)); + dbClient.ceQueueDao().insert(dbSession, createCeQueue(projectToBeDeleted.getMainBranchComponent(), projectToBeDeleted.projectUuid(), Status.IN_PROGRESS)); + dbClient.ceQueueDao().insert(dbSession, createCeQueue(anotherLivingProject.getMainBranchComponent(), anotherLivingProject.projectUuid(), Status.PENDING)); dbSession.commit(); - underTest.deleteProject(dbSession, projectToBeDeleted.uuid(), projectToBeDeleted.qualifier(), projectToBeDeleted.name(), projectToBeDeleted.getKey()); + ProjectDto projectDto = projectToBeDeleted.getProjectDto(); + underTest.deleteProject(dbSession, projectDto.getUuid(), + projectDto.getQualifier(), projectDto.getName(), projectDto.getKey()); dbSession.commit(); assertThat(db.countRowsOfTable("ce_queue")).isOne(); - assertThat(db.countSql("select count(*) from ce_queue where entity_uuid='" + projectToBeDeleted.uuid() + "'")).isZero(); + assertThat(db.countSql("select count(*) from ce_queue where entity_uuid='" + projectToBeDeleted.projectUuid() + "'")).isZero(); } @Test @@ -1174,17 +1171,18 @@ public class PurgeDaoIT { @Test public void purge_shouldDeleteOrphanIssues() { RuleDto rule = db.rules().insert(); - ComponentDto project = db.components().insertPublicProject().getMainBranchComponent(); + ProjectData projectData = db.components().insertPublicProject(); + ComponentDto mainBranch = projectData.getMainBranchComponent(); ComponentDto otherProject = db.components().insertPublicProject().getMainBranchComponent(); - ComponentDto dir = db.components().insertComponent(newDirectory(project, "path")); - ComponentDto file = db.components().insertComponent(newFileDto(project, dir)); + ComponentDto dir = db.components().insertComponent(newDirectory(mainBranch, "path")); + ComponentDto file = db.components().insertComponent(newFileDto(mainBranch, dir)); - IssueDto issue1 = db.issues().insert(rule, project, file); - IssueDto orphanIssue = db.issues().insert(rule, project, file, issue -> issue.setComponentUuid("nonExisting")); + IssueDto issue1 = db.issues().insert(rule, mainBranch, file); + IssueDto orphanIssue = db.issues().insert(rule, mainBranch, file, issue -> issue.setComponentUuid("nonExisting")); IssueChangeDto orphanIssueChange = db.issues().insertChange(orphanIssue); db.issues().insertNewCodeReferenceIssue(orphanIssue); - underTest.purge(dbSession, newConfigurationWith30Days(system2, project.uuid(), project.uuid()), PurgeListener.EMPTY, new PurgeProfiler()); + underTest.purge(dbSession, newConfigurationWith30Days(system2, mainBranch.uuid(), projectData.projectUuid()), PurgeListener.EMPTY, new PurgeProfiler()); db.commit(); assertThat(db.countRowsOfTable("issue_changes")).isZero(); @@ -1195,27 +1193,28 @@ public class PurgeDaoIT { @Test public void should_delete_old_closed_issues() { RuleDto rule = db.rules().insert(); - ComponentDto project = db.components().insertPublicProject().getMainBranchComponent(); + ProjectData projectData = db.components().insertPublicProject(); + ComponentDto mainBranch = projectData.getMainBranchComponent(); - ComponentDto dir = db.components().insertComponent(newDirectory(project, "path")); - ComponentDto file = db.components().insertComponent(newFileDto(project, dir)); + ComponentDto dir = db.components().insertComponent(newDirectory(mainBranch, "path")); + ComponentDto file = db.components().insertComponent(newFileDto(mainBranch, dir)); - IssueDto oldClosed = db.issues().insert(rule, project, file, issue -> { + IssueDto oldClosed = db.issues().insert(rule, mainBranch, file, issue -> { issue.setStatus("CLOSED"); issue.setIssueCloseDate(DateUtils.addDays(new Date(), -31)); }); db.issues().insertNewCodeReferenceIssue(newCodeReferenceIssue(oldClosed)); - IssueDto notOldEnoughClosed = db.issues().insert(rule, project, file, issue -> { + IssueDto notOldEnoughClosed = db.issues().insert(rule, mainBranch, file, issue -> { issue.setStatus("CLOSED"); issue.setIssueCloseDate(new Date()); }); db.issues().insertNewCodeReferenceIssue(newCodeReferenceIssue(notOldEnoughClosed)); - IssueDto notClosed = db.issues().insert(rule, project, file); + IssueDto notClosed = db.issues().insert(rule, mainBranch, file); db.issues().insertNewCodeReferenceIssue(newCodeReferenceIssue(notClosed)); when(system2.now()).thenReturn(new Date().getTime()); - underTest.purge(dbSession, newConfigurationWith30Days(system2, project.uuid(), project.uuid()), PurgeListener.EMPTY, new PurgeProfiler()); + underTest.purge(dbSession, newConfigurationWith30Days(system2, mainBranch.uuid(), projectData.projectUuid()), PurgeListener.EMPTY, new PurgeProfiler()); dbSession.commit(); // old closed got deleted @@ -1238,19 +1237,20 @@ public class PurgeDaoIT { @Test public void delete_disabled_components_without_issues() { - ComponentDto project = db.components().insertPublicProject(p -> p.setEnabled(true)).getMainBranchComponent(); - ComponentDto enabledFileWithIssues = db.components().insertComponent(newFileDto(project).setEnabled(true)); - ComponentDto disabledFileWithIssues = db.components().insertComponent(newFileDto(project).setEnabled(false)); - ComponentDto enabledFileWithoutIssues = db.components().insertComponent(newFileDto(project).setEnabled(true)); - ComponentDto disabledFileWithoutIssues = db.components().insertComponent(newFileDto(project).setEnabled(false)); + ProjectData projectData = db.components().insertPublicProject(p -> p.setEnabled(true)); + ComponentDto mainBranch = projectData.getMainBranchComponent(); + ComponentDto enabledFileWithIssues = db.components().insertComponent(newFileDto(mainBranch).setEnabled(true)); + ComponentDto disabledFileWithIssues = db.components().insertComponent(newFileDto(mainBranch).setEnabled(false)); + ComponentDto enabledFileWithoutIssues = db.components().insertComponent(newFileDto(mainBranch).setEnabled(true)); + ComponentDto disabledFileWithoutIssues = db.components().insertComponent(newFileDto(mainBranch).setEnabled(false)); RuleDto rule = db.rules().insert(); - IssueDto closed1 = db.issues().insert(rule, project, enabledFileWithIssues, issue -> { + IssueDto closed1 = db.issues().insert(rule, mainBranch, enabledFileWithIssues, issue -> { issue.setStatus("CLOSED"); issue.setResolution(Issue.RESOLUTION_FIXED); issue.setIssueCloseDate(new Date()); }); - IssueDto closed2 = db.issues().insert(rule, project, disabledFileWithIssues, issue -> { + IssueDto closed2 = db.issues().insert(rule, mainBranch, disabledFileWithIssues, issue -> { issue.setStatus("CLOSED"); issue.setResolution(Issue.RESOLUTION_FIXED); issue.setIssueCloseDate(new Date()); @@ -1259,23 +1259,25 @@ public class PurgeDaoIT { Set<String> disabledComponentUuids = ImmutableSet.of(disabledFileWithIssues.uuid(), disabledFileWithoutIssues.uuid()); underTest.purge(dbSession, - newConfigurationWith30Days(System2.INSTANCE, project.uuid(), project.uuid(), disabledComponentUuids), + newConfigurationWith30Days(System2.INSTANCE, mainBranch.uuid(), projectData.projectUuid(), disabledComponentUuids), purgeListener, new PurgeProfiler()); - assertThat(db.getDbClient().componentDao().selectByBranchUuid(project.uuid(), dbSession)) + assertThat(db.getDbClient().componentDao().selectByBranchUuid(mainBranch.uuid(), dbSession)) .extracting("uuid") - .containsOnly(project.uuid(), enabledFileWithIssues.uuid(), disabledFileWithIssues.uuid(), + .containsOnly(mainBranch.uuid(), enabledFileWithIssues.uuid(), disabledFileWithIssues.uuid(), enabledFileWithoutIssues.uuid()); - verify(purgeListener).onComponentsDisabling(project.uuid(), disabledComponentUuids); + verify(purgeListener).onComponentsDisabling(mainBranch.uuid(), disabledComponentUuids); } @Test public void delete_ce_analysis_older_than_180_and_scanner_context_older_than_40_days_of_specified_project_when_purging_project() { LocalDateTime now = LocalDateTime.now(); - ComponentDto project1 = db.components().insertPublicProject().getMainBranchComponent(); - Consumer<CeQueueDto> belongsToProject1 = t -> t.setEntityUuid(project1.uuid()).setComponentUuid(project1.uuid()); - ComponentDto project2 = db.components().insertPublicProject().getMainBranchComponent(); - Consumer<CeQueueDto> belongsToProject2 = t -> t.setEntityUuid(project2.uuid()).setComponentUuid(project2.uuid()); + ProjectData projectData1 = db.components().insertPublicProject(); + ComponentDto mainBranch1 = projectData1.getMainBranchComponent(); + Consumer<CeQueueDto> belongsToProject1 = t -> t.setEntityUuid(projectData1.projectUuid()).setComponentUuid(mainBranch1.uuid()); + ProjectData projectData2 = db.components().insertPublicProject(); + ComponentDto project2 = projectData2.getMainBranchComponent(); + Consumer<CeQueueDto> belongsToProject2 = t -> t.setEntityUuid(projectData2.projectUuid()).setComponentUuid(project2.uuid()); insertCeActivityAndChildDataWithDate("VERY_OLD_1", now.minusDays(180).minusMonths(10), belongsToProject1); insertCeActivityAndChildDataWithDate("JUST_OLD_ENOUGH_1", now.minusDays(180).minusDays(1), belongsToProject1); @@ -1287,7 +1289,7 @@ public class PurgeDaoIT { insertCeActivityAndChildDataWithDate("RECENT_2", now.minusDays(1), belongsToProject2); when(system2.now()).thenReturn(now.toInstant(ZoneOffset.UTC).toEpochMilli()); - underTest.purge(db.getSession(), newConfigurationWith30Days(System2.INSTANCE, project1.uuid(), project1.uuid()), + underTest.purge(db.getSession(), newConfigurationWith30Days(System2.INSTANCE, mainBranch1.uuid(), projectData1.projectUuid()), PurgeListener.EMPTY, new PurgeProfiler()); assertThat(selectActivity("VERY_OLD_1")).isEmpty(); @@ -1330,22 +1332,23 @@ public class PurgeDaoIT { @Test public void delete_ce_analysis_older_than_180_and_scanner_context_older_than_40_days_of_project_and_branches_when_purging_project() { LocalDateTime now = LocalDateTime.now(); - ComponentDto project1 = db.components().insertPublicProject().getMainBranchComponent(); - ComponentDto branch1 = db.components().insertProjectBranch(project1, b -> b.setExcludeFromPurge(true)); - Consumer<CeQueueDto> belongsToProject1 = t -> t.setEntityUuid(project1.uuid()).setComponentUuid(project1.uuid()); - Consumer<CeQueueDto> belongsToBranch1 = t -> t.setEntityUuid(project1.uuid()).setComponentUuid(branch1.uuid()); - - insertCeActivityAndChildDataWithDate("VERY_OLD_1", now.minusDays(180).minusMonths(10), belongsToProject1); - insertCeActivityAndChildDataWithDate("JUST_OLD_ENOUGH_1", now.minusDays(180).minusDays(1), belongsToProject1); - insertCeActivityAndChildDataWithDate("NOT_OLD_ENOUGH_1", now.minusDays(180), belongsToProject1); - insertCeActivityAndChildDataWithDate("RECENT_1", now.minusDays(1), belongsToProject1); + ProjectData projectData = db.components().insertPublicProject(); + ComponentDto mainBranch1 = projectData.getMainBranchComponent(); + ComponentDto branch1 = db.components().insertProjectBranch(mainBranch1, b -> b.setExcludeFromPurge(true)); + Consumer<CeQueueDto> belongsToMainBranch1 = t -> t.setEntityUuid(projectData.projectUuid()).setComponentUuid(mainBranch1.uuid()); + Consumer<CeQueueDto> belongsToBranch1 = t -> t.setEntityUuid(projectData.projectUuid()).setComponentUuid(branch1.uuid()); + + insertCeActivityAndChildDataWithDate("VERY_OLD_1", now.minusDays(180).minusMonths(10), belongsToMainBranch1); + insertCeActivityAndChildDataWithDate("JUST_OLD_ENOUGH_1", now.minusDays(180).minusDays(1), belongsToMainBranch1); + insertCeActivityAndChildDataWithDate("NOT_OLD_ENOUGH_1", now.minusDays(180), belongsToMainBranch1); + insertCeActivityAndChildDataWithDate("RECENT_1", now.minusDays(1), belongsToMainBranch1); insertCeActivityAndChildDataWithDate("VERY_OLD_2", now.minusDays(180).minusMonths(10), belongsToBranch1); insertCeActivityAndChildDataWithDate("JUST_OLD_ENOUGH_2", now.minusDays(180).minusDays(1), belongsToBranch1); insertCeActivityAndChildDataWithDate("NOT_OLD_ENOUGH_2", now.minusDays(180), belongsToBranch1); insertCeActivityAndChildDataWithDate("RECENT_2", now.minusDays(1), belongsToBranch1); when(system2.now()).thenReturn(now.toInstant(ZoneOffset.UTC).toEpochMilli()); - underTest.purge(db.getSession(), newConfigurationWith30Days(System2.INSTANCE, project1.uuid(), project1.uuid()), + underTest.purge(db.getSession(), newConfigurationWith30Days(System2.INSTANCE, mainBranch1.uuid(), projectData.projectUuid()), PurgeListener.EMPTY, new PurgeProfiler()); assertThat(selectActivity("VERY_OLD_1")).isEmpty(); @@ -1388,10 +1391,11 @@ public class PurgeDaoIT { @Test public void delete_ce_analysis_of_branch_older_than_180_and_scanner_context_older_than_40_days_when_purging_branch() { LocalDateTime now = LocalDateTime.now(); - ComponentDto project1 = db.components().insertPublicProject().getMainBranchComponent(); - ComponentDto branch1 = db.components().insertProjectBranch(project1); - Consumer<CeQueueDto> belongsToProject1 = t -> t.setEntityUuid(project1.uuid()).setComponentUuid(project1.uuid()); - Consumer<CeQueueDto> belongsToBranch1 = t -> t.setEntityUuid(project1.uuid()).setComponentUuid(branch1.uuid()); + ProjectData projectData1 = db.components().insertPublicProject(); + ComponentDto mainBranch1 = projectData1.getMainBranchComponent(); + ComponentDto branch1 = db.components().insertProjectBranch(mainBranch1); + Consumer<CeQueueDto> belongsToProject1 = t -> t.setEntityUuid(mainBranch1.uuid()).setComponentUuid(mainBranch1.uuid()); + Consumer<CeQueueDto> belongsToBranch1 = t -> t.setEntityUuid(mainBranch1.uuid()).setComponentUuid(branch1.uuid()); insertCeActivityAndChildDataWithDate("VERY_OLD_1", now.minusDays(180).minusMonths(10), belongsToProject1); insertCeActivityAndChildDataWithDate("JUST_OLD_ENOUGH_1", now.minusDays(180).minusDays(1), belongsToProject1); @@ -1403,7 +1407,7 @@ public class PurgeDaoIT { insertCeActivityAndChildDataWithDate("RECENT_2", now.minusDays(1), belongsToBranch1); when(system2.now()).thenReturn(now.toInstant(ZoneOffset.UTC).toEpochMilli()); - underTest.purge(db.getSession(), newConfigurationWith30Days(System2.INSTANCE, branch1.uuid(), branch1.uuid()), + underTest.purge(db.getSession(), newConfigurationWith30Days(System2.INSTANCE, branch1.uuid(), projectData1.projectUuid()), PurgeListener.EMPTY, new PurgeProfiler()); assertThat(selectActivity("VERY_OLD_1")).isNotEmpty(); @@ -1856,21 +1860,21 @@ public class PurgeDaoIT { dbSession.commit(); } - private CeQueueDto createCeQueue(ComponentDto component, String mainBranch, Status status) { + private CeQueueDto createCeQueue(ComponentDto component, String entityUuid, Status status) { CeQueueDto queueDto = new CeQueueDto(); queueDto.setUuid(Uuids.create()); queueDto.setTaskType(REPORT); queueDto.setComponentUuid(component.uuid()); - queueDto.setEntityUuid(mainBranch); + queueDto.setEntityUuid(entityUuid); queueDto.setSubmitterUuid("submitter uuid"); queueDto.setCreatedAt(1_300_000_000_000L); queueDto.setStatus(status); return queueDto; } - private CeActivityDto insertCeActivity(ComponentDto component, String mainBranch) { + private CeActivityDto insertCeActivity(ComponentDto component, String entityUuid) { Status unusedStatus = Status.values()[RandomUtils.nextInt(Status.values().length)]; - CeQueueDto queueDto = createCeQueue(component, mainBranch, unusedStatus); + CeQueueDto queueDto = createCeQueue(component, entityUuid, unusedStatus); CeActivityDto dto = new CeActivityDto(queueDto); dto.setStatus(CeActivityDto.Status.SUCCESS); @@ -1958,10 +1962,6 @@ public class PurgeDaoIT { .map(row -> (String) row.get("UUID")); } - private static PurgeConfiguration newConfigurationWith30Days(String rootUuid) { - return new PurgeConfiguration(rootUuid, rootUuid, 30, Optional.of(30), System2.INSTANCE, emptySet()); - } - private static PurgeConfiguration newConfigurationWith30Days(System2 system2, String rootUuid, String projectUuid) { return newConfigurationWith30Days(system2, rootUuid, projectUuid, Collections.emptySet()); } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeCommands.java b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeCommands.java index 9364e72325b..964059a1164 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeCommands.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeCommands.java @@ -35,7 +35,7 @@ class PurgeCommands { private static final int MAX_SNAPSHOTS_PER_QUERY = 1000; private static final int MAX_RESOURCES_PER_QUERY = 1000; - private static final String[] UNPROCESSED_STATUS = new String[] {"U"}; + private static final String[] UNPROCESSED_STATUS = new String[]{"U"}; private final DbSession session; private final PurgeMapper purgeMapper; @@ -322,54 +322,54 @@ class PurgeCommands { void deleteCeActivity(String rootUuid) { profiler.start("deleteCeActivity (ce_scanner_context)"); - purgeMapper.deleteCeScannerContextOfCeActivityByRootUuidOrBefore(rootUuid, null); + purgeMapper.deleteCeScannerContextOfCeActivityByRootUuidOrBefore(rootUuid, null, null); session.commit(); profiler.stop(); profiler.start("deleteCeActivity (ce_task_characteristics)"); - purgeMapper.deleteCeTaskCharacteristicsOfCeActivityByRootUuidOrBefore(rootUuid, null); + purgeMapper.deleteCeTaskCharacteristicsOfCeActivityByRootUuidOrBefore(rootUuid, null, null); session.commit(); profiler.stop(); profiler.start("deleteCeActivity (ce_task_input)"); - purgeMapper.deleteCeTaskInputOfCeActivityByRootUuidOrBefore(rootUuid, null); + purgeMapper.deleteCeTaskInputOfCeActivityByRootUuidOrBefore(rootUuid, null, null); session.commit(); profiler.stop(); profiler.start("deleteCeActivity (ce_task_message)"); - purgeMapper.deleteCeTaskMessageOfCeActivityByRootUuidOrBefore(rootUuid, null); + purgeMapper.deleteCeTaskMessageOfCeActivityByRootUuidOrBefore(rootUuid, null, null); session.commit(); profiler.stop(); profiler.start("deleteCeActivity (ce_activity)"); - purgeMapper.deleteCeActivityByRootUuidOrBefore(rootUuid, null); + purgeMapper.deleteCeActivityByRootUuidOrBefore(rootUuid, null, null); session.commit(); profiler.stop(); } - void deleteCeActivityBefore(@Nullable String rootUuid, long createdAt) { + void deleteCeActivityBefore(@Nullable String rootUuid, @Nullable String entityUuidToPurge, long createdAt) { profiler.start("deleteCeActivityBefore (ce_scanner_context)"); - purgeMapper.deleteCeScannerContextOfCeActivityByRootUuidOrBefore(rootUuid, createdAt); + purgeMapper.deleteCeScannerContextOfCeActivityByRootUuidOrBefore(rootUuid, entityUuidToPurge, createdAt); session.commit(); profiler.stop(); profiler.start("deleteCeActivityBefore (ce_task_characteristics)"); - purgeMapper.deleteCeTaskCharacteristicsOfCeActivityByRootUuidOrBefore(rootUuid, createdAt); + purgeMapper.deleteCeTaskCharacteristicsOfCeActivityByRootUuidOrBefore(rootUuid, entityUuidToPurge, createdAt); session.commit(); profiler.stop(); profiler.start("deleteCeActivityBefore (ce_task_input)"); - purgeMapper.deleteCeTaskInputOfCeActivityByRootUuidOrBefore(rootUuid, createdAt); + purgeMapper.deleteCeTaskInputOfCeActivityByRootUuidOrBefore(rootUuid, entityUuidToPurge, createdAt); session.commit(); profiler.stop(); profiler.start("deleteCeActivityBefore (ce_task_message)"); - purgeMapper.deleteCeTaskMessageOfCeActivityByRootUuidOrBefore(rootUuid, createdAt); + purgeMapper.deleteCeTaskMessageOfCeActivityByRootUuidOrBefore(rootUuid, entityUuidToPurge, createdAt); session.commit(); profiler.stop(); profiler.start("deleteCeActivityBefore (ce_activity)"); - purgeMapper.deleteCeActivityByRootUuidOrBefore(rootUuid, createdAt); + purgeMapper.deleteCeActivityByRootUuidOrBefore(rootUuid, entityUuidToPurge, createdAt); session.commit(); profiler.stop(); } - void deleteCeScannerContextBefore(@Nullable String rootUuid, long createdAt) { + void deleteCeScannerContextBefore(@Nullable String rootUuid, @Nullable String entityUuidToPurge, long createdAt) { // assuming CeScannerContext of rows in table CE_QUEUE can't be older than createdAt profiler.start("deleteCeScannerContextBefore"); - purgeMapper.deleteCeScannerContextOfCeActivityByRootUuidOrBefore(rootUuid, createdAt); + purgeMapper.deleteCeScannerContextOfCeActivityByRootUuidOrBefore(rootUuid, entityUuidToPurge, createdAt); session.commit(); } @@ -503,7 +503,7 @@ class PurgeCommands { } - public void deleteReportSubscriptions(String rootUuid){ + public void deleteReportSubscriptions(String rootUuid) { profiler.start("deleteReportSubscriptions (report_subscriptions)"); purgeMapper.deleteReportSubscriptionsByBranchUuid(rootUuid); session.commit(); diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeDao.java index 7f00a5af85c..02fd941f2e7 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeDao.java @@ -20,6 +20,7 @@ package org.sonar.db.purge; import java.util.Collection; +import java.util.Comparator; import java.util.Date; import java.util.List; import java.util.Optional; @@ -68,8 +69,8 @@ public class PurgeDao implements Dao { purgeDisabledComponents(commands, conf, listener); deleteOldClosedIssues(conf, mapper, listener); deleteOrphanIssues(mapper, rootUuid); - purgeOldCeActivities(rootUuid, commands); - purgeOldCeScannerContexts(rootUuid, commands); + purgeOldCeActivities(session, rootUuid, commands); + purgeOldCeScannerContexts(session, rootUuid, commands); deleteOldDisabledComponents(commands, mapper, rootUuid); purgeStaleBranches(commands, conf, mapper, rootUuid); @@ -159,23 +160,42 @@ public class PurgeDao implements Dao { public void purgeCeActivities(DbSession session, PurgeProfiler profiler) { PurgeMapper mapper = session.getMapper(PurgeMapper.class); PurgeCommands commands = new PurgeCommands(session, mapper, profiler, system2); - purgeOldCeActivities(null, commands); + purgeOldCeActivities(session, null, commands); } - private void purgeOldCeActivities(@Nullable String rootUuid, PurgeCommands commands) { + private void purgeOldCeActivities(DbSession session, @Nullable String rootUuid, PurgeCommands commands) { + String entityUuidToPurge = getEntityUuidToPurge(session, rootUuid); Date sixMonthsAgo = DateUtils.addDays(new Date(system2.now()), -180); - commands.deleteCeActivityBefore(rootUuid, sixMonthsAgo.getTime()); + commands.deleteCeActivityBefore(rootUuid, entityUuidToPurge, sixMonthsAgo.getTime()); + } + + /** + * When the rootUuid is the main branch of a project, we also want to clean the old activities and context of other branches. + * This is probably to ensure that the cleanup happens regularly on branch that are not as active as the main branch. + */ + @Nullable + private static String getEntityUuidToPurge(DbSession session, @Nullable String rootUuid) { + if (rootUuid == null) { + return null; + } + BranchDto branch = session.getMapper(BranchMapper.class).selectByUuid(rootUuid); + String entityUuidToPurge = null; + if (branch != null && branch.isMain()) { + entityUuidToPurge = branch.getProjectUuid(); + } + return entityUuidToPurge; } public void purgeCeScannerContexts(DbSession session, PurgeProfiler profiler) { PurgeMapper mapper = session.getMapper(PurgeMapper.class); PurgeCommands commands = new PurgeCommands(session, mapper, profiler, system2); - purgeOldCeScannerContexts(null, commands); + purgeOldCeScannerContexts(session, null, commands); } - private void purgeOldCeScannerContexts(@Nullable String rootUuid, PurgeCommands commands) { + private void purgeOldCeScannerContexts(DbSession session, @Nullable String rootUuid, PurgeCommands commands) { Date fourWeeksAgo = DateUtils.addDays(new Date(system2.now()), -28); - commands.deleteCeScannerContextBefore(rootUuid, fourWeeksAgo.getTime()); + String entityUuidToPurge = getEntityUuidToPurge(session, rootUuid); + commands.deleteCeScannerContextBefore(rootUuid, entityUuidToPurge, fourWeeksAgo.getTime()); } private static final class NewCodePeriodAnalysisFilter implements Predicate<PurgeableAnalysisDto> { @@ -206,8 +226,9 @@ public class PurgeDao implements Dao { long start = System2.INSTANCE.now(); List<String> branchUuids = session.getMapper(BranchMapper.class).selectByProjectUuid(uuid).stream() + //Main branch is deleted last + .sorted(Comparator.comparing(BranchDto::isMain)) .map(BranchDto::getUuid) - .filter(branchUuid -> !uuid.equals(branchUuid)) .toList(); branchUuids.forEach(id -> deleteRootComponent(id, purgeMapper, purgeCommands)); diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java index d50cf87a01b..019033026ee 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java @@ -112,22 +112,22 @@ public interface PurgeMapper { void deleteFileSourcesByFileUuid(@Param("fileUuids") List<String> fileUuids); void deleteCeTaskCharacteristicsOfCeActivityByRootUuidOrBefore(@Nullable @Param("rootUuid") String rootUuid, - @Nullable @Param("createdAtBefore") Long createdAtBefore); + @Nullable @Param("entityUuidToPurge") String entityUuidToPurge, @Nullable @Param("createdAtBefore") Long createdAtBefore); void deleteCeTaskInputOfCeActivityByRootUuidOrBefore(@Nullable @Param("rootUuid") String rootUuid, - @Nullable @Param("createdAtBefore") Long createdAtBefore); + @Nullable @Param("entityUuidToPurge") String entityUuidToPurge, @Nullable @Param("createdAtBefore") Long createdAtBefore); void deleteCeScannerContextOfCeActivityByRootUuidOrBefore(@Nullable @Param("rootUuid") String rootUuid, - @Nullable @Param("createdAtBefore") Long createdAtBefore); + @Nullable @Param("entityUuidToPurge") String entityUuidToPurge, @Nullable @Param("createdAtBefore") Long createdAtBefore); void deleteCeTaskMessageOfCeActivityByRootUuidOrBefore(@Nullable @Param("rootUuid") String rootUuid, - @Nullable @Param("createdAtBefore") Long createdAtBefore); + @Nullable @Param("entityUuidToPurge") String entityUuidToPurge, @Nullable @Param("createdAtBefore") Long createdAtBefore); /** * Delete rows in CE_ACTIVITY of tasks of the specified component and/or created before specified date. */ void deleteCeActivityByRootUuidOrBefore(@Nullable @Param("rootUuid") String rootUuid, - @Nullable @Param("createdAtBefore") Long createdAtBefore); + @Nullable @Param("entityUuidToPurge") String entityUuidToPurge, @Nullable @Param("createdAtBefore") Long createdAtBefore); void deleteCeScannerContextOfCeQueueByRootUuid(@Param("rootUuid") String rootUuid); @@ -183,5 +183,5 @@ public interface PurgeMapper { void deleteReportSchedulesByPortfolioUuids(@Param("portfolioUuids") List<String> portfolioUuids); - void deleteReportSubscriptionsByPortfolioUuids(@Param("portfolioUuids") List<String> portfolioUuids); + void deleteReportSubscriptionsByPortfolioUuids(@Param("portfolioUuids") List<String> portfolioUuids); } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDao.java index 6466c28bcf9..bdd569bb24b 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/user/UserDao.java @@ -37,6 +37,7 @@ import org.sonar.db.Pagination; import org.sonar.db.audit.AuditPersister; import org.sonar.db.audit.model.UserNewValue; import org.sonar.db.component.ComponentDto; +import org.sonar.db.entity.EntityDto; import org.sonar.db.project.ProjectDto; import static java.util.Locale.ENGLISH; @@ -166,8 +167,8 @@ public class UserDao implements Dao { auditPersister.deactivateUser(dbSession, new UserNewValue(user.getUuid(), user.getLogin())); } - public void cleanHomepage(DbSession dbSession, ProjectDto project) { - mapper(dbSession).clearHomepages("PROJECT", project.getUuid(), system2.now()); + public void cleanHomepage(DbSession dbSession, EntityDto entityDto) { + mapper(dbSession).clearHomepages("PROJECT", entityDto.getUuid(), system2.now()); } public void cleanHomepage(DbSession dbSession, ComponentDto component) { diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml index 1020644d056..409ac4a2103 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml @@ -276,8 +276,8 @@ and not exists(select 1 from snapshots sp where sp.component_uuid=p.uuid) and not exists( select 1 from snapshots sp - inner join project_branches pb on sp.component_uuid = pb.uuid - where pb.project_uuid = p.uuid + inner join project_branches pb2 on sp.component_uuid = pb2.uuid + where pb2.project_uuid = pb.project_uuid ) </if> <if test="query.anyBranchAnalyzedAfter != null"> @@ -285,8 +285,8 @@ exists( -- branches of projects and applications select 1 from snapshots s - inner join project_branches pb on s.component_uuid = pb.uuid - where pb.project_uuid = p.uuid + inner join project_branches pb2 on s.component_uuid = pb2.uuid + where pb2.project_uuid = pb.project_uuid and s.status='P' and s.islast = ${_true} and s.created_at >= #{query.anyBranchAnalyzedAfter,jdbcType=BIGINT} @@ -306,8 +306,8 @@ exists( -- branches of projects and applications select 1 from snapshots s - inner join project_branches pb on s.component_uuid = pb.uuid - where pb.project_uuid = p.uuid + inner join project_branches pb2 on s.component_uuid = pb2.uuid + where pb2.project_uuid = pb.project_uuid and s.status='P' and s.islast = ${_true} and s.created_at < #{query.anyBranchAnalyzedBefore,jdbcType=BIGINT} @@ -326,8 +326,8 @@ and ( (select max(s.created_at) from snapshots s - inner join project_branches pb on s.component_uuid = pb.uuid - where pb.project_uuid = p.uuid + inner join project_branches pb2 on s.component_uuid = pb2.uuid + where pb2.project_uuid = pb.project_uuid and s.status='P' and s.islast = ${_true} ) < #{query.allBranchesAnalyzedBefore,jdbcType=BIGINT} diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml index c4c8bd4b96f..02bf75033f8 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml @@ -481,7 +481,9 @@ created_at < #{createdAtBefore,jdbcType=BIGINT} and ( component_uuid=#{rootUuid,jdbcType=VARCHAR} - or entity_uuid=#{rootUuid,jdbcType=VARCHAR} + <if test="entityUuidToPurge != null"> + or entity_uuid=#{entityUuidToPurge,jdbcType=VARCHAR} + </if> ) </when> <when test="createdAtBefore != null"> @@ -489,7 +491,9 @@ </when> <when test="rootUuid != null"> component_uuid=#{rootUuid,jdbcType=VARCHAR} - or entity_uuid=#{rootUuid,jdbcType=VARCHAR} + <if test="entityUuidToPurge != null"> + or entity_uuid=#{entityUuidToPurge,jdbcType=VARCHAR} + </if> </when> <!-- safety net when both variables are null to never generate a delete statement deleting the whole table --> diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/branch/ws/DeleteActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/branch/ws/DeleteActionIT.java index 14a64e315ec..24a66e06610 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/branch/ws/DeleteActionIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/branch/ws/DeleteActionIT.java @@ -126,21 +126,6 @@ public class DeleteActionIT { } @Test - public void fail_if_branch_is_main() { - ProjectDto project = db.components().insertPublicProject().getProjectDto(); - db.executeUpdateSql("UPDATE project_branches set KEE = 'main'"); - userSession.logIn().addProjectPermission(UserRole.ADMIN, project); - - // not found because the DB keys don't contain the name - assertThatThrownBy(() -> tester.newRequest() - .setParam("project", project.getKey()) - .setParam("branch", "main") - .execute()) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("Only non-main branches can be deleted"); - } - - @Test public void definition() { WebService.Action definition = tester.getDef(); assertThat(definition.key()).isEqualTo("delete"); diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/component/ComponentCleanerServiceIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/component/ComponentCleanerServiceIT.java index 8ba028ae503..29c17d9eea5 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/component/ComponentCleanerServiceIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/component/ComponentCleanerServiceIT.java @@ -29,9 +29,10 @@ import org.sonar.db.DbSession; import org.sonar.db.DbTester; import org.sonar.db.component.BranchDto; import org.sonar.db.component.ComponentDto; -import org.sonar.db.component.ComponentTesting; +import org.sonar.db.component.ProjectData; import org.sonar.db.component.SnapshotDto; import org.sonar.db.issue.IssueDto; +import org.sonar.db.portfolio.PortfolioDto; import org.sonar.db.project.ProjectDto; import org.sonar.db.rule.RuleDto; import org.sonar.db.webhook.WebhookDto; @@ -43,7 +44,6 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import static org.sonar.db.component.ComponentTesting.newFileDto; import static org.sonar.server.es.ProjectIndexer.Cause.PROJECT_DELETION; public class ComponentCleanerServiceIT { @@ -51,7 +51,7 @@ public class ComponentCleanerServiceIT { private final System2 system2 = System2.INSTANCE; @Rule - public final DbTester db = DbTester.create(system2); + public final DbTester db = DbTester.create(system2, true); private final DbClient dbClient = db.getDbClient(); private final DbSession dbSession = db.getSession(); @@ -85,13 +85,15 @@ public class ComponentCleanerServiceIT { } @Test - public void delete_component_from_db() { - ComponentDto componentDto1 = db.components().insertPublicProject().getMainBranchComponent(); - ComponentDto componentDto2 = db.components().insertPublicProject().getMainBranchComponent(); + public void delete_project_from_db() { + ProjectData projectData1 = db.components().insertPublicProject(); + ComponentDto componentDto1 = projectData1.getMainBranchComponent(); + ProjectData projectData2 = db.components().insertPublicProject(); + ComponentDto componentDto2 = projectData2.getMainBranchComponent(); mockResourceTypeAsValidProject(); - underTest.deleteComponent(dbSession, componentDto1); + underTest.delete(dbSession, projectData1.getProjectDto()); dbSession.commit(); assertNotExists(componentDto1); @@ -100,53 +102,87 @@ public class ComponentCleanerServiceIT { @Test public void fail_with_IAE_if_project_non_deletable() { - ComponentDto componentDto1 = db.components().insertPublicProject().getMainBranchComponent(); + ProjectData projectData = db.components().insertPublicProject(); mockResourceTypeAsNonDeletable(); dbSession.commit(); - - assertThatThrownBy(() -> underTest.deleteComponent(dbSession, componentDto1)) + ProjectDto project = projectData.getProjectDto(); + assertThatThrownBy(() -> underTest.delete(dbSession, project)) .withFailMessage("Only projects can be deleted") .isInstanceOf(IllegalArgumentException.class); } + @Test public void delete_application_from_db_and_index() { DbData data1 = insertProjectData(); DbData data2 = insertProjectData(); DbData data3 = insertProjectData(); - ProjectDto app1 = insertApplication(data2.project); - ProjectDto app2 = insertApplication(data3.project); + ProjectData app1 = insertApplication(data2.project); + ProjectData app2 = insertApplication(data3.project); - underTest.deleteApplication(dbSession, app1); + underTest.delete(dbSession, app1.getProjectDto()); dbSession.commit(); - assertProjectOrAppExists(app1, false); - assertProjectOrAppExists(app2, true); + assertProjectOrAppExists(app1.getProjectDto(), app1.getMainBranchDto(), false); + assertProjectOrAppExists(app2.getProjectDto(), app2.getMainBranchDto(), true); assertExists(data1); assertExists(data2); assertExists(data3); } - private ProjectDto insertApplication(ProjectDto project) { - ProjectDto app = db.components().insertPublicApplication().getProjectDto(); - db.components().addApplicationProject(app, project); + @Test + public void delete_WhenDeletingPortfolio_ShouldDeleteComponents() { + PortfolioDto portfolioDto1 = db.components().insertPrivatePortfolioDto(); + PortfolioDto portfolioDto2 = db.components().insertPrivatePortfolioDto(); + mockResourceTypeAsValidProject(); + underTest.delete(dbSession, portfolioDto1); + + assertThat(dbClient.componentDao().selectByUuid(dbSession, portfolioDto1.getUuid())).isEmpty(); + assertThat(dbClient.componentDao().selectByUuid(dbSession, portfolioDto2.getUuid())).isPresent(); + + assertThat(projectIndexers.hasBeenCalled(portfolioDto1.getUuid(), PROJECT_DELETION)).isTrue(); + + } + + private ProjectData insertApplication(ProjectDto project) { + ProjectData app = db.components().insertPublicApplication(); + db.components().addApplicationProject(app.getProjectDto(), project); return app; } @Test - public void delete_branch() { + public void deleteBranch_whenDeletingNonMainBranch_shouldDeleteComponentsAndProjects() { DbData data1 = insertProjectData(); DbData data2 = insertProjectData(); DbData data3 = insertProjectData(); - underTest.deleteBranch(dbSession, data1.branch); - dbSession.commit(); + BranchDto otherBranch = db.components().insertProjectBranch(data1.project); - assertNotExists(data1); + underTest.deleteBranch(dbSession, otherBranch); + + assertExists(data1); + assertExists(data2); + assertExists(data3); + + assertThat(dbClient.componentDao().selectByUuid(dbSession, otherBranch.getUuid())).isEmpty(); + assertThat(dbClient.branchDao().selectByUuid(dbSession, otherBranch.getUuid())).isEmpty(); + } + + @Test + public void deleteBranch_whenMainBranch_shouldThrowIllegalArgumentException() { + DbData data1 = insertProjectData(); + DbData data2 = insertProjectData(); + DbData data3 = insertProjectData(); + + assertThatThrownBy(() -> underTest.deleteBranch(dbSession, data1.mainBranch)).isInstanceOf(IllegalArgumentException.class) + .hasMessage("Only non-main branches can be deleted"); + + assertExists(data1); assertExists(data2); assertExists(data3); } + @Test public void delete_webhooks_from_projects() { ProjectDto project1 = db.components().insertPrivateProject().getProjectDto(); @@ -170,29 +206,15 @@ public class ComponentCleanerServiceIT { assertThat(db.countRowsOfTable(db.getSession(), "webhook_deliveries")).isOne(); } - @Test - public void fail_with_IAE_if_not_a_project() { - mockResourceTypeAsValidProject(); - ComponentDto project = ComponentTesting.newPrivateProjectDto(); - db.components().insertComponent(project); - ComponentDto file = newFileDto(project); - dbClient.componentDao().insertOnMainBranch(dbSession, file); - dbSession.commit(); - - assertThatThrownBy(() -> underTest.deleteComponent(dbSession, file)) - .isInstanceOf(IllegalArgumentException.class); - } - private DbData insertProjectData() { - ComponentDto componentDto = db.components().insertPublicProject().getMainBranchComponent(); - ProjectDto project = dbClient.projectDao().selectByUuid(dbSession, componentDto.uuid()).get(); - BranchDto branch = dbClient.branchDao().selectByUuid(dbSession, project.getUuid()).get(); + ProjectData projectData = db.components().insertPublicProject(); + ComponentDto mainBranch = projectData.getMainBranchComponent(); RuleDto rule = db.rules().insert(); - IssueDto issue = db.issues().insert(rule, project, componentDto); - SnapshotDto analysis = db.components().insertSnapshot(componentDto); + IssueDto issue = db.issues().insert(rule, mainBranch, mainBranch); + SnapshotDto analysis = db.components().insertSnapshot(mainBranch); mockResourceTypeAsValidProject(); - return new DbData(project, branch, analysis, issue); + return new DbData(projectData.getProjectDto(), projectData.getMainBranchDto(), analysis, issue); } private void mockResourceTypeAsValidProject() { @@ -209,24 +231,24 @@ public class ComponentCleanerServiceIT { private void assertNotExists(DbData data) { assertDataInDb(data, false); - assertThat(projectIndexers.hasBeenCalled(data.branch.getUuid(), PROJECT_DELETION)).isTrue(); + assertThat(projectIndexers.hasBeenCalled(data.project.getUuid(), PROJECT_DELETION)).isTrue(); } private void assertExists(DbData data) { assertDataInDb(data, true); - assertThat(projectIndexers.hasBeenCalled(data.branch.getUuid(), PROJECT_DELETION)).isFalse(); + assertThat(projectIndexers.hasBeenCalled(data.project.getUuid(), PROJECT_DELETION)).isFalse(); } private void assertDataInDb(DbData data, boolean exists) { - assertProjectOrAppExists(data.project, exists); + assertProjectOrAppExists(data.project, data.mainBranch, exists); assertThat(dbClient.snapshotDao().selectByUuid(dbSession, data.snapshot.getUuid()).isPresent()).isEqualTo(exists); assertThat(dbClient.issueDao().selectByKey(dbSession, data.issue.getKey()).isPresent()).isEqualTo(exists); } - private void assertProjectOrAppExists(ProjectDto appOrProject, boolean exists) { + private void assertProjectOrAppExists(ProjectDto appOrProject, BranchDto branch, boolean exists) { assertThat(dbClient.projectDao().selectByUuid(dbSession, appOrProject.getUuid()).isPresent()).isEqualTo(exists); - assertThat(dbClient.componentDao().selectByUuid(dbSession, appOrProject.getUuid()).isPresent()).isEqualTo(exists); - assertThat(dbClient.branchDao().selectByUuid(dbSession, appOrProject.getUuid()).isPresent()).isEqualTo(exists); + assertThat(dbClient.componentDao().selectByUuid(dbSession, branch.getUuid()).isPresent()).isEqualTo(exists); + assertThat(dbClient.branchDao().selectByUuid(dbSession, branch.getUuid()).isPresent()).isEqualTo(exists); } private void assertNotExists(ComponentDto componentDto) { @@ -240,16 +262,16 @@ public class ComponentCleanerServiceIT { private void assertComponentExists(ComponentDto componentDto, boolean exists) { assertThat(dbClient.componentDao().selectByUuid(dbSession, componentDto.uuid()).isPresent()).isEqualTo(exists); } - + private static class DbData { final ProjectDto project; - final BranchDto branch; + final BranchDto mainBranch; final SnapshotDto snapshot; final IssueDto issue; - DbData(ProjectDto project, BranchDto branch, SnapshotDto snapshot, IssueDto issue) { + DbData(ProjectDto project, BranchDto mainBranch, SnapshotDto snapshot, IssueDto issue) { this.project = project; - this.branch = branch; + this.mainBranch = mainBranch; this.snapshot = snapshot; this.issue = issue; } diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/project/ws/BulkDeleteActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/project/ws/BulkDeleteActionIT.java index eca57db706a..39b93707262 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/project/ws/BulkDeleteActionIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/project/ws/BulkDeleteActionIT.java @@ -41,6 +41,9 @@ import org.sonar.db.DbClient; 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.entity.EntityDto; +import org.sonar.db.portfolio.PortfolioDto; import org.sonar.db.project.ProjectDto; import org.sonar.server.component.ComponentCleanerService; import org.sonar.server.exceptions.UnauthorizedException; @@ -70,7 +73,8 @@ import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_QUALIFI public class BulkDeleteActionIT { @Rule - public final DbTester db = DbTester.create(System2.INSTANCE); + public final DbTester db = DbTester.create(System2.INSTANCE, true); + @Rule public final UserSessionRule userSession = UserSessionRule.standalone().logIn(); @@ -84,8 +88,8 @@ public class BulkDeleteActionIT { @Test public void delete_projects() { userSession.addPermission(ADMINISTER); - ComponentDto project1ToDelete = db.components().insertPrivateProject().getMainBranchComponent(); - ComponentDto project2ToDelete = db.components().insertPrivateProject().getMainBranchComponent(); + ProjectDto project1ToDelete = db.components().insertPrivateProject().getProjectDto(); + ProjectDto project2ToDelete = db.components().insertPrivateProject().getProjectDto(); ComponentDto toKeep = db.components().insertPrivateProject().getMainBranchComponent(); TestResponse result = ws.newRequest() @@ -94,29 +98,29 @@ public class BulkDeleteActionIT { assertThat(result.getStatus()).isEqualTo(HttpURLConnection.HTTP_NO_CONTENT); assertThat(result.getInput()).isEmpty(); - verifyComponentDeleted(project1ToDelete, project2ToDelete); + verifyEntityDeleted(project1ToDelete, project2ToDelete); verifyListenersOnProjectsDeleted(project1ToDelete, project2ToDelete); } @Test public void delete_projects_by_keys() { userSession.addPermission(ADMINISTER); - ComponentDto toDeleteInOrg1 = db.components().insertPrivateProject().getMainBranchComponent(); - ComponentDto toDeleteInOrg2 = db.components().insertPrivateProject().getMainBranchComponent(); + ProjectDto toDeleteInOrg1 = db.components().insertPrivateProject().getProjectDto(); + ProjectDto toDeleteInOrg2 = db.components().insertPrivateProject().getProjectDto(); ComponentDto toKeep = db.components().insertPrivateProject().getMainBranchComponent(); ws.newRequest() .setParam(PARAM_PROJECTS, toDeleteInOrg1.getKey() + "," + toDeleteInOrg2.getKey()) .execute(); - verifyComponentDeleted(toDeleteInOrg1, toDeleteInOrg2); + verifyEntityDeleted(toDeleteInOrg1, toDeleteInOrg2); verifyListenersOnProjectsDeleted(toDeleteInOrg1, toDeleteInOrg2); } @Test public void throw_IllegalArgumentException_if_request_without_any_parameters() { userSession.addPermission(ADMINISTER); - ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent(); + ProjectDto project = db.components().insertPrivateProject().getProjectDto(); try { TestRequest request = ws.newRequest(); @@ -132,14 +136,14 @@ public class BulkDeleteActionIT { @Test public void projects_that_dont_exist_are_ignored_and_dont_break_bulk_deletion() { userSession.addPermission(ADMINISTER); - ComponentDto toDelete1 = db.components().insertPrivateProject().getMainBranchComponent(); - ComponentDto toDelete2 = db.components().insertPrivateProject().getMainBranchComponent(); + ProjectDto toDelete1 = db.components().insertPrivateProject().getProjectDto(); + ProjectDto toDelete2 = db.components().insertPrivateProject().getProjectDto(); ws.newRequest() .setParam("projects", toDelete1.getKey() + ",missing," + toDelete2.getKey() + ",doesNotExist") .execute(); - verifyComponentDeleted(toDelete1, toDelete2); + verifyEntityDeleted(toDelete1, toDelete2); verifyListenersOnProjectsDeleted(toDelete1, toDelete2); } @@ -148,11 +152,13 @@ public class BulkDeleteActionIT { userSession.logIn().addPermission(ADMINISTER); long aLongTimeAgo = 1_000_000_000L; long recentTime = 3_000_000_000L; - ComponentDto oldProject = db.components().insertPublicProject().getMainBranchComponent(); - db.getDbClient().snapshotDao().insert(db.getSession(), newAnalysis(oldProject).setCreatedAt(aLongTimeAgo)); - ComponentDto recentProject = db.components().insertPublicProject().getMainBranchComponent(); - db.getDbClient().snapshotDao().insert(db.getSession(), newAnalysis(recentProject).setCreatedAt(aLongTimeAgo)); - ComponentDto branch = db.components().insertProjectBranch(recentProject); + ProjectData oldProjectData = db.components().insertPublicProject(); + ProjectDto oldProject = oldProjectData.getProjectDto(); + db.getDbClient().snapshotDao().insert(db.getSession(), newAnalysis(oldProjectData.getMainBranchComponent()).setCreatedAt(aLongTimeAgo)); + ProjectData recentProjectData = db.components().insertPublicProject(); + ProjectDto recentProject = recentProjectData.getProjectDto(); + db.getDbClient().snapshotDao().insert(db.getSession(), newAnalysis(recentProjectData.getMainBranchComponent()).setCreatedAt(aLongTimeAgo)); + ComponentDto branch = db.components().insertProjectBranch(recentProjectData.getMainBranchComponent()); db.getDbClient().snapshotDao().insert(db.getSession(), newAnalysis(branch).setCreatedAt(recentTime)); db.commit(); @@ -160,60 +166,62 @@ public class BulkDeleteActionIT { .setParam(PARAM_ANALYZED_BEFORE, formatDate(new Date(recentTime))) .execute(); - verifyComponentDeleted(oldProject); + verifyEntityDeleted(oldProject); verifyListenersOnProjectsDeleted(oldProject); } @Test public void provisioned_projects() { userSession.logIn().addPermission(ADMINISTER); - ComponentDto provisionedProject = db.components().insertPrivateProject().getMainBranchComponent(); - ComponentDto analyzedProject = db.components().insertPrivateProject().getMainBranchComponent(); - db.components().insertSnapshot(newAnalysis(analyzedProject)); + ProjectDto provisionedProject = db.components().insertPrivateProject().getProjectDto(); + ProjectData analyzedProjectData = db.components().insertPrivateProject(); + ProjectDto analyzedProject = analyzedProjectData.getProjectDto(); + db.components().insertSnapshot(newAnalysis(analyzedProjectData.getMainBranchComponent())); ws.newRequest().setParam(PARAM_PROJECTS, provisionedProject.getKey() + "," + analyzedProject.getKey()).setParam(PARAM_ON_PROVISIONED_ONLY, "true").execute(); - verifyComponentDeleted(provisionedProject); + verifyEntityDeleted(provisionedProject); verifyListenersOnProjectsDeleted(provisionedProject); } @Test public void delete_more_than_50_projects() { userSession.logIn().addPermission(ADMINISTER); - ComponentDto[] projects = IntStream.range(0, 55).mapToObj(i -> db.components().insertPrivateProject().getMainBranchComponent()).toArray(ComponentDto[]::new); + ProjectDto[] projects = IntStream.range(0, 55).mapToObj(i -> db.components().insertPrivateProject().getProjectDto()).toArray(ProjectDto[]::new); - List<String> projectKeys = Stream.of(projects).map(ComponentDto::getKey).collect(Collectors.toList()); + List<String> projectKeys = Stream.of(projects).map(ProjectDto::getKey).collect(Collectors.toList()); ws.newRequest().setParam(PARAM_PROJECTS, String.join(",", projectKeys)).execute(); - verifyComponentDeleted(projects); + verifyEntityDeleted(projects); verifyListenersOnProjectsDeleted(projects); } @Test public void projects_and_views() { userSession.logIn().addPermission(ADMINISTER); - ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent(); + ProjectDto project = db.components().insertPrivateProject().getProjectDto(); ComponentDto view = db.components().insertPrivatePortfolio(); + PortfolioDto portfolioDto = db.components().getPortfolioDto(view); ws.newRequest() .setParam(PARAM_PROJECTS, project.getKey() + "," + view.getKey()) .setParam(PARAM_QUALIFIERS, String.join(",", Qualifiers.PROJECT, Qualifiers.VIEW)) .execute(); - verifyComponentDeleted(project, view); - verifyListenersOnProjectsDeleted(project, view); + verifyEntityDeleted(project, portfolioDto); + verifyListenersOnProjectsDeleted(project, portfolioDto); } @Test public void delete_by_key_query_with_partial_match_case_insensitive() { userSession.logIn().addPermission(ADMINISTER); - ComponentDto matchKeyProject = db.components().insertPrivateProject(p -> p.setKey("project-_%-key")).getMainBranchComponent(); - ComponentDto matchUppercaseKeyProject = db.components().insertPrivateProject(p -> p.setKey("PROJECT-_%-KEY")).getMainBranchComponent(); - ComponentDto noMatchProject = db.components().insertPrivateProject(p -> p.setKey("project-key-without-escaped-characters")).getMainBranchComponent(); + ProjectDto matchKeyProject = db.components().insertPrivateProject(p -> p.setKey("project-_%-key")).getProjectDto(); + ProjectDto matchUppercaseKeyProject = db.components().insertPrivateProject(p -> p.setKey("PROJECT-_%-KEY")).getProjectDto(); + ProjectDto noMatchProject = db.components().insertPrivateProject(p -> p.setKey("project-key-without-escaped-characters")).getProjectDto(); ws.newRequest().setParam(Param.TEXT_QUERY, "JeCt-_%-k").execute(); - verifyComponentDeleted(matchKeyProject, matchUppercaseKeyProject); + verifyEntityDeleted(matchKeyProject, matchUppercaseKeyProject); verifyListenersOnProjectsDeleted(matchKeyProject, matchUppercaseKeyProject); } @@ -230,7 +238,7 @@ public class BulkDeleteActionIT { .setParam("projects", StringUtils.join(keys, ",")) .execute(); - verify(componentCleanerService, times(1_000)).deleteComponent(any(DbSession.class), any(ComponentDto.class)); + verify(componentCleanerService, times(1_000)).delete(any(DbSession.class), any(EntityDto.class)); ArgumentCaptor<Set<Project>> projectsCaptor = ArgumentCaptor.forClass(Set.class); verify(projectLifeCycleListeners).onProjectsDeleted(projectsCaptor.capture()); assertThat(projectsCaptor.getValue()).hasSize(1_000); @@ -239,9 +247,9 @@ public class BulkDeleteActionIT { @Test public void projectLifeCycleListeners_onProjectsDeleted_called_even_if_delete_fails() { userSession.logIn().addPermission(ADMINISTER); - ComponentDto project1 = db.components().insertPrivateProject().getMainBranchComponent(); - ComponentDto project2 = db.components().insertPrivateProject().getMainBranchComponent(); - ComponentDto project3 = db.components().insertPrivateProject().getMainBranchComponent(); + ProjectDto project1 = db.components().insertPrivateProject().getProjectDto(); + ProjectDto project2 = db.components().insertPrivateProject().getProjectDto(); + ProjectDto project3 = db.components().insertPrivateProject().getProjectDto(); ComponentCleanerService componentCleanerService = mock(ComponentCleanerService.class); RuntimeException expectedException = new RuntimeException("Faking delete failing on 2nd project"); doNothing() @@ -262,14 +270,14 @@ public class BulkDeleteActionIT { @Test public void global_administrator_deletes_projects_by_keys() { userSession.logIn().addPermission(ADMINISTER); - ComponentDto toDelete1 = db.components().insertPrivateProject().getMainBranchComponent(); - ComponentDto toDelete2 = db.components().insertPrivateProject().getMainBranchComponent(); + ProjectDto toDelete1 = db.components().insertPrivateProject().getProjectDto(); + ProjectDto toDelete2 = db.components().insertPrivateProject().getProjectDto(); ws.newRequest() .setParam("projects", toDelete1.getKey() + "," + toDelete2.getKey()) .execute(); - verifyComponentDeleted(toDelete1, toDelete2); + verifyEntityDeleted(toDelete1, toDelete2); verifyListenersOnProjectsDeleted(toDelete1, toDelete2); } @@ -304,12 +312,12 @@ public class BulkDeleteActionIT { verifyNoMoreInteractions(projectLifeCycleListeners); } - private void verifyComponentDeleted(ComponentDto... projects) { - ArgumentCaptor<ComponentDto> argument = ArgumentCaptor.forClass(ComponentDto.class); - verify(componentCleanerService, times(projects.length)).deleteComponent(any(DbSession.class), argument.capture()); + private void verifyEntityDeleted(EntityDto... entities) { + ArgumentCaptor<EntityDto> argument = ArgumentCaptor.forClass(EntityDto.class); + verify(componentCleanerService, times(entities.length)).delete(any(DbSession.class), argument.capture()); - for (ComponentDto project : projects) { - assertThat(argument.getAllValues()).extracting(ComponentDto::uuid).contains(project.uuid()); + for (EntityDto entity : entities) { + assertThat(argument.getAllValues()).extracting(EntityDto::getUuid).contains(entity.getUuid()); } } @@ -317,8 +325,8 @@ public class BulkDeleteActionIT { verifyNoMoreInteractions(componentCleanerService); } - private void verifyListenersOnProjectsDeleted(ComponentDto... components) { + private void verifyListenersOnProjectsDeleted(EntityDto... entityDtos) { verify(projectLifeCycleListeners) - .onProjectsDeleted(Arrays.stream(components).map(Project::from).collect(Collectors.toSet())); + .onProjectsDeleted(Arrays.stream(entityDtos).map(Project::from).collect(Collectors.toSet())); } } diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/project/ws/DeleteActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/project/ws/DeleteActionIT.java index 9763ef867eb..ee78e7c66c7 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/project/ws/DeleteActionIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/project/ws/DeleteActionIT.java @@ -20,9 +20,11 @@ package org.sonar.server.project.ws; import java.util.List; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.mockito.ArgumentCaptor; +import org.sonar.api.resources.ResourceType; import org.sonar.api.resources.ResourceTypes; import org.sonar.api.utils.System2; import org.sonar.api.web.UserRole; @@ -31,6 +33,7 @@ import org.sonar.db.DbSession; import org.sonar.db.DbTester; import org.sonar.db.component.ComponentDbTester; import org.sonar.db.component.ComponentDto; +import org.sonar.db.component.ProjectData; import org.sonar.db.permission.GlobalPermission; import org.sonar.db.project.ProjectDto; import org.sonar.db.user.UserDto; @@ -51,8 +54,10 @@ import static java.util.Collections.singleton; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import static org.sonar.db.user.UserTesting.newUserDto; import static org.sonar.server.component.TestComponentFinder.from; import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_PROJECT; @@ -61,7 +66,7 @@ public class DeleteActionIT { private final System2 system2 = System2.INSTANCE; @Rule - public final DbTester db = DbTester.create(system2); + public final DbTester db = DbTester.create(system2, true); @Rule public final UserSessionRule userSessionRule = UserSessionRule.standalone(); @@ -79,35 +84,49 @@ public class DeleteActionIT { userSessionRule, projectLifeCycleListeners); private final WsActionTester tester = new WsActionTester(underTest); + @Before + public void before() { + mockResourceTypeAsValidProject(); + } + + private void mockResourceTypeAsValidProject() { + ResourceType resourceType = mock(ResourceType.class); + when(resourceType.getBooleanProperty(anyString())).thenReturn(true); + when(mockResourceTypes.get(anyString())).thenReturn(resourceType); + } + @Test public void global_administrator_deletes_project_by_key() { - ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent(); + ProjectData projectData = db.components().insertPrivateProject(); + ComponentDto project = projectData.getMainBranchComponent(); userSessionRule.logIn().addPermission(GlobalPermission.ADMINISTER); call(tester.newRequest().setParam(PARAM_PROJECT, project.getKey())); assertThat(verifyDeletedKey()).isEqualTo(project.getKey()); - verify(projectLifeCycleListeners).onProjectsDeleted(singleton(Project.from(project))); + verify(projectLifeCycleListeners).onProjectsDeleted(singleton(Project.from(projectData.getProjectDto()))); } @Test public void project_administrator_deletes_the_project_by_key() { - ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent(); - userSessionRule.logIn().addProjectPermission(UserRole.ADMIN, project); + ProjectData projectData = db.components().insertPrivateProject(); + ComponentDto project = projectData.getMainBranchComponent(); + userSessionRule.logIn().addProjectPermission(UserRole.ADMIN, projectData.getProjectDto()); call(tester.newRequest().setParam(PARAM_PROJECT, project.getKey())); assertThat(verifyDeletedKey()).isEqualTo(project.getKey()); - verify(projectLifeCycleListeners).onProjectsDeleted(singleton(Project.from(project))); + verify(projectLifeCycleListeners).onProjectsDeleted(singleton(Project.from(projectData.getProjectDto()))); } @Test public void project_deletion_also_ensure_that_homepage_on_this_project_if_it_exists_is_cleared() { - ComponentDto project = db.components().insertPrivateProject().getMainBranchComponent(); + ProjectData projectData = db.components().insertPrivateProject(); + ComponentDto project = projectData.getMainBranchComponent(); UserDto insert = dbClient.userDao().insert(dbSession, - newUserDto().setHomepageType("PROJECT").setHomepageParameter(project.uuid())); + newUserDto().setHomepageType("PROJECT").setHomepageParameter(projectData.projectUuid())); dbSession.commit(); - userSessionRule.logIn().addProjectPermission(UserRole.ADMIN, project); + userSessionRule.logIn().addProjectPermission(UserRole.ADMIN, projectData.getProjectDto()); DeleteAction underTest = new DeleteAction( new ComponentCleanerService(dbClient, mockResourceTypes, new TestProjectIndexers()), from(db), dbClient, userSessionRule, projectLifeCycleListeners); diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/branch/ws/DeleteAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/branch/ws/DeleteAction.java index b55c94a2ca7..b636143d1d7 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/branch/ws/DeleteAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/branch/ws/DeleteAction.java @@ -85,9 +85,6 @@ public class DeleteAction implements BranchWsAction { dbClient.branchDao().selectByBranchKey(dbSession, project.getUuid(), branchKey), "Branch '%s' not found for project '%s'", branchKey, projectKey); - if (branch.isMain()) { - throw new IllegalArgumentException("Only non-main branches can be deleted"); - } componentCleanerService.deleteBranch(dbSession, branch); projectLifeCycleListeners.onProjectBranchesDeleted(singleton(Project.fromProjectDtoWithTags(project))); response.noContent(); diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ComponentCleanerService.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ComponentCleanerService.java index ccbfb44bf1a..9325afef071 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ComponentCleanerService.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ComponentCleanerService.java @@ -20,14 +20,14 @@ package org.sonar.server.component; import java.util.List; +import org.sonar.api.resources.Qualifiers; import org.sonar.api.resources.ResourceType; import org.sonar.api.resources.ResourceTypes; -import org.sonar.api.resources.Scopes; import org.sonar.api.server.ServerSide; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.component.BranchDto; -import org.sonar.db.component.ComponentDto; +import org.sonar.db.entity.EntityDto; import org.sonar.db.project.ProjectDto; import org.sonar.server.es.ProjectIndexers; @@ -55,37 +55,26 @@ public class ComponentCleanerService { } public void deleteBranch(DbSession dbSession, BranchDto branch) { + if (branch.isMain()) { + throw new IllegalArgumentException("Only non-main branches can be deleted"); + } dbClient.purgeDao().deleteBranch(dbSession, branch.getUuid()); projectIndexers.commitAndIndexBranches(dbSession, singletonList(branch), PROJECT_DELETION); } - public void delete(DbSession dbSession, ProjectDto project) { - dbClient.purgeDao().deleteProject(dbSession, project.getUuid(), project.getQualifier(), project.getName(), project.getKey()); - dbClient.userDao().cleanHomepage(dbSession, project); - dbClient.userTokenDao().deleteByProjectUuid(dbSession, project.getKey(), project.getUuid()); - projectIndexers.commitAndIndexProjects(dbSession, singletonList(project), PROJECT_DELETION); - } - - public void deleteApplication(DbSession dbSession, ProjectDto application) { - dbClient.purgeDao().deleteProject(dbSession, application.getUuid(), application.getQualifier(), application.getName(), application.getKey()); - dbClient.userDao().cleanHomepage(dbSession, application); - projectIndexers.commitAndIndexProjects(dbSession, singletonList(application), PROJECT_DELETION); - } + public void delete(DbSession dbSession, EntityDto entity) { + checkArgument(isDeletable(entity), "Only projects can be deleted"); - public void deleteComponent(DbSession dbSession, ComponentDto component) { - checkArgument(hasProjectScope(component) && isDeletable(component), "Only projects can be deleted"); - dbClient.purgeDao().deleteProject(dbSession, component.uuid(), component.qualifier(), component.name(), component.getKey()); - dbClient.userDao().cleanHomepage(dbSession, component); - dbClient.userTokenDao().deleteByProjectUuid(dbSession, component.getKey(), component.uuid()); - projectIndexers.commitAndIndexComponents(dbSession, singletonList(component), PROJECT_DELETION); - } - - private static boolean hasProjectScope(ComponentDto project) { - return Scopes.PROJECT.equals(project.scope()); + dbClient.purgeDao().deleteProject(dbSession, entity.getUuid(), entity.getQualifier(), entity.getName(), entity.getKey()); + dbClient.userDao().cleanHomepage(dbSession, entity); + if (Qualifiers.PROJECT.equals(entity.getQualifier())) { + dbClient.userTokenDao().deleteByProjectUuid(dbSession, entity.getKey(), entity.getUuid()); + } + projectIndexers.commitAndIndexEntities(dbSession, singletonList(entity), PROJECT_DELETION); } - private boolean isDeletable(ComponentDto project) { - ResourceType resourceType = resourceTypes.get(project.qualifier()); + private boolean isDeletable(EntityDto entityDto) { + ResourceType resourceType = resourceTypes.get(entityDto.getQualifier()); // this essentially means PROJECTS, VIEWS and APPS (not SUBVIEWS) return resourceType != null && resourceType.getBooleanProperty("deletable"); } diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/project/ws/BulkDeleteAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/project/ws/BulkDeleteAction.java index d19c97b8dd8..3f8ed5b98fa 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/project/ws/BulkDeleteAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/project/ws/BulkDeleteAction.java @@ -151,7 +151,7 @@ public class BulkDeleteAction implements ProjectsWsAction { List<EntityDto> entities = dbClient.entityDao().selectByKeys(dbSession, componentDtos.stream().map(ComponentDto::getKey).collect(toSet())); try { - componentDtos.forEach(p -> componentCleanerService.deleteComponent(dbSession, p)); + entities.forEach(e -> componentCleanerService.delete(dbSession, e)); } finally { projectLifeCycleListeners.onProjectsDeleted(entities.stream().map(Project::from).collect(MoreCollectors.toSet(componentDtos.size()))); } |