From: Teryk Bellahsene Date: Fri, 29 May 2015 14:56:42 +0000 (+0200) Subject: SONAR-6529 WS api/projects/bulk_delete change action from delete to bulk_delete X-Git-Tag: 5.2-RC1~1768 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=d686c696d9cebbb533e609551c959d6f4eb8cba9;p=sonarqube.git SONAR-6529 WS api/projects/bulk_delete change action from delete to bulk_delete --- diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java index 74bb372ca4a..ab1d0513ecc 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java @@ -207,6 +207,7 @@ import org.sonar.server.plugins.ws.PluginWSCommons; import org.sonar.server.plugins.ws.PluginsWs; import org.sonar.server.plugins.ws.UninstallAction; import org.sonar.server.plugins.ws.UpdatesAction; +import org.sonar.server.project.ws.BulkDeleteAction; import org.sonar.server.project.ws.GhostsAction; import org.sonar.server.project.ws.ProjectsWs; import org.sonar.server.project.ws.ProvisionedAction; @@ -578,7 +579,7 @@ public class PlatformLevel4 extends PlatformLevel { ResourcesWs.class, ComponentsWs.class, ProjectsWs.class, - org.sonar.server.project.ws.DeleteAction.class, + BulkDeleteAction.class, org.sonar.server.component.ws.AppAction.class, org.sonar.server.component.ws.SearchAction.class, EventsWs.class, diff --git a/server/sonar-server/src/main/java/org/sonar/server/project/ws/BulkDeleteAction.java b/server/sonar-server/src/main/java/org/sonar/server/project/ws/BulkDeleteAction.java new file mode 100644 index 00000000000..6ebf54a3574 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/project/ws/BulkDeleteAction.java @@ -0,0 +1,101 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.ws; + +import org.sonar.api.server.ws.Request; +import org.sonar.api.server.ws.Response; +import org.sonar.api.server.ws.WebService; +import org.sonar.api.web.UserRole; +import org.sonar.core.component.ComponentDto; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.persistence.MyBatis; +import org.sonar.server.component.ComponentCleanerService; +import org.sonar.server.db.DbClient; +import org.sonar.server.user.UserSession; + +import javax.annotation.Nullable; + +import java.util.List; + +public class BulkDeleteAction implements ProjectsWsAction { + private static final String ACTION = "bulk_delete"; + + private static final String PARAM_UUIDS = "ids"; + private static final String PARAM_KEYS = "keys"; + + private final ComponentCleanerService componentCleanerService; + private final DbClient dbClient; + private final UserSession userSession; + + public BulkDeleteAction(ComponentCleanerService componentCleanerService, DbClient dbClient, UserSession userSession) { + this.componentCleanerService = componentCleanerService; + this.dbClient = dbClient; + this.userSession = userSession; + } + + @Override + public void define(WebService.NewController context) { + WebService.NewAction action = context + .createAction(ACTION) + .setPost(true) + .setDescription("Delete one or several projects.
Requires 'Administer System' permission.") + .setSince("5.2") + .setHandler(this); + + action + .createParam(PARAM_UUIDS) + .setDescription("List of project UUIDs to delete") + .setExampleValue("ce4c03d6-430f-40a9-b777-ad877c00aa4d,c526ef20-131b-4486-9357-063fa64b5079"); + + action + .createParam(PARAM_KEYS) + .setDescription("List of project keys to delete") + .setExampleValue("org.apache.hbas:hbase,com.microsoft.roslyn:roslyn"); + } + + @Override + public void handle(Request request, Response response) throws Exception { + userSession.checkGlobalPermission(UserRole.ADMIN); + List uuids = request.paramAsStrings(PARAM_UUIDS); + List keys = request.paramAsStrings(PARAM_KEYS); + + DbSession dbSession = dbClient.openSession(false); + try { + List projects = searchProjects(dbSession, uuids, keys); + componentCleanerService.delete(dbSession, projects); + } finally { + MyBatis.closeQuietly(dbSession); + } + + response.noContent(); + } + + private List searchProjects(DbSession dbSession, @Nullable List uuids, @Nullable List keys) { + if (uuids != null) { + return dbClient.componentDao().selectByUuids(dbSession, uuids); + } + if (keys != null) { + return dbClient.componentDao().selectByKeys(dbSession, keys); + } + + throw new IllegalArgumentException("UUIDs or keys must be provided"); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/project/ws/DeleteAction.java b/server/sonar-server/src/main/java/org/sonar/server/project/ws/DeleteAction.java deleted file mode 100644 index 87670ef6f68..00000000000 --- a/server/sonar-server/src/main/java/org/sonar/server/project/ws/DeleteAction.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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.ws; - -import org.sonar.api.server.ws.Request; -import org.sonar.api.server.ws.Response; -import org.sonar.api.server.ws.WebService; -import org.sonar.api.web.UserRole; -import org.sonar.core.component.ComponentDto; -import org.sonar.core.persistence.DbSession; -import org.sonar.core.persistence.MyBatis; -import org.sonar.server.component.ComponentCleanerService; -import org.sonar.server.db.DbClient; -import org.sonar.server.user.UserSession; - -import javax.annotation.Nullable; - -import java.util.List; - -public class DeleteAction implements ProjectsWsAction { - private static final String ACTION = "delete"; - - private static final String PARAM_UUIDS = "ids"; - private static final String PARAM_KEYS = "keys"; - - private final ComponentCleanerService componentCleanerService; - private final DbClient dbClient; - private final UserSession userSession; - - public DeleteAction(ComponentCleanerService componentCleanerService, DbClient dbClient, UserSession userSession) { - this.componentCleanerService = componentCleanerService; - this.dbClient = dbClient; - this.userSession = userSession; - } - - @Override - public void define(WebService.NewController context) { - WebService.NewAction action = context - .createAction(ACTION) - .setPost(true) - .setDescription("Delete one or several projects.
Requires 'Administer System' permission.") - .setSince("5.2") - .setHandler(this); - - action - .createParam(PARAM_UUIDS) - .setDescription("List of project UUIDs to delete") - .setExampleValue("ce4c03d6-430f-40a9-b777-ad877c00aa4d,c526ef20-131b-4486-9357-063fa64b5079"); - - action - .createParam(PARAM_KEYS) - .setDescription("List of project keys to delete") - .setExampleValue("org.apache.hbas:hbase,com.microsoft.roslyn:roslyn"); - } - - @Override - public void handle(Request request, Response response) throws Exception { - userSession.checkGlobalPermission(UserRole.ADMIN); - List uuids = request.paramAsStrings(PARAM_UUIDS); - List keys = request.paramAsStrings(PARAM_KEYS); - - DbSession dbSession = dbClient.openSession(false); - try { - List projects = searchProjects(dbSession, uuids, keys); - componentCleanerService.delete(dbSession, projects); - } finally { - MyBatis.closeQuietly(dbSession); - } - - response.noContent(); - } - - private List searchProjects(DbSession dbSession, @Nullable List uuids, @Nullable List keys) { - if (uuids != null) { - return dbClient.componentDao().selectByUuids(dbSession, uuids); - } - if (keys != null) { - return dbClient.componentDao().selectByKeys(dbSession, keys); - } - - throw new IllegalArgumentException("UUIDs or keys must be provided"); - } -} diff --git a/server/sonar-server/src/test/java/org/sonar/server/project/ws/BulkDeleteActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/project/ws/BulkDeleteActionTest.java new file mode 100644 index 00000000000..f73b4baa96a --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/project/ws/BulkDeleteActionTest.java @@ -0,0 +1,252 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.ws; + +import com.google.common.collect.ImmutableMap; +import java.util.Arrays; +import org.junit.After; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.rules.ExpectedException; +import org.sonar.api.config.Settings; +import org.sonar.api.resources.Qualifiers; +import org.sonar.api.resources.ResourceType; +import org.sonar.api.resources.ResourceTypes; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.utils.System2; +import org.sonar.api.web.UserRole; +import org.sonar.core.component.ComponentDto; +import org.sonar.core.component.SnapshotDto; +import org.sonar.core.issue.db.IssueDto; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.persistence.DbTester; +import org.sonar.core.purge.PurgeDao; +import org.sonar.core.purge.PurgeProfiler; +import org.sonar.core.resource.ResourceDao; +import org.sonar.core.rule.RuleDto; +import org.sonar.server.component.ComponentCleanerService; +import org.sonar.server.component.ComponentTesting; +import org.sonar.server.component.SnapshotTesting; +import org.sonar.server.component.db.ComponentDao; +import org.sonar.server.component.db.SnapshotDao; +import org.sonar.server.db.DbClient; +import org.sonar.server.es.EsTester; +import org.sonar.server.exceptions.ForbiddenException; +import org.sonar.server.issue.IssueTesting; +import org.sonar.server.issue.db.IssueDao; +import org.sonar.server.issue.index.IssueAuthorizationIndexer; +import org.sonar.server.issue.index.IssueIndexDefinition; +import org.sonar.server.issue.index.IssueIndexer; +import org.sonar.server.rule.RuleTesting; +import org.sonar.server.rule.db.RuleDao; +import org.sonar.server.source.index.SourceLineDoc; +import org.sonar.server.source.index.SourceLineIndexDefinition; +import org.sonar.server.source.index.SourceLineIndexer; +import org.sonar.server.test.index.TestDoc; +import org.sonar.server.test.index.TestIndexDefinition; +import org.sonar.server.test.index.TestIndexer; +import org.sonar.server.tester.UserSessionRule; +import org.sonar.server.ws.WsTester; +import org.sonar.test.DbTests; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@Category(DbTests.class) +public class BulkDeleteActionTest { + + private static final String ACTION = "bulk_delete"; + @ClassRule + public static DbTester db = new DbTester(); + @ClassRule + public static EsTester es = new EsTester().addDefinitions(new IssueIndexDefinition(new Settings()), new SourceLineIndexDefinition(new Settings()), + new TestIndexDefinition(new Settings())); + @Rule + public UserSessionRule userSessionRule = UserSessionRule.standalone(); + @Rule + public ExpectedException expectedException = ExpectedException.none(); + WsTester ws; + DbClient dbClient; + DbSession dbSession; + ResourceType resourceType; + + @Before + public void setUp() throws Exception { + ComponentDao componentDao = new ComponentDao(); + ResourceDao resourceDao = new ResourceDao(db.myBatis(), System2.INSTANCE); + PurgeDao purgeDao = new PurgeDao(db.myBatis(), resourceDao, new PurgeProfiler(), System2.INSTANCE); + dbClient = new DbClient(db.database(), db.myBatis(), componentDao, purgeDao, new RuleDao(System2.INSTANCE), new IssueDao(db.myBatis()), new SnapshotDao(System2.INSTANCE)); + dbSession = dbClient.openSession(false); + resourceType = mock(ResourceType.class); + when(resourceType.getBooleanProperty(anyString())).thenReturn(true); + ResourceTypes mockResourceTypes = mock(ResourceTypes.class); + when(mockResourceTypes.get(anyString())).thenReturn(resourceType); + ws = new WsTester(new ProjectsWs(new BulkDeleteAction(new ComponentCleanerService(dbClient, new IssueAuthorizationIndexer(dbClient, es.client()), new IssueIndexer( + dbClient, es.client()), new SourceLineIndexer(dbClient, es.client()), new TestIndexer(dbClient, es.client()), mockResourceTypes), dbClient, userSessionRule))); + db.truncateTables(); + es.truncateIndices(); + } + + @After + public void tearDown() throws Exception { + dbSession.close(); + } + + @Test + public void delete_projects_and_data_in_db_by_uuids() throws Exception { + userSessionRule.setGlobalPermissions(UserRole.ADMIN); + long snapshotId1 = insertNewProjectInDbAndReturnSnapshotId(1); + long snapshotId2 = insertNewProjectInDbAndReturnSnapshotId(2); + long snapshotId3 = insertNewProjectInDbAndReturnSnapshotId(3); + long snapshotId4 = insertNewProjectInDbAndReturnSnapshotId(4); + + ws.newPostRequest("api/projects", ACTION) + .setParam("ids", "project-uuid-1, project-uuid-3, project-uuid-4").execute(); + dbSession.commit(); + + assertThat(dbClient.componentDao().selectByUuids(dbSession, Arrays.asList("project-uuid-1", "project-uuid-3", "project-uuid-4"))).isEmpty(); + assertThat(dbClient.componentDao().selectByUuid(dbSession, "project-uuid-2")).isNotNull(); + assertThat(dbClient.snapshotDao().getNullableByKey(dbSession, snapshotId1)).isNull(); + assertThat(dbClient.snapshotDao().getNullableByKey(dbSession, snapshotId3)).isNull(); + assertThat(dbClient.snapshotDao().getNullableByKey(dbSession, snapshotId4)).isNull(); + assertThat(dbClient.snapshotDao().getNullableByKey(dbSession, snapshotId2)).isNotNull(); + assertThat(dbClient.issueDao().selectByKeys(dbSession, Arrays.asList("issue-key-1", "issue-key-3", "issue-key-4"))).isEmpty(); + assertThat(dbClient.issueDao().selectByKey(dbSession, "issue-key-2")).isNotNull(); + } + + @Test + public void delete_projects_and_data_in_db_by_keys() throws Exception { + userSessionRule.setGlobalPermissions(UserRole.ADMIN); + insertNewProjectInDbAndReturnSnapshotId(1); + insertNewProjectInDbAndReturnSnapshotId(2); + insertNewProjectInDbAndReturnSnapshotId(3); + insertNewProjectInDbAndReturnSnapshotId(4); + + ws.newPostRequest("api/projects", ACTION) + .setParam("keys", "project-key-1, project-key-3, project-key-4").execute(); + dbSession.commit(); + + assertThat(dbClient.componentDao().selectByUuids(dbSession, Arrays.asList("project-uuid-1", "project-uuid-3", "project-uuid-4"))).isEmpty(); + assertThat(dbClient.componentDao().selectByUuid(dbSession, "project-uuid-2")).isNotNull(); + } + + @Test + public void delete_documents_indexes() throws Exception { + userSessionRule.setGlobalPermissions(UserRole.ADMIN); + insertNewProjectInIndexes(1); + insertNewProjectInIndexes(2); + insertNewProjectInIndexes(3); + insertNewProjectInIndexes(4); + + ws.newPostRequest("api/projects", ACTION) + .setParam("keys", "project-key-1, project-key-3, project-key-4").execute(); + + String remainingProjectUuid = "project-uuid-2"; + assertThat(es.getDocumentFieldValues(IssueIndexDefinition.INDEX, IssueIndexDefinition.TYPE_ISSUE, IssueIndexDefinition.FIELD_ISSUE_PROJECT_UUID)) + .containsOnly(remainingProjectUuid); + assertThat(es.getDocumentFieldValues(IssueIndexDefinition.INDEX, IssueIndexDefinition.TYPE_AUTHORIZATION, IssueIndexDefinition.FIELD_AUTHORIZATION_PROJECT_UUID)) + .containsOnly(remainingProjectUuid); + assertThat(es.getDocumentFieldValues(SourceLineIndexDefinition.INDEX, SourceLineIndexDefinition.TYPE, SourceLineIndexDefinition.FIELD_PROJECT_UUID)) + .containsOnly(remainingProjectUuid); + assertThat(es.getDocumentFieldValues(TestIndexDefinition.INDEX, TestIndexDefinition.TYPE, TestIndexDefinition.FIELD_PROJECT_UUID)) + .containsOnly(remainingProjectUuid); + } + + @Test + public void web_service_returns_204() throws Exception { + userSessionRule.setGlobalPermissions(UserRole.ADMIN); + insertNewProjectInDbAndReturnSnapshotId(1); + + WsTester.Result result = ws.newPostRequest("api/projects", ACTION).setParam("ids", "project-uuid-1").execute(); + + result.assertNoContent(); + } + + @Test + public void fail_if_insufficient_privileges() throws Exception { + userSessionRule.setGlobalPermissions(UserRole.CODEVIEWER, UserRole.ISSUE_ADMIN, UserRole.USER); + expectedException.expect(ForbiddenException.class); + + ws.newPostRequest("api/projects", ACTION).setParam("uuids", "whatever-the-uuid").execute(); + } + + @Test + public void fail_if_scope_is_not_project() throws Exception { + userSessionRule.setGlobalPermissions(UserRole.ADMIN); + expectedException.expect(IllegalArgumentException.class); + dbClient.componentDao().insert(dbSession, ComponentTesting.newFileDto(ComponentTesting.newProjectDto(), "file-uuid")); + dbSession.commit(); + + ws.newPostRequest("api/projects", ACTION).setParam("uuids", "file-uuid").execute(); + } + + @Test + public void fail_if_qualifier_is_not_deletable() throws Exception { + userSessionRule.setGlobalPermissions(UserRole.ADMIN); + expectedException.expect(IllegalArgumentException.class); + dbClient.componentDao().insert(dbSession, ComponentTesting.newProjectDto("project-uuid").setQualifier(Qualifiers.FILE)); + dbSession.commit(); + when(resourceType.getBooleanProperty(anyString())).thenReturn(false); + + ws.newPostRequest("api/projects", ACTION).setParam("uuids", "project-uuid").execute(); + } + + private long insertNewProjectInDbAndReturnSnapshotId(int id) { + String suffix = String.valueOf(id); + ComponentDto project = ComponentTesting + .newProjectDto("project-uuid-" + suffix) + .setKey("project-key-" + suffix); + RuleDto rule = RuleTesting.newDto(RuleKey.of("sonarqube", "rule-" + suffix)); + dbClient.ruleDao().insert(dbSession, rule); + IssueDto issue = IssueTesting.newDto(rule, project, project).setKee("issue-key-" + suffix); + dbClient.componentDao().insert(dbSession, project); + SnapshotDto snapshot = dbClient.snapshotDao().insert(dbSession, SnapshotTesting.createForProject(project)); + dbClient.issueDao().insert(dbSession, issue); + dbSession.commit(); + + return snapshot.getId(); + } + + private void insertNewProjectInIndexes(int id) throws Exception { + String suffix = String.valueOf(id); + ComponentDto project = ComponentTesting + .newProjectDto("project-uuid-" + suffix) + .setKey("project-key-" + suffix); + dbClient.componentDao().insert(dbSession, project); + dbSession.commit(); + + es.putDocuments(IssueIndexDefinition.INDEX, IssueIndexDefinition.TYPE_ISSUE, IssueTesting.newDoc("issue-key-" + suffix, project)); + SourceLineDoc sourceLineDoc = new SourceLineDoc() + .setProjectUuid(project.uuid()) + .setFileUuid(project.uuid()); + es.putDocuments(IssueIndexDefinition.INDEX, IssueIndexDefinition.TYPE_AUTHORIZATION, + ImmutableMap.of(IssueIndexDefinition.FIELD_AUTHORIZATION_PROJECT_UUID, project.uuid())); + + es.putDocuments(SourceLineIndexDefinition.INDEX, SourceLineIndexDefinition.TYPE, sourceLineDoc); + TestDoc testDoc = new TestDoc().setUuid("test-uuid-" + suffix).setProjectUuid(project.uuid()).setFileUuid(project.uuid()); + es.putDocuments(TestIndexDefinition.INDEX, TestIndexDefinition.TYPE, testDoc); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/project/ws/DeleteActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/project/ws/DeleteActionTest.java deleted file mode 100644 index 89f655fc10a..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/project/ws/DeleteActionTest.java +++ /dev/null @@ -1,252 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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.ws; - -import com.google.common.collect.ImmutableMap; -import org.junit.After; -import org.junit.Before; -import org.junit.ClassRule; -import org.junit.Rule; -import org.junit.Test; -import org.junit.experimental.categories.Category; -import org.junit.rules.ExpectedException; -import org.sonar.api.config.Settings; -import org.sonar.api.resources.Qualifiers; -import org.sonar.api.resources.ResourceType; -import org.sonar.api.resources.ResourceTypes; -import org.sonar.api.rule.RuleKey; -import org.sonar.api.utils.System2; -import org.sonar.api.web.UserRole; -import org.sonar.core.component.ComponentDto; -import org.sonar.core.component.SnapshotDto; -import org.sonar.core.issue.db.IssueDto; -import org.sonar.core.persistence.DbSession; -import org.sonar.core.persistence.DbTester; -import org.sonar.core.purge.PurgeDao; -import org.sonar.core.purge.PurgeProfiler; -import org.sonar.core.resource.ResourceDao; -import org.sonar.core.rule.RuleDto; -import org.sonar.server.component.ComponentCleanerService; -import org.sonar.server.component.ComponentTesting; -import org.sonar.server.component.SnapshotTesting; -import org.sonar.server.component.db.ComponentDao; -import org.sonar.server.component.db.SnapshotDao; -import org.sonar.server.db.DbClient; -import org.sonar.server.es.EsTester; -import org.sonar.server.exceptions.ForbiddenException; -import org.sonar.server.issue.IssueTesting; -import org.sonar.server.issue.db.IssueDao; -import org.sonar.server.issue.index.IssueAuthorizationIndexer; -import org.sonar.server.issue.index.IssueIndexDefinition; -import org.sonar.server.issue.index.IssueIndexer; -import org.sonar.server.rule.RuleTesting; -import org.sonar.server.rule.db.RuleDao; -import org.sonar.server.source.index.SourceLineDoc; -import org.sonar.server.source.index.SourceLineIndexDefinition; -import org.sonar.server.source.index.SourceLineIndexer; -import org.sonar.server.test.index.TestDoc; -import org.sonar.server.test.index.TestIndexDefinition; -import org.sonar.server.test.index.TestIndexer; -import org.sonar.server.tester.UserSessionRule; -import org.sonar.server.ws.WsTester; -import org.sonar.test.DbTests; - -import java.util.Arrays; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Matchers.anyString; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -@Category(DbTests.class) -public class DeleteActionTest { - - @ClassRule - public static DbTester db = new DbTester(); - @ClassRule - public static EsTester es = new EsTester().addDefinitions(new IssueIndexDefinition(new Settings()), new SourceLineIndexDefinition(new Settings()), - new TestIndexDefinition(new Settings())); - @Rule - public UserSessionRule userSessionRule = UserSessionRule.standalone(); - @Rule - public ExpectedException expectedException = ExpectedException.none(); - WsTester ws; - DbClient dbClient; - DbSession dbSession; - ResourceType resourceType; - - @Before - public void setUp() throws Exception { - ComponentDao componentDao = new ComponentDao(); - ResourceDao resourceDao = new ResourceDao(db.myBatis(), System2.INSTANCE); - PurgeDao purgeDao = new PurgeDao(db.myBatis(), resourceDao, new PurgeProfiler(), System2.INSTANCE); - dbClient = new DbClient(db.database(), db.myBatis(), componentDao, purgeDao, new RuleDao(System2.INSTANCE), new IssueDao(db.myBatis()), new SnapshotDao(System2.INSTANCE)); - dbSession = dbClient.openSession(false); - resourceType = mock(ResourceType.class); - when(resourceType.getBooleanProperty(anyString())).thenReturn(true); - ResourceTypes mockResourceTypes = mock(ResourceTypes.class); - when(mockResourceTypes.get(anyString())).thenReturn(resourceType); - ws = new WsTester(new ProjectsWs(new DeleteAction(new ComponentCleanerService(dbClient, new IssueAuthorizationIndexer(dbClient, es.client()), new IssueIndexer( - dbClient, es.client()), new SourceLineIndexer(dbClient, es.client()), new TestIndexer(dbClient, es.client()), mockResourceTypes), dbClient, userSessionRule))); - db.truncateTables(); - es.truncateIndices(); - } - - @After - public void tearDown() throws Exception { - dbSession.close(); - } - - @Test - public void delete_projects_and_data_in_db_by_uuids() throws Exception { - userSessionRule.setGlobalPermissions(UserRole.ADMIN); - long snapshotId1 = insertNewProjectInDbAndReturnSnapshotId(1); - long snapshotId2 = insertNewProjectInDbAndReturnSnapshotId(2); - long snapshotId3 = insertNewProjectInDbAndReturnSnapshotId(3); - long snapshotId4 = insertNewProjectInDbAndReturnSnapshotId(4); - - ws.newPostRequest("api/projects", "delete") - .setParam("ids", "project-uuid-1, project-uuid-3, project-uuid-4").execute(); - dbSession.commit(); - - assertThat(dbClient.componentDao().selectByUuids(dbSession, Arrays.asList("project-uuid-1", "project-uuid-3", "project-uuid-4"))).isEmpty(); - assertThat(dbClient.componentDao().selectByUuid(dbSession, "project-uuid-2")).isNotNull(); - assertThat(dbClient.snapshotDao().getNullableByKey(dbSession, snapshotId1)).isNull(); - assertThat(dbClient.snapshotDao().getNullableByKey(dbSession, snapshotId3)).isNull(); - assertThat(dbClient.snapshotDao().getNullableByKey(dbSession, snapshotId4)).isNull(); - assertThat(dbClient.snapshotDao().getNullableByKey(dbSession, snapshotId2)).isNotNull(); - assertThat(dbClient.issueDao().selectByKeys(dbSession, Arrays.asList("issue-key-1", "issue-key-3", "issue-key-4"))).isEmpty(); - assertThat(dbClient.issueDao().selectByKey(dbSession, "issue-key-2")).isNotNull(); - } - - @Test - public void delete_projects_and_data_in_db_by_keys() throws Exception { - userSessionRule.setGlobalPermissions(UserRole.ADMIN); - insertNewProjectInDbAndReturnSnapshotId(1); - insertNewProjectInDbAndReturnSnapshotId(2); - insertNewProjectInDbAndReturnSnapshotId(3); - insertNewProjectInDbAndReturnSnapshotId(4); - - ws.newPostRequest("api/projects", "delete") - .setParam("keys", "project-key-1, project-key-3, project-key-4").execute(); - dbSession.commit(); - - assertThat(dbClient.componentDao().selectByUuids(dbSession, Arrays.asList("project-uuid-1", "project-uuid-3", "project-uuid-4"))).isEmpty(); - assertThat(dbClient.componentDao().selectByUuid(dbSession, "project-uuid-2")).isNotNull(); - } - - @Test - public void delete_documents_indexes() throws Exception { - userSessionRule.setGlobalPermissions(UserRole.ADMIN); - insertNewProjectInIndexes(1); - insertNewProjectInIndexes(2); - insertNewProjectInIndexes(3); - insertNewProjectInIndexes(4); - - ws.newPostRequest("api/projects", "delete") - .setParam("keys", "project-key-1, project-key-3, project-key-4").execute(); - - String remainingProjectUuid = "project-uuid-2"; - assertThat(es.getDocumentFieldValues(IssueIndexDefinition.INDEX, IssueIndexDefinition.TYPE_ISSUE, IssueIndexDefinition.FIELD_ISSUE_PROJECT_UUID)) - .containsOnly(remainingProjectUuid); - assertThat(es.getDocumentFieldValues(IssueIndexDefinition.INDEX, IssueIndexDefinition.TYPE_AUTHORIZATION, IssueIndexDefinition.FIELD_AUTHORIZATION_PROJECT_UUID)) - .containsOnly(remainingProjectUuid); - assertThat(es.getDocumentFieldValues(SourceLineIndexDefinition.INDEX, SourceLineIndexDefinition.TYPE, SourceLineIndexDefinition.FIELD_PROJECT_UUID)) - .containsOnly(remainingProjectUuid); - assertThat(es.getDocumentFieldValues(TestIndexDefinition.INDEX, TestIndexDefinition.TYPE, TestIndexDefinition.FIELD_PROJECT_UUID)) - .containsOnly(remainingProjectUuid); - } - - @Test - public void web_service_returns_204() throws Exception { - userSessionRule.setGlobalPermissions(UserRole.ADMIN); - insertNewProjectInDbAndReturnSnapshotId(1); - - WsTester.Result result = ws.newPostRequest("api/projects", "delete").setParam("ids", "project-uuid-1").execute(); - - result.assertNoContent(); - } - - @Test - public void fail_if_insufficient_privileges() throws Exception { - userSessionRule.setGlobalPermissions(UserRole.CODEVIEWER, UserRole.ISSUE_ADMIN, UserRole.USER); - expectedException.expect(ForbiddenException.class); - - ws.newPostRequest("api/projects", "delete").setParam("uuids", "whatever-the-uuid").execute(); - } - - @Test - public void fail_if_scope_is_not_project() throws Exception { - userSessionRule.setGlobalPermissions(UserRole.ADMIN); - expectedException.expect(IllegalArgumentException.class); - dbClient.componentDao().insert(dbSession, ComponentTesting.newFileDto(ComponentTesting.newProjectDto(), "file-uuid")); - dbSession.commit(); - - ws.newPostRequest("api/projects", "delete").setParam("uuids", "file-uuid").execute(); - } - - @Test - public void fail_if_qualifier_is_not_deletable() throws Exception { - userSessionRule.setGlobalPermissions(UserRole.ADMIN); - expectedException.expect(IllegalArgumentException.class); - dbClient.componentDao().insert(dbSession, ComponentTesting.newProjectDto("project-uuid").setQualifier(Qualifiers.FILE)); - dbSession.commit(); - when(resourceType.getBooleanProperty(anyString())).thenReturn(false); - - ws.newPostRequest("api/projects", "delete").setParam("uuids", "project-uuid").execute(); - } - - private long insertNewProjectInDbAndReturnSnapshotId(int id) { - String suffix = String.valueOf(id); - ComponentDto project = ComponentTesting - .newProjectDto("project-uuid-" + suffix) - .setKey("project-key-" + suffix); - RuleDto rule = RuleTesting.newDto(RuleKey.of("sonarqube", "rule-" + suffix)); - dbClient.ruleDao().insert(dbSession, rule); - IssueDto issue = IssueTesting.newDto(rule, project, project).setKee("issue-key-" + suffix); - dbClient.componentDao().insert(dbSession, project); - SnapshotDto snapshot = dbClient.snapshotDao().insert(dbSession, SnapshotTesting.createForProject(project)); - dbClient.issueDao().insert(dbSession, issue); - dbSession.commit(); - - return snapshot.getId(); - } - - private void insertNewProjectInIndexes(int id) throws Exception { - String suffix = String.valueOf(id); - ComponentDto project = ComponentTesting - .newProjectDto("project-uuid-" + suffix) - .setKey("project-key-" + suffix); - dbClient.componentDao().insert(dbSession, project); - dbSession.commit(); - - es.putDocuments(IssueIndexDefinition.INDEX, IssueIndexDefinition.TYPE_ISSUE, IssueTesting.newDoc("issue-key-" + suffix, project)); - SourceLineDoc sourceLineDoc = new SourceLineDoc() - .setProjectUuid(project.uuid()) - .setFileUuid(project.uuid()); - es.putDocuments(IssueIndexDefinition.INDEX, IssueIndexDefinition.TYPE_AUTHORIZATION, - ImmutableMap.of(IssueIndexDefinition.FIELD_AUTHORIZATION_PROJECT_UUID, project.uuid())); - - es.putDocuments(SourceLineIndexDefinition.INDEX, SourceLineIndexDefinition.TYPE, sourceLineDoc); - TestDoc testDoc = new TestDoc().setUuid("test-uuid-" + suffix).setProjectUuid(project.uuid()).setFileUuid(project.uuid()); - es.putDocuments(TestIndexDefinition.INDEX, TestIndexDefinition.TYPE, testDoc); - } -} diff --git a/server/sonar-server/src/test/java/org/sonar/server/project/ws/ProjectsWsTest.java b/server/sonar-server/src/test/java/org/sonar/server/project/ws/ProjectsWsTest.java index 3dbe27a988d..34d94846bd1 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/project/ws/ProjectsWsTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/project/ws/ProjectsWsTest.java @@ -40,7 +40,7 @@ public class ProjectsWsTest { @Before public void setUp() { ws = new WsTester(new ProjectsWs( - new DeleteAction(mock(ComponentCleanerService.class), mock(DbClient.class), mock(UserSession.class)), + new BulkDeleteAction(mock(ComponentCleanerService.class), mock(DbClient.class), mock(UserSession.class)), new GhostsAction(mock(DbClient.class), mock(UserSession.class)), new ProvisionedAction(mock(DbClient.class), mock(UserSession.class)) ));