Skip to content

feat: Calendar Sync#24124

Merged
keithwillcode merged 81 commits intomainfrom
feat/calendar_sync
Feb 26, 2026
Merged

feat: Calendar Sync#24124
keithwillcode merged 81 commits intomainfrom
feat/calendar_sync

Conversation

@volnei
Copy link
Contributor

@volnei volnei commented Sep 27, 2025

What does this PR do?

Implements calendar sync functionality that allows Cal.com to sync booking changes from external calendars (Google Calendar, Office365) back to Cal.com. When a user cancels or reschedules a Cal.com booking directly in their external calendar, the change is synced back to Cal.com.

Key Changes

  1. Infinite Loop Prevention: Added flags to prevent infinite loops when calendar events are cancelled/rescheduled from external calendars:

    • skipCalendarSyncTaskCancellation flag in handleCancelBooking - skips calendar event deletion
    • skipCalendarSyncTaskCreation flag in handleNewBooking - skips calendar event creation (existing)

    The flow is:

    • User deletes/modifies event in Google Calendar
    • Google sends webhook to Cal.com
    • CalendarSyncService processes the change
    • handleCancelBooking/RegularBookingService is called with the appropriate skip flag set to true
    • This prevents Cal.com from trying to update the external calendar again
  2. CalendarSyncService Implementation:

    • cancelBooking(): Cancels a booking when the corresponding calendar event is deleted (uses handleCancelBooking)
    • rescheduleBooking(): Updates booking times when the calendar event is rescheduled (uses RegularBookingService.createBooking() directly with rescheduleUid)
    • Error handling: Errors are caught and logged but don't block the calendar sync operation
    • Uses safeStringify(error) for proper error serialization in logs
  3. Schema Update: Added skipCalendarSyncTaskCancellation to bookingCancelSchema in zod-utils.ts

  4. Booking Reference Consistency: Moved booking reference update (deleted: true) outside the skipCalendarSyncTaskCancellation conditional block - references are always marked as deleted for data consistency since the external event is already deleted

  5. Comprehensive Test Suite: Added 16 unit tests covering:

    • Event filtering (only Cal.com events processed)
    • Booking cancellation scenarios
    • Booking rescheduling scenarios
    • Edge cases (null UIDs, malformed UIDs, missing bookings)
    • Error handling (verifies errors are caught and logged, not thrown)

Updates since last revision

  • Added Sentry metrics telemetry to CalendarSyncService for observability:
    • calendar.sync.handleEvents.calls - tracks sync invocations by integration
    • calendar.sync.handleEvents.events_count - distribution of Cal.com events per sync
    • calendar.sync.cancelBooking.calls / calendar.sync.rescheduleBooking.calls - tracks success/error/skipped status
    • Duration metrics (duration_ms) for cancel and reschedule operations
  • Fixed null assertions by replacing ! operators with proper null checks:
    • booking.userId and booking.userPrimaryEmail checked before cancellation
    • booking.eventTypeId checked before rescheduling
    • Returns early with warning log and "skipped" metric when required data is missing
  • Fixed RAQB import error:
    • CalendarSyncService.ts: Changed getRegularBookingService to use dynamic import
    • getRoutedUsers.ts: Changed findTeamMembersMatchingAttributeLogic to use dynamic import (root cause fix)
    • This prevents react-awesome-query-builder from being loaded at module evaluation time, fixing the "Super expression must either be null or a function" error in the calendar-subscription webhook endpoint

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. N/A - internal implementation detail.
  • I confirm automated tests are in place that prove my fix is effective or that my feature works.

How should this be tested?

  1. Set up a calendar subscription webhook for Google Calendar or Office365
  2. Create a booking in Cal.com that syncs to the external calendar
  3. Delete the event directly in Google Calendar/Office365
  4. Verify the booking is cancelled in Cal.com without creating an infinite loop
  5. Similarly test rescheduling by modifying the event time in the external calendar

Human Review Checklist

  • Verify the hardcoded values in rescheduleBooking are acceptable: language: "en", metadata: {}, timeZone: event.timeZone ?? "UTC"
  • Verify that using rescheduleUid triggers a proper reschedule flow (not creating a duplicate booking)
  • Verify that removing allRemainingBookings/cancelSubsequentBookings is correct - now only cancels single bookings, not recurring series
  • Review edge cases where the skip flags might incorrectly skip legitimate calendar updates
  • Verify that silently catching errors in CalendarSyncService (logging but not throwing) is acceptable behavior
  • Verify the dynamic imports in CalendarSyncService.ts and getRoutedUsers.ts don't introduce any timing/race condition issues

Checklist

  • My code follows the style guidelines of this project
  • I have commented my code, particularly in hard-to-understand areas
  • I have checked if my changes generate no new warnings

Link to Devin run: https://app.devin.ai/sessions/20575cfa274d4721bc8badeda938cc93
Requested by: Volnei Munhoz (volnei@cal.com) (@volnei)


Open with Devin

volnei and others added 30 commits September 16, 2025 14:38
- Add tests for /api/cron/calendar-subscriptions-cleanup route (9 tests)
- Add tests for /api/cron/calendar-subscriptions route (10 tests)
- Add tests for /api/webhooks/calendar-subscription/[provider] route (11 tests)
- Add missing feature flags for calendar-subscription-cache and calendar-subscription-sync
- All 30 tests pass with comprehensive coverage of authentication, feature flags, error handling, and service instantiation

Tests cover:
- Authentication scenarios (API key validation, Bearer tokens, query parameters)
- Feature flag combinations (cache/sync enabled/disabled states)
- Success and error handling (including non-Error exceptions)
- Service instantiation with proper dependency injection
- Provider validation for webhook endpoints

Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
…ositories, and adapters

- Add unit tests for CalendarSubscriptionService with subscription, webhook, and event processing
- Add unit tests for CalendarCacheEventService with cache operations and cleanup
- Add unit tests for CalendarSyncService with Cal.com event filtering and booking operations
- Add unit tests for CalendarCacheEventRepository with CRUD operations
- Add unit tests for SelectedCalendarRepository with calendar selection management
- Add unit tests for GoogleCalendarSubscriptionAdapter with subscription and event fetching
- Add unit tests for Office365CalendarSubscriptionAdapter with placeholder implementation
- Add unit tests for AdaptersFactory with provider management and adapter creation
- Fix lint issues by removing explicit 'any' type casting and unused variables
- All tests follow Cal.com conventions using Vitest framework with proper mocking

Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
…g missing mocks

- Add comprehensive mocks for defaultResponderForAppDir, logger, performance monitoring, and Sentry
- Fix slow test execution (933ms -> <100ms) caused by missing dependency mocks
- Ensure consistent test performance across different environments

Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
devin-ai-integration bot and others added 3 commits January 20, 2026 14:39
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
…rSyncService

Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
…port in server context

Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
@github-actions
Copy link
Contributor

github-actions bot commented Jan 20, 2026

E2E results are ready!

devin-ai-integration bot and others added 2 commits January 20, 2026 18:28
…avoid RAQB import in server context

Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
Copy link
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 potential issue.

View issue and 5 additional flags in Devin Review.

Open in Devin Review

@volnei volnei marked this pull request as ready for review January 23, 2026 17:01
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

1 issue found across 5 files

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="packages/features/bookings/lib/handleCancelBooking.ts">

<violation number="1" location="packages/features/bookings/lib/handleCancelBooking.ts:623">
P2: Avoid logging raw ZodError objects; log only sanitized fields (message/name) to prevent leaking sensitive data.

(Based on your team's feedback about not logging raw ZodError objects.) [FEEDBACK_USED]</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@github-actions
Copy link
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

volnei and others added 3 commits February 18, 2026 08:51
Add actionSource: "SYSTEM" to handleCancelBooking call after it became
required, and mock @sentry/nextjs in test files.
@keithwillcode keithwillcode added this to the v6.3 milestone Feb 26, 2026
@keithwillcode keithwillcode merged commit 5242e41 into main Feb 26, 2026
46 checks passed
@keithwillcode keithwillcode deleted the feat/calendar_sync branch February 26, 2026 16:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants