aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/webhook/WebhookDeliveryDao.java10
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/DbTester.java7
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java6
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookDbTesting.java14
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookDeliveryDaoTest.java86
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookDeliveryDbTester.java40
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookTesting.java9
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/MigrateWebhooksToWebhooksTable.java6
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/webhook/WebhookCallerImpl.java6
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/webhook/ws/ListAction.java94
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhookDeliveriesAction.java11
-rw-r--r--server/sonar-server/src/main/resources/org/sonar/server/webhook/ws/example-deliveries.json7
-rw-r--r--server/sonar-server/src/main/resources/org/sonar/server/webhook/ws/example-delivery.json32
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/webhook/WebhookCallerImplTest.java4
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/webhook/WebhookDeliveryStorageTest.java5
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/webhook/ws/ListActionTest.java333
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/webhook/ws/WebhookDeliveriesActionTest.java86
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/webhook/ws/WebhookDeliveryActionTest.java10
-rw-r--r--sonar-core/src/main/resources/org/sonar/l10n/core.properties2
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/server/ws/WebService.java9
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/webhooks/DeliveriesRequest.java40
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/webhooks/WebhooksService.java18
-rw-r--r--sonar-ws/src/main/protobuf/ws-webhooks.proto32
-rw-r--r--tests/src/test/java/org/sonarqube/tests/webhook/WebhooksTest.java2
24 files changed, 570 insertions, 299 deletions
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/webhook/WebhookDeliveryDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/webhook/WebhookDeliveryDao.java
index e8e63d856af..4d42c7c8b89 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/webhook/WebhookDeliveryDao.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/webhook/WebhookDeliveryDao.java
@@ -20,11 +20,15 @@
package org.sonar.db.webhook;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
import org.apache.ibatis.session.RowBounds;
import org.sonar.db.Dao;
import org.sonar.db.DbSession;
+import static java.util.function.Function.identity;
+import static java.util.stream.Collectors.toMap;
+
public class WebhookDeliveryDao implements Dao {
public Optional<WebhookDeliveryDto> selectByUuid(DbSession dbSession, String uuid) {
@@ -72,6 +76,12 @@ public class WebhookDeliveryDao implements Dao {
mapper(dbSession).deleteComponentBeforeDate(componentUuid, beforeDate);
}
+ public Map<String, WebhookDeliveryLiteDto> selectLatestDeliveries(DbSession dbSession, List<WebhookDto> webhooks) {
+ return webhooks.stream()
+ .flatMap(webhook -> selectByWebhookUuid(dbSession, webhook.getUuid(),0,1).stream())
+ .collect(toMap(WebhookDeliveryLiteDto::getWebhookUuid, identity()));
+ }
+
private static WebhookDeliveryMapper mapper(DbSession dbSession) {
return dbSession.getMapper(WebhookDeliveryMapper.class);
}
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/DbTester.java b/server/sonar-db-dao/src/test/java/org/sonar/db/DbTester.java
index 19b4b44a15e..2081a83f136 100644
--- a/server/sonar-db-dao/src/test/java/org/sonar/db/DbTester.java
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/DbTester.java
@@ -49,6 +49,7 @@ import org.sonar.db.source.FileSourceTester;
import org.sonar.db.user.RootFlagAssertions;
import org.sonar.db.user.UserDbTester;
import org.sonar.db.webhook.WebhookDbTester;
+import org.sonar.db.webhook.WebhookDeliveryDbTester;
import static com.google.common.base.Preconditions.checkState;
import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
@@ -85,6 +86,7 @@ public class DbTester extends AbstractDbTester<TestDb> {
private final FileSourceTester fileSourceTester;
private final PluginDbTester pluginDbTester;
private final WebhookDbTester webhookDbTester;
+ private final WebhookDeliveryDbTester webhookDeliveryDbTester;
public DbTester(System2 system2, @Nullable String schemaPath) {
super(TestDb.create(schemaPath));
@@ -109,6 +111,7 @@ public class DbTester extends AbstractDbTester<TestDb> {
this.fileSourceTester = new FileSourceTester(this);
this.pluginDbTester = new PluginDbTester(this);
this.webhookDbTester = new WebhookDbTester(this);
+ this.webhookDeliveryDbTester = new WebhookDeliveryDbTester(this);
}
public static DbTester create() {
@@ -255,6 +258,10 @@ public class DbTester extends AbstractDbTester<TestDb> {
return webhookDbTester;
}
+ public WebhookDeliveryDbTester webhookDelivery() {
+ return webhookDeliveryDbTester;
+ }
+
@Override
protected void after() {
if (session != null) {
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java
index 93b97c13772..62bffd56a95 100644
--- a/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java
@@ -73,7 +73,7 @@ import static org.sonar.db.component.ComponentTesting.newDirectory;
import static org.sonar.db.component.ComponentTesting.newFileDto;
import static org.sonar.db.component.ComponentTesting.newModuleDto;
import static org.sonar.db.component.ComponentTesting.newProjectCopy;
-import static org.sonar.db.webhook.WebhookDbTesting.newWebhookDeliveryDto;
+import static org.sonar.db.webhook.WebhookDbTesting.newDto;
import static org.sonar.db.webhook.WebhookDbTesting.selectAllDeliveryUuids;
public class PurgeDaoTest {
@@ -524,8 +524,8 @@ public class PurgeDaoTest {
@Test
public void deleteProject_deletes_webhook_deliveries() {
ComponentDto project = dbTester.components().insertPublicProject();
- dbClient.webhookDeliveryDao().insert(dbSession, newWebhookDeliveryDto().setComponentUuid(project.uuid()).setUuid("D1"));
- dbClient.webhookDeliveryDao().insert(dbSession, newWebhookDeliveryDto().setComponentUuid("P2").setUuid("D2"));
+ dbClient.webhookDeliveryDao().insert(dbSession, newDto().setComponentUuid(project.uuid()).setUuid("D1"));
+ dbClient.webhookDeliveryDao().insert(dbSession, newDto().setComponentUuid("P2").setUuid("D2"));
underTest.deleteProject(dbSession, project.uuid());
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookDbTesting.java b/server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookDbTesting.java
index 88ed7eacf15..56173748467 100644
--- a/server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookDbTesting.java
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookDbTesting.java
@@ -35,7 +35,19 @@ public class WebhookDbTesting {
// only statics
}
- public static WebhookDeliveryDto newWebhookDeliveryDto() {
+ /**
+ * Build a {@link WebhookDeliveryDto} with all mandatory fields.
+ * Optional fields are kept null.
+ */
+ public static WebhookDeliveryDto newDto(String uuid, String webhookUuid, String componentUuid, String ceTaskUuid) {
+ return newDto()
+ .setUuid(uuid)
+ .setWebhookUuid(webhookUuid)
+ .setComponentUuid(componentUuid)
+ .setCeTaskUuid(ceTaskUuid);
+ }
+
+ public static WebhookDeliveryDto newDto() {
return new WebhookDeliveryDto()
.setUuid(randomAlphanumeric(40))
.setComponentUuid(randomAlphanumeric(40))
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookDeliveryDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookDeliveryDaoTest.java
index 5b547035876..4dc3c7c9d89 100644
--- a/server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookDeliveryDaoTest.java
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookDeliveryDaoTest.java
@@ -30,8 +30,9 @@ import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
+import static com.google.common.collect.ImmutableList.of;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.sonar.db.webhook.WebhookDbTesting.newWebhookDeliveryDto;
+import static org.sonar.db.webhook.WebhookTesting.newProjectWebhook;
public class WebhookDeliveryDaoTest {
@@ -57,7 +58,7 @@ public class WebhookDeliveryDaoTest {
@Test
public void selectOrderedByComponentUuid_returns_empty_if_no_records() {
- underTest.insert(dbSession, newDto("D1", "WEBHOOK_UUID_1", "COMPONENT_1", "TASK_1"));
+ underTest.insert(dbSession, WebhookDbTesting.newDto("D1", "WEBHOOK_UUID_1", "COMPONENT_1", "TASK_1"));
List<WebhookDeliveryLiteDto> deliveries = underTest.selectOrderedByComponentUuid(dbSession, "ANOTHER_COMPONENT", 0, 10);
@@ -66,9 +67,9 @@ public class WebhookDeliveryDaoTest {
@Test
public void selectOrderedByComponentUuid_returns_records_ordered_by_date() {
- WebhookDeliveryDto dto1 = newDto("D1", "WEBHOOK_UUID_1", "COMPONENT_1", "TASK_1").setCreatedAt(BEFORE);
- WebhookDeliveryDto dto2 = newDto("D2", "WEBHOOK_UUID_1", "COMPONENT_1", "TASK_1").setCreatedAt(NOW);
- WebhookDeliveryDto dto3 = newDto("D3", "WEBHOOK_UUID_1", "COMPONENT_2", "TASK_1").setCreatedAt(NOW);
+ WebhookDeliveryDto dto1 = WebhookDbTesting.newDto("D1", "WEBHOOK_UUID_1", "COMPONENT_1", "TASK_1").setCreatedAt(BEFORE);
+ WebhookDeliveryDto dto2 = WebhookDbTesting.newDto("D2", "WEBHOOK_UUID_1", "COMPONENT_1", "TASK_1").setCreatedAt(NOW);
+ WebhookDeliveryDto dto3 = WebhookDbTesting.newDto("D3", "WEBHOOK_UUID_1", "COMPONENT_2", "TASK_1").setCreatedAt(NOW);
underTest.insert(dbSession, dto3);
underTest.insert(dbSession, dto2);
underTest.insert(dbSession, dto1);
@@ -80,7 +81,7 @@ public class WebhookDeliveryDaoTest {
@Test
public void selectOrderedByCeTaskUuid_returns_empty_if_no_records() {
- underTest.insert(dbSession, newDto("D1", "WEBHOOK_UUID_1", "COMPONENT_1", "TASK_1"));
+ underTest.insert(dbSession, WebhookDbTesting.newDto("D1", "WEBHOOK_UUID_1", "COMPONENT_1", "TASK_1"));
List<WebhookDeliveryLiteDto> deliveries = underTest.selectOrderedByCeTaskUuid(dbSession, "ANOTHER_TASK", 0, 10);
@@ -89,9 +90,9 @@ public class WebhookDeliveryDaoTest {
@Test
public void selectOrderedByCeTaskUuid_returns_records_ordered_by_date() {
- WebhookDeliveryDto dto1 = newDto("D1", "WEBHOOK_UUID_1", "COMPONENT_1", "TASK_1").setCreatedAt(BEFORE);
- WebhookDeliveryDto dto2 = newDto("D2", "WEBHOOK_UUID_1", "COMPONENT_1", "TASK_1").setCreatedAt(NOW);
- WebhookDeliveryDto dto3 = newDto("D3", "WEBHOOK_UUID_1", "COMPONENT_2", "TASK_2").setCreatedAt(NOW);
+ WebhookDeliveryDto dto1 = WebhookDbTesting.newDto("D1", "WEBHOOK_UUID_1", "COMPONENT_1", "TASK_1").setCreatedAt(BEFORE);
+ WebhookDeliveryDto dto2 = WebhookDbTesting.newDto("D2", "WEBHOOK_UUID_1", "COMPONENT_1", "TASK_1").setCreatedAt(NOW);
+ WebhookDeliveryDto dto3 = WebhookDbTesting.newDto("D3", "WEBHOOK_UUID_1", "COMPONENT_2", "TASK_2").setCreatedAt(NOW);
underTest.insert(dbSession, dto3);
underTest.insert(dbSession, dto2);
underTest.insert(dbSession, dto1);
@@ -104,7 +105,7 @@ public class WebhookDeliveryDaoTest {
@Test
public void selectByWebhookUuid_returns_empty_if_no_records() {
- underTest.insert(dbSession, newDto("D1", "WEBHOOK_UUID_1", "COMPONENT_1", "TASK_1"));
+ underTest.insert(dbSession, WebhookDbTesting.newDto("D1", "WEBHOOK_UUID_1", "COMPONENT_1", "TASK_1"));
List<WebhookDeliveryLiteDto> deliveries = underTest.selectByWebhookUuid(dbSession, "a-webhook-uuid", 0, 10);
@@ -114,9 +115,9 @@ public class WebhookDeliveryDaoTest {
@Test
public void selectByWebhookUuid_returns_records_ordered_by_date() {
WebhookDto webhookDto = dbWebhooks.insert(WebhookTesting.newProjectWebhook("COMPONENT_1"));
- WebhookDeliveryDto dto1 = newDto("D1", webhookDto.getUuid(), "COMPONENT_1", "TASK_1").setCreatedAt(BEFORE);
- WebhookDeliveryDto dto2 = newDto("D2", webhookDto.getUuid(), "COMPONENT_1", "TASK_2").setCreatedAt(NOW);
- WebhookDeliveryDto dto3 = newDto("D3", "fake-webhook-uuid", "COMPONENT_2", "TASK_1").setCreatedAt(NOW);
+ WebhookDeliveryDto dto1 = WebhookDbTesting.newDto("D1", webhookDto.getUuid(), "COMPONENT_1", "TASK_1").setCreatedAt(BEFORE);
+ WebhookDeliveryDto dto2 = WebhookDbTesting.newDto("D2", webhookDto.getUuid(), "COMPONENT_1", "TASK_2").setCreatedAt(NOW);
+ WebhookDeliveryDto dto3 = WebhookDbTesting.newDto("D3", "fake-webhook-uuid", "COMPONENT_2", "TASK_1").setCreatedAt(NOW);
underTest.insert(dbSession, dto3);
underTest.insert(dbSession, dto2);
underTest.insert(dbSession, dto1);
@@ -129,25 +130,40 @@ public class WebhookDeliveryDaoTest {
@Test
public void selectByWebhookUuid_returns_records_according_to_pagination() {
WebhookDto webhookDto = dbWebhooks.insert(WebhookTesting.newProjectWebhook("COMPONENT_1"));
- WebhookDeliveryDto dto1 = newDto("D1", webhookDto.getUuid(), "COMPONENT_1", "TASK_1").setCreatedAt(BEFORE);
- underTest.insert(dbSession, dto1);
- WebhookDeliveryDto dto2 = newDto("D2", webhookDto.getUuid(), "COMPONENT_1", "TASK_2").setCreatedAt(NOW);
- underTest.insert(dbSession, dto2);
- underTest.insert(dbSession, newDto("D3", webhookDto.getUuid(), "COMPONENT_1", "TASK_2").setCreatedAt(NOW));
- underTest.insert(dbSession, newDto("D4", webhookDto.getUuid(), "COMPONENT_1", "TASK_2").setCreatedAt(NOW));
- underTest.insert(dbSession, newDto("D5", webhookDto.getUuid(), "COMPONENT_1", "TASK_2").setCreatedAt(NOW));
- underTest.insert(dbSession, newDto("D6", webhookDto.getUuid(), "COMPONENT_1", "TASK_2").setCreatedAt(NOW));
+ underTest.insert(dbSession, WebhookDbTesting.newDto("D1", webhookDto.getUuid(), "COMPONENT_1", "TASK_2").setCreatedAt(NOW - 5_000L));
+ underTest.insert(dbSession, WebhookDbTesting.newDto("D2", webhookDto.getUuid(), "COMPONENT_1", "TASK_2").setCreatedAt(NOW - 4_000L));
+ underTest.insert(dbSession, WebhookDbTesting.newDto("D3", webhookDto.getUuid(), "COMPONENT_1", "TASK_2").setCreatedAt(NOW - 3_000L));
+ underTest.insert(dbSession, WebhookDbTesting.newDto("D4", webhookDto.getUuid(), "COMPONENT_1", "TASK_2").setCreatedAt(NOW - 2_000L));
+ underTest.insert(dbSession, WebhookDbTesting.newDto("D5", webhookDto.getUuid(), "COMPONENT_1", "TASK_2").setCreatedAt(NOW - 1_000L));
+ underTest.insert(dbSession, WebhookDbTesting.newDto("D6", webhookDto.getUuid(), "COMPONENT_1", "TASK_2").setCreatedAt(NOW));
- List<WebhookDeliveryLiteDto> deliveries = underTest.selectByWebhookUuid(dbSession, webhookDto.getUuid(), 1, 3);
+ List<WebhookDeliveryLiteDto> deliveries = underTest.selectByWebhookUuid(dbSession, webhookDto.getUuid(), 2, 2);
- assertThat(deliveries).extracting(WebhookDeliveryLiteDto::getUuid).containsExactlyInAnyOrder("D3", "D4", "D5");
+ assertThat(deliveries).extracting(WebhookDeliveryLiteDto::getUuid).containsExactly("D4", "D3");
}
+ @Test
+ public void selectLatestDelivery_of_a_webhook() {
+ WebhookDto webhook1 = dbWebhooks.insert(newProjectWebhook("COMPONENT_1"));
+ underTest.insert(dbSession, WebhookDbTesting.newDto("WH1-DELIVERY-1-UUID", webhook1.getUuid(), "COMPONENT_1", "TASK_1").setCreatedAt(BEFORE));
+ underTest.insert(dbSession, WebhookDbTesting.newDto("WH1-DELIVERY-2-UUID", webhook1.getUuid(), "COMPONENT_1", "TASK_2").setCreatedAt(NOW));
+
+ WebhookDto webhook2 = dbWebhooks.insert(newProjectWebhook("COMPONENT_1"));
+ underTest.insert(dbSession, WebhookDbTesting.newDto("WH2-DELIVERY-1-UUID", webhook2.getUuid(), "COMPONENT_1", "TASK_1").setCreatedAt(BEFORE));
+ underTest.insert(dbSession, WebhookDbTesting.newDto("WH2-DELIVERY-2-UUID", webhook2.getUuid(), "COMPONENT_1", "TASK_2").setCreatedAt(NOW));
+
+ Map<String, WebhookDeliveryLiteDto> map = underTest.selectLatestDeliveries(dbSession, of(webhook1, webhook2));
+ assertThat(map).containsKeys(webhook1.getUuid());
+ assertThat(map.get(webhook1.getUuid())).extracting(WebhookDeliveryLiteDto::getUuid).contains("WH1-DELIVERY-2-UUID");
+
+ assertThat(map).containsKeys(webhook2.getUuid());
+ assertThat(map.get(webhook2.getUuid())).extracting(WebhookDeliveryLiteDto::getUuid).contains("WH2-DELIVERY-2-UUID");
+ }
@Test
public void insert_row_with_only_mandatory_columns() {
- WebhookDeliveryDto dto = newDto("DELIVERY_1", "WEBHOOK_UUID_1", "COMPONENT_1", "TASK_1")
+ WebhookDeliveryDto dto = WebhookDbTesting.newDto("DELIVERY_1", "WEBHOOK_UUID_1", "COMPONENT_1", "TASK_1")
.setHttpStatus(null)
.setDurationMs(null)
.setErrorStacktrace(null);
@@ -165,7 +181,7 @@ public class WebhookDeliveryDaoTest {
@Test
public void insert_row_with_all_columns() {
- WebhookDeliveryDto dto = newDto("DELIVERY_1", "WEBHOOK_UUID_1", "COMPONENT_1", "TASK_1");
+ WebhookDeliveryDto dto = WebhookDbTesting.newDto("DELIVERY_1", "WEBHOOK_UUID_1", "COMPONENT_1", "TASK_1");
underTest.insert(dbSession, dto);
@@ -179,9 +195,9 @@ public class WebhookDeliveryDaoTest {
@Test
public void deleteComponentBeforeDate_deletes_rows_before_date() {
- underTest.insert(dbSession, newDto("DELIVERY_1", "WEBHOOK_UUID_1", "COMPONENT_1", "TASK_1").setCreatedAt(1_000_000L));
- underTest.insert(dbSession, newDto("DELIVERY_2", "WEBHOOK_UUID_1", "COMPONENT_1", "TASK_2").setCreatedAt(2_000_000L));
- underTest.insert(dbSession, newDto("DELIVERY_3", "WEBHOOK_UUID_1", "COMPONENT_2", "TASK_3").setCreatedAt(1_000_000L));
+ underTest.insert(dbSession, WebhookDbTesting.newDto("DELIVERY_1", "WEBHOOK_UUID_1", "COMPONENT_1", "TASK_1").setCreatedAt(1_000_000L));
+ underTest.insert(dbSession, WebhookDbTesting.newDto("DELIVERY_2", "WEBHOOK_UUID_1", "COMPONENT_1", "TASK_2").setCreatedAt(2_000_000L));
+ underTest.insert(dbSession, WebhookDbTesting.newDto("DELIVERY_3", "WEBHOOK_UUID_1", "COMPONENT_2", "TASK_3").setCreatedAt(1_000_000L));
// should delete the old delivery on COMPONENT_1 and keep the one of COMPONENT_2
underTest.deleteComponentBeforeDate(dbSession, "COMPONENT_1", 1_500_000L);
@@ -199,7 +215,7 @@ public class WebhookDeliveryDaoTest {
@Test
public void deleteComponentBeforeDate_does_nothing_on_invalid_uuid() {
- underTest.insert(dbSession, newDto("DELIVERY_1", "WEBHOOK_UUID_1", "COMPONENT_1", "TASK_1").setCreatedAt(1_000_000L));
+ underTest.insert(dbSession, WebhookDbTesting.newDto("DELIVERY_1", "WEBHOOK_UUID_1", "COMPONENT_1", "TASK_1").setCreatedAt(1_000_000L));
underTest.deleteComponentBeforeDate(dbSession, "COMPONENT_2", 1_500_000L);
@@ -217,18 +233,6 @@ public class WebhookDeliveryDaoTest {
assertThat(actual.getCreatedAt()).isEqualTo(expected.getCreatedAt());
}
- /**
- * Build a {@link WebhookDeliveryDto} with all mandatory fields.
- * Optional fields are kept null.
- */
- private static WebhookDeliveryDto newDto(String uuid, String webhookUuid, String componentUuid, String ceTaskUuid) {
- return newWebhookDeliveryDto()
- .setUuid(uuid)
- .setWebhookUuid(webhookUuid)
- .setComponentUuid(componentUuid)
- .setCeTaskUuid(ceTaskUuid);
- }
-
private WebhookDeliveryDto selectByUuid(String uuid) {
Optional<WebhookDeliveryDto> dto = underTest.selectByUuid(dbSession, uuid);
assertThat(dto).isPresent();
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookDeliveryDbTester.java b/server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookDeliveryDbTester.java
new file mode 100644
index 00000000000..d31d270018e
--- /dev/null
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookDeliveryDbTester.java
@@ -0,0 +1,40 @@
+/*
+ * 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.db.webhook;
+
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+
+public class WebhookDeliveryDbTester {
+
+ private final DbTester dbTester;
+
+ public WebhookDeliveryDbTester(DbTester dbTester) {
+ this.dbTester = dbTester;
+ }
+
+ public WebhookDeliveryLiteDto insert(WebhookDeliveryDto dto) {
+ DbSession dbSession = dbTester.getSession();
+ dbTester.getDbClient().webhookDeliveryDao().insert(dbSession, dto);
+ dbSession.commit();
+ return dto;
+ }
+
+}
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookTesting.java b/server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookTesting.java
index 4568a137869..58ba1e46606 100644
--- a/server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookTesting.java
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookTesting.java
@@ -19,10 +19,11 @@
*/
package org.sonar.db.webhook;
-import java.util.Calendar;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.organization.OrganizationDto;
+import java.util.Calendar;
+
import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
public class WebhookTesting {
@@ -46,6 +47,12 @@ public class WebhookTesting {
.setOrganizationUuid(organizationDto.getUuid());
}
+ public static WebhookDto newOrganizationWebhook(String name, String organizationUuid) {
+ return getWebhookDto()
+ .setName(name)
+ .setOrganizationUuid(organizationUuid);
+ }
+
private static WebhookDto getWebhookDto() {
return new WebhookDto()
.setUuid(randomAlphanumeric(40))
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/MigrateWebhooksToWebhooksTable.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/MigrateWebhooksToWebhooksTable.java
index 5e133925daa..fb782d5f236 100644
--- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/MigrateWebhooksToWebhooksTable.java
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/MigrateWebhooksToWebhooksTable.java
@@ -25,6 +25,7 @@ import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
+import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
@@ -33,6 +34,7 @@ import org.sonar.db.Database;
import org.sonar.server.platform.db.migration.step.DataChange;
import org.sonar.server.platform.db.migration.version.v63.DefaultOrganizationUuidProvider;
+import static com.google.common.base.Preconditions.checkNotNull;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
@@ -124,11 +126,13 @@ public class MigrateWebhooksToWebhooksTable extends DataChange {
for (String value : values) {
PropertyRow name = properties.get("sonar.webhooks.project." + value + ".name");
PropertyRow url = properties.get("sonar.webhooks.project." + value + ".url");
- webhooks.add(new Webhook(name, url, null, projectUuidOf(context, name)));
+ String projectUuid = checkNotNull(projectUuidOf(context, name), "Project was not found for property : sonar.webhooks.project.%s", value);
+ webhooks.add(new Webhook(name, url, null, projectUuid));
}
return webhooks;
}
+ @CheckForNull
private static String projectUuidOf(Context context, PropertyRow row) throws SQLException {
return context
.prepareSelect("select uuid from projects where id = ?")
diff --git a/server/sonar-server/src/main/java/org/sonar/server/webhook/WebhookCallerImpl.java b/server/sonar-server/src/main/java/org/sonar/server/webhook/WebhookCallerImpl.java
index f5d231fc98b..e9e70ca09c1 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/webhook/WebhookCallerImpl.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/webhook/WebhookCallerImpl.java
@@ -65,12 +65,14 @@ public class WebhookCallerImpl implements WebhookCaller {
Request request = buildHttpRequest(webhook, payload);
try (Response response = execute(request)) {
builder.setHttpStatus(response.code());
- builder.setDurationInMs((int) (system.now() - startedAt));
}
} catch (Exception e) {
builder.setError(e);
}
- return builder.build();
+
+ return builder
+ .setDurationInMs((int) (system.now() - startedAt))
+ .build();
}
private static Request buildHttpRequest(Webhook webhook, WebhookPayload payload) {
diff --git a/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/ListAction.java b/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/ListAction.java
index d90f1f0490c..cc8f70ab27c 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/ListAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/ListAction.java
@@ -21,6 +21,7 @@ package org.sonar.server.webhook.ws;
import com.google.common.io.Resources;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
import javax.annotation.Nullable;
import org.sonar.api.server.ws.Request;
@@ -30,13 +31,17 @@ 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.WebhookDeliveryLiteDto;
import org.sonar.db.webhook.WebhookDto;
import org.sonar.server.organization.DefaultOrganizationProvider;
import org.sonar.server.user.UserSession;
-import org.sonarqube.ws.Webhooks.ListWsResponse.Builder;
+import org.sonarqube.ws.Webhooks;
+import org.sonarqube.ws.Webhooks.ListResponse;
+import org.sonarqube.ws.Webhooks.ListResponseElement;
import static java.util.Optional.ofNullable;
import static org.apache.commons.lang.StringUtils.isNotBlank;
+import static org.sonar.api.utils.DateUtils.formatDateTime;
import static org.sonar.server.webhook.ws.WebhooksWsParameters.LIST_ACTION;
import static org.sonar.server.webhook.ws.WebhooksWsParameters.ORGANIZATION_KEY_PARAM;
import static org.sonar.server.webhook.ws.WebhooksWsParameters.PROJECT_KEY_PARAM;
@@ -45,7 +50,6 @@ import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
import static org.sonar.server.ws.WsUtils.checkFoundWithOptional;
import static org.sonar.server.ws.WsUtils.checkStateWithOptional;
import static org.sonar.server.ws.WsUtils.writeProtobuf;
-import static org.sonarqube.ws.Webhooks.ListWsResponse.newBuilder;
public class ListAction implements WebhooksWsAction {
@@ -92,55 +96,77 @@ public class ListAction implements WebhooksWsAction {
userSession.checkLoggedIn();
- writeResponse(request, response, doHandle(organizationKey, projectKey));
-
+ try (DbSession dbSession = dbClient.openSession(true)) {
+ List<WebhookDto> webhookDtos = doHandle(dbSession, organizationKey, projectKey);
+ Map<String, WebhookDeliveryLiteDto> lastDeliveries = loadLastDeliveriesOf(dbSession, webhookDtos);
+ writeResponse(request, response, webhookDtos, lastDeliveries);
+ }
}
- private List<WebhookDto> doHandle(@Nullable String organizationKey, @Nullable String projectKey) {
-
- try (DbSession dbSession = dbClient.openSession(true)) {
+ private Map<String, WebhookDeliveryLiteDto> loadLastDeliveriesOf(DbSession dbSession, List<WebhookDto> webhookDtos) {
+ return dbClient.webhookDeliveryDao().selectLatestDeliveries(dbSession, webhookDtos);
+ }
- OrganizationDto organizationDto;
- if (isNotBlank(organizationKey)) {
- Optional<OrganizationDto> dtoOptional = dbClient.organizationDao().selectByKey(dbSession, organizationKey);
- organizationDto = checkFoundWithOptional(dtoOptional, "No organization with key '%s'", organizationKey);
- } else {
- organizationDto = defaultOrganizationDto(dbSession);
- }
+ private List<WebhookDto> doHandle(DbSession dbSession, @Nullable String organizationKey, @Nullable String projectKey) {
- if (isNotBlank(projectKey)) {
+ OrganizationDto organizationDto;
+ if (isNotBlank(organizationKey)) {
+ Optional<OrganizationDto> dtoOptional = dbClient.organizationDao().selectByKey(dbSession, organizationKey);
+ organizationDto = checkFoundWithOptional(dtoOptional, "No organization with key '%s'", organizationKey);
+ } else {
+ organizationDto = defaultOrganizationDto(dbSession);
+ }
- Optional<ComponentDto> optional = ofNullable(dbClient.componentDao().selectByKey(dbSession, projectKey).orNull());
- ComponentDto componentDto = checkFoundWithOptional(optional, "project %s does not exist", projectKey);
- webhookSupport.checkPermission(componentDto);
- webhookSupport.checkThatProjectBelongsToOrganization(componentDto, organizationDto, "Project '%s' does not belong to organisation '%s'", projectKey, organizationKey);
- webhookSupport.checkPermission(componentDto);
- return dbClient.webhookDao().selectByProject(dbSession, componentDto);
+ if (isNotBlank(projectKey)) {
- } else {
+ Optional<ComponentDto> optional = ofNullable(dbClient.componentDao().selectByKey(dbSession, projectKey).orNull());
+ ComponentDto componentDto = checkFoundWithOptional(optional, "project %s does not exist", projectKey);
+ webhookSupport.checkPermission(componentDto);
+ webhookSupport.checkThatProjectBelongsToOrganization(componentDto, organizationDto, "Project '%s' does not belong to organisation '%s'", projectKey, organizationKey);
+ webhookSupport.checkPermission(componentDto);
+ return dbClient.webhookDao().selectByProject(dbSession, componentDto);
- webhookSupport.checkPermission(organizationDto);
- return dbClient.webhookDao().selectByOrganization(dbSession, organizationDto);
+ } else {
- }
+ webhookSupport.checkPermission(organizationDto);
+ return dbClient.webhookDao().selectByOrganization(dbSession, organizationDto);
}
- }
-
- private static void writeResponse(Request request, Response response, List<WebhookDto> webhookDtos) {
- Builder responseBuilder = newBuilder();
+ }
+ private static void writeResponse(Request request, Response response, List<WebhookDto> webhookDtos, Map<String, WebhookDeliveryLiteDto> lastDeliveries) {
+ ListResponse.Builder responseBuilder = ListResponse.newBuilder();
webhookDtos
.stream()
- .forEach(webhook -> responseBuilder.addWebhooksBuilder()
- .setKey(webhook.getUuid())
- .setName(webhook.getName())
- .setUrl(webhook.getUrl()));
-
+ .forEach(webhook -> {
+ ListResponseElement.Builder responseElementBuilder = responseBuilder.addWebhooksBuilder();
+ responseElementBuilder
+ .setKey(webhook.getUuid())
+ .setName(webhook.getName())
+ .setUrl(webhook.getUrl());
+ addLastDelivery(responseElementBuilder, webhook, lastDeliveries);
+ });
writeProtobuf(responseBuilder.build(), request, response);
}
+ private static void addLastDelivery(ListResponseElement.Builder responseElementBuilder, WebhookDto webhook, Map<String, WebhookDeliveryLiteDto> lastDeliveries) {
+ if (lastDeliveries.containsKey(webhook.getUuid())) {
+ WebhookDeliveryLiteDto delivery = lastDeliveries.get(webhook.getUuid());
+ Webhooks.LatestDelivery.Builder builder = responseElementBuilder.getLatestDeliveryBuilder()
+ .setId(delivery.getUuid())
+ .setAt(formatDateTime(delivery.getCreatedAt()))
+ .setSuccess(delivery.isSuccess());
+ if (delivery.getHttpStatus() != null) {
+ builder.setHttpStatus(delivery.getHttpStatus());
+ }
+ if (delivery.getDurationMs() != null) {
+ builder.setDurationMs(delivery.getDurationMs());
+ }
+ builder.build();
+ }
+ }
+
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/WebhookDeliveriesAction.java b/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhookDeliveriesAction.java
index ee5e469f57a..0ae3dd4a777 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhookDeliveriesAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhookDeliveriesAction.java
@@ -42,6 +42,7 @@ import static java.util.Objects.requireNonNull;
import static org.apache.commons.lang.StringUtils.isNotBlank;
import static org.sonar.api.server.ws.WebService.Param.PAGE;
import static org.sonar.api.server.ws.WebService.Param.PAGE_SIZE;
+import static org.sonar.api.utils.Paging.offset;
import static org.sonar.core.util.Uuids.UUID_EXAMPLE_02;
import static org.sonar.server.es.SearchOptions.MAX_LIMIT;
import static org.sonar.server.webhook.ws.WebhookWsSupport.copyDtoToProtobuf;
@@ -115,17 +116,17 @@ public class WebhookDeliveriesAction implements WebhooksWsAction {
int totalElements;
try (DbSession dbSession = dbClient.openSession(false)) {
if (isNotBlank(webhookUuid)) {
- deliveries = dbClient.webhookDeliveryDao().selectByWebhookUuid(dbSession, webhookUuid, page - 1, pageSize);
- component = getComponentDto(dbSession, deliveries);
totalElements = dbClient.webhookDeliveryDao().countDeliveriesByWebhookUuid(dbSession, webhookUuid);
+ deliveries = dbClient.webhookDeliveryDao().selectByWebhookUuid(dbSession, webhookUuid, offset(page, pageSize), pageSize);
+ component = getComponentDto(dbSession, deliveries);
} else if (componentKey != null) {
component = componentFinder.getByKey(dbSession, componentKey);
- deliveries = dbClient.webhookDeliveryDao().selectOrderedByComponentUuid(dbSession, component.uuid(), page - 1, pageSize);
totalElements = dbClient.webhookDeliveryDao().countDeliveriesByComponentUuid(dbSession, component.uuid());
+ deliveries = dbClient.webhookDeliveryDao().selectOrderedByComponentUuid(dbSession, component.uuid(), offset(page, pageSize), pageSize);
} else {
- deliveries = dbClient.webhookDeliveryDao().selectOrderedByCeTaskUuid(dbSession, ceTaskId, page - 1, pageSize);
- component = getComponentDto(dbSession, deliveries);
totalElements = dbClient.webhookDeliveryDao().countDeliveriesByCeTaskUuid(dbSession, ceTaskId);
+ deliveries = dbClient.webhookDeliveryDao().selectOrderedByCeTaskUuid(dbSession, ceTaskId, offset(page, pageSize), pageSize);
+ component = getComponentDto(dbSession, deliveries);
}
}
return new Data(component, deliveries).withPagingInfo(page, pageSize, totalElements);
diff --git a/server/sonar-server/src/main/resources/org/sonar/server/webhook/ws/example-deliveries.json b/server/sonar-server/src/main/resources/org/sonar/server/webhook/ws/example-deliveries.json
index 129d9dbfc21..d95c99d3ba3 100644
--- a/server/sonar-server/src/main/resources/org/sonar/server/webhook/ws/example-deliveries.json
+++ b/server/sonar-server/src/main/resources/org/sonar/server/webhook/ws/example-deliveries.json
@@ -1,4 +1,9 @@
{
+ "paging": {
+ "pageIndex": 1,
+ "pageSize": 10,
+ "total": 1
+ },
"deliveries": [
{
"id": "d1",
@@ -12,4 +17,4 @@
"durationMs": 10
}
]
-}
+} \ No newline at end of file
diff --git a/server/sonar-server/src/main/resources/org/sonar/server/webhook/ws/example-delivery.json b/server/sonar-server/src/main/resources/org/sonar/server/webhook/ws/example-delivery.json
index d95c99d3ba3..d4da8033df5 100644
--- a/server/sonar-server/src/main/resources/org/sonar/server/webhook/ws/example-delivery.json
+++ b/server/sonar-server/src/main/resources/org/sonar/server/webhook/ws/example-delivery.json
@@ -1,20 +1,14 @@
{
- "paging": {
- "pageIndex": 1,
- "pageSize": 10,
- "total": 1
- },
- "deliveries": [
- {
- "id": "d1",
- "componentKey": "my-project",
- "ceTaskId": "task-1",
- "name": "Jenkins",
- "url": "http://jenkins",
- "at": "2017-07-14T04:40:00+0200",
- "success": true,
- "httpStatus": 200,
- "durationMs": 10
- }
- ]
-} \ No newline at end of file
+ "delivery": {
+ "id": "d1",
+ "componentKey": "my-project",
+ "ceTaskId": "task-1",
+ "name": "Jenkins",
+ "url": "http://jenkins",
+ "at": "2017-07-14T04:40:00+0200",
+ "success": true,
+ "httpStatus": 200,
+ "durationMs": 10,
+ "payload": "{\"status\"=\"SUCCESS\"}"
+ }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/webhook/WebhookCallerImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/webhook/WebhookCallerImplTest.java
index 1e174e6ba54..60ac24ee262 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/webhook/WebhookCallerImplTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/webhook/WebhookCallerImplTest.java
@@ -90,7 +90,7 @@ public class WebhookCallerImplTest {
WebhookDelivery delivery = newSender().call(webhook, PAYLOAD);
assertThat(delivery.getHttpStatus()).isEmpty();
- assertThat(delivery.getDurationInMs()).isEmpty();
+ assertThat(delivery.getDurationInMs().get()).isGreaterThanOrEqualTo(0);
// message can be "Connection refused" or "connect timed out"
assertThat(delivery.getErrorMessage().get()).matches("(.*Connection refused.*)|(.*connect timed out.*)");
assertThat(delivery.getAt()).isEqualTo(NOW);
@@ -105,7 +105,7 @@ public class WebhookCallerImplTest {
WebhookDelivery delivery = newSender().call(webhook, PAYLOAD);
assertThat(delivery.getHttpStatus()).isEmpty();
- assertThat(delivery.getDurationInMs()).isEmpty();
+ assertThat(delivery.getDurationInMs().get()).isGreaterThanOrEqualTo(0);
assertThat(delivery.getError().get()).isInstanceOf(IllegalArgumentException.class);
assertThat(delivery.getErrorMessage().get()).isEqualTo("Webhook URL is not valid: this_is_not_an_url");
assertThat(delivery.getAt()).isEqualTo(NOW);
diff --git a/server/sonar-server/src/test/java/org/sonar/server/webhook/WebhookDeliveryStorageTest.java b/server/sonar-server/src/test/java/org/sonar/server/webhook/WebhookDeliveryStorageTest.java
index 158fdd73002..1fac932cbc9 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/webhook/WebhookDeliveryStorageTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/webhook/WebhookDeliveryStorageTest.java
@@ -28,12 +28,13 @@ import org.sonar.core.util.UuidFactory;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
+import org.sonar.db.webhook.WebhookDbTesting;
import org.sonar.db.webhook.WebhookDeliveryDto;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
-import static org.sonar.db.webhook.WebhookDbTesting.newWebhookDeliveryDto;
+import static org.sonar.db.webhook.WebhookDbTesting.newDto;
import static org.sonar.db.webhook.WebhookDbTesting.selectAllDeliveryUuids;
public class WebhookDeliveryStorageTest {
@@ -111,7 +112,7 @@ public class WebhookDeliveryStorageTest {
}
private static WebhookDeliveryDto newDto(String uuid, String componentUuid, long at) {
- return newWebhookDeliveryDto()
+ return WebhookDbTesting.newDto()
.setUuid(uuid)
.setComponentUuid(componentUuid)
.setCreatedAt(at);
diff --git a/server/sonar-server/src/test/java/org/sonar/server/webhook/ws/ListActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/webhook/ws/ListActionTest.java
index 995eb3b106b..0a6505d2f14 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/webhook/ws/ListActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/webhook/ws/ListActionTest.java
@@ -19,6 +19,7 @@
*/
package org.sonar.server.webhook.ws;
+import java.util.List;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
@@ -31,6 +32,7 @@ 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.WebhookDeliveryDbTester;
import org.sonar.db.webhook.WebhookDto;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.exceptions.NotFoundException;
@@ -38,8 +40,8 @@ import org.sonar.server.exceptions.UnauthorizedException;
import org.sonar.server.organization.DefaultOrganizationProvider;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.ws.WsActionTester;
-import org.sonarqube.ws.Webhooks.ListWsResponse;
-import org.sonarqube.ws.Webhooks.ListWsResponse.List;
+import org.sonarqube.ws.Webhooks;
+import org.sonarqube.ws.Webhooks.ListResponse;
import static java.lang.String.format;
import static org.assertj.core.api.Assertions.assertThat;
@@ -48,6 +50,8 @@ 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.db.webhook.WebhookDbTesting.newDto;
+import static org.sonar.db.webhook.WebhookTesting.newOrganizationWebhook;
import static org.sonar.server.organization.TestDefaultOrganizationProvider.from;
import static org.sonar.server.tester.UserSessionRule.standalone;
import static org.sonar.server.webhook.ws.WebhooksWsParameters.ORGANIZATION_KEY_PARAM;
@@ -55,199 +59,252 @@ import static org.sonar.server.webhook.ws.WebhooksWsParameters.PROJECT_KEY_PARAM
public class ListActionTest {
- @Rule
- public ExpectedException expectedException = none();
+ private static final long NOW = 1_500_000_000L;
+ private static final long BEFORE = NOW - 1_000L;
- @Rule
- public UserSessionRule userSession = standalone();
+ @Rule
+ public ExpectedException expectedException = none();
- @Rule
- public DbTester db = create();
+ @Rule
+ public UserSessionRule userSession = standalone();
- private DbClient dbClient = db.getDbClient();
- private DefaultOrganizationProvider defaultOrganizationProvider = from(db);
- private WebhookSupport webhookSupport = new WebhookSupport(userSession);
- private ListAction underTest = new ListAction(dbClient, userSession, defaultOrganizationProvider, webhookSupport);
+ @Rule
+ public DbTester db = create();
- private ComponentDbTester componentDbTester = db.components();
- private WebhookDbTester webhookDbTester = db.webhooks();
- private OrganizationDbTester organizationDbTester = db.organizations();
- private WsActionTester wsActionTester = new WsActionTester(underTest);
+ private DbClient dbClient = db.getDbClient();
+ private DefaultOrganizationProvider defaultOrganizationProvider = from(db);
+ private WebhookSupport webhookSupport = new WebhookSupport(userSession);
+ private ListAction underTest = new ListAction(dbClient, userSession, defaultOrganizationProvider, webhookSupport);
- @Test
- public void definition() {
+ private ComponentDbTester componentDbTester = db.components();
+ private WebhookDbTester webhookDbTester = db.webhooks();
+ private WebhookDeliveryDbTester webhookDeliveryDbTester = db.webhookDelivery();
+ private OrganizationDbTester organizationDbTester = db.organizations();
+ private WsActionTester wsActionTester = new WsActionTester(underTest);
- WebService.Action action = wsActionTester.getDef();
+ @Test
+ public void definition() {
- assertThat(action).isNotNull();
- assertThat(action.isInternal()).isFalse();
- assertThat(action.isPost()).isFalse();
- assertThat(action.responseExampleAsString()).isNotEmpty();
- assertThat(action.params())
- .extracting(Param::key, Param::isRequired)
- .containsExactlyInAnyOrder(
- tuple("organization", false),
- tuple("project", false));
+ WebService.Action action = wsActionTester.getDef();
- }
+ assertThat(action).isNotNull();
+ assertThat(action.isInternal()).isFalse();
+ assertThat(action.isPost()).isFalse();
+ assertThat(action.responseExampleAsString()).isNotEmpty();
+ assertThat(action.params())
+ .extracting(Param::key, Param::isRequired)
+ .containsExactlyInAnyOrder(
+ tuple("organization", false),
+ tuple("project", false));
- @Test
- public void List_global_webhooks() {
+ }
- WebhookDto dto1 = webhookDbTester.insertWebhook(db.getDefaultOrganization());
- WebhookDto dto2 = webhookDbTester.insertWebhook(db.getDefaultOrganization());
- userSession.logIn().addPermission(ADMINISTER, db.getDefaultOrganization().getUuid());
+ @Test
+ public void list_webhooks_and_their_latest_delivery() {
+ WebhookDto webhook1 = webhookDbTester.insert(newOrganizationWebhook("aaa", defaultOrganizationProvider.get().getUuid()));
+ webhookDeliveryDbTester.insert(newDto("WH1-DELIVERY-1-UUID", webhook1.getUuid(), "COMPONENT_1", "TASK_1").setCreatedAt(BEFORE));
+ webhookDeliveryDbTester.insert(newDto("WH1-DELIVERY-2-UUID", webhook1.getUuid(), "COMPONENT_1", "TASK_2").setCreatedAt(NOW));
- ListWsResponse response = wsActionTester.newRequest()
- .executeProtobuf(ListWsResponse.class);
+ WebhookDto webhook2 = webhookDbTester.insert(newOrganizationWebhook("bbb", defaultOrganizationProvider.get().getUuid()));
+ webhookDeliveryDbTester.insert(newDto("WH2-DELIVERY-1-UUID", webhook2.getUuid(), "COMPONENT_1", "TASK_1").setCreatedAt(BEFORE));
+ webhookDeliveryDbTester.insert(newDto("WH2-DELIVERY-2-UUID", webhook2.getUuid(), "COMPONENT_1", "TASK_2").setCreatedAt(NOW));
- assertThat(response.getWebhooksList())
- .extracting(List::getName, List::getUrl)
- .contains(tuple(dto1.getName(), dto1.getUrl()),
- tuple(dto2.getName(), dto2.getUrl()));
+ userSession.logIn().addPermission(ADMINISTER, db.getDefaultOrganization().getUuid());
- }
+ ListResponse response = wsActionTester.newRequest().executeProtobuf(ListResponse.class);
- @Test
- public void List_project_webhooks_when_no_organization_is_provided() {
+ List<Webhooks.ListResponseElement> elements = response.getWebhooksList();
+ assertThat(elements.size()).isEqualTo(2);
- ComponentDto project1 = componentDbTester.insertPrivateProject();
- userSession.logIn().addProjectPermission(ADMIN, project1);
+ assertThat(elements.get(0)).extracting(Webhooks.ListResponseElement::getKey).containsExactly(webhook1.getUuid());
+ assertThat(elements.get(0)).extracting(Webhooks.ListResponseElement::getName).containsExactly("aaa");
+ assertThat(elements.get(0).getLatestDelivery()).isNotNull();
+ assertThat(elements.get(0).getLatestDelivery()).extracting(Webhooks.LatestDelivery::getId).containsExactly("WH1-DELIVERY-2-UUID");
- WebhookDto dto1 = webhookDbTester.insertWebhook(project1);
- WebhookDto dto2 = webhookDbTester.insertWebhook(project1);
+ assertThat(elements.get(1)).extracting(Webhooks.ListResponseElement::getKey).containsExactly(webhook2.getUuid());
+ assertThat(elements.get(1)).extracting(Webhooks.ListResponseElement::getName).containsExactly("bbb");
+ assertThat(elements.get(1).getLatestDelivery()).isNotNull();
+ assertThat(elements.get(1).getLatestDelivery()).extracting(Webhooks.LatestDelivery::getId).containsExactly("WH2-DELIVERY-2-UUID");
+ }
- ListWsResponse response = wsActionTester.newRequest()
- .setParam(PROJECT_KEY_PARAM, project1.getKey())
- .executeProtobuf(ListWsResponse.class);
+ @Test
+ public void list_webhooks_when_no_delivery() {
+ WebhookDto webhook1 = webhookDbTester.insert(newOrganizationWebhook("aaa", defaultOrganizationProvider.get().getUuid()));
+ WebhookDto webhook2 = webhookDbTester.insert(newOrganizationWebhook("bbb", defaultOrganizationProvider.get().getUuid()));
- assertThat(response.getWebhooksList())
- .extracting(List::getName, List::getUrl)
- .contains(tuple(dto1.getName(), dto1.getUrl()),
- tuple(dto2.getName(), dto2.getUrl()));
+ userSession.logIn().addPermission(ADMINISTER, db.getDefaultOrganization().getUuid());
- }
+ ListResponse response = wsActionTester.newRequest().executeProtobuf(ListResponse.class);
- @Test
- public void List_organization_webhooks() {
+ List<Webhooks.ListResponseElement> elements = response.getWebhooksList();
+ assertThat(elements.size()).isEqualTo(2);
- OrganizationDto organizationDto = organizationDbTester.insert();
- WebhookDto dto1 = webhookDbTester.insertWebhook(organizationDto);
- WebhookDto dto2 = webhookDbTester.insertWebhook(organizationDto);
- userSession.logIn().addPermission(ADMINISTER, organizationDto.getUuid());
+ assertThat(elements.get(0)).extracting(Webhooks.ListResponseElement::getKey).containsExactly(webhook1.getUuid());
+ assertThat(elements.get(0)).extracting(Webhooks.ListResponseElement::getName).containsExactly("aaa");
+ assertThat(elements.get(0).hasLatestDelivery()).isFalse();
- ListWsResponse response = wsActionTester.newRequest()
- .setParam(ORGANIZATION_KEY_PARAM, organizationDto.getKey())
- .executeProtobuf(ListWsResponse.class);
+ assertThat(elements.get(1)).extracting(Webhooks.ListResponseElement::getKey).containsExactly(webhook2.getUuid());
+ assertThat(elements.get(1)).extracting(Webhooks.ListResponseElement::getName).containsExactly("bbb");
+ assertThat(elements.get(1).hasLatestDelivery()).isFalse();
+ }
- assertThat(response.getWebhooksList())
- .extracting(List::getName, List::getUrl)
- .contains(tuple(dto1.getName(), dto1.getUrl()),
- tuple(dto2.getName(), dto2.getUrl()));
+ @Test
+ public void list_global_webhooks() {
- }
+ WebhookDto dto1 = webhookDbTester.insertWebhook(db.getDefaultOrganization());
+ WebhookDto dto2 = webhookDbTester.insertWebhook(db.getDefaultOrganization());
+ userSession.logIn().addPermission(ADMINISTER, db.getDefaultOrganization().getUuid());
- @Test
- public void List_project_webhooks_when_organization_is_provided() {
+ ListResponse response = wsActionTester.newRequest()
+ .executeProtobuf(ListResponse.class);
- OrganizationDto organization = organizationDbTester.insert();
- ComponentDto project = componentDbTester.insertPrivateProject(organization);
- userSession.logIn().addProjectPermission(ADMIN, project);
+ assertThat(response.getWebhooksList())
+ .extracting(Webhooks.ListResponseElement::getName, Webhooks.ListResponseElement::getUrl)
+ .contains(tuple(dto1.getName(), dto1.getUrl()),
+ tuple(dto2.getName(), dto2.getUrl()));
- WebhookDto dto1 = webhookDbTester.insertWebhook(project);
- WebhookDto dto2 = webhookDbTester.insertWebhook(project);
+ }
- ListWsResponse response = wsActionTester.newRequest()
- .setParam(ORGANIZATION_KEY_PARAM, organization.getKey())
- .setParam(PROJECT_KEY_PARAM, project.getKey())
- .executeProtobuf(ListWsResponse.class);
+ @Test
+ public void list_project_webhooks_when_no_organization_is_provided() {
- assertThat(response.getWebhooksList())
- .extracting(List::getName, List::getUrl)
- .contains(tuple(dto1.getName(), dto1.getUrl()),
- tuple(dto2.getName(), dto2.getUrl()));
+ ComponentDto project1 = componentDbTester.insertPrivateProject();
+ userSession.logIn().addProjectPermission(ADMIN, project1);
- }
+ WebhookDto dto1 = webhookDbTester.insertWebhook(project1);
+ WebhookDto dto2 = webhookDbTester.insertWebhook(project1);
- @Test
- public void return_NotFoundException_if_requested_project_is_not_found() throws Exception {
+ ListResponse response = wsActionTester.newRequest()
+ .setParam(PROJECT_KEY_PARAM, project1.getKey())
+ .executeProtobuf(ListResponse.class);
- userSession.logIn().setSystemAdministrator();
- expectedException.expect(NotFoundException.class);
+ assertThat(response.getWebhooksList())
+ .extracting(Webhooks.ListResponseElement::getName, Webhooks.ListResponseElement::getUrl)
+ .contains(tuple(dto1.getName(), dto1.getUrl()),
+ tuple(dto2.getName(), dto2.getUrl()));
- wsActionTester.newRequest()
- .setParam(PROJECT_KEY_PARAM, "pipo")
- .executeProtobuf(ListWsResponse.class);
+ }
- }
+ @Test
+ public void list_organization_webhooks() {
- @Test
- public void return_NotFoundException_if_requested_organization_is_not_found() throws Exception {
+ OrganizationDto organizationDto = organizationDbTester.insert();
+ WebhookDto dto1 = webhookDbTester.insertWebhook(organizationDto);
+ WebhookDto dto2 = webhookDbTester.insertWebhook(organizationDto);
+ userSession.logIn().addPermission(ADMINISTER, organizationDto.getUuid());
- userSession.logIn().setSystemAdministrator();
- expectedException.expect(NotFoundException.class);
+ ListResponse response = wsActionTester.newRequest()
+ .setParam(ORGANIZATION_KEY_PARAM, organizationDto.getKey())
+ .executeProtobuf(ListResponse.class);
- wsActionTester.newRequest()
- .setParam(ORGANIZATION_KEY_PARAM, "pipo")
- .executeProtobuf(ListWsResponse.class);
+ assertThat(response.getWebhooksList())
+ .extracting(Webhooks.ListResponseElement::getName, Webhooks.ListResponseElement::getUrl)
+ .contains(tuple(dto1.getName(), dto1.getUrl()),
+ tuple(dto2.getName(), dto2.getUrl()));
- }
+ }
- @Test
- public void fail_if_project_exists_but_does_not_belong_to_requested_organization() {
+ @Test
+ public void list_project_webhooks_when_organization_is_provided() {
- OrganizationDto organization = organizationDbTester.insert();
- ComponentDto project = componentDbTester.insertPrivateProject();
+ OrganizationDto organization = organizationDbTester.insert();
+ ComponentDto project = componentDbTester.insertPrivateProject(organization);
+ userSession.logIn().addProjectPermission(ADMIN, project);
- expectedException.expect(NotFoundException.class);
- expectedException.expectMessage(format("Project '%s' does not belong to organisation '%s'", project.getKey(), organization.getKey()));
+ WebhookDto dto1 = webhookDbTester.insertWebhook(project);
+ WebhookDto dto2 = webhookDbTester.insertWebhook(project);
- userSession.logIn().addProjectPermission(ADMIN, project);
+ ListResponse response = wsActionTester.newRequest()
+ .setParam(ORGANIZATION_KEY_PARAM, organization.getKey())
+ .setParam(PROJECT_KEY_PARAM, project.getKey())
+ .executeProtobuf(ListResponse.class);
- wsActionTester.newRequest()
- .setParam(ORGANIZATION_KEY_PARAM, organization.getKey())
- .setParam(PROJECT_KEY_PARAM, project.getKey())
- .execute();
+ assertThat(response.getWebhooksList())
+ .extracting(Webhooks.ListResponseElement::getName, Webhooks.ListResponseElement::getUrl)
+ .contains(tuple(dto1.getName(), dto1.getUrl()),
+ tuple(dto2.getName(), dto2.getUrl()));
- }
+ }
- @Test
- public void return_UnauthorizedException_if_not_logged_in() throws Exception {
+ @Test
+ public void return_NotFoundException_if_requested_project_is_not_found() throws Exception {
- userSession.anonymous();
- expectedException.expect(UnauthorizedException.class);
+ userSession.logIn().setSystemAdministrator();
+ expectedException.expect(NotFoundException.class);
- wsActionTester.newRequest()
- .executeProtobuf(ListWsResponse.class);
+ wsActionTester.newRequest()
+ .setParam(PROJECT_KEY_PARAM, "pipo")
+ .executeProtobuf(ListResponse.class);
- }
+ }
- @Test
- public void throw_ForbiddenException_if_not_organization_administrator() {
+ @Test
+ public void return_NotFoundException_if_requested_organization_is_not_found() throws Exception {
- userSession.logIn();
+ userSession.logIn().setSystemAdministrator();
+ expectedException.expect(NotFoundException.class);
- expectedException.expect(ForbiddenException.class);
- expectedException.expectMessage("Insufficient privileges");
+ wsActionTester.newRequest()
+ .setParam(ORGANIZATION_KEY_PARAM, "pipo")
+ .executeProtobuf(ListResponse.class);
- wsActionTester.newRequest()
- .executeProtobuf(ListWsResponse.class);
- }
+ }
- @Test
- public void throw_ForbiddenException_if_not_project_administrator() {
+ @Test
+ public void fail_if_project_exists_but_does_not_belong_to_requested_organization() {
- ComponentDto project = componentDbTester.insertPrivateProject();
+ OrganizationDto organization = organizationDbTester.insert();
+ ComponentDto project = componentDbTester.insertPrivateProject();
- userSession.logIn();
+ expectedException.expect(NotFoundException.class);
+ expectedException.expectMessage(format("Project '%s' does not belong to organisation '%s'", project.getKey(), organization.getKey()));
- expectedException.expect(ForbiddenException.class);
- expectedException.expectMessage("Insufficient privileges");
+ userSession.logIn().addProjectPermission(ADMIN, project);
- wsActionTester.newRequest()
- .setParam(PROJECT_KEY_PARAM, project.getKey())
- .executeProtobuf(ListWsResponse.class);
+ wsActionTester.newRequest()
+ .setParam(ORGANIZATION_KEY_PARAM, organization.getKey())
+ .setParam(PROJECT_KEY_PARAM, project.getKey())
+ .execute();
- }
+ }
+
+ @Test
+ public void return_UnauthorizedException_if_not_logged_in() throws Exception {
+
+ userSession.anonymous();
+ expectedException.expect(UnauthorizedException.class);
+
+ wsActionTester.newRequest()
+ .executeProtobuf(ListResponse.class);
+
+ }
+
+ @Test
+ public void throw_ForbiddenException_if_not_organization_administrator() {
+
+ userSession.logIn();
+
+ expectedException.expect(ForbiddenException.class);
+ expectedException.expectMessage("Insufficient privileges");
+
+ wsActionTester.newRequest()
+ .executeProtobuf(ListResponse.class);
+ }
+
+ @Test
+ public void throw_ForbiddenException_if_not_project_administrator() {
+
+ ComponentDto project = componentDbTester.insertPrivateProject();
+
+ userSession.logIn();
+
+ expectedException.expect(ForbiddenException.class);
+ expectedException.expectMessage("Insufficient privileges");
+
+ wsActionTester.newRequest()
+ .setParam(PROJECT_KEY_PARAM, project.getKey())
+ .executeProtobuf(ListResponse.class);
+
+ }
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/webhook/ws/WebhookDeliveriesActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/webhook/ws/WebhookDeliveriesActionTest.java
index 6d3d3327dff..4637386f5be 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/webhook/ws/WebhookDeliveriesActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/webhook/ws/WebhookDeliveriesActionTest.java
@@ -29,7 +29,7 @@ import org.sonar.api.web.UserRole;
import org.sonar.db.DbClient;
import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto;
-import org.sonar.db.component.ComponentTesting;
+import org.sonar.db.webhook.WebhookDeliveryDbTester;
import org.sonar.db.webhook.WebhookDeliveryDto;
import org.sonar.server.component.ComponentFinder;
import org.sonar.server.component.TestComponentFinder;
@@ -40,7 +40,8 @@ import org.sonar.server.ws.WsActionTester;
import org.sonarqube.ws.Webhooks;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.sonar.db.webhook.WebhookDbTesting.newWebhookDeliveryDto;
+import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
+import static org.sonar.db.webhook.WebhookDbTesting.newDto;
import static org.sonar.test.JsonAssert.assertJson;
public class WebhookDeliveriesActionTest {
@@ -55,6 +56,8 @@ public class WebhookDeliveriesActionTest {
public DbTester db = DbTester.create(System2.INSTANCE);
private DbClient dbClient = db.getDbClient();
+ private WebhookDeliveryDbTester webhookDeliveryDbTester = db.webhookDelivery();
+
private WsActionTester ws;
private ComponentDto project;
@@ -63,7 +66,7 @@ public class WebhookDeliveriesActionTest {
ComponentFinder componentFinder = TestComponentFinder.from(db);
WebhookDeliveriesAction underTest = new WebhookDeliveriesAction(dbClient, userSession, componentFinder);
ws = new WsActionTester(underTest);
- project = db.components().insertComponent(ComponentTesting.newPrivateProjectDto(db.organizations().insert()).setDbKey("my-project"));
+ project = db.components().insertComponent(newPrivateProjectDto(db.organizations().insert()).setDbKey("my-project"));
}
@Test
@@ -116,7 +119,7 @@ public class WebhookDeliveriesActionTest {
@Test
public void search_by_component_and_return_records_of_example() {
- WebhookDeliveryDto dto = newWebhookDeliveryDto()
+ WebhookDeliveryDto dto = newDto()
.setUuid("d1")
.setComponentUuid(project.uuid())
.setCeTaskUuid("task-1")
@@ -140,9 +143,9 @@ public class WebhookDeliveriesActionTest {
@Test
public void search_by_task_and_return_records() {
- WebhookDeliveryDto dto1 = newWebhookDeliveryDto().setComponentUuid(project.uuid()).setCeTaskUuid("t1");
- WebhookDeliveryDto dto2 = newWebhookDeliveryDto().setComponentUuid(project.uuid()).setCeTaskUuid("t1");
- WebhookDeliveryDto dto3 = newWebhookDeliveryDto().setComponentUuid(project.uuid()).setCeTaskUuid("t2");
+ WebhookDeliveryDto dto1 = newDto().setComponentUuid(project.uuid()).setCeTaskUuid("t1");
+ WebhookDeliveryDto dto2 = newDto().setComponentUuid(project.uuid()).setCeTaskUuid("t1");
+ WebhookDeliveryDto dto3 = newDto().setComponentUuid(project.uuid()).setCeTaskUuid("t2");
dbClient.webhookDeliveryDao().insert(db.getSession(), dto1);
dbClient.webhookDeliveryDao().insert(db.getSession(), dto2);
dbClient.webhookDeliveryDao().insert(db.getSession(), dto3);
@@ -158,9 +161,9 @@ public class WebhookDeliveriesActionTest {
@Test
public void search_by_webhook_and_return_records() {
- WebhookDeliveryDto dto1 = newWebhookDeliveryDto().setComponentUuid(project.uuid()).setCeTaskUuid("t1").setWebhookUuid("wh-1-uuid");
- WebhookDeliveryDto dto2 = newWebhookDeliveryDto().setComponentUuid(project.uuid()).setCeTaskUuid("t1").setWebhookUuid("wh-1-uuid");
- WebhookDeliveryDto dto3 = newWebhookDeliveryDto().setComponentUuid(project.uuid()).setCeTaskUuid("t2").setWebhookUuid("wh-2-uuid");
+ WebhookDeliveryDto dto1 = newDto().setComponentUuid(project.uuid()).setCeTaskUuid("t1").setWebhookUuid("wh-1-uuid");
+ WebhookDeliveryDto dto2 = newDto().setComponentUuid(project.uuid()).setCeTaskUuid("t1").setWebhookUuid("wh-1-uuid");
+ WebhookDeliveryDto dto3 = newDto().setComponentUuid(project.uuid()).setCeTaskUuid("t2").setWebhookUuid("wh-2-uuid");
dbClient.webhookDeliveryDao().insert(db.getSession(), dto1);
dbClient.webhookDeliveryDao().insert(db.getSession(), dto2);
dbClient.webhookDeliveryDao().insert(db.getSession(), dto3);
@@ -168,15 +171,72 @@ public class WebhookDeliveriesActionTest {
userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
Webhooks.DeliveriesWsResponse response = ws.newRequest()
- .setParam("ceTaskId", "t1")
+ .setParam("webhook", "wh-1-uuid")
.executeProtobuf(Webhooks.DeliveriesWsResponse.class);
assertThat(response.getDeliveriesCount()).isEqualTo(2);
assertThat(response.getDeliveriesList()).extracting(Webhooks.Delivery::getId).containsOnly(dto1.getUuid(), dto2.getUuid());
}
@Test
+ public void validate_default_pagination() {
+
+ for (int i = 0; i < 15; i++) {
+ webhookDeliveryDbTester.insert(newDto().setComponentUuid(project.uuid()).setCeTaskUuid("t1").setWebhookUuid("wh-1-uuid"));
+ }
+
+ userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
+
+ Webhooks.DeliveriesWsResponse response = ws.newRequest()
+ .setParam("webhook", "wh-1-uuid")
+ .executeProtobuf(Webhooks.DeliveriesWsResponse.class);
+
+ assertThat(response.getDeliveriesCount()).isEqualTo(10);
+
+ }
+
+ @Test
+ public void validate_pagination_first_page() {
+
+ for (int i = 0; i < 12; i++) {
+ webhookDeliveryDbTester.insert(newDto().setComponentUuid(project.uuid()).setCeTaskUuid("t1").setWebhookUuid("wh-1-uuid"));
+ }
+
+ userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
+
+ Webhooks.DeliveriesWsResponse response = ws.newRequest()
+ .setParam("webhook", "wh-1-uuid")
+ .setParam("p", "1")
+ .setParam("ps", "10")
+ .executeProtobuf(Webhooks.DeliveriesWsResponse.class);
+
+ assertThat(response.getDeliveriesCount()).isEqualTo(10);
+ assertThat(response.getPaging().getTotal()).isEqualTo(12);
+ assertThat(response.getPaging().getPageIndex()).isEqualTo(1);
+ }
+
+ @Test
+ public void validate_pagination_last_page() {
+
+ for (int i = 0; i < 12; i++) {
+ webhookDeliveryDbTester.insert(newDto().setComponentUuid(project.uuid()).setCeTaskUuid("t1").setWebhookUuid("wh-1-uuid"));
+ }
+
+ userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
+
+ Webhooks.DeliveriesWsResponse response = ws.newRequest()
+ .setParam("webhook", "wh-1-uuid")
+ .setParam("p", "2")
+ .setParam("ps", "10")
+ .executeProtobuf(Webhooks.DeliveriesWsResponse.class);
+
+ assertThat(response.getDeliveriesCount()).isEqualTo(2);
+ assertThat(response.getPaging().getTotal()).isEqualTo(12);
+ assertThat(response.getPaging().getPageIndex()).isEqualTo(2);
+ }
+
+ @Test
public void search_by_component_and_throw_ForbiddenException_if_not_admin_of_project() {
- WebhookDeliveryDto dto = newWebhookDeliveryDto()
+ WebhookDeliveryDto dto = newDto()
.setComponentUuid(project.uuid());
dbClient.webhookDeliveryDao().insert(db.getSession(), dto);
db.commit();
@@ -192,7 +252,7 @@ public class WebhookDeliveriesActionTest {
@Test
public void search_by_task_and_throw_ForbiddenException_if_not_admin_of_project() {
- WebhookDeliveryDto dto = newWebhookDeliveryDto()
+ WebhookDeliveryDto dto = newDto()
.setComponentUuid(project.uuid());
dbClient.webhookDeliveryDao().insert(db.getSession(), dto);
db.commit();
diff --git a/server/sonar-server/src/test/java/org/sonar/server/webhook/ws/WebhookDeliveryActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/webhook/ws/WebhookDeliveryActionTest.java
index dbc6db0af65..1b54fc6ae2b 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/webhook/ws/WebhookDeliveryActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/webhook/ws/WebhookDeliveryActionTest.java
@@ -42,7 +42,7 @@ import org.sonarqube.ws.MediaTypes;
import org.sonarqube.ws.Webhooks;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.sonar.db.webhook.WebhookDbTesting.newWebhookDeliveryDto;
+import static org.sonar.db.webhook.WebhookDbTesting.newDto;
import static org.sonar.test.JsonAssert.assertJson;
public class WebhookDeliveryActionTest {
@@ -100,7 +100,7 @@ public class WebhookDeliveryActionTest {
@Test
public void load_the_delivery_of_example() {
- WebhookDeliveryDto dto = newWebhookDeliveryDto()
+ WebhookDeliveryDto dto = newDto()
.setUuid("d1")
.setComponentUuid(project.uuid())
.setCeTaskUuid("task-1")
@@ -125,7 +125,7 @@ public class WebhookDeliveryActionTest {
@Test
public void return_delivery_that_failed_to_be_sent() {
- WebhookDeliveryDto dto = newWebhookDeliveryDto()
+ WebhookDeliveryDto dto = newDto()
.setComponentUuid(project.uuid())
.setSuccess(false)
.setHttpStatus(null)
@@ -147,7 +147,7 @@ public class WebhookDeliveryActionTest {
@Test
public void return_delivery_with_none_of_optional_fields() {
- WebhookDeliveryDto dto = newWebhookDeliveryDto()
+ WebhookDeliveryDto dto = newDto()
.setComponentUuid(project.uuid())
.setCeTaskUuid(null)
.setHttpStatus(null)
@@ -171,7 +171,7 @@ public class WebhookDeliveryActionTest {
@Test
public void throw_ForbiddenException_if_not_admin_of_project() {
- WebhookDeliveryDto dto = newWebhookDeliveryDto()
+ WebhookDeliveryDto dto = newDto()
.setComponentUuid(project.uuid());
dbClient.webhookDeliveryDao().insert(db.getSession(), dto);
db.commit();
diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
index f8b475cabd6..282bd2bf3cc 100644
--- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties
+++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
@@ -2824,7 +2824,7 @@ webhooks.delivery.duration_x=Duration: {0}
webhooks.delivery.payload=Payload:
webhooks.delivery.response_x=Response: {0}
webhooks.documentation_link=Webhooks documentation
-webhooks.last_execution=Last execution
+webhooks.last_execution=Last delivery
webhooks.last_execution.none=Never
webhooks.maximum_reached=You reached your maximum number of {0} webhooks. You can still update or delete an existing one.
webhooks.name=Name
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/WebService.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/WebService.java
index cdd5d5b25f4..90d3e237a54 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/WebService.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/ws/WebService.java
@@ -244,6 +244,7 @@ public interface WebService extends Definable<WebService.Context> {
class NewAction {
private final String key;
+ private static final String PAGE_PARAM_DESCRIPTION = "1-based page number";
private String deprecatedKey;
private String description;
private String since;
@@ -353,7 +354,7 @@ public interface WebService extends Definable<WebService.Context> {
*/
public NewAction addPagingParams(int defaultPageSize) {
createParam(Param.PAGE)
- .setDescription("1-based page number")
+ .setDescription(PAGE_PARAM_DESCRIPTION)
.setExampleValue("42")
.setDeprecatedKey("pageIndex", "5.2")
.setDefaultValue("1");
@@ -378,7 +379,7 @@ public interface WebService extends Definable<WebService.Context> {
public NewParam createPageParam() {
return createParam(Param.PAGE)
- .setDescription("1-based page number")
+ .setDescription(PAGE_PARAM_DESCRIPTION)
.setExampleValue("42")
.setDeprecatedKey("pageIndex", "5.2")
.setDefaultValue("1");
@@ -395,11 +396,11 @@ public interface WebService extends Definable<WebService.Context> {
/**
* Add predefined parameters related to pagination of results with a maximum page size.
- * Note the maximum is a documentation only feature. It does not check anything.
+ * @since 7.1
*/
public NewAction addPagingParamsSince(int defaultPageSize, int maxPageSize, String version) {
createParam(Param.PAGE)
- .setDescription("1-based page number")
+ .setDescription(PAGE_PARAM_DESCRIPTION)
.setExampleValue("42")
.setDefaultValue("1")
.setSince(version);
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/webhooks/DeliveriesRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/webhooks/DeliveriesRequest.java
index 4b001cf6ddf..871bc1b2abb 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/webhooks/DeliveriesRequest.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/webhooks/DeliveriesRequest.java
@@ -19,7 +19,6 @@
*/
package org.sonarqube.ws.client.webhooks;
-import java.util.List;
import javax.annotation.Generated;
/**
@@ -33,6 +32,9 @@ public class DeliveriesRequest {
private String ceTaskId;
private String componentKey;
+ private String p;
+ private String ps;
+ private String webhook;
/**
* Example value: "AU-Tpxb--iU5OvuD2FLy"
@@ -57,4 +59,40 @@ public class DeliveriesRequest {
public String getComponentKey() {
return componentKey;
}
+
+ /**
+ * Example value: "42"
+ */
+ public DeliveriesRequest setP(String p) {
+ this.p = p;
+ return this;
+ }
+
+ public String getP() {
+ return p;
+ }
+
+ /**
+ * Example value: "20"
+ */
+ public DeliveriesRequest setPs(String ps) {
+ this.ps = ps;
+ return this;
+ }
+
+ public String getPs() {
+ return ps;
+ }
+
+ /**
+ * Example value: "AU-TpxcA-iU5OvuD2FLz"
+ */
+ public DeliveriesRequest 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 7eb3b34a329..92bae24c990 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
@@ -19,17 +19,16 @@
*/
package org.sonarqube.ws.client.webhooks;
-import java.util.stream.Collectors;
import javax.annotation.Generated;
import org.sonarqube.ws.MediaTypes;
+import org.sonarqube.ws.Webhooks.CreateWsResponse;
+import org.sonarqube.ws.Webhooks.DeliveriesWsResponse;
+import org.sonarqube.ws.Webhooks.DeliveryWsResponse;
+import org.sonarqube.ws.Webhooks.ListResponse;
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 <a href="https://next.sonarqube.com/sonarqube/web_api/api/webhooks">Further information about this web service online</a>
@@ -84,7 +83,10 @@ public class WebhooksService extends BaseService {
return call(
new GetRequest(path("deliveries"))
.setParam("ceTaskId", request.getCeTaskId())
- .setParam("componentKey", request.getComponentKey()),
+ .setParam("componentKey", request.getComponentKey())
+ .setParam("p", request.getP())
+ .setParam("ps", request.getPs())
+ .setParam("webhook", request.getWebhook()),
DeliveriesWsResponse.parser());
}
@@ -109,12 +111,12 @@ public class WebhooksService extends BaseService {
* @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/webhooks/list">Further information about this action online (including a response example)</a>
* @since 7.1
*/
- public ListWsResponse list(ListRequest request) {
+ public ListResponse list(ListRequest request) {
return call(
new GetRequest(path("list"))
.setParam("organization", request.getOrganization())
.setParam("project", request.getProject()),
- ListWsResponse.parser());
+ ListResponse.parser());
}
/**
diff --git a/sonar-ws/src/main/protobuf/ws-webhooks.proto b/sonar-ws/src/main/protobuf/ws-webhooks.proto
index ea616f84955..81e45aa24fd 100644
--- a/sonar-ws/src/main/protobuf/ws-webhooks.proto
+++ b/sonar-ws/src/main/protobuf/ws-webhooks.proto
@@ -26,24 +26,24 @@ option java_package = "org.sonarqube.ws";
option java_outer_classname = "Webhooks";
option optimize_for = SPEED;
-// GET api/webhooks/list
-message ListWsResponse {
- repeated List webhooks = 1;
+message LatestDelivery {
+ optional string id = 1;
+ optional string at = 2;
+ optional bool success = 3;
+ optional int32 httpStatus = 4;
+ optional int32 durationMs = 5;
+}
- message List {
- optional string key = 1;
- optional string name = 2;
- optional string url = 3;
- optional LatestDelivery latestDelivery = 4;
+message ListResponseElement {
+ optional string key = 1;
+ optional string name = 2;
+ optional string url = 3;
+ optional LatestDelivery latestDelivery = 4;
+}
- message LatestDelivery {
- optional string id = 1;
- optional string at = 2;
- optional string success = 3;
- optional string httpStatus = 4;
- optional string durationMs = 5;
- }
- }
+// GET api/webhooks/list
+message ListResponse {
+ repeated ListResponseElement webhooks = 1;
}
// POST api/webhooks/create
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 27d83fc1f1f..1bf641ae2ec 100644
--- a/tests/src/test/java/org/sonarqube/tests/webhook/WebhooksTest.java
+++ b/tests/src/test/java/org/sonarqube/tests/webhook/WebhooksTest.java
@@ -151,7 +151,7 @@ public class WebhooksTest {
assertThat(detail.getSuccess()).isFalse();
assertThat(detail.hasHttpStatus()).isFalse();
- assertThat(detail.hasDurationMs()).isFalse();
+ assertThat(detail.hasDurationMs()).isTrue();
assertThat(detail.getPayload()).isNotEmpty();
assertThat(detail.getErrorStacktrace())
.contains("java.net.UnknownHostException")