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;
}
@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);
}
@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)
}
@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)
{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() {
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;
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;
@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()
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);
}
@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);
}
.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);
}
.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);
}
@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()
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);
@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);
}
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()));
}
}
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;
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;
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)
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();