Browse Source

SONAR-10346 Add latest deliveries information to webhooks search ws.

tags/7.5
Guillaume Jambet 6 years ago
parent
commit
74745908c3
24 changed files with 570 additions and 299 deletions
  1. 10
    0
      server/sonar-db-dao/src/main/java/org/sonar/db/webhook/WebhookDeliveryDao.java
  2. 7
    0
      server/sonar-db-dao/src/test/java/org/sonar/db/DbTester.java
  3. 3
    3
      server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java
  4. 13
    1
      server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookDbTesting.java
  5. 45
    41
      server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookDeliveryDaoTest.java
  6. 40
    0
      server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookDeliveryDbTester.java
  7. 8
    1
      server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookTesting.java
  8. 5
    1
      server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/MigrateWebhooksToWebhooksTable.java
  9. 4
    2
      server/sonar-server/src/main/java/org/sonar/server/webhook/WebhookCallerImpl.java
  10. 60
    34
      server/sonar-server/src/main/java/org/sonar/server/webhook/ws/ListAction.java
  11. 6
    5
      server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhookDeliveriesAction.java
  12. 6
    1
      server/sonar-server/src/main/resources/org/sonar/server/webhook/ws/example-deliveries.json
  13. 13
    19
      server/sonar-server/src/main/resources/org/sonar/server/webhook/ws/example-delivery.json
  14. 2
    2
      server/sonar-server/src/test/java/org/sonar/server/webhook/WebhookCallerImplTest.java
  15. 3
    2
      server/sonar-server/src/test/java/org/sonar/server/webhook/WebhookDeliveryStorageTest.java
  16. 195
    138
      server/sonar-server/src/test/java/org/sonar/server/webhook/ws/ListActionTest.java
  17. 73
    13
      server/sonar-server/src/test/java/org/sonar/server/webhook/ws/WebhookDeliveriesActionTest.java
  18. 5
    5
      server/sonar-server/src/test/java/org/sonar/server/webhook/ws/WebhookDeliveryActionTest.java
  19. 1
    1
      sonar-core/src/main/resources/org/sonar/l10n/core.properties
  20. 5
    4
      sonar-plugin-api/src/main/java/org/sonar/api/server/ws/WebService.java
  21. 39
    1
      sonar-ws/src/main/java/org/sonarqube/ws/client/webhooks/DeliveriesRequest.java
  22. 10
    8
      sonar-ws/src/main/java/org/sonarqube/ws/client/webhooks/WebhooksService.java
  23. 16
    16
      sonar-ws/src/main/protobuf/ws-webhooks.proto
  24. 1
    1
      tests/src/test/java/org/sonarqube/tests/webhook/WebhooksTest.java

+ 10
- 0
server/sonar-db-dao/src/main/java/org/sonar/db/webhook/WebhookDeliveryDao.java View File

@@ -20,11 +20,15 @@
package org.sonar.db.webhook;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.apache.ibatis.session.RowBounds;
import org.sonar.db.Dao;
import org.sonar.db.DbSession;

import static java.util.function.Function.identity;
import static java.util.stream.Collectors.toMap;

public class WebhookDeliveryDao implements Dao {

public Optional<WebhookDeliveryDto> selectByUuid(DbSession dbSession, String uuid) {
@@ -72,6 +76,12 @@ public class WebhookDeliveryDao implements Dao {
mapper(dbSession).deleteComponentBeforeDate(componentUuid, beforeDate);
}

public Map<String, WebhookDeliveryLiteDto> selectLatestDeliveries(DbSession dbSession, List<WebhookDto> webhooks) {
return webhooks.stream()
.flatMap(webhook -> selectByWebhookUuid(dbSession, webhook.getUuid(),0,1).stream())
.collect(toMap(WebhookDeliveryLiteDto::getWebhookUuid, identity()));
}

private static WebhookDeliveryMapper mapper(DbSession dbSession) {
return dbSession.getMapper(WebhookDeliveryMapper.class);
}

+ 7
- 0
server/sonar-db-dao/src/test/java/org/sonar/db/DbTester.java View 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) {

+ 3
- 3
server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java View 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());


+ 13
- 1
server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookDbTesting.java View 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))

+ 45
- 41
server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookDeliveryDaoTest.java View 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();

+ 40
- 0
server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookDeliveryDbTester.java View File

@@ -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;
}

}

+ 8
- 1
server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookTesting.java View File

@@ -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))

+ 5
- 1
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/MigrateWebhooksToWebhooksTable.java View 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 = ?")

+ 4
- 2
server/sonar-server/src/main/java/org/sonar/server/webhook/WebhookCallerImpl.java View 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) {

+ 60
- 34
server/sonar-server/src/main/java/org/sonar/server/webhook/ws/ListAction.java View 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);

+ 6
- 5
server/sonar-server/src/main/java/org/sonar/server/webhook/ws/WebhookDeliveriesAction.java View 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);

+ 6
- 1
server/sonar-server/src/main/resources/org/sonar/server/webhook/ws/example-deliveries.json View File

@@ -1,4 +1,9 @@
{
"paging": {
"pageIndex": 1,
"pageSize": 10,
"total": 1
},
"deliveries": [
{
"id": "d1",
@@ -12,4 +17,4 @@
"durationMs": 10
}
]
}
}

+ 13
- 19
server/sonar-server/src/main/resources/org/sonar/server/webhook/ws/example-delivery.json View 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
}
]
}
"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\"}"
}
}

+ 2
- 2
server/sonar-server/src/test/java/org/sonar/server/webhook/WebhookCallerImplTest.java View 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);

+ 3
- 2
server/sonar-server/src/test/java/org/sonar/server/webhook/WebhookDeliveryStorageTest.java View 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);

+ 195
- 138
server/sonar-server/src/test/java/org/sonar/server/webhook/ws/ListActionTest.java View 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);

}

}

+ 73
- 13
server/sonar-server/src/test/java/org/sonar/server/webhook/ws/WebhookDeliveriesActionTest.java View 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();

+ 5
- 5
server/sonar-server/src/test/java/org/sonar/server/webhook/ws/WebhookDeliveryActionTest.java View 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();

+ 1
- 1
sonar-core/src/main/resources/org/sonar/l10n/core.properties View 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

+ 5
- 4
sonar-plugin-api/src/main/java/org/sonar/api/server/ws/WebService.java View 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);

+ 39
- 1
sonar-ws/src/main/java/org/sonarqube/ws/client/webhooks/DeliveriesRequest.java View 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;
}
}

+ 10
- 8
sonar-ws/src/main/java/org/sonarqube/ws/client/webhooks/WebhooksService.java View File

@@ -19,17 +19,16 @@
*/
package org.sonarqube.ws.client.webhooks;

import java.util.stream.Collectors;
import javax.annotation.Generated;
import org.sonarqube.ws.MediaTypes;
import org.sonarqube.ws.Webhooks.CreateWsResponse;
import org.sonarqube.ws.Webhooks.DeliveriesWsResponse;
import org.sonarqube.ws.Webhooks.DeliveryWsResponse;
import org.sonarqube.ws.Webhooks.ListResponse;
import org.sonarqube.ws.client.BaseService;
import org.sonarqube.ws.client.GetRequest;
import org.sonarqube.ws.client.PostRequest;
import org.sonarqube.ws.client.WsConnector;
import org.sonarqube.ws.Webhooks.CreateWsResponse;
import org.sonarqube.ws.Webhooks.DeliveriesWsResponse;
import org.sonarqube.ws.Webhooks.DeliveryWsResponse;
import org.sonarqube.ws.Webhooks.ListWsResponse;

/**
* @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/webhooks">Further information about this web service online</a>
@@ -84,7 +83,10 @@ public class WebhooksService extends BaseService {
return call(
new GetRequest(path("deliveries"))
.setParam("ceTaskId", request.getCeTaskId())
.setParam("componentKey", request.getComponentKey()),
.setParam("componentKey", request.getComponentKey())
.setParam("p", request.getP())
.setParam("ps", request.getPs())
.setParam("webhook", request.getWebhook()),
DeliveriesWsResponse.parser());
}

@@ -109,12 +111,12 @@ public class WebhooksService extends BaseService {
* @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/webhooks/list">Further information about this action online (including a response example)</a>
* @since 7.1
*/
public ListWsResponse list(ListRequest request) {
public ListResponse list(ListRequest request) {
return call(
new GetRequest(path("list"))
.setParam("organization", request.getOrganization())
.setParam("project", request.getProject()),
ListWsResponse.parser());
ListResponse.parser());
}

/**

+ 16
- 16
sonar-ws/src/main/protobuf/ws-webhooks.proto View 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

+ 1
- 1
tests/src/test/java/org/sonarqube/tests/webhook/WebhooksTest.java View 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")

Loading…
Cancel
Save