From: Guillaume Jambet Date: Thu, 15 Feb 2018 16:14:59 +0000 (+0100) Subject: SONAR-10346 Add latest deliveries information to webhooks search ws. X-Git-Tag: 7.5~1601 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=74745908c3dd0c19cdde5e5a44fb9bb43c451d9b;p=sonarqube.git SONAR-10346 Add latest deliveries information to webhooks search ws. --- 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 selectByUuid(DbSession dbSession, String uuid) { @@ -72,6 +76,12 @@ public class WebhookDeliveryDao implements Dao { mapper(dbSession).deleteComponentBeforeDate(componentUuid, beforeDate); } + public Map selectLatestDeliveries(DbSession dbSession, List 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 { 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 { 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 { 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 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 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 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 deliveries = underTest.selectByWebhookUuid(dbSession, webhookDto.getUuid(), 1, 3); + List 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 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 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 webhookDtos = doHandle(dbSession, organizationKey, projectKey); + Map lastDeliveries = loadLastDeliveriesOf(dbSession, webhookDtos); + writeResponse(request, response, webhookDtos, lastDeliveries); + } } - private List doHandle(@Nullable String organizationKey, @Nullable String projectKey) { - - try (DbSession dbSession = dbClient.openSession(true)) { + private Map loadLastDeliveriesOf(DbSession dbSession, List webhookDtos) { + return dbClient.webhookDeliveryDao().selectLatestDeliveries(dbSession, webhookDtos); + } - OrganizationDto organizationDto; - if (isNotBlank(organizationKey)) { - Optional dtoOptional = dbClient.organizationDao().selectByKey(dbSession, organizationKey); - organizationDto = checkFoundWithOptional(dtoOptional, "No organization with key '%s'", organizationKey); - } else { - organizationDto = defaultOrganizationDto(dbSession); - } + private List doHandle(DbSession dbSession, @Nullable String organizationKey, @Nullable String projectKey) { - if (isNotBlank(projectKey)) { + OrganizationDto organizationDto; + if (isNotBlank(organizationKey)) { + Optional dtoOptional = dbClient.organizationDao().selectByKey(dbSession, organizationKey); + organizationDto = checkFoundWithOptional(dtoOptional, "No organization with key '%s'", organizationKey); + } else { + organizationDto = defaultOrganizationDto(dbSession); + } - Optional 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 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 webhookDtos) { - Builder responseBuilder = newBuilder(); + } + private static void writeResponse(Request request, Response response, List webhookDtos, Map 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 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 = 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 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 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 { 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 { */ 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 { 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 { /** * 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 Further information about this web service online @@ -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 Further information about this action online (including a response example) * @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")