/** * Webhook Entity * * Represents a webhook subscription for external integrations */ export enum WebhookEvent { BOOKING_CREATED = 'booking.created', BOOKING_UPDATED = 'booking.updated', BOOKING_CANCELLED = 'booking.cancelled', BOOKING_CONFIRMED = 'booking.confirmed', RATE_QUOTE_CREATED = 'rate_quote.created', DOCUMENT_UPLOADED = 'document.uploaded', ORGANIZATION_UPDATED = 'organization.updated', USER_CREATED = 'user.created', } export enum WebhookStatus { ACTIVE = 'active', INACTIVE = 'inactive', FAILED = 'failed', } interface WebhookProps { id: string; organizationId: string; url: string; events: WebhookEvent[]; secret: string; status: WebhookStatus; description?: string; headers?: Record; retryCount: number; lastTriggeredAt?: Date; failureCount: number; createdAt: Date; updatedAt: Date; } export class Webhook { private constructor(private readonly props: WebhookProps) {} static create( props: Omit & { id: string }, ): Webhook { return new Webhook({ ...props, status: WebhookStatus.ACTIVE, retryCount: 0, failureCount: 0, createdAt: new Date(), updatedAt: new Date(), }); } static fromPersistence(props: WebhookProps): Webhook { return new Webhook(props); } get id(): string { return this.props.id; } get organizationId(): string { return this.props.organizationId; } get url(): string { return this.props.url; } get events(): WebhookEvent[] { return this.props.events; } get secret(): string { return this.props.secret; } get status(): WebhookStatus { return this.props.status; } get description(): string | undefined { return this.props.description; } get headers(): Record | undefined { return this.props.headers; } get retryCount(): number { return this.props.retryCount; } get lastTriggeredAt(): Date | undefined { return this.props.lastTriggeredAt; } get failureCount(): number { return this.props.failureCount; } get createdAt(): Date { return this.props.createdAt; } get updatedAt(): Date { return this.props.updatedAt; } /** * Check if webhook is active */ isActive(): boolean { return this.props.status === WebhookStatus.ACTIVE; } /** * Check if webhook subscribes to an event */ subscribesToEvent(event: WebhookEvent): boolean { return this.props.events.includes(event); } /** * Activate webhook */ activate(): Webhook { return new Webhook({ ...this.props, status: WebhookStatus.ACTIVE, updatedAt: new Date(), }); } /** * Deactivate webhook */ deactivate(): Webhook { return new Webhook({ ...this.props, status: WebhookStatus.INACTIVE, updatedAt: new Date(), }); } /** * Mark webhook as failed */ markAsFailed(): Webhook { return new Webhook({ ...this.props, status: WebhookStatus.FAILED, failureCount: this.props.failureCount + 1, updatedAt: new Date(), }); } /** * Record successful trigger */ recordTrigger(): Webhook { return new Webhook({ ...this.props, lastTriggeredAt: new Date(), retryCount: this.props.retryCount + 1, failureCount: 0, // Reset failure count on success updatedAt: new Date(), }); } /** * Update webhook */ update(updates: { url?: string; events?: WebhookEvent[]; description?: string; headers?: Record; }): Webhook { return new Webhook({ ...this.props, ...updates, updatedAt: new Date(), }); } /** * Convert to plain object */ toObject(): WebhookProps { return { ...this.props }; } }