From bad30545b5ed235f7778c00baedbbe0e41006049 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Gr=C3=A9goire=20Aubert?= Date: Wed, 14 Feb 2018 15:54:21 +0100 Subject: [PATCH] SONAR-10345 Add IT's for webhooks management --- .../java/org/sonarqube/qa/util/Tester.java | 10 + .../org/sonarqube/qa/util/TesterSession.java | 2 + .../org/sonarqube/qa/util/WebhookTester.java | 120 ++++++++ .../qa/util/pageobjects/WebhooksPage.java | 47 +++ .../ws/client/webhooks/CreateRequest.java | 89 ++++++ .../ws/client/webhooks/DeleteRequest.java | 48 +++ .../ws/client/webhooks/ListRequest.java | 61 ++++ .../ws/client/webhooks/UpdateRequest.java | 76 +++++ .../ws/client/webhooks/WebhooksService.java | 66 +++++ .../tests/project/ProjectBadgesTest.java | 2 +- .../tests/webhook/ExternalServer.java | 11 + .../tests/webhook/WebhooksPageTest.java | 139 +++++++++ .../tests/webhook/WebhooksSuite.java | 1 + .../sonarqube/tests/webhook/WebhooksTest.java | 277 ++++-------------- 14 files changed, 725 insertions(+), 224 deletions(-) create mode 100644 server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/WebhookTester.java create mode 100644 sonar-ws/src/main/java/org/sonarqube/ws/client/webhooks/CreateRequest.java create mode 100644 sonar-ws/src/main/java/org/sonarqube/ws/client/webhooks/DeleteRequest.java create mode 100644 sonar-ws/src/main/java/org/sonarqube/ws/client/webhooks/ListRequest.java create mode 100644 sonar-ws/src/main/java/org/sonarqube/ws/client/webhooks/UpdateRequest.java create mode 100644 tests/src/test/java/org/sonarqube/tests/webhook/WebhooksPageTest.java diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/Tester.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/Tester.java index 9401ef2a907..fe1d5d8236e 100644 --- a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/Tester.java +++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/Tester.java @@ -207,6 +207,11 @@ public class Tester extends ExternalResource implements TesterSession { return rootSession.qGates(); } + @Override + public WebhookTester webhooks() { + return rootSession.webhooks(); + } + private static class TesterSessionImpl implements TesterSession { private final WsClient client; @@ -262,5 +267,10 @@ public class Tester extends ExternalResource implements TesterSession { public QGateTester qGates() { return new QGateTester(this); } + + @Override + public WebhookTester webhooks() { + return new WebhookTester(this); + } } } diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/TesterSession.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/TesterSession.java index f9e1b05483d..d0e3afdcbd2 100644 --- a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/TesterSession.java +++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/TesterSession.java @@ -41,4 +41,6 @@ public interface TesterSession { QGateTester qGates(); + WebhookTester webhooks(); + } diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/WebhookTester.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/WebhookTester.java new file mode 100644 index 00000000000..5b0382051cf --- /dev/null +++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/WebhookTester.java @@ -0,0 +1,120 @@ +/* + * 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.sonarqube.qa.util; + +import java.util.List; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; +import javax.annotation.Nullable; +import org.sonarqube.ws.Organizations.Organization; +import org.sonarqube.ws.Projects.CreateWsResponse.Project; +import org.sonarqube.ws.Webhooks.CreateWsResponse.Webhook; +import org.sonarqube.ws.Webhooks.Delivery; +import org.sonarqube.ws.client.webhooks.CreateRequest; +import org.sonarqube.ws.client.webhooks.DeleteRequest; +import org.sonarqube.ws.client.webhooks.DeliveriesRequest; +import org.sonarqube.ws.client.webhooks.DeliveryRequest; +import org.sonarqube.ws.client.webhooks.ListRequest; +import org.sonarqube.ws.client.webhooks.WebhooksService; + +import static java.util.Arrays.stream; +import static java.util.Objects.requireNonNull; +import static org.assertj.core.api.Assertions.assertThat; + +public class WebhookTester { + private static final AtomicInteger ID_GENERATOR = new AtomicInteger(); + + private final TesterSession session; + + WebhookTester(TesterSession session) { + this.session = session; + } + + public WebhooksService service() { + return session.wsClient().webhooks(); + } + + public Webhook generate(Consumer... populators) { + return generate(null, null, populators); + } + + public Webhook generate(Organization organization, Consumer... populators) { + return generate(organization, null, populators); + } + + public Webhook generate(Project project, Consumer... populators) { + return generate(null, project, populators); + } + + @SafeVarargs + public final Webhook generate( + @Nullable Organization organization, + @Nullable Project project, + Consumer... populators + ) { + int id = ID_GENERATOR.getAndIncrement(); + CreateRequest request = new CreateRequest() + .setName("Webhook " + id) + .setUrl("https://webhook-" + id) + .setProject(project != null ? project.getKey(): null) + .setOrganization(organization != null ? organization.getKey() : null); + stream(populators).forEach(p -> p.accept(request)); + return service().create(request).getWebhook(); + } + + public void deleteAllGlobal() { + service().list(new ListRequest()).getWebhooksList().forEach(p -> + service().delete(new DeleteRequest().setWebhook(p.getKey())) + ); + } + + public List getPersistedDeliveries(Project project) { + DeliveriesRequest deliveriesReq = new DeliveriesRequest().setComponentKey(project.getKey()); + return service().deliveries(deliveriesReq).getDeliveriesList(); + } + + public Delivery getPersistedDeliveryByName(Project project, String webhookName) { + List deliveries = getPersistedDeliveries(project); + Optional delivery = deliveries.stream().filter(d -> d.getName().equals(webhookName)).findFirst(); + assertThat(delivery).isPresent(); + return delivery.get(); + } + + public Delivery getDetailOfPersistedDelivery(Delivery delivery) { + Delivery detail = service().delivery(new DeliveryRequest().setDeliveryId(delivery.getId())).getDelivery(); + return requireNonNull(detail); + } + + public void assertThatPersistedDeliveryIsValid(Delivery delivery, @Nullable Project project, @Nullable String url) { + assertThat(delivery.getId()).isNotEmpty(); + assertThat(delivery.getName()).isNotEmpty(); + assertThat(delivery.hasSuccess()).isTrue(); + assertThat(delivery.getHttpStatus()).isGreaterThanOrEqualTo(200); + assertThat(delivery.getDurationMs()).isGreaterThanOrEqualTo(0); + assertThat(delivery.getAt()).isNotEmpty(); + if (project != null) { + assertThat(delivery.getComponentKey()).isEqualTo(project.getKey()); + } + if (url != null) { + assertThat(delivery.getUrl()).startsWith(url); + } + } +} diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/WebhooksPage.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/WebhooksPage.java index af6c8c6f816..75260c11e11 100644 --- a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/WebhooksPage.java +++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/WebhooksPage.java @@ -20,9 +20,13 @@ package org.sonarqube.qa.util.pageobjects; import com.codeborne.selenide.ElementsCollection; +import com.codeborne.selenide.SelenideElement; +import static com.codeborne.selenide.Condition.cssClass; +import static com.codeborne.selenide.Condition.enabled; import static com.codeborne.selenide.Condition.exist; import static com.codeborne.selenide.Condition.text; +import static com.codeborne.selenide.Condition.visible; import static com.codeborne.selenide.Selenide.$; import static com.codeborne.selenide.Selenide.$$; @@ -37,12 +41,55 @@ public class WebhooksPage { return this; } + public WebhooksPage hasNoWebhooks() { + $(".boxed-group").shouldHave(text("No webhook defined")); + return this; + } + public WebhooksPage countWebhooks(Integer number) { getWebhooks().shouldHaveSize(number); return this; } + public WebhooksPage createWebhook(String name, String url) { + $(".js-webhook-create").shouldBe(visible).shouldBe(enabled).shouldNotHave(cssClass("disabled")).click(); + modalShouldBeOpen("Create Webhook"); + $("#webhook-name").shouldBe(visible).sendKeys(name); + $("#webhook-url").shouldBe(visible).sendKeys(url); + $("button[type='submit']").shouldBe(visible).click(); + modalShouldBeClosed(); + return this; + } + + public WebhooksPage createIsDisabled() { + $(".js-webhook-create").shouldBe(visible).shouldHave(cssClass("disabled")).click(); + modalShouldBeClosed(); + return this; + } + + public WebhooksPage deleteWebhook(String webhookName) { + SelenideElement webhook = getWebhook(webhookName); + webhook.$(".dropdown-toggle").shouldBe(visible).click(); + webhook.$(".js-webhook-delete").shouldBe(visible).click(); + modalShouldBeOpen("Delete Webhook"); + $("button.button-red").shouldBe(visible).click(); + modalShouldBeClosed(); + return this; + } + + private static SelenideElement getWebhook(String webhookName) { + return getWebhooks().find(text(webhookName)).should(exist); + } + private static ElementsCollection getWebhooks() { return $$(".boxed-group tbody tr"); } + + private static void modalShouldBeOpen(String title) { + $(".modal-head").shouldBe(visible).shouldHave(text(title)); + } + + private static void modalShouldBeClosed() { + $(".modal-head").shouldNot(exist); + } } diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/webhooks/CreateRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/webhooks/CreateRequest.java new file mode 100644 index 00000000000..2162c20f54e --- /dev/null +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/webhooks/CreateRequest.java @@ -0,0 +1,89 @@ +/* + * 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.sonarqube.ws.client.webhooks; + +import java.util.List; +import javax.annotation.Generated; + +/** + * This is part of the internal API. + * This is a POST request. + * @see Further information about this action online (including a response example) + * @since 7.1 + */ +@Generated("sonar-ws-generator") +public class CreateRequest { + + private String name; + private String organization; + private String project; + private String url; + + /** + * This is a mandatory parameter. + * Example value: "My Webhook" + */ + public CreateRequest setName(String name) { + this.name = name; + return this; + } + + public String getName() { + return name; + } + + /** + * This is part of the internal API. + * Example value: "my-org" + */ + public CreateRequest setOrganization(String organization) { + this.organization = organization; + return this; + } + + public String getOrganization() { + return organization; + } + + /** + * Example value: "my_project" + */ + public CreateRequest setProject(String project) { + this.project = project; + return this; + } + + public String getProject() { + return project; + } + + /** + * This is a mandatory parameter. + * Example value: "https://www.my-webhook-listener.com/sonar" + */ + public CreateRequest setUrl(String url) { + this.url = url; + return this; + } + + public String getUrl() { + return url; + } +} diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/webhooks/DeleteRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/webhooks/DeleteRequest.java new file mode 100644 index 00000000000..aab9cea892a --- /dev/null +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/webhooks/DeleteRequest.java @@ -0,0 +1,48 @@ +/* + * 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.sonarqube.ws.client.webhooks; + +import java.util.List; +import javax.annotation.Generated; + +/** + * This is part of the internal API. + * This is a POST request. + * @see Further information about this action online (including a response example) + * @since 7.1 + */ +@Generated("sonar-ws-generator") +public class DeleteRequest { + + private String webhook; + + /** + * This is a mandatory parameter. + * Example value: "my_project" + */ + public DeleteRequest setWebhook(String webhook) { + this.webhook = webhook; + return this; + } + + public String getWebhook() { + return webhook; + } +} diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/webhooks/ListRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/webhooks/ListRequest.java new file mode 100644 index 00000000000..d56a8804e2f --- /dev/null +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/webhooks/ListRequest.java @@ -0,0 +1,61 @@ +/* + * 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.sonarqube.ws.client.webhooks; + +import java.util.List; +import javax.annotation.Generated; + +/** + * This is part of the internal API. + * This is a POST request. + * @see Further information about this action online (including a response example) + * @since 7.1 + */ +@Generated("sonar-ws-generator") +public class ListRequest { + + private String organization; + private String project; + + /** + * This is part of the internal API. + * Example value: "my-org" + */ + public ListRequest setOrganization(String organization) { + this.organization = organization; + return this; + } + + public String getOrganization() { + return organization; + } + + /** + * Example value: "my_project" + */ + public ListRequest setProject(String project) { + this.project = project; + return this; + } + + public String getProject() { + return project; + } +} diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/webhooks/UpdateRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/webhooks/UpdateRequest.java new file mode 100644 index 00000000000..64b8ad6811f --- /dev/null +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/webhooks/UpdateRequest.java @@ -0,0 +1,76 @@ +/* + * 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.sonarqube.ws.client.webhooks; + +import java.util.List; +import javax.annotation.Generated; + +/** + * This is part of the internal API. + * This is a POST request. + * @see Further information about this action online (including a response example) + * @since 7.1 + */ +@Generated("sonar-ws-generator") +public class UpdateRequest { + + private String name; + private String url; + private String webhook; + + /** + * This is a mandatory parameter. + * Example value: "My Webhook" + */ + public UpdateRequest setName(String name) { + this.name = name; + return this; + } + + public String getName() { + return name; + } + + /** + * This is a mandatory parameter. + * Example value: "https://www.my-webhook-listener.com/sonar" + */ + public UpdateRequest setUrl(String url) { + this.url = url; + return this; + } + + public String getUrl() { + return url; + } + + /** + * This is a mandatory parameter. + * Example value: "my_project" + */ + public UpdateRequest setWebhook(String webhook) { + this.webhook = webhook; + return this; + } + + public String getWebhook() { + return webhook; + } +} diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/webhooks/WebhooksService.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/webhooks/WebhooksService.java index 9f8b4587acc..7eb3b34a329 100644 --- a/sonar-ws/src/main/java/org/sonarqube/ws/client/webhooks/WebhooksService.java +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/webhooks/WebhooksService.java @@ -26,8 +26,10 @@ 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 org.sonarqube.ws.Webhooks.CreateWsResponse; import org.sonarqube.ws.Webhooks.DeliveriesWsResponse; import org.sonarqube.ws.Webhooks.DeliveryWsResponse; +import org.sonarqube.ws.Webhooks.ListWsResponse; /** * @see Further information about this web service online @@ -39,6 +41,38 @@ public class WebhooksService extends BaseService { super(wsConnector, "api/webhooks"); } + /** + * + * This is part of the internal API. + * This is a POST request. + * @see Further information about this action online (including a response example) + * @since 7.1 + */ + public CreateWsResponse create(CreateRequest request) { + return call( + new PostRequest(path("create")) + .setParam("name", request.getName()) + .setParam("organization", request.getOrganization()) + .setParam("project", request.getProject()) + .setParam("url", request.getUrl()), + CreateWsResponse.parser()); + } + + /** + * + * This is part of the internal API. + * This is a POST request. + * @see Further information about this action online (including a response example) + * @since 7.1 + */ + public void delete(DeleteRequest request) { + call( + new PostRequest(path("delete")) + .setParam("webhook", request.getWebhook()) + .setMediaType(MediaTypes.JSON) + ).content(); + } + /** * * This is part of the internal API. @@ -67,4 +101,36 @@ public class WebhooksService extends BaseService { .setParam("deliveryId", request.getDeliveryId()), DeliveryWsResponse.parser()); } + + /** + * + * This is part of the internal API. + * This is a GET request. + * @see Further information about this action online (including a response example) + * @since 7.1 + */ + public ListWsResponse list(ListRequest request) { + return call( + new GetRequest(path("list")) + .setParam("organization", request.getOrganization()) + .setParam("project", request.getProject()), + ListWsResponse.parser()); + } + + /** + * + * This is part of the internal API. + * This is a POST request. + * @see Further information about this action online (including a response example) + * @since 7.1 + */ + public void update(UpdateRequest request) { + call( + new PostRequest(path("update")) + .setParam("name", request.getName()) + .setParam("url", request.getUrl()) + .setParam("webhook", request.getWebhook()) + .setMediaType(MediaTypes.JSON) + ).content(); + } } diff --git a/tests/src/test/java/org/sonarqube/tests/project/ProjectBadgesTest.java b/tests/src/test/java/org/sonarqube/tests/project/ProjectBadgesTest.java index 161861e58b1..3e9f23f0a60 100644 --- a/tests/src/test/java/org/sonarqube/tests/project/ProjectBadgesTest.java +++ b/tests/src/test/java/org/sonarqube/tests/project/ProjectBadgesTest.java @@ -115,7 +115,7 @@ public class ProjectBadgesTest { } private void shouldHaveUrl(SelenideElement badgesModal, String url) { - badgesModal.$(".badge-snippet pre") + badgesModal.$(".code-snippet pre") .shouldBe(Condition.visible) .shouldHave(Condition.text(url)); } diff --git a/tests/src/test/java/org/sonarqube/tests/webhook/ExternalServer.java b/tests/src/test/java/org/sonarqube/tests/webhook/ExternalServer.java index 220dc1f59e5..66cdb5c2413 100644 --- a/tests/src/test/java/org/sonarqube/tests/webhook/ExternalServer.java +++ b/tests/src/test/java/org/sonarqube/tests/webhook/ExternalServer.java @@ -93,7 +93,18 @@ class ExternalServer extends ExternalResource { return jetty.getURI().resolve(path).toString(); } + void waitUntilAllWebHooksCalled(int expectedNumberOfRequests) throws InterruptedException { + // Wait up to 30 seconds max + for (int i = 0; i < 60; i++) { + if (getPayloadRequests().size() == expectedNumberOfRequests) { + break; + } + Thread.sleep(500); + } + } + void clear() { payloads.clear(); } + } diff --git a/tests/src/test/java/org/sonarqube/tests/webhook/WebhooksPageTest.java b/tests/src/test/java/org/sonarqube/tests/webhook/WebhooksPageTest.java new file mode 100644 index 00000000000..ac3e8ed3318 --- /dev/null +++ b/tests/src/test/java/org/sonarqube/tests/webhook/WebhooksPageTest.java @@ -0,0 +1,139 @@ +/* + * 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.sonarqube.tests.webhook; + +import com.sonar.orchestrator.Orchestrator; +import org.junit.After; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.sonarqube.qa.util.Tester; +import org.sonarqube.qa.util.pageobjects.WebhooksPage; +import org.sonarqube.ws.Projects.CreateWsResponse.Project; +import org.sonarqube.ws.Users.CreateWsResponse.User; +import org.sonarqube.ws.Webhooks.CreateWsResponse.Webhook; + +import static util.ItUtils.runProjectAnalysis; + +public class WebhooksPageTest +{ + @ClassRule + public static Orchestrator orchestrator = WebhooksSuite.ORCHESTRATOR; + + @Rule + public Tester tester = new Tester(orchestrator); + + @Before + @After + public void reset() { + tester.webhooks().deleteAllGlobal(); + } + + @Test + public void list_global_webhooks() { + tester.webhooks().generate(); + Webhook webhook = tester.webhooks().generate(); + tester.wsClient().users().skipOnboardingTutorial(); + WebhooksPage webhooksPage = tester.openBrowser().logIn().submitCredentials("admin").openWebhooks(); + webhooksPage + .countWebhooks(2) + .hasWebhook(webhook.getUrl()) + .hasWebhook(webhook.getName()); + } + + @Test + public void list_project_webhooks() { + User user = tester.users().generateAdministratorOnDefaultOrganization(); + tester.wsClient().users().skipOnboardingTutorial(); + + Project project = tester.projects().provision(); + analyseProject(project); + + Webhook webhook1 = tester.webhooks().generate(project); + Webhook webhook2 = tester.webhooks().generate(project); + + WebhooksPage webhooksPage = tester.openBrowser().logIn().submitCredentials(user.getLogin()).openProjectWebhooks(project.getKey()); + webhooksPage + .countWebhooks(2) + .hasWebhook(webhook1.getUrl()) + .hasWebhook(webhook2.getUrl()); + } + + @Test + public void create_new_webhook() { + User user = tester.users().generateAdministratorOnDefaultOrganization(); + tester.wsClient().users().skipOnboardingTutorial(); + + Project project = tester.projects().provision(); + analyseProject(project); + + WebhooksPage webhooksPage = tester.openBrowser().logIn().submitCredentials(user.getLogin()).openProjectWebhooks(project.getKey()); + webhooksPage + .hasNoWebhooks() + .createWebhook("my-webhook", "http://greg:pass@test.com") + .countWebhooks(1) + .hasWebhook("my-webhook") + .hasWebhook("http://greg:pass@test.com"); + } + + @Test + public void prevent_webhook_creation() { + tester.wsClient().users().skipOnboardingTutorial(); + + Webhook webhook = tester.webhooks().generate(); + for (int i = 0; i < 9; i++) { + tester.webhooks().generate(); + } + + WebhooksPage webhooksPage = tester.openBrowser().logIn().submitCredentials("admin").openWebhooks(); + webhooksPage + .countWebhooks(10) + .createIsDisabled() + .deleteWebhook(webhook.getName()) + .countWebhooks(9) + .createWebhook("my-new-webhook", "http://my-new-webhook.com"); + } + + @Test + public void delete_webhook() { + User user = tester.users().generateAdministratorOnDefaultOrganization(); + tester.wsClient().users().skipOnboardingTutorial(); + + Project project = tester.projects().provision(); + analyseProject(project); + + tester.webhooks().generate(project); + tester.webhooks().generate(project); + Webhook webhook = tester.webhooks().generate(project); + + WebhooksPage webhooksPage = tester.openBrowser().logIn().submitCredentials(user.getLogin()).openProjectWebhooks(project.getKey()); + webhooksPage + .countWebhooks(3) + .deleteWebhook(webhook.getName()) + .countWebhooks(2); + } + + private void analyseProject(Project project) { + runProjectAnalysis(orchestrator, "shared/xoo-sample", + "sonar.projectKey", project.getKey(), + "sonar.projectName", project.getName()); + } +} diff --git a/tests/src/test/java/org/sonarqube/tests/webhook/WebhooksSuite.java b/tests/src/test/java/org/sonarqube/tests/webhook/WebhooksSuite.java index 261c88ff139..6412ec5c48e 100644 --- a/tests/src/test/java/org/sonarqube/tests/webhook/WebhooksSuite.java +++ b/tests/src/test/java/org/sonarqube/tests/webhook/WebhooksSuite.java @@ -28,6 +28,7 @@ import static util.ItUtils.xooPlugin; @RunWith(Suite.class) @Suite.SuiteClasses({ + WebhooksPageTest.class, WebhooksTest.class }) public class WebhooksSuite { diff --git a/tests/src/test/java/org/sonarqube/tests/webhook/WebhooksTest.java b/tests/src/test/java/org/sonarqube/tests/webhook/WebhooksTest.java index 2531d8674b1..ad73ced0d2b 100644 --- a/tests/src/test/java/org/sonarqube/tests/webhook/WebhooksTest.java +++ b/tests/src/test/java/org/sonarqube/tests/webhook/WebhooksTest.java @@ -20,64 +20,41 @@ package org.sonarqube.tests.webhook; import com.sonar.orchestrator.Orchestrator; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; -import javax.annotation.Nullable; -import org.apache.commons.lang3.StringUtils; import org.junit.After; import org.junit.Before; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.sonarqube.qa.util.Tester; -import org.sonarqube.qa.util.pageobjects.WebhooksPage; import org.sonarqube.ws.Issues.Issue; import org.sonarqube.ws.Organizations.Organization; import org.sonarqube.ws.Projects.CreateWsResponse.Project; import org.sonarqube.ws.Qualitygates; import org.sonarqube.ws.Qualityprofiles.CreateWsResponse.QualityProfile; -import org.sonarqube.ws.Users; -import org.sonarqube.ws.Users.CreateWsResponse.User; import org.sonarqube.ws.Webhooks; -import org.sonarqube.ws.client.HttpException; -import org.sonarqube.ws.client.WsClient; +import org.sonarqube.ws.Webhooks.CreateWsResponse.Webhook; import org.sonarqube.ws.client.issues.BulkChangeRequest; import org.sonarqube.ws.client.issues.SearchRequest; -import org.sonarqube.ws.client.projects.DeleteRequest; import org.sonarqube.ws.client.qualitygates.CreateConditionRequest; -import org.sonarqube.ws.client.settings.ResetRequest; -import org.sonarqube.ws.client.settings.SetRequest; -import org.sonarqube.ws.client.webhooks.DeliveriesRequest; -import org.sonarqube.ws.client.webhooks.DeliveryRequest; -import util.ItUtils; import static java.util.Collections.singletonList; -import static java.util.Objects.requireNonNull; -import static java.util.stream.IntStream.range; import static org.assertj.core.api.Assertions.assertThat; import static util.ItUtils.jsonToMap; import static util.ItUtils.runProjectAnalysis; public class WebhooksTest { - private static final String PROJECT_KEY = "my-project"; - private static final String PROJECT_NAME = "My Project"; - private static final String GLOBAL_WEBHOOK_PROPERTY = "sonar.webhooks.global"; - private static final String PROJECT_WEBHOOK_PROPERTY = "sonar.webhooks.project"; - @ClassRule public static Orchestrator orchestrator = WebhooksSuite.ORCHESTRATOR; + @ClassRule public static ExternalServer externalServer = new ExternalServer(); @Rule public Tester tester = new Tester(orchestrator); - private WsClient adminWs = ItUtils.newAdminWsClient(orchestrator); - @Before public void setUp() { externalServer.clear(); @@ -86,28 +63,20 @@ public class WebhooksTest { @Before @After public void reset() { - disableGlobalWebhooks(); - try { - // delete project and related properties/webhook deliveries - adminWs.projects().delete(new DeleteRequest().setProject(PROJECT_KEY)); - } catch (HttpException e) { - // ignore because project may not exist - } + tester.webhooks().deleteAllGlobal(); } @Test public void call_multiple_global_and_project_webhooks_when_analysis_is_done() throws InterruptedException { - orchestrator.getServer().provisionProject(PROJECT_KEY, PROJECT_NAME); - enableGlobalWebhooks( - new Webhook("Jenkins", externalServer.urlFor("/jenkins")), - new Webhook("HipChat", externalServer.urlFor("/hipchat"))); - enableProjectWebhooks(PROJECT_KEY, - new Webhook("Burgr", externalServer.urlFor("/burgr"))); + Project project = tester.projects().provision(); + Webhook jenkins = tester.webhooks().generate(p -> p.setName("Jenkins").setUrl(externalServer.urlFor("/jenkins"))); + Webhook hipchat = tester.webhooks().generate(p -> p.setName("HipChat").setUrl(externalServer.urlFor("/hipchat"))); + Webhook burgr = tester.webhooks().generate(project, p -> p.setName("Burgr").setUrl(externalServer.urlFor("/burgr"))); - analyseProject(); + analyseProject(project); // the same payload has been sent to three servers - waitUntilAllWebHooksCalled(3); + externalServer.waitUntilAllWebHooksCalled(3); assertThat(externalServer.getPayloadRequests()).hasSize(3); PayloadRequest request = externalServer.getPayloadRequests().get(0); for (int i = 1; i < 3; i++) { @@ -116,137 +85,104 @@ public class WebhooksTest { } // verify HTTP headers - assertThat(request.getHttpHeaders().get("X-SonarQube-Project")).isEqualTo(PROJECT_KEY); + assertThat(request.getHttpHeaders().get("X-SonarQube-Project")).isEqualTo(project.getKey()); // verify content of payload Map payload = jsonToMap(request.getJson()); assertThat(payload.get("status")).isEqualTo("SUCCESS"); assertThat(payload.get("analysedAt")).isNotNull(); - Map project = (Map) payload.get("project"); - assertThat(project.get("key")).isEqualTo(PROJECT_KEY); - assertThat(project.get("name")).isEqualTo(PROJECT_NAME); - assertThat(project.get("url")).isEqualTo(orchestrator.getServer().getUrl() + "/dashboard?id=" + PROJECT_KEY); + Map projectPayload = (Map) payload.get("project"); + assertThat(projectPayload.get("key")).isEqualTo(project.getKey()); + assertThat(projectPayload.get("name")).isEqualTo(project.getName()); + assertThat(projectPayload.get("url")).isEqualTo(orchestrator.getServer().getUrl() + "/dashboard?id=" + project.getKey()); Map gate = (Map) payload.get("qualityGate"); assertThat(gate.get("name")).isEqualTo("Sonar way"); assertThat(gate.get("status")).isEqualTo("OK"); assertThat(gate.get("conditions")).isNotNull(); // verify list of persisted deliveries (api/webhooks/deliveries) - List deliveries = getPersistedDeliveries(); + List deliveries = tester.webhooks().getPersistedDeliveries(project); assertThat(deliveries).hasSize(3); for (Webhooks.Delivery delivery : deliveries) { - assertThatPersistedDeliveryIsValid(delivery); + tester.webhooks().assertThatPersistedDeliveryIsValid(delivery, project, externalServer.urlFor("/")); assertThat(delivery.getSuccess()).isTrue(); assertThat(delivery.getHttpStatus()).isEqualTo(200); - assertThat(delivery.getName()).isIn("Jenkins", "HipChat", "Burgr"); + assertThat(delivery.getName()).isIn(jenkins.getName(), hipchat.getName(), burgr.getName()); assertThat(delivery.hasErrorStacktrace()).isFalse(); // payload is available only in api/webhooks/delivery to avoid loading multiple DB CLOBs assertThat(delivery.hasPayload()).isFalse(); } // verify detail of persisted delivery (api/webhooks/delivery) - Webhooks.Delivery detail = getDetailOfPersistedDelivery(deliveries.get(0)); - assertThatPersistedDeliveryIsValid(detail); + Webhooks.Delivery detail = tester.webhooks().getDetailOfPersistedDelivery(deliveries.get(0)); + tester.webhooks().assertThatPersistedDeliveryIsValid(detail, project, externalServer.urlFor("/")); assertThat(detail.getPayload()).isEqualTo(request.getJson()); } @Test public void persist_delivery_as_failed_if_external_server_returns_an_error_code() { - enableGlobalWebhooks( - new Webhook("Fail", externalServer.urlFor("/fail")), - new Webhook("HipChat", externalServer.urlFor("/hipchat"))); + Project project = tester.projects().provision(); + Webhook fail = tester.webhooks().generate(p -> p.setName("Fail").setUrl(externalServer.urlFor("/fail"))); + Webhook hipchat = tester.webhooks().generate(p -> p.setName("HipChat").setUrl(externalServer.urlFor("/hipchat"))); - analyseProject(); + analyseProject(project); // all webhooks are called, even if one returns an error code assertThat(externalServer.getPayloadRequests()).hasSize(2); // verify persisted deliveries - Webhooks.Delivery failedDelivery = getPersistedDeliveryByName("Fail"); - assertThatPersistedDeliveryIsValid(failedDelivery); + Webhooks.Delivery failedDelivery = tester.webhooks().getPersistedDeliveryByName(project, fail.getName()); + tester.webhooks().assertThatPersistedDeliveryIsValid(failedDelivery, project, fail.getUrl()); assertThat(failedDelivery.getSuccess()).isFalse(); assertThat(failedDelivery.getHttpStatus()).isEqualTo(500); - Webhooks.Delivery successfulDelivery = getPersistedDeliveryByName("HipChat"); - assertThatPersistedDeliveryIsValid(successfulDelivery); + Webhooks.Delivery successfulDelivery = tester.webhooks().getPersistedDeliveryByName(project, hipchat.getName()); + tester.webhooks().assertThatPersistedDeliveryIsValid(successfulDelivery, project, hipchat.getUrl()); assertThat(successfulDelivery.getSuccess()).isTrue(); assertThat(successfulDelivery.getHttpStatus()).isEqualTo(200); } - /** - * Restrict calls to ten webhooks per type (global or project) - */ - @Test - public void do_not_become_a_denial_of_service_attacker() throws InterruptedException { - orchestrator.getServer().provisionProject(PROJECT_KEY, PROJECT_NAME); - - List globalWebhooks = range(0, 15).mapToObj(i -> new Webhook("G" + i, externalServer.urlFor("/global"))).collect(Collectors.toList()); - enableGlobalWebhooks(globalWebhooks.toArray(new Webhook[globalWebhooks.size()])); - List projectWebhooks = range(0, 15).mapToObj(i -> new Webhook("P" + i, externalServer.urlFor("/project"))).collect(Collectors.toList()); - enableProjectWebhooks(PROJECT_KEY, projectWebhooks.toArray(new Webhook[projectWebhooks.size()])); - - analyseProject(); - - // only the first ten global webhooks and ten project webhooks are called - waitUntilAllWebHooksCalled(10 + 10); - assertThat(externalServer.getPayloadRequests()).hasSize(10 + 10); - assertThat(externalServer.getPayloadRequestsOnPath("/global")).hasSize(10); - assertThat(externalServer.getPayloadRequestsOnPath("/project")).hasSize(10); - - // verify persisted deliveries - assertThat(getPersistedDeliveries()).hasSize(10 + 10); - } - @Test - public void persist_delivery_as_failed_if_webhook_url_is_malformed() { - enableGlobalWebhooks(new Webhook("Jenkins", "this_is_not_an_url")); + public void persist_delivery_as_failed_if_webhook_is_not_reachable() { + Project project = tester.projects().provision(); + Webhook badUrl = tester.webhooks().generate(p -> p.setUrl("http://does_not_exist")); - analyseProject(); + analyseProject(project); assertThat(externalServer.getPayloadRequests()).isEmpty(); // verify persisted deliveries - Webhooks.Delivery delivery = getPersistedDeliveryByName("Jenkins"); - Webhooks.Delivery detail = getDetailOfPersistedDelivery(delivery); + Webhooks.Delivery delivery = tester.webhooks().getPersistedDeliveryByName(project, badUrl.getName()); + Webhooks.Delivery detail = tester.webhooks().getDetailOfPersistedDelivery(delivery); assertThat(detail.getSuccess()).isFalse(); assertThat(detail.hasHttpStatus()).isFalse(); assertThat(detail.hasDurationMs()).isFalse(); assertThat(detail.getPayload()).isNotEmpty(); assertThat(detail.getErrorStacktrace()) - .contains("java.lang.IllegalArgumentException") - .contains("Webhook URL is not valid: this_is_not_an_url"); - } - - @Test - public void ignore_webhook_if_url_is_missing() { - // property sets, as used to define webhooks, do - // not allow to validate values yet - enableGlobalWebhooks(new Webhook("Jenkins", null)); - - analyseProject(); - - assertThat(externalServer.getPayloadRequests()).isEmpty(); - assertThat(getPersistedDeliveries()).isEmpty(); + .contains("java.net.UnknownHostException") + .contains("Name or service not known"); } @Test public void send_webhook_on_issue_change() throws InterruptedException { Organization defaultOrganization = tester.organizations().getDefaultOrganization(); - Project wsProject = tester.projects().provision(r -> r.setProject(PROJECT_KEY).setName(PROJECT_NAME)); - enableProjectWebhooks(PROJECT_KEY, new Webhook("Burgr", externalServer.urlFor("/burgr"))); + Project project = tester.projects().provision(); + Webhook burgr = tester.webhooks().generate(project, p -> p.setName("Burgr").setUrl(externalServer.urlFor("/burgr"))); + // quality profile with one issue per line QualityProfile qualityProfile = tester.qProfiles().createXooProfile(defaultOrganization); tester.qProfiles().activateRule(qualityProfile, "xoo:OneIssuePerLine"); - tester.qProfiles().assignQProfileToProject(qualityProfile, wsProject); + tester.qProfiles().assignQProfileToProject(qualityProfile, project); // quality gate definition Qualitygates.CreateResponse qGate = tester.qGates().generate(); tester.qGates().service().createCondition(new CreateConditionRequest().setGateId(String.valueOf(qGate.getId())) .setMetric("reliability_rating").setOp("GT").setError("1")); - tester.qGates().associateProject(qGate, wsProject); + tester.qGates().associateProject(qGate, project); // analyze project and clear first webhook - analyseProject(); - waitUntilAllWebHooksCalled(1); + + analyseProject(project); + externalServer.waitUntilAllWebHooksCalled(1); externalServer.clear(); // change an issue to blocker bug, QG status goes from OK to ERROR, so webhook is called @@ -255,18 +191,18 @@ public class WebhooksTest { tester.wsClient().issues().bulkChange(new BulkChangeRequest().setIssues(singletonList(firstIssue.getKey())) .setSetSeverity(singletonList("BLOCKER")) .setSetType(singletonList("BUG"))); - waitUntilAllWebHooksCalled(1); + externalServer.waitUntilAllWebHooksCalled(1); PayloadRequest request = externalServer.getPayloadRequests().get(0); - assertThat(request.getHttpHeaders().get("X-SonarQube-Project")).isEqualTo(PROJECT_KEY); + assertThat(request.getHttpHeaders().get("X-SonarQube-Project")).isEqualTo(project.getKey()); // verify content of payload Map payload = jsonToMap(request.getJson()); assertThat(payload.get("status")).isEqualTo("SUCCESS"); assertThat(payload.get("analysedAt")).isNotNull(); - Map project = (Map) payload.get("project"); - assertThat(project.get("key")).isEqualTo(PROJECT_KEY); - assertThat(project.get("name")).isEqualTo(PROJECT_NAME); - assertThat(project.get("url")).isEqualTo(orchestrator.getServer().getUrl() + "/dashboard?id=" + PROJECT_KEY); + Map projectPayload = (Map) payload.get("project"); + assertThat(projectPayload.get("key")).isEqualTo(project.getKey()); + assertThat(projectPayload.get("name")).isEqualTo(project.getName()); + assertThat(projectPayload.get("url")).isEqualTo(orchestrator.getServer().getUrl() + "/dashboard?id=" + project.getKey()); Map gate = (Map) payload.get("qualityGate"); assertThat(gate.get("name")).isEqualTo(qGate.getName()); assertThat(gate.get("status")).isEqualTo("ERROR"); @@ -276,127 +212,22 @@ public class WebhooksTest { // change severity of issue, won't change the QG status, so no webhook called tester.wsClient().issues().bulkChange(new BulkChangeRequest().setIssues(singletonList(firstIssue.getKey())) .setSetSeverity(singletonList("MINOR"))); - waitUntilAllWebHooksCalled(1); + externalServer.waitUntilAllWebHooksCalled(1); assertThat(externalServer.getPayloadRequests()).isEmpty(); // resolve issue as won't fix, QG status goes to OK, so webhook called tester.wsClient().issues().bulkChange(new BulkChangeRequest().setIssues(singletonList(firstIssue.getKey())) .setDoTransition("wontfix")); - waitUntilAllWebHooksCalled(1); + externalServer.waitUntilAllWebHooksCalled(1); request = externalServer.getPayloadRequests().get(0); payload = jsonToMap(request.getJson()); gate = (Map) payload.get("qualityGate"); assertThat(gate.get("status")).isEqualTo("OK"); } - @Test - public void list_global_webhooks() { - enableGlobalWebhooks(new Webhook("foo", "http://foo.bar"), new Webhook("bar", "https://bar.baz/test")); - tester.wsClient().users().skipOnboardingTutorial(); - WebhooksPage webhooksPage = tester.openBrowser().logIn().submitCredentials("admin").openWebhooks(); - webhooksPage - .countWebhooks(2) - .hasWebhook("http://foo.bar"); - } - - @Test - public void list_project_webhooks() { - analyseProject(); - enableProjectWebhooks(PROJECT_KEY, new Webhook("foo", "http://foo.bar"), new Webhook("bar", "https://bar.baz/test")); - User user = tester.users().generateAdministratorOnDefaultOrganization(); - tester.wsClient().users().skipOnboardingTutorial(); - WebhooksPage webhooksPage = tester.openBrowser().logIn().submitCredentials(user.getLogin()).openProjectWebhooks(PROJECT_KEY); - webhooksPage - .countWebhooks(2) - .hasWebhook("http://foo.bar"); - } - - private void analyseProject() { + private void analyseProject(Project project) { runProjectAnalysis(orchestrator, "shared/xoo-sample", - "sonar.projectKey", PROJECT_KEY, - "sonar.projectName", PROJECT_NAME); - } - - private List getPersistedDeliveries() { - DeliveriesRequest deliveriesReq = new DeliveriesRequest().setComponentKey(PROJECT_KEY); - return adminWs.webhooks().deliveries(deliveriesReq).getDeliveriesList(); - } - - private Webhooks.Delivery getPersistedDeliveryByName(String webhookName) { - List deliveries = getPersistedDeliveries(); - return deliveries.stream().filter(d -> d.getName().equals(webhookName)).findFirst().get(); - } - - private Webhooks.Delivery getDetailOfPersistedDelivery(Webhooks.Delivery delivery) { - Webhooks.Delivery detail = adminWs.webhooks().delivery(new DeliveryRequest().setDeliveryId(delivery.getId())).getDelivery(); - return requireNonNull(detail); - } - - private void assertThatPersistedDeliveryIsValid(Webhooks.Delivery delivery) { - assertThat(delivery.getId()).isNotEmpty(); - assertThat(delivery.getName()).isNotEmpty(); - assertThat(delivery.hasSuccess()).isTrue(); - assertThat(delivery.getHttpStatus()).isGreaterThanOrEqualTo(200); - assertThat(delivery.getDurationMs()).isGreaterThanOrEqualTo(0); - assertThat(delivery.getAt()).isNotEmpty(); - assertThat(delivery.getComponentKey()).isEqualTo(PROJECT_KEY); - assertThat(delivery.getUrl()).startsWith(externalServer.urlFor("/")); - } - - private void enableGlobalWebhooks(Webhook... webhooks) { - enableWebhooks(null, GLOBAL_WEBHOOK_PROPERTY, webhooks); - } - - private void enableProjectWebhooks(String projectKey, Webhook... webhooks) { - enableWebhooks(projectKey, PROJECT_WEBHOOK_PROPERTY, webhooks); - } - - private void enableWebhooks(@Nullable String projectKey, String property, Webhook... webhooks) { - List webhookIds = new ArrayList<>(); - for (int i = 0; i < webhooks.length; i++) { - Webhook webhook = webhooks[i]; - String id = String.valueOf(i + 1); - webhookIds.add(id); - setProperty(projectKey, property + "." + id + ".name", webhook.name); - setProperty(projectKey, property + "." + id + ".url", webhook.url); - } - setProperty(projectKey, property, StringUtils.join(webhookIds, ",")); + "sonar.projectKey", project.getKey(), + "sonar.projectName", project.getName()); } - - private void disableGlobalWebhooks() { - setProperty(null, GLOBAL_WEBHOOK_PROPERTY, null); - } - - private void setProperty(@Nullable String componentKey, String key, @Nullable String value) { - if (value == null) { - ResetRequest req = new ResetRequest().setKeys(Collections.singletonList(key)).setComponent(componentKey); - adminWs.settings().reset(req); - } else { - SetRequest req = new SetRequest().setKey(key).setValue(value).setComponent(componentKey); - adminWs.settings().set(req); - } - } - - private static class Webhook { - private final String name; - private final String url; - - Webhook(@Nullable String name, @Nullable String url) { - this.name = name; - this.url = url; - } - } - - /** - * Wait up to 30 seconds - */ - private static void waitUntilAllWebHooksCalled(int expectedNumberOfRequests) throws InterruptedException { - for (int i = 0; i < 60; i++) { - if (externalServer.getPayloadRequests().size() == expectedNumberOfRequests) { - break; - } - Thread.sleep(500); - } - } - } -- 2.39.5