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

package org.sonar.db.webhook; package org.sonar.db.webhook;


import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
import org.apache.ibatis.session.RowBounds; import org.apache.ibatis.session.RowBounds;
import org.sonar.db.Dao; import org.sonar.db.Dao;
import org.sonar.db.DbSession; 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 class WebhookDeliveryDao implements Dao {


public Optional<WebhookDeliveryDto> selectByUuid(DbSession dbSession, String uuid) { public Optional<WebhookDeliveryDto> selectByUuid(DbSession dbSession, String uuid) {
mapper(dbSession).deleteComponentBeforeDate(componentUuid, beforeDate); 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) { private static WebhookDeliveryMapper mapper(DbSession dbSession) {
return dbSession.getMapper(WebhookDeliveryMapper.class); return dbSession.getMapper(WebhookDeliveryMapper.class);
} }

+ 7
- 0
server/sonar-db-dao/src/test/java/org/sonar/db/DbTester.java View File

import org.sonar.db.user.RootFlagAssertions; import org.sonar.db.user.RootFlagAssertions;
import org.sonar.db.user.UserDbTester; import org.sonar.db.user.UserDbTester;
import org.sonar.db.webhook.WebhookDbTester; import org.sonar.db.webhook.WebhookDbTester;
import org.sonar.db.webhook.WebhookDeliveryDbTester;


import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Preconditions.checkState;
import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric; import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
private final FileSourceTester fileSourceTester; private final FileSourceTester fileSourceTester;
private final PluginDbTester pluginDbTester; private final PluginDbTester pluginDbTester;
private final WebhookDbTester webhookDbTester; private final WebhookDbTester webhookDbTester;
private final WebhookDeliveryDbTester webhookDeliveryDbTester;


public DbTester(System2 system2, @Nullable String schemaPath) { public DbTester(System2 system2, @Nullable String schemaPath) {
super(TestDb.create(schemaPath)); super(TestDb.create(schemaPath));
this.fileSourceTester = new FileSourceTester(this); this.fileSourceTester = new FileSourceTester(this);
this.pluginDbTester = new PluginDbTester(this); this.pluginDbTester = new PluginDbTester(this);
this.webhookDbTester = new WebhookDbTester(this); this.webhookDbTester = new WebhookDbTester(this);
this.webhookDeliveryDbTester = new WebhookDeliveryDbTester(this);
} }


public static DbTester create() { public static DbTester create() {
return webhookDbTester; return webhookDbTester;
} }


public WebhookDeliveryDbTester webhookDelivery() {
return webhookDeliveryDbTester;
}

@Override @Override
protected void after() { protected void after() {
if (session != null) { if (session != null) {

+ 3
- 3
server/sonar-db-dao/src/test/java/org/sonar/db/purge/PurgeDaoTest.java View File

import static org.sonar.db.component.ComponentTesting.newFileDto; import static org.sonar.db.component.ComponentTesting.newFileDto;
import static org.sonar.db.component.ComponentTesting.newModuleDto; import static org.sonar.db.component.ComponentTesting.newModuleDto;
import static org.sonar.db.component.ComponentTesting.newProjectCopy; 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; import static org.sonar.db.webhook.WebhookDbTesting.selectAllDeliveryUuids;


public class PurgeDaoTest { public class PurgeDaoTest {
@Test @Test
public void deleteProject_deletes_webhook_deliveries() { public void deleteProject_deletes_webhook_deliveries() {
ComponentDto project = dbTester.components().insertPublicProject(); 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()); underTest.deleteProject(dbSession, project.uuid());



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

// only statics // 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() return new WebhookDeliveryDto()
.setUuid(randomAlphanumeric(40)) .setUuid(randomAlphanumeric(40))
.setComponentUuid(randomAlphanumeric(40)) .setComponentUuid(randomAlphanumeric(40))

+ 45
- 41
server/sonar-db-dao/src/test/java/org/sonar/db/webhook/WebhookDeliveryDaoTest.java View File

import org.sonar.db.DbSession; import org.sonar.db.DbSession;
import org.sonar.db.DbTester; import org.sonar.db.DbTester;


import static com.google.common.collect.ImmutableList.of;
import static org.assertj.core.api.Assertions.assertThat; 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 { public class WebhookDeliveryDaoTest {




@Test @Test
public void selectOrderedByComponentUuid_returns_empty_if_no_records() { 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); List<WebhookDeliveryLiteDto> deliveries = underTest.selectOrderedByComponentUuid(dbSession, "ANOTHER_COMPONENT", 0, 10);




@Test @Test
public void selectOrderedByComponentUuid_returns_records_ordered_by_date() { 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, dto3);
underTest.insert(dbSession, dto2); underTest.insert(dbSession, dto2);
underTest.insert(dbSession, dto1); underTest.insert(dbSession, dto1);


@Test @Test
public void selectOrderedByCeTaskUuid_returns_empty_if_no_records() { 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); List<WebhookDeliveryLiteDto> deliveries = underTest.selectOrderedByCeTaskUuid(dbSession, "ANOTHER_TASK", 0, 10);




@Test @Test
public void selectOrderedByCeTaskUuid_returns_records_ordered_by_date() { 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, dto3);
underTest.insert(dbSession, dto2); underTest.insert(dbSession, dto2);
underTest.insert(dbSession, dto1); underTest.insert(dbSession, dto1);
@Test @Test
public void selectByWebhookUuid_returns_empty_if_no_records() { 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); List<WebhookDeliveryLiteDto> deliveries = underTest.selectByWebhookUuid(dbSession, "a-webhook-uuid", 0, 10);


@Test @Test
public void selectByWebhookUuid_returns_records_ordered_by_date() { public void selectByWebhookUuid_returns_records_ordered_by_date() {
WebhookDto webhookDto = dbWebhooks.insert(WebhookTesting.newProjectWebhook("COMPONENT_1")); 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, dto3);
underTest.insert(dbSession, dto2); underTest.insert(dbSession, dto2);
underTest.insert(dbSession, dto1); underTest.insert(dbSession, dto1);
@Test @Test
public void selectByWebhookUuid_returns_records_according_to_pagination() { public void selectByWebhookUuid_returns_records_according_to_pagination() {
WebhookDto webhookDto = dbWebhooks.insert(WebhookTesting.newProjectWebhook("COMPONENT_1")); 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 @Test
public void insert_row_with_only_mandatory_columns() { 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) .setHttpStatus(null)
.setDurationMs(null) .setDurationMs(null)
.setErrorStacktrace(null); .setErrorStacktrace(null);


@Test @Test
public void insert_row_with_all_columns() { 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); underTest.insert(dbSession, dto);




@Test @Test
public void deleteComponentBeforeDate_deletes_rows_before_date() { 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 // should delete the old delivery on COMPONENT_1 and keep the one of COMPONENT_2
underTest.deleteComponentBeforeDate(dbSession, "COMPONENT_1", 1_500_000L); underTest.deleteComponentBeforeDate(dbSession, "COMPONENT_1", 1_500_000L);


@Test @Test
public void deleteComponentBeforeDate_does_nothing_on_invalid_uuid() { 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); underTest.deleteComponentBeforeDate(dbSession, "COMPONENT_2", 1_500_000L);


assertThat(actual.getCreatedAt()).isEqualTo(expected.getCreatedAt()); 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) { private WebhookDeliveryDto selectByUuid(String uuid) {
Optional<WebhookDeliveryDto> dto = underTest.selectByUuid(dbSession, uuid); Optional<WebhookDeliveryDto> dto = underTest.selectByUuid(dbSession, uuid);
assertThat(dto).isPresent(); assertThat(dto).isPresent();

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

/*
* 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

*/ */
package org.sonar.db.webhook; package org.sonar.db.webhook;


import java.util.Calendar;
import org.sonar.db.component.ComponentDto; import org.sonar.db.component.ComponentDto;
import org.sonar.db.organization.OrganizationDto; import org.sonar.db.organization.OrganizationDto;


import java.util.Calendar;

import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric; import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;


public class WebhookTesting { public class WebhookTesting {
.setOrganizationUuid(organizationDto.getUuid()); .setOrganizationUuid(organizationDto.getUuid());
} }


public static WebhookDto newOrganizationWebhook(String name, String organizationUuid) {
return getWebhookDto()
.setName(name)
.setOrganizationUuid(organizationUuid);
}

private static WebhookDto getWebhookDto() { private static WebhookDto getWebhookDto() {
return new WebhookDto() return new WebhookDto()
.setUuid(randomAlphanumeric(40)) .setUuid(randomAlphanumeric(40))

+ 5
- 1
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/MigrateWebhooksToWebhooksTable.java View File

import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Function; import java.util.function.Function;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers; import org.sonar.api.utils.log.Loggers;
import org.sonar.server.platform.db.migration.step.DataChange; import org.sonar.server.platform.db.migration.step.DataChange;
import org.sonar.server.platform.db.migration.version.v63.DefaultOrganizationUuidProvider; 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.toList;
import static java.util.stream.Collectors.toMap; import static java.util.stream.Collectors.toMap;


for (String value : values) { for (String value : values) {
PropertyRow name = properties.get("sonar.webhooks.project." + value + ".name"); PropertyRow name = properties.get("sonar.webhooks.project." + value + ".name");
PropertyRow url = properties.get("sonar.webhooks.project." + value + ".url"); 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; return webhooks;
} }


@CheckForNull
private static String projectUuidOf(Context context, PropertyRow row) throws SQLException { private static String projectUuidOf(Context context, PropertyRow row) throws SQLException {
return context return context
.prepareSelect("select uuid from projects where id = ?") .prepareSelect("select uuid from projects where id = ?")

+ 4
- 2
server/sonar-server/src/main/java/org/sonar/server/webhook/WebhookCallerImpl.java View File

Request request = buildHttpRequest(webhook, payload); Request request = buildHttpRequest(webhook, payload);
try (Response response = execute(request)) { try (Response response = execute(request)) {
builder.setHttpStatus(response.code()); builder.setHttpStatus(response.code());
builder.setDurationInMs((int) (system.now() - startedAt));
} }
} catch (Exception e) { } catch (Exception e) {
builder.setError(e); builder.setError(e);
} }
return builder.build();

return builder
.setDurationInMs((int) (system.now() - startedAt))
.build();
} }


private static Request buildHttpRequest(Webhook webhook, WebhookPayload payload) { 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



import com.google.common.io.Resources; import com.google.common.io.Resources;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.Request;
import org.sonar.db.DbSession; import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto; import org.sonar.db.component.ComponentDto;
import org.sonar.db.organization.OrganizationDto; import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.webhook.WebhookDeliveryLiteDto;
import org.sonar.db.webhook.WebhookDto; import org.sonar.db.webhook.WebhookDto;
import org.sonar.server.organization.DefaultOrganizationProvider; import org.sonar.server.organization.DefaultOrganizationProvider;
import org.sonar.server.user.UserSession; 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 java.util.Optional.ofNullable;
import static org.apache.commons.lang.StringUtils.isNotBlank; 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.LIST_ACTION;
import static org.sonar.server.webhook.ws.WebhooksWsParameters.ORGANIZATION_KEY_PARAM; import static org.sonar.server.webhook.ws.WebhooksWsParameters.ORGANIZATION_KEY_PARAM;
import static org.sonar.server.webhook.ws.WebhooksWsParameters.PROJECT_KEY_PARAM; import static org.sonar.server.webhook.ws.WebhooksWsParameters.PROJECT_KEY_PARAM;
import static org.sonar.server.ws.WsUtils.checkFoundWithOptional; import static org.sonar.server.ws.WsUtils.checkFoundWithOptional;
import static org.sonar.server.ws.WsUtils.checkStateWithOptional; import static org.sonar.server.ws.WsUtils.checkStateWithOptional;
import static org.sonar.server.ws.WsUtils.writeProtobuf; import static org.sonar.server.ws.WsUtils.writeProtobuf;
import static org.sonarqube.ws.Webhooks.ListWsResponse.newBuilder;


public class ListAction implements WebhooksWsAction { public class ListAction implements WebhooksWsAction {




userSession.checkLoggedIn(); 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 webhookDtos
.stream() .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); 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) { private OrganizationDto defaultOrganizationDto(DbSession dbSession) {
String uuid = defaultOrganizationProvider.get().getUuid(); String uuid = defaultOrganizationProvider.get().getUuid();
Optional<OrganizationDto> organizationDto = dbClient.organizationDao().selectByUuid(dbSession, uuid); 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

import static org.apache.commons.lang.StringUtils.isNotBlank; 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;
import static org.sonar.api.server.ws.WebService.Param.PAGE_SIZE; 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.core.util.Uuids.UUID_EXAMPLE_02;
import static org.sonar.server.es.SearchOptions.MAX_LIMIT; import static org.sonar.server.es.SearchOptions.MAX_LIMIT;
import static org.sonar.server.webhook.ws.WebhookWsSupport.copyDtoToProtobuf; import static org.sonar.server.webhook.ws.WebhookWsSupport.copyDtoToProtobuf;
int totalElements; int totalElements;
try (DbSession dbSession = dbClient.openSession(false)) { try (DbSession dbSession = dbClient.openSession(false)) {
if (isNotBlank(webhookUuid)) { if (isNotBlank(webhookUuid)) {
deliveries = dbClient.webhookDeliveryDao().selectByWebhookUuid(dbSession, webhookUuid, page - 1, pageSize);
component = getComponentDto(dbSession, deliveries);
totalElements = dbClient.webhookDeliveryDao().countDeliveriesByWebhookUuid(dbSession, webhookUuid); totalElements = dbClient.webhookDeliveryDao().countDeliveriesByWebhookUuid(dbSession, webhookUuid);
deliveries = dbClient.webhookDeliveryDao().selectByWebhookUuid(dbSession, webhookUuid, offset(page, pageSize), pageSize);
component = getComponentDto(dbSession, deliveries);
} else if (componentKey != null) { } else if (componentKey != null) {
component = componentFinder.getByKey(dbSession, componentKey); component = componentFinder.getByKey(dbSession, componentKey);
deliveries = dbClient.webhookDeliveryDao().selectOrderedByComponentUuid(dbSession, component.uuid(), page - 1, pageSize);
totalElements = dbClient.webhookDeliveryDao().countDeliveriesByComponentUuid(dbSession, component.uuid()); totalElements = dbClient.webhookDeliveryDao().countDeliveriesByComponentUuid(dbSession, component.uuid());
deliveries = dbClient.webhookDeliveryDao().selectOrderedByComponentUuid(dbSession, component.uuid(), offset(page, pageSize), pageSize);
} else { } else {
deliveries = dbClient.webhookDeliveryDao().selectOrderedByCeTaskUuid(dbSession, ceTaskId, page - 1, pageSize);
component = getComponentDto(dbSession, deliveries);
totalElements = dbClient.webhookDeliveryDao().countDeliveriesByCeTaskUuid(dbSession, ceTaskId); 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); 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

{ {
"paging": {
"pageIndex": 1,
"pageSize": 10,
"total": 1
},
"deliveries": [ "deliveries": [
{ {
"id": "d1", "id": "d1",
"durationMs": 10 "durationMs": 10
} }
] ]
}
}

+ 13
- 19
server/sonar-server/src/main/resources/org/sonar/server/webhook/ws/example-delivery.json View File

{ {
"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

WebhookDelivery delivery = newSender().call(webhook, PAYLOAD); WebhookDelivery delivery = newSender().call(webhook, PAYLOAD);


assertThat(delivery.getHttpStatus()).isEmpty(); assertThat(delivery.getHttpStatus()).isEmpty();
assertThat(delivery.getDurationInMs()).isEmpty();
assertThat(delivery.getDurationInMs().get()).isGreaterThanOrEqualTo(0);
// message can be "Connection refused" or "connect timed out" // message can be "Connection refused" or "connect timed out"
assertThat(delivery.getErrorMessage().get()).matches("(.*Connection refused.*)|(.*connect timed out.*)"); assertThat(delivery.getErrorMessage().get()).matches("(.*Connection refused.*)|(.*connect timed out.*)");
assertThat(delivery.getAt()).isEqualTo(NOW); assertThat(delivery.getAt()).isEqualTo(NOW);
WebhookDelivery delivery = newSender().call(webhook, PAYLOAD); WebhookDelivery delivery = newSender().call(webhook, PAYLOAD);


assertThat(delivery.getHttpStatus()).isEmpty(); assertThat(delivery.getHttpStatus()).isEmpty();
assertThat(delivery.getDurationInMs()).isEmpty();
assertThat(delivery.getDurationInMs().get()).isGreaterThanOrEqualTo(0);
assertThat(delivery.getError().get()).isInstanceOf(IllegalArgumentException.class); assertThat(delivery.getError().get()).isInstanceOf(IllegalArgumentException.class);
assertThat(delivery.getErrorMessage().get()).isEqualTo("Webhook URL is not valid: this_is_not_an_url"); assertThat(delivery.getErrorMessage().get()).isEqualTo("Webhook URL is not valid: this_is_not_an_url");
assertThat(delivery.getAt()).isEqualTo(NOW); assertThat(delivery.getAt()).isEqualTo(NOW);

+ 3
- 2
server/sonar-server/src/test/java/org/sonar/server/webhook/WebhookDeliveryStorageTest.java View File

import org.sonar.db.DbClient; import org.sonar.db.DbClient;
import org.sonar.db.DbSession; import org.sonar.db.DbSession;
import org.sonar.db.DbTester; import org.sonar.db.DbTester;
import org.sonar.db.webhook.WebhookDbTesting;
import org.sonar.db.webhook.WebhookDeliveryDto; import org.sonar.db.webhook.WebhookDeliveryDto;


import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; 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; import static org.sonar.db.webhook.WebhookDbTesting.selectAllDeliveryUuids;


public class WebhookDeliveryStorageTest { public class WebhookDeliveryStorageTest {
} }


private static WebhookDeliveryDto newDto(String uuid, String componentUuid, long at) { private static WebhookDeliveryDto newDto(String uuid, String componentUuid, long at) {
return newWebhookDeliveryDto()
return WebhookDbTesting.newDto()
.setUuid(uuid) .setUuid(uuid)
.setComponentUuid(componentUuid) .setComponentUuid(componentUuid)
.setCreatedAt(at); .setCreatedAt(at);

+ 195
- 138
server/sonar-server/src/test/java/org/sonar/server/webhook/ws/ListActionTest.java View File

*/ */
package org.sonar.server.webhook.ws; package org.sonar.server.webhook.ws;


import java.util.List;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.ExpectedException; import org.junit.rules.ExpectedException;
import org.sonar.db.organization.OrganizationDbTester; import org.sonar.db.organization.OrganizationDbTester;
import org.sonar.db.organization.OrganizationDto; import org.sonar.db.organization.OrganizationDto;
import org.sonar.db.webhook.WebhookDbTester; import org.sonar.db.webhook.WebhookDbTester;
import org.sonar.db.webhook.WebhookDeliveryDbTester;
import org.sonar.db.webhook.WebhookDto; import org.sonar.db.webhook.WebhookDto;
import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.organization.DefaultOrganizationProvider; import org.sonar.server.organization.DefaultOrganizationProvider;
import org.sonar.server.tester.UserSessionRule; import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.ws.WsActionTester; 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 java.lang.String.format;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.sonar.api.web.UserRole.ADMIN; import static org.sonar.api.web.UserRole.ADMIN;
import static org.sonar.db.DbTester.create; import static org.sonar.db.DbTester.create;
import static org.sonar.db.permission.OrganizationPermission.ADMINISTER; 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.organization.TestDefaultOrganizationProvider.from;
import static org.sonar.server.tester.UserSessionRule.standalone; import static org.sonar.server.tester.UserSessionRule.standalone;
import static org.sonar.server.webhook.ws.WebhooksWsParameters.ORGANIZATION_KEY_PARAM; import static org.sonar.server.webhook.ws.WebhooksWsParameters.ORGANIZATION_KEY_PARAM;


public class ListActionTest { 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

import org.sonar.db.DbClient; import org.sonar.db.DbClient;
import org.sonar.db.DbTester; import org.sonar.db.DbTester;
import org.sonar.db.component.ComponentDto; 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.db.webhook.WebhookDeliveryDto;
import org.sonar.server.component.ComponentFinder; import org.sonar.server.component.ComponentFinder;
import org.sonar.server.component.TestComponentFinder; import org.sonar.server.component.TestComponentFinder;
import org.sonarqube.ws.Webhooks; import org.sonarqube.ws.Webhooks;


import static org.assertj.core.api.Assertions.assertThat; 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; import static org.sonar.test.JsonAssert.assertJson;


public class WebhookDeliveriesActionTest { public class WebhookDeliveriesActionTest {
public DbTester db = DbTester.create(System2.INSTANCE); public DbTester db = DbTester.create(System2.INSTANCE);


private DbClient dbClient = db.getDbClient(); private DbClient dbClient = db.getDbClient();
private WebhookDeliveryDbTester webhookDeliveryDbTester = db.webhookDelivery();

private WsActionTester ws; private WsActionTester ws;
private ComponentDto project; private ComponentDto project;


ComponentFinder componentFinder = TestComponentFinder.from(db); ComponentFinder componentFinder = TestComponentFinder.from(db);
WebhookDeliveriesAction underTest = new WebhookDeliveriesAction(dbClient, userSession, componentFinder); WebhookDeliveriesAction underTest = new WebhookDeliveriesAction(dbClient, userSession, componentFinder);
ws = new WsActionTester(underTest); 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 @Test


@Test @Test
public void search_by_component_and_return_records_of_example() { public void search_by_component_and_return_records_of_example() {
WebhookDeliveryDto dto = newWebhookDeliveryDto()
WebhookDeliveryDto dto = newDto()
.setUuid("d1") .setUuid("d1")
.setComponentUuid(project.uuid()) .setComponentUuid(project.uuid())
.setCeTaskUuid("task-1") .setCeTaskUuid("task-1")


@Test @Test
public void search_by_task_and_return_records() { 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(), dto1);
dbClient.webhookDeliveryDao().insert(db.getSession(), dto2); dbClient.webhookDeliveryDao().insert(db.getSession(), dto2);
dbClient.webhookDeliveryDao().insert(db.getSession(), dto3); dbClient.webhookDeliveryDao().insert(db.getSession(), dto3);


@Test @Test
public void search_by_webhook_and_return_records() { 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(), dto1);
dbClient.webhookDeliveryDao().insert(db.getSession(), dto2); dbClient.webhookDeliveryDao().insert(db.getSession(), dto2);
dbClient.webhookDeliveryDao().insert(db.getSession(), dto3); dbClient.webhookDeliveryDao().insert(db.getSession(), dto3);
userSession.logIn().addProjectPermission(UserRole.ADMIN, project); userSession.logIn().addProjectPermission(UserRole.ADMIN, project);


Webhooks.DeliveriesWsResponse response = ws.newRequest() Webhooks.DeliveriesWsResponse response = ws.newRequest()
.setParam("ceTaskId", "t1")
.setParam("webhook", "wh-1-uuid")
.executeProtobuf(Webhooks.DeliveriesWsResponse.class); .executeProtobuf(Webhooks.DeliveriesWsResponse.class);
assertThat(response.getDeliveriesCount()).isEqualTo(2); assertThat(response.getDeliveriesCount()).isEqualTo(2);
assertThat(response.getDeliveriesList()).extracting(Webhooks.Delivery::getId).containsOnly(dto1.getUuid(), dto2.getUuid()); 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 @Test
public void search_by_component_and_throw_ForbiddenException_if_not_admin_of_project() { public void search_by_component_and_throw_ForbiddenException_if_not_admin_of_project() {
WebhookDeliveryDto dto = newWebhookDeliveryDto()
WebhookDeliveryDto dto = newDto()
.setComponentUuid(project.uuid()); .setComponentUuid(project.uuid());
dbClient.webhookDeliveryDao().insert(db.getSession(), dto); dbClient.webhookDeliveryDao().insert(db.getSession(), dto);
db.commit(); db.commit();


@Test @Test
public void search_by_task_and_throw_ForbiddenException_if_not_admin_of_project() { public void search_by_task_and_throw_ForbiddenException_if_not_admin_of_project() {
WebhookDeliveryDto dto = newWebhookDeliveryDto()
WebhookDeliveryDto dto = newDto()
.setComponentUuid(project.uuid()); .setComponentUuid(project.uuid());
dbClient.webhookDeliveryDao().insert(db.getSession(), dto); dbClient.webhookDeliveryDao().insert(db.getSession(), dto);
db.commit(); db.commit();

+ 5
- 5
server/sonar-server/src/test/java/org/sonar/server/webhook/ws/WebhookDeliveryActionTest.java View File

import org.sonarqube.ws.Webhooks; import org.sonarqube.ws.Webhooks;


import static org.assertj.core.api.Assertions.assertThat; 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; import static org.sonar.test.JsonAssert.assertJson;


public class WebhookDeliveryActionTest { public class WebhookDeliveryActionTest {


@Test @Test
public void load_the_delivery_of_example() { public void load_the_delivery_of_example() {
WebhookDeliveryDto dto = newWebhookDeliveryDto()
WebhookDeliveryDto dto = newDto()
.setUuid("d1") .setUuid("d1")
.setComponentUuid(project.uuid()) .setComponentUuid(project.uuid())
.setCeTaskUuid("task-1") .setCeTaskUuid("task-1")


@Test @Test
public void return_delivery_that_failed_to_be_sent() { public void return_delivery_that_failed_to_be_sent() {
WebhookDeliveryDto dto = newWebhookDeliveryDto()
WebhookDeliveryDto dto = newDto()
.setComponentUuid(project.uuid()) .setComponentUuid(project.uuid())
.setSuccess(false) .setSuccess(false)
.setHttpStatus(null) .setHttpStatus(null)


@Test @Test
public void return_delivery_with_none_of_optional_fields() { public void return_delivery_with_none_of_optional_fields() {
WebhookDeliveryDto dto = newWebhookDeliveryDto()
WebhookDeliveryDto dto = newDto()
.setComponentUuid(project.uuid()) .setComponentUuid(project.uuid())
.setCeTaskUuid(null) .setCeTaskUuid(null)
.setHttpStatus(null) .setHttpStatus(null)


@Test @Test
public void throw_ForbiddenException_if_not_admin_of_project() { public void throw_ForbiddenException_if_not_admin_of_project() {
WebhookDeliveryDto dto = newWebhookDeliveryDto()
WebhookDeliveryDto dto = newDto()
.setComponentUuid(project.uuid()); .setComponentUuid(project.uuid());
dbClient.webhookDeliveryDao().insert(db.getSession(), dto); dbClient.webhookDeliveryDao().insert(db.getSession(), dto);
db.commit(); db.commit();

+ 1
- 1
sonar-core/src/main/resources/org/sonar/l10n/core.properties View File

webhooks.delivery.payload=Payload: webhooks.delivery.payload=Payload:
webhooks.delivery.response_x=Response: {0} webhooks.delivery.response_x=Response: {0}
webhooks.documentation_link=Webhooks documentation webhooks.documentation_link=Webhooks documentation
webhooks.last_execution=Last execution
webhooks.last_execution=Last delivery
webhooks.last_execution.none=Never 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.maximum_reached=You reached your maximum number of {0} webhooks. You can still update or delete an existing one.
webhooks.name=Name webhooks.name=Name

+ 5
- 4
sonar-plugin-api/src/main/java/org/sonar/api/server/ws/WebService.java View File



class NewAction { class NewAction {
private final String key; private final String key;
private static final String PAGE_PARAM_DESCRIPTION = "1-based page number";
private String deprecatedKey; private String deprecatedKey;
private String description; private String description;
private String since; private String since;
*/ */
public NewAction addPagingParams(int defaultPageSize) { public NewAction addPagingParams(int defaultPageSize) {
createParam(Param.PAGE) createParam(Param.PAGE)
.setDescription("1-based page number")
.setDescription(PAGE_PARAM_DESCRIPTION)
.setExampleValue("42") .setExampleValue("42")
.setDeprecatedKey("pageIndex", "5.2") .setDeprecatedKey("pageIndex", "5.2")
.setDefaultValue("1"); .setDefaultValue("1");


public NewParam createPageParam() { public NewParam createPageParam() {
return createParam(Param.PAGE) return createParam(Param.PAGE)
.setDescription("1-based page number")
.setDescription(PAGE_PARAM_DESCRIPTION)
.setExampleValue("42") .setExampleValue("42")
.setDeprecatedKey("pageIndex", "5.2") .setDeprecatedKey("pageIndex", "5.2")
.setDefaultValue("1"); .setDefaultValue("1");


/** /**
* Add predefined parameters related to pagination of results with a maximum page size. * 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) { public NewAction addPagingParamsSince(int defaultPageSize, int maxPageSize, String version) {
createParam(Param.PAGE) createParam(Param.PAGE)
.setDescription("1-based page number")
.setDescription(PAGE_PARAM_DESCRIPTION)
.setExampleValue("42") .setExampleValue("42")
.setDefaultValue("1") .setDefaultValue("1")
.setSince(version); .setSince(version);

+ 39
- 1
sonar-ws/src/main/java/org/sonarqube/ws/client/webhooks/DeliveriesRequest.java View File

*/ */
package org.sonarqube.ws.client.webhooks; package org.sonarqube.ws.client.webhooks;


import java.util.List;
import javax.annotation.Generated; import javax.annotation.Generated;


/** /**


private String ceTaskId; private String ceTaskId;
private String componentKey; private String componentKey;
private String p;
private String ps;
private String webhook;


/** /**
* Example value: "AU-Tpxb--iU5OvuD2FLy" * Example value: "AU-Tpxb--iU5OvuD2FLy"
public String getComponentKey() { public String getComponentKey() {
return componentKey; 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

*/ */
package org.sonarqube.ws.client.webhooks; package org.sonarqube.ws.client.webhooks;


import java.util.stream.Collectors;
import javax.annotation.Generated; import javax.annotation.Generated;
import org.sonarqube.ws.MediaTypes; 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.BaseService;
import org.sonarqube.ws.client.GetRequest; import org.sonarqube.ws.client.GetRequest;
import org.sonarqube.ws.client.PostRequest; import org.sonarqube.ws.client.PostRequest;
import org.sonarqube.ws.client.WsConnector; 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> * @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/webhooks">Further information about this web service online</a>
return call( return call(
new GetRequest(path("deliveries")) new GetRequest(path("deliveries"))
.setParam("ceTaskId", request.getCeTaskId()) .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()); DeliveriesWsResponse.parser());
} }


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


/** /**

+ 16
- 16
sonar-ws/src/main/protobuf/ws-webhooks.proto View File

option java_outer_classname = "Webhooks"; option java_outer_classname = "Webhooks";
option optimize_for = SPEED; 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 // POST api/webhooks/create

+ 1
- 1
tests/src/test/java/org/sonarqube/tests/webhook/WebhooksTest.java View File



assertThat(detail.getSuccess()).isFalse(); assertThat(detail.getSuccess()).isFalse();
assertThat(detail.hasHttpStatus()).isFalse(); assertThat(detail.hasHttpStatus()).isFalse();
assertThat(detail.hasDurationMs()).isFalse();
assertThat(detail.hasDurationMs()).isTrue();
assertThat(detail.getPayload()).isNotEmpty(); assertThat(detail.getPayload()).isNotEmpty();
assertThat(detail.getErrorStacktrace()) assertThat(detail.getErrorStacktrace())
.contains("java.net.UnknownHostException") .contains("java.net.UnknownHostException")

Loading…
Cancel
Save