Files
calendar/packages/features/webhooks/lib/service/WebhookTaskerProducerService.ts
T
2026-01-22 16:11:10 -03:00

225 lines
7.2 KiB
TypeScript

import { WebhookTriggerEvents } from "@calcom/prisma/enums";
import { v4 as uuidv4 } from "uuid";
import type { BookingTriggerEvents, PaymentTriggerEvents } from "../factory/versioned/PayloadBuilderFactory";
import type { ILogger, ITasker } from "../interface/infrastructure";
import type {
IWebhookProducerService,
QueueBookingWebhookParams,
QueueFormWebhookParams,
QueueOOOWebhookParams,
QueuePaymentWebhookParams,
QueueRecordingWebhookParams,
} from "../interface/WebhookProducerService";
import type { WebhookTaskPayload } from "../types/webhookTask";
/**
* Lightweight Producer Service for webhook delivery.
*
* DEPENDENCIES: Only Tasker and Logger (no Prisma, no repositories)
*
* This service queues minimal webhook tasks to be processed by WebhookTaskConsumer.
* The consumer handles the heavy lifting (DB queries, payload building, HTTP delivery).
*/
export class WebhookTaskerProducerService implements IWebhookProducerService {
private readonly log: ILogger;
constructor(
private readonly tasker: ITasker,
logger: ILogger
) {
this.log = logger.getSubLogger({ prefix: ["[WebhookTaskerProducerService]"] });
}
async queueBookingCreatedWebhook(params: QueueBookingWebhookParams): Promise<void> {
await this.queueBookingWebhook(WebhookTriggerEvents.BOOKING_CREATED, params);
}
async queueBookingCancelledWebhook(params: QueueBookingWebhookParams): Promise<void> {
await this.queueBookingWebhook(WebhookTriggerEvents.BOOKING_CANCELLED, params);
}
async queueBookingRescheduledWebhook(params: QueueBookingWebhookParams): Promise<void> {
await this.queueBookingWebhook(WebhookTriggerEvents.BOOKING_RESCHEDULED, params);
}
/**
* Queue a webhook for requested bookings.
*
* This fires when bookings require confirmation (status = PENDING).
*/
async queueBookingRequestedWebhook(params: QueueBookingWebhookParams): Promise<void> {
await this.queueBookingWebhook(WebhookTriggerEvents.BOOKING_REQUESTED, params);
}
async queueBookingRejectedWebhook(params: QueueBookingWebhookParams): Promise<void> {
await this.queueBookingWebhook(WebhookTriggerEvents.BOOKING_REJECTED, params);
}
async queueBookingPaymentInitiatedWebhook(params: QueuePaymentWebhookParams): Promise<void> {
await this.queuePaymentWebhook(WebhookTriggerEvents.BOOKING_PAYMENT_INITIATED, params);
}
async queueBookingPaidWebhook(params: QueuePaymentWebhookParams): Promise<void> {
await this.queuePaymentWebhook(WebhookTriggerEvents.BOOKING_PAID, params);
}
async queueBookingNoShowUpdatedWebhook(params: QueueBookingWebhookParams): Promise<void> {
await this.queueBookingWebhook(WebhookTriggerEvents.BOOKING_NO_SHOW_UPDATED, params);
}
async queueFormSubmittedWebhook(params: QueueFormWebhookParams): Promise<void> {
const operationId = params.operationId || uuidv4();
this.log.debug("Queueing form webhook task", {
operationId,
triggerEvent: WebhookTriggerEvents.FORM_SUBMITTED,
formId: params.formId,
});
const taskPayload: WebhookTaskPayload = {
operationId,
triggerEvent: WebhookTriggerEvents.FORM_SUBMITTED,
formId: params.formId,
teamId: params.teamId,
userId: params.userId,
oAuthClientId: params.oAuthClientId,
metadata: params.metadata,
timestamp: new Date().toISOString(),
};
await this.queueTask(operationId, taskPayload);
}
async queueRecordingReadyWebhook(params: QueueRecordingWebhookParams): Promise<void> {
const operationId = params.operationId || uuidv4();
this.log.debug("Queueing recording webhook task", {
operationId,
triggerEvent: WebhookTriggerEvents.RECORDING_READY,
recordingId: params.recordingId,
bookingUid: params.bookingUid,
});
const taskPayload: WebhookTaskPayload = {
operationId,
triggerEvent: WebhookTriggerEvents.RECORDING_READY,
recordingId: params.recordingId,
bookingUid: params.bookingUid,
eventTypeId: params.eventTypeId,
teamId: params.teamId,
userId: params.userId,
oAuthClientId: params.oAuthClientId,
metadata: params.metadata,
timestamp: new Date().toISOString(),
};
await this.queueTask(operationId, taskPayload);
}
async queueOOOCreatedWebhook(params: QueueOOOWebhookParams): Promise<void> {
const operationId = params.operationId || uuidv4();
this.log.debug("Queueing OOO webhook task", {
operationId,
triggerEvent: WebhookTriggerEvents.OOO_CREATED,
oooEntryId: params.oooEntryId,
userId: params.userId,
});
const taskPayload: WebhookTaskPayload = {
operationId,
triggerEvent: WebhookTriggerEvents.OOO_CREATED,
oooEntryId: params.oooEntryId,
userId: params.userId,
teamId: params.teamId,
oAuthClientId: params.oAuthClientId,
metadata: params.metadata,
timestamp: new Date().toISOString(),
};
await this.queueTask(operationId, taskPayload);
}
/**
* Internal helper to queue booking-related webhooks
*/
private async queueBookingWebhook(
triggerEvent: Exclude<
BookingTriggerEvents,
typeof WebhookTriggerEvents.BOOKING_PAYMENT_INITIATED | typeof WebhookTriggerEvents.BOOKING_PAID
>,
params: QueueBookingWebhookParams
): Promise<void> {
const operationId = params.operationId || uuidv4();
this.log.debug("Queueing booking webhook task", {
operationId,
triggerEvent,
bookingUid: params.bookingUid,
eventTypeId: params.eventTypeId,
});
const taskPayload: WebhookTaskPayload = {
operationId,
triggerEvent,
bookingUid: params.bookingUid,
eventTypeId: params.eventTypeId,
teamId: params.teamId,
userId: params.userId,
orgId: params.orgId,
oAuthClientId: params.oAuthClientId,
metadata: params.metadata,
timestamp: new Date().toISOString(),
};
await this.queueTask(operationId, taskPayload);
}
/**
* Internal helper to queue payment-related webhooks
*/
private async queuePaymentWebhook(
triggerEvent: PaymentTriggerEvents,
params: QueuePaymentWebhookParams
): Promise<void> {
const operationId = params.operationId || uuidv4();
this.log.debug("Queueing payment webhook task", {
operationId,
triggerEvent,
bookingUid: params.bookingUid,
});
const taskPayload: WebhookTaskPayload = {
operationId,
triggerEvent,
bookingUid: params.bookingUid,
eventTypeId: params.eventTypeId,
teamId: params.teamId,
userId: params.userId,
orgId: params.orgId,
oAuthClientId: params.oAuthClientId,
metadata: params.metadata,
timestamp: new Date().toISOString(),
};
await this.queueTask(operationId, taskPayload);
}
/**
* Internal helper to queue task via Tasker
*/
private async queueTask(operationId: string, taskPayload: WebhookTaskPayload): Promise<void> {
try {
await this.tasker.create("webhookDelivery", taskPayload);
this.log.debug("Webhook delivery task queued", { operationId });
} catch (error) {
this.log.error("Failed to queue webhook delivery task", {
operationId,
error: error instanceof Error ? error.message : String(error),
});
throw error;
}
}
}