]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-10345 Add Webhooks update ws
authorGuillaume Jambet <guillaume.jambet@sonarsource.com>
Thu, 8 Feb 2018 11:07:36 +0000 (12:07 +0100)
committerGuillaume Jambet <guillaume.jambet@gmail.com>
Thu, 1 Mar 2018 14:21:05 +0000 (15:21 +0100)
13 files changed:
server/sonar-db-dao/src/main/java/org/sonar/db/webhook/WebhookDao.java
server/sonar-db-dao/src/main/java/org/sonar/db/webhook/WebhookMapper.java
server/sonar-db-dao/src/main/resources/org/sonar/db/webhook/WebhookMapper.xml
server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookDaoTest.java
server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookDbTester.java
server/sonar-server/src/main/java/org/sonar/server/webhook/ws/CreateAction.java
server/sonar-server/src/main/java/org/sonar/server/webhook/ws/UpdateAction.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhookSupport.java
server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhooksWsModule.java
server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhooksWsParameters.java
server/sonar-server/src/test/java/org/sonar/server/webhook/ws/CreateActionTest.java
server/sonar-server/src/test/java/org/sonar/server/webhook/ws/UpdateActionTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/webhook/ws/WebhooksWsModuleTest.java

index 9bec0fe477f5c9bb802ac914a6e4ff4ba41fa2e3..b972edbe6653be7064b1c3f8d65c1babf82a362c 100644 (file)
@@ -39,6 +39,14 @@ public class WebhookDao implements Dao {
     return Optional.ofNullable(mapper(dbSession).selectByUuid(uuid));
   }
 
+  public List<WebhookDto> selectByOrganizationUuid(DbSession dbSession, String organizationUuid) {
+    return mapper(dbSession).selectForOrganizationUuidOrderedByName(organizationUuid);
+  }
+
+  public List<WebhookDto> selectByProjectUuid(DbSession dbSession, String projectUuid) {
+    return mapper(dbSession).selectForProjectUuidOrderedByName(projectUuid);
+  }
+
   public void insert(DbSession dbSession, WebhookDto dto) {
 
     checkState(dto.getOrganizationUuid() != null || dto.getProjectUuid() != null,
@@ -48,14 +56,11 @@ public class WebhookDao implements Dao {
       "A webhook can not be linked to both an organization and a project.");
 
     mapper(dbSession).insert(dto.setCreatedAt(system2.now()));
-  }
 
-  public List<WebhookDto> selectByOrganizationUuid(DbSession dbSession, String organizationUuid) {
-    return mapper(dbSession).selectForOrganizationUuidOrderedByName(organizationUuid);
   }
 
-  public List<WebhookDto> selectByProjectUuid(DbSession dbSession, String projectUuid) {
-    return mapper(dbSession).selectForProjectUuidOrderedByName(projectUuid);
+  public void update(DbSession dbSession, WebhookDto dto) {
+    mapper(dbSession).update(dto.setUpdatedAt(system2.now()));
   }
 
   private static WebhookMapper mapper(DbSession dbSession) {
index 12499745c35e63f3a96ce2b82f8606fe9a8afdfb..3b5706c62bc08e6af42de3997e46b8088819d5bf 100644 (file)
@@ -34,4 +34,6 @@ public interface WebhookMapper {
 
   void insert(WebhookDto dto);
 
+  void update(WebhookDto dto);
+
 }
index c85df4636123141061bc5f03e600d2b5458cf352..0eb69994650ddadd3f297b1eea1a25891ccaf5a9 100644 (file)
     )
   </insert>
 
+  <update id="update" parameterType="org.sonar.db.webhook.WebhookDto">
+    update webhooks set
+    name=#{name},
+    url=#{url},
+    updated_at=#{updatedAt, jdbcType=BIGINT}
+    where uuid=#{uuid, jdbcType=VARCHAR}
+  </update>
+
 </mapper>
index 4b52eaae3c7f2a7b43066a7838b7c142c6245da8..01e8ef62225163baaf464d00d69ee30454d25f05 100644 (file)
@@ -28,6 +28,8 @@ 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.organization.OrganizationDbTester;
+import org.sonar.db.organization.OrganizationDto;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
@@ -44,6 +46,8 @@ public class WebhookDaoTest {
   private final DbClient dbClient = dbTester.getDbClient();
   private final DbSession dbSession = dbTester.getSession();
   private final WebhookDao underTest = dbClient.webhookDao();
+  private final WebhookDbTester webhookDbTester = dbTester.webhooks();
+  private final OrganizationDbTester organizationDbTester = dbTester.organizations();
 
   @Test
   public void selectByUuid_returns_empty_if_uuid_does_not_exist() {
@@ -83,17 +87,36 @@ public class WebhookDaoTest {
 
     underTest.insert(dbSession, dto);
 
-    WebhookDto stored = selectByUuid(dto.getUuid());
+    WebhookDto reloaded = selectByUuid(dto.getUuid());
 
-    assertThat(stored.getUuid()).isEqualTo(dto.getUuid());
-    assertThat(stored.getName()).isEqualTo(dto.getName());
-    assertThat(stored.getUrl()).isEqualTo(dto.getUrl());
-    assertThat(stored.getOrganizationUuid()).isNull();
-    assertThat(stored.getProjectUuid()).isEqualTo(dto.getProjectUuid());
-    assertThat(new Date(stored.getCreatedAt())).isInSameMinuteWindowAs(new Date(system2.now()));
-    assertThat(stored.getUpdatedAt()).isNull();
+    assertThat(reloaded.getUuid()).isEqualTo(dto.getUuid());
+    assertThat(reloaded.getName()).isEqualTo(dto.getName());
+    assertThat(reloaded.getUrl()).isEqualTo(dto.getUrl());
+    assertThat(reloaded.getOrganizationUuid()).isNull();
+    assertThat(reloaded.getProjectUuid()).isEqualTo(dto.getProjectUuid());
+    assertThat(new Date(reloaded.getCreatedAt())).isInSameMinuteWindowAs(new Date(system2.now()));
+    assertThat(reloaded.getUpdatedAt()).isNull();
   }
 
+  @Test
+  public void update() {
+
+    OrganizationDto organization = organizationDbTester.insert();
+    WebhookDto dto = webhookDbTester.insertForOrganizationUuid(organization.getUuid());
+
+    underTest.update(dbSession, dto.setName("a-fancy-webhook").setUrl("http://www.fancy-webhook.io"));
+
+    WebhookDto reloaded = underTest.selectByUuid(dbSession, dto.uuid).get();
+    assertThat(reloaded.getUuid()).isEqualTo(dto.getUuid());
+    assertThat(reloaded.getName()).isEqualTo("a-fancy-webhook");
+    assertThat(reloaded.getUrl()).isEqualTo("http://www.fancy-webhook.io");
+    assertThat(reloaded.getProjectUuid()).isNull();
+    assertThat(reloaded.getOrganizationUuid()).isEqualTo(dto.getOrganizationUuid());
+    assertThat(reloaded.getCreatedAt()).isEqualTo(dto.getCreatedAt());
+    assertThat(new Date(reloaded.getUpdatedAt())).isInSameMinuteWindowAs(new Date(system2.now()));
+  }
+
+
   @Test
   public void fail_if_webhook_does_not_have_an_organization_nor_a_project() {
 
index a0fcc49a0f3a6635f6a41c854f7a2e1be729f80b..7b744a52db6b45cde8f4c4bc1e06cd4e3fa5b6c2 100644 (file)
  */
 package org.sonar.db.webhook;
 
+import java.util.Optional;
 import org.sonar.db.DbSession;
 import org.sonar.db.DbTester;
 
+import static org.sonar.db.webhook.WebhookTesting.newWebhookDtoForOrganization;
+import static org.sonar.db.webhook.WebhookTesting.newWebhookDtoForProject;
+
 public class WebhookDbTester {
 
   private final DbTester dbTester;
@@ -31,11 +35,11 @@ public class WebhookDbTester {
   }
 
   public WebhookDto insertForOrganizationUuid(String organizationUuid) {
-    return insert(WebhookTesting.newWebhookDtoForOrganization(organizationUuid));
+    return insert(newWebhookDtoForOrganization(organizationUuid));
   }
 
   public WebhookDto insertWebhookForProjectUuid(String projectUuid) {
-    return insert(WebhookTesting.newWebhookDtoForProject(projectUuid));
+    return insert(newWebhookDtoForProject(projectUuid));
   }
 
   public WebhookDto insert(WebhookDto dto) {
@@ -45,4 +49,8 @@ public class WebhookDbTester {
     return dto;
   }
 
+  public Optional<WebhookDto> selectWebhook(String uuid) {
+    DbSession dbSession = dbTester.getSession();
+    return dbTester.getDbClient().webhookDao().selectByUuid(dbSession, uuid);
+  }
 }
index b5962eb3814dc24cd83ef0741f1dd7c78bf55d02..f3ecf08f1ffd15b42d100272a9ad7695040fa3f3 100644 (file)
@@ -37,10 +37,7 @@ import org.sonarqube.ws.Webhooks;
 
 import static com.google.common.base.Preconditions.checkState;
 import static java.lang.String.format;
-import static java.util.Locale.ENGLISH;
 import static org.apache.commons.lang.StringUtils.isNotBlank;
-import static org.sonar.api.web.UserRole.ADMIN;
-import static org.sonar.db.permission.OrganizationPermission.ADMINISTER;
 import static org.sonar.server.webhook.ws.WebhooksWsParameters.ACTION_CREATE;
 import static org.sonar.server.webhook.ws.WebhooksWsParameters.NAME_PARAM;
 import static org.sonar.server.webhook.ws.WebhooksWsParameters.NAME_PARAM_MAXIMUM_LENGTH;
@@ -68,12 +65,15 @@ public class CreateAction implements WebhooksWsAction {
   private final UserSession userSession;
   private final DefaultOrganizationProvider defaultOrganizationProvider;
   private final UuidFactory uuidFactory;
+  private final WebhookSupport webhookSupport;
 
-  public CreateAction(DbClient dbClient, UserSession userSession, DefaultOrganizationProvider defaultOrganizationProvider, UuidFactory uuidFactory) {
+  public CreateAction(DbClient dbClient, UserSession userSession, DefaultOrganizationProvider defaultOrganizationProvider,
+                      UuidFactory uuidFactory, WebhookSupport webhookSupport) {
     this.dbClient = dbClient;
     this.userSession = userSession;
     this.defaultOrganizationProvider = defaultOrganizationProvider;
     this.uuidFactory = uuidFactory;
+    this.webhookSupport = webhookSupport;
   }
 
   @Override
@@ -139,13 +139,13 @@ public class CreateAction implements WebhooksWsAction {
         Optional<ComponentDto> dtoOptional = Optional.ofNullable(dbClient.componentDao().selectByKey(dbSession, projectKey).orNull());
         ComponentDto componentDto = checkFoundWithOptional(dtoOptional, "No project with key '%s'", projectKey);
         checkThatProjectBelongsToOrganization(componentDto, organizationDto, "Project '%s' does not belong to organisation '%s'", projectKey, organizationKey);
-        checkUserPermissionOn(componentDto);
+        webhookSupport.checkUserPermissionOn(componentDto);
         projectDto = componentDto;
       } else {
-        checkUserPermissionOn(organizationDto);
+        webhookSupport.checkUserPermissionOn(organizationDto);
       }
 
-      checkUrlPattern(url, "Url parameter with value '%s' is not a valid url", url);
+      webhookSupport.checkUrlPattern(url, "Url parameter with value '%s' is not a valid url", url);
 
       WebhookDto webhookDto = doHandle(dbSession, organizationDto, projectDto, name, url);
 
@@ -166,7 +166,7 @@ public class CreateAction implements WebhooksWsAction {
     WebhookDto dto = new WebhookDto()
       .setUuid(uuidFactory.create())
       .setName(name)
-      .setUrl(url)
+      .setUrl(url);
 
     if (project != null) {
       checkNumberOfWebhook(numberOfWebhookOf(dbSession, project), "Maximum number of webhook reached for project '%s'", project.getKey());
@@ -203,30 +203,12 @@ public class CreateAction implements WebhooksWsAction {
     return dbClient.webhookDao().selectByProjectUuid(dbSession, project.uuid()).size();
   }
 
-  private void checkUserPermissionOn(ComponentDto componentDto) {
-    userSession.checkComponentPermission(ADMIN, componentDto);
-  }
-
-  private void checkUserPermissionOn(OrganizationDto organizationDto) {
-    userSession.checkPermission(ADMINISTER, organizationDto);
-  }
-
   private static void checkThatProjectBelongsToOrganization(ComponentDto componentDto, OrganizationDto organizationDto, String message, Object... messageArguments) {
     if (!organizationDto.getUuid().equals(componentDto.getOrganizationUuid())) {
       throw new NotFoundException(format(message, messageArguments));
     }
   }
 
-  private static void checkUrlPattern(String url, String message, Object... messageArguments) {
-    if (!url.toLowerCase(ENGLISH).startsWith("http://") && !url.toLowerCase(ENGLISH).startsWith("https://")) {
-      throw new IllegalArgumentException(format(message, messageArguments));
-    }
-    String sub = url.substring("http://".length());
-    if (sub.contains(":") && !sub.substring(sub.indexOf(':')).contains("@")) {
-      throw new IllegalArgumentException(format(message, messageArguments));
-    }
-  }
-
   private OrganizationDto defaultOrganizationDto(DbSession dbSession) {
     String uuid = defaultOrganizationProvider.get().getUuid();
     Optional<OrganizationDto> organizationDto = dbClient.organizationDao().selectByUuid(dbSession, uuid);
diff --git a/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/UpdateAction.java b/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/UpdateAction.java
new file mode 100644 (file)
index 0000000..e9b3a81
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.webhook.ws;
+
+import java.util.Optional;
+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.db.organization.OrganizationDto;
+import org.sonar.db.webhook.WebhookDto;
+import org.sonar.server.user.UserSession;
+
+import static java.util.Optional.ofNullable;
+import static org.sonar.server.webhook.ws.WebhooksWsParameters.KEY_PARAM;
+import static org.sonar.server.webhook.ws.WebhooksWsParameters.KEY_PARAM_MAXIMUN_LENGTH;
+import static org.sonar.server.webhook.ws.WebhooksWsParameters.NAME_PARAM;
+import static org.sonar.server.webhook.ws.WebhooksWsParameters.NAME_PARAM_MAXIMUM_LENGTH;
+import static org.sonar.server.webhook.ws.WebhooksWsParameters.UPDATE_ACTION;
+import static org.sonar.server.webhook.ws.WebhooksWsParameters.URL_PARAM;
+import static org.sonar.server.webhook.ws.WebhooksWsParameters.URL_PARAM_MAXIMUM_LENGTH;
+import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
+import static org.sonar.server.ws.KeyExamples.NAME_WEBHOOK_EXAMPLE_001;
+import static org.sonar.server.ws.KeyExamples.URL_WEBHOOK_EXAMPLE_001;
+import static org.sonar.server.ws.WsUtils.checkFoundWithOptional;
+import static org.sonar.server.ws.WsUtils.checkStateWithOptional;
+
+public class UpdateAction implements WebhooksWsAction {
+
+  private final DbClient dbClient;
+  private final UserSession userSession;
+  private final WebhookSupport webhookSupport;
+
+  public UpdateAction(DbClient dbClient, UserSession userSession, WebhookSupport webhookSupport) {
+    this.dbClient = dbClient;
+    this.userSession = userSession;
+    this.webhookSupport = webhookSupport;
+  }
+
+  @Override
+  public void define(WebService.NewController controller) {
+
+    WebService.NewAction action = controller.createAction(UPDATE_ACTION)
+      .setPost(true)
+      .setDescription("Update a Webhook.<br>" +
+        "Requires the global, organization or project permission.")
+      .setSince("7.1")
+      .setHandler(this);
+
+    action.createParam(KEY_PARAM)
+      .setRequired(true)
+      .setMaximumLength(KEY_PARAM_MAXIMUN_LENGTH)
+      .setDescription("The key of the webhook to be updated")
+      .setExampleValue(KEY_PROJECT_EXAMPLE_001);
+
+    action.createParam(NAME_PARAM)
+      .setRequired(true)
+      .setMaximumLength(NAME_PARAM_MAXIMUM_LENGTH)
+      .setDescription("new name of the webhook")
+      .setExampleValue(NAME_WEBHOOK_EXAMPLE_001);
+
+    action.createParam(URL_PARAM)
+      .setRequired(true)
+      .setMaximumLength(URL_PARAM_MAXIMUM_LENGTH)
+      .setDescription("new url to be called by the webhook")
+      .setExampleValue(URL_WEBHOOK_EXAMPLE_001);
+
+  }
+
+  @Override
+  public void handle(Request request, Response response) throws Exception {
+
+    userSession.checkLoggedIn();
+
+    String webhookKey = request.param(KEY_PARAM);
+    String name = request.mandatoryParam(NAME_PARAM);
+    String url = request.mandatoryParam(URL_PARAM);
+
+    webhookSupport.checkUrlPattern(url, "Url parameter with value '%s' is not a valid url", url);
+
+    try (DbSession dbSession = dbClient.openSession(false)) {
+
+      Optional<WebhookDto> dtoOptional = dbClient.webhookDao().selectByUuid(dbSession, webhookKey);
+      WebhookDto webhookDto = checkFoundWithOptional(dtoOptional, "No webhook with key '%s'", webhookKey);
+
+      String organizationUuid = webhookDto.getOrganizationUuid();
+      if (organizationUuid != null) {
+        Optional<OrganizationDto> optionalDto = dbClient.organizationDao().selectByUuid(dbSession, organizationUuid);
+        OrganizationDto organizationDto = checkStateWithOptional(optionalDto, "the requested organization '%s' was not found", organizationUuid);
+        webhookSupport.checkUserPermissionOn(organizationDto);
+        updateWebhook(dbSession, webhookDto, name, url);
+      }
+
+      String projectUuid = webhookDto.getProjectUuid();
+      if (projectUuid != null) {
+        Optional<ComponentDto> optionalDto = ofNullable(dbClient.componentDao().selectByUuid(dbSession, projectUuid).orNull());
+        ComponentDto componentDto = checkStateWithOptional(optionalDto, "the requested project '%s' was not found", projectUuid);
+        webhookSupport.checkUserPermissionOn(componentDto);
+        updateWebhook(dbSession, webhookDto, name, url);
+      }
+
+      dbSession.commit();
+    }
+
+    response.noContent();
+  }
+
+  private void updateWebhook(DbSession dbSession, WebhookDto webhookDto, String name, String url) {
+    dbClient.webhookDao().update(dbSession, webhookDto.setName(name).setUrl(url));
+  }
+
+}
index 3201daf29bae6b316192e62e79746f9dd7521ead..2d6257069d4342f8349198c80b47a7c6cb048850 100644 (file)
  */
 package org.sonar.server.webhook.ws;
 
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.organization.OrganizationDto;
+import org.sonar.server.user.UserSession;
+
 import static java.lang.String.format;
 import static java.util.Locale.ENGLISH;
+import static org.sonar.api.web.UserRole.ADMIN;
+import static org.sonar.db.permission.OrganizationPermission.ADMINISTER;
+
+public class WebhookSupport {
+
+  private final UserSession userSession;
 
-class WebhookSupport {
+  public WebhookSupport(UserSession userSession) {
+    this.userSession = userSession;
+  }
+
+  void checkUserPermissionOn(ComponentDto componentDto) {
+    userSession.checkComponentPermission(ADMIN, componentDto);
+  }
 
-  private WebhookSupport() {
-    // only statics
+  void checkUserPermissionOn(OrganizationDto organizationDto) {
+    userSession.checkPermission(ADMINISTER, organizationDto);
   }
 
-  static void checkUrlPattern(String url, String message, Object... messageArguments) {
+  void checkUrlPattern(String url, String message, Object... messageArguments) {
     if (!url.toLowerCase(ENGLISH).startsWith("http://") && !url.toLowerCase(ENGLISH).startsWith("https://")) {
       throw new IllegalArgumentException(format(message, messageArguments));
     }
index 92334805107e9123aca3edeef01c97a4f95c2f7b..3927c5da41b3535a4340ce10b038c623b3b719fe 100644 (file)
@@ -25,9 +25,11 @@ public class WebhooksWsModule extends Module {
   @Override
   protected void configureModule() {
     add(
+      WebhookSupport.class,
       WebhooksWs.class,
       SearchAction.class,
       CreateAction.class,
+      UpdateAction.class,
       WebhookDeliveryAction.class,
       WebhookDeliveriesAction.class);
   }
index 0d800f476c64e844e1762dcc5e2508c10d8c47ce..10b2c7b04980131072924eb9d0da4bc7630dca5e 100644 (file)
@@ -25,6 +25,7 @@ class WebhooksWsParameters {
 
 
   static final String ACTION_CREATE = "create";
+  static final String UPDATE_ACTION = "update";
   static final String SEARCH_ACTION = "search";
 
 
@@ -38,6 +39,8 @@ class WebhooksWsParameters {
   static final int URL_PARAM_MAXIMUM_LENGTH = 512;
   static final String COMPONENT_KEY_PARAM = "component";
   static final int COMPONENT_KEY_PARAM_MAXIMUM_LENGTH = 255;
+  static final String KEY_PARAM = "key";
+  static final int KEY_PARAM_MAXIMUN_LENGTH = 40;
 
   private WebhooksWsParameters() {
     // prevent instantiation
index 3ffbf303afa2385607abb54e8365d278dede7427..fc3f7bcdfb443cc718b3debefade7dfdc4caad1e 100644 (file)
@@ -74,7 +74,8 @@ public class CreateActionTest {
   private DefaultOrganizationProvider defaultOrganizationProvider = from(db);
   private UuidFactory uuidFactory = UuidFactoryFast.getInstance();
 
-  private org.sonar.server.webhook.ws.CreateAction underTest = new CreateAction(dbClient, userSession, defaultOrganizationProvider, uuidFactory);
+  private WebhookSupport webhookSupport = new WebhookSupport(userSession);
+  private org.sonar.server.webhook.ws.CreateAction underTest = new CreateAction(dbClient, userSession, defaultOrganizationProvider, uuidFactory, webhookSupport);
   private WsActionTester wsActionTester = new WsActionTester(underTest);
 
   @Test
diff --git a/server/sonar-server/src/test/java/org/sonar/server/webhook/ws/UpdateActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/webhook/ws/UpdateActionTest.java
new file mode 100644 (file)
index 0000000..0bf1e5e
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.webhook.ws;
+
+import java.util.Optional;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.ComponentDbTester;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.organization.OrganizationDbTester;
+import org.sonar.db.organization.OrganizationDto;
+import org.sonar.db.webhook.WebhookDbTester;
+import org.sonar.db.webhook.WebhookDto;
+import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.server.organization.DefaultOrganizationProvider;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.TestResponse;
+import org.sonar.server.ws.WsActionTester;
+
+import static java.net.HttpURLConnection.HTTP_NO_CONTENT;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.AssertionsForClassTypes.tuple;
+import static org.junit.rules.ExpectedException.none;
+import static org.sonar.api.web.UserRole.ADMIN;
+import static org.sonar.db.DbTester.create;
+import static org.sonar.db.permission.OrganizationPermission.ADMINISTER;
+import static org.sonar.server.organization.TestDefaultOrganizationProvider.from;
+import static org.sonar.server.tester.UserSessionRule.standalone;
+import static org.sonar.server.webhook.ws.WebhooksWsParameters.KEY_PARAM;
+import static org.sonar.server.webhook.ws.WebhooksWsParameters.NAME_PARAM;
+import static org.sonar.server.webhook.ws.WebhooksWsParameters.URL_PARAM;
+import static org.sonar.server.ws.KeyExamples.NAME_WEBHOOK_EXAMPLE_001;
+import static org.sonar.server.ws.KeyExamples.URL_WEBHOOK_EXAMPLE_001;
+
+public class UpdateActionTest {
+
+  @Rule
+  public ExpectedException expectedException = none();
+
+  @Rule
+  public UserSessionRule userSession = standalone();
+
+  @Rule
+  public DbTester db = create();
+  private DbClient dbClient = db.getDbClient();
+  private WebhookDbTester webhookDbTester = db.webhooks();
+  private OrganizationDbTester organizationDbTester = db.organizations();
+  private ComponentDbTester componentDbTester = db.components();
+
+  private DefaultOrganizationProvider defaultOrganizationProvider = from(db);
+
+  private WebhookSupport webhookSupport = new WebhookSupport(userSession);
+  private UpdateAction underTest = new UpdateAction(dbClient, userSession, webhookSupport);
+  private WsActionTester wsActionTester = new WsActionTester(underTest);
+
+  @Test
+  public void test_ws_definition() {
+
+    WebService.Action action = wsActionTester.getDef();
+    assertThat(action).isNotNull();
+    assertThat(action.isInternal()).isFalse();
+    assertThat(action.isPost()).isTrue();
+
+    assertThat(action.params())
+      .extracting(WebService.Param::key, WebService.Param::isRequired)
+      .containsExactlyInAnyOrder(
+        tuple("key", true),
+        tuple("name", true),
+        tuple("url", true));
+
+  }
+
+  @Test
+  public void update_a_project_webhook() {
+
+    ComponentDto project = componentDbTester.insertPrivateProject();
+    WebhookDto dto = webhookDbTester.insertWebhookForProjectUuid(project.uuid());
+    userSession.logIn().addProjectPermission(ADMIN, project);
+
+    TestResponse response = wsActionTester.newRequest()
+      .setParam(KEY_PARAM, dto.getUuid())
+      .setParam(NAME_PARAM, NAME_WEBHOOK_EXAMPLE_001)
+      .setParam(URL_PARAM, URL_WEBHOOK_EXAMPLE_001)
+      .execute();
+
+    assertThat(response.getStatus()).isEqualTo(HTTP_NO_CONTENT);
+    Optional<WebhookDto> reloaded = webhookDbTester.selectWebhook(dto.getUuid());
+    assertThat(reloaded.get()).isNotNull();
+    assertThat(reloaded.get().getName()).isEqualTo(NAME_WEBHOOK_EXAMPLE_001);
+    assertThat(reloaded.get().getUrl()).isEqualTo(URL_WEBHOOK_EXAMPLE_001);
+    assertThat(reloaded.get().getOrganizationUuid()).isNull();
+    assertThat(reloaded.get().getProjectUuid()).isEqualTo(dto.getProjectUuid());
+
+  }
+
+  @Test
+  public void update_an_organization_webhook() {
+
+    OrganizationDto organization = organizationDbTester.insert();
+    WebhookDto dto = webhookDbTester.insertForOrganizationUuid(organization.getUuid());
+    userSession.logIn().addPermission(ADMINISTER, organization.getUuid());
+
+    TestResponse response = wsActionTester.newRequest()
+      .setParam(KEY_PARAM, dto.getUuid())
+      .setParam(NAME_PARAM, NAME_WEBHOOK_EXAMPLE_001)
+      .setParam(URL_PARAM, URL_WEBHOOK_EXAMPLE_001)
+      .execute();
+
+    assertThat(response.getStatus()).isEqualTo(HTTP_NO_CONTENT);
+    Optional<WebhookDto> reloaded = webhookDbTester.selectWebhook(dto.getUuid());
+    assertThat(reloaded.get()).isNotNull();
+    assertThat(reloaded.get().getName()).isEqualTo(NAME_WEBHOOK_EXAMPLE_001);
+    assertThat(reloaded.get().getUrl()).isEqualTo(URL_WEBHOOK_EXAMPLE_001);
+    assertThat(reloaded.get().getOrganizationUuid()).isEqualTo(dto.getOrganizationUuid());
+    assertThat(reloaded.get().getProjectUuid()).isNull();
+
+  }
+
+  @Test
+  public void fail_if_webhook_does_not_exist() {
+
+    userSession.logIn().addPermission(ADMINISTER, defaultOrganizationProvider.get().getUuid());
+
+    expectedException.expect(NotFoundException.class);
+    expectedException.expectMessage("No webhook with key 'inexistent-webhook-uuid'");
+
+    wsActionTester.newRequest()
+      .setParam(KEY_PARAM, "inexistent-webhook-uuid")
+      .setParam(NAME_PARAM, NAME_WEBHOOK_EXAMPLE_001)
+      .setParam(URL_PARAM, URL_WEBHOOK_EXAMPLE_001)
+      .execute();
+  }
+
+  @Test
+  public void fail_if_not_logged_in() throws Exception {
+
+    OrganizationDto organization = organizationDbTester.insert();
+    WebhookDto dto = webhookDbTester.insertForOrganizationUuid(organization.getUuid());
+    userSession.anonymous();
+
+    expectedException.expect(UnauthorizedException.class);
+
+    wsActionTester.newRequest()
+      .setParam(KEY_PARAM, dto.getUuid())
+      .setParam(NAME_PARAM, NAME_WEBHOOK_EXAMPLE_001)
+      .setParam(URL_PARAM, URL_WEBHOOK_EXAMPLE_001)
+      .execute();
+
+  }
+
+  @Test
+  public void fail_if_no_permission_on_webhook_scope_project() {
+
+    ComponentDto project = componentDbTester.insertPrivateProject();
+    WebhookDto dto = webhookDbTester.insertWebhookForProjectUuid(project.uuid());
+
+    userSession.logIn();
+
+    expectedException.expect(ForbiddenException.class);
+    expectedException.expectMessage("Insufficient privileges");
+
+    wsActionTester.newRequest()
+      .setParam(KEY_PARAM, dto.getUuid())
+      .setParam(NAME_PARAM, NAME_WEBHOOK_EXAMPLE_001)
+      .setParam(URL_PARAM, URL_WEBHOOK_EXAMPLE_001)
+      .execute();
+
+  }
+
+  @Test
+  public void fail_if_no_permission_on_webhook_scope_organization() {
+
+    OrganizationDto organization = organizationDbTester.insert();
+    WebhookDto dto = webhookDbTester.insertForOrganizationUuid(organization.getUuid());
+
+    userSession.logIn();
+
+    expectedException.expect(ForbiddenException.class);
+    expectedException.expectMessage("Insufficient privileges");
+
+    wsActionTester.newRequest()
+      .setParam(KEY_PARAM, dto.getUuid())
+      .setParam(NAME_PARAM, NAME_WEBHOOK_EXAMPLE_001)
+      .setParam(URL_PARAM, URL_WEBHOOK_EXAMPLE_001)
+      .execute();
+
+  }
+
+  @Test
+  public void fail_if_url_is_not_valid() throws Exception {
+
+    ComponentDto project = componentDbTester.insertPrivateProject();
+    WebhookDto dto = webhookDbTester.insertWebhookForProjectUuid(project.uuid());
+    userSession.logIn().addProjectPermission(ADMIN, project);
+
+    expectedException.expect(IllegalArgumentException.class);
+
+    wsActionTester.newRequest()
+      .setParam(KEY_PARAM, dto.getUuid())
+      .setParam(NAME_PARAM, NAME_WEBHOOK_EXAMPLE_001)
+      .setParam(URL_PARAM, "htp://www.wrong-protocol.com/")
+      .execute();
+
+  }
+
+  @Test
+  public void fail_if_credential_in_url_is_have_a_wrong_format() throws Exception {
+
+    ComponentDto project = componentDbTester.insertPrivateProject();
+    WebhookDto dto = webhookDbTester.insertWebhookForProjectUuid(project.uuid());
+    userSession.logIn().addProjectPermission(ADMIN, project);
+
+    expectedException.expect(IllegalArgumentException.class);
+
+    wsActionTester.newRequest()
+      .setParam(KEY_PARAM, dto.getUuid())
+      .setParam(NAME_PARAM, NAME_WEBHOOK_EXAMPLE_001)
+      .setParam(URL_PARAM, "http://:www.wrong-protocol.com/")
+      .execute();
+
+  }
+
+}
index 96aae56460a3839b223b0bef6139f267ff03887e..7afb02511bdcc15ad9457b0dfb8740cafb96898e 100644 (file)
@@ -32,7 +32,7 @@ public class WebhooksWsModuleTest {
   public void verify_count_of_added_components() {
     ComponentContainer container = new ComponentContainer();
     underTest.configure(container);
-    assertThat(container.size()).isEqualTo(3 + 4);
+    assertThat(container.size()).isEqualTo(3 + 6);
   }
 
 }