summaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorDuarte Meneses <duarte.meneses@sonarsource.com>2017-08-21 16:01:34 +0200
committerJanos Gyerik <janos.gyerik@sonarsource.com>2017-09-12 11:34:48 +0200
commitb6b7953d88cdcca4edabc6b40101500df93fad6d (patch)
tree6c8a3b629c520ea299ce28036c27969b3dbbdac7 /server
parent60b1fcc8948c4eeeb3e8843002fe0211994ddb7f (diff)
downloadsonarqube-b6b7953d88cdcca4edabc6b40101500df93fad6d.tar.gz
sonarqube-b6b7953d88cdcca4edabc6b40101500df93fad6d.zip
SONAR-9616 Ability to manually delete a non-main branch
Diffstat (limited to 'server')
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeCommands.java6
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeDao.java11
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java2
-rw-r--r--server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml4
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentTesting.java1
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java33
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/component/ComponentCleanerService.java8
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/component/ComponentFinder.java3
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/projectbranch/ws/BranchWsModule.java1
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/projectbranch/ws/DeleteAction.java105
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/component/ComponentCleanerServiceTest.java14
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/projectbranch/ws/BranchWsModuleTest.java2
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/projectbranch/ws/DeleteActionTest.java163
13 files changed, 331 insertions, 22 deletions
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 9cf7789d96b..91039c40d61 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
@@ -256,9 +256,9 @@ class PurgeCommands {
profiler.stop();
}
- void deleteBranches(String rootUuid) {
- profiler.start("deleteBranches (project_branches)");
- purgeMapper.deleteBranchesByProjectUuid(rootUuid);
+ void deleteBranch(String rootUuid) {
+ profiler.start("deleteBranch (project_branches)");
+ purgeMapper.deleteBranchByUuid(rootUuid);
session.commit();
profiler.stop();
}
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 4a9fc5d79b8..de29166e268 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
@@ -166,7 +166,14 @@ public class PurgeDao implements Dao {
return result;
}
- public void deleteRootComponent(DbSession session, String uuid) {
+ public void deleteBranch(DbSession session, String uuid) {
+ PurgeProfiler profiler = new PurgeProfiler();
+ PurgeMapper purgeMapper = mapper(session);
+ PurgeCommands purgeCommands = new PurgeCommands(session, profiler);
+ deleteRootComponent(uuid, purgeMapper, purgeCommands);
+ }
+
+ public void deleteProject(DbSession session, String uuid) {
PurgeProfiler profiler = new PurgeProfiler();
PurgeMapper purgeMapper = mapper(session);
PurgeCommands purgeCommands = new PurgeCommands(session, profiler);
@@ -196,7 +203,7 @@ public class PurgeDao implements Dao {
commands.deleteCeActivity(rootUuid);
commands.deleteCeQueue(rootUuid);
commands.deleteWebhookDeliveries(rootUuid);
- commands.deleteBranches(rootUuid);
+ commands.deleteBranch(rootUuid);
}
/**
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 86928616be0..6ea97e7aa28 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
@@ -94,5 +94,5 @@ public interface PurgeMapper {
void deleteWebhookDeliveriesByProjectUuid(@Param("projectUuid") String projectUuid);
- void deleteBranchesByProjectUuid(@Param("projectUuid") String projectUuid);
+ void deleteBranchByUuid(@Param("uuid") String uuid);
}
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 026672b7190..1b167408045 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
@@ -320,8 +320,8 @@
delete from webhook_deliveries where component_uuid=#{projectUuid,jdbcType=VARCHAR}
</delete>
- <delete id="deleteBranchesByProjectUuid">
- delete from project_branches where project_uuid=#{projectUuid,jdbcType=VARCHAR}
+ <delete id="deleteBranchByUuid">
+ delete from project_branches where uuid=#{uuid,jdbcType=VARCHAR}
</delete>
</mapper>
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentTesting.java b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentTesting.java
index d57256a2a4d..7695b2d77ea 100644
--- a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentTesting.java
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentTesting.java
@@ -221,7 +221,6 @@ public class ComponentTesting {
return new BranchDto()
.setKey(key)
.setUuid(branchComponent.uuid())
- // MainBranchProjectUuid will be null if it's a main branch
.setProjectUuid(projectUuid)
.setKeeType(BRANCH)
.setBranchType(branchType);
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java
index 92259a36b2a..39698bf8df4 100644
--- a/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java
@@ -178,7 +178,7 @@ public class PurgeDaoTest {
public void delete_project_and_associated_data() {
dbTester.prepareDbUnit(getClass(), "shouldDeleteProject.xml");
- underTest.deleteRootComponent(dbSession, "A");
+ underTest.deleteProject(dbSession, "A");
dbSession.commit();
assertThat(dbTester.countRowsOfTable("projects")).isZero();
@@ -189,6 +189,17 @@ public class PurgeDaoTest {
}
@Test
+ public void delete_branch_and_associated_data() {
+ ComponentDto project = dbTester.components().insertMainBranch();
+ ComponentDto branch = dbTester.components().insertProjectBranch(project);
+
+ underTest.deleteBranch(dbSession, project.uuid());
+ dbSession.commit();
+ assertThat(dbTester.countRowsOfTable("project_branches")).isEqualTo(1);
+ assertThat(dbTester.countRowsOfTable("projects")).isEqualTo(1);
+ }
+
+ @Test
public void delete_project_in_ce_activity_when_deleting_project() {
ComponentDto projectToBeDeleted = ComponentTesting.newPrivateProjectDto(dbTester.getDefaultOrganization());
ComponentDto anotherLivingProject = ComponentTesting.newPrivateProjectDto(dbTester.getDefaultOrganization());
@@ -199,7 +210,7 @@ public class PurgeDaoTest {
insertCeActivity(anotherLivingProject);
dbSession.commit();
- underTest.deleteRootComponent(dbSession, projectToBeDeleted.uuid());
+ underTest.deleteProject(dbSession, projectToBeDeleted.uuid());
dbSession.commit();
assertThat(dbTester.countRowsOfTable("ce_activity")).isEqualTo(1);
@@ -217,7 +228,7 @@ public class PurgeDaoTest {
dbClient.ceQueueDao().insert(dbSession, createCeQueue(anotherLivingProject, Status.PENDING));
dbSession.commit();
- underTest.deleteRootComponent(dbSession, projectToBeDeleted.uuid());
+ underTest.deleteProject(dbSession, projectToBeDeleted.uuid());
dbSession.commit();
assertThat(dbTester.countRowsOfTable("ce_queue")).isEqualTo(1);
@@ -249,7 +260,7 @@ public class PurgeDaoTest {
assertThat(dbTester.countRowsOfTable("issues")).isGreaterThan(issueCount);
assertThat(dbTester.countRowsOfTable("project_branches")).isGreaterThan(branchCount);
- underTest.deleteRootComponent(dbSession, projectToDelete.uuid());
+ underTest.deleteProject(dbSession, projectToDelete.uuid());
dbSession.commit();
assertThat(dbTester.countRowsOfTable("projects")).isEqualTo(projectEntryCount);
@@ -261,7 +272,7 @@ public class PurgeDaoTest {
public void delete_view_and_child() {
dbTester.prepareDbUnit(getClass(), "view_sub_view_and_tech_project.xml");
- underTest.deleteRootComponent(dbSession, "A");
+ underTest.deleteProject(dbSession, "A");
dbSession.commit();
assertThat(dbTester.countSql("select count(1) from projects where uuid='A'")).isZero();
assertThat(dbTester.countRowsOfTable("projects")).isZero();
@@ -275,7 +286,7 @@ public class PurgeDaoTest {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Couldn't find root component with uuid " + module.uuid());
- underTest.deleteRootComponent(dbSession, module.uuid());
+ underTest.deleteProject(dbSession, module.uuid());
}
@Test
@@ -286,7 +297,7 @@ public class PurgeDaoTest {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Couldn't find root component with uuid " + directory.uuid());
- underTest.deleteRootComponent(dbSession, directory.uuid());
+ underTest.deleteProject(dbSession, directory.uuid());
}
@Test
@@ -297,7 +308,7 @@ public class PurgeDaoTest {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Couldn't find root component with uuid " + file.uuid());
- underTest.deleteRootComponent(dbSession, file.uuid());
+ underTest.deleteProject(dbSession, file.uuid());
}
@Test
@@ -308,7 +319,7 @@ public class PurgeDaoTest {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Couldn't find root component with uuid " + subview.uuid());
- underTest.deleteRootComponent(dbSession, subview.uuid());
+ underTest.deleteProject(dbSession, subview.uuid());
}
@Test
@@ -316,7 +327,7 @@ public class PurgeDaoTest {
dbTester.prepareDbUnit(getClass(), "view_sub_view_and_tech_project.xml");
// view
- underTest.deleteRootComponent(dbSession, "A");
+ underTest.deleteProject(dbSession, "A");
dbSession.commit();
assertThat(dbTester.countSql("select count(1) from projects where uuid='A'")).isZero();
}
@@ -356,7 +367,7 @@ public class PurgeDaoTest {
dbClient.webhookDeliveryDao().insert(dbSession, newWebhookDeliveryDto().setComponentUuid(project.uuid()).setUuid("D1"));
dbClient.webhookDeliveryDao().insert(dbSession, newWebhookDeliveryDto().setComponentUuid("P2").setUuid("D2"));
- underTest.deleteRootComponent(dbSession, project.uuid());
+ underTest.deleteProject(dbSession, project.uuid());
assertThat(selectAllDeliveryUuids(dbTester, dbSession)).containsOnly("D2");
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ComponentCleanerService.java b/server/sonar-server/src/main/java/org/sonar/server/component/ComponentCleanerService.java
index 8c840c4749d..ffd9ce378c2 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/component/ComponentCleanerService.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/component/ComponentCleanerService.java
@@ -51,11 +51,17 @@ public class ComponentCleanerService {
}
}
+ public void deleteBranch(DbSession dbSession, ComponentDto branch) {
+ // TODO: detect if other branches depend on it?
+ dbClient.purgeDao().deleteBranch(dbSession, branch.uuid());
+ projectIndexers.commitAndIndex(dbSession, singletonList(branch), ProjectIndexer.Cause.PROJECT_DELETION);
+ }
+
public void delete(DbSession dbSession, ComponentDto project) {
if (hasNotProjectScope(project) || isNotDeletable(project)) {
throw new IllegalArgumentException("Only projects can be deleted");
}
- dbClient.purgeDao().deleteRootComponent(dbSession, project.uuid());
+ dbClient.purgeDao().deleteProject(dbSession, project.uuid());
projectIndexers.commitAndIndex(dbSession, singletonList(project), ProjectIndexer.Cause.PROJECT_DELETION);
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ComponentFinder.java b/server/sonar-server/src/main/java/org/sonar/server/component/ComponentFinder.java
index dc2f394bb11..a9f26f817a5 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/component/ComponentFinder.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/component/ComponentFinder.java
@@ -151,6 +151,9 @@ public class ComponentFinder {
return checkFoundWithOptional(organizationDto, "Organization with uuid '%s' not found", organizationUuid);
}
+ /**
+ * Components of the main branch won't be found
+ */
public ComponentDto getByKeyAndBranch(DbSession dbSession, String key, String branch) {
return checkComponent(dbClient.componentDao().selectByKeyAndBranch(dbSession, key, branch), "Component '%s' on branch '%s' not found", key, branch);
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/projectbranch/ws/BranchWsModule.java b/server/sonar-server/src/main/java/org/sonar/server/projectbranch/ws/BranchWsModule.java
index 0a0d4c2cd80..d47f197991c 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/projectbranch/ws/BranchWsModule.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/projectbranch/ws/BranchWsModule.java
@@ -27,6 +27,7 @@ public class BranchWsModule extends Module {
add(
ListAction.class,
ShowAction.class,
+ DeleteAction.class,
BranchesWs.class);
}
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/projectbranch/ws/DeleteAction.java b/server/sonar-server/src/main/java/org/sonar/server/projectbranch/ws/DeleteAction.java
new file mode 100644
index 00000000000..89e3b9bb631
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/projectbranch/ws/DeleteAction.java
@@ -0,0 +1,105 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.projectbranch.ws;
+
+import com.google.common.io.Resources;
+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.server.ws.WebService.NewController;
+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.component.BranchKeyType;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.server.component.ComponentCleanerService;
+import org.sonar.server.component.ComponentFinder;
+import org.sonar.server.user.UserSession;
+
+import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
+import static org.sonar.server.ws.WsUtils.checkFoundWithOptional;
+import static org.sonarqube.ws.client.projectbranches.ProjectBranchesParameters.ACTION_DELETE;
+import static org.sonarqube.ws.client.projectbranches.ProjectBranchesParameters.PARAM_BRANCH;
+import static org.sonarqube.ws.client.projectbranches.ProjectBranchesParameters.PARAM_PROJECT;;
+
+public class DeleteAction implements BranchWsAction {
+ private final DbClient dbClient;
+ private final UserSession userSession;
+ private final ComponentCleanerService componentCleanerService;
+ private final ComponentFinder componentFinder;
+
+ public DeleteAction(DbClient dbClient, ComponentFinder componentFinder, UserSession userSession, ComponentCleanerService componentCleanerService) {
+ this.dbClient = dbClient;
+ this.componentFinder = componentFinder;
+ this.userSession = userSession;
+ this.componentCleanerService = componentCleanerService;
+ }
+
+ @Override
+ public void define(NewController context) {
+ WebService.NewAction action = context.createAction(ACTION_DELETE)
+ .setSince("6.6")
+ .setDescription("Delete a non-main branch of a project. Requires permission to administer the project.")
+ .setResponseExample(Resources.getResource(getClass(), "list-example.json"))
+ .setInternal(true)
+ .setPost(true)
+ .setHandler(this);
+
+ action
+ .createParam(PARAM_PROJECT)
+ .setDescription("Project key")
+ .setExampleValue(KEY_PROJECT_EXAMPLE_001)
+ .setRequired(true);
+ action
+ .createParam(PARAM_BRANCH)
+ .setDescription("Name of the branch to delete. Can't be the main branch of the project.")
+ .setExampleValue("branch1")
+ .setRequired(true);
+ }
+
+ @Override
+ public void handle(Request request, Response response) throws Exception {
+ userSession.checkLoggedIn();
+ String projectKey = request.mandatoryParam(PARAM_PROJECT);
+ String branchKey = request.mandatoryParam(PARAM_BRANCH);
+
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ ComponentDto project = componentFinder.getRootComponentByUuidOrKey(dbSession, null, projectKey);
+ checkPermission(project);
+
+ BranchDto branch = checkFoundWithOptional(
+ dbClient.branchDao().selectByKey(dbSession, project.uuid(), BranchKeyType.BRANCH, branchKey),
+ "Branch '%s' not found for project '%s'", branchKey, projectKey);
+
+ if (branch.isMain()) {
+ throw new IllegalArgumentException("Only non-main branches can be deleted");
+ }
+ ComponentDto branchComponent = componentFinder.getByKeyAndBranch(dbSession, projectKey, branchKey);
+ componentCleanerService.deleteBranch(dbSession, branchComponent);
+ response.noContent();
+ }
+ }
+
+ private void checkPermission(ComponentDto project) {
+ userSession.hasComponentPermission(UserRole.ADMIN, project);
+ }
+
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ComponentCleanerServiceTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ComponentCleanerServiceTest.java
index 6e390230fe2..7a8256beb98 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/component/ComponentCleanerServiceTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/component/ComponentCleanerServiceTest.java
@@ -91,6 +91,20 @@ public class ComponentCleanerServiceTest {
}
@Test
+ public void delete_branch() {
+ DbData data1 = insertData(1);
+ DbData data2 = insertData(2);
+ DbData data3 = insertData(3);
+
+ underTest.deleteBranch(dbSession, data1.project);
+ dbSession.commit();
+
+ assertNotExists(data1);
+ assertExists(data2);
+ assertExists(data3);
+ }
+
+ @Test
public void fail_with_IAE_if_not_a_project() throws Exception {
mockResourceTypeAsValidProject();
ComponentDto project = ComponentTesting.newPrivateProjectDto(db.organizations().insert());
diff --git a/server/sonar-server/src/test/java/org/sonar/server/projectbranch/ws/BranchWsModuleTest.java b/server/sonar-server/src/test/java/org/sonar/server/projectbranch/ws/BranchWsModuleTest.java
index 39810004936..2ecb8a885a2 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/projectbranch/ws/BranchWsModuleTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/projectbranch/ws/BranchWsModuleTest.java
@@ -30,6 +30,6 @@ public class BranchWsModuleTest {
public void verify_count_of_added_components() {
ComponentContainer container = new ComponentContainer();
new BranchWsModule().configure(container);
- assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 3);
+ assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 4);
}
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/projectbranch/ws/DeleteActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/projectbranch/ws/DeleteActionTest.java
new file mode 100644
index 00000000000..b467725bb7a
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/projectbranch/ws/DeleteActionTest.java
@@ -0,0 +1,163 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.projectbranch.ws;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.mockito.ArgumentCaptor;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.api.utils.System2;
+import org.sonar.api.web.UserRole;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.ComponentTesting;
+import org.sonar.server.component.ComponentCleanerService;
+import org.sonar.server.component.ComponentFinder;
+import org.sonar.server.component.TestComponentFinder;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.WsActionTester;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+public class DeleteActionTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Rule
+ public DbTester db = DbTester.create(System2.INSTANCE);
+
+ private ComponentCleanerService componentCleanerService = mock(ComponentCleanerService.class);
+ private ComponentFinder componentFinder = TestComponentFinder.from(db);
+
+ @Rule
+ public UserSessionRule userSession = UserSessionRule.standalone();
+
+ public WsActionTester tester = new WsActionTester(new DeleteAction(db.getDbClient(), componentFinder, userSession, componentCleanerService));
+
+ @Test
+ public void test_definition() {
+ WebService.Action definition = tester.getDef();
+ assertThat(definition.key()).isEqualTo("delete");
+ assertThat(definition.isPost()).isTrue();
+ assertThat(definition.isInternal()).isTrue();
+ assertThat(definition.params()).extracting(WebService.Param::key).containsExactlyInAnyOrder("project", "branch");
+ assertThat(definition.since()).isEqualTo("6.6");
+ }
+
+ @Test
+ public void fail_if_missing_project_parameter() {
+ userSession.logIn();
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("The 'project' parameter is missing");
+
+ tester.newRequest().execute();
+ }
+
+ @Test
+ public void fail_if_missing_branch_parameter() {
+ userSession.logIn();
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("The 'branch' parameter is missing");
+
+ tester.newRequest().setParam("project", "projectName").execute();
+ }
+
+ @Test
+ public void fail_if_not_logged_in() {
+ expectedException.expect(UnauthorizedException.class);
+ expectedException.expectMessage("Authentication is required");
+
+ tester.newRequest().execute();
+ }
+
+ public void fail_branch_does_not_exist() {
+ ComponentDto project = db.components().insertPrivateProject();
+ ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(project));
+ userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
+
+ expectedException.expect(NotFoundException.class);
+ expectedException.expectMessage("Branch 'branch1' not found");
+
+ tester.newRequest()
+ .setParam("project", file.getDbKey())
+ .setParam("branch", "branch1")
+ .execute();
+ }
+
+ @Test
+ public void fail_if_project_does_not_exist() {
+ userSession.logIn();
+
+ expectedException.expect(NotFoundException.class);
+ expectedException.expectMessage("Project key 'foo' not found");
+
+ tester.newRequest()
+ .setParam("project", "foo")
+ .setParam("branch", "branch1")
+ .execute();
+ }
+
+ @Test
+ public void fail_if_branch_is_main() {
+ ComponentDto project = db.components().insertMainBranch();
+ 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
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Only non-main branches can be deleted");
+
+ tester.newRequest()
+ .setParam("project", project.getKey())
+ .setParam("branch", "main")
+ .execute();
+ }
+
+ @Test
+ public void delete_branch() {
+
+ ComponentDto project = db.components().insertMainBranch();
+ ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("branch1"));
+
+ userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
+
+ tester.newRequest()
+ .setParam("project", project.getKey())
+ .setParam("branch", "branch1")
+ .execute();
+ verifyDeletedKey(branch.getDbKey());
+ }
+
+ private void verifyDeletedKey(String key) {
+ ArgumentCaptor<ComponentDto> argument = ArgumentCaptor.forClass(ComponentDto.class);
+ verify(componentCleanerService).deleteBranch(any(DbSession.class), argument.capture());
+ assertThat(argument.getValue().getDbKey()).isEqualTo(key);
+ }
+
+}