Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions src/webhook/webhook.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,31 @@ export class WebhookController {

/** List recent webhook deliveries for the authenticated merchant. */
@Get('deliveries')
async listDeliveries(@Request() req: any, @Query('limit') limit?: string) {
async listDeliveries(
@Request() req: any,
@Query('limit') limit?: string,
@Query('event') event?: string,
@Query('status') status?: string,
@Query('after') after?: string,
@Query('before') before?: string,
) {
const merchantId = await this.merchantId(req);
return this.webhooks.listDeliveries(merchantId, this.clampLimit(limit));
return this.webhooks.listDeliveries(merchantId, {
limit: this.clampLimit(limit),
event,
status,
after: after ? new Date(after) : undefined,
before: before ? new Date(before) : undefined,
});
}

/** Get a single delivery record by its delivery ID, including full attempt log. */
@Get('deliveries/:id')
async getDelivery(@Request() req: any, @Param('id', ParseUUIDPipe) id: string) {
const merchantId = await this.merchantId(req);
const delivery = await this.webhooks.getDelivery(merchantId, id);
if (!delivery) throw new NotFoundException('Delivery not found');
return delivery;
}

/** Bound a client-supplied limit to a sane [1, 100] range (default 50). */
Expand Down
36 changes: 31 additions & 5 deletions src/webhook/webhook.service.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import { Injectable, Logger } from '@nestjs/common';
import { db } from '../db/index';
import { webhookDeadLetters, webhookDeliveries, merchants } from '../db/schema';
import { and, desc, eq } from 'drizzle-orm';
import { and, desc, eq, gte, lte } from 'drizzle-orm';
import { WebhookQueueService } from './webhook-queue.service';

export interface ListDeliveriesOptions {
limit?: number;
event?: string;
status?: string;
after?: Date;
before?: Date;
}

@Injectable()
export class WebhookService {
private readonly logger = new Logger(WebhookService.name);
Expand All @@ -27,12 +35,30 @@ export class WebhookService {
await this.queue.enqueue({ merchantId, event, body: data, sessionId });
}

/** Recent delivery records for a merchant (most recent first). */
async listDeliveries(merchantId: string, limit = 50) {
/** Recent delivery records for a merchant with optional filtering. */
async listDeliveries(merchantId: string, options: ListDeliveriesOptions = {}) {
const { limit = 50, event, status, after, before } = options;

const conditions = [eq(webhookDeliveries.merchantId, merchantId)];
if (event) conditions.push(eq(webhookDeliveries.event, event));
if (status) conditions.push(eq(webhookDeliveries.status, status as any));
if (after) conditions.push(gte(webhookDeliveries.createdAt, after));
if (before) conditions.push(lte(webhookDeliveries.createdAt, before));

return db.query.webhookDeliveries.findMany({
where: eq(webhookDeliveries.merchantId, merchantId),
where: and(...conditions),
orderBy: [desc(webhookDeliveries.createdAt)],
limit,
limit: Math.min(limit, 100),
});
}

/** Single delivery record with full attempt log. */
async getDelivery(merchantId: string, deliveryId: string) {
return db.query.webhookDeliveries.findFirst({
where: and(
eq(webhookDeliveries.merchantId, merchantId),
eq(webhookDeliveries.deliveryId, deliveryId),
),
});
}

Expand Down
Loading