From 645d874eda971f79a0927aa6370adb9489f148b7 Mon Sep 17 00:00:00 2001 From: Teryk Bellahsene Date: Wed, 3 Aug 2016 15:36:53 +0200 Subject: [PATCH] SONAR-7928 SONAR-4770 Create WS api/components/update_key --- .../java/it/component/ComponentsWsTest.java | 23 ++ .../java/it/serverSystem/ClusterTest.java | 3 +- .../server/component/ComponentService.java | 42 ++- .../component/ws/ComponentsWsModule.java | 3 +- .../server/component/ws/UpdateKeyAction.java | 103 +++++++ .../component/ComponentServiceTest.java | 289 +++++++----------- .../ComponentServiceUpdateKeyTest.java | 195 ++++++++++++ .../component/ws/ComponentsWsModuleTest.java | 2 +- .../component/ws/UpdateKeyActionTest.java | 158 ++++++++++ .../db/component/ResourceKeyUpdaterDao.java | 2 +- .../sonar/db/component/ComponentDbTester.java | 9 + .../component/ResourceKeyUpdaterDaoTest.java | 4 +- .../client/component/ComponentsService.java | 11 + .../component/ComponentsWsParameters.java | 14 +- .../ws/client/component/UpdateWsRequest.java | 86 ++++++ .../client/component/UpdateWsRequestTest.java | 49 +++ 16 files changed, 782 insertions(+), 211 deletions(-) create mode 100644 server/sonar-server/src/main/java/org/sonar/server/component/ws/UpdateKeyAction.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/component/ComponentServiceUpdateKeyTest.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/component/ws/UpdateKeyActionTest.java create mode 100644 sonar-ws/src/main/java/org/sonarqube/ws/client/component/UpdateWsRequest.java create mode 100644 sonar-ws/src/test/java/org/sonarqube/ws/client/component/UpdateWsRequestTest.java diff --git a/it/it-tests/src/test/java/it/component/ComponentsWsTest.java b/it/it-tests/src/test/java/it/component/ComponentsWsTest.java index 4f8593d2646..a137231dc8e 100644 --- a/it/it-tests/src/test/java/it/component/ComponentsWsTest.java +++ b/it/it-tests/src/test/java/it/component/ComponentsWsTest.java @@ -24,11 +24,15 @@ import com.sonar.orchestrator.build.SonarScanner; import it.Category4Suite; import org.junit.Before; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; import org.sonarqube.ws.WsComponents; +import org.sonarqube.ws.client.HttpException; import org.sonarqube.ws.client.WsClient; import org.sonarqube.ws.client.component.SearchWsRequest; import org.sonarqube.ws.client.component.ShowWsRequest; +import org.sonarqube.ws.client.component.UpdateWsRequest; import util.ItUtils; import static java.util.Collections.singletonList; @@ -39,6 +43,11 @@ public class ComponentsWsTest { @ClassRule public static final Orchestrator orchestrator = Category4Suite.ORCHESTRATOR; private static final String FILE_KEY = "sample:src/main/xoo/sample/Sample.xoo"; + private static final String PROJECT_KEY = "sample"; + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + WsClient wsClient; @Before @@ -66,4 +75,18 @@ public class ComponentsWsTest { assertThat(response).isNotNull(); assertThat(response.getComponents(0).getKey()).isEqualTo(FILE_KEY); } + + @Test + public void update_key() { + String newProjectKey = "another_project_key"; + WsComponents.Component project = wsClient.components().show(new ShowWsRequest().setKey(PROJECT_KEY)).getComponent(); + assertThat(project.getKey()).isEqualTo(PROJECT_KEY); + + wsClient.components().updateKey(UpdateWsRequest.builder() + .setKey(PROJECT_KEY) + .setNewKey(newProjectKey) + .build()); + + assertThat(wsClient.components().show(new ShowWsRequest().setId(project.getId())).getComponent().getKey()).isEqualTo(newProjectKey); + } } diff --git a/it/it-tests/src/test/java/it/serverSystem/ClusterTest.java b/it/it-tests/src/test/java/it/serverSystem/ClusterTest.java index 8a792854e7e..d7e61642980 100644 --- a/it/it-tests/src/test/java/it/serverSystem/ClusterTest.java +++ b/it/it-tests/src/test/java/it/serverSystem/ClusterTest.java @@ -36,6 +36,7 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; import org.apache.commons.io.FileUtils; +import org.apache.commons.lang.StringUtils; import org.junit.Test; import org.sonarqube.ws.Issues; import org.sonarqube.ws.client.rule.SearchWsRequest; @@ -140,7 +141,7 @@ public class ClusterTest { private static void expectLog(Orchestrator orchestrator, String expectedLog) throws IOException { File logFile = orchestrator.getServer().getLogs(); try (Stream lines = Files.lines(logFile.toPath())) { - assertThat(lines.anyMatch(s -> containsIgnoreCase(s, expectedLog))).isTrue(); + assertThat(lines.anyMatch(s -> StringUtils.containsIgnoreCase(s, expectedLog))).isTrue(); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ComponentService.java b/server/sonar-server/src/main/java/org/sonar/server/component/ComponentService.java index a6099d8d04c..ae1c6297f6c 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/component/ComponentService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/component/ComponentService.java @@ -22,6 +22,7 @@ package org.sonar.server.component; import com.google.common.base.Joiner; import com.google.common.base.Optional; import com.google.common.collect.Collections2; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import java.util.Collection; import java.util.Date; @@ -33,6 +34,7 @@ import javax.annotation.CheckForNull; import javax.annotation.Nullable; import org.sonar.api.ce.ComputeEngineSide; import org.sonar.api.i18n.I18n; +import org.sonar.api.resources.Qualifiers; import org.sonar.api.resources.Scopes; import org.sonar.api.server.ServerSide; import org.sonar.api.utils.System2; @@ -48,14 +50,16 @@ import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.user.UserSession; import static com.google.common.collect.Lists.newArrayList; +import static org.sonar.core.component.ComponentKeys.isValidModuleKey; import static org.sonar.db.component.ComponentDtoFunctions.toKey; +import static org.sonar.server.ws.WsUtils.checkRequest; @ServerSide @ComputeEngineSide public class ComponentService { + private static final Set ACCEPTED_QUALIFIERS = ImmutableSet.of(Qualifiers.PROJECT, Qualifiers.MODULE); private final DbClient dbClient; - private final I18n i18n; private final UserSession userSession; private final System2 system2; @@ -83,7 +87,7 @@ public class ComponentService { DbSession session = dbClient.openSession(false); try { Optional component = dbClient.componentDao().selectByKey(session, key); - return component.isPresent() ? component.get() : null; + return component.orNull(); } finally { session.close(); } @@ -108,19 +112,23 @@ public class ComponentService { } public void updateKey(String projectOrModuleKey, String newKey) { - DbSession session = dbClient.openSession(false); + DbSession dbSession = dbClient.openSession(false); try { - ComponentDto projectOrModule = getByKey(session, projectOrModuleKey); - userSession.checkComponentUuidPermission(UserRole.ADMIN, projectOrModule.projectUuid()); - dbClient.resourceKeyUpdaterDao().updateKey(projectOrModule.uuid(), newKey); - session.commit(); - - session.commit(); + updateKey(dbSession, projectOrModuleKey, newKey); } finally { - session.close(); + dbSession.close(); } } + public void updateKey(DbSession dbSession, String projectOrModuleKey, String newKey) { + ComponentDto component = componentFinder.getByKey(dbSession, projectOrModuleKey); + userSession.checkComponentUuidPermission(UserRole.ADMIN, component.projectUuid()); + checkIsProjectOrModule(component); + checkProjectOrModuleKeyFormat(newKey); + + dbClient.resourceKeyUpdaterDao().updateKey(component.uuid(), newKey); + } + public Map checkModuleKeysBeforeRenaming(String projectKey, String stringToReplace, String replacementString) { DbSession session = dbClient.openSession(false); try { @@ -271,10 +279,12 @@ public class ComponentService { } private void checkKeyFormat(String qualifier, String kee) { - if (!ComponentKeys.isValidModuleKey(kee)) { - throw new BadRequestException(formatMessage("Malformed key for %s: %s. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit.", - qualifier, kee)); - } + checkRequest(isValidModuleKey(kee), formatMessage("Malformed key for %s: %s. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit.", + qualifier, kee)); + } + + private static void checkProjectOrModuleKeyFormat(String key) { + checkRequest(isValidModuleKey(key), "Malformed key for '%s'. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit.", key); } private void checkBranchFormat(String qualifier, @Nullable String branch) { @@ -291,4 +301,8 @@ public class ComponentService { private ComponentDto getByKey(DbSession session, String key) { return componentFinder.getByKey(session, key); } + + private static void checkIsProjectOrModule(ComponentDto component) { + checkRequest(ACCEPTED_QUALIFIERS.contains(component.qualifier()), "Component updated must be a module or a key"); + } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentsWsModule.java b/server/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentsWsModule.java index ffcc59c5ca9..71f711d068b 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentsWsModule.java +++ b/server/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentsWsModule.java @@ -33,6 +33,7 @@ public class ComponentsWsModule extends Module { SearchAction.class, TreeAction.class, ShowAction.class, - SearchViewComponentsAction.class); + SearchViewComponentsAction.class, + UpdateKeyAction.class); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ws/UpdateKeyAction.java b/server/sonar-server/src/main/java/org/sonar/server/component/ws/UpdateKeyAction.java new file mode 100644 index 00000000000..28200a480bc --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/component/ws/UpdateKeyAction.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.component.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.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.component.ComponentDto; +import org.sonar.server.component.ComponentFinder; +import org.sonar.server.component.ComponentFinder.ParamNames; +import org.sonar.server.component.ComponentService; +import org.sonarqube.ws.client.component.UpdateWsRequest; + +import static org.sonar.core.util.Uuids.UUID_EXAMPLE_01; +import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_ID; +import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_KEY; +import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_NEW_KEY; + +public class UpdateKeyAction implements ComponentsWsAction { + private final DbClient dbClient; + private final ComponentFinder componentFinder; + private final ComponentService componentService; + + public UpdateKeyAction(DbClient dbClient, ComponentFinder componentFinder, ComponentService componentService) { + this.dbClient = dbClient; + this.componentFinder = componentFinder; + this.componentService = componentService; + } + + @Override + public void define(WebService.NewController context) { + WebService.NewAction action = context.createAction("update_key") + .setDescription("Update a project or module key and all its sub-components keys.
" + + "Either '%s' or '%s' must be provided, not both.
" + + "Requires one of the following permissions: " + + "
    " + + "
  • 'Administer System'
  • " + + "
  • 'Administer' rights on the specified project
  • " + + "
  • 'Browse' on the specified project
  • " + + "
", PARAM_ID, PARAM_KEY) + .setSince("6.1") + .setPost(true) + .setHandler(this); + + action.createParam(PARAM_ID) + .setDescription("Project or module id") + .setExampleValue(UUID_EXAMPLE_01); + + action.createParam(PARAM_KEY) + .setDescription("Project or module key") + .setExampleValue("my_old_project"); + + action.createParam(PARAM_NEW_KEY) + .setDescription("New component key") + .setRequired(true) + .setExampleValue("my_new_project"); + } + + @Override + public void handle(Request request, Response response) throws Exception { + doHandle(toWsRequest(request)); + response.noContent(); + } + + private void doHandle(UpdateWsRequest request) { + DbSession dbSession = dbClient.openSession(false); + try { + ComponentDto projectOrModule = componentFinder.getByUuidOrKey(dbSession, request.getId(), request.getKey(), ParamNames.ID_AND_KEY); + componentService.updateKey(dbSession, projectOrModule.key(), request.getNewKey()); + dbSession.commit(); + } finally { + dbClient.closeSession(dbSession); + } + } + + private static UpdateWsRequest toWsRequest(Request request) { + return UpdateWsRequest.builder() + .setId(request.param(PARAM_ID)) + .setKey(request.param(PARAM_KEY)) + .setNewKey(request.mandatoryParam(PARAM_NEW_KEY)) + .build(); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ComponentServiceTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ComponentServiceTest.java index 0aa8c3d09b8..45b104da4e0 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/component/ComponentServiceTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/component/ComponentServiceTest.java @@ -21,15 +21,12 @@ package org.sonar.server.component; import com.google.common.base.Optional; import java.util.Arrays; -import java.util.Locale; import java.util.Map; import org.assertj.core.api.Fail; import org.junit.Before; import org.junit.Rule; import org.junit.Test; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; -import org.sonar.api.i18n.I18n; +import org.junit.rules.ExpectedException; import org.sonar.api.resources.Qualifiers; import org.sonar.api.utils.System2; import org.sonar.api.web.UserRole; @@ -43,12 +40,12 @@ import org.sonar.db.component.ResourceIndexDao; import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.exceptions.NotFoundException; +import org.sonar.server.i18n.I18nRule; import org.sonar.server.tester.UserSessionRule; import static com.google.common.collect.Lists.newArrayList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.guava.api.Assertions.assertThat; -import static org.junit.Assert.fail; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doAnswer; @@ -57,138 +54,68 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.sonar.core.permission.GlobalPermissions.PROVISIONING; - public class ComponentServiceTest { - System2 system2 = System2.INSTANCE; - @Rule - public DbTester dbTester = DbTester.create(system2); - + public UserSessionRule userSession = UserSessionRule.standalone(); @Rule - public UserSessionRule userSessionRule = UserSessionRule.standalone(); + public ExpectedException expectedException = ExpectedException.none(); + @Rule + public DbTester dbTester = DbTester.create(System2.INSTANCE); DbClient dbClient = dbTester.getDbClient(); - DbSession session = dbTester.getSession(); - I18n i18n = mock(I18n.class); - ComponentService service; + DbSession dbSession = dbTester.getSession(); + + I18nRule i18n = new I18nRule(); + + ComponentService underTest; @Before public void setUp() { - when(i18n.message(Locale.getDefault(), "qualifier.TRK", "Project")).thenReturn("Project"); - service = new ComponentService(dbClient, i18n, userSessionRule, System2.INSTANCE, new ComponentFinder(dbClient)); + i18n.put("qualifier.TRK", "Project"); + + underTest = new ComponentService(dbClient, i18n, userSession, System2.INSTANCE, new ComponentFinder(dbClient)); } @Test public void get_by_key() { ComponentDto project = createProject(); - assertThat(service.getByKey(project.getKey())).isNotNull(); + assertThat(underTest.getByKey(project.getKey())).isNotNull(); } @Test public void get_nullable_by_key() { ComponentDto project = createProject(); - assertThat(service.getNullableByKey(project.getKey())).isNotNull(); - assertThat(service.getNullableByKey("unknown")).isNull(); + assertThat(underTest.getNullableByKey(project.getKey())).isNotNull(); + assertThat(underTest.getNullableByKey("unknown")).isNull(); } @Test public void get_by_uuid() { ComponentDto project = createProject(); - assertThat(service.getNonNullByUuid(project.uuid())).isNotNull(); + assertThat(underTest.getNonNullByUuid(project.uuid())).isNotNull(); } @Test public void get_nullable_by_uuid() { ComponentDto project = createProject(); - assertThat(service.getByUuid(project.uuid())).isPresent(); - assertThat(service.getByUuid("unknown")).isAbsent(); - } - - @Test - public void update_project_key() { - ComponentDto project = createProject(); - ComponentDto file = ComponentTesting.newFileDto(project).setKey("sample:root:src/File.xoo"); - dbClient.componentDao().insert(session, file); - - session.commit(); - - userSessionRule.login("john").addProjectUuidPermissions(UserRole.ADMIN, project.uuid()); - service.updateKey(project.key(), "sample2:root"); - session.commit(); - - // Check project key has been updated - assertThat(service.getNullableByKey(project.key())).isNull(); - assertThat(service.getNullableByKey("sample2:root")).isNotNull(); - - // Check file key has been updated - assertThat(service.getNullableByKey(file.key())).isNull(); - assertThat(service.getNullableByKey("sample2:root:src/File.xoo")).isNotNull(); - } - - @Test - public void update_module_key() { - ComponentDto project = createProject(); - ComponentDto module = ComponentTesting.newModuleDto(project).setKey("sample:root:module"); - dbClient.componentDao().insert(session, module); - - ComponentDto file = ComponentTesting.newFileDto(module).setKey("sample:root:module:src/File.xoo"); - dbClient.componentDao().insert(session, file); - - session.commit(); - - userSessionRule.login("john").addProjectUuidPermissions(UserRole.ADMIN, project.uuid()); - service.updateKey(module.key(), "sample:root2:module"); - session.commit(); - - // Project key has not changed - assertThat(service.getNullableByKey(project.key())).isNotNull(); - - // Check module key has been updated - assertThat(service.getNullableByKey(module.key())).isNull(); - assertThat(service.getNullableByKey("sample:root2:module")).isNotNull(); - - // Check file key has been updated - assertThat(service.getNullableByKey(file.key())).isNull(); - assertThat(service.getNullableByKey("sample:root2:module:src/File.xoo")).isNotNull(); - } - - @Test - public void update_provisioned_project_key() { - ComponentDto provisionedProject = ComponentTesting.newProjectDto().setKey("provisionedProject"); - dbClient.componentDao().insert(session, provisionedProject); - - session.commit(); - - userSessionRule.login("john").addProjectUuidPermissions(UserRole.ADMIN, provisionedProject.uuid()); - service.updateKey(provisionedProject.key(), "provisionedProject2"); - session.commit(); - - // Check project key has been updated - assertThat(service.getNullableByKey(provisionedProject.key())).isNull(); - assertThat(service.getNullableByKey("provisionedProject2")).isNotNull(); - } - - @Test(expected = ForbiddenException.class) - public void fail_to_update_project_key_without_admin_permission() { - ComponentDto project = createProject(); - userSessionRule.login("john").addProjectUuidPermissions(UserRole.USER, project.uuid()); - service.updateKey(project.key(), "sample2:root"); + assertThat(underTest.getByUuid(project.uuid())).isPresent(); + assertThat(underTest.getByUuid("unknown")).isAbsent(); } @Test public void check_module_keys_before_renaming() { ComponentDto project = createProject(); ComponentDto module = ComponentTesting.newModuleDto(project).setKey("sample:root:module"); - dbClient.componentDao().insert(session, module); + dbClient.componentDao().insert(dbSession, module); ComponentDto file = ComponentTesting.newFileDto(module).setKey("sample:root:module:src/File.xoo"); - dbClient.componentDao().insert(session, file); + dbClient.componentDao().insert(dbSession, file); - session.commit(); + dbSession.commit(); - userSessionRule.login("john").addProjectUuidPermissions(UserRole.ADMIN, project.uuid()); - Map result = service.checkModuleKeysBeforeRenaming(project.key(), "sample", "sample2"); + userSession.login("john").addProjectUuidPermissions(UserRole.ADMIN, project.uuid()); + Map result = underTest.checkModuleKeysBeforeRenaming(project.key(), "sample", "sample2"); assertThat(result).hasSize(2); assertThat(result.get("sample:root")).isEqualTo("sample2:root"); @@ -199,86 +126,91 @@ public class ComponentServiceTest { public void check_module_keys_before_renaming_return_duplicate_key() { ComponentDto project = createProject(); ComponentDto module = ComponentTesting.newModuleDto(project).setKey("sample:root:module"); - dbClient.componentDao().insert(session, module); + dbClient.componentDao().insert(dbSession, module); ComponentDto module2 = ComponentTesting.newModuleDto(project).setKey("foo:module"); - dbClient.componentDao().insert(session, module2); + dbClient.componentDao().insert(dbSession, module2); - session.commit(); + dbSession.commit(); - userSessionRule.login("john").addProjectUuidPermissions(UserRole.ADMIN, project.uuid()); - Map result = service.checkModuleKeysBeforeRenaming(project.key(), "sample:root", "foo"); + userSession.login("john").addProjectUuidPermissions(UserRole.ADMIN, project.uuid()); + Map result = underTest.checkModuleKeysBeforeRenaming(project.key(), "sample:root", "foo"); assertThat(result).hasSize(2); assertThat(result.get("sample:root")).isEqualTo("foo"); assertThat(result.get("sample:root:module")).isEqualTo("#duplicate_key#"); } - @Test(expected = ForbiddenException.class) + @Test public void fail_to_check_module_keys_before_renaming_without_admin_permission() { + expectedException.expect(ForbiddenException.class); + ComponentDto project = createProject(); - userSessionRule.login("john").addProjectUuidPermissions(UserRole.USER, project.uuid()); - service.checkModuleKeysBeforeRenaming(project.key(), "sample", "sample2"); + userSession.login("john").addProjectUuidPermissions(UserRole.USER, project.uuid()); + + underTest.checkModuleKeysBeforeRenaming(project.key(), "sample", "sample2"); } @Test public void bulk_update_project_key() { ComponentDto project = createProject(); ComponentDto module = ComponentTesting.newModuleDto(project).setKey("sample:root:module"); - dbClient.componentDao().insert(session, module); + dbClient.componentDao().insert(dbSession, module); ComponentDto file = ComponentTesting.newFileDto(module).setKey("sample:root:module:src/File.xoo"); - dbClient.componentDao().insert(session, file); + dbClient.componentDao().insert(dbSession, file); - session.commit(); + dbSession.commit(); - userSessionRule.login("john").addProjectUuidPermissions(UserRole.ADMIN, project.uuid()); - service.bulkUpdateKey(project.key(), "sample", "sample2"); - session.commit(); + userSession.login("john").addProjectUuidPermissions(UserRole.ADMIN, project.uuid()); + underTest.bulkUpdateKey(project.key(), "sample", "sample2"); + dbSession.commit(); // Check project key has been updated - assertThat(service.getNullableByKey(project.key())).isNull(); - assertThat(service.getNullableByKey("sample2:root")).isNotNull(); + assertThat(underTest.getNullableByKey(project.key())).isNull(); + assertThat(underTest.getNullableByKey("sample2:root")).isNotNull(); // Check module key has been updated - assertThat(service.getNullableByKey(module.key())).isNull(); - assertThat(service.getNullableByKey("sample2:root:module")).isNotNull(); + assertThat(underTest.getNullableByKey(module.key())).isNull(); + assertThat(underTest.getNullableByKey("sample2:root:module")).isNotNull(); // Check file key has been updated - assertThat(service.getNullableByKey(file.key())).isNull(); - assertThat(service.getNullableByKey("sample2:root:module:src/File.xoo")).isNotNull(); + assertThat(underTest.getNullableByKey(file.key())).isNull(); + assertThat(underTest.getNullableByKey("sample2:root:module:src/File.xoo")).isNotNull(); } @Test public void bulk_update_provisioned_project_key() { ComponentDto provisionedProject = ComponentTesting.newProjectDto().setKey("provisionedProject"); - dbClient.componentDao().insert(session, provisionedProject); + dbClient.componentDao().insert(dbSession, provisionedProject); - session.commit(); + dbSession.commit(); - userSessionRule.login("john").addProjectUuidPermissions(UserRole.ADMIN, provisionedProject.uuid()); - service.bulkUpdateKey(provisionedProject.key(), "provisionedProject", "provisionedProject2"); - session.commit(); + userSession.login("john").addProjectUuidPermissions(UserRole.ADMIN, provisionedProject.uuid()); + underTest.bulkUpdateKey(provisionedProject.key(), "provisionedProject", "provisionedProject2"); + dbSession.commit(); // Check project key has been updated - assertThat(service.getNullableByKey(provisionedProject.key())).isNull(); - assertThat(service.getNullableByKey("provisionedProject2")).isNotNull(); + assertThat(underTest.getNullableByKey(provisionedProject.key())).isNull(); + assertThat(underTest.getNullableByKey("provisionedProject2")).isNotNull(); } - @Test(expected = ForbiddenException.class) + @Test public void fail_to_bulk_update_project_key_without_admin_permission() { + expectedException.expect(ForbiddenException.class); + ComponentDto project = createProject(); - userSessionRule.login("john").addProjectPermissions(UserRole.USER, project.key()); - service.bulkUpdateKey("sample:root", "sample", "sample2"); + userSession.login("john").addProjectPermissions(UserRole.USER, project.key()); + underTest.bulkUpdateKey("sample:root", "sample", "sample2"); } @Test public void create_project() { - userSessionRule.login("john").setGlobalPermissions(PROVISIONING); + userSession.login("john").setGlobalPermissions(PROVISIONING); - String key = service.create(NewComponent.create("struts", "Struts project")).getKey(); + String key = underTest.create(NewComponent.create("struts", "Struts project")).getKey(); - ComponentDto project = service.getNullableByKey(key); + ComponentDto project = underTest.getNullableByKey(key); assertThat(project.key()).isEqualTo("struts"); assertThat(project.deprecatedKey()).isEqualTo("struts"); assertThat(project.uuid()).isNotNull(); @@ -294,22 +226,22 @@ public class ComponentServiceTest { @Test public void create_new_project_with_branch() { - userSessionRule.login("john").setGlobalPermissions(PROVISIONING); + userSession.login("john").setGlobalPermissions(PROVISIONING); - String key = service.create(NewComponent.create("struts", "Struts project").setBranch("origin/branch")).getKey(); + String key = underTest.create(NewComponent.create("struts", "Struts project").setBranch("origin/branch")).getKey(); - ComponentDto project = service.getNullableByKey(key); + ComponentDto project = underTest.getNullableByKey(key); assertThat(project.key()).isEqualTo("struts:origin/branch"); assertThat(project.deprecatedKey()).isEqualTo("struts:origin/branch"); } @Test public void create_view() { - userSessionRule.login("john").setGlobalPermissions(PROVISIONING); + userSession.login("john").setGlobalPermissions(PROVISIONING); - String key = service.create(NewComponent.create("all-project", "All Projects").setQualifier(Qualifiers.VIEW)).getKey(); + String key = underTest.create(NewComponent.create("all-project", "All Projects").setQualifier(Qualifiers.VIEW)).getKey(); - ComponentDto project = service.getNullableByKey(key); + ComponentDto project = underTest.getNullableByKey(key); assertThat(project.key()).isEqualTo("all-project"); assertThat(project.deprecatedKey()).isEqualTo("all-project"); assertThat(project.uuid()).isNotNull(); @@ -326,12 +258,12 @@ public class ComponentServiceTest { @Test public void create_developer() throws Exception { // No permission should be required to create a developer - userSessionRule.anonymous(); + userSession.anonymous(); - String key = service.createDeveloper(dbTester.getSession(), NewComponent.create("DEV:jon.name@mail.com", "John").setQualifier("DEV")).getKey(); + String key = underTest.createDeveloper(dbTester.getSession(), NewComponent.create("DEV:jon.name@mail.com", "John").setQualifier("DEV")).getKey(); dbTester.getSession().commit(); - ComponentDto dev = service.getNullableByKey(key); + ComponentDto dev = underTest.getNullableByKey(key); assertThat(dev.key()).isEqualTo("DEV:jon.name@mail.com"); assertThat(dev.deprecatedKey()).isEqualTo("DEV:jon.name@mail.com"); assertThat(dev.uuid()).isNotNull(); @@ -347,51 +279,41 @@ public class ComponentServiceTest { @Test public void fail_to_create_new_component_on_invalid_key() { - userSessionRule.login("john").setGlobalPermissions(PROVISIONING); + userSession.login("john").setGlobalPermissions(PROVISIONING); + expectedException.expect(BadRequestException.class); + expectedException.expectMessage("Malformed key for Project: struts?parent. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit."); - try { - service.create(NewComponent.create("struts?parent", "Struts project")); - fail(); - } catch (Exception e) { - assertThat(e).isInstanceOf(BadRequestException.class).hasMessage( - "Malformed key for Project: struts?parent. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit."); - } + underTest.create(NewComponent.create("struts?parent", "Struts project")); } @Test public void fail_to_create_new_component_on_invalid_branch() { - userSessionRule.login("john").setGlobalPermissions(PROVISIONING); + expectedException.expect(BadRequestException.class); + expectedException.expectMessage("Malformed branch for Project: origin?branch. Allowed characters are alphanumeric, '-', '_', '.' and '/', with at least one non-digit."); - try { - service.create(NewComponent.create("struts", "Struts project").setBranch("origin?branch")); - fail(); - } catch (Exception e) { - assertThat(e).isInstanceOf(BadRequestException.class).hasMessage( - "Malformed branch for Project: origin?branch. Allowed characters are alphanumeric, '-', '_', '.' and '/', with at least one non-digit."); - } + userSession.login("john").setGlobalPermissions(PROVISIONING); + + underTest.create(NewComponent.create("struts", "Struts project").setBranch("origin?branch")); } @Test public void fail_to_create_new_component_if_key_already_exists() { - userSessionRule.login("john").setGlobalPermissions(PROVISIONING); + expectedException.expect(BadRequestException.class); + expectedException.expectMessage("Could not create Project, key already exists: struts"); + userSession.login("john").setGlobalPermissions(PROVISIONING); ComponentDto project = ComponentTesting.newProjectDto().setKey("struts"); - dbClient.componentDao().insert(session, project); - session.commit(); + dbClient.componentDao().insert(dbSession, project); + dbSession.commit(); - try { - service.create(NewComponent.create("struts", "Struts project")); - fail(); - } catch (Exception e) { - assertThat(e).isInstanceOf(BadRequestException.class).hasMessage("Could not create Project, key already exists: struts"); - } + underTest.create(NewComponent.create("struts", "Struts project")); } @Test public void remove_duplicated_components_when_creating_project() throws Exception { String projectKey = "PROJECT_KEY"; - userSessionRule.login("john").setGlobalPermissions(PROVISIONING); + userSession.login("john").setGlobalPermissions(PROVISIONING); DbSession session = mock(DbSession.class); @@ -403,21 +325,18 @@ public class ComponentServiceTest { when(dbClient.componentDao()).thenReturn(componentDao); when(dbClient.componentIndexDao()).thenReturn(mock(ResourceIndexDao.class)); - doAnswer(new Answer() { - public Object answer(InvocationOnMock invocation) { - ((ComponentDto) invocation.getArguments()[1]).setId(1L); - return null; - } + doAnswer(invocation -> { + ((ComponentDto) invocation.getArguments()[1]).setId(1L); + return null; }).when(componentDao).insert(eq(session), any(ComponentDto.class)); when(componentDao.selectComponentsHavingSameKeyOrderedById(session, projectKey)).thenReturn(newArrayList( ComponentTesting.newProjectDto().setId(1L).setKey(projectKey), ComponentTesting.newProjectDto().setId(2L).setKey(projectKey), - ComponentTesting.newProjectDto().setId(3L).setKey(projectKey) - )); + ComponentTesting.newProjectDto().setId(3L).setKey(projectKey))); - service = new ComponentService(dbClient, i18n, userSessionRule, System2.INSTANCE, new ComponentFinder(dbClient)); - service.create(NewComponent.create(projectKey, projectKey)); + underTest = new ComponentService(dbClient, i18n, userSession, System2.INSTANCE, new ComponentFinder(dbClient)); + underTest.create(NewComponent.create(projectKey, projectKey)); verify(componentDao).delete(session, 2L); verify(componentDao).delete(session, 3L); @@ -428,15 +347,15 @@ public class ComponentServiceTest { ComponentDto project = createProject(); String moduleKey = "sample:root:module"; ComponentDto module = ComponentTesting.newModuleDto(project).setKey(moduleKey); - dbClient.componentDao().insert(session, module); + dbClient.componentDao().insert(dbSession, module); String fileKey = "sample:root:module:Foo.xoo"; ComponentDto file = ComponentTesting.newFileDto(module).setKey(fileKey); - dbClient.componentDao().insert(session, file); - session.commit(); + dbClient.componentDao().insert(dbSession, file); + dbSession.commit(); - assertThat(service.componentUuids(Arrays.asList(moduleKey, fileKey))).hasSize(2); - assertThat(service.componentUuids(null)).isEmpty(); - assertThat(service.componentUuids(Arrays.asList())).isEmpty(); + assertThat(underTest.componentUuids(Arrays.asList(moduleKey, fileKey))).hasSize(2); + assertThat(underTest.componentUuids(null)).isEmpty(); + assertThat(underTest.componentUuids(Arrays.asList())).isEmpty(); } @Test @@ -445,7 +364,7 @@ public class ComponentServiceTest { String fileKey = "sample:root:module:Foo.xoo"; try { - service.componentUuids(Arrays.asList(moduleKey, fileKey)); + underTest.componentUuids(Arrays.asList(moduleKey, fileKey)); Fail.fail("Should throw NotFoundException"); } catch (NotFoundException notFound) { assertThat(notFound.getMessage()).contains(moduleKey).contains(fileKey); @@ -457,13 +376,13 @@ public class ComponentServiceTest { String moduleKey = "sample:root:module"; String fileKey = "sample:root:module:Foo.xoo"; - assertThat(service.componentUuids(session, Arrays.asList(moduleKey, fileKey), true)).isEmpty(); + assertThat(underTest.componentUuids(dbSession, Arrays.asList(moduleKey, fileKey), true)).isEmpty(); } private ComponentDto createProject() { ComponentDto project = ComponentTesting.newProjectDto().setKey("sample:root"); - dbClient.componentDao().insert(session, project); - session.commit(); + dbClient.componentDao().insert(dbSession, project); + dbSession.commit(); return project; } diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ComponentServiceUpdateKeyTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ComponentServiceUpdateKeyTest.java new file mode 100644 index 00000000000..1fbe57b1474 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/component/ComponentServiceUpdateKeyTest.java @@ -0,0 +1,195 @@ +/* + * 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.component; + +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.core.permission.GlobalPermissions; +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.ComponentTesting; +import org.sonar.server.exceptions.BadRequestException; +import org.sonar.server.exceptions.ForbiddenException; +import org.sonar.server.i18n.I18nRule; +import org.sonar.server.tester.UserSessionRule; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.guava.api.Assertions.assertThat; +import static org.sonar.db.component.ComponentTesting.newFileDto; +import static org.sonar.db.component.ComponentTesting.newProjectDto; + +public class ComponentServiceUpdateKeyTest { + + @Rule + public UserSessionRule userSession = UserSessionRule.standalone(); + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Rule + public DbTester db = DbTester.create(System2.INSTANCE); + ComponentDbTester componentDb = new ComponentDbTester(db); + DbClient dbClient = db.getDbClient(); + DbSession dbSession = db.getSession(); + + I18nRule i18n = new I18nRule(); + + ComponentService underTest; + + @Before + public void setUp() { + i18n.put("qualifier.TRK", "Project"); + + underTest = new ComponentService(dbClient, i18n, userSession, System2.INSTANCE, new ComponentFinder(dbClient)); + } + + @Test + public void update_project_key() { + ComponentDto project = insertSampleRootProject(); + ComponentDto file = ComponentTesting.newFileDto(project).setKey("sample:root:src/File.xoo"); + dbClient.componentDao().insert(dbSession, file); + + dbSession.commit(); + + userSession.login("john").addProjectUuidPermissions(UserRole.ADMIN, project.uuid()); + underTest.updateKey(project.key(), "sample2:root"); + dbSession.commit(); + + // Check project key has been updated + assertThat(underTest.getNullableByKey(project.key())).isNull(); + assertThat(underTest.getNullableByKey("sample2:root")).isNotNull(); + + // Check file key has been updated + assertThat(underTest.getNullableByKey(file.key())).isNull(); + assertThat(underTest.getNullableByKey("sample2:root:src/File.xoo")).isNotNull(); + } + + @Test + public void update_module_key() { + ComponentDto project = insertSampleRootProject(); + ComponentDto module = ComponentTesting.newModuleDto(project).setKey("sample:root:module"); + dbClient.componentDao().insert(dbSession, module); + + ComponentDto file = ComponentTesting.newFileDto(module).setKey("sample:root:module:src/File.xoo"); + dbClient.componentDao().insert(dbSession, file); + + dbSession.commit(); + + userSession.login("john").addProjectUuidPermissions(UserRole.ADMIN, project.uuid()); + underTest.updateKey(module.key(), "sample:root2:module"); + dbSession.commit(); + + assertThat(dbClient.componentDao().selectByKey(dbSession, project.key())).isPresent(); + + assertComponentKeyHasBeenUpdated(module.key(), "sample:root2:module"); + assertComponentKeyHasBeenUpdated(file.key(), "sample:root2:module:src/File.xoo"); + } + + @Test + public void update_provisioned_project_key() { + ComponentDto provisionedProject = newProjectDto().setKey("provisionedProject"); + dbClient.componentDao().insert(dbSession, provisionedProject); + + dbSession.commit(); + + userSession.login("john").addProjectUuidPermissions(UserRole.ADMIN, provisionedProject.uuid()); + underTest.updateKey(provisionedProject.key(), "provisionedProject2"); + dbSession.commit(); + + assertComponentKeyHasBeenUpdated(provisionedProject.key(), "provisionedProject2"); + } + + @Test + public void fail_to_update_project_key_without_admin_permission() { + expectedException.expect(ForbiddenException.class); + + ComponentDto project = insertSampleRootProject(); + userSession.login("john").addProjectUuidPermissions(UserRole.USER, project.uuid()); + + underTest.updateKey(project.key(), "sample2:root"); + } + + @Test + public void fail_if_old_key_and_new_key_are_the_same() { + setGlobalAdminPermission(); + ComponentDto project = insertSampleRootProject(); + ComponentDto anotherProject = componentDb.insertProject(); + + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("Impossible to update key: a component with key \"" + anotherProject.key() + "\" already exists."); + + underTest.updateKey(dbSession, project.key(), anotherProject.key()); + } + + @Test + public void fail_if_new_key_is_empty() { + setGlobalAdminPermission(); + ComponentDto project = insertSampleRootProject(); + + expectedException.expect(BadRequestException.class); + expectedException.expectMessage("Malformed key for ''. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit."); + + underTest.updateKey(dbSession, project.key(), ""); + } + + @Test + public void fail_if_new_key_is_not_formatted_correctly() { + setGlobalAdminPermission(); + ComponentDto project = insertSampleRootProject(); + + expectedException.expect(BadRequestException.class); + expectedException.expectMessage("Malformed key for 'sample?root'. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit."); + + underTest.updateKey(dbSession, project.key(), "sample?root"); + } + + @Test + public void fail_if_update_is_not_on_module_or_project() { + setGlobalAdminPermission(); + ComponentDto project = insertSampleRootProject(); + ComponentDto file = componentDb.insertComponent(newFileDto(project)); + + expectedException.expect(BadRequestException.class); + expectedException.expectMessage("Component updated must be a module or a key"); + + underTest.updateKey(dbSession, file.key(), "file:key"); + } + + private void setGlobalAdminPermission() { + userSession.setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN); + } + + private ComponentDto insertSampleRootProject() { + return componentDb.insertComponent(newProjectDto().setKey("sample:root")); + } + + private void assertComponentKeyHasBeenUpdated(String oldKey, String newKey) { + assertThat(dbClient.componentDao().selectByKey(dbSession, oldKey)).isAbsent(); + assertThat(dbClient.componentDao().selectByKey(dbSession, newKey)).isPresent(); + } + +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentsWsModuleTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentsWsModuleTest.java index be80f98fc97..2602ff9c008 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentsWsModuleTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentsWsModuleTest.java @@ -29,6 +29,6 @@ public class ComponentsWsModuleTest { public void verify_count_of_added_components() { ComponentContainer container = new ComponentContainer(); new ComponentsWsModule().configure(container); - assertThat(container.size()).isEqualTo(8 + 2); + assertThat(container.size()).isEqualTo(9 + 2); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ws/UpdateKeyActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ws/UpdateKeyActionTest.java new file mode 100644 index 00000000000..9784f7ac77e --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/component/ws/UpdateKeyActionTest.java @@ -0,0 +1,158 @@ +/* + * 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.component.ws; + +import javax.annotation.Nullable; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.server.ws.WebService; +import org.sonar.api.server.ws.WebService.Param; +import org.sonar.api.utils.System2; +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.server.component.ComponentFinder; +import org.sonar.server.component.ComponentService; +import org.sonar.server.exceptions.NotFoundException; +import org.sonar.server.ws.TestRequest; +import org.sonar.server.ws.WsActionTester; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.sonar.db.component.ComponentTesting.newProjectDto; +import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_ID; +import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_KEY; +import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_NEW_KEY; + +public class UpdateKeyActionTest { + private static final String ANOTHER_KEY = "another_key"; + @Rule + public ExpectedException expectedException = ExpectedException.none(); + @Rule + public DbTester db = DbTester.create(System2.INSTANCE); + ComponentDbTester componentDb = new ComponentDbTester(db); + DbClient dbClient = db.getDbClient(); + + ComponentService componentService = mock(ComponentService.class); + + WsActionTester ws = new WsActionTester(new UpdateKeyAction(dbClient, new ComponentFinder(dbClient), componentService)); + + @Test + public void call_by_key() { + ComponentDto project = insertProject(); + + callByKey(project.key(), ANOTHER_KEY); + + assertCallComponentService(project.key(), ANOTHER_KEY); + } + + @Test + public void call_by_uuid() { + ComponentDto project = insertProject(); + + callByUuid(project.uuid(), ANOTHER_KEY); + + assertCallComponentService(project.key(), ANOTHER_KEY); + } + + @Test + public void fail_if_new_key_is_not_provided() { + expectedException.expect(IllegalArgumentException.class); + + ComponentDto project = insertProject(); + + callByKey(project.key(), null); + } + + @Test + public void fail_if_uuid_nor_key_provided() { + expectedException.expect(IllegalArgumentException.class); + + call(null, null, ANOTHER_KEY); + } + + @Test + public void fail_if_uuid_and_key_provided() { + expectedException.expect(IllegalArgumentException.class); + + ComponentDto project = insertProject(); + + call(project.uuid(), project.key(), ANOTHER_KEY); + } + + @Test + public void fail_if_project_does_not_exist() { + expectedException.expect(NotFoundException.class); + + callByUuid("UNKNOWN_UUID", ANOTHER_KEY); + } + + @Test + public void api_definition() { + WebService.Action definition = ws.getDef(); + + assertThat(definition.since()).isEqualTo("6.1"); + assertThat(definition.isPost()).isTrue(); + assertThat(definition.key()).isEqualTo("update_key"); + assertThat(definition.params()) + .hasSize(3) + .extracting(Param::key) + .containsOnlyOnce("id", "key", "newKey"); + } + + private void assertCallComponentService(@Nullable String oldKey, @Nullable String newKey) { + verify(componentService).updateKey(any(DbSession.class), eq(oldKey), eq(newKey)); + } + + private ComponentDto insertProject() { + return componentDb.insertComponent(newProjectDto()); + } + + private String callByUuid(@Nullable String uuid, @Nullable String newKey) { + return call(uuid, null, newKey); + } + + private String callByKey(@Nullable String key, @Nullable String newKey) { + return call(null, key, newKey); + } + + private String call(@Nullable String uuid, @Nullable String key, @Nullable String newKey) { + TestRequest request = ws.newRequest(); + + if (uuid != null) { + request.setParam(PARAM_ID, uuid); + } + if (key != null) { + request.setParam(PARAM_KEY, key); + } + if (newKey != null) { + request.setParam(PARAM_NEW_KEY, newKey); + } + + return request.execute().getInput(); + } +} diff --git a/sonar-db/src/main/java/org/sonar/db/component/ResourceKeyUpdaterDao.java b/sonar-db/src/main/java/org/sonar/db/component/ResourceKeyUpdaterDao.java index c675163fc76..4c0fb971c34 100644 --- a/sonar-db/src/main/java/org/sonar/db/component/ResourceKeyUpdaterDao.java +++ b/sonar-db/src/main/java/org/sonar/db/component/ResourceKeyUpdaterDao.java @@ -49,7 +49,7 @@ public class ResourceKeyUpdaterDao implements Dao { ResourceKeyUpdaterMapper mapper = session.getMapper(ResourceKeyUpdaterMapper.class); try { if (mapper.countResourceByKey(newKey) > 0) { - throw new IllegalStateException("Impossible to update key: a resource with \"" + newKey + "\" key already exists."); + throw new IllegalArgumentException("Impossible to update key: a component with key \"" + newKey + "\" already exists."); } // must SELECT first everything diff --git a/sonar-db/src/test/java/org/sonar/db/component/ComponentDbTester.java b/sonar-db/src/test/java/org/sonar/db/component/ComponentDbTester.java index ebee0a46a45..4e1fab7a094 100644 --- a/sonar-db/src/test/java/org/sonar/db/component/ComponentDbTester.java +++ b/sonar-db/src/test/java/org/sonar/db/component/ComponentDbTester.java @@ -26,6 +26,7 @@ import org.sonar.db.DbSession; import org.sonar.db.DbTester; import static java.util.Arrays.asList; +import static org.sonar.db.component.ComponentTesting.newProjectDto; import static org.sonar.db.component.SnapshotTesting.newAnalysis; public class ComponentDbTester { @@ -69,6 +70,14 @@ public class ComponentDbTester { return component; } + public ComponentDto insertProject() { + ComponentDto project = newProjectDto(); + dbClient.componentDao().insert(dbSession, project); + db.commit(); + + return project; + } + public void insertComponents(ComponentDto... components) { dbClient.componentDao().insert(dbSession, asList(components)); db.commit(); diff --git a/sonar-db/src/test/java/org/sonar/db/component/ResourceKeyUpdaterDaoTest.java b/sonar-db/src/test/java/org/sonar/db/component/ResourceKeyUpdaterDaoTest.java index 971aab92c4d..df673897e46 100644 --- a/sonar-db/src/test/java/org/sonar/db/component/ResourceKeyUpdaterDaoTest.java +++ b/sonar-db/src/test/java/org/sonar/db/component/ResourceKeyUpdaterDaoTest.java @@ -57,8 +57,8 @@ public class ResourceKeyUpdaterDaoTest { public void shouldNotUpdateKey() { db.prepareDbUnit(getClass(), "shared.xml"); - thrown.expect(IllegalStateException.class); - thrown.expectMessage("Impossible to update key: a resource with \"org.struts:struts-ui\" key already exists."); + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("Impossible to update key: a component with key \"org.struts:struts-ui\" already exists."); underTest.updateKey("B", "org.struts:struts-ui"); } diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/component/ComponentsService.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/component/ComponentsService.java index 24154b93a2e..ffcbe8715ed 100644 --- a/sonar-ws/src/main/java/org/sonarqube/ws/client/component/ComponentsService.java +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/component/ComponentsService.java @@ -25,6 +25,7 @@ import org.sonarqube.ws.WsComponents.ShowWsResponse; import org.sonarqube.ws.WsComponents.TreeWsResponse; import org.sonarqube.ws.client.BaseService; import org.sonarqube.ws.client.GetRequest; +import org.sonarqube.ws.client.PostRequest; import org.sonarqube.ws.client.WsConnector; import static org.sonarqube.ws.client.component.ComponentsWsParameters.ACTION_SHOW; @@ -33,6 +34,7 @@ import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_BAS import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_BASE_COMPONENT_KEY; import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_ID; import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_KEY; +import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_NEW_KEY; import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_QUALIFIERS; import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_STRATEGY; @@ -70,4 +72,13 @@ public class ComponentsService extends BaseService { .setParam(PARAM_KEY, request.getKey()); return call(get, ShowWsResponse.parser()); } + + public void updateKey(UpdateWsRequest request) { + PostRequest post = new PostRequest(path("update_key")) + .setParam(PARAM_ID, request.getId()) + .setParam(PARAM_KEY, request.getKey()) + .setParam(PARAM_NEW_KEY, request.getNewKey()); + + call(post); + } } diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/component/ComponentsWsParameters.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/component/ComponentsWsParameters.java index b85fea13bbd..456b46985a4 100644 --- a/sonar-ws/src/main/java/org/sonarqube/ws/client/component/ComponentsWsParameters.java +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/component/ComponentsWsParameters.java @@ -20,20 +20,22 @@ package org.sonarqube.ws.client.component; public class ComponentsWsParameters { - private ComponentsWsParameters() { - // static utility class - } - - //actions + // actions public static final String ACTION_TREE = "tree"; - public static final String ACTION_SHOW = "show"; + public static final String ACTION_SHOW = "show"; // parameters public static final String PARAM_QUALIFIERS = "qualifiers"; + public static final String PARAM_LANGUAGE = "language"; public static final String PARAM_BASE_COMPONENT_ID = "baseComponentId"; public static final String PARAM_BASE_COMPONENT_KEY = "baseComponentKey"; public static final String PARAM_STRATEGY = "strategy"; public static final String PARAM_ID = "id"; public static final String PARAM_KEY = "key"; + public static final String PARAM_NEW_KEY = "newKey"; + + private ComponentsWsParameters() { + // static utility class + } } diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/component/UpdateWsRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/component/UpdateWsRequest.java new file mode 100644 index 00000000000..b93d4892c93 --- /dev/null +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/component/UpdateWsRequest.java @@ -0,0 +1,86 @@ +/* + * 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.sonarqube.ws.client.component; + +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; + +import static com.google.common.base.Preconditions.checkArgument; + +public class UpdateWsRequest { + private final String id; + private final String key; + private final String newKey; + + public UpdateWsRequest(Builder builder) { + this.id = builder.id; + this.key = builder.key; + this.newKey = builder.newKey; + } + + @CheckForNull + public String getId() { + return id; + } + + @CheckForNull + public String getKey() { + return key; + } + + public String getNewKey() { + return newKey; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private String id; + private String key; + private String newKey; + + private Builder() { + // enforce method constructor + } + + public Builder setId(@Nullable String id) { + this.id = id; + return this; + } + + public Builder setKey(@Nullable String key) { + this.key = key; + return this; + } + + public Builder setNewKey(String newKey) { + this.newKey = newKey; + return this; + } + + public UpdateWsRequest build() { + checkArgument(newKey != null && !newKey.isEmpty(), "The new key must not be empty"); + return new UpdateWsRequest(this); + } + } +} diff --git a/sonar-ws/src/test/java/org/sonarqube/ws/client/component/UpdateWsRequestTest.java b/sonar-ws/src/test/java/org/sonarqube/ws/client/component/UpdateWsRequestTest.java new file mode 100644 index 00000000000..76d0a307c2f --- /dev/null +++ b/sonar-ws/src/test/java/org/sonarqube/ws/client/component/UpdateWsRequestTest.java @@ -0,0 +1,49 @@ +/* + * 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.sonarqube.ws.client.component; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class UpdateWsRequestTest { + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + UpdateWsRequest.Builder underTest = UpdateWsRequest.builder(); + + @Test + public void fail_if_new_key_is_null() { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("The new key must not be empty"); + + underTest.setNewKey(null).build(); + } + + @Test + public void fail_if_new_key_is_empty() { + expectedException.expect(IllegalArgumentException.class); + expectedException.expectMessage("The new key must not be empty"); + + underTest.setNewKey("").build(); + } +} -- 2.39.5