Skip to content

chore: migrate booking requested webhook trigger#27546

Merged
alishaz-polymath merged 38 commits intomainfrom
chore/migrate-booking-requested-webhook-trigger
Feb 19, 2026
Merged

chore: migrate booking requested webhook trigger#27546
alishaz-polymath merged 38 commits intomainfrom
chore/migrate-booking-requested-webhook-trigger

Conversation

@alishaz-polymath
Copy link
Copy Markdown
Member

@alishaz-polymath alishaz-polymath commented Feb 3, 2026

What does this PR do?

Migrates BOOKING_REQUESTED webhook delivery from the legacy inline path (getWebhooks → sendPayload) to the producer–consumer pipeline (Trigger.dev or sync), so it uses the same flow as other booking triggers (e.g. BOOKING_CREATED, BOOKING_RESCHEDULED).

Core Changes

  • handleBookingRequested.ts: Replaced inline getWebhookssendPayload with getWebhookProducer().queueBookingRequestedWebhook(...). Missing evt.uid is handled with if/else + inner try/catch so workflow scheduling is never skipped. Added oAuthClientId parameter for platform webhook subscriber filtering.
  • RegularBookingService: When a booking requires confirmation (!isConfirmedByDefault), the BOOKING_REQUESTED webhook is queued via deps.webhookProducer.queueBookingRequestedWebhook(...). Queued after booking update so the consumer fetches complete booking data (location, metadata, references).
  • handlePaymentSuccess.ts: Now passes oAuthClientId through to handleBookingRequested so platform webhooks fire for paid events requiring confirmation.
  • DI wiring: webhookProducer is wired into RegularBookingService.module.ts via webhookProducerModuleLoader.
  • API v2 Integration: Added WEBHOOK_PRODUCER injection token in regular-booking.tokens.ts and useFactory: () => getWebhookProducer() in RegularBookingModule to bridge Cal.com DI into NestJS. RegularBookingService (API v2) now receives and forwards the producer via @Inject(WEBHOOK_PRODUCER).
  • BookingWebhookDataFetcher: Upgraded from scaffold to full implementation — fetches booking via BookingRepository.getBookingForCalEventBuilderFromUid(), builds CalendarEvent via CalendarEventBuilder.fromBooking(), and returns complete data for webhook payload building. Now accepts platform metadata fields (platformClientId, platformRescheduleUrl, etc.).
  • Webhook task payload schema: Extended bookingWebhookTaskPayloadSchema with platformClientId, platformRescheduleUrl, platformCancelUrl, platformBookingUrl.
  • Platform library exports: IWebhookProducerService, getWebhookProducer, Tasker, and getTasker are now exported from @calcom/platform-libraries.
  • Trigger.dev config: Tuned to small-1x machine, concurrencyLimit: 25, retry with 30s–600s exponential backoff and OOM fallback to medium-1x.
  • E2E (webhook.e2e.ts): Added null guard for body.payload.metadata to prevent crash when metadata is absent.

BOOKING_REQUESTED Routes (2 entry points, both migrated)

Route Location How it queues
Non-payment booking requiring confirmation RegularBookingService.ts — fires when booking.status === PENDING && !isDryRun deps.webhookProducer.queueBookingRequestedWebhook() via DI
Paid booking requiring confirmation handlePaymentSuccess.tshandleBookingRequested.ts — fires when !isConfirmed && requiresConfirmation getWebhookProducer().queueBookingRequestedWebhook() via container getter

Test Changes

Removed legacy tests that asserted inline webhook delivery (sendPayload / expectBookingRequestedWebhookToHaveBeenFired):

  • 8 tests from fresh-booking.test.ts, reschedule.test.ts, collective-scheduling.test.ts

Added new test suites (49 total tests):

Location Tests Coverage
packages/features/webhooks/lib/__tests__/producer/WebhookTaskerProducerService.test.ts 14 Producer queues correct task payloads for all booking triggers
packages/features/webhooks/lib/__tests__/consumer/WebhookTaskConsumer.test.ts 8 Consumer processes tasks with correct 5-param constructor
packages/features/webhooks/lib/__tests__/consumer/triggers/booking-requested.test.ts 19 BOOKING_REQUESTED consumer scenarios + payload content verification
packages/features/bookings/lib/handleNewBooking/test/webhook-producer-booking-requested.test.ts 8 Integration tests verifying booking flow invokes producer correctly

Integration test scenarios (8 total in webhook-producer-booking-requested.test.ts):

  1. Basic confirmation → queueBookingRequestedWebhook called
  2. Booker-is-organizer + confirmation → still called
  3. Confirmation threshold NOT met → not called (BOOKING_CREATED instead)
  4. Confirmation threshold IS met → called
  5. Paid event + confirmation → called after payment succeeds
  6. Reschedule + confirmation (non-organizer) → called (not BOOKING_RESCHEDULED)
  7. Reschedule + confirmation (organizer) → not called (BOOKING_RESCHEDULED instead)
  8. Collective scheduling + confirmation → called

Payload content verification tests (11 new tests in booking-requested.test.ts):

  • Verifies PENDING status and BOOKING_REQUESTED triggerEvent
  • Verifies eventTitle, eventDescription, requiresConfirmation, price, currency, length
  • Verifies bookingId, title, location, organizer, attendees
  • Verifies response label normalization (name → your_name, email → email_address)
  • Verifies metadata handling (empty object when absent, includes videoCallUrl when present)
  • Verifies attendee firstName/lastName derivation from full name
  • Verifies assignmentReason uses legacy array format from booking
  • Verifies default values (price: 0, currency: "usd", length: null)
  • Verifies destinationCalendar as null when not set
  • Verifies paid event payload structure

Reusable test infrastructure:

  • packages/testing/src/lib/webhookProducer.tsMockWebhookProducer type, createMockWebhookProducer(), expectWebhookProducerCalled(), expectWebhookProducerNotCalled(), resetMockWebhookProducer() for extendable use as more triggers migrate.

Updates Since Last Revision

Code review findings (migration quality score: 8.5/10):

  • ✅ All BOOKING_REQUESTED routes are covered (non-payment and payment paths)
  • ✅ No performance pitfalls identified — producer is lightweight, consumer runs async
  • ✅ Subscriber filtering params are consistent across both entry points
  • ⚠️ handleBookingRequested uses getWebhookProducer() container getter instead of DI injection (acceptable for standalone function; would need refactoring to class/service for proper DI)
  • ⚠️ buildDTO switch only handles BOOKING_REQUESTED (expected for incremental migration; other triggers will be added as they migrate)

Backward compatibility verified:

  • Webhook payload shape matches legacy format (v2021-10-20)

  • assignmentReason uses legacy array format from booking

  • Response labels normalized (nameyour_name, emailemail_address)

  • Default values preserved (price: 0, currency: "usd", length: null)

  • Fixes #XXXX

  • Fixes CAL-XXXX

Visual Demo (For contributors especially)

Video Demo (if applicable):

Loom

  • Before: BOOKING_REQUESTED webhook is sent inline via handleWebhookTrigger; no task appears in Trigger.dev.
  • After: Create a booking that requires confirmation → BOOKING_REQUESTED is queued via producer → when Trigger.dev is configured, a webhook.deliver run appears in the Trigger.dev dashboard.

Image Demo (if applicable):

image

Mandatory Tasks (DO NOT REMOVE)

  • I have self-reviewed the code (A decent size PR without self-review might be rejected).
  • I have updated the developer docs in /docs if this PR makes changes that would require a documentation change. If N/A, write N/A here and check the checkbox.
  • I confirm automated tests are in place that prove my fix is effective or that my feature works.

How should this be tested?

  1. Environment: For Trigger.dev path: ENABLE_ASYNC_TASKER=true, TRIGGER_SECRET_KEY, TRIGGER_API_URL set; worker running.
  2. Minimal setup: One event type with "Requires confirmation" enabled; at least one webhook subscriber for BOOKING_REQUESTED.
  3. Happy path:
    • Create a booking for that event type.
    • Expected: BOOKING_REQUESTED webhook is received by the subscriber.
    • With Trigger.dev: a webhook.deliver task run appears in the dashboard.
  4. Run tests:
    TZ=UTC yarn test packages/features/webhooks/lib/__tests__
    TZ=UTC yarn vitest run packages/features/bookings/lib/handleNewBooking/test/webhook-producer-booking-requested.test.ts

Human Review Checklist

  • Verify handleBookingRequested.ts if/else + inner try/catch ensures workflows always run (lines 75-94)
  • Verify paid event test (scenario 5) actually exercises the post-payment webhook path — note that handleBookingRequested.ts uses getWebhookProducer() directly, which may not be intercepted by the DI-injected mock in RegularBookingService
  • Confirm webhook payload shape is backward-compatible (especially assignmentReason, responses normalization, default values)
  • Verify the 11 new payload verification tests in booking-requested.test.ts adequately cover the payload shapes that were verified by the deleted tests
  • Consider removing 11 overlapping tests identified in code review (6 from payload builder section, 5 from consumer scenario section) in a follow-up PR
  • Note: This PR is large (39 files, +2725/-1639 lines) — consider whether any parts could be split

Checklist

  • I haven't read the contributing guide
  • My code doesn't follow the style guidelines of this project
  • I haven't commented my code, particularly in hard-to-understand areas
  • I haven't checked if my changes generate no new warnings
  • My PR is too large (>500 lines or >10 files) and should be split into smaller PRs

Link to Devin run: https://app.devin.ai/sessions/0e475005d5fd4c30a7fcc9c464f091a3
Requested by: ali@cal.com (@alishaz-polymath)

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 3, 2026

Hey there and thank you for opening this pull request! 👋🏼

We require pull request titles to follow the Conventional Commits specification and it looks like your proposed title needs to be adjusted.

Details:

No release type found in pull request title "Chore/migrate booking requested webhook trigger". Add a prefix to indicate what kind of release this pull request corresponds to. For reference, see https://www.conventionalcommits.org/

Available types:
 - feat: A new feature
 - fix: A bug fix
 - docs: Documentation only changes
 - style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
 - refactor: A code change that neither fixes a bug nor adds a feature
 - perf: A code change that improves performance
 - test: Adding missing tests or correcting existing tests
 - build: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
 - ci: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
 - chore: Other changes that don't modify src or test files
 - revert: Reverts a previous commit

@alishaz-polymath alishaz-polymath changed the title Chore/migrate booking requested webhook trigger chore: migrate booking requested webhook trigger Feb 3, 2026
@keithwillcode keithwillcode added the core area: core, team members only label Feb 3, 2026
devin-ai-integration bot and others added 3 commits February 4, 2026 10:44
- Export IWebhookProducerService and getWebhookProducer from platform-libraries
- Add WEBHOOK_PRODUCER token and useFactory provider in RegularBookingModule
- Inject webhookProducer in RegularBookingService and pass to base class

This follows the composition root pattern where only the NestJS module
knows about getWebhookProducer(), and all consumers depend only on the
IWebhookProducerService interface via constructor injection.

Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
- Remove failing BOOKING_REQUESTED tests from fresh-booking.test.ts (4 tests)
- Remove failing BOOKING_REQUESTED tests from reschedule.test.ts (2 tests)
- Remove failing BOOKING_REQUESTED test from collective-scheduling.test.ts (1 test)
- Replace WebhookTaskConsumer.test.ts with placeholder (constructor changed)
- Create new webhook architecture test suite:
  - producer/WebhookTaskerProducerService.test.ts (14 tests)
  - consumer/WebhookTaskConsumer.test.ts (8 tests)
  - consumer/triggers/booking-requested.test.ts (8 tests)

The new test suite is organized by trigger type for extensibility as more
triggers are migrated to the producer/consumer pattern.

Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
@alishaz-polymath alishaz-polymath added run-ci Approve CI to run for external contributors ready-for-e2e labels Feb 4, 2026
@alishaz-polymath alishaz-polymath marked this pull request as ready for review February 4, 2026 14:20
@alishaz-polymath alishaz-polymath requested review from a team as code owners February 4, 2026 14:20
@graphite-app graphite-app bot added the enterprise area: enterprise, audit log, organisation, SAML, SSO label Feb 4, 2026
@graphite-app graphite-app bot requested a review from a team February 4, 2026 14:20
cubic-dev-ai[bot]

This comment was marked as resolved.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 4, 2026

E2E results are ready!

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Feb 4, 2026

Devin AI is addressing Cubic AI's review feedback

A Devin session has been created to address the issues identified by Cubic AI.

View Devin Session

@devin-ai-integration

This comment was marked as outdated.

@alishaz-polymath alishaz-polymath marked this pull request as draft February 4, 2026 14:41
devin-ai-integration[bot]

This comment was marked as resolved.

cubic-dev-ai[bot]

This comment was marked as resolved.

@github-actions
Copy link
Copy Markdown
Contributor

Devin AI is addressing Cubic AI's review feedback

A Devin session has been created to address the issues identified by Cubic AI.

View Devin Session

…bhook

Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
@devin-ai-integration

This comment was marked as outdated.

@alishaz-polymath alishaz-polymath added run-ci Approve CI to run for external contributors and removed run-ci Approve CI to run for external contributors labels Feb 17, 2026
Copy link
Copy Markdown
Contributor

@joeauyeung joeauyeung left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New implementation LGTM. Also tested this out and it's working as expected with the task running on Trigger and the webhook being sent.

Copy link
Copy Markdown
Contributor

@volnei volnei left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice architectural change! Loved it! I left a nit but could be done in follow up.

@alishaz-polymath alishaz-polymath enabled auto-merge (squash) February 19, 2026 12:19
@alishaz-polymath alishaz-polymath merged commit 5d65df9 into main Feb 19, 2026
56 checks passed
@alishaz-polymath alishaz-polymath deleted the chore/migrate-booking-requested-webhook-trigger branch February 19, 2026 16:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core area: core, team members only enterprise area: enterprise, audit log, organisation, SAML, SSO ready-for-e2e run-ci Approve CI to run for external contributors size/XXL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants