]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-8353 add WebhookDeliveryDao
authorSimon Brandhof <simon.brandhof@sonarsource.com>
Wed, 9 Nov 2016 22:56:12 +0000 (23:56 +0100)
committerSimon Brandhof <simon.brandhof@sonarsource.com>
Mon, 14 Nov 2016 11:18:50 +0000 (12:18 +0100)
12 files changed:
server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java
sonar-db/src/main/java/org/sonar/db/DaoModule.java
sonar-db/src/main/java/org/sonar/db/DbClient.java
sonar-db/src/main/java/org/sonar/db/MyBatis.java
sonar-db/src/main/java/org/sonar/db/webhook/WebhookDeliveryDao.java [new file with mode: 0644]
sonar-db/src/main/java/org/sonar/db/webhook/WebhookDeliveryDto.java [new file with mode: 0644]
sonar-db/src/main/java/org/sonar/db/webhook/WebhookDeliveryLiteDto.java [new file with mode: 0644]
sonar-db/src/main/java/org/sonar/db/webhook/WebhookDeliveryMapper.java [new file with mode: 0644]
sonar-db/src/main/java/org/sonar/db/webhook/package-info.java [new file with mode: 0644]
sonar-db/src/main/resources/org/sonar/db/webhook/WebhookDeliveryMapper.xml [new file with mode: 0644]
sonar-db/src/test/java/org/sonar/db/DaoModuleTest.java
sonar-db/src/test/java/org/sonar/db/webhook/WebhookDeliveryDaoTest.java [new file with mode: 0644]

index b9b59c7dc48235b94b1579a35507e7a88c25ae62..d070de48002c771f0e098f6cb42303cf93f6a602 100644 (file)
@@ -106,7 +106,7 @@ public class ComputeEngineContainerImplTest {
     assertThat(picoContainer.getParent().getParent().getParent().getComponentAdapters()).hasSize(
       COMPONENTS_IN_LEVEL_1_AT_CONSTRUCTION
         + 25 // level 1
-        + 46 // content of DaoModule
+        + 47 // content of DaoModule
         + 2 // content of EsSearchModule
         + 62 // content of CorePropertyDefinitions
         + 1 // content of CePropertyDefinitions
index 76ac3cb5f181a92a06fd5d343b99de165be0cc25..dcd9b568e2d66b4af5e0ae2df359b77b281590ff 100644 (file)
@@ -66,6 +66,7 @@ import org.sonar.db.user.RoleDao;
 import org.sonar.db.user.UserDao;
 import org.sonar.db.user.UserGroupDao;
 import org.sonar.db.user.UserTokenDao;
+import org.sonar.db.webhook.WebhookDeliveryDao;
 
 public class DaoModule extends Module {
   private static final List<Class<? extends Dao>> classes = ImmutableList.<Class<? extends Dao>>builder().add(
@@ -112,7 +113,8 @@ public class DaoModule extends Module {
     UserDao.class,
     UserGroupDao.class,
     UserPermissionDao.class,
-    UserTokenDao.class).build();
+    UserTokenDao.class,
+    WebhookDeliveryDao.class).build();
 
   @Override
   protected void configureModule() {
index 1db3feefa4880ecc7206b2dd116f24d21e196850..02744a29ec16051ad0b7e441368d7bf58451984f 100644 (file)
@@ -66,6 +66,7 @@ import org.sonar.db.user.RoleDao;
 import org.sonar.db.user.UserDao;
 import org.sonar.db.user.UserGroupDao;
 import org.sonar.db.user.UserTokenDao;
+import org.sonar.db.webhook.WebhookDeliveryDao;
 
 public class DbClient {
 
@@ -115,6 +116,7 @@ public class DbClient {
   private final ActiveRuleDao activeRuleDao;
   private final QProfileChangeDao qProfileChangeDao;
   private final UserPermissionDao userPermissionDao;
+  private final WebhookDeliveryDao webhookDeliveryDao;
 
   public DbClient(Database database, MyBatis myBatis, Dao... daos) {
     this.database = database;
@@ -168,6 +170,7 @@ public class DbClient {
     activeRuleDao = getDao(map, ActiveRuleDao.class);
     qProfileChangeDao = getDao(map, QProfileChangeDao.class);
     userPermissionDao = getDao(map, UserPermissionDao.class);
+    webhookDeliveryDao = getDao(map, WebhookDeliveryDao.class);
   }
 
   public DbSession openSession(boolean batch) {
@@ -358,6 +361,10 @@ public class DbClient {
     return userPermissionDao;
   }
 
+  public WebhookDeliveryDao webhookDeliveryDao() {
+    return webhookDeliveryDao;
+  }
+
   protected <K extends Dao> K getDao(Map<Class, Dao> map, Class<K> clazz) {
     return (K) map.get(clazz);
   }
index cc9b6a98df9dbe1e73acbbe73dc78208a534b51b..f25bae7c0b2b7a9c5fc9ad24839d8daddfd06612 100644 (file)
@@ -122,6 +122,7 @@ import org.sonar.db.version.SchemaMigrationMapper;
 import org.sonar.db.version.v45.Migration45Mapper;
 import org.sonar.db.version.v50.Migration50Mapper;
 import org.sonar.db.version.v53.Migration53Mapper;
+import org.sonar.db.webhook.WebhookDeliveryMapper;
 
 public class MyBatis {
 
@@ -248,7 +249,8 @@ public class MyBatis {
       UserGroupMapper.class,
       UserMapper.class,
       UserPermissionMapper.class,
-      UserTokenMapper.class
+      UserTokenMapper.class,
+      WebhookDeliveryMapper.class
     };
     confBuilder.loadMappers(mappers);
 
diff --git a/sonar-db/src/main/java/org/sonar/db/webhook/WebhookDeliveryDao.java b/sonar-db/src/main/java/org/sonar/db/webhook/WebhookDeliveryDao.java
new file mode 100644 (file)
index 0000000..69af157
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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 java.util.Optional;
+import org.sonar.db.Dao;
+import org.sonar.db.DbSession;
+
+public class WebhookDeliveryDao implements Dao {
+
+  public Optional<WebhookDeliveryDto> selectByUuid(DbSession dbSession, String uuid) {
+    return Optional.ofNullable(mapper(dbSession).selectByUuid(uuid));
+  }
+  public void insert(DbSession dbSession, WebhookDeliveryDto dto) {
+    mapper(dbSession).insert(dto);
+  }
+
+  public void deleteComponentBeforeDate(DbSession dbSession, String componentUuid, long beforeDate) {
+    mapper(dbSession).deleteComponentBeforeDate(componentUuid, beforeDate);
+  }
+
+  private static WebhookDeliveryMapper mapper(DbSession dbSession) {
+    return dbSession.getMapper(WebhookDeliveryMapper.class);
+  }
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/webhook/WebhookDeliveryDto.java b/sonar-db/src/main/java/org/sonar/db/webhook/WebhookDeliveryDto.java
new file mode 100644 (file)
index 0000000..75378b2
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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 javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import org.apache.commons.lang.builder.ToStringBuilder;
+
+public class WebhookDeliveryDto extends WebhookDeliveryLiteDto<WebhookDeliveryDto> {
+  /** Error message if HTTP request cannot be sent, else null */
+  private String errorStacktrace;
+  /** The payload that has been sent, cannot be null */
+  private String payload;
+
+  @CheckForNull
+  public String getErrorStacktrace() {
+    return errorStacktrace;
+  }
+
+  public WebhookDeliveryDto setErrorStacktrace(@Nullable String s) {
+    this.errorStacktrace = s;
+    return this;
+  }
+
+  @CheckForNull
+  public String getPayload() {
+    return payload;
+  }
+
+  public WebhookDeliveryDto setPayload(@Nullable String s) {
+    this.payload = s;
+    return this;
+  }
+
+  @Override
+  public String toString() {
+    return new ToStringBuilder(this)
+      .append("uuid", uuid)
+      .append("componentUuid", componentUuid)
+      .append("name", name)
+      .append("success", success)
+      .append("httpStatus", httpStatus)
+      .append("durationMs", durationMs)
+      .append("url", url)
+      .append("createdAt", createdAt)
+      .append("errorStacktrace", errorStacktrace)
+      .toString();
+  }
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/webhook/WebhookDeliveryLiteDto.java b/sonar-db/src/main/java/org/sonar/db/webhook/WebhookDeliveryLiteDto.java
new file mode 100644 (file)
index 0000000..b6b510d
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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 javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import org.apache.commons.lang.builder.ToStringBuilder;
+
+public class WebhookDeliveryLiteDto<T extends WebhookDeliveryLiteDto> {
+  /** Technical unique identifier, can't be null */
+  protected String uuid;
+  /** Component UUID, can't be null */
+  protected String componentUuid;
+  /** Compute Engine task UUID, can't be null */
+  protected String ceTaskUuid;
+  /** Name, can't be null */
+  protected String name;
+  protected boolean success;
+  /** HTTP response status. Null if HTTP request cannot be sent */
+  protected Integer httpStatus;
+  /** Duration in ms. Null if HTTP request cannot be sent */
+  protected Integer durationMs;
+  /** URL, cannot be null */
+  protected String url;
+  /** Time of delivery */
+  protected long createdAt;
+
+  public String getUuid() {
+    return uuid;
+  }
+
+  public T setUuid(String s) {
+    this.uuid = s;
+    return (T)this;
+  }
+
+  public String getComponentUuid() {
+    return componentUuid;
+  }
+
+  public T setComponentUuid(String s) {
+    this.componentUuid = s;
+    return (T)this;
+  }
+
+  public String getCeTaskUuid() {
+    return ceTaskUuid;
+  }
+
+  public T setCeTaskUuid(String s) {
+    this.ceTaskUuid = s;
+    return (T)this;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public T setName(String s) {
+    this.name = s;
+    return (T)this;
+  }
+
+  public boolean isSuccess() {
+    return success;
+  }
+
+  public T setSuccess(boolean b) {
+    this.success = b;
+    return (T)this;
+  }
+
+  @CheckForNull
+  public Integer getHttpStatus() {
+    return httpStatus;
+  }
+
+  public T setHttpStatus(@Nullable Integer i) {
+    this.httpStatus = i;
+    return (T)this;
+  }
+
+  @CheckForNull
+  public Integer getDurationMs() {
+    return durationMs;
+  }
+
+  public T setDurationMs(@Nullable Integer i) {
+    this.durationMs = i;
+    return (T)this;
+  }
+
+  public String getUrl() {
+    return url;
+  }
+
+  public T setUrl(String s) {
+    this.url = s;
+    return (T)this;
+  }
+
+  public long getCreatedAt() {
+    return createdAt;
+  }
+
+  public T setCreatedAt(long l) {
+    this.createdAt = l;
+    return (T)this;
+  }
+
+  @Override
+  public String toString() {
+    return new ToStringBuilder(this)
+      .append("uuid", uuid)
+      .append("componentUuid", componentUuid)
+      .append("ceTaskUuid", ceTaskUuid)
+      .append("name", name)
+      .append("success", success)
+      .append("httpStatus", httpStatus)
+      .append("durationMs", durationMs)
+      .append("url", url)
+      .append("createdAt", createdAt)
+      .toString();
+  }
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/webhook/WebhookDeliveryMapper.java b/sonar-db/src/main/java/org/sonar/db/webhook/WebhookDeliveryMapper.java
new file mode 100644 (file)
index 0000000..0d39cc3
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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 javax.annotation.CheckForNull;
+import org.apache.ibatis.annotations.Param;
+
+public interface WebhookDeliveryMapper {
+
+  @CheckForNull
+  WebhookDeliveryDto selectByUuid(@Param("uuid") String uuid);
+
+  void insert(WebhookDeliveryDto dto);
+
+  void deleteComponentBeforeDate(@Param("componentUuid") String componentUuid, @Param("beforeDate") long beforeDate);
+}
diff --git a/sonar-db/src/main/java/org/sonar/db/webhook/package-info.java b/sonar-db/src/main/java/org/sonar/db/webhook/package-info.java
new file mode 100644 (file)
index 0000000..9e3b24e
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.db.webhook;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+
diff --git a/sonar-db/src/main/resources/org/sonar/db/webhook/WebhookDeliveryMapper.xml b/sonar-db/src/main/resources/org/sonar/db/webhook/WebhookDeliveryMapper.xml
new file mode 100644 (file)
index 0000000..ac90c32
--- /dev/null
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="org.sonar.db.webhook.WebhookDeliveryMapper">
+
+  <select id="selectByUuid" parameterType="String" resultType="org.sonar.db.webhook.WebhookDeliveryDto">
+    select
+    uuid,
+    component_uuid as componentUuid,
+    ce_task_uuid as ceTaskUuid,
+    name,
+    url,
+    success,
+    http_status as httpStatus,
+    duration_ms as durationMs,
+    payload,
+    error_stacktrace as errorStacktrace,
+    created_at as createdAt
+    from webhook_deliveries
+    where uuid = #{uuid,jdbcType=VARCHAR}
+  </select>
+
+  <insert id="insert" parameterType="org.sonar.db.webhook.WebhookDeliveryDto" useGeneratedKeys="false">
+    insert into webhook_deliveries (
+    uuid,
+    component_uuid,
+    ce_task_uuid,
+    name,
+    url,
+    success,
+    http_status,
+    duration_ms,
+    payload,
+    error_stacktrace,
+    created_at
+    ) values (
+    #{uuid,jdbcType=VARCHAR},
+    #{componentUuid,jdbcType=VARCHAR},
+    #{ceTaskUuid,jdbcType=VARCHAR},
+    #{name,jdbcType=VARCHAR},
+    #{url,jdbcType=VARCHAR},
+    #{success,jdbcType=BOOLEAN},
+    #{httpStatus,jdbcType=INTEGER},
+    #{durationMs,jdbcType=INTEGER},
+    #{payload,jdbcType=VARCHAR},
+    #{errorStacktrace,jdbcType=VARCHAR},
+    #{createdAt,jdbcType=TIMESTAMP}
+    )
+  </insert>
+
+  <delete id="deleteComponentBeforeDate" parameterType="map">
+    delete from webhook_deliveries
+    where
+    component_uuid = #{componentUuid,jdbcType=VARCHAR} and
+    created_at &lt; #{beforeDate,jdbcType=BIGINT}
+  </delete>
+</mapper>
index 5ccd64f9158dad21eb58d018b700b40a7dfb21cd..e666e3a2435893d3da3a11b025fca98768bd2249 100644 (file)
@@ -29,6 +29,6 @@ public class DaoModuleTest {
   public void verify_count_of_added_components() {
     ComponentContainer container = new ComponentContainer();
     new DaoModule().configure(container);
-    assertThat(container.size()).isEqualTo(2 + 44);
+    assertThat(container.size()).isEqualTo(2 + 45);
   }
 }
diff --git a/sonar-db/src/test/java/org/sonar/db/webhook/WebhookDeliveryDaoTest.java b/sonar-db/src/test/java/org/sonar/db/webhook/WebhookDeliveryDaoTest.java
new file mode 100644 (file)
index 0000000..96f3654
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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 java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.utils.System2;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+
+public class WebhookDeliveryDaoTest {
+
+  private static final long DATE_1 = 1_999_000L;
+  
+  @Rule
+  public final DbTester dbTester = DbTester.create(System2.INSTANCE).setDisableDefaultOrganization(true);
+
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  private final DbClient dbClient = dbTester.getDbClient();
+  private final DbSession dbSession = dbTester.getSession();
+  private final WebhookDeliveryDao underTest = dbClient.webhookDeliveryDao();
+
+  @Test
+  public void insert_row_with_only_mandatory_columns() {
+    WebhookDeliveryDto dto = newDto("DELIVERY_1", "COMPONENT_1", "TASK_1");
+
+    underTest.insert(dbSession, dto);
+
+    WebhookDeliveryDto stored = selectByUuid(dto.getUuid());
+    verifyMandatoryFields(dto, stored);
+
+    // optional fields are null
+    assertThat(stored.getHttpStatus()).isNull();
+    assertThat(stored.getDurationMs()).isNull();
+    assertThat(stored.getErrorStacktrace()).isNull();
+  }
+
+  @Test
+  public void insert_row_with_all_columns() {
+    WebhookDeliveryDto dto = newDto("DELIVERY_1", "COMPONENT_1", "TASK_1")
+      .setDurationMs(10000)
+      .setHttpStatus(200)
+      .setErrorStacktrace("timeout")
+      .setPayload("{json}");
+
+    underTest.insert(dbSession, dto);
+
+    WebhookDeliveryDto stored = selectByUuid(dto.getUuid());
+    verifyMandatoryFields(dto, stored);
+    assertThat(stored.getHttpStatus()).isEqualTo(dto.getHttpStatus());
+    assertThat(stored.getDurationMs()).isEqualTo(dto.getDurationMs());
+    assertThat(stored.getErrorStacktrace()).isEqualTo(dto.getErrorStacktrace());
+  }
+
+  @Test
+  public void delete_rows_before_date() {
+    underTest.insert(dbSession, newDto("DELIVERY_1", "COMPONENT_1", "TASK_1").setCreatedAt(1_000_000L));
+    underTest.insert(dbSession, newDto("DELIVERY_2", "COMPONENT_1", "TASK_2").setCreatedAt(2_000_000L));
+    underTest.insert(dbSession, newDto("DELIVERY_3", "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);
+
+    List<Object> uuids = dbTester.select(dbSession, "select uuid as \"uuid\" from webhook_deliveries")
+      .stream()
+      .map(columns -> columns.get("uuid"))
+      .collect(Collectors.toList());
+    assertThat(uuids).containsOnly("DELIVERY_2", "DELIVERY_3");
+
+  }
+
+  private void verifyMandatoryFields(WebhookDeliveryDto expected, WebhookDeliveryDto actual) {
+    assertThat(actual.getUuid()).isEqualTo(expected.getUuid());
+    assertThat(actual.getComponentUuid()).isEqualTo(expected.getComponentUuid());
+    assertThat(actual.getCeTaskUuid()).isEqualTo(expected.getCeTaskUuid());
+    assertThat(actual.getName()).isEqualTo(expected.getName());
+    assertThat(actual.getUrl()).isEqualTo(expected.getUrl());
+    assertThat(actual.isSuccess()).isEqualTo(expected.isSuccess());
+    assertThat(actual.getPayload()).isEqualTo(expected.getPayload());
+    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 componentUuid, String ceTaskUuid) {
+    return new WebhookDeliveryDto()
+      .setUuid(uuid)
+      .setComponentUuid(componentUuid)
+      .setCeTaskUuid(ceTaskUuid)
+      .setName("Jenkins")
+      .setUrl("http://jenkins")
+      .setSuccess(true)
+      .setPayload("{json}")
+      .setCreatedAt(DATE_1);
+  }
+
+  private WebhookDeliveryDto selectByUuid(String uuid) {
+    Optional<WebhookDeliveryDto> dto = underTest.selectByUuid(dbSession, uuid);
+    assertThat(dto).isPresent();
+    return dto.get();
+  }
+}