]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-8773 Move WS update_key and bulk_update_key to api/projects
authorTeryk Bellahsene <teryk.bellahsene@sonarsource.com>
Thu, 16 Feb 2017 16:10:48 +0000 (17:10 +0100)
committerTeryk Bellahsene <teryk.bellahsene@sonarsource.com>
Wed, 22 Feb 2017 16:53:38 +0000 (17:53 +0100)
30 files changed:
it/it-tests/src/test/java/it/Category4Suite.java
it/it-tests/src/test/java/it/component/ComponentsWsTest.java
it/it-tests/src/test/java/it/component/ProjectSearchTest.java [deleted file]
it/it-tests/src/test/java/it/component/ProjectsWsTest.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/component/ws/BulkUpdateKeyAction.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentsWsModule.java
server/sonar-server/src/main/java/org/sonar/server/component/ws/UpdateKeyAction.java [deleted file]
server/sonar-server/src/main/java/org/sonar/server/project/ws/BulkUpdateKeyAction.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/project/ws/ProjectsWsModule.java
server/sonar-server/src/main/java/org/sonar/server/project/ws/UpdateKeyAction.java [new file with mode: 0644]
server/sonar-server/src/main/resources/org/sonar/server/component/ws/bulk_update_key-example.json [deleted file]
server/sonar-server/src/main/resources/org/sonar/server/project/ws/bulk_update_key-example.json [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/component/ws/BulkUpdateKeyActionTest.java [deleted file]
server/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentsWsModuleTest.java
server/sonar-server/src/test/java/org/sonar/server/component/ws/UpdateKeyActionTest.java [deleted file]
server/sonar-server/src/test/java/org/sonar/server/project/ws/BulkUpdateKeyActionTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/project/ws/ProjectsWsModuleTest.java
server/sonar-server/src/test/java/org/sonar/server/project/ws/UpdateKeyActionTest.java [new file with mode: 0644]
sonar-ws/src/main/java/org/sonarqube/ws/client/component/BulkUpdateWsRequest.java [deleted file]
sonar-ws/src/main/java/org/sonarqube/ws/client/component/ComponentsService.java
sonar-ws/src/main/java/org/sonarqube/ws/client/component/ComponentsWsParameters.java
sonar-ws/src/main/java/org/sonarqube/ws/client/component/UpdateWsRequest.java [deleted file]
sonar-ws/src/main/java/org/sonarqube/ws/client/project/BulkUpdateKeyWsRequest.java [new file with mode: 0644]
sonar-ws/src/main/java/org/sonarqube/ws/client/project/ProjectsService.java
sonar-ws/src/main/java/org/sonarqube/ws/client/project/ProjectsWsParameters.java
sonar-ws/src/main/java/org/sonarqube/ws/client/project/UpdateKeyWsRequest.java [new file with mode: 0644]
sonar-ws/src/main/protobuf/ws-components.proto
sonar-ws/src/main/protobuf/ws-projects.proto
sonar-ws/src/test/java/org/sonarqube/ws/client/component/UpdateWsRequestTest.java [deleted file]
sonar-ws/src/test/java/org/sonarqube/ws/client/project/UpdateKeyWsRequestTest.java [new file with mode: 0644]

index 101287d0099cdc8ac491942a2077f7cdd536c249..c2705a975895f731cb81915628247a99759d6788 100644 (file)
@@ -23,7 +23,7 @@ import com.sonar.orchestrator.Orchestrator;
 import it.analysisExclusion.FileExclusionsTest;
 import it.analysisExclusion.IssueExclusionsTest;
 import it.component.ComponentsWsTest;
-import it.component.ProjectSearchTest;
+import it.component.ProjectsWsTest;
 import it.dbCleaner.PurgeTest;
 import it.duplication.CrossProjectDuplicationsOnRemoveFileTest;
 import it.duplication.CrossProjectDuplicationsTest;
@@ -69,7 +69,7 @@ import static util.ItUtils.xooPlugin;
   BaseIdentityProviderTest.class,
   OAuth2IdentityProviderTest.class,
   // component search
-  ProjectSearchTest.class,
+  ProjectsWsTest.class,
   ComponentsWsTest.class,
   // analysis exclusion
   FileExclusionsTest.class,
index 605accaeb0b949e753f149fd89057d3e9045c24d..4820f670cf8857704fc553f582275ddf64942c3b 100644 (file)
@@ -28,12 +28,9 @@ import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.sonarqube.ws.WsComponents;
-import org.sonarqube.ws.WsComponents.BulkUpdateKeyWsResponse.Key;
 import org.sonarqube.ws.client.WsClient;
-import org.sonarqube.ws.client.component.BulkUpdateWsRequest;
 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;
@@ -44,7 +41,6 @@ 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();
@@ -76,37 +72,4 @@ 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);
-  }
-
-  @Test
-  public void bulk_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);
-
-    WsComponents.BulkUpdateKeyWsResponse result = wsClient.components().bulkUpdateKey(BulkUpdateWsRequest.builder()
-      .setKey(PROJECT_KEY)
-      .setFrom(PROJECT_KEY)
-      .setTo(newProjectKey)
-      .build());
-
-    assertThat(wsClient.components().show(new ShowWsRequest().setId(project.getId())).getComponent().getKey()).isEqualTo(newProjectKey);
-    assertThat(result.getKeysCount()).isEqualTo(1);
-    assertThat(result.getKeys(0))
-      .extracting(Key::getKey, Key::getNewKey, Key::getDuplicate)
-      .containsOnlyOnce(PROJECT_KEY, newProjectKey, false);
-  }
 }
diff --git a/it/it-tests/src/test/java/it/component/ProjectSearchTest.java b/it/it-tests/src/test/java/it/component/ProjectSearchTest.java
deleted file mode 100644 (file)
index 48941aa..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * 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 it.component;
-
-import com.sonar.orchestrator.Orchestrator;
-import com.sonar.orchestrator.build.SonarScanner;
-import it.Category4Suite;
-import java.io.IOException;
-import org.apache.commons.io.IOUtils;
-import org.apache.http.HttpResponse;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.util.EntityUtils;
-import org.junit.Before;
-import org.junit.ClassRule;
-import org.junit.Test;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static util.ItUtils.projectDir;
-
-public class ProjectSearchTest {
-
-  @ClassRule
-  public static final Orchestrator orchestrator = Category4Suite.ORCHESTRATOR;
-
-  @Before
-  public void inspectProject() {
-    orchestrator.resetData();
-    orchestrator.executeBuild(SonarScanner.create(projectDir("shared/xoo-sample")));
-  }
-
-  /**
-   * SONAR-3105
-   */
-  @Test
-  public void projects_web_service() throws IOException {
-    SonarScanner build = SonarScanner.create(projectDir("shared/xoo-sample"));
-    orchestrator.executeBuild(build);
-
-    String url = orchestrator.getServer().getUrl() + "/api/projects/index?key=sample&versions=true";
-    HttpClient httpclient = new DefaultHttpClient();
-    try {
-      HttpGet get = new HttpGet(url);
-      HttpResponse response = httpclient.execute(get);
-
-      assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200);
-      String content = IOUtils.toString(response.getEntity().getContent());
-      assertThat(content).doesNotContain("error");
-      assertThat(content).contains("sample");
-      EntityUtils.consume(response.getEntity());
-
-    } finally {
-      httpclient.getConnectionManager().shutdown();
-    }
-  }
-}
diff --git a/it/it-tests/src/test/java/it/component/ProjectsWsTest.java b/it/it-tests/src/test/java/it/component/ProjectsWsTest.java
new file mode 100644 (file)
index 0000000..a8f7601
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * 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 it.component;
+
+import com.sonar.orchestrator.Orchestrator;
+import com.sonar.orchestrator.build.SonarScanner;
+import it.Category4Suite;
+import java.io.IOException;
+import org.apache.commons.io.IOUtils;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.util.EntityUtils;
+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.WsProjects.BulkUpdateKeyWsResponse;
+import org.sonarqube.ws.WsProjects.BulkUpdateKeyWsResponse.Key;
+import org.sonarqube.ws.client.WsClient;
+import org.sonarqube.ws.client.component.ShowWsRequest;
+import org.sonarqube.ws.client.project.BulkUpdateKeyWsRequest;
+import org.sonarqube.ws.client.project.UpdateKeyWsRequest;
+import util.ItUtils;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static util.ItUtils.projectDir;
+
+public class ProjectsWsTest {
+
+  @ClassRule
+  public static final Orchestrator orchestrator = Category4Suite.ORCHESTRATOR;
+  private static final String PROJECT_KEY = "sample";
+
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  private WsClient wsClient;
+
+  @Before
+  public void inspectProject() {
+    orchestrator.resetData();
+    orchestrator.executeBuild(SonarScanner.create(projectDir("shared/xoo-sample")));
+
+    wsClient = ItUtils.newAdminWsClient(orchestrator);
+  }
+
+  /**
+   * SONAR-3105
+   */
+  @Test
+  public void projects_web_service() throws IOException {
+    SonarScanner build = SonarScanner.create(projectDir("shared/xoo-sample"));
+    orchestrator.executeBuild(build);
+
+    String url = orchestrator.getServer().getUrl() + "/api/projects/index?key=sample&versions=true";
+    HttpClient httpclient = new DefaultHttpClient();
+    try {
+      HttpGet get = new HttpGet(url);
+      HttpResponse response = httpclient.execute(get);
+
+      assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200);
+      String content = IOUtils.toString(response.getEntity().getContent());
+      assertThat(content).doesNotContain("error");
+      assertThat(content).contains("sample");
+      EntityUtils.consume(response.getEntity());
+
+    } finally {
+      httpclient.getConnectionManager().shutdown();
+    }
+  }
+
+  @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.projects().updateKey(UpdateKeyWsRequest.builder()
+      .setKey(PROJECT_KEY)
+      .setNewKey(newProjectKey)
+      .build());
+
+    assertThat(wsClient.components().show(new ShowWsRequest().setId(project.getId())).getComponent().getKey()).isEqualTo(newProjectKey);
+  }
+
+  @Test
+  public void bulk_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);
+
+    BulkUpdateKeyWsResponse result = wsClient.projects().bulkUpdateKey(BulkUpdateKeyWsRequest.builder()
+      .setKey(PROJECT_KEY)
+      .setFrom(PROJECT_KEY)
+      .setTo(newProjectKey)
+      .build());
+
+    assertThat(wsClient.components().show(new ShowWsRequest().setId(project.getId())).getComponent().getKey()).isEqualTo(newProjectKey);
+    assertThat(result.getKeysCount()).isEqualTo(1);
+    assertThat(result.getKeys(0))
+      .extracting(Key::getKey, Key::getNewKey, Key::getDuplicate)
+      .containsOnlyOnce(PROJECT_KEY, newProjectKey, false);
+  }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ws/BulkUpdateKeyAction.java b/server/sonar-server/src/main/java/org/sonar/server/component/ws/BulkUpdateKeyAction.java
deleted file mode 100644 (file)
index 0cee6c3..0000000
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * 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.component.ws;
-
-import com.google.common.collect.ImmutableList;
-import java.util.Map;
-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.ComponentDto;
-import org.sonar.db.component.ComponentKeyUpdaterDao;
-import org.sonar.server.component.ComponentFinder;
-import org.sonar.server.component.ComponentFinder.ParamNames;
-import org.sonar.server.component.ComponentService;
-import org.sonar.server.user.UserSession;
-import org.sonarqube.ws.WsComponents;
-import org.sonarqube.ws.WsComponents.BulkUpdateKeyWsResponse;
-import org.sonarqube.ws.client.component.BulkUpdateWsRequest;
-
-import static org.sonar.core.util.Uuids.UUID_EXAMPLE_01;
-import static org.sonar.db.component.ComponentKeyUpdaterDao.checkIsProjectOrModule;
-import static org.sonar.server.ws.WsUtils.checkRequest;
-import static org.sonar.server.ws.WsUtils.writeProtobuf;
-import static org.sonarqube.ws.client.component.ComponentsWsParameters.ACTION_BULK_UPDATE_KEY;
-import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_DRY_RUN;
-import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_FROM;
-import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_PROJECT;
-import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_PROJECT_ID;
-import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_TO;
-
-public class BulkUpdateKeyAction implements ComponentsWsAction {
-  private final DbClient dbClient;
-  private final ComponentFinder componentFinder;
-  private final ComponentKeyUpdaterDao componentKeyUpdater;
-  private final ComponentService componentService;
-  private final UserSession userSession;
-
-  public BulkUpdateKeyAction(DbClient dbClient, ComponentFinder componentFinder, ComponentService componentService, UserSession userSession) {
-    this.dbClient = dbClient;
-    this.componentKeyUpdater = dbClient.componentKeyUpdaterDao();
-    this.componentFinder = componentFinder;
-    this.componentService = componentService;
-    this.userSession = userSession;
-  }
-
-  @Override
-  public void define(WebService.NewController context) {
-    WebService.NewAction action = context.createAction(ACTION_BULK_UPDATE_KEY)
-      .setDescription("Bulk update a project or module key and all its sub-components keys. " +
-        "The bulk update allows to replace a part of the current key by another string on the current project and all its sub-modules.<br>" +
-        "It's possible to simulate the bulk update by setting the parameter '%s' at true. No key is updated with a dry run.<br>" +
-        "Ex: to rename a project with key 'my_project' to 'my_new_project' and all its sub-components keys, call the WS with parameters:" +
-        "<ul>" +
-        "  <li>%s: my_project</li>" +
-        "  <li>%s: my_</li>" +
-        "  <li>%s: my_new_</li>" +
-        "</ul>" +
-        "Either '%s' or '%s' must be provided, not both.<br> " +
-        "Requires one of the following permissions: " +
-        "<ul>" +
-        "<li>'Administer System'</li>" +
-        "<li>'Administer' rights on the specified project</li>" +
-        "</ul>",
-        PARAM_DRY_RUN,
-        PARAM_PROJECT, PARAM_FROM, PARAM_TO,
-        PARAM_PROJECT_ID, PARAM_PROJECT)
-      .setSince("6.1")
-      .setPost(true)
-      .setResponseExample(getClass().getResource("bulk_update_key-example.json"))
-      .setHandler(this);
-
-    action.createParam(PARAM_PROJECT_ID)
-      .setDescription("Project or module ID")
-      .setDeprecatedKey("id", "6.4")
-      .setExampleValue(UUID_EXAMPLE_01);
-
-    action.createParam(PARAM_PROJECT)
-      .setDescription("Project or module key")
-      .setDeprecatedKey("key", "6.4")
-      .setExampleValue("my_old_project");
-
-    action.createParam(PARAM_FROM)
-      .setDescription("String to match in components keys")
-      .setRequired(true)
-      .setExampleValue("_old");
-
-    action.createParam(PARAM_TO)
-      .setDescription("String replacement in components keys")
-      .setRequired(true)
-      .setExampleValue("_new");
-
-    action.createParam(PARAM_DRY_RUN)
-      .setDescription("Simulate bulk update. No component key is updated.")
-      .setBooleanPossibleValues()
-      .setDefaultValue(false);
-  }
-
-  @Override
-  public void handle(Request request, Response response) throws Exception {
-    writeProtobuf(doHandle(toWsRequest(request)), request, response);
-  }
-
-  private BulkUpdateKeyWsResponse doHandle(BulkUpdateWsRequest request) {
-    DbSession dbSession = dbClient.openSession(false);
-    try {
-      ComponentDto projectOrModule = componentFinder.getByUuidOrKey(dbSession, request.getId(), request.getKey(), ParamNames.ID_AND_KEY);
-      checkIsProjectOrModule(projectOrModule);
-      userSession.checkComponentPermission(UserRole.ADMIN, projectOrModule);
-
-      Map<String, String> newKeysByOldKeys = componentKeyUpdater.simulateBulkUpdateKey(dbSession, projectOrModule.uuid(), request.getFrom(), request.getTo());
-      Map<String, Boolean> newKeysWithDuplicateMap = componentKeyUpdater.checkComponentKeys(dbSession, ImmutableList.copyOf(newKeysByOldKeys.values()));
-
-      if (!request.isDryRun()) {
-        checkNoDuplicate(newKeysWithDuplicateMap);
-        bulkUpdateKey(dbSession, request, projectOrModule);
-      }
-
-      return buildResponse(newKeysByOldKeys, newKeysWithDuplicateMap);
-    } finally {
-      dbClient.closeSession(dbSession);
-    }
-  }
-
-  private static void checkNoDuplicate(Map<String, Boolean> newKeysWithDuplicateMap) {
-    newKeysWithDuplicateMap.entrySet().forEach(entry -> checkRequest(!entry.getValue(), "Impossible to update key: a component with key \"%s\" already exists.", entry.getKey()));
-  }
-
-  private void bulkUpdateKey(DbSession dbSession, BulkUpdateWsRequest request, ComponentDto projectOrModule) {
-    componentService.bulkUpdateKey(dbSession, projectOrModule.uuid(), request.getFrom(), request.getTo());
-    dbSession.commit();
-  }
-
-  private static BulkUpdateKeyWsResponse buildResponse(Map<String, String> newKeysByOldKeys, Map<String, Boolean> newKeysWithDuplicateMap) {
-    WsComponents.BulkUpdateKeyWsResponse.Builder response = WsComponents.BulkUpdateKeyWsResponse.newBuilder();
-
-    newKeysByOldKeys.entrySet().stream()
-      // sort by old key
-      .sorted((e1, e2) -> e1.getKey().compareTo(e2.getKey()))
-      .forEach(
-        entry -> {
-          String newKey = entry.getValue();
-          response.addKeysBuilder()
-            .setKey(entry.getKey())
-            .setNewKey(newKey)
-            .setDuplicate(newKeysWithDuplicateMap.getOrDefault(newKey, false));
-        });
-
-    return response.build();
-  }
-
-  private static BulkUpdateWsRequest toWsRequest(Request request) {
-    return BulkUpdateWsRequest.builder()
-      .setId(request.param(PARAM_PROJECT_ID))
-      .setKey(request.param(PARAM_PROJECT))
-      .setFrom(request.mandatoryParam(PARAM_FROM))
-      .setTo(request.mandatoryParam(PARAM_TO))
-      .setDryRun(request.mandatoryParamAsBoolean(PARAM_DRY_RUN))
-      .build();
-  }
-}
index 8d4526d1be8debf97cd3e3eee7b4b59173c45c48..a9a4edf5f5823859c69c5195f21420f55adc86c3 100644 (file)
@@ -33,8 +33,6 @@ public class ComponentsWsModule extends Module {
       SuggestionsAction.class,
       TreeAction.class,
       ShowAction.class,
-      UpdateKeyAction.class,
-      BulkUpdateKeyAction.class,
       SearchProjectsAction.class,
       ProjectMeasuresQueryValidator.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
deleted file mode 100644 (file)
index 38e59a5..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * 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.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.ACTION_UPDATE_KEY;
-import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_NEW_PROJECT;
-import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_PROJECT;
-import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_PROJECT_ID;
-
-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(ACTION_UPDATE_KEY)
-      .setDescription("Update a project or module key and all its sub-components keys.<br>" +
-        "Either '%s' or '%s' must be provided, not both.<br> " +
-        "Requires one of the following permissions: " +
-        "<ul>" +
-        "<li>'Administer System'</li>" +
-        "<li>'Administer' rights on the specified project</li>" +
-        "</ul>",
-        PARAM_PROJECT, PARAM_PROJECT_ID)
-      .setSince("6.1")
-      .setPost(true)
-      .setHandler(this);
-
-    action.createParam(PARAM_PROJECT_ID)
-      .setDescription("Project or module id")
-      .setDeprecatedKey("id", "6.4")
-      .setExampleValue(UUID_EXAMPLE_01);
-
-    action.createParam(PARAM_PROJECT)
-      .setDescription("Project or module key")
-      .setDeprecatedKey("key", "6.4")
-      .setExampleValue("my_old_project");
-
-    action.createParam(PARAM_NEW_PROJECT)
-      .setDescription("New component key")
-      .setRequired(true)
-      .setDeprecatedKey("newKey", "6.4")
-      .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.PROJECT_ID_AND_PROJECT);
-      componentService.updateKey(dbSession, projectOrModule, request.getNewKey());
-      dbSession.commit();
-    } finally {
-      dbClient.closeSession(dbSession);
-    }
-  }
-
-  private static UpdateWsRequest toWsRequest(Request request) {
-    return UpdateWsRequest.builder()
-      .setId(request.param(PARAM_PROJECT_ID))
-      .setKey(request.param(PARAM_PROJECT))
-      .setNewKey(request.mandatoryParam(PARAM_NEW_PROJECT))
-      .build();
-  }
-}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/project/ws/BulkUpdateKeyAction.java b/server/sonar-server/src/main/java/org/sonar/server/project/ws/BulkUpdateKeyAction.java
new file mode 100644 (file)
index 0000000..f7924fc
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * 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.project.ws;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Comparator;
+import java.util.Map;
+import org.sonar.api.server.ws.Change;
+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.ComponentDto;
+import org.sonar.db.component.ComponentKeyUpdaterDao;
+import org.sonar.server.component.ComponentFinder;
+import org.sonar.server.component.ComponentFinder.ParamNames;
+import org.sonar.server.component.ComponentService;
+import org.sonar.server.user.UserSession;
+import org.sonarqube.ws.WsProjects.BulkUpdateKeyWsResponse;
+import org.sonarqube.ws.client.project.BulkUpdateKeyWsRequest;
+
+import static org.sonar.core.util.Uuids.UUID_EXAMPLE_01;
+import static org.sonar.db.component.ComponentKeyUpdaterDao.checkIsProjectOrModule;
+import static org.sonar.server.ws.WsUtils.checkRequest;
+import static org.sonar.server.ws.WsUtils.writeProtobuf;
+import static org.sonarqube.ws.client.project.ProjectsWsParameters.ACTION_BULK_UPDATE_KEY;
+import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_DRY_RUN;
+import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_FROM;
+import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_PROJECT;
+import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_PROJECT_ID;
+import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_TO;
+
+public class BulkUpdateKeyAction implements ProjectsWsAction {
+  private final DbClient dbClient;
+  private final ComponentFinder componentFinder;
+  private final ComponentKeyUpdaterDao componentKeyUpdater;
+  private final ComponentService componentService;
+  private final UserSession userSession;
+
+  public BulkUpdateKeyAction(DbClient dbClient, ComponentFinder componentFinder, ComponentService componentService, UserSession userSession) {
+    this.dbClient = dbClient;
+    this.componentKeyUpdater = dbClient.componentKeyUpdaterDao();
+    this.componentFinder = componentFinder;
+    this.componentService = componentService;
+    this.userSession = userSession;
+  }
+
+  @Override
+  public void define(WebService.NewController context) {
+    doDefine(context);
+  }
+
+  public WebService.NewAction doDefine(WebService.NewController context) {
+    WebService.NewAction action = context.createAction(ACTION_BULK_UPDATE_KEY)
+      .setDescription("Bulk update a project or module key and all its sub-components keys. " +
+        "The bulk update allows to replace a part of the current key by another string on the current project and all its sub-modules.<br>" +
+        "It's possible to simulate the bulk update by setting the parameter '%s' at true. No key is updated with a dry run.<br>" +
+        "Ex: to rename a project with key 'my_project' to 'my_new_project' and all its sub-components keys, call the WS with parameters:" +
+        "<ul>" +
+        "  <li>%s: my_project</li>" +
+        "  <li>%s: my_</li>" +
+        "  <li>%s: my_new_</li>" +
+        "</ul>" +
+        "Either '%s' or '%s' must be provided, not both.<br> " +
+        "Requires one of the following permissions: " +
+        "<ul>" +
+        "<li>'Administer System'</li>" +
+        "<li>'Administer' rights on the specified project</li>" +
+        "</ul>",
+        PARAM_DRY_RUN,
+        PARAM_PROJECT, PARAM_FROM, PARAM_TO,
+        PARAM_PROJECT_ID, PARAM_PROJECT)
+      .setSince("6.1")
+      .setPost(true)
+      .setResponseExample(getClass().getResource("bulk_update_key-example.json"))
+      .setHandler(this);
+
+    action.setChangelog(
+      new Change("6.4", "Moved from api/components/bulk_update_key to api/projects/bulk_update_key"));
+
+    action.createParam(PARAM_PROJECT_ID)
+      .setDescription("Project or module ID")
+      .setDeprecatedKey("id", "6.4")
+      .setExampleValue(UUID_EXAMPLE_01);
+
+    action.createParam(PARAM_PROJECT)
+      .setDescription("Project or module key")
+      .setDeprecatedKey("key", "6.4")
+      .setExampleValue("my_old_project");
+
+    action.createParam(PARAM_FROM)
+      .setDescription("String to match in components keys")
+      .setRequired(true)
+      .setExampleValue("_old");
+
+    action.createParam(PARAM_TO)
+      .setDescription("String replacement in components keys")
+      .setRequired(true)
+      .setExampleValue("_new");
+
+    action.createParam(PARAM_DRY_RUN)
+      .setDescription("Simulate bulk update. No component key is updated.")
+      .setBooleanPossibleValues()
+      .setDefaultValue(false);
+
+    return action;
+  }
+
+  @Override
+  public void handle(Request request, Response response) throws Exception {
+    writeProtobuf(doHandle(toWsRequest(request)), request, response);
+  }
+
+  private BulkUpdateKeyWsResponse doHandle(BulkUpdateKeyWsRequest request) {
+    DbSession dbSession = dbClient.openSession(false);
+    try {
+      ComponentDto projectOrModule = componentFinder.getByUuidOrKey(dbSession, request.getId(), request.getKey(), ParamNames.ID_AND_KEY);
+      checkIsProjectOrModule(projectOrModule);
+      userSession.checkComponentPermission(UserRole.ADMIN, projectOrModule);
+
+      Map<String, String> newKeysByOldKeys = componentKeyUpdater.simulateBulkUpdateKey(dbSession, projectOrModule.uuid(), request.getFrom(), request.getTo());
+      Map<String, Boolean> newKeysWithDuplicateMap = componentKeyUpdater.checkComponentKeys(dbSession, ImmutableList.copyOf(newKeysByOldKeys.values()));
+
+      if (!request.isDryRun()) {
+        checkNoDuplicate(newKeysWithDuplicateMap);
+        bulkUpdateKey(dbSession, request, projectOrModule);
+      }
+
+      return buildResponse(newKeysByOldKeys, newKeysWithDuplicateMap);
+    } finally {
+      dbClient.closeSession(dbSession);
+    }
+  }
+
+  private static void checkNoDuplicate(Map<String, Boolean> newKeysWithDuplicateMap) {
+    newKeysWithDuplicateMap.entrySet().forEach(entry -> checkRequest(!entry.getValue(), "Impossible to update key: a component with key \"%s\" already exists.", entry.getKey()));
+  }
+
+  private void bulkUpdateKey(DbSession dbSession, BulkUpdateKeyWsRequest request, ComponentDto projectOrModule) {
+    componentService.bulkUpdateKey(dbSession, projectOrModule.uuid(), request.getFrom(), request.getTo());
+    dbSession.commit();
+  }
+
+  private static BulkUpdateKeyWsResponse buildResponse(Map<String, String> newKeysByOldKeys, Map<String, Boolean> newKeysWithDuplicateMap) {
+    BulkUpdateKeyWsResponse.Builder response = BulkUpdateKeyWsResponse.newBuilder();
+
+    newKeysByOldKeys.entrySet().stream()
+      // sort by old key
+      .sorted(Comparator.comparing(Map.Entry::getKey))
+      .forEach(
+        entry -> {
+          String newKey = entry.getValue();
+          response.addKeysBuilder()
+            .setKey(entry.getKey())
+            .setNewKey(newKey)
+            .setDuplicate(newKeysWithDuplicateMap.getOrDefault(newKey, false));
+        });
+
+    return response.build();
+  }
+
+  private static BulkUpdateKeyWsRequest toWsRequest(Request request) {
+    return BulkUpdateKeyWsRequest.builder()
+      .setId(request.param(PARAM_PROJECT_ID))
+      .setKey(request.param(PARAM_PROJECT))
+      .setFrom(request.mandatoryParam(PARAM_FROM))
+      .setTo(request.mandatoryParam(PARAM_TO))
+      .setDryRun(request.mandatoryParamAsBoolean(PARAM_DRY_RUN))
+      .build();
+  }
+}
index d3c00cf99edf95662ae5f33896e3d0d7f4365953..4fcc3bebc44fe10860e3f6d34cc07e889800d2e6 100644 (file)
@@ -31,6 +31,8 @@ public class ProjectsWsModule extends Module {
       IndexAction.class,
       BulkDeleteAction.class,
       DeleteAction.class,
+      UpdateKeyAction.class,
+      BulkUpdateKeyAction.class,
       GhostsAction.class,
       ProvisionedAction.class,
       SearchMyProjectsAction.class,
diff --git a/server/sonar-server/src/main/java/org/sonar/server/project/ws/UpdateKeyAction.java b/server/sonar-server/src/main/java/org/sonar/server/project/ws/UpdateKeyAction.java
new file mode 100644 (file)
index 0000000..8fc2254
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * 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.project.ws;
+
+import org.sonar.api.server.ws.Change;
+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.project.UpdateKeyWsRequest;
+
+import static org.sonar.core.util.Uuids.UUID_EXAMPLE_01;
+import static org.sonarqube.ws.client.project.ProjectsWsParameters.ACTION_UPDATE_KEY;
+import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_NEW_PROJECT;
+import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_PROJECT;
+import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_PROJECT_ID;
+
+public class UpdateKeyAction implements ProjectsWsAction {
+  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) {
+    doDefine(context);
+  }
+
+  public WebService.NewAction doDefine(WebService.NewController context) {
+    WebService.NewAction action = context.createAction(ACTION_UPDATE_KEY)
+      .setDescription("Update a project or module key and all its sub-components keys.<br>" +
+        "Either '%s' or '%s' must be provided, not both.<br> " +
+        "Requires one of the following permissions: " +
+        "<ul>" +
+        "<li>'Administer System'</li>" +
+        "<li>'Administer' rights on the specified project</li>" +
+        "</ul>",
+        PARAM_PROJECT, PARAM_PROJECT_ID)
+      .setSince("6.1")
+      .setPost(true)
+      .setHandler(this);
+
+    action.setChangelog(
+      new Change("6.4", "Move from api/components/update_key to api/projects/update_key"));
+
+    action.createParam(PARAM_PROJECT_ID)
+      .setDescription("Project or module id")
+      .setDeprecatedKey("id", "6.4")
+      .setExampleValue(UUID_EXAMPLE_01);
+
+    action.createParam(PARAM_PROJECT)
+      .setDescription("Project or module key")
+      .setDeprecatedKey("key", "6.4")
+      .setExampleValue("my_old_project");
+
+    action.createParam(PARAM_NEW_PROJECT)
+      .setDescription("New component key")
+      .setRequired(true)
+      .setDeprecatedKey("newKey", "6.4")
+      .setExampleValue("my_new_project");
+
+    return action;
+  }
+
+  @Override
+  public void handle(Request request, Response response) throws Exception {
+    doHandle(toWsRequest(request));
+    response.noContent();
+  }
+
+  private void doHandle(UpdateKeyWsRequest request) {
+    DbSession dbSession = dbClient.openSession(false);
+    try {
+      ComponentDto projectOrModule = componentFinder.getByUuidOrKey(dbSession, request.getId(), request.getKey(), ParamNames.PROJECT_ID_AND_PROJECT);
+      componentService.updateKey(dbSession, projectOrModule, request.getNewKey());
+      dbSession.commit();
+    } finally {
+      dbClient.closeSession(dbSession);
+    }
+  }
+
+  private static UpdateKeyWsRequest toWsRequest(Request request) {
+    return UpdateKeyWsRequest.builder()
+      .setId(request.param(PARAM_PROJECT_ID))
+      .setKey(request.param(PARAM_PROJECT))
+      .setNewKey(request.mandatoryParam(PARAM_NEW_PROJECT))
+      .build();
+  }
+}
diff --git a/server/sonar-server/src/main/resources/org/sonar/server/component/ws/bulk_update_key-example.json b/server/sonar-server/src/main/resources/org/sonar/server/component/ws/bulk_update_key-example.json
deleted file mode 100644 (file)
index 7ad4b3d..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-{
-  "keys": [
-    {
-      "key": "my_project",
-      "newKey": "my_new_project",
-      "duplicate": false
-    },
-    {
-      "key": "my_project:module_1",
-      "newKey": "my_new_project:module_1",
-      "duplicate": true
-    },
-    {
-      "key": "my_project:module_2",
-      "newKey": "my_new_project:module_2",
-      "duplicate": false
-    }
-  ]
-}
diff --git a/server/sonar-server/src/main/resources/org/sonar/server/project/ws/bulk_update_key-example.json b/server/sonar-server/src/main/resources/org/sonar/server/project/ws/bulk_update_key-example.json
new file mode 100644 (file)
index 0000000..7ad4b3d
--- /dev/null
@@ -0,0 +1,19 @@
+{
+  "keys": [
+    {
+      "key": "my_project",
+      "newKey": "my_new_project",
+      "duplicate": false
+    },
+    {
+      "key": "my_project:module_1",
+      "newKey": "my_new_project:module_1",
+      "duplicate": true
+    },
+    {
+      "key": "my_project:module_2",
+      "newKey": "my_new_project:module_2",
+      "duplicate": false
+    }
+  ]
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ws/BulkUpdateKeyActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ws/BulkUpdateKeyActionTest.java
deleted file mode 100644 (file)
index a95509b..0000000
+++ /dev/null
@@ -1,310 +0,0 @@
-/*
- * 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.component.ws;
-
-import com.google.common.base.Throwables;
-import java.io.IOException;
-import javax.annotation.Nullable;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.sonar.api.config.MapSettings;
-import org.sonar.api.server.ws.WebService;
-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.db.organization.OrganizationDto;
-import org.sonar.server.component.ComponentFinder;
-import org.sonar.server.component.ComponentService;
-import org.sonar.server.component.index.ComponentIndexDefinition;
-import org.sonar.server.es.EsTester;
-import org.sonar.server.exceptions.BadRequestException;
-import org.sonar.server.exceptions.ForbiddenException;
-import org.sonar.server.exceptions.NotFoundException;
-import org.sonar.server.measure.index.ProjectMeasuresIndexDefinition;
-import org.sonar.server.tester.UserSessionRule;
-import org.sonar.server.ws.TestRequest;
-import org.sonar.server.ws.WsActionTester;
-import org.sonarqube.ws.MediaTypes;
-import org.sonarqube.ws.WsComponents;
-import org.sonarqube.ws.WsComponents.BulkUpdateKeyWsResponse;
-import org.sonarqube.ws.WsComponents.BulkUpdateKeyWsResponse.Key;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.tuple;
-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.newFileDto;
-import static org.sonar.db.component.ComponentTesting.newModuleDto;
-import static org.sonar.db.component.ComponentTesting.newProjectDto;
-import static org.sonar.test.JsonAssert.assertJson;
-import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_DRY_RUN;
-import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_FROM;
-import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_PROJECT;
-import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_PROJECT_ID;
-import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_TO;
-
-public class BulkUpdateKeyActionTest {
-  private static final String MY_PROJECT_KEY = "my_project";
-  private static final String FROM = "my_";
-  private static final String TO = "your_";
-
-  private System2 system2 = System2.INSTANCE;
-
-  @Rule
-  public ExpectedException expectedException = ExpectedException.none();
-  @Rule
-  public UserSessionRule userSession = UserSessionRule.standalone();
-  @Rule
-  public EsTester es = new EsTester(new ProjectMeasuresIndexDefinition(new MapSettings()),
-    new ComponentIndexDefinition(new MapSettings()));
-  @Rule
-  public DbTester db = DbTester.create(system2);
-
-  private ComponentDbTester componentDb = new ComponentDbTester(db);
-  private DbClient dbClient = db.getDbClient();
-  private ComponentFinder componentFinder = new ComponentFinder(dbClient);
-  private ComponentService componentService = mock(ComponentService.class);
-  private WsActionTester ws = new WsActionTester(
-    new BulkUpdateKeyAction(dbClient, componentFinder, componentService, userSession));
-
-  @Before
-  public void setUp() {
-    userSession.logIn().setRoot();
-  }
-
-  @Test
-  public void json_example() {
-    OrganizationDto organizationDto = db.organizations().insert();
-    ComponentDto project = componentDb.insertComponent(newProjectDto(organizationDto).setKey("my_project"));
-    componentDb.insertComponent(newModuleDto(project).setKey("my_project:module_1"));
-    ComponentDto anotherProject = componentDb.insertComponent(newProjectDto(organizationDto).setKey("another_project"));
-    componentDb.insertComponent(newModuleDto(anotherProject).setKey("my_new_project:module_1"));
-    ComponentDto module2 = componentDb.insertComponent(newModuleDto(project).setKey("my_project:module_2"));
-    componentDb.insertComponent(newFileDto(module2, null));
-
-    String result = ws.newRequest()
-      .setParam(PARAM_PROJECT, "my_project")
-      .setParam(PARAM_FROM, "my_")
-      .setParam(PARAM_TO, "my_new_")
-      .setParam(PARAM_DRY_RUN, String.valueOf(true))
-      .execute().getInput();
-
-    assertJson(result).withStrictArrayOrder().isSimilarTo(getClass().getResource("bulk_update_key-example.json"));
-  }
-
-  @Test
-  public void dry_run_by_key() {
-    insertMyProject();
-
-    BulkUpdateKeyWsResponse result = callDryRunByKey(MY_PROJECT_KEY, FROM, TO);
-
-    assertThat(result.getKeysCount()).isEqualTo(1);
-    assertThat(result.getKeys(0).getNewKey()).isEqualTo("your_project");
-  }
-
-  @Test
-  public void bulk_update_project_key() {
-    ComponentDto project = insertMyProject();
-    ComponentDto module = componentDb.insertComponent(newModuleDto(project).setKey("my_project:root:module"));
-    ComponentDto inactiveModule = componentDb.insertComponent(newModuleDto(project).setKey("my_project:root:inactive_module").setEnabled(false));
-    ComponentDto file = componentDb.insertComponent(newFileDto(module, null).setKey("my_project:root:module:src/File.xoo"));
-    ComponentDto inactiveFile = componentDb.insertComponent(newFileDto(module, null).setKey("my_project:root:module:src/InactiveFile.xoo").setEnabled(false));
-
-    BulkUpdateKeyWsResponse result = callByUuid(project.uuid(), FROM, TO);
-
-    assertThat(result.getKeysCount()).isEqualTo(2);
-    assertThat(result.getKeysList()).extracting(Key::getKey, Key::getNewKey, Key::getDuplicate)
-      .containsExactly(
-        tuple(project.key(), "your_project", false),
-        tuple(module.key(), "your_project:root:module", false));
-
-    verify(componentService).bulkUpdateKey(any(DbSession.class), eq(project.uuid()), eq(FROM), eq(TO));
-  }
-
-  @Test
-  public void bulk_update_provisioned_project_key() {
-    String newKey = "provisionedProject2";
-    ComponentDto provisionedProject = componentDb.insertProject();
-
-    callByKey(provisionedProject.key(), provisionedProject.getKey(), newKey);
-
-    verify(componentService).bulkUpdateKey(any(DbSession.class), eq(provisionedProject.uuid()), eq(provisionedProject.getKey()), eq(newKey));
-  }
-
-  @Test
-  public void fail_to_bulk_if_a_component_already_exists_with_the_same_key() {
-    componentDb.insertComponent(newProjectDto(db.getDefaultOrganization()).setKey("my_project"));
-    componentDb.insertComponent(newProjectDto(db.getDefaultOrganization()).setKey("your_project"));
-
-    expectedException.expect(BadRequestException.class);
-    expectedException.expectMessage("Impossible to update key: a component with key \"your_project\" already exists.");
-
-    callByKey("my_project", "my_", "your_");
-  }
-
-  @Test
-  public void fail_to_bulk_update_with_invalid_new_key() {
-    insertMyProject();
-
-    expectedException.expect(IllegalArgumentException.class);
-    expectedException.expectMessage("Malformed key for 'my?project'. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit.");
-
-    callByKey(MY_PROJECT_KEY, FROM, "my?");
-  }
-
-  @Test
-  public void fail_to_dry_bulk_update_with_invalid_new_key() {
-    insertMyProject();
-
-    expectedException.expect(IllegalArgumentException.class);
-    expectedException.expectMessage("Malformed key for 'my?project'. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit.");
-
-    callDryRunByKey(MY_PROJECT_KEY, FROM, "my?");
-  }
-
-  @Test
-  public void fail_to_bulk_update_if_not_project_or_module() {
-    ComponentDto project = insertMyProject();
-    ComponentDto file = componentDb.insertComponent(newFileDto(project, null));
-
-    expectedException.expect(IllegalArgumentException.class);
-    expectedException.expectMessage("Component updated must be a module or a key");
-
-    callByKey(file.key(), FROM, TO);
-  }
-
-  @Test
-  public void fail_if_from_string_is_not_provided() {
-    expectedException.expect(IllegalArgumentException.class);
-
-    ComponentDto project = insertMyProject();
-
-    callDryRunByKey(project.key(), null, TO);
-  }
-
-  @Test
-  public void fail_if_to_string_is_not_provided() {
-    expectedException.expect(IllegalArgumentException.class);
-
-    ComponentDto project = insertMyProject();
-
-    callDryRunByKey(project.key(), FROM, null);
-  }
-
-  @Test
-  public void fail_if_uuid_nor_key_provided() {
-    expectedException.expect(IllegalArgumentException.class);
-
-    call(null, null, FROM, TO, false);
-  }
-
-  @Test
-  public void fail_if_uuid_and_key_provided() {
-    expectedException.expect(IllegalArgumentException.class);
-
-    ComponentDto project = insertMyProject();
-
-    call(project.uuid(), project.key(), FROM, TO, false);
-  }
-
-  @Test
-  public void fail_if_project_does_not_exist() {
-    expectedException.expect(NotFoundException.class);
-
-    callDryRunByUuid("UNKNOWN_UUID", FROM, TO);
-  }
-
-  @Test
-  public void throw_ForbiddenException_if_not_project_administrator() {
-    userSession.logIn();
-    ComponentDto project = insertMyProject();
-
-    expectedException.expect(ForbiddenException.class);
-
-    callDryRunByUuid(project.uuid(), FROM, TO);
-  }
-
-  @Test
-  public void api_definition() {
-    WebService.Action definition = ws.getDef();
-
-    assertThat(definition.isPost()).isTrue();
-    assertThat(definition.since()).isEqualTo("6.1");
-    assertThat(definition.key()).isEqualTo("bulk_update_key");
-    assertThat(definition.params())
-      .hasSize(5)
-      .extracting(WebService.Param::key)
-      .containsOnlyOnce("projectId", "project", "from", "to", "dryRun");
-  }
-
-  private ComponentDto insertMyProject() {
-    return componentDb.insertComponent(newProjectDto(db.organizations().insert()).setKey(MY_PROJECT_KEY));
-  }
-
-  private WsComponents.BulkUpdateKeyWsResponse callDryRunByUuid(@Nullable String uuid, @Nullable String from, @Nullable String to) {
-    return call(uuid, null, from, to, true);
-  }
-
-  private BulkUpdateKeyWsResponse callDryRunByKey(@Nullable String key, @Nullable String from, @Nullable String to) {
-    return call(null, key, from, to, true);
-  }
-
-  private WsComponents.BulkUpdateKeyWsResponse callByUuid(@Nullable String uuid, @Nullable String from, @Nullable String to) {
-    return call(uuid, null, from, to, false);
-  }
-
-  private BulkUpdateKeyWsResponse callByKey(@Nullable String key, @Nullable String from, @Nullable String to) {
-    return call(null, key, from, to, false);
-  }
-
-  private BulkUpdateKeyWsResponse call(@Nullable String uuid, @Nullable String key, @Nullable String from, @Nullable String to, @Nullable Boolean dryRun) {
-    TestRequest request = ws.newRequest()
-      .setMediaType(MediaTypes.PROTOBUF);
-
-    if (uuid != null) {
-      request.setParam(PARAM_PROJECT_ID, uuid);
-    }
-    if (key != null) {
-      request.setParam(PARAM_PROJECT, key);
-    }
-    if (from != null) {
-      request.setParam(PARAM_FROM, from);
-    }
-    if (to != null) {
-      request.setParam(PARAM_TO, to);
-    }
-    if (dryRun != null) {
-      request.setParam(PARAM_DRY_RUN, String.valueOf(dryRun));
-    }
-
-    try {
-      return WsComponents.BulkUpdateKeyWsResponse.parseFrom(request.execute().getInputStream());
-    } catch (IOException e) {
-      throw Throwables.propagate(e);
-    }
-  }
-}
index d59687fd3f0105782b658f0610ec63c71c1654ed..6c965f414e7b82db4ff190eb44c6baaf671a9108 100644 (file)
@@ -30,6 +30,6 @@ public class ComponentsWsModuleTest {
   public void verify_count_of_added_components() {
     ComponentContainer container = new ComponentContainer();
     new ComponentsWsModule().configure(container);
-    assertThat(container.size()).isEqualTo(11 + COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER);
+    assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 9);
   }
 }
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
deleted file mode 100644 (file)
index bbfd6d5..0000000
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * 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.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_NEW_PROJECT;
-import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_PROJECT;
-import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_PROJECT_ID;
-
-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(ANOTHER_KEY);
-  }
-
-  @Test
-  public void call_by_uuid() {
-    ComponentDto project = insertProject();
-
-    callByUuid(project.uuid(), ANOTHER_KEY);
-
-    assertCallComponentService(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("projectId", "project", "newProject");
-  }
-
-  private void assertCallComponentService(@Nullable String newKey) {
-    verify(componentService).updateKey(any(DbSession.class), any(ComponentDto.class), eq(newKey));
-  }
-
-  private ComponentDto insertProject() {
-    return componentDb.insertComponent(newProjectDto(db.organizations().insert()));
-  }
-
-  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_PROJECT_ID, uuid);
-    }
-    if (key != null) {
-      request.setParam(PARAM_PROJECT, key);
-    }
-    if (newKey != null) {
-      request.setParam(PARAM_NEW_PROJECT, newKey);
-    }
-
-    return request.execute().getInput();
-  }
-}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/project/ws/BulkUpdateKeyActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/project/ws/BulkUpdateKeyActionTest.java
new file mode 100644 (file)
index 0000000..2a682db
--- /dev/null
@@ -0,0 +1,310 @@
+/*
+ * 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.project.ws;
+
+import com.google.common.base.Throwables;
+import java.io.IOException;
+import javax.annotation.Nullable;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.config.MapSettings;
+import org.sonar.api.server.ws.WebService;
+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.db.organization.OrganizationDto;
+import org.sonar.server.component.ComponentFinder;
+import org.sonar.server.component.ComponentService;
+import org.sonar.server.component.index.ComponentIndexDefinition;
+import org.sonar.server.es.EsTester;
+import org.sonar.server.exceptions.BadRequestException;
+import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.measure.index.ProjectMeasuresIndexDefinition;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.TestRequest;
+import org.sonar.server.ws.WsActionTester;
+import org.sonarqube.ws.MediaTypes;
+import org.sonarqube.ws.WsProjects.BulkUpdateKeyWsResponse;
+import org.sonarqube.ws.WsProjects.BulkUpdateKeyWsResponse.Key;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.tuple;
+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.newFileDto;
+import static org.sonar.db.component.ComponentTesting.newModuleDto;
+import static org.sonar.db.component.ComponentTesting.newProjectDto;
+import static org.sonar.test.JsonAssert.assertJson;
+import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_DRY_RUN;
+import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_FROM;
+import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_PROJECT;
+import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_PROJECT_ID;
+import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_TO;
+
+public class BulkUpdateKeyActionTest {
+  private static final String MY_PROJECT_KEY = "my_project";
+  private static final String FROM = "my_";
+  private static final String TO = "your_";
+
+  private System2 system2 = System2.INSTANCE;
+
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+  @Rule
+  public UserSessionRule userSession = UserSessionRule.standalone();
+  @Rule
+  public EsTester es = new EsTester(new ProjectMeasuresIndexDefinition(new MapSettings()),
+    new ComponentIndexDefinition(new MapSettings()));
+  @Rule
+  public DbTester db = DbTester.create(system2);
+
+  private ComponentDbTester componentDb = new ComponentDbTester(db);
+  private DbClient dbClient = db.getDbClient();
+  private ComponentFinder componentFinder = new ComponentFinder(dbClient);
+  private ComponentService componentService = mock(ComponentService.class);
+  private WsActionTester ws = new WsActionTester(
+    new BulkUpdateKeyAction(dbClient, componentFinder, componentService, userSession));
+
+  @Before
+  public void setUp() {
+    userSession.logIn().setRoot();
+  }
+
+  @Test
+  public void json_example() {
+    OrganizationDto organizationDto = db.organizations().insert();
+    ComponentDto project = componentDb.insertComponent(newProjectDto(organizationDto).setKey("my_project"));
+    componentDb.insertComponent(newModuleDto(project).setKey("my_project:module_1"));
+    ComponentDto anotherProject = componentDb.insertComponent(newProjectDto(organizationDto).setKey("another_project"));
+    componentDb.insertComponent(newModuleDto(anotherProject).setKey("my_new_project:module_1"));
+    ComponentDto module2 = componentDb.insertComponent(newModuleDto(project).setKey("my_project:module_2"));
+    componentDb.insertComponent(newFileDto(module2, null));
+
+    String result = ws.newRequest()
+      .setParam(PARAM_PROJECT, "my_project")
+      .setParam(PARAM_FROM, "my_")
+      .setParam(PARAM_TO, "my_new_")
+      .setParam(PARAM_DRY_RUN, String.valueOf(true))
+      .execute().getInput();
+
+    assertJson(result).withStrictArrayOrder().isSimilarTo(getClass().getResource("bulk_update_key-example.json"));
+  }
+
+  @Test
+  public void dry_run_by_key() {
+    insertMyProject();
+
+    BulkUpdateKeyWsResponse result = callDryRunByKey(MY_PROJECT_KEY, FROM, TO);
+
+    assertThat(result.getKeysCount()).isEqualTo(1);
+    assertThat(result.getKeys(0).getNewKey()).isEqualTo("your_project");
+  }
+
+  @Test
+  public void bulk_update_project_key() {
+    ComponentDto project = insertMyProject();
+    ComponentDto module = componentDb.insertComponent(newModuleDto(project).setKey("my_project:root:module"));
+    ComponentDto inactiveModule = componentDb.insertComponent(newModuleDto(project).setKey("my_project:root:inactive_module").setEnabled(false));
+    ComponentDto file = componentDb.insertComponent(newFileDto(module, null).setKey("my_project:root:module:src/File.xoo"));
+    ComponentDto inactiveFile = componentDb.insertComponent(newFileDto(module, null).setKey("my_project:root:module:src/InactiveFile.xoo").setEnabled(false));
+
+    BulkUpdateKeyWsResponse result = callByUuid(project.uuid(), FROM, TO);
+
+    assertThat(result.getKeysCount()).isEqualTo(2);
+    assertThat(result.getKeysList()).extracting(Key::getKey, Key::getNewKey, Key::getDuplicate)
+      .containsExactly(
+        tuple(project.key(), "your_project", false),
+        tuple(module.key(), "your_project:root:module", false));
+
+    verify(componentService).bulkUpdateKey(any(DbSession.class), eq(project.uuid()), eq(FROM), eq(TO));
+  }
+
+  @Test
+  public void bulk_update_provisioned_project_key() {
+    String newKey = "provisionedProject2";
+    ComponentDto provisionedProject = componentDb.insertProject();
+
+    callByKey(provisionedProject.key(), provisionedProject.getKey(), newKey);
+
+    verify(componentService).bulkUpdateKey(any(DbSession.class), eq(provisionedProject.uuid()), eq(provisionedProject.getKey()), eq(newKey));
+  }
+
+  @Test
+  public void fail_to_bulk_if_a_component_already_exists_with_the_same_key() {
+    componentDb.insertComponent(newProjectDto(db.getDefaultOrganization()).setKey("my_project"));
+    componentDb.insertComponent(newProjectDto(db.getDefaultOrganization()).setKey("your_project"));
+
+    expectedException.expect(BadRequestException.class);
+    expectedException.expectMessage("Impossible to update key: a component with key \"your_project\" already exists.");
+
+    callByKey("my_project", "my_", "your_");
+  }
+
+  @Test
+  public void fail_to_bulk_update_with_invalid_new_key() {
+    insertMyProject();
+
+    expectedException.expect(IllegalArgumentException.class);
+    expectedException.expectMessage("Malformed key for 'my?project'. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit.");
+
+    callByKey(MY_PROJECT_KEY, FROM, "my?");
+  }
+
+  @Test
+  public void fail_to_dry_bulk_update_with_invalid_new_key() {
+    insertMyProject();
+
+    expectedException.expect(IllegalArgumentException.class);
+    expectedException.expectMessage("Malformed key for 'my?project'. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit.");
+
+    callDryRunByKey(MY_PROJECT_KEY, FROM, "my?");
+  }
+
+  @Test
+  public void fail_to_bulk_update_if_not_project_or_module() {
+    ComponentDto project = insertMyProject();
+    ComponentDto file = componentDb.insertComponent(newFileDto(project, null));
+
+    expectedException.expect(IllegalArgumentException.class);
+    expectedException.expectMessage("Component updated must be a module or a key");
+
+    callByKey(file.key(), FROM, TO);
+  }
+
+  @Test
+  public void fail_if_from_string_is_not_provided() {
+    expectedException.expect(IllegalArgumentException.class);
+
+    ComponentDto project = insertMyProject();
+
+    callDryRunByKey(project.key(), null, TO);
+  }
+
+  @Test
+  public void fail_if_to_string_is_not_provided() {
+    expectedException.expect(IllegalArgumentException.class);
+
+    ComponentDto project = insertMyProject();
+
+    callDryRunByKey(project.key(), FROM, null);
+  }
+
+  @Test
+  public void fail_if_uuid_nor_key_provided() {
+    expectedException.expect(IllegalArgumentException.class);
+
+    call(null, null, FROM, TO, false);
+  }
+
+  @Test
+  public void fail_if_uuid_and_key_provided() {
+    expectedException.expect(IllegalArgumentException.class);
+
+    ComponentDto project = insertMyProject();
+
+    call(project.uuid(), project.key(), FROM, TO, false);
+  }
+
+  @Test
+  public void fail_if_project_does_not_exist() {
+    expectedException.expect(NotFoundException.class);
+
+    callDryRunByUuid("UNKNOWN_UUID", FROM, TO);
+  }
+
+  @Test
+  public void throw_ForbiddenException_if_not_project_administrator() {
+    userSession.logIn();
+    ComponentDto project = insertMyProject();
+
+    expectedException.expect(ForbiddenException.class);
+
+    callDryRunByUuid(project.uuid(), FROM, TO);
+  }
+
+  @Test
+  public void api_definition() {
+    WebService.Action definition = ws.getDef();
+
+    assertThat(definition.isPost()).isTrue();
+    assertThat(definition.since()).isEqualTo("6.1");
+    assertThat(definition.key()).isEqualTo("bulk_update_key");
+    assertThat(definition.params())
+      .hasSize(5)
+      .extracting(WebService.Param::key)
+      .containsOnlyOnce("projectId", "project", "from", "to", "dryRun");
+  }
+
+  private ComponentDto insertMyProject() {
+    return componentDb.insertComponent(newProjectDto(db.organizations().insert()).setKey(MY_PROJECT_KEY));
+  }
+
+  private BulkUpdateKeyWsResponse callDryRunByUuid(@Nullable String uuid, @Nullable String from, @Nullable String to) {
+    return call(uuid, null, from, to, true);
+  }
+
+  private BulkUpdateKeyWsResponse callDryRunByKey(@Nullable String key, @Nullable String from, @Nullable String to) {
+    return call(null, key, from, to, true);
+  }
+
+  private BulkUpdateKeyWsResponse callByUuid(@Nullable String uuid, @Nullable String from, @Nullable String to) {
+    return call(uuid, null, from, to, false);
+  }
+
+  private BulkUpdateKeyWsResponse callByKey(@Nullable String key, @Nullable String from, @Nullable String to) {
+    return call(null, key, from, to, false);
+  }
+
+  private BulkUpdateKeyWsResponse call(@Nullable String uuid, @Nullable String key, @Nullable String from, @Nullable String to, @Nullable Boolean dryRun) {
+    TestRequest request = ws.newRequest()
+      .setMediaType(MediaTypes.PROTOBUF);
+
+    if (uuid != null) {
+      request.setParam(PARAM_PROJECT_ID, uuid);
+    }
+    if (key != null) {
+      request.setParam(PARAM_PROJECT, key);
+    }
+    if (from != null) {
+      request.setParam(PARAM_FROM, from);
+    }
+    if (to != null) {
+      request.setParam(PARAM_TO, to);
+    }
+    if (dryRun != null) {
+      request.setParam(PARAM_DRY_RUN, String.valueOf(dryRun));
+    }
+
+    try {
+      return BulkUpdateKeyWsResponse.parseFrom(request.execute().getInputStream());
+    } catch (IOException e) {
+      throw Throwables.propagate(e);
+    }
+  }
+}
index abd0abd5105cfed0f208066e6ae51689bb416c55..1a8b29a0624a92137f76156da656c9cbfefb8fac 100644 (file)
@@ -30,6 +30,6 @@ public class ProjectsWsModuleTest {
   public void verify_count_of_added_components() {
     ComponentContainer container = new ComponentContainer();
     new ProjectsWsModule().configure(container);
-    assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 11);
+    assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 13);
   }
 }
diff --git a/server/sonar-server/src/test/java/org/sonar/server/project/ws/UpdateKeyActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/project/ws/UpdateKeyActionTest.java
new file mode 100644 (file)
index 0000000..222a6ec
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * 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.project.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.project.ProjectsWsParameters.PARAM_NEW_PROJECT;
+import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_PROJECT;
+import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_PROJECT_ID;
+
+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 org.sonar.server.project.ws.UpdateKeyAction(dbClient, new ComponentFinder(dbClient), componentService));
+
+  @Test
+  public void call_by_key() {
+    ComponentDto project = insertProject();
+
+    callByKey(project.key(), ANOTHER_KEY);
+
+    assertCallComponentService(ANOTHER_KEY);
+  }
+
+  @Test
+  public void call_by_uuid() {
+    ComponentDto project = insertProject();
+
+    callByUuid(project.uuid(), ANOTHER_KEY);
+
+    assertCallComponentService(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("projectId", "project", "newProject");
+  }
+
+  private void assertCallComponentService(@Nullable String newKey) {
+    verify(componentService).updateKey(any(DbSession.class), any(ComponentDto.class), eq(newKey));
+  }
+
+  private ComponentDto insertProject() {
+    return componentDb.insertComponent(newProjectDto(db.organizations().insert()));
+  }
+
+  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_PROJECT_ID, uuid);
+    }
+    if (key != null) {
+      request.setParam(PARAM_PROJECT, key);
+    }
+    if (newKey != null) {
+      request.setParam(PARAM_NEW_PROJECT, newKey);
+    }
+
+    return request.execute().getInput();
+  }
+}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/component/BulkUpdateWsRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/component/BulkUpdateWsRequest.java
deleted file mode 100644 (file)
index d86e661..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * 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.sonarqube.ws.client.component;
-
-import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
-import javax.annotation.concurrent.Immutable;
-
-import static com.google.common.base.Preconditions.checkArgument;
-
-@Immutable
-public class BulkUpdateWsRequest {
-  private final String id;
-  private final String key;
-  private final String from;
-  private final String to;
-  private final boolean dryRun;
-
-  public BulkUpdateWsRequest(Builder builder) {
-    this.id = builder.id;
-    this.key = builder.key;
-    this.from = builder.from;
-    this.to = builder.to;
-    this.dryRun = builder.dryRun;
-  }
-
-  @CheckForNull
-  public String getId() {
-    return id;
-  }
-
-  @CheckForNull
-  public String getKey() {
-    return key;
-  }
-
-  public String getFrom() {
-    return from;
-  }
-
-  public String getTo() {
-    return to;
-  }
-
-  public boolean isDryRun() {
-    return dryRun;
-  }
-
-  public static Builder builder() {
-    return new Builder();
-  }
-
-  public static class Builder {
-    private String id;
-    private String key;
-    private String from;
-    private String to;
-    private boolean dryRun;
-
-    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 setFrom(String from) {
-      this.from = from;
-      return this;
-    }
-
-    public Builder setTo(String to) {
-      this.to = to;
-      return this;
-    }
-
-    public Builder setDryRun(boolean dryRun) {
-      this.dryRun = dryRun;
-      return this;
-    }
-
-    public BulkUpdateWsRequest build() {
-      checkArgument(from != null && !from.isEmpty(), "The string to match must not be empty");
-      checkArgument(to != null && !to.isEmpty(), "The string replacement must not be empty");
-      return new BulkUpdateWsRequest(this);
-    }
-  }
-}
index 5a4817ddc1d039252ea71c052611bd3824507cca..202e7eb33ea41acb6a24ab0b3932d7df12cc1045 100644 (file)
@@ -21,35 +21,26 @@ package org.sonarqube.ws.client.component;
 
 import com.google.common.base.Joiner;
 import java.util.List;
-import org.sonarqube.ws.WsComponents.BulkUpdateKeyWsResponse;
 import org.sonarqube.ws.WsComponents.SearchProjectsWsResponse;
 import org.sonarqube.ws.WsComponents.SearchWsResponse;
 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.sonar.api.server.ws.WebService.Param;
-import static org.sonarqube.ws.client.component.ComponentsWsParameters.ACTION_BULK_UPDATE_KEY;
 import static org.sonarqube.ws.client.component.ComponentsWsParameters.ACTION_SEARCH;
 import static org.sonarqube.ws.client.component.ComponentsWsParameters.ACTION_SEARCH_PROJECTS;
 import static org.sonarqube.ws.client.component.ComponentsWsParameters.ACTION_SHOW;
 import static org.sonarqube.ws.client.component.ComponentsWsParameters.ACTION_TREE;
-import static org.sonarqube.ws.client.component.ComponentsWsParameters.ACTION_UPDATE_KEY;
 import static org.sonarqube.ws.client.component.ComponentsWsParameters.CONTROLLER_COMPONENTS;
 import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_COMPONENT;
 import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_COMPONENT_ID;
 import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_FILTER;
-import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_FROM;
-import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_NEW_PROJECT;
 import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_ORGANIZATION;
-import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_PROJECT;
-import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_PROJECT_ID;
 import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_QUALIFIERS;
 import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_STRATEGY;
-import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_TO;
 
 public class ComponentsService extends BaseService {
 
@@ -87,25 +78,6 @@ public class ComponentsService extends BaseService {
     return call(get, ShowWsResponse.parser());
   }
 
-  public void updateKey(UpdateWsRequest request) {
-    PostRequest post = new PostRequest(path(ACTION_UPDATE_KEY))
-      .setParam(PARAM_PROJECT_ID, request.getId())
-      .setParam(PARAM_PROJECT, request.getKey())
-      .setParam(PARAM_NEW_PROJECT, request.getNewKey());
-
-    call(post);
-  }
-
-  public BulkUpdateKeyWsResponse bulkUpdateKey(BulkUpdateWsRequest request) {
-    PostRequest post = new PostRequest(path(ACTION_BULK_UPDATE_KEY))
-      .setParam(PARAM_PROJECT_ID, request.getId())
-      .setParam(PARAM_PROJECT, request.getKey())
-      .setParam(PARAM_FROM, request.getFrom())
-      .setParam(PARAM_TO, request.getTo());
-
-    return call(post, BulkUpdateKeyWsResponse.parser());
-  }
-
   public SearchProjectsWsResponse searchProjects(SearchProjectsRequest request) {
     List<String> additionalFields = request.getAdditionalFields();
     GetRequest get = new GetRequest(path(ACTION_SEARCH_PROJECTS))
index 2b62d38f457ea17f1dc757ec4d57825cb561bbfb..de4f3016a8a19b69214eaeae024a8cf6a8e82581 100644 (file)
@@ -25,10 +25,8 @@ public class ComponentsWsParameters {
 
   // actions
   public static final String ACTION_SEARCH = "search";
-  public static final String ACTION_UPDATE_KEY = "update_key";
   public static final String ACTION_TREE = "tree";
   public static final String ACTION_SHOW = "show";
-  public static final String ACTION_BULK_UPDATE_KEY = "bulk_update_key";
   public static final String ACTION_SEARCH_PROJECTS = "search_projects";
   public static final String ACTION_SUGGESTIONS = "suggestions";
 
@@ -37,15 +35,9 @@ public class ComponentsWsParameters {
   public static final String PARAM_QUALIFIERS = "qualifiers";
   public static final String PARAM_LANGUAGE = "language";
   public static final String PARAM_STRATEGY = "strategy";
-  public static final String PARAM_FROM = "from";
-  public static final String PARAM_TO = "to";
-  public static final String PARAM_DRY_RUN = "dryRun";
   public static final String PARAM_FILTER = "filter";
-  public static final String PARAM_PROJECT_ID = "projectId";
-  public static final String PARAM_PROJECT = "project";
   public static final String PARAM_COMPONENT_ID = "componentId";
   public static final String PARAM_COMPONENT = "component";
-  public static final String PARAM_NEW_PROJECT = "newProject";
 
   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
deleted file mode 100644 (file)
index 074337a..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * 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.sonarqube.ws.client.component;
-
-import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
-import javax.annotation.concurrent.Immutable;
-
-import static com.google.common.base.Preconditions.checkArgument;
-
-@Immutable
-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/main/java/org/sonarqube/ws/client/project/BulkUpdateKeyWsRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/project/BulkUpdateKeyWsRequest.java
new file mode 100644 (file)
index 0000000..41a72f3
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * 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.sonarqube.ws.client.project;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+@Immutable
+public class BulkUpdateKeyWsRequest {
+  private final String id;
+  private final String key;
+  private final String from;
+  private final String to;
+  private final boolean dryRun;
+
+  public BulkUpdateKeyWsRequest(Builder builder) {
+    this.id = builder.id;
+    this.key = builder.key;
+    this.from = builder.from;
+    this.to = builder.to;
+    this.dryRun = builder.dryRun;
+  }
+
+  @CheckForNull
+  public String getId() {
+    return id;
+  }
+
+  @CheckForNull
+  public String getKey() {
+    return key;
+  }
+
+  public String getFrom() {
+    return from;
+  }
+
+  public String getTo() {
+    return to;
+  }
+
+  public boolean isDryRun() {
+    return dryRun;
+  }
+
+  public static Builder builder() {
+    return new Builder();
+  }
+
+  public static class Builder {
+    private String id;
+    private String key;
+    private String from;
+    private String to;
+    private boolean dryRun;
+
+    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 setFrom(String from) {
+      this.from = from;
+      return this;
+    }
+
+    public Builder setTo(String to) {
+      this.to = to;
+      return this;
+    }
+
+    public Builder setDryRun(boolean dryRun) {
+      this.dryRun = dryRun;
+      return this;
+    }
+
+    public BulkUpdateKeyWsRequest build() {
+      checkArgument(from != null && !from.isEmpty(), "The string to match must not be empty");
+      checkArgument(to != null && !to.isEmpty(), "The string replacement must not be empty");
+      return new BulkUpdateKeyWsRequest(this);
+    }
+  }
+}
index b2646fa7ccc5a3a3f2e207d89b7d75c091ac159c..80ae0bf1ea7676feb5dd4ea86b4ffa84c6e6f7f1 100644 (file)
 package org.sonarqube.ws.client.project;
 
 import com.google.common.base.Joiner;
-import org.sonarqube.ws.WsProjects;
+import org.sonarqube.ws.WsProjects.BulkUpdateKeyWsResponse;
 import org.sonarqube.ws.WsProjects.CreateWsResponse;
+import org.sonarqube.ws.WsProjects.SearchWsResponse;
 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.sonar.api.server.ws.WebService.Param.*;
+import static org.sonar.api.server.ws.WebService.Param.PAGE;
+import static org.sonar.api.server.ws.WebService.Param.PAGE_SIZE;
+import static org.sonar.api.server.ws.WebService.Param.TEXT_QUERY;
+import static org.sonarqube.ws.client.project.ProjectsWsParameters.ACTION_BULK_UPDATE_KEY;
 import static org.sonarqube.ws.client.project.ProjectsWsParameters.ACTION_CREATE;
 import static org.sonarqube.ws.client.project.ProjectsWsParameters.ACTION_SEARCH;
+import static org.sonarqube.ws.client.project.ProjectsWsParameters.ACTION_UPDATE_KEY;
 import static org.sonarqube.ws.client.project.ProjectsWsParameters.CONTROLLER;
 import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_BRANCH;
 import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_NAME;
+import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_NEW_PROJECT;
 import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_ORGANIZATION;
 import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_PROJECT;
+import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_PROJECT_ID;
 import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_QUALIFIERS;
 
 /**
@@ -70,13 +77,32 @@ public class ProjectsService extends BaseService {
       .setParam("key", request.getKey()));
   }
 
-  public WsProjects.SearchWsResponse search(SearchWsRequest request) {
+  public void updateKey(UpdateKeyWsRequest request) {
+    PostRequest post = new PostRequest(path(ACTION_UPDATE_KEY))
+      .setParam(PARAM_PROJECT_ID, request.getId())
+      .setParam(PARAM_PROJECT, request.getKey())
+      .setParam(PARAM_NEW_PROJECT, request.getNewKey());
+
+    call(post);
+  }
+
+  public BulkUpdateKeyWsResponse bulkUpdateKey(BulkUpdateKeyWsRequest request) {
+    PostRequest post = new PostRequest(path(ACTION_BULK_UPDATE_KEY))
+      .setParam(PARAM_PROJECT_ID, request.getId())
+      .setParam(PARAM_PROJECT, request.getKey())
+      .setParam(ProjectsWsParameters.PARAM_FROM, request.getFrom())
+      .setParam(ProjectsWsParameters.PARAM_TO, request.getTo());
+
+    return call(post, BulkUpdateKeyWsResponse.parser());
+  }
+
+  public SearchWsResponse search(SearchWsRequest request) {
     GetRequest get = new GetRequest(path(ACTION_SEARCH))
       .setParam(PARAM_ORGANIZATION, request.getOrganization())
       .setParam(PARAM_QUALIFIERS, Joiner.on(",").join(request.getQualifiers()))
       .setParam(TEXT_QUERY, request.getQuery())
       .setParam(PAGE, request.getPage())
       .setParam(PAGE_SIZE, request.getPageSize());
-    return call(get, WsProjects.SearchWsResponse.parser());
+    return call(get, SearchWsResponse.parser());
   }
 }
index fe4371a341feb7badaefe7e09a5851b6dd658861..044b8448e15ab13d0633d68df0bb6f358970bfed 100644 (file)
@@ -28,13 +28,20 @@ public class ProjectsWsParameters {
   public static final String ACTION_CREATE = "create";
   public static final String ACTION_INDEX = "index";
   public static final String ACTION_SEARCH = "search";
+  public static final String ACTION_UPDATE_KEY = "update_key";
+  public static final String ACTION_BULK_UPDATE_KEY = "bulk_update_key";
 
   public static final String PARAM_PROJECT = "project";
   public static final String PARAM_PROJECT_ID = "projectId";
+  public static final String PARAM_NEW_PROJECT = "newProject";
   public static final String PARAM_NAME = "name";
   public static final String PARAM_BRANCH = "branch";
   public static final String PARAM_ORGANIZATION = "organization";
   public static final String PARAM_QUALIFIERS = "qualifiers";
+  public static final String PARAM_FROM = "from";
+  public static final String PARAM_TO = "to";
+  public static final String PARAM_DRY_RUN = "dryRun";
+
 
   public static final String FILTER_LANGUAGE = "language";
 
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/project/UpdateKeyWsRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/project/UpdateKeyWsRequest.java
new file mode 100644 (file)
index 0000000..206cb82
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * 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.sonarqube.ws.client.project;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+@Immutable
+public class UpdateKeyWsRequest {
+  private final String id;
+  private final String key;
+  private final String newKey;
+
+  public UpdateKeyWsRequest(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 UpdateKeyWsRequest build() {
+      checkArgument(newKey != null && !newKey.isEmpty(), "The new key must not be empty");
+      return new UpdateKeyWsRequest(this);
+    }
+  }
+}
index 2c5b2673eb4da071641be5ea2878231b3a12853b..effdadc738683b350914358918ac7fa3679be243 100644 (file)
@@ -56,17 +56,6 @@ message SuggestionsWsResponse {
   }
 }
 
-// WS api/components/prepare_bulk_update_key
-message BulkUpdateKeyWsResponse {
-  repeated Key keys = 1;
-
-  message Key {
-    optional string key = 1;
-    optional string newKey = 2;
-    optional bool duplicate = 3;
-  }
-}
-
 // WS api/components/search_projects
 message SearchProjectsWsResponse {
   optional sonarqube.ws.commons.Paging paging = 1;
index 7dcb6260857967ebc20df189dc6d630f6601cd8d..86ed442a8d6bddc6e447c5bde1f9f342ed035826 100644 (file)
@@ -71,3 +71,13 @@ message SearchWsResponse {
   }
 }
 
+// WS api/projects/prepare_bulk_update_key
+message BulkUpdateKeyWsResponse {
+  repeated Key keys = 1;
+
+  message Key {
+    optional string key = 1;
+    optional string newKey = 2;
+    optional bool duplicate = 3;
+  }
+}
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
deleted file mode 100644 (file)
index 4999b60..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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.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();
-  }
-}
diff --git a/sonar-ws/src/test/java/org/sonarqube/ws/client/project/UpdateKeyWsRequestTest.java b/sonar-ws/src/test/java/org/sonarqube/ws/client/project/UpdateKeyWsRequestTest.java
new file mode 100644 (file)
index 0000000..0eb5197
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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.sonarqube.ws.client.project;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public class UpdateKeyWsRequestTest {
+
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  UpdateKeyWsRequest.Builder underTest = UpdateKeyWsRequest.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();
+  }
+}