aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-server
diff options
context:
space:
mode:
authorStas Vilchik <vilchiks@gmail.com>2016-07-27 11:12:30 +0200
committerStas Vilchik <vilchiks@gmail.com>2016-07-28 17:31:14 +0200
commit7d256b42cf12089fc468b99c0a44204f2879d87b (patch)
treea1fe6ef26e826863558c1876f517c52085c2dcf1 /server/sonar-server
parent1d702b6b507bd7c346381cf371d16fd8c556b99e (diff)
downloadsonarqube-7d256b42cf12089fc468b99c0a44204f2879d87b.tar.gz
sonarqube-7d256b42cf12089fc468b99c0a44204f2879d87b.zip
SONAR-7927 Create WS to delete project link
Diffstat (limited to 'server/sonar-server')
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/projectlink/ws/DeleteAction.java103
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/projectlink/ws/ProjectLinksModule.java3
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/CreateActionTest.java2
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/DeleteActionTest.java217
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/ProjectLinksWsModuleTest.java2
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/ProjectLinksWsTest.java15
6 files changed, 337 insertions, 5 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/projectlink/ws/DeleteAction.java b/server/sonar-server/src/main/java/org/sonar/server/projectlink/ws/DeleteAction.java
new file mode 100644
index 00000000000..2adce3cfd83
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/projectlink/ws/DeleteAction.java
@@ -0,0 +1,103 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.projectlink.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.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.component.ComponentLinkDto;
+import org.sonar.server.user.UserSession;
+import org.sonar.server.ws.WsUtils;
+import org.sonarqube.ws.client.projectlinks.DeleteWsRequest;
+
+import static org.sonar.db.component.ComponentLinkDto.PROVIDED_TYPES;
+import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException;
+import static org.sonarqube.ws.client.projectlinks.ProjectLinksWsParameters.ACTION_DELETE;
+import static org.sonarqube.ws.client.projectlinks.ProjectLinksWsParameters.PARAM_ID;
+
+public class DeleteAction implements ProjectLinksWsAction {
+ private final DbClient dbClient;
+ private final UserSession userSession;
+
+ public DeleteAction(DbClient dbClient, UserSession userSession) {
+ this.dbClient = dbClient;
+ this.userSession = userSession;
+ }
+
+ @Override
+ public void define(WebService.NewController context) {
+ WebService.NewAction action = context.createAction(ACTION_DELETE)
+ .setDescription("Delete existing project link.<br>" +
+ "Requires 'Administer' permission on the specified project, " +
+ "or global 'Administer' permission.")
+ .setHandler(this)
+ .setPost(true)
+ .setSince("6.1");
+
+ action.createParam(PARAM_ID)
+ .setRequired(true)
+ .setDescription("Link id")
+ .setExampleValue("17");
+ }
+
+ @Override
+ public void handle(Request request, Response response) throws Exception {
+ DeleteWsRequest deleteWsRequest = toDeleteWsRequest(request);
+ doHandle(deleteWsRequest);
+ response.noContent();
+ }
+
+ private static DeleteWsRequest toDeleteWsRequest(Request request) {
+ return new DeleteWsRequest()
+ .setId(request.mandatoryParamAsLong(PARAM_ID));
+ }
+
+ private void doHandle(DeleteWsRequest request) {
+ DbSession dbSession = dbClient.openSession(false);
+ try {
+ long id = request.getId();
+ ComponentLinkDto link = dbClient.componentLinkDao().selectById(dbSession, id);
+
+ link = WsUtils.checkFound(link, "Link with id '%s' not found", id);
+ checkPermissions(link.getComponentUuid());
+ checkNotProvided(link);
+
+ dbClient.componentLinkDao().delete(dbSession, link.getId());
+ dbSession.commit();
+ } finally {
+ dbClient.closeSession(dbSession);
+ }
+ }
+
+ private static void checkNotProvided(ComponentLinkDto link) {
+ String type = link.getType();
+ boolean isProvided = type != null && PROVIDED_TYPES.contains(type);
+ WsUtils.checkRequest(!isProvided, "Provided link cannot be deleted.");
+ }
+
+ private void checkPermissions(String projectUuid) {
+ if (!userSession.hasComponentUuidPermission(UserRole.ADMIN, projectUuid)) {
+ throw insufficientPrivilegesException();
+ }
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/projectlink/ws/ProjectLinksModule.java b/server/sonar-server/src/main/java/org/sonar/server/projectlink/ws/ProjectLinksModule.java
index 34bfa5a358d..76b59a9aa5c 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/projectlink/ws/ProjectLinksModule.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/projectlink/ws/ProjectLinksModule.java
@@ -29,7 +29,8 @@ public class ProjectLinksModule extends Module {
ProjectLinksWs.class,
// actions
SearchAction.class,
- CreateAction.class);
+ CreateAction.class,
+ DeleteAction.class);
}
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/CreateActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/CreateActionTest.java
index e68caa5acde..14d88957dc2 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/CreateActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/CreateActionTest.java
@@ -198,7 +198,7 @@ public class CreateActionTest {
WsProjectLinks.CreateWsResponse response = WsProjectLinks.CreateWsResponse.parseFrom(responseStream);
- String newId = response.getLink().getId();
+ long newId = Long.valueOf(response.getLink().getId());
ComponentLinkDto link = dbClient.componentLinkDao().selectById(dbSession, newId);
assertThat(link.getName()).isEqualTo("Custom");
diff --git a/server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/DeleteActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/DeleteActionTest.java
new file mode 100644
index 00000000000..db5aa9a44b4
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/DeleteActionTest.java
@@ -0,0 +1,217 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.projectlink.ws;
+
+import java.io.IOException;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.utils.System2;
+import org.sonar.api.web.UserRole;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.ComponentDbTester;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.ComponentLinkDto;
+import org.sonar.server.exceptions.BadRequestException;
+import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.TestResponse;
+import org.sonar.server.ws.WsActionTester;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN;
+import static org.sonar.core.util.Uuids.UUID_EXAMPLE_01;
+import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
+import static org.sonarqube.ws.client.projectlinks.ProjectLinksWsParameters.PARAM_ID;
+
+public class DeleteActionTest {
+
+ private final String PROJECT_KEY = KEY_PROJECT_EXAMPLE_001;
+ private final String PROJECT_UUID = UUID_EXAMPLE_01;
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Rule
+ public UserSessionRule userSession = UserSessionRule.standalone();
+
+ @Rule
+ public DbTester db = DbTester.create(System2.INSTANCE);
+ DbClient dbClient = db.getDbClient();
+ DbSession dbSession = db.getSession();
+ ComponentDbTester componentDb = new ComponentDbTester(db);
+
+ WsActionTester ws;
+
+ DeleteAction underTest;
+
+ @Before
+ public void setUp() {
+ underTest = new DeleteAction(dbClient, userSession);
+ ws = new WsActionTester(underTest);
+
+ userSession.login("login").setGlobalPermissions(SYSTEM_ADMIN);
+ }
+
+ @Test
+ public void no_response() {
+ ComponentDto project = insertProject();
+ ComponentLinkDto link = insertCustomLink(project.uuid());
+
+ TestResponse response = deleteLink(link.getId());
+
+ assertThat(response.getStatus()).isEqualTo(204);
+ assertThat(response.getInput()).isEmpty();
+ }
+
+ @Test
+ public void actual_removal() {
+ ComponentDto project = insertProject();
+ ComponentLinkDto link = insertCustomLink(project.uuid());
+ long id = link.getId();
+
+ deleteLink(id);
+ assertLinkIsDeleted(id);
+ }
+
+ @Test
+ public void project_admin() throws IOException {
+ userSession.login("login");
+
+ ComponentDto project = insertProject();
+ userSession.addProjectUuidPermissions(UserRole.ADMIN, project.uuid());
+
+ ComponentLinkDto link = insertCustomLink(project.uuid());
+ long id = link.getId();
+
+ deleteLink(id);
+ assertLinkIsDeleted(id);
+ }
+
+ @Test
+ public void keep_links_of_another_project() {
+ ComponentDto project1 = insertProject();
+ ComponentDto project2 = insertProject("another", "abcd");
+ ComponentLinkDto customLink1 = insertCustomLink(project1.uuid());
+ ComponentLinkDto customLink2 = insertCustomLink(project2.uuid());
+ Long id1 = customLink1.getId();
+ Long id2 = customLink2.getId();
+
+ deleteLink(id1);
+ assertLinkIsDeleted(id1);
+ assertLinkIsNotDeleted(id2);
+ }
+
+ @Test
+ public void fail_when_delete_provided_link() {
+ ComponentDto project = insertProject();
+ ComponentLinkDto link = insertHomepageLink(project.uuid());
+
+ expectedException.expect(BadRequestException.class);
+ deleteLink(link.getId());
+ }
+
+ @Test
+ public void fail_when_no_link() {
+ expectedException.expect(NotFoundException.class);
+ deleteLink("175");
+ }
+
+ @Test
+ public void fail_if_anonymous() {
+ userSession.anonymous();
+
+ ComponentDto project = insertProject();
+ ComponentLinkDto link = insertCustomLink(project.uuid());
+
+ expectedException.expect(ForbiddenException.class);
+ deleteLink(link.getId());
+ }
+
+ @Test
+ public void fail_if_not_project_admin() {
+ userSession.login("login");
+
+ ComponentDto project = insertProject();
+ ComponentLinkDto link = insertCustomLink(project.uuid());
+
+ expectedException.expect(ForbiddenException.class);
+ deleteLink(link.getId());
+ }
+
+ private ComponentDto insertProject(String projectKey, String projectUuid) {
+ return componentDb.insertComponent(new ComponentDto()
+ .setUuid(projectUuid)
+ .setKey(projectKey)
+ .setUuidPath("")
+ .setRootUuid(""));
+ }
+
+ private ComponentDto insertProject() {
+ return insertProject(PROJECT_KEY, PROJECT_UUID);
+ }
+
+ private void insertLink(ComponentLinkDto linkDto) {
+ dbClient.componentLinkDao().insert(dbSession, linkDto);
+ dbSession.commit();
+ }
+
+ private ComponentLinkDto insertHomepageLink(String projectUuid) {
+ ComponentLinkDto link = new ComponentLinkDto()
+ .setComponentUuid(projectUuid)
+ .setName("Homepage")
+ .setType("homepage")
+ .setHref("http://example.org");
+ insertLink(link);
+ return link;
+ }
+
+ private ComponentLinkDto insertCustomLink(String projectUuid) {
+ ComponentLinkDto link = new ComponentLinkDto()
+ .setComponentUuid(projectUuid)
+ .setName("Custom")
+ .setHref("http://example.org/custom");
+ insertLink(link);
+ return link;
+ }
+
+ private TestResponse deleteLink(String id) {
+ return ws.newRequest()
+ .setMethod("POST")
+ .setParam(PARAM_ID, id)
+ .execute();
+ }
+
+ private TestResponse deleteLink(Long id) {
+ return deleteLink(String.valueOf(id));
+ }
+
+ private void assertLinkIsDeleted(Long id) {
+ assertThat(dbClient.componentLinkDao().selectById(dbSession, id)).isNull();
+ }
+
+ private void assertLinkIsNotDeleted(Long id) {
+ assertThat(dbClient.componentLinkDao().selectById(dbSession, id)).isNotNull();
+ }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/ProjectLinksWsModuleTest.java b/server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/ProjectLinksWsModuleTest.java
index e63a38e15f3..e0aa5d20be4 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/ProjectLinksWsModuleTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/ProjectLinksWsModuleTest.java
@@ -30,6 +30,6 @@ public class ProjectLinksWsModuleTest {
public void verify_count_of_added_components() {
ComponentContainer container = new ComponentContainer();
new ProjectLinksModule().configure(container);
- assertThat(container.size()).isEqualTo(2 + 3);
+ assertThat(container.size()).isEqualTo(2 + 4);
}
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/ProjectLinksWsTest.java b/server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/ProjectLinksWsTest.java
index edafecf8c89..ffcd6ed0296 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/ProjectLinksWsTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/ProjectLinksWsTest.java
@@ -42,7 +42,8 @@ public class ProjectLinksWsTest {
public void setUp() {
WsTester tester = new WsTester(new ProjectLinksWs(
new SearchAction(mock(DbClient.class), userSessionRule, mock(ComponentFinder.class)),
- new CreateAction(mock(DbClient.class), userSessionRule, mock(ComponentFinder.class))
+ new CreateAction(mock(DbClient.class), userSessionRule, mock(ComponentFinder.class)),
+ new DeleteAction(mock(DbClient.class), userSessionRule)
));
controller = tester.controller("api/project_links");
}
@@ -52,7 +53,7 @@ public class ProjectLinksWsTest {
assertThat(controller).isNotNull();
assertThat(controller.description()).isNotEmpty();
assertThat(controller.since()).isEqualTo("6.1");
- assertThat(controller.actions()).hasSize(2);
+ assertThat(controller.actions()).hasSize(3);
}
@Test
@@ -74,4 +75,14 @@ public class ProjectLinksWsTest {
assertThat(action.responseExampleAsString()).isNotEmpty();
assertThat(action.params()).hasSize(4);
}
+
+ @Test
+ public void define_delete_action() {
+ WebService.Action action = controller.action("delete");
+ assertThat(action).isNotNull();
+ assertThat(action.isPost()).isTrue();
+ assertThat(action.handler()).isNotNull();
+ assertThat(action.responseExample()).isNull();
+ assertThat(action.params()).hasSize(1);
+ }
}