]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-19558 - Refactor rest of tests in developer server
authorBenjamin Campomenosi <109955405+benjamin-campomenosi-sonarsource@users.noreply.github.com>
Thu, 6 Jul 2023 09:16:27 +0000 (11:16 +0200)
committersonartech <sonartech@sonarsource.com>
Thu, 6 Jul 2023 20:03:12 +0000 (20:03 +0000)
server/sonar-webserver-api/src/main/java/org/sonar/server/project/DeletedProject.java [new file with mode: 0644]
server/sonar-webserver-api/src/main/java/org/sonar/server/project/ProjectLifeCycleListener.java
server/sonar-webserver-api/src/main/java/org/sonar/server/project/ProjectLifeCycleListeners.java
server/sonar-webserver-api/src/main/java/org/sonar/server/project/ProjectLifeCycleListenersImpl.java
server/sonar-webserver-api/src/test/java/org/sonar/server/project/ProjectLifeCycleListenersImplTest.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/project/ws/BulkDeleteActionIT.java
server/sonar-webserver-webapi/src/it/java/org/sonar/server/project/ws/DeleteActionIT.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/project/ws/BulkDeleteAction.java
server/sonar-webserver-webapi/src/main/java/org/sonar/server/project/ws/DeleteAction.java

diff --git a/server/sonar-webserver-api/src/main/java/org/sonar/server/project/DeletedProject.java b/server/sonar-webserver-api/src/main/java/org/sonar/server/project/DeletedProject.java
new file mode 100644 (file)
index 0000000..965ec4f
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * 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.server.project;
+
+import javax.annotation.Nullable;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * This record is used to refresh application/portfolio after the deletion of a project / portfolio
+ * @param project could refer to a project or a portfolio deleted
+ * @param mainBranchUuid refer to the main branch of the project deleted, is null when this record is used for a portfolio
+ */
+public record DeletedProject(Project project, @Nullable String mainBranchUuid) {
+  public DeletedProject(Project project, String mainBranchUuid) {
+    this.project = checkNotNull(project, "project can't be null");
+    this.mainBranchUuid = mainBranchUuid;
+  }
+}
index d30709aa1e6619eb324cbcc73799b091fc0bcbcf..f16ea78b849919b709c9dfde454b1108e49e99cb 100644 (file)
@@ -27,7 +27,7 @@ public interface ProjectLifeCycleListener {
   /**
    * This method is called after the specified projects have been deleted.
    */
-  void onProjectsDeleted(Set<Project> projects);
+  void onProjectsDeleted(Set<DeletedProject> projects);
 
   /**
    * This method is called after the specified projects have been deleted.
index 4613e71b1b1dd15d6890ba01640404d9b339fb5b..9b0ba01ccc2a88abbcf7cd04c1bdc2b6ba5e9b65 100644 (file)
@@ -30,7 +30,7 @@ public interface ProjectLifeCycleListeners {
    * This method ensures all {@link ProjectLifeCycleListener} implementations are called, even if one or more of
    * them fail with an exception.
    */
-  void onProjectsDeleted(Set<Project> projects);
+  void onProjectsDeleted(Set<DeletedProject> projects);
 
   /**
    * This method is called after the specified project branches have been deleted and will call method
index 78a4b644cc81fd816a0ecebf36b5783d04ed8cee..c80d6110ce5fa97b09906a31d02d583e473476cf 100644 (file)
@@ -50,7 +50,7 @@ public class ProjectLifeCycleListenersImpl implements ProjectLifeCycleListeners
   }
 
   @Override
-  public void onProjectsDeleted(Set<Project> projects) {
+  public void onProjectsDeleted(Set<DeletedProject> projects) {
     checkNotNull(projects, "projects can't be null");
     if (projects.isEmpty()) {
       return;
index 8f90941e936e490e2979760de817d36521bec50e..5caab5128b89872a706bef761946c8c5e415f63e 100644 (file)
@@ -33,6 +33,8 @@ import org.mockito.Mockito;
 import org.sonar.core.util.stream.MoreCollectors;
 
 import static java.util.Collections.singleton;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatCode;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.same;
 import static org.mockito.Mockito.doThrow;
@@ -74,14 +76,14 @@ public class ProjectLifeCycleListenersImplTest {
   }
 
   @Test
-  @UseDataProvider("oneOrManyProjects")
-  public void onProjectsDeleted_does_not_fail_if_there_is_no_listener(Set<Project> projects) {
-    underTestNoListeners.onProjectsDeleted(projects);
+  @UseDataProvider("oneOrManyDeletedProjects")
+  public void onProjectsDeleted_does_not_fail_if_there_is_no_listener(Set<DeletedProject> projects) {
+    assertThatCode(() -> underTestNoListeners.onProjectsDeleted(projects)).doesNotThrowAnyException();
   }
 
   @Test
-  @UseDataProvider("oneOrManyProjects")
-  public void onProjectsDeleted_calls_all_listeners_in_order_of_addition_to_constructor(Set<Project> projects) {
+  @UseDataProvider("oneOrManyDeletedProjects")
+  public void onProjectsDeleted_calls_all_listeners_in_order_of_addition_to_constructor(Set<DeletedProject> projects) {
     InOrder inOrder = Mockito.inOrder(listener1, listener2, listener3);
 
     underTestWithListeners.onProjectsDeleted(projects);
@@ -93,8 +95,8 @@ public class ProjectLifeCycleListenersImplTest {
   }
 
   @Test
-  @UseDataProvider("oneOrManyProjects")
-  public void onProjectsDeleted_calls_all_listeners_even_if_one_throws_an_Exception(Set<Project> projects) {
+  @UseDataProvider("oneOrManyDeletedProjects")
+  public void onProjectsDeleted_calls_all_listeners_even_if_one_throws_an_Exception(Set<DeletedProject> projects) {
     InOrder inOrder = Mockito.inOrder(listener1, listener2, listener3);
     doThrow(new RuntimeException("Faking listener2 throwing an exception"))
       .when(listener2)
@@ -109,8 +111,8 @@ public class ProjectLifeCycleListenersImplTest {
   }
 
   @Test
-  @UseDataProvider("oneOrManyProjects")
-  public void onProjectsDeleted_calls_all_listeners_even_if_one_throws_an_Error(Set<Project> projects) {
+  @UseDataProvider("oneOrManyDeletedProjects")
+  public void onProjectsDeleted_calls_all_listeners_even_if_one_throws_an_Error(Set<DeletedProject> projects) {
     InOrder inOrder = Mockito.inOrder(listener1, listener2, listener3);
     doThrow(new Error("Faking listener2 throwing an Error"))
       .when(listener2)
@@ -204,7 +206,16 @@ public class ProjectLifeCycleListenersImplTest {
       {IntStream.range(0, 1 + new Random().nextInt(10)).mapToObj(i -> newUniqueProject()).collect(MoreCollectors.toSet())}
     };
   }
-  // SDSDS
+
+  @DataProvider
+  public static Object[][] oneOrManyDeletedProjects() {
+    return new Object[][] {
+      {singleton(newUniqueProject())},
+      {IntStream.range(0, 1 + new Random().nextInt(10)).mapToObj(i -> new DeletedProject(newUniqueProject(), "branch_" + i))
+        .collect(MoreCollectors.toSet())}
+    };
+  }
+
 
   @Test
   public void onProjectsRekeyed_throws_NPE_if_set_is_null() {
index 4d096c1af82e99d01bdd8f15d9ff76faa9c8cc4f..258ad145f70253a6ac4ff7cb498d75ccd047aca3 100644 (file)
@@ -22,7 +22,9 @@ package org.sonar.server.project.ws;
 import java.net.HttpURLConnection;
 import java.util.Arrays;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
@@ -40,13 +42,16 @@ import org.sonar.core.util.stream.MoreCollectors;
 import org.sonar.db.DbClient;
 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.PortfolioData;
 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;
+import org.sonar.server.project.DeletedProject;
 import org.sonar.server.project.Project;
 import org.sonar.server.project.ProjectLifeCycleListeners;
 import org.sonar.server.tester.UserSessionRule;
@@ -88,8 +93,10 @@ public class BulkDeleteActionIT {
   @Test
   public void delete_projects() {
     userSession.addPermission(ADMINISTER);
-    ProjectDto project1ToDelete = db.components().insertPrivateProject().getProjectDto();
-    ProjectDto project2ToDelete = db.components().insertPrivateProject().getProjectDto();
+    ProjectData projectData1ToDelete = db.components().insertPrivateProject();
+    ProjectDto project1ToDelete = projectData1ToDelete.getProjectDto();
+    ProjectData projectData2ToDelete = db.components().insertPrivateProject();
+    ProjectDto project2ToDelete = projectData2ToDelete.getProjectDto();
     ComponentDto toKeep = db.components().insertPrivateProject().getMainBranchComponent();
 
     TestResponse result = ws.newRequest()
@@ -99,21 +106,21 @@ public class BulkDeleteActionIT {
     assertThat(result.getStatus()).isEqualTo(HttpURLConnection.HTTP_NO_CONTENT);
     assertThat(result.getInput()).isEmpty();
     verifyEntityDeleted(project1ToDelete, project2ToDelete);
-    verifyListenersOnProjectsDeleted(project1ToDelete, project2ToDelete);
+    verifyListenersOnProjectsDeleted(projectData1ToDelete, projectData2ToDelete);
   }
 
   @Test
   public void delete_projects_by_keys() {
     userSession.addPermission(ADMINISTER);
-    ProjectDto toDeleteInOrg1 = db.components().insertPrivateProject().getProjectDto();
-    ProjectDto toDeleteInOrg2 = db.components().insertPrivateProject().getProjectDto();
+    ProjectData toDeleteInOrg1 = db.components().insertPrivateProject();
+    ProjectData toDeleteInOrg2 = db.components().insertPrivateProject();
     ComponentDto toKeep = db.components().insertPrivateProject().getMainBranchComponent();
 
     ws.newRequest()
-      .setParam(PARAM_PROJECTS, toDeleteInOrg1.getKey() + "," + toDeleteInOrg2.getKey())
+      .setParam(PARAM_PROJECTS, toDeleteInOrg1.getProjectDto().getKey() + "," + toDeleteInOrg2.getProjectDto().getKey())
       .execute();
 
-    verifyEntityDeleted(toDeleteInOrg1, toDeleteInOrg2);
+    verifyEntityDeleted(toDeleteInOrg1.getProjectDto(), toDeleteInOrg2.getProjectDto());
     verifyListenersOnProjectsDeleted(toDeleteInOrg1, toDeleteInOrg2);
   }
 
@@ -136,14 +143,14 @@ public class BulkDeleteActionIT {
   @Test
   public void projects_that_dont_exist_are_ignored_and_dont_break_bulk_deletion() {
     userSession.addPermission(ADMINISTER);
-    ProjectDto toDelete1 = db.components().insertPrivateProject().getProjectDto();
-    ProjectDto toDelete2 = db.components().insertPrivateProject().getProjectDto();
+    ProjectData toDelete1 = db.components().insertPrivateProject();
+    ProjectData toDelete2 = db.components().insertPrivateProject();
 
     ws.newRequest()
-      .setParam("projects", toDelete1.getKey() + ",missing," + toDelete2.getKey() + ",doesNotExist")
+      .setParam("projects", toDelete1.getProjectDto().getKey() + ",missing," + toDelete2.getProjectDto().getKey() + ",doesNotExist")
       .execute();
 
-    verifyEntityDeleted(toDelete1, toDelete2);
+    verifyEntityDeleted(toDelete1.getProjectDto(), toDelete2.getProjectDto());
     verifyListenersOnProjectsDeleted(toDelete1, toDelete2);
   }
 
@@ -167,61 +174,61 @@ public class BulkDeleteActionIT {
       .execute();
 
     verifyEntityDeleted(oldProject);
-    verifyListenersOnProjectsDeleted(oldProject);
+    verifyListenersOnProjectsDeleted(oldProjectData);
   }
 
   @Test
   public void provisioned_projects() {
     userSession.logIn().addPermission(ADMINISTER);
-    ProjectDto provisionedProject = db.components().insertPrivateProject().getProjectDto();
+    ProjectData provisionedProject = db.components().insertPrivateProject();
     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();
+    ws.newRequest().setParam(PARAM_PROJECTS, provisionedProject.getProjectDto().getKey() + "," + analyzedProject.getKey()).setParam(PARAM_ON_PROVISIONED_ONLY, "true").execute();
 
-    verifyEntityDeleted(provisionedProject);
+    verifyEntityDeleted(provisionedProject.getProjectDto());
     verifyListenersOnProjectsDeleted(provisionedProject);
   }
 
   @Test
   public void delete_more_than_50_projects() {
     userSession.logIn().addPermission(ADMINISTER);
-    ProjectDto[] projects = IntStream.range(0, 55).mapToObj(i -> db.components().insertPrivateProject().getProjectDto()).toArray(ProjectDto[]::new);
+    ProjectData[] projects = IntStream.range(0, 55).mapToObj(i -> db.components().insertPrivateProject()).toArray(ProjectData[]::new);
 
-    List<String> projectKeys = Stream.of(projects).map(ProjectDto::getKey).collect(Collectors.toList());
+    List<String> projectKeys = Stream.of(projects).map(ProjectData::getProjectDto).map(ProjectDto::getKey).collect(Collectors.toList());
     ws.newRequest().setParam(PARAM_PROJECTS, String.join(",", projectKeys)).execute();
 
-    verifyEntityDeleted(projects);
+    verifyEntityDeleted(Stream.of(projects).map(ProjectData::getProjectDto).toArray(ProjectDto[]::new));
     verifyListenersOnProjectsDeleted(projects);
   }
 
   @Test
   public void projects_and_views() {
     userSession.logIn().addPermission(ADMINISTER);
-    ProjectDto project = db.components().insertPrivateProject().getProjectDto();
+    ProjectData project = db.components().insertPrivateProject();
     ComponentDto view = db.components().insertPrivatePortfolio();
     PortfolioDto portfolioDto = db.components().getPortfolioDto(view);
 
     ws.newRequest()
-      .setParam(PARAM_PROJECTS, project.getKey() + "," + view.getKey())
+      .setParam(PARAM_PROJECTS, project.getProjectDto().getKey() + "," + view.getKey())
       .setParam(PARAM_QUALIFIERS, String.join(",", Qualifiers.PROJECT, Qualifiers.VIEW))
       .execute();
 
-    verifyEntityDeleted(project, portfolioDto);
+    verifyEntityDeleted(project.getProjectDto(), portfolioDto);
     verifyListenersOnProjectsDeleted(project, portfolioDto);
   }
 
   @Test
   public void delete_by_key_query_with_partial_match_case_insensitive() {
     userSession.logIn().addPermission(ADMINISTER);
-    ProjectDto matchKeyProject = db.components().insertPrivateProject(p -> p.setKey("project-_%-key")).getProjectDto();
-    ProjectDto matchUppercaseKeyProject = db.components().insertPrivateProject(p -> p.setKey("PROJECT-_%-KEY")).getProjectDto();
+    ProjectData matchKeyProject = db.components().insertPrivateProject(p -> p.setKey("project-_%-key"));
+    ProjectData matchUppercaseKeyProject = db.components().insertPrivateProject(p -> p.setKey("PROJECT-_%-KEY"));
     ProjectDto noMatchProject = db.components().insertPrivateProject(p -> p.setKey("project-key-without-escaped-characters")).getProjectDto();
 
     ws.newRequest().setParam(Param.TEXT_QUERY, "JeCt-_%-k").execute();
 
-    verifyEntityDeleted(matchKeyProject, matchUppercaseKeyProject);
+    verifyEntityDeleted(matchKeyProject.getProjectDto(), matchUppercaseKeyProject.getProjectDto());
     verifyListenersOnProjectsDeleted(matchKeyProject, matchUppercaseKeyProject);
   }
 
@@ -239,7 +246,7 @@ public class BulkDeleteActionIT {
       .execute();
 
     verify(componentCleanerService, times(1_000)).deleteEntity(any(DbSession.class), any(EntityDto.class));
-    ArgumentCaptor<Set<Project>> projectsCaptor = ArgumentCaptor.forClass(Set.class);
+    ArgumentCaptor<Set<DeletedProject>> projectsCaptor = ArgumentCaptor.forClass(Set.class);
     verify(projectLifeCycleListeners).onProjectsDeleted(projectsCaptor.capture());
     assertThat(projectsCaptor.getValue()).hasSize(1_000);
   }
@@ -247,9 +254,9 @@ public class BulkDeleteActionIT {
   @Test
   public void projectLifeCycleListeners_onProjectsDeleted_called_even_if_delete_fails() {
     userSession.logIn().addPermission(ADMINISTER);
-    ProjectDto project1 = db.components().insertPrivateProject().getProjectDto();
-    ProjectDto project2 = db.components().insertPrivateProject().getProjectDto();
-    ProjectDto project3 = db.components().insertPrivateProject().getProjectDto();
+    ProjectData project1 = db.components().insertPrivateProject();
+    ProjectData project2 = db.components().insertPrivateProject();
+    ProjectData project3 = db.components().insertPrivateProject();
     ComponentCleanerService componentCleanerService = mock(ComponentCleanerService.class);
     RuntimeException expectedException = new RuntimeException("Faking delete failing on 2nd project");
     doNothing()
@@ -259,7 +266,7 @@ public class BulkDeleteActionIT {
 
     try {
       ws.newRequest()
-        .setParam("projects", project1.getKey() + "," + project2.getKey() + "," + project3.getKey())
+        .setParam("projects", project1.getProjectDto().getKey() + "," + project2.getProjectDto().getKey() + "," + project3.getProjectDto().getKey())
         .execute();
     } catch (RuntimeException e) {
       assertThat(e).isSameAs(expectedException);
@@ -270,14 +277,14 @@ public class BulkDeleteActionIT {
   @Test
   public void global_administrator_deletes_projects_by_keys() {
     userSession.logIn().addPermission(ADMINISTER);
-    ProjectDto toDelete1 = db.components().insertPrivateProject().getProjectDto();
-    ProjectDto toDelete2 = db.components().insertPrivateProject().getProjectDto();
+    ProjectData toDelete1 = db.components().insertPrivateProject();
+    ProjectData toDelete2 = db.components().insertPrivateProject();
 
     ws.newRequest()
-      .setParam("projects", toDelete1.getKey() + "," + toDelete2.getKey())
+      .setParam("projects", toDelete1.getProjectDto().getKey() + "," + toDelete2.getProjectDto().getKey())
       .execute();
 
-    verifyEntityDeleted(toDelete1, toDelete2);
+    verifyEntityDeleted(toDelete1.getProjectDto(), toDelete2.getProjectDto());
     verifyListenersOnProjectsDeleted(toDelete1, toDelete2);
   }
 
@@ -325,8 +332,25 @@ public class BulkDeleteActionIT {
     verifyNoMoreInteractions(componentCleanerService);
   }
 
-  private void verifyListenersOnProjectsDeleted(EntityDto... entityDtos) {
+  private void verifyListenersOnProjectsDeleted(ProjectData projectData, PortfolioDto portfolioDto) {
+    Map<EntityDto, String> entityWithBranh = new HashMap<>();
+    entityWithBranh.put(projectData.getProjectDto(), projectData.getMainBranchDto().getUuid());
+    entityWithBranh.put(portfolioDto, null);
+
+    verifyListenersOnProjectsDeleted(entityWithBranh);
+  }
+
+  private void verifyListenersOnProjectsDeleted(ProjectData... projectData) {
+    verifyListenersOnProjectsDeleted(Arrays.stream(projectData).collect(Collectors.toMap(ProjectData::getProjectDto, data -> data.getMainBranchDto().getUuid())));
+  }
+
+  private void verifyListenersOnProjectsDeleted(PortfolioData... portfolioData) {
+    verifyListenersOnProjectsDeleted(Arrays.stream(portfolioData).collect(Collectors.toMap(PortfolioData::getPortfolioDto, null)));
+  }
+
+  private void verifyListenersOnProjectsDeleted(Map<EntityDto, String> entityWithBranchUuid) {
     verify(projectLifeCycleListeners)
-      .onProjectsDeleted(Arrays.stream(entityDtos).map(Project::from).collect(Collectors.toSet()));
+      .onProjectsDeleted(entityWithBranchUuid.entrySet()
+        .stream().map(entry -> new DeletedProject(Project.from(entry.getKey()), entry.getValue())).collect(Collectors.toSet()));
   }
 }
index 035b3ef9ffc504d1a6006fe01a9ad5e46f84689a..fec4fc61215c9742aceab5e9929abf638df084f6 100644 (file)
@@ -39,6 +39,7 @@ import org.sonar.server.component.ComponentCleanerService;
 import org.sonar.server.es.TestIndexers;
 import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.server.project.DeletedProject;
 import org.sonar.server.project.Project;
 import org.sonar.server.project.ProjectLifeCycleListeners;
 import org.sonar.server.tester.UserSessionRule;
@@ -80,13 +81,15 @@ public class DeleteActionIT {
   @Test
   public void global_administrator_deletes_project_by_key() {
     ProjectData projectData = db.components().insertPrivateProject();
-    ComponentDto project = projectData.getMainBranchComponent();
+    ComponentDto rootComponent = projectData.getMainBranchComponent();
     userSessionRule.logIn().addPermission(GlobalPermission.ADMINISTER);
 
-    call(tester.newRequest().setParam(PARAM_PROJECT, project.getKey()));
+    call(tester.newRequest().setParam(PARAM_PROJECT, rootComponent.getKey()));
 
-    assertThat(verifyDeletedKey()).isEqualTo(project.getKey());
-    verify(projectLifeCycleListeners).onProjectsDeleted(singleton(Project.from(projectData.getProjectDto())));
+    assertThat(verifyDeletedKey()).isEqualTo(rootComponent.getKey());
+    Project from = Project.from(projectData.getProjectDto());
+    String mainBranchUuid = projectData.getMainBranchDto().getUuid();
+    verify(projectLifeCycleListeners).onProjectsDeleted(singleton(new DeletedProject(from, mainBranchUuid)));
   }
 
   @Test
@@ -98,7 +101,7 @@ public class DeleteActionIT {
     call(tester.newRequest().setParam(PARAM_PROJECT, project.getKey()));
 
     assertThat(verifyDeletedKey()).isEqualTo(project.getKey());
-    verify(projectLifeCycleListeners).onProjectsDeleted(singleton(Project.from(projectData.getProjectDto())));
+    verify(projectLifeCycleListeners).onProjectsDeleted(singleton(new DeletedProject(Project.from(projectData.getProjectDto()),projectData.getMainBranchDto().getUuid())));
   }
 
   @Test
index 57d33bc5ff8c7b67e410e162f1705d013f93ca84..fdc1793fd8cd878c3c1e72dbcca7c56aea03713f 100644 (file)
 package org.sonar.server.project.ws;
 
 import com.google.common.base.Strings;
+import com.google.common.collect.ImmutableSet;
 import java.util.Date;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
@@ -38,11 +40,13 @@ import org.sonar.api.utils.DateUtils;
 import org.sonar.core.util.stream.MoreCollectors;
 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.component.ComponentQuery;
 import org.sonar.db.entity.EntityDto;
 import org.sonar.db.permission.GlobalPermission;
 import org.sonar.server.component.ComponentCleanerService;
+import org.sonar.server.project.DeletedProject;
 import org.sonar.server.project.Project;
 import org.sonar.server.project.ProjectLifeCycleListeners;
 import org.sonar.server.project.Visibility;
@@ -118,7 +122,7 @@ public class BulkDeleteAction implements ProjectsWsAction {
 
     action.createParam(PARAM_VISIBILITY)
       .setDescription("Filter the projects that should be visible to everyone (%s), or only specific user/groups (%s).<br/>" +
-          "If no visibility is specified, the default project visibility will be used.",
+        "If no visibility is specified, the default project visibility will be used.",
         Visibility.PUBLIC.getLabel(), Visibility.PRIVATE.getLabel())
       .setRequired(false)
       .setInternal(true)
@@ -154,12 +158,22 @@ public class BulkDeleteAction implements ProjectsWsAction {
       try {
         entities.forEach(p -> componentCleanerService.deleteEntity(dbSession, p));
       } finally {
-        projectLifeCycleListeners.onProjectsDeleted(entities.stream().map(Project::from).collect(Collectors.toSet()));
+        callDeleteListeners(dbSession, componentDtos, entities);
       }
     }
     response.noContent();
   }
 
+  private void callDeleteListeners(DbSession dbSession, Set<ComponentDto> componentDtos, List<EntityDto> entities) {
+    Set<String> entityUuids = entities.stream().map(EntityDto::getUuid).collect(toSet());
+    Map<String, String> mainBranchUuidByEntityUuid = dbClient.branchDao().selectMainBranchesByProjectUuids(dbSession, entityUuids).stream()
+      .collect(Collectors.toMap(BranchDto::getProjectUuid, BranchDto::getUuid));
+
+    ImmutableSet<DeletedProject> deletedProjects = entities.stream().map(entity -> new DeletedProject(Project.from(entity), mainBranchUuidByEntityUuid.get(entity.getUuid())))
+      .collect(MoreCollectors.toSet(componentDtos.size()));
+    projectLifeCycleListeners.onProjectsDeleted(deletedProjects);
+  }
+
   private static void checkAtLeastOneParameterIsPresent(SearchRequest searchRequest) {
     boolean analyzedBeforePresent = !Strings.isNullOrEmpty(searchRequest.getAnalyzedBefore());
     List<String> projects = searchRequest.getProjects();
index 3fc9097d39895858d64dd912ac83791f8d219f73..7b0e6742df7540758c59a712b8fbab04029e26a9 100644 (file)
@@ -25,10 +25,12 @@ import org.sonar.api.server.ws.WebService;
 import org.sonar.api.web.UserRole;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
+import org.sonar.db.component.BranchDto;
 import org.sonar.db.permission.GlobalPermission;
 import org.sonar.db.project.ProjectDto;
 import org.sonar.server.component.ComponentCleanerService;
 import org.sonar.server.component.ComponentFinder;
+import org.sonar.server.project.DeletedProject;
 import org.sonar.server.project.Project;
 import org.sonar.server.project.ProjectLifeCycleListeners;
 import org.sonar.server.user.UserSession;
@@ -80,9 +82,10 @@ public class DeleteAction implements ProjectsWsAction {
 
     try (DbSession dbSession = dbClient.openSession(false)) {
       ProjectDto project = componentFinder.getProjectByKey(dbSession, key);
+      BranchDto mainBranch = componentFinder.getMainBranch(dbSession, project);
       checkPermission(project);
       componentCleanerService.deleteEntity(dbSession, project);
-      projectLifeCycleListeners.onProjectsDeleted(singleton(Project.fromProjectDtoWithTags(project)));
+      projectLifeCycleListeners.onProjectsDeleted(singleton(new DeletedProject(Project.fromProjectDtoWithTags(project), mainBranch.getUuid())));
     }
 
     response.noContent();