diff options
author | Stas Vilchik <vilchiks@gmail.com> | 2016-07-27 11:12:30 +0200 |
---|---|---|
committer | Stas Vilchik <vilchiks@gmail.com> | 2016-07-28 17:31:14 +0200 |
commit | 7d256b42cf12089fc468b99c0a44204f2879d87b (patch) | |
tree | a1fe6ef26e826863558c1876f517c52085c2dcf1 /server/sonar-server | |
parent | 1d702b6b507bd7c346381cf371d16fd8c556b99e (diff) | |
download | sonarqube-7d256b42cf12089fc468b99c0a44204f2879d87b.tar.gz sonarqube-7d256b42cf12089fc468b99c0a44204f2879d87b.zip |
SONAR-7927 Create WS to delete project link
Diffstat (limited to 'server/sonar-server')
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); + } } |