mirror of
https://github.com/papra-hq/papra.git
synced 2025-12-21 12:09:39 -06:00
fix(webhooks): corrected webhooks last triggered date (#582)
This commit is contained in:
committed by
GitHub
parent
75340f0ce7
commit
182ccbb30b
5
.changeset/poor-seals-try.md
Normal file
5
.changeset/poor-seals-try.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@papra/docker": patch
|
||||
---
|
||||
|
||||
Fixed the webhook last triggered date always showing "never" in the webhook list.
|
||||
@@ -0,0 +1,50 @@
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import { createInMemoryDatabase } from '../app/database/database.test-utils';
|
||||
import { createWebhookRepository } from './webhook.repository';
|
||||
|
||||
describe('webhook repository', () => {
|
||||
describe('getOrganizationWebhooks', () => {
|
||||
test('includes the most recent webhook delivery timestamp as lastTriggeredAt', async () => {
|
||||
const { db } = await createInMemoryDatabase({
|
||||
organizations: [
|
||||
{ id: 'org_1', name: 'Test Organization' },
|
||||
],
|
||||
webhooks: [
|
||||
{ id: 'wbh_1', name: 'Test Webhook', url: 'https://example.com/webhook', organizationId: 'org_1' },
|
||||
],
|
||||
webhookDeliveries: [
|
||||
{ id: 'wbh_dlv_1', webhookId: 'wbh_1', eventName: 'document:created', requestPayload: '{}', responsePayload: '{}', responseStatus: 200, createdAt: new Date('2025-01-01') },
|
||||
{ id: 'wbh_dlv_2', webhookId: 'wbh_1', eventName: 'document:updated', requestPayload: '{}', responsePayload: '{}', responseStatus: 200, createdAt: new Date('2025-01-15') },
|
||||
{ id: 'wbh_dlv_3', webhookId: 'wbh_1', eventName: 'document:deleted', requestPayload: '{}', responsePayload: '{}', responseStatus: 200, createdAt: new Date('2025-01-10') },
|
||||
],
|
||||
});
|
||||
|
||||
const webhookRepository = createWebhookRepository({ db });
|
||||
const { webhooks } = await webhookRepository.getOrganizationWebhooks({ organizationId: 'org_1' });
|
||||
|
||||
expect(webhooks).to.have.length(1);
|
||||
const [webhook] = webhooks;
|
||||
|
||||
expect(webhook?.lastTriggeredAt).to.eql(new Date('2025-01-15'));
|
||||
});
|
||||
|
||||
test('no deliveries results in null lastTriggeredAt', async () => {
|
||||
const { db } = await createInMemoryDatabase({
|
||||
organizations: [
|
||||
{ id: 'org_1', name: 'Test Organization' },
|
||||
],
|
||||
webhooks: [
|
||||
{ id: 'wbh_1', name: 'Test Webhook', url: 'https://example.com/webhook', organizationId: 'org_1' },
|
||||
],
|
||||
});
|
||||
|
||||
const webhookRepository = createWebhookRepository({ db });
|
||||
const { webhooks } = await webhookRepository.getOrganizationWebhooks({ organizationId: 'org_1' });
|
||||
|
||||
expect(webhooks).to.have.length(1);
|
||||
const [webhook] = webhooks;
|
||||
|
||||
expect(webhook?.lastTriggeredAt).to.eql(null);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -2,7 +2,7 @@ import type { EventName } from '@papra/webhooks';
|
||||
import type { Database } from '../app/database/database.types';
|
||||
import type { Webhook, WebhookDeliveryInsert, WebhookEvent } from './webhooks.types';
|
||||
import { injectArguments } from '@corentinth/chisels';
|
||||
import { and, eq, getTableColumns } from 'drizzle-orm';
|
||||
import { and, eq, getTableColumns, max } from 'drizzle-orm';
|
||||
import { omitUndefined } from '../shared/utils';
|
||||
import { webhookDeliveriesTable, webhookEventsTable, webhooksTable } from './webhooks.tables';
|
||||
|
||||
@@ -113,14 +113,25 @@ async function deleteOrganizationWebhook({ db, webhookId, organizationId }: { db
|
||||
}
|
||||
|
||||
async function getOrganizationWebhooks({ db, organizationId }: { db: Database; organizationId: string }) {
|
||||
// Create a subquery for the latest delivery date per webhook
|
||||
const latestDeliverySubquery = db
|
||||
.select({
|
||||
webhookId: webhookDeliveriesTable.webhookId,
|
||||
lastTriggeredAt: max(webhookDeliveriesTable.createdAt).as('last_triggered_at'),
|
||||
})
|
||||
.from(webhookDeliveriesTable)
|
||||
.groupBy(webhookDeliveriesTable.webhookId)
|
||||
.as('latest_delivery');
|
||||
|
||||
const rawWebhooks = await db
|
||||
.select()
|
||||
.from(webhooksTable)
|
||||
.leftJoin(webhookEventsTable, eq(webhooksTable.id, webhookEventsTable.webhookId))
|
||||
.leftJoin(latestDeliverySubquery, eq(webhooksTable.id, latestDeliverySubquery.webhookId))
|
||||
.where(eq(webhooksTable.organizationId, organizationId));
|
||||
|
||||
const webhooksRecord = rawWebhooks
|
||||
.reduce((acc, { webhooks, webhook_events }) => {
|
||||
.reduce((acc, { webhooks, webhook_events, latest_delivery }) => {
|
||||
const webhookId = webhooks.id;
|
||||
const webhookEvents = webhook_events;
|
||||
|
||||
@@ -128,6 +139,7 @@ async function getOrganizationWebhooks({ db, organizationId }: { db: Database; o
|
||||
acc[webhookId] = {
|
||||
...webhooks,
|
||||
events: [],
|
||||
lastTriggeredAt: latest_delivery?.lastTriggeredAt ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -136,7 +148,7 @@ async function getOrganizationWebhooks({ db, organizationId }: { db: Database; o
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, {} as Record<string, Webhook & { events: WebhookEvent[] }>);
|
||||
}, {} as Record<string, Webhook & { events: WebhookEvent[]; lastTriggeredAt: Date | null }>);
|
||||
|
||||
return { webhooks: Object.values(webhooksRecord) };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user