aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorGuillaume Jambet <guillaume.jambet@sonarsource.com>2018-02-08 12:07:36 +0100
committerGuillaume Jambet <guillaume.jambet@gmail.com>2018-03-01 15:21:05 +0100
commit07a20e83c4037ab7853c57aa92e614d47be74cf4 (patch)
treef5a6682a6d0cc52ba5aed8152f084688891dfc62 /server
parent67ac2e4220a7ce34533d1c14c8f5f9652c37e08f (diff)
downloadsonarqube-07a20e83c4037ab7853c57aa92e614d47be74cf4.tar.gz
sonarqube-07a20e83c4037ab7853c57aa92e614d47be74cf4.zip
SONAR-10345 Add Webhooks update ws
Diffstat (limited to 'server')
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/webhook/WebhookDao.java15
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/webhook/WebhookMapper.java2
-rw-r--r--server/sonar-db-dao/src/main/resources/org/sonar/db/webhook/WebhookMapper.xml8
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookDaoTest.java39
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookDbTester.java12
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/webhook/ws/CreateAction.java34
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/webhook/ws/UpdateAction.java131
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhookSupport.java24
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhooksWsModule.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhooksWsParameters.java3
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/webhook/ws/CreateActionTest.java3
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/webhook/ws/UpdateActionTest.java246
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/webhook/ws/WebhooksWsModuleTest.java2
13 files changed, 474 insertions, 47 deletions
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/webhook/WebhookDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/webhook/WebhookDao.java
index 9bec0fe477f..b972edbe665 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/webhook/WebhookDao.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/webhook/WebhookDao.java
@@ -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) {
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/webhook/WebhookMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/webhook/WebhookMapper.java
index 12499745c35..3b5706c62bc 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/webhook/WebhookMapper.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/webhook/WebhookMapper.java
@@ -34,4 +34,6 @@ public interface WebhookMapper {
void insert(WebhookDto dto);
+ void update(WebhookDto dto);
+
}
diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/webhook/WebhookMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/webhook/WebhookMapper.xml
index c85df463612..0eb69994650 100644
--- a/server/sonar-db-dao/src/main/resources/org/sonar/db/webhook/WebhookMapper.xml
+++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/webhook/WebhookMapper.xml
@@ -55,4 +55,12 @@
)
</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>
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookDaoTest.java
index 4b52eaae3c7..01e8ef62225 100644
--- a/server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookDaoTest.java
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookDaoTest.java
@@ -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,18 +87,37 @@ 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() {
expectedException.expect(IllegalStateException.class);
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookDbTester.java b/server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookDbTester.java
index a0fcc49a0f3..7b744a52db6 100644
--- a/server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookDbTester.java
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookDbTester.java
@@ -19,9 +19,13 @@
*/
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);
+ }
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/CreateAction.java b/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/CreateAction.java
index b5962eb3814..f3ecf08f1ff 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/CreateAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/CreateAction.java
@@ -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
index 00000000000..e9b3a819771
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/UpdateAction.java
@@ -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));
+ }
+
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhookSupport.java b/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhookSupport.java
index 3201daf29ba..2d6257069d4 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhookSupport.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhookSupport.java
@@ -19,16 +19,32 @@
*/
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));
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhooksWsModule.java b/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhooksWsModule.java
index 92334805107..3927c5da41b 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhooksWsModule.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhooksWsModule.java
@@ -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);
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhooksWsParameters.java b/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhooksWsParameters.java
index 0d800f476c6..10b2c7b0498 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhooksWsParameters.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhooksWsParameters.java
@@ -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
diff --git a/server/sonar-server/src/test/java/org/sonar/server/webhook/ws/CreateActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/webhook/ws/CreateActionTest.java
index 3ffbf303afa..fc3f7bcdfb4 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/webhook/ws/CreateActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/webhook/ws/CreateActionTest.java
@@ -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
index 00000000000..0bf1e5ebc29
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/webhook/ws/UpdateActionTest.java
@@ -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();
+
+ }
+
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/webhook/ws/WebhooksWsModuleTest.java b/server/sonar-server/src/test/java/org/sonar/server/webhook/ws/WebhooksWsModuleTest.java
index 96aae56460a..7afb02511bd 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/webhook/ws/WebhooksWsModuleTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/webhook/ws/WebhooksWsModuleTest.java
@@ -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);
}
}