]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-10346 Add latest deliveries information to webhooks search ws.
authorGuillaume Jambet <guillaume.jambet@sonarsource.com>
Thu, 15 Feb 2018 16:14:59 +0000 (17:14 +0100)
committerGuillaume Jambet <guillaume.jambet@gmail.com>
Thu, 1 Mar 2018 14:21:05 +0000 (15:21 +0100)
24 files changed:
server/sonar-db-dao/src/main/java/org/sonar/db/webhook/WebhookDeliveryDao.java
server/sonar-db-dao/src/test/java/org/sonar/db/DbTester.java
server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java
server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookDbTesting.java
server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookDeliveryDaoTest.java
server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookDeliveryDbTester.java [new file with mode: 0644]
server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookTesting.java
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/MigrateWebhooksToWebhooksTable.java
server/sonar-server/src/main/java/org/sonar/server/webhook/WebhookCallerImpl.java
server/sonar-server/src/main/java/org/sonar/server/webhook/ws/ListAction.java
server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhookDeliveriesAction.java
server/sonar-server/src/main/resources/org/sonar/server/webhook/ws/example-deliveries.json
server/sonar-server/src/main/resources/org/sonar/server/webhook/ws/example-delivery.json
server/sonar-server/src/test/java/org/sonar/server/webhook/WebhookCallerImplTest.java
server/sonar-server/src/test/java/org/sonar/server/webhook/WebhookDeliveryStorageTest.java
server/sonar-server/src/test/java/org/sonar/server/webhook/ws/ListActionTest.java
server/sonar-server/src/test/java/org/sonar/server/webhook/ws/WebhookDeliveriesActionTest.java
server/sonar-server/src/test/java/org/sonar/server/webhook/ws/WebhookDeliveryActionTest.java
sonar-core/src/main/resources/org/sonar/l10n/core.properties
sonar-plugin-api/src/main/java/org/sonar/api/server/ws/WebService.java
sonar-ws/src/main/java/org/sonarqube/ws/client/webhooks/DeliveriesRequest.java
sonar-ws/src/main/java/org/sonarqube/ws/client/webhooks/WebhooksService.java
sonar-ws/src/main/protobuf/ws-webhooks.proto
tests/src/test/java/org/sonarqube/tests/webhook/WebhooksTest.java

index e8e63d856af7d845b0f4a6aeb715754b92b5886a..4d42c7c8b890eb73da72b71def6d18332fa4ec0b 100644 (file)
 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);
   }
index 19b4b44a15e68c7c3d23322e1ee297d8bdcb86c5..2081a83f13669a0216368a8c26730ae9bcadeb44 100644 (file)
@@ -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) {
index 93b97c137721d096e236efe49f728f70894b50d7..62bffd56a9598f1bac42b300811b961b2bb55ae5 100644 (file)
@@ -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());
 
index 88ed7eacf15111ad4b74481506f7152a77ce27b5..561737484676c8b6fea1757671973ab293959775 100644 (file)
@@ -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))
index 5b5470358766c6f9f33428c6f48b2b392d7bc1ba..4dc3c7c9d89d52906da35fad8a2fe1176478c9e1 100644 (file)
@@ -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 (file)
index 0000000..d31d270
--- /dev/null
@@ -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;
+  }
+
+}
index 4568a13786995be74b08f0a2c555910ed4140d17..58ba1e46606504823b9fb909eeab7fa36a57a447 100644 (file)
  */
 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))
index 5e133925daa184d58679c6730eb3fe37e031cca2..fb782d5f236e84a7a7fc467186ad67812d329cba 100644 (file)
@@ -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 = ?")
index f5d231fc98bcc58cf64db63a7709a07fb311c217..e9e70ca09c13a190bade52f41ca46ee8971b981d 100644 (file)
@@ -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) {
index d90f1f0490ce541a2cfd0eaebcaa08fffce6401f..cc8f70ab27cbf14e2a672ab0db08553b46de4abf 100644 (file)
@@ -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);
index ee5e469f57ab792cc45a7e80e29bc142f8438adc..0ae3dd4a7778830a4fa80abc62384221a737b7e7 100644 (file)
@@ -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);
index 129d9dbfc218c313416268bb78da2ddf4905ee5b..d95c99d3ba356243fab40b9f99f9b128d681ec1e 100644 (file)
@@ -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
index d95c99d3ba356243fab40b9f99f9b128d681ec1e..d4da8033df5b1b9a505f19b7ac0d7199f669794c 100644 (file)
@@ -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\"}"
+  }
+}
index 1e174e6ba5415366f834e5d15f7f0867789ccac8..60ac24ee262d25d679e8bf466b570e7a6519e95e 100644 (file)
@@ -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);
index 158fdd730023ffcb67ad9e039180013080071c62..1fac932cbc9ba996fd90c14599dd24c266713960 100644 (file)
@@ -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);
index 995eb3b106bf0609540da179a338d2de06e84066..0a6505d2f143d35a66e9f33a59b89212850c49d2 100644 (file)
@@ -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);
+
+    }
 
 }
index 6d3d3327dff1ecd434396e6dff552c709bedcaa2..4637386f5be766f2b3007554caec1bdd7f8458cd 100644 (file)
@@ -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();
index dbc6db0af65c0ed9174432a91d63a5d507e3f36a..1b54fc6ae2b004aced918d582a59fd24f7e890f1 100644 (file)
@@ -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();
index f8b475cabd683fde23b0a1d6cfcdc1a763e64ef9..282bd2bf3cc1db1bfd8e4bd0cddc14d6fd730cab 100644 (file)
@@ -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
index cdd5d5b25f4ecf0b341dbad087181731ab9ff81e..90d3e237a548d01239df67699f861651502c136e 100644 (file)
@@ -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);
index 4b001cf6ddfcdf0f507cdeb04ec7b41de57c9c55..871bc1b2abb2fc8812d08f406fe3b33c9bc82504 100644 (file)
@@ -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;
+  }
 }
index 7eb3b34a329350e20ae838c742b22c85c7491ec6..92bae24c990c5d6dae575f88602637e4c8f4385b 100644 (file)
  */
 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());
   }
 
   /**
index ea616f84955ba5ea8bf16f0fe9bd805832be10e2..81e45aa24fd40e7e7022ade181f4da77634c12a4 100644 (file)
@@ -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
index 27d83fc1f1fd38b36822814dd563c3427b306c60..1bf641ae2ecdb209a32175e4efcd656c54ae661c 100644 (file)
@@ -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")