CREATE TABLE "WEBHOOK_DELIVERIES" (
"UUID" VARCHAR(40) NOT NULL PRIMARY KEY,
- "WEBHOOK_UUID" VARCHAR(40),
+ "WEBHOOK_UUID" VARCHAR(40) NOT NULL,
"COMPONENT_UUID" VARCHAR(40) NOT NULL,
"ANALYSIS_UUID" VARCHAR(40),
"CE_TASK_UUID" VARCHAR(40),
"URL" VARCHAR(2000) NOT NULL,
"SUCCESS" BOOLEAN NOT NULL,
"HTTP_STATUS" INT,
- "DURATION_MS" INT,
+ "DURATION_MS" INT NOT NULL,
"PAYLOAD" CLOB NOT NULL,
"ERROR_STACKTRACE" CLOB,
"CREATED_AT" BIGINT NOT NULL
*/
package org.sonar.db.webhook;
+import java.util.Collections;
import java.util.List;
import java.util.Optional;
import org.sonar.api.utils.System2;
import org.sonar.db.organization.OrganizationDto;
import static com.google.common.base.Preconditions.checkState;
+import static java.util.Collections.emptyList;
public class WebhookDao implements Dao {
private final System2 system2;
+ private final WebhookDeliveryDao webhookDeliveryDao;
- public WebhookDao(System2 system2) {
+ public WebhookDao(System2 system2, WebhookDeliveryDao webhookDeliveryDao) {
this.system2 = system2;
+ this.webhookDeliveryDao = webhookDeliveryDao;
}
public Optional<WebhookDto> selectByUuid(DbSession dbSession, String uuid) {
}
public void delete(DbSession dbSession, String uuid) {
+ Optional<WebhookDto> webhookDto = selectByUuid(dbSession, uuid);
+ cascadeDeletionToDeliveries(dbSession, webhookDto.map(Collections::singletonList).orElse(emptyList()));
mapper(dbSession).delete(uuid);
}
- private static WebhookMapper mapper(DbSession dbSession) {
- return dbSession.getMapper(WebhookMapper.class);
- }
-
- public void cleanWebhooks(DbSession dbSession, OrganizationDto organization) {
+ public void deleteByOrganization(DbSession dbSession, OrganizationDto organization) {
+ cascadeDeletionToDeliveries(dbSession, selectByOrganizationUuid(dbSession, organization.getUuid()));
mapper(dbSession).deleteForOrganizationUuid(organization.getUuid());
}
- public void cleanWebhooks(DbSession dbSession, ComponentDto componentDto) {
+ public void deleteByProject(DbSession dbSession, ComponentDto componentDto) {
+ cascadeDeletionToDeliveries(dbSession, selectByProject(dbSession, componentDto));
mapper(dbSession).deleteForProjectUuid(componentDto.uuid());
}
+ private void cascadeDeletionToDeliveries(DbSession dbSession, List<WebhookDto> webhooks) {
+ webhooks.forEach(wh -> webhookDeliveryDao.deleteByWebhook(dbSession, wh));
+ }
+
+ private static WebhookMapper mapper(DbSession dbSession) {
+ return dbSession.getMapper(WebhookMapper.class);
+ }
+
}
private static WebhookDeliveryMapper mapper(DbSession dbSession) {
return dbSession.getMapper(WebhookDeliveryMapper.class);
}
+
+ public void deleteByWebhook(DbSession dbSession, WebhookDto webhook) {
+ mapper(dbSession).deleteByWebhookUuid(webhook.getUuid());
+ }
}
void insert(WebhookDeliveryDto dto);
void deleteComponentBeforeDate(@Param("componentUuid") String componentUuid, @Param("beforeDate") long beforeDate);
+
+ void deleteByWebhookUuid(@Param("webhookUuid") String webhookUuid);
}
)
</insert>
+ <select id="deleteByWebhookUuid" parameterType="String">
+ delete from webhook_deliveries
+ where webhook_uuid = #{webhookUuid,jdbcType=VARCHAR}
+ </select>
+
<delete id="deleteComponentBeforeDate" parameterType="map">
delete from webhook_deliveries
where
@Test
public void deleteProject_deletes_webhook_deliveries() {
ComponentDto project = dbTester.components().insertPublicProject();
- dbClient.webhookDeliveryDao().insert(dbSession, newDto().setComponentUuid(project.uuid()).setUuid("D1"));
- dbClient.webhookDeliveryDao().insert(dbSession, newDto().setComponentUuid("P2").setUuid("D2"));
+ dbClient.webhookDeliveryDao().insert(dbSession, newDto().setComponentUuid(project.uuid()).setUuid("D1").setDurationMs(1000).setWebhookUuid("webhook-uuid"));
+ dbClient.webhookDeliveryDao().insert(dbSession, newDto().setComponentUuid("P2").setUuid("D2").setDurationMs(1000).setWebhookUuid("webhook-uuid"));
underTest.deleteProject(dbSession, project.uuid());
import org.sonar.db.organization.OrganizationDto;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.db.webhook.WebhookDbTesting.newDto;
public class WebhookDaoTest {
private final DbClient dbClient = dbTester.getDbClient();
private final DbSession dbSession = dbTester.getSession();
private final WebhookDao underTest = dbClient.webhookDao();
+ private final WebhookDeliveryDao deliveryDao = dbClient.webhookDeliveryDao();
private final WebhookDbTester webhookDbTester = dbTester.webhooks();
+ private final WebhookDeliveryDbTester webhookDeliveryDbTester = dbTester.webhookDelivery();
private final ComponentDbTester componentDbTester = dbTester.components();
private final OrganizationDbTester organizationDbTester = dbTester.organizations();
-
@Test
public void selectByUuid_returns_empty_if_uuid_does_not_exist() {
assertThat(underTest.selectByUuid(dbSession, "missing")).isEmpty();
webhookDbTester.insertWebhook(componentDto);
webhookDbTester.insertWebhook(componentDto);
- underTest.cleanWebhooks(dbSession, componentDto);
+ underTest.deleteByProject(dbSession, componentDto);
Optional<WebhookDto> reloaded = underTest.selectByUuid(dbSession, componentDto.uuid());
assertThat(reloaded).isEmpty();
webhookDbTester.insertWebhook(organization);
webhookDbTester.insertWebhook(organization);
- underTest.cleanWebhooks(dbSession, organization);
+ underTest.deleteByOrganization(dbSession, organization);
Optional<WebhookDto> reloaded = underTest.selectByUuid(dbSession, organization.getUuid());
assertThat(reloaded).isEmpty();
assertThat(reloaded).isEmpty();
}
+ @Test
+ public void ensure_deliveries_are_deleted_when_a_webhook_is_deleted_by_uuid() {
+
+ OrganizationDto organization = organizationDbTester.insert();
+
+ WebhookDto dto = webhookDbTester.insertWebhook(organization);
+ webhookDeliveryDbTester.insert(newDto().setWebhookUuid(dto.getUuid()));
+ webhookDeliveryDbTester.insert(newDto().setWebhookUuid(dto.getUuid()));
+
+ underTest.delete(dbSession, dto.getUuid());
+
+ Optional<WebhookDto> reloaded = underTest.selectByUuid(dbSession, dto.getUuid());
+ assertThat(reloaded).isEmpty();
+
+ int deliveriesCount = deliveryDao.countDeliveriesByWebhookUuid(dbSession, dto.getUuid());
+ assertThat(deliveriesCount).isEqualTo(0);
+ }
+
+ @Test
+ public void ensure_deliveries_are_deleted_when_a_webhooks_are_deleted_by_organization() {
+
+ OrganizationDto organization = organizationDbTester.insert();
+
+ WebhookDto dto = webhookDbTester.insertWebhook(organization);
+ webhookDeliveryDbTester.insert(newDto().setWebhookUuid(dto.getUuid()));
+ webhookDeliveryDbTester.insert(newDto().setWebhookUuid(dto.getUuid()));
+
+ underTest.deleteByOrganization(dbSession, organization);
+
+ Optional<WebhookDto> reloaded = underTest.selectByUuid(dbSession, dto.getUuid());
+ assertThat(reloaded).isEmpty();
+
+ int deliveriesCount = deliveryDao.countDeliveriesByWebhookUuid(dbSession, dto.getUuid());
+ assertThat(deliveriesCount).isEqualTo(0);
+ }
+
+ @Test
+ public void ensure_deliveries_are_deleted_when_a_webhooks_are_deleted_by_project() {
+
+ OrganizationDto organization = organizationDbTester.insert();
+ ComponentDto componentDto = componentDbTester.insertPrivateProject(organization);
+ WebhookDto dto = webhookDbTester.insertWebhook(componentDto);
+ webhookDeliveryDbTester.insert(newDto().setWebhookUuid(dto.getUuid()));
+ webhookDeliveryDbTester.insert(newDto().setWebhookUuid(dto.getUuid()));
+
+ underTest.deleteByProject(dbSession, componentDto);
+
+ Optional<WebhookDto> reloaded = underTest.selectByUuid(dbSession, dto.getUuid());
+ assertThat(reloaded).isEmpty();
+
+ int deliveriesCount = deliveryDao.countDeliveriesByWebhookUuid(dbSession, dto.getUuid());
+ assertThat(deliveriesCount).isEqualTo(0);
+ }
+
@Test
public void fail_if_webhook_does_not_have_an_organization_nor_a_project() {
public static WebhookDeliveryDto newDto() {
return new WebhookDeliveryDto()
.setUuid(randomAlphanumeric(40))
+ .setWebhookUuid(randomAlphanumeric(40))
.setComponentUuid(randomAlphanumeric(40))
.setCeTaskUuid(randomAlphanumeric(40))
.setAnalysisUuid(randomAlphanumeric(40))
@Test
public void insert_row_with_only_mandatory_columns() {
WebhookDeliveryDto dto = WebhookDbTesting.newDto("DELIVERY_1", "WEBHOOK_UUID_1", "COMPONENT_1", "TASK_1")
+ .setDurationMs(1000)
.setHttpStatus(null)
- .setDurationMs(null)
.setErrorStacktrace(null);
underTest.insert(dbSession, dto);
WebhookDeliveryDto stored = selectByUuid(dto.getUuid());
verifyMandatoryFields(dto, stored);
+ assertThat(stored.getDurationMs()).isEqualTo(1000);
+
// optional fields are null
assertThat(stored.getHttpStatus()).isNull();
- assertThat(stored.getDurationMs()).isNull();
assertThat(stored.getErrorStacktrace()).isNull();
}
assertThat(stored.getErrorStacktrace()).isEqualTo(dto.getErrorStacktrace());
}
+ @Test
+ public void deleteByWebhook() {
+
+ WebhookDto webhookDto = dbWebhooks.insert(WebhookTesting.newProjectWebhook("COMPONENT_1"));
+
+ underTest.insert(dbSession, WebhookDbTesting.newDto("DELIVERY_1", webhookDto.getUuid(), "COMPONENT_1", "TASK_1").setCreatedAt(1_000_000L));
+ underTest.insert(dbSession, WebhookDbTesting.newDto("DELIVERY_2", webhookDto.getUuid(), "COMPONENT_1", "TASK_2").setCreatedAt(2_000_000L));
+ underTest.insert(dbSession, WebhookDbTesting.newDto("DELIVERY_3", "WONT BE DELETED WEBHOOK_UUID_2", "COMPONENT_2", "TASK_3").setCreatedAt(1_000_000L));
+
+ underTest.deleteByWebhook(dbSession, webhookDto);
+
+ assertThat(dbTester.countRowsOfTable(dbSession, "webhook_deliveries")).isEqualTo(1); }
+
@Test
public void deleteComponentBeforeDate_deletes_rows_before_date() {
underTest.insert(dbSession, WebhookDbTesting.newDto("DELIVERY_1", "WEBHOOK_UUID_1", "COMPONENT_1", "TASK_1").setCreatedAt(1_000_000L));
import java.sql.SQLException;
import org.sonar.db.Database;
import org.sonar.server.platform.db.migration.sql.AddColumnsBuilder;
+import org.sonar.server.platform.db.migration.sql.AlterColumnsBuilder;
import org.sonar.server.platform.db.migration.step.DdlChange;
+import static org.sonar.server.platform.db.migration.def.BigIntegerColumnDef.newBigIntegerColumnDefBuilder;
import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder;
public class AddWebhookKeyToWebhookDeliveriesTable extends DdlChange {
@Override
public void execute(Context context) throws SQLException {
+
+ context.execute("delete from webhook_deliveries");
+
context.execute(new AddColumnsBuilder(getDialect(), "webhook_deliveries")
.addColumn(newVarcharColumnDefBuilder()
.setColumnName("webhook_uuid")
- .setIsNullable(true)
+ .setIsNullable(false)
.setLimit(40)
.build())
.build());
+
+ context.execute(new AlterColumnsBuilder(getDialect(), "webhook_deliveries")
+ .updateColumn(newBigIntegerColumnDefBuilder()
+ .setColumnName("duration_ms")
+ .setIsNullable(false)
+ .build())
+ .build());
}
}
import org.junit.rules.ExpectedException;
import org.sonar.db.CoreDbTester;
+import static java.lang.String.valueOf;
+import static java.sql.Types.BIGINT;
import static java.sql.Types.VARCHAR;
+import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
+import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
+import static org.apache.commons.lang.RandomStringUtils.randomNumeric;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.sonar.db.CoreDbTester.createForSchema;
public class AddWebhookKeyToWebhookDeliveriesTableTest {
private AddWebhookKeyToWebhookDeliveriesTable underTest = new AddWebhookKeyToWebhookDeliveriesTable(dbTester.database());
@Test
- public void column_is_added_to_table() throws SQLException {
+ public void table_has_been_truncated() throws SQLException {
+
+ insertDelivery();
+
+ underTest.execute();
+
+ int count = dbTester.countRowsOfTable("webhook_deliveries");
+ assertThat(count).isEqualTo(0);
+ }
+
+ @Test
+ public void column_webhook_uuid_has_been_added_to_table() throws SQLException {
underTest.execute();
- dbTester.assertColumnDefinition("webhook_deliveries", "webhook_uuid", VARCHAR, 40, true);
+ dbTester.assertColumnDefinition("webhook_deliveries", "webhook_uuid", VARCHAR, 40, false);
+ }
+
+ @Test
+ public void column_duration_ms_is_now_not_nullable() throws SQLException {
+ underTest.execute();
+
+ dbTester.assertColumnDefinition("webhook_deliveries", "duration_ms", BIGINT, null, false);
}
@Test
underTest.execute();
}
+
+ private void insertDelivery() {
+ dbTester.executeInsert("webhook_deliveries",
+ "uuid", randomAlphanumeric(40),
+// "webhook_uuid", randomAlphanumeric(40),
+ "component_uuid", randomAlphanumeric(40),
+ "name", randomAlphabetic(60),
+ "url", randomAlphabetic(200),
+ "success", true,
+ "duration_ms", randomNumeric(7),
+ "payload", randomAlphanumeric(1000),
+ "created_at", valueOf(1_55555_555));
+ }
+
}
checkArgument(!hasNotProjectScope(project) && !isNotDeletable(project) && project.getMainBranchProjectUuid() == null, "Only projects can be deleted");
dbClient.purgeDao().deleteProject(dbSession, project.uuid());
dbClient.userDao().cleanHomepage(dbSession, project);
- dbClient.webhookDao().cleanWebhooks(dbSession, project);
+ dbClient.webhookDao().deleteByProject(dbSession, project);
projectIndexers.commitAndIndex(dbSession, singletonList(project), ProjectIndexer.Cause.PROJECT_DELETION);
}
private void deleteProjects(DbSession dbSession, OrganizationDto organization) {
List<ComponentDto> roots = dbClient.componentDao().selectAllRootsByOrganization(dbSession, organization.getUuid());
- roots.forEach(project -> dbClient.webhookDao().cleanWebhooks(dbSession, project));
+ roots.forEach(project -> dbClient.webhookDao().deleteByProject(dbSession, project));
componentCleanerService.delete(dbSession, roots);
}
dbClient.organizationMemberDao().deleteByOrganizationUuid(dbSession, organization.getUuid());
dbClient.organizationDao().deleteByUuid(dbSession, organization.getUuid());
dbClient.userDao().cleanHomepage(dbSession, organization);
- dbClient.webhookDao().cleanWebhooks(dbSession, organization);
+ dbClient.webhookDao().deleteByOrganization(dbSession, organization);
userIndexer.commitAndIndexByLogins(dbSession, logins);
}
.setComponentUuid(project.uuid())
.setSuccess(false)
.setHttpStatus(null)
- .setDurationMs(null)
.setErrorStacktrace("IOException -> can not connect");
dbClient.webhookDeliveryDao().insert(db.getSession(), dto);
db.commit();
Webhooks.Delivery actual = response.getDelivery();
assertThat(actual.hasHttpStatus()).isFalse();
- assertThat(actual.hasDurationMs()).isFalse();
assertThat(actual.getErrorStacktrace()).isEqualTo(dto.getErrorStacktrace());
}
.setComponentUuid(project.uuid())
.setCeTaskUuid(null)
.setHttpStatus(null)
- .setDurationMs(null)
.setErrorStacktrace(null)
.setAnalysisUuid(null);
dbClient.webhookDeliveryDao().insert(db.getSession(), dto);
Webhooks.Delivery actual = response.getDelivery();
assertThat(actual.hasHttpStatus()).isFalse();
- assertThat(actual.hasDurationMs()).isFalse();
assertThat(actual.hasErrorStacktrace()).isFalse();
assertThat(actual.hasCeTaskId()).isFalse();
}