Skip to content

Feat/calendar sync#6

Open
DhirenMhatre wants to merge 74 commits into
mainfrom
feat/calendar_sync
Open

Feat/calendar sync#6
DhirenMhatre wants to merge 74 commits into
mainfrom
feat/calendar_sync

Conversation

@DhirenMhatre

Copy link
Copy Markdown

What does this PR do?

  • Fixes #XXXX (GitHub issue number)
  • Fixes CAL-XXXX (Linear issue number - should be visible at the bottom of the GitHub issue description)

Visual Demo (For contributors especially)

A visual demonstration is strongly recommended, for both the original and new change (video / image - any one).

Video Demo (if applicable):

  • Show screen recordings of the issue or feature.
  • Demonstrate how to reproduce the issue, the behavior before and after the change.

Image Demo (if applicable):

  • Add side-by-side screenshots of the original and updated change.
  • Highlight any significant change(s).

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?

  • Are there environment variables that should be set?
  • What are the minimal test data to have?
  • What is expected (happy path) to have (input and output)?
  • Any other important info that could help to test that PR

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

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>
@DhirenMhatre

Copy link
Copy Markdown
Author

@codity review

@codity-dm

codity-dm Bot commented May 24, 2026

Copy link
Copy Markdown

Policy Check Failed

✗ 2/3 policy checks failed:

• Need 2 more approval(s) (0/2) — comment LGTM or approve via review
• Missing ticket reference (expected: JIRA-, ENG-, #*)

✓ 1 checks passed:
• Test files included (1 test file(s))


To merge this PR:

  1. Address the failed checks listed above
  2. Ensure branch protection requires the codity/policy-check status

Configure policies in your dashboard

@codity-dm

codity-dm Bot commented May 24, 2026

Copy link
Copy Markdown

PR Summary

What Changed

  • Added bidirectional calendar sync to process booking cancellations and reschedules from external calendars (Google/Office365).
  • Introduced loop prevention flags to stop infinite sync cycles when changes originate from calendar webhooks.
  • Added comprehensive test coverage for sync service edge cases.

Key Changes by Area

Calendar Sync: New CalendarSyncService processes external calendar events with @cal.com iCalUIDs, cancels bookings on deletion, reschedules on moves.

Booking Cancellation: handleCancelBooking.ts now supports skipCalendarSyncTaskCancellation flag to skip calendar event deletion when cancellation comes from external sync.

Schema: Added skipCalendarSyncTaskCancellation to bookingCancelSchema in zod-utils.ts and new domain types in BookingCancel.d.ts.

Files Changed

File Changes Summary
packages/features/bookings/lib/handleCancelBooking.ts Added skipCalendarSyncTaskCancellation flag; separated calendar deletion from reference cleanup
packages/features/calendar-subscription/lib/sync/CalendarSyncService.ts New service with cancelBooking() and rescheduleBooking() methods; includes Sentry metrics
packages/features/calendar-subscription/lib/sync/tests/CalendarSyncService.test.ts Unit tests for event filtering, cancellation/rescheduling flows, and error handling
packages/prisma/zod-utils.ts Added skipCalendarSyncTaskCancellation to bookingCancelSchema

Review Focus Areas

  • Loop prevention logic: verify flags correctly prevent infinite sync cycles in both directions.
  • Data consistency: check that skipping calendar deletion does not leave orphaned references.
  • Error handling in CalendarSyncService: malformed UIDs and missing bookings should fail gracefully.

Architecture

Design Decisions: The explicit flag pattern (skipCalendarSyncTaskCancellation / skipCalendarSyncTaskCreation) was chosen over implicit detection to make sync boundaries explicit and debuggable. This trades some verbosity for clarity on which direction initiated the change.

Scalability & Extensibility: Out of scope. Current implementation handles Google/Office365 only. Adding new calendar providers requires extending CalendarSyncService event parsing logic.

Risks:

  • Intentional: Flags rely on caller discipline. Missing flags will cause loops.
  • Unintentional: Sentry metrics added but no alerting thresholds defined. Monitor for sync lag.

Merge Status

MERGEABLE — PR Score 71/100, above threshold (50). All gates passed.

Comment on lines +105 to +115
await handleCancelBooking({
userId: booking.userId,
bookingData: {
uid: booking.uid,
cancellationReason: "Cancelled on user's calendar",
cancelledBy: booking.userPrimaryEmail,
// Skip calendar event deletion to avoid infinite loops
// (Google/Office365 → Cal.com → Google/Office365 → ...)
skipCalendarSyncTaskCancellation: true,
},
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Functional Medium

handleCancelBooking is called without an actionSource, so it defaults to "UNKNOWN" inside the handler. The handler explicitly checks for this and emits log.warn("Booking cancellation with unknown actionSource", ...) on every single calendar-sync cancellation. "WEBHOOK" is the closest semantic match in the existing ActionSource enum (API_V1 | API_V2 | WEBAPP | WEBHOOK | MAGIC_LINK | UNKNOWN) and should be passed here so audit records are correctly attributed and the warning is not emitted.

Suggested fix
      await handleCancelBooking({
        userId: booking.userId,
        actionSource: "WEBHOOK",
        bookingData: {
          uid: booking.uid,
          cancellationReason: "Cancelled on user's calendar",
          cancelledBy: booking.userPrimaryEmail,
          // Skip calendar event deletion to avoid infinite loops
          // (Google/Office365 → Cal.com → Google/Office365 → ...)
          skipCalendarSyncTaskCancellation: true,
        },
      });
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert typescript developer with deep knowledge of security, performance, and best practices.

### Context

File: packages/features/calendar-subscription/lib/sync/CalendarSyncService.ts
Lines: 105-115
Issue Type: functional-medium
Severity: medium

Issue Description:
`handleCancelBooking` is called without an `actionSource`, so it defaults to `"UNKNOWN"` inside the handler. The handler explicitly checks for this and emits `log.warn("Booking cancellation with unknown actionSource", ...)` on every single calendar-sync cancellation. `"WEBHOOK"` is the closest semantic match in the existing `ActionSource` enum (`API_V1 | API_V2 | WEBAPP | WEBHOOK | MAGIC_LINK | UNKNOWN`) and should be passed here so audit records are correctly attributed and the warning is not emitted.

Current Code:
      await handleCancelBooking({
        userId: booking.userId,
        bookingData: {
          uid: booking.uid,
          cancellationReason: "Cancelled on user's calendar",
          cancelledBy: booking.userPrimaryEmail,
          // Skip calendar event deletion to avoid infinite loops
          // (Google/Office365 → Cal.com → Google/Office365 → ...)
          skipCalendarSyncTaskCancellation: true,
        },
      });

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow typescript best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue

import logger from "@calcom/lib/logger";
import { safeStringify } from "@calcom/lib/safeStringify";
import type { SelectedCalendar } from "@calcom/prisma/client";
import { metrics } from "@sentry/nextjs";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Maintainability Medium

CalendarSyncService lives in a shared packages/features package but statically imports { metrics } from @sentry/nextjs, a Next.js-specific SDK. This hard-couples the module to the Next.js runtime and will fail to initialize correctly (or outright throw) when the package is loaded in a standalone Node.js server, a background worker, or any non-Next.js consumer. The same file already avoids this problem for RegularBookingService.container via a dynamic import() — the Sentry metrics import should follow the same pattern or use the provider-agnostic @sentry/core package.

Suggested change
import { metrics } from "@sentry/nextjs";
import { metrics } from "@sentry/core";
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert typescript developer with deep knowledge of security, performance, and best practices.

### Context

File: packages/features/calendar-subscription/lib/sync/CalendarSyncService.ts
Lines: 7-7
Issue Type: maintainability-medium
Severity: medium

Issue Description:
`CalendarSyncService` lives in a shared `packages/features` package but statically imports `{ metrics }` from `@sentry/nextjs`, a Next.js-specific SDK. This hard-couples the module to the Next.js runtime and will fail to initialize correctly (or outright throw) when the package is loaded in a standalone Node.js server, a background worker, or any non-Next.js consumer. The same file already avoids this problem for `RegularBookingService.container` via a dynamic `import()` — the Sentry metrics import should follow the same pattern or use the provider-agnostic `@sentry/core` package.

Current Code:
import { metrics } from "@sentry/nextjs";

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow typescript best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue

@codity-dm

codity-dm Bot commented May 24, 2026

Copy link
Copy Markdown

Security Scan Summary

Metric Value
Vulnerabilities Critical: 0
Overall Risk Clean
Files Scanned 4

No critical security issues detected

Scan completed in 12.2s

Security scan powered by Codity.ai

@codity-dm

codity-dm Bot commented May 24, 2026

Copy link
Copy Markdown

License Compliance Scan

Metric Value
Packages Scanned 448
High Risk (Strong Copyleft) 0
Medium Risk (Weak Copyleft) 5
Low Risk (Permissive) 399
Unknown License 44

Weak copyleft licenses found - verify compatibility

Some packages have unknown licenses - manual review required

Medium Risk Licenses - 5 packages

(MPL-2.0 OR Apache-2.0) (1 packages):

  • dompurify 3.3.1

MPL-2.0 (4 packages):

  • @vercel/og 0.6.3
  • ical.js 1.5.0
  • web-push 3.6.7
  • @dub/analytics 0.0.27
Unknown Licenses - 44 packages
  • @calcom/atoms workspace:*
  • @calcom/config workspace:*
  • @calcom/dayjs workspace:*
  • @calcom/tsconfig workspace:*
  • @calcom/types workspace:*
  • @calcom/dailyvideo workspace:*
  • @calcom/lib workspace:*
  • @calcom/features workspace:*
  • @calcom/ui workspace:*
  • @calcom/zoomvideo workspace:*
  • @calcom/testing workspace:*
  • @calcom/prisma workspace:*
  • @calcom/office365video workspace:*
  • @calcom/emails workspace:*
  • @calcom/app-store workspace:*
  • dub-package npm:dub@^0.61.12
  • @calcom/lib *
  • @calcom/types *
  • @calcom/platform-constants workspace:*
  • @radix-ui/react-dialog-atoms npm:@radix-ui/react-dialog@1.0.4

...and 24 more

Powered by Codity.ai · Docs

@codity-dm

codity-dm Bot commented May 24, 2026

Copy link
Copy Markdown

Code Quality Report — test-org-codity/cal.com · PR #6

Scanned: 2026-05-24 19:49 UTC | Score: 52/100 | Provider: github

Executive Summary

Severity Count
Critical 0
High 1
Medium 3
Low 15
Top Findings

[CQ-LLM-001] packages/features/bookings/lib/handleCancelBooking.ts:162 (Complexity · HIGH)

Issue: The handler function has increased cyclomatic complexity due to additional conditional logic for skipCalendarSyncTaskCancellation.
Suggestion: Consider refactoring the handler function to reduce complexity, possibly by extracting parts of the logic into smaller functions.

if (!skipCalendarSyncTaskCancellation) { ... }

[CQ-LLM-002] packages/features/bookings/lib/handleCancelBooking.ts:611 (Error_Handling · MEDIUM)

Issue: Swallowed exceptions in the try-catch block for event cancellation without proper handling or rethrowing.
Suggestion: Consider rethrowing the error or handling it in a way that allows for better visibility of issues.

catch (error) { log.error(`Error deleting integrations`, safeStringify({ error })); }

[CQ-LLM-003] packages/features/calendar-subscription/lib/sync/CalendarSyncService.ts:75 (Documentation · MEDIUM)

Issue: Missing documentation for the cancelBooking method, which is a public API.
Suggestion: Add JSDoc comments to describe the purpose and parameters of the cancelBooking method.

async cancelBooking(event: CalendarSubscriptionEventItem) { ... }

[CQ-LLM-004] packages/features/calendar-subscription/lib/sync/CalendarSyncService.ts:139 (Testability · MEDIUM)

Issue: Dynamic import of RegularBookingService can lead to issues with testability and dependency management.
Suggestion: Consider using dependency injection to manage service dependencies instead of dynamic imports.

const { getRegularBookingService } = await import(...);

[CQ-LLM-005] packages/features/calendar-subscription/lib/sync/CalendarSyncService.ts:34 (Maintainability · LOW)

Issue: The use of magic strings in metrics attributes can lead to maintainability issues.
Suggestion: Define constants for metric attribute keys to avoid typos and improve maintainability.

attributes: { status: 'success' },

[CQ-009] packages/features/calendar-subscription/lib/sync/__tests__/CalendarSyncService.test.ts:3 (Style · LOW)

Issue: Line exceeds 120 characters (131 chars)
Suggestion: Break long lines into multiple lines for readability

import type { CalendarSubscriptionEventItem } from "@calcom/features/calendar-subscription/lib/CalendarSubscriptionPort....

[CQ-008] packages/features/calendar-subscription/lib/sync/__tests__/CalendarSyncService.test.ts:49 (Maintainability · LOW)

Issue: Magic number 86400000 in code
Suggestion: Extract to a named constant

channelExpiration: new Date(Date.now() + 86400000),

[CQ-008] packages/features/calendar-subscription/lib/sync/__tests__/CalendarSyncService.test.ts:62 (Maintainability · LOW)

Issue: Magic number 2023 in code
Suggestion: Extract to a named constant

startTime: new Date("2023-12-01T10:00:00Z"),

[CQ-008] packages/features/calendar-subscription/lib/sync/__tests__/CalendarSyncService.test.ts:63 (Maintainability · LOW)

Issue: Magic number 2023 in code
Suggestion: Extract to a named constant

endTime: new Date("2023-12-01T11:00:00Z"),

[CQ-008] packages/features/calendar-subscription/lib/sync/__tests__/CalendarSyncService.test.ts:74 (Maintainability · LOW)

Issue: Magic number 2023 in code
Suggestion: Extract to a named constant

start: new Date("2023-12-01T10:00:00Z"),

Per-File Breakdown

File Critical High Medium Low Total
packages/features/bookings/lib/handleCancelBooking.ts 0 1 1 0 2
packages/features/calendar-subscription/lib/sync/CalendarSyncService.ts 0 0 2 1 3
packages/features/calendar-subscription/lib/sync/__tests__/CalendarSyncService.test.ts 0 0 0 14 14

Recommendations

  1. Resolve High severity issues, especially error handling gaps and performance bottlenecks.
  • Run automated tests after applying fixes to verify no regressions.

@DhirenMhatre

Copy link
Copy Markdown
Author

@codity review

@codity-dm

codity-dm Bot commented May 25, 2026

Copy link
Copy Markdown

Policy Check Failed

✗ 2/3 policy checks failed:

• Need 2 more approval(s) (0/2) — comment LGTM or approve via review
• Missing ticket reference (expected: JIRA-, ENG-, #*)

✓ 1 checks passed:
• Test files included (1 test file(s))


To merge this PR:

  1. Address the failed checks listed above
  2. Ensure branch protection requires the codity/policy-check status

Configure policies in your dashboard

@codity-dm

codity-dm Bot commented May 25, 2026

Copy link
Copy Markdown

PR review started! Estimated time: 5-10 minutes.
Using default review instructions (no custom configuration found)

Learn More

View Analytics Dashboard

Ask Codity questions: Mention @codity {your question} in a comment to get answers about the code.

Trigger a manual review: Comment @codity review on a PR or MR.

Generate unit tests: Comment /generate-tests to auto-generate tests for Go, Python, Ruby, JavaScript, TypeScript, and Java files.

Run security scan again: Comment /security-scan to run SAST and dependency vulnerability scans for all major languages in your repo.

View Full Docs

Comment on lines +14 to +22
vi.mock("@calcom/features/bookings/lib/handleCancelBooking", () => ({
default: mockHandleCancelBooking,
}));

vi.mock("@calcom/features/bookings/di/RegularBookingService.container", () => ({
getRegularBookingService: () => ({
createBooking: mockCreateBooking,
}),
}));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Functional High

CalendarSyncService calls metrics.count(...) and metrics.distribution(...) from @sentry/nextjs as the very first statement inside handleEvents, cancelBooking, and rescheduleBooking — before any try/catch. The sibling test file CalendarSubscriptionService.test.ts already carries a vi.mock('@sentry/nextjs', ...) for exactly this reason, but this new test file omits it entirely. Without the mock, every test that invokes any of those three methods will throw TypeError: Cannot read properties of undefined (reading 'count') and the entire suite will fail at runtime.

Suggested fix
vi.mock("@calcom/features/bookings/lib/handleCancelBooking", () => ({
  default: mockHandleCancelBooking,
}));

vi.mock("@calcom/features/bookings/di/RegularBookingService.container", () => ({
  getRegularBookingService: () => ({
    createBooking: mockCreateBooking,
  }),
}));

vi.mock("@sentry/nextjs", () => ({
  metrics: {
    count: vi.fn(),
    distribution: vi.fn(),
  },
}));
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert typescript developer with deep knowledge of security, performance, and best practices.

### Context

File: packages/features/calendar-subscription/lib/sync/__tests__/CalendarSyncService.test.ts
Lines: 14-22
Issue Type: functional-high
Severity: high

Issue Description:
`CalendarSyncService` calls `metrics.count(...)` and `metrics.distribution(...)` from `@sentry/nextjs` as the very first statement inside `handleEvents`, `cancelBooking`, and `rescheduleBooking` — before any try/catch. The sibling test file `CalendarSubscriptionService.test.ts` already carries a `vi.mock('@sentry/nextjs', ...)` for exactly this reason, but this new test file omits it entirely. Without the mock, every test that invokes any of those three methods will throw `TypeError: Cannot read properties of undefined (reading 'count')` and the entire suite will fail at runtime.

Current Code:
vi.mock("@calcom/features/bookings/lib/handleCancelBooking", () => ({
  default: mockHandleCancelBooking,
}));

vi.mock("@calcom/features/bookings/di/RegularBookingService.container", () => ({
  getRegularBookingService: () => ({
    createBooking: mockCreateBooking,
  }),
}));

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow typescript best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue

Comment on lines 86 to 90
const booking = await this.deps.bookingRepository.findBookingByUidWithEventType({ bookingUid });
if (!booking) {
log.debug("Unable to sync, booking not found");
log.debug("Unable to sync, booking not found in database", { bookingUid });
return;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Robustness Medium

The bookingRepository.findBookingByUidWithEventType call in cancelBooking (and its mirror in rescheduleBooking at line ~152) sits outside the try/catch block. When this DB call throws — e.g. on a transient connection error — the exception propagates up to Promise.all in handleEvents. Promise.all fails fast on the first rejection, so any other calendar events in the same batch that haven't started yet are silently skipped. A single flaky DB call can therefore cause an entire sync batch to be partially applied with no retry and no per-event error logged.

Suggested fix
    let booking;
    try {
      booking = await this.deps.bookingRepository.findBookingByUidWithEventType({ bookingUid });
    } catch (error) {
      log.error("Unable to sync, failed to fetch booking from database", {
        bookingUid,
        error: safeStringify(error),
      });
      return;
    }
    if (!booking) {
      log.debug("Unable to sync, booking not found in database", { bookingUid });
      return;
    }
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert typescript developer with deep knowledge of security, performance, and best practices.

### Context

File: packages/features/calendar-subscription/lib/sync/CalendarSyncService.ts
Lines: 86-90
Issue Type: robustness-medium
Severity: medium

Issue Description:
The `bookingRepository.findBookingByUidWithEventType` call in `cancelBooking` (and its mirror in `rescheduleBooking` at line ~152) sits outside the try/catch block. When this DB call throws — e.g. on a transient connection error — the exception propagates up to `Promise.all` in `handleEvents`. `Promise.all` fails fast on the first rejection, so any other calendar events in the same batch that haven't started yet are silently skipped. A single flaky DB call can therefore cause an entire sync batch to be partially applied with no retry and no per-event error logged.

Current Code:
    const booking = await this.deps.bookingRepository.findBookingByUidWithEventType({ bookingUid });
    if (!booking) {
      log.debug("Unable to sync, booking not found in database", { bookingUid });
      return;
    }

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow typescript best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue

@DhirenMhatre

Copy link
Copy Markdown
Author

@codity review

@codity-dm

codity-dm Bot commented May 31, 2026

Copy link
Copy Markdown

Policy Check Failed

✗ 2/3 policy checks failed:

• Need 2 more approval(s) (0/2) — comment LGTM or approve via review
• Missing ticket reference (expected: JIRA-, ENG-, #*)

✓ 1 checks passed:
• Test files included (1 test file(s))


To merge this PR:

  1. Address the failed checks listed above
  2. Ensure branch protection requires the codity/policy-check status

Configure policies in your dashboard

@codity-dm

codity-dm Bot commented May 31, 2026

Copy link
Copy Markdown

PR Summary

What Changed

  • Added bidirectional calendar sync so Cal.com can process booking cancellations and reschedules that originate from external calendars (Google/Office365).
  • Added loop-prevention flags to stop infinite sync cycles when changes flow back from calendar webhooks.
  • Added comprehensive test coverage for the new sync service.

Key Changes by Area

Calendar Sync Service: New CalendarSyncService that processes incoming calendar events, identifies Cal.com bookings via iCalUID, and triggers appropriate cancellation or rescheduling with loop-prevention flags.

Booking Cancellation: Updated handleCancelBooking to accept skipCalendarSyncTaskCancellation flag. New type definitions in BookingCancel.d.ts support structured cancellation data.

Schema Validation: Added skipCalendarSyncTaskCancellation to bookingCancelSchema in zod-utils.

Files Changed

File Changes Summary
packages/features/bookings/lib/handleCancelBooking.ts Added skipCalendarSyncTaskCancellation flag to prevent infinite loops
packages/features/calendar-subscription/lib/sync/CalendarSyncService.ts New service with cancelBooking() and rescheduleBooking() methods, Sentry metrics
packages/features/calendar-subscription/lib/sync/tests/CalendarSyncService.test.ts New test suite for event filtering, cancellation, and rescheduling flows
packages/prisma/zod-utils.ts Added skipCalendarSyncTaskCancellation to bookingCancelSchema
packages/features/bookings/lib/dto/BookingCancel.d.ts New types: CancelRegularBookingData and CancelBookingMeta

Review Focus Areas

  • Missing actionSource in cancellations: CalendarSyncService.ts:105-115 calls handleCancelBooking without actionSource, causing warn logs on every sync cancellation. Check if this is intentional or should pass a dedicated source like "CALENDAR_SYNC".
  • Loop prevention logic: Verify the interaction between skipCalendarSyncTaskCancellation and skipCalendarSyncTaskCreation flags covers all edge cases in bidirectional flows.

Architecture

Design Decisions: The sync service uses iCalUID suffix matching to identify Cal.com bookings rather than external IDs. This is a pragmatic choice given iCal format constraints, though it assumes Cal.com UID conventions are stable.

Risks:

  • Intentional (acceptable): Missing actionSource causes noisy warn logs but no functional impact. The handler defaults to "UNKNOWN" and continues processing.
  • Unintentional: UID suffix matching could misidentify events if external calendars copy or mirror Cal.com UIDs. The test coverage does not include collision scenarios.

Merge Status

MERGEABLE — PR Score 55/100, above threshold (50). All gates passed.

cancelSubsequentBookings: z.boolean().optional(),
cancellationReason: z.string().optional(),
skipCancellationReasonValidation: z.boolean().optional(),
skipCalendarSyncTaskCancellation: z.boolean().optional(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Security High

The skipCalendarSyncTaskCancellation flag is added to bookingCancelSchema, which is extended by bookingCancelWithCsrfSchema (line 415). That CSRF schema is parsed verbatim in apps/web/app/api/cancel/route.ts (line 23) and the resulting object is passed directly to handleCancelBooking without stripping internal fields. Any authenticated user with a valid CSRF token can therefore POST { uid: "...", csrfToken: "...", skipCalendarSyncTaskCancellation: true } and bypass calendar event deletion entirely: causing their provider calendar (Google/Office365) to retain the event while Cal.com marks the booking cancelled and its references deleted. The flag was designed as an internal circuit-breaker, but it has leaked into the public API surface. Fix: remove the field from bookingCancelSchema and instead thread it through the CancelBookingInput TypeScript type (which is never directly serialized from user input) so it cannot be supplied by external callers.

Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert bash developer with deep knowledge of security, performance, and best practices.

### Context

File: packages/prisma/zod-utils.ts
Lines: 393-393
Issue Type: security-high
Severity: high

Issue Description:
The `skipCalendarSyncTaskCancellation` flag is added to `bookingCancelSchema`, which is extended by `bookingCancelWithCsrfSchema` (line 415). That CSRF schema is parsed verbatim in `apps/web/app/api/cancel/route.ts` (line 23) and the resulting object is passed directly to `handleCancelBooking` without stripping internal fields. Any authenticated user with a valid CSRF token can therefore POST `{ uid: "...", csrfToken: "...", skipCalendarSyncTaskCancellation: true }` and bypass calendar event deletion entirely: causing their provider calendar (Google/Office365) to retain the event while Cal.com marks the booking cancelled and its references deleted. The flag was designed as an internal circuit-breaker, but it has leaked into the public API surface. Fix: remove the field from `bookingCancelSchema` and instead thread it through the `CancelBookingInput` TypeScript type (which is never directly serialized from user input) so it cannot be supplied by external callers.

Current Code:
  skipCalendarSyncTaskCancellation: z.boolean().optional(),

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow bash best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue Jira

Comment on lines +650 to 654
try {
await bookingReferenceRepository.updateManyByBookingId(bookingToDelete.id, { deleted: true });
} catch (error) {
log.error(`Error deleting integrations`, safeStringify({ error }));
log.error(`Error marking booking references as deleted`, safeStringify({ error }));
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Functional Medium

In the original code, bookingReferenceRepository.updateManyByBookingId(..., { deleted: true }) lived inside the same try block as eventManager.cancelEvent. If cancelEvent threw (e.g. network timeout, OAuth revocation), the catch swallowed the error and references were NOT marked deleted, leaving them in a state where a retry could re-attempt the calendar API call. This refactor moves reference deletion into a separate always-executed try/catch, so references are now marked deleted: true even when cancelEvent fails. The comment at line 649 justifies this only for the sync path ("external event is already deleted"), but the block also runs on the normal cancellation path where the calendar event may genuinely still exist on the provider. A failed calendar delete followed by a reference being marked deleted creates a phantom orphaned calendar event that cannot be cleaned up through normal retry paths. The behavioural change should either be explicitly bounded to skipCalendarSyncTaskCancellation === true, or the comment should acknowledge and justify the normal-path change.

Suggested fix
  if (skipCalendarSyncTaskCancellation) {
    // Sync path: external event is already deleted on the provider calendar;
    // always mark references deleted for consistency.
    try {
      await bookingReferenceRepository.updateManyByBookingId(bookingToDelete.id, { deleted: true });
    } catch (error) {
      log.error(`Error marking booking references as deleted`, safeStringify({ error }));
    }
  }
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert bash developer with deep knowledge of security, performance, and best practices.

### Context

File: packages/features/bookings/lib/handleCancelBooking.ts
Lines: 650-654
Issue Type: functional-medium
Severity: medium

Issue Description:
In the original code, `bookingReferenceRepository.updateManyByBookingId(..., { deleted: true })` lived inside the same try block as `eventManager.cancelEvent`. If `cancelEvent` threw (e.g. network timeout, OAuth revocation), the catch swallowed the error and references were NOT marked deleted, leaving them in a state where a retry could re-attempt the calendar API call. This refactor moves reference deletion into a separate always-executed try/catch, so references are now marked `deleted: true` even when `cancelEvent` fails. The comment at line 649 justifies this only for the sync path ("external event is already deleted"), but the block also runs on the normal cancellation path where the calendar event may genuinely still exist on the provider. A failed calendar delete followed by a reference being marked deleted creates a phantom orphaned calendar event that cannot be cleaned up through normal retry paths. The behavioural change should either be explicitly bounded to `skipCalendarSyncTaskCancellation === true`, or the comment should acknowledge and justify the normal-path change.

Current Code:
  try {
    await bookingReferenceRepository.updateManyByBookingId(bookingToDelete.id, { deleted: true });
  } catch (error) {
    log.error(`Error marking booking references as deleted`, safeStringify({ error }));
  }

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow bash best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue Jira

});
} catch (error) {
// Log error but don't block - calendar change should still be reflected
log.error("Failed to cancel booking from calendar sync", { bookingUid, error: safeStringify(error) });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Robustness Medium

handleCancelBooking throws HttpError (4xx) for several expected, non-fatal conditions that will commonly occur during calendar sync: "This booking has already been cancelled" (idempotent re-delivery of the same webhook), "Cannot cancel a booking that has already ended" (user deletes a past event), "This event type does not allow cancellations". All of these are caught here and emitted as log.error, indistinguishable from genuine system failures (DB down, unhandled exception). Operators monitoring error-level logs or Sentry issues will see constant noise from routine sync scenarios, masking real failures and causing alert fatigue. The catch should distinguish 4xx HttpError instances (log at warn/info) from actual system errors (log at error).

Suggested fix
        const isExpectedClientError =
          typeof (error as { statusCode?: number })?.statusCode === "number" &&
          (error as { statusCode: number }).statusCode >= 400 &&
          (error as { statusCode: number }).statusCode < 500;
        if (isExpectedClientError) {
          log.warn("Calendar sync cancellation skipped due to booking validation", {
            bookingUid,
            message: String((error as Error).message),
          });
        } else {
          log.error("Failed to cancel booking from calendar sync", { bookingUid, error: safeStringify(error) });
        }
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert bash developer with deep knowledge of security, performance, and best practices.

### Context

File: packages/features/calendar-subscription/lib/sync/CalendarSyncService.ts
Lines: 126-126
Issue Type: robustness-medium
Severity: medium

Issue Description:
`handleCancelBooking` throws `HttpError` (4xx) for several expected, non-fatal conditions that will commonly occur during calendar sync: "This booking has already been cancelled" (idempotent re-delivery of the same webhook), "Cannot cancel a booking that has already ended" (user deletes a past event), "This event type does not allow cancellations". All of these are caught here and emitted as `log.error`, indistinguishable from genuine system failures (DB down, unhandled exception). Operators monitoring error-level logs or Sentry issues will see constant noise from routine sync scenarios, masking real failures and causing alert fatigue. The catch should distinguish 4xx `HttpError` instances (log at warn/info) from actual system errors (log at error).

Current Code:
        log.error("Failed to cancel booking from calendar sync", { bookingUid, error: safeStringify(error) });

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow bash best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue Jira

start: event.start?.toISOString() ?? booking.startTime.toISOString(),
end: event.end?.toISOString() ?? booking.endTime.toISOString(),
timeZone: event.timeZone ?? "UTC",
language: "en",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Functional Medium

rescheduleBooking passes a hardcoded language: "en" to createBooking. The booking service uses this language to look up translations for confirmation emails sent to all attendees. Attendees of non-English bookings will receive reschedule notifications in English regardless of their locale preference. The user's locale is available on the booking object (if selected in the repository query) or can default to the event's attendee locale. At minimum, this should fall back to the booking owner's locale rather than always forcing English.

Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert bash developer with deep knowledge of security, performance, and best practices.

### Context

File: packages/features/calendar-subscription/lib/sync/CalendarSyncService.ts
Lines: 177-177
Issue Type: functional-medium
Severity: medium

Issue Description:
`rescheduleBooking` passes a hardcoded `language: "en"` to `createBooking`. The booking service uses this language to look up translations for confirmation emails sent to all attendees. Attendees of non-English bookings will receive reschedule notifications in English regardless of their locale preference. The user's locale is available on the `booking` object (if selected in the repository query) or can default to the event's attendee locale. At minimum, this should fall back to the booking owner's locale rather than always forcing English.

Current Code:
          language: "en",

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow bash best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue Jira

import logger from "@calcom/lib/logger";
import { safeStringify } from "@calcom/lib/safeStringify";
import type { SelectedCalendar } from "@calcom/prisma/client";
import { metrics } from "@sentry/nextjs";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Maintainability Medium

CalendarSyncService lives in packages/features/calendar-subscription/, a shared feature package that is (or may be) consumed from workers, cron jobs, or other non-Next.js runtimes. Importing { metrics } from @sentry/nextjs rather than a framework-agnostic package like @sentry/core couples this service to the Next.js Sentry integration. @sentry/nextjs monkey-patches Next.js internals at import time and may fail or behave unpredictably outside a Next.js request context. Using the framework-neutral @sentry/core (which @sentry/nextjs re-exports for this purpose) removes the runtime constraint.

Suggested change
import { metrics } from "@sentry/nextjs";
import { metrics } from "@sentry/core";
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert bash developer with deep knowledge of security, performance, and best practices.

### Context

File: packages/features/calendar-subscription/lib/sync/CalendarSyncService.ts
Lines: 7-7
Issue Type: maintainability-medium
Severity: medium

Issue Description:
`CalendarSyncService` lives in `packages/features/calendar-subscription/`, a shared feature package that is (or may be) consumed from workers, cron jobs, or other non-Next.js runtimes. Importing `{ metrics }` from `@sentry/nextjs` rather than a framework-agnostic package like `@sentry/core` couples this service to the Next.js Sentry integration. `@sentry/nextjs` monkey-patches Next.js internals at import time and may fail or behave unpredictably outside a Next.js request context. Using the framework-neutral `@sentry/core` (which `@sentry/nextjs` re-exports for this purpose) removes the runtime constraint.

Current Code:
import { metrics } from "@sentry/nextjs";

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow bash best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue Jira

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Performance Medium

Promise.all fans out all filtered Cal.com events concurrently with no concurrency cap. A calendar diff with 50: 100 changed events (e.g. after a long offline period or a full re-sync) launches that many simultaneous handleCancelBooking or createBooking calls, each of which opens multiple Prisma queries, sends emails, fires webhooks, and calls third-party calendar APIs. This can exhaust the Prisma connection pool, trigger rate-limits on downstream services, and spike DB load for every subscriber that syncs at the same time. Sequential processing or a bounded concurrency limit (e.g. p-limit with 5 or less) would keep throughput acceptable while protecting downstream resources.

Suggested fix
    for (const e of calEvents) {
      if (e.status === "cancelled") {
        await this.cancelBooking(e);
      } else {
        await this.rescheduleBooking(e);
      }
    }
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert bash developer with deep knowledge of security, performance, and best practices.

### Context

File: packages/features/calendar-subscription/lib/sync/CalendarSyncService.ts
Lines: 61-69
Issue Type: performance-medium
Severity: medium

Issue Description:
`Promise.all` fans out all filtered Cal.com events concurrently with no concurrency cap. A calendar diff with 50: 100 changed events (e.g. after a long offline period or a full re-sync) launches that many simultaneous `handleCancelBooking` or `createBooking` calls, each of which opens multiple Prisma queries, sends emails, fires webhooks, and calls third-party calendar APIs. This can exhaust the Prisma connection pool, trigger rate-limits on downstream services, and spike DB load for every subscriber that syncs at the same time. Sequential processing or a bounded concurrency limit (e.g. p-limit with 5 or less) would keep throughput acceptable while protecting downstream resources.

Current Code:
    await Promise.all(
      calEvents.map((e) => {
        if (e.status === "cancelled") {
          return this.cancelBooking(e);
        } else {
          return this.rescheduleBooking(e);
        }
      })
    );

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow bash best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue Jira

@codity-dm

codity-dm Bot commented May 31, 2026

Copy link
Copy Markdown

Nitpicks (Low Priority)

Found 1 low-priority suggestions for code improvement

Click to expand nitpicks

packages/features/calendar-subscription/lib/sync/CalendarSyncService.ts (lines 105-115)

Maintainability Low

handleCancelBooking is called without an actionSource. The handler defaults to "UNKNOWN" (line 180 of handleCancelBooking.ts) and emits a log.warn("Booking cancellation with unknown actionSource") on every single calendar-sync cancellation. ActionSource is defined as z.enum(["API_V1", "API_V2", "WEBAPP", "WEBHOOK", "MAGIC_LINK", "UNKNOWN"]). Calendar sync is driven by an incoming webhook from Google/Office365, so "WEBHOOK" is the correct semantic value. Passing it suppresses the warning, improves audit trail accuracy, and makes log-based dashboards accurate.

Code Suggestion or Comments
await handleCancelBooking({
        userId: booking.userId,
        actionSource: "WEBHOOK",
        bookingData: {
          uid: booking.uid,
          cancellationReason: "Cancelled on user's calendar",
          cancelledBy: booking.userPrimaryEmail,
          // Skip calendar event deletion to avoid infinite loops
          // (Google/Office365 → Cal.com → Google/Office365 → ...)
          skipCalendarSyncTaskCancellation: true,
        },
      });
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert bash developer with deep knowledge of security, performance, and best practices.

### Context

File: packages/features/calendar-subscription/lib/sync/CalendarSyncService.ts
Lines: 105-115
Issue Type: maintainability-low
Severity: low

Issue Description:
`handleCancelBooking` is called without an `actionSource`. The handler defaults to `"UNKNOWN"` (line 180 of handleCancelBooking.ts) and emits a `log.warn("Booking cancellation with unknown actionSource")` on every single calendar-sync cancellation. `ActionSource` is defined as `z.enum(["API_V1", "API_V2", "WEBAPP", "WEBHOOK", "MAGIC_LINK", "UNKNOWN"])`. Calendar sync is driven by an incoming webhook from Google/Office365, so `"WEBHOOK"` is the correct semantic value. Passing it suppresses the warning, improves audit trail accuracy, and makes log-based dashboards accurate.

Current Code:
      await handleCancelBooking({
        userId: booking.userId,
        bookingData: {
          uid: booking.uid,
          cancellationReason: "Cancelled on user's calendar",
          cancelledBy: booking.userPrimaryEmail,
          // Skip calendar event deletion to avoid infinite loops
          // (Google/Office365 → Cal.com → Google/Office365 → ...)
          skipCalendarSyncTaskCancellation: true,
        },
      });

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow bash best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---



Like Dislike

@codity-dm

codity-dm Bot commented May 31, 2026

Copy link
Copy Markdown

Security Scan Summary

Metric Value
Vulnerabilities Critical: 0
Overall Risk Clean
Files Scanned 4

No critical security issues detected

Scan completed in 14.0s

Security scan powered by Codity.ai

@codity-dm

codity-dm Bot commented May 31, 2026

Copy link
Copy Markdown

License Compliance Scan

Metric Value
Packages Scanned 448
High Risk (Strong Copyleft) 0
Medium Risk (Weak Copyleft) 5
Low Risk (Permissive) 399
Unknown License 44

Weak copyleft licenses found - verify compatibility

Some packages have unknown licenses - manual review required

Medium Risk Licenses - 5 packages

(MPL-2.0 OR Apache-2.0) (1 packages):

  • dompurify 3.3.1

MPL-2.0 (4 packages):

  • @vercel/og 0.6.3
  • ical.js 1.5.0
  • web-push 3.6.7
  • @dub/analytics 0.0.27
Unknown Licenses - 44 packages
  • @calcom/atoms workspace:*
  • @calcom/config workspace:*
  • @calcom/dayjs workspace:*
  • @calcom/dailyvideo workspace:*
  • @calcom/types workspace:*
  • @calcom/tsconfig workspace:*
  • @calcom/features workspace:*
  • @calcom/lib workspace:*
  • @calcom/office365video workspace:*
  • @calcom/zoomvideo workspace:*
  • @calcom/ui workspace:*
  • @calcom/testing workspace:*
  • @calcom/prisma workspace:*
  • @calcom/app-store workspace:*
  • dub-package npm:dub@^0.61.12
  • @calcom/types *
  • @calcom/lib *
  • @calcom/emails workspace:*
  • @radix-ui/react-dialog-atoms npm:@radix-ui/react-dialog@1.0.4
  • @calcom/platform-constants workspace:*

...and 24 more

Powered by Codity.ai · Docs

@codity-dm

codity-dm Bot commented May 31, 2026

Copy link
Copy Markdown

Code Quality Report — test-org-codity/cal.com · PR #6

Scanned: 2026-05-31 09:02 UTC | Score: 52/100 | Provider: github

Executive Summary

Severity Count
Critical 0
High 1
Medium 3
Low 15
Top Findings

[CQ-LLM-001] packages/features/bookings/lib/handleCancelBooking.ts:162 (Complexity · HIGH)

Issue: The handler function has increased cyclomatic complexity due to additional conditional logic for skipCalendarSyncTaskCancellation.
Suggestion: Consider refactoring the handler function to reduce complexity, possibly by extracting parts of the logic into smaller functions.

if (!skipCalendarSyncTaskCancellation) { ... }

[CQ-LLM-002] packages/features/bookings/lib/handleCancelBooking.ts:611 (Error_Handling · MEDIUM)

Issue: Error handling for booking reference deletion is inconsistent; the error message does not specify the context of the operation.
Suggestion: Ensure that error messages provide context for better debugging and maintainability.

log.error(`Error marking booking references as deleted`, safeStringify({ error }));

[CQ-LLM-003] packages/features/calendar-subscription/lib/sync/CalendarSyncService.ts:31 (Documentation · MEDIUM)

Issue: Missing JSDoc for the cancelBooking method, which is a public API.
Suggestion: Add JSDoc comments to describe the purpose, parameters, and return values of the cancelBooking method.

async cancelBooking(event: CalendarSubscriptionEventItem) {

[CQ-LLM-004] packages/features/calendar-subscription/lib/sync/CalendarSyncService.ts:59 (Testability · MEDIUM)

Issue: The cancelBooking method directly calls handleCancelBooking, which may make it difficult to test in isolation without mocking.
Suggestion: Consider using dependency injection for handleCancelBooking to improve testability.

await handleCancelBooking({ ... });

[CQ-LLM-005] packages/features/calendar-subscription/lib/sync/CalendarSyncService.ts:80 (Maintainability · LOW)

Issue: The use of magic strings for logging messages can lead to maintenance issues.
Suggestion: Define constants for log messages to avoid duplication and improve maintainability.

log.debug('Unable to sync, booking UID not found in iCalUID');

[CQ-009] packages/features/calendar-subscription/lib/sync/__tests__/CalendarSyncService.test.ts:3 (Style · LOW)

Issue: Line exceeds 120 characters (131 chars)
Suggestion: Break long lines into multiple lines for readability

import type { CalendarSubscriptionEventItem } from "@calcom/features/calendar-subscription/lib/CalendarSubscriptionPort....

[CQ-008] packages/features/calendar-subscription/lib/sync/__tests__/CalendarSyncService.test.ts:49 (Maintainability · LOW)

Issue: Magic number 86400000 in code
Suggestion: Extract to a named constant

channelExpiration: new Date(Date.now() + 86400000),

[CQ-008] packages/features/calendar-subscription/lib/sync/__tests__/CalendarSyncService.test.ts:62 (Maintainability · LOW)

Issue: Magic number 2023 in code
Suggestion: Extract to a named constant

startTime: new Date("2023-12-01T10:00:00Z"),

[CQ-008] packages/features/calendar-subscription/lib/sync/__tests__/CalendarSyncService.test.ts:63 (Maintainability · LOW)

Issue: Magic number 2023 in code
Suggestion: Extract to a named constant

endTime: new Date("2023-12-01T11:00:00Z"),

[CQ-008] packages/features/calendar-subscription/lib/sync/__tests__/CalendarSyncService.test.ts:74 (Maintainability · LOW)

Issue: Magic number 2023 in code
Suggestion: Extract to a named constant

start: new Date("2023-12-01T10:00:00Z"),

Per-File Breakdown

File Critical High Medium Low Total
packages/features/bookings/lib/handleCancelBooking.ts 0 1 1 0 2
packages/features/calendar-subscription/lib/sync/CalendarSyncService.ts 0 0 2 1 3
packages/features/calendar-subscription/lib/sync/__tests__/CalendarSyncService.test.ts 0 0 0 14 14

Recommendations

  1. Resolve High severity issues, especially error handling gaps and performance bottlenecks.
  • Run automated tests after applying fixes to verify no regressions.

@DhirenMhatre

Copy link
Copy Markdown
Author

@codity review

@codity-dm

codity-dm Bot commented May 31, 2026

Copy link
Copy Markdown

Policy Check Failed

✗ 2/3 policy checks failed:

• Need 2 more approval(s) (0/2) — comment LGTM or approve via review
• Missing ticket reference (expected: JIRA-, ENG-, #*)

✓ 1 checks passed:
• Test files included (1 test file(s))


To merge this PR:

  1. Address the failed checks listed above
  2. Ensure branch protection requires the codity/policy-check status

Configure policies in your dashboard

@codity-dm

codity-dm Bot commented May 31, 2026

Copy link
Copy Markdown

PR Summary

What Changed

  • Added bidirectional calendar sync so external calendar changes (Google/Office365) automatically cancel or reschedule Cal.com bookings.
  • Introduced loop-prevention flags to stop infinite sync cycles when webhooks trigger each other.
  • Separated calendar event deletion from reference cleanup for data consistency.

Key Changes by Area

Calendar Sync Service: New CalendarSyncService processes webhook events with @cal.com iCalUIDs, delegates to booking handlers with loop-prevention flags, and includes Sentry metrics.

Booking Cancellation: Extended handleCancelBooking with skipCalendarSyncTaskCancellation flag and new domain types in BookingCancel.d.ts.

Schema: Added skipCalendarSyncTaskCancellation to bookingCancelSchema in zod-utils.

Files Changed

File Changes Summary
packages/features/bookings/lib/handleCancelBooking.ts Added loop-prevention flag and separated calendar deletion from reference cleanup
packages/features/calendar-subscription/lib/sync/CalendarSyncService.ts New service for processing external calendar cancellations and reschedules
packages/features/calendar-subscription/lib/sync/tests/CalendarSyncService.test.ts Comprehensive test suite for sync flows and edge cases
packages/prisma/zod-utils.ts Added skipCalendarSyncTaskCancellation to booking cancel schema

Review Focus Areas

  • Loop prevention logic: verify flags properly break sync cycles in both directions.
  • Data consistency: confirm booking references are always marked deleted even when skipping external operations.
  • Error handling in CalendarSyncService: sync failures are logged but don't block processing. Check this is appropriate for all failure modes.

Architecture

Design Decisions: Dynamic imports avoid loading react-awesome-query-builder in server contexts. Defensive error handling keeps sync failures non-blocking. The split between calendar deletion and reference cleanup prioritizes data consistency over atomicity.

Risks: Intentional: Non-blocking error handling means sync failures are silent. Unintentional: Mixed-case iCalUID matching could miss events if external calendars normalize differently.

Merge Status

NOT MERGEABLE — PR Score 41/100, below threshold (50)

  • [H4] PR quality score (41) is below merge floor (50)

@codity-dm

codity-dm Bot commented May 31, 2026

Copy link
Copy Markdown

Workflow Diagrams

Automatically generated sequence diagrams showing the workflows in this PR

1. Calendar Sync Bidirectional Flow with Loop Prevention

Complex complexity • Components: CalendarSyncService, handleCancelBooking, RegularBookingService

sequenceDiagram
    title Bidirectional Calendar Synchronization with Loop Prevention

    participant ExternalCalendar as External Calendar
    participant CalendarSyncService as CalendarSyncService
    participant BookingRepository as BookingRepository
    participant handleCancelBooking as handleCancelBooking
    participant RegularBookingService as RegularBookingService
    participant EventManager as EventManager
    participant Database as Database

    ExternalCalendar->>CalendarSyncService: Webhook: calendar event changed

    Note over CalendarSyncService: Filter events with iCalUID ending in @cal.com

    alt Event status is cancelled
        CalendarSyncService->>BookingRepository: findBookingByUidWithEventType(bookingUid)
        BookingRepository-->>CalendarSyncService: booking data

        alt Booking found with required user data
            CalendarSyncService->>handleCancelBooking: cancel booking with skipCalendarSyncTaskCancellation=true

            Note over handleCancelBooking: Skip calendar event deletion to prevent infinite loop

            handleCancelBooking->>Database: Mark booking references as deleted
            handleCancelBooking-->>CalendarSyncService: cancellation complete
        end

    else Event status is confirmed or updated
        CalendarSyncService->>BookingRepository: findBookingByUidWithEventType(bookingUid)
        BookingRepository-->>CalendarSyncService: booking data

        alt Booking found with eventTypeId
            CalendarSyncService->>RegularBookingService: createBooking with rescheduleUid and skipCalendarSyncTaskCreation=true

            Note over RegularBookingService: Skip calendar event creation to prevent infinite loop

            RegularBookingService->>Database: Create rescheduled booking
            RegularBookingService-->>CalendarSyncService: reschedule complete
        end
    end

    Note over CalendarSyncService: Record Sentry metrics for monitoring
Loading

Note: Diagrams show detected patterns only. Complex workflows may require manual review.

cancelSubsequentBookings: z.boolean().optional(),
cancellationReason: z.string().optional(),
skipCancellationReasonValidation: z.boolean().optional(),
skipCalendarSyncTaskCancellation: z.boolean().optional(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Security High

The skipCalendarSyncTaskCancellation flag is added to bookingCancelSchema, which is the base for bookingCancelWithCsrfSchema used directly in the public /api/cancel route (apps/web/app/api/cancel/route.ts:23). Any authenticated user can include skipCalendarSyncTaskCancellation: true in their POST body to bypass calendar synchronization on cancellation. This is an internal-only escape hatch that should never be exposed to client requests: it should be stripped at the API boundary or live on an internal-only schema that is never merged with the public one.

Suggested fix
  // NOTE: skipCalendarSyncTaskCancellation is intentionally omitted here;
  // it is only set programmatically by CalendarSyncService and must not be
  // accepted from untrusted request bodies.
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert bash developer with deep knowledge of security, performance, and best practices.

### Context

File: packages/prisma/zod-utils.ts
Lines: 393-393
Issue Type: security-high
Severity: high

Issue Description:
The `skipCalendarSyncTaskCancellation` flag is added to `bookingCancelSchema`, which is the base for `bookingCancelWithCsrfSchema` used directly in the public `/api/cancel` route (`apps/web/app/api/cancel/route.ts:23`). Any authenticated user can include `skipCalendarSyncTaskCancellation: true` in their POST body to bypass calendar synchronization on cancellation. This is an internal-only escape hatch that should never be exposed to client requests: it should be stripped at the API boundary or live on an internal-only schema that is never merged with the public one.

Current Code:
  skipCalendarSyncTaskCancellation: z.boolean().optional(),

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow bash best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue Jira

Comment on lines +650 to 654
try {
await bookingReferenceRepository.updateManyByBookingId(bookingToDelete.id, { deleted: true });
} catch (error) {
log.error(`Error deleting integrations`, safeStringify({ error }));
log.error(`Error marking booking references as deleted`, safeStringify({ error }));
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Functional High

The refactor separates bookingReferenceRepository.updateManyByBookingId from the eventManager.cancelEvent try-block, so it now runs unconditionally even when cancelEvent throws. In the original code both calls lived in the same try-block: if cancelEvent failed the references were NOT marked deleted, keeping the DB consistent with the external calendar state. Now, if cancelEvent fails on the normal (non-skip) path, the booking references in cal.com are marked deleted while the actual calendar event may still exist: creating a silent inconsistency that is very hard to detect or reconcile later.

Suggested fix
  // When skipping calendar event deletion the external event was already removed;
  // mark references deleted for consistency. When NOT skipping, only mark
  // references deleted after a successful cancelEvent.
  if (skipCalendarSyncTaskCancellation) {
    try {
      await bookingReferenceRepository.updateManyByBookingId(bookingToDelete.id, { deleted: true });
    } catch (error) {
      log.error(`Error marking booking references as deleted`, safeStringify({ error }));
    }
  }
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert bash developer with deep knowledge of security, performance, and best practices.

### Context

File: packages/features/bookings/lib/handleCancelBooking.ts
Lines: 650-654
Issue Type: functional-high
Severity: high

Issue Description:
The refactor separates `bookingReferenceRepository.updateManyByBookingId` from the `eventManager.cancelEvent` try-block, so it now runs unconditionally even when `cancelEvent` throws. In the original code both calls lived in the same try-block: if `cancelEvent` failed the references were NOT marked deleted, keeping the DB consistent with the external calendar state. Now, if `cancelEvent` fails on the normal (non-skip) path, the booking references in cal.com are marked deleted while the actual calendar event may still exist: creating a silent inconsistency that is very hard to detect or reconcile later.

Current Code:
  // Always mark booking references as deleted for data consistency
  // (even when skipCalendarSyncTaskCancellation is true, since the external event is already deleted)
  try {
    await bookingReferenceRepository.updateManyByBookingId(bookingToDelete.id, { deleted: true });
  } catch (error) {
    log.error(`Error marking booking references as deleted`, safeStringify({ error }));
  }

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow bash best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue Jira

import { beforeEach, describe, expect, test, vi } from "vitest";

import type { CalendarSubscriptionEventItem } from "@calcom/features/calendar-subscription/lib/CalendarSubscriptionPort.interface";
import type { BookingRepository } from "@calcom/lib/server/repository/booking";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Functional Medium

The test imports BookingRepository from @calcom/lib/server/repository/booking, which does not exist anywhere in the repository (confirmed by filesystem inspection: no file at packages/lib/server/repository/booking.ts or any booking export under that path). The production code imports from @calcom/features/bookings/repositories/BookingRepository. This broken import will cause a TypeScript compilation error, preventing CI from passing.

Suggested change
import type { BookingRepository } from "@calcom/lib/server/repository/booking";
import type { BookingRepository } from "@calcom/features/bookings/repositories/BookingRepository";
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert bash developer with deep knowledge of security, performance, and best practices.

### Context

File: packages/features/calendar-subscription/lib/sync/__tests__/CalendarSyncService.test.ts
Lines: 4-4
Issue Type: functional-medium
Severity: medium

Issue Description:
The test imports `BookingRepository` from `@calcom/lib/server/repository/booking`, which does not exist anywhere in the repository (confirmed by filesystem inspection: no file at `packages/lib/server/repository/booking.ts` or any `booking` export under that path). The production code imports from `@calcom/features/bookings/repositories/BookingRepository`. This broken import will cause a TypeScript compilation error, preventing CI from passing.

Current Code:
import type { BookingRepository } from "@calcom/lib/server/repository/booking";

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow bash best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue Jira

status: "cancelled",
};

describe("CalendarSyncService", () => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Robustness Low

The test file does not mock @sentry/nextjs, but CalendarSyncService calls metrics.count and metrics.distribution on every code path. Every sibling test in this package (CalendarSubscriptionService.test.ts) explicitly mocks @sentry/nextjs. Without the mock the tests either fail (if Sentry throws in the test environment) or silently call through to the real SDK (potentially making network requests or causing test flakes). Add a vi.mock("@sentry/nextjs", ...) at the top of the file, matching the pattern used in the sibling test.

Suggested fix
vi.mock("@sentry/nextjs", () => ({
  metrics: {
    count: vi.fn(),
    distribution: vi.fn(),
  },
}));

describe("CalendarSyncService", () => {
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert bash developer with deep knowledge of security, performance, and best practices.

### Context

File: packages/features/calendar-subscription/lib/sync/__tests__/CalendarSyncService.test.ts
Lines: 118-118
Issue Type: robustness-low
Severity: low

Issue Description:
The test file does not mock `@sentry/nextjs`, but `CalendarSyncService` calls `metrics.count` and `metrics.distribution` on every code path. Every sibling test in this package (`CalendarSubscriptionService.test.ts`) explicitly mocks `@sentry/nextjs`. Without the mock the tests either fail (if Sentry throws in the test environment) or silently call through to the real SDK (potentially making network requests or causing test flakes). Add a `vi.mock("@sentry/nextjs", ...)` at the top of the file, matching the pattern used in the sibling test.

Current Code:
describe("CalendarSyncService", () => {

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow bash best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue Jira

Comment on lines +104 to +115
try {
await handleCancelBooking({
userId: booking.userId,
bookingData: {
uid: booking.uid,
cancellationReason: "Cancelled on user's calendar",
cancelledBy: booking.userPrimaryEmail,
// Skip calendar event deletion to avoid infinite loops
// (Google/Office365 → Cal.com → Google/Office365 → ...)
skipCalendarSyncTaskCancellation: true,
},
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Maintainability Medium

handleCancelBooking is called without actionSource, so every calendar-sync-triggered cancellation defaults to "UNKNOWN" and emits a log.warn("Booking cancellation with unknown actionSource", ...) (handleCancelBooking.ts:181-188). This produces permanent log noise for a legitimate, expected code path. The ActionSourceSchema enum (packages/features/booking-audit/lib/types/actionSource.ts) does not include a "CALENDAR_SYNC" value; "WEBHOOK" is the closest semantic match and would suppress the spurious warning.

Suggested fix
      await handleCancelBooking({
        userId: booking.userId,
        actionSource: "WEBHOOK",
        bookingData: {
          uid: booking.uid,
          cancellationReason: "Cancelled on user's calendar",
          cancelledBy: booking.userPrimaryEmail,
          // Skip calendar event deletion to avoid infinite loops
          // (Google/Office365 → Cal.com → Google/Office365 → ...)
          skipCalendarSyncTaskCancellation: true,
        },
      });
Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert bash developer with deep knowledge of security, performance, and best practices.

### Context

File: packages/features/calendar-subscription/lib/sync/CalendarSyncService.ts
Lines: 104-115
Issue Type: maintainability-medium
Severity: medium

Issue Description:
`handleCancelBooking` is called without `actionSource`, so every calendar-sync-triggered cancellation defaults to `"UNKNOWN"` and emits a `log.warn("Booking cancellation with unknown actionSource", ...)` (handleCancelBooking.ts:181-188). This produces permanent log noise for a legitimate, expected code path. The `ActionSourceSchema` enum (`packages/features/booking-audit/lib/types/actionSource.ts`) does not include a `"CALENDAR_SYNC"` value; `"WEBHOOK"` is the closest semantic match and would suppress the spurious warning.

Current Code:
      await handleCancelBooking({
        userId: booking.userId,
        bookingData: {
          uid: booking.uid,
          cancellationReason: "Cancelled on user's calendar",
          cancelledBy: booking.userPrimaryEmail,
          // Skip calendar event deletion to avoid infinite loops
          // (Google/Office365 → Cal.com → Google/Office365 → ...)
          skipCalendarSyncTaskCancellation: true,
        },
      });

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow bash best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue Jira

eventTypeId: booking.eventTypeId,
start: event.start?.toISOString() ?? booking.startTime.toISOString(),
end: event.end?.toISOString() ?? booking.endTime.toISOString(),
timeZone: event.timeZone ?? "UTC",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Functional Medium

language: "en" is hardcoded in the rescheduleBooking call to createBooking. The booking object already contains user locale information accessible via the organizer's profile. Using a hardcoded "en" means all reschedule notifications triggered via calendar sync will be sent in English regardless of the organizer's and attendees' configured locale, breaking the i18n experience.

Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert bash developer with deep knowledge of security, performance, and best practices.

### Context

File: packages/features/calendar-subscription/lib/sync/CalendarSyncService.ts
Lines: 176-176
Issue Type: functional-medium
Severity: medium

Issue Description:
`language: "en"` is hardcoded in the `rescheduleBooking` call to `createBooking`. The booking object already contains user locale information accessible via the organizer's profile. Using a hardcoded `"en"` means all reschedule notifications triggered via calendar sync will be sent in English regardless of the organizer's and attendees' configured locale, breaking the i18n experience.

Current Code:
            language: "en",

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow bash best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue Jira

Comment on lines +124 to +126
} catch (error) {
// Log error but don't block - calendar change should still be reflected
log.error("Failed to cancel booking from calendar sync", { bookingUid, error: safeStringify(error) });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Robustness Medium

When handleCancelBooking throws for a legitimate business-logic reason: e.g. eventType.disableCancelling === true (HTTP 400), or the booking has already ended (HTTP 400): the error is caught, logged, and silently discarded. The result is a persistent split-brain state: the external calendar (Google/Office365) shows the event as cancelled, but the cal.com booking remains CONFIRMED in the database. There is no retry, alert, or dead-letter queue. Consider distinguishing between transient errors (worth retrying) and permanent business-rule rejections (worth surfacing via a metric or separate alert rather than simply swallowing).

Prompt for AI assistance

Copy the prompt below and paste it into ChatGPT, Claude, or any LLM:

You are an expert bash developer with deep knowledge of security, performance, and best practices.

### Context

File: packages/features/calendar-subscription/lib/sync/CalendarSyncService.ts
Lines: 124-126
Issue Type: robustness-medium
Severity: medium

Issue Description:
When `handleCancelBooking` throws for a legitimate business-logic reason: e.g. `eventType.disableCancelling === true` (HTTP 400), or the booking has already ended (HTTP 400): the error is caught, logged, and silently discarded. The result is a persistent split-brain state: the external calendar (Google/Office365) shows the event as cancelled, but the cal.com booking remains `CONFIRMED` in the database. There is no retry, alert, or dead-letter queue. Consider distinguishing between transient errors (worth retrying) and permanent business-rule rejections (worth surfacing via a metric or separate alert rather than simply swallowing).

Current Code:
      // Log error but don't block - calendar change should still be reflected
      log.error("Failed to cancel booking from calendar sync", { bookingUid, error: safeStringify(error) });

---

### Instructions

1. Fix the issue described above
2. Maintain the exact indentation and code style from the original
3. Follow bash best practices and language-specific idioms
4. Ensure the fix addresses the root cause, not just the symptoms
5. Add brief inline comments explaining the fix if needed

### Constraints

- Do not change functionality beyond fixing the identified issue
- Preserve existing variable names and function signatures unless they are part of the problem
- Ensure the fix is production-ready

---


Like Dislike Create Issue Jira

@codity-dm

codity-dm Bot commented May 31, 2026

Copy link
Copy Markdown

Security Scan Summary

Metric Value
Vulnerabilities Critical: 0
Overall Risk Clean
Files Scanned 4

No critical security issues detected

Scan completed in 12.8s

Security scan powered by Codity.ai

@codity-dm

codity-dm Bot commented May 31, 2026

Copy link
Copy Markdown

License Compliance Scan

Metric Value
Packages Scanned 448
High Risk (Strong Copyleft) 0
Medium Risk (Weak Copyleft) 5
Low Risk (Permissive) 399
Unknown License 44

Weak copyleft licenses found - verify compatibility

Some packages have unknown licenses - manual review required

Medium Risk Licenses - 5 packages

(MPL-2.0 OR Apache-2.0) (1 packages):

  • dompurify 3.3.1

MPL-2.0 (4 packages):

  • ical.js 1.5.0
  • @vercel/og 0.6.3
  • web-push 3.6.7
  • @dub/analytics 0.0.27
Unknown Licenses - 44 packages
  • @calcom/atoms workspace:*
  • @calcom/dayjs workspace:*
  • @calcom/config workspace:*
  • @calcom/types workspace:*
  • @calcom/dailyvideo workspace:*
  • @calcom/ui workspace:*
  • @calcom/tsconfig workspace:*
  • @calcom/office365video workspace:*
  • @calcom/lib workspace:*
  • @calcom/features workspace:*
  • @calcom/zoomvideo workspace:*
  • @calcom/testing workspace:*
  • @calcom/prisma workspace:*
  • @calcom/emails workspace:*
  • @calcom/app-store workspace:*
  • dub-package npm:dub@^0.61.12
  • @calcom/lib *
  • @calcom/types *
  • @calcom/platform-constants workspace:*
  • @radix-ui/react-dialog-atoms npm:@radix-ui/react-dialog@1.0.4

...and 24 more

Powered by Codity.ai · Docs

@codity-dm

codity-dm Bot commented May 31, 2026

Copy link
Copy Markdown

Code Quality Report — test-org-codity/cal.com · PR #6

Scanned: 2026-05-31 09:16 UTC | Score: 52/100 | Provider: github

Executive Summary

Severity Count
Critical 0
High 1
Medium 3
Low 15
Top Findings

[CQ-LLM-001] packages/features/bookings/lib/handleCancelBooking.ts:162 (Complexity · HIGH)

Issue: The handler function has increased cyclomatic complexity due to additional conditional logic for skipCalendarSyncTaskCancellation.
Suggestion: Consider refactoring the handler function to reduce complexity, possibly by extracting parts of the logic into smaller functions.

if (!skipCalendarSyncTaskCancellation) { ... }

[CQ-LLM-002] packages/features/bookings/lib/handleCancelBooking.ts:611 (Error_Handling · MEDIUM)

Issue: Swallowed exceptions in the try-catch block for event cancellation may lead to silent failures.
Suggestion: Ensure that all caught exceptions are logged or handled appropriately to avoid silent failures.

catch (error) { log.error(`Error deleting integrations`, safeStringify({ error })); }

[CQ-LLM-003] packages/features/calendar-subscription/lib/sync/CalendarSyncService.ts:31 (Documentation · MEDIUM)

Issue: Missing documentation for the cancelBooking method, which is a public API.
Suggestion: Add JSDoc comments to the cancelBooking method to describe its purpose, parameters, and return values.

async cancelBooking(event: CalendarSubscriptionEventItem) { ... }

[CQ-LLM-004] packages/features/calendar-subscription/lib/sync/__tests__/CalendarSyncService.test.ts:1 (Testability · MEDIUM)

Issue: The test file lacks setup for mocking dependencies, which may lead to tests that are not isolated.
Suggestion: Ensure that all dependencies are properly mocked to isolate the tests and avoid side effects.

const { mockHandleCancelBooking, mockCreateBooking } = vi.hoisted(() => ({ ... }));

[CQ-LLM-005] packages/features/calendar-subscription/lib/sync/CalendarSyncService.ts:75 (Maintainability · LOW)

Issue: Magic string used for the default time zone value.
Suggestion: Consider defining the default time zone as a constant to improve maintainability.

timeZone: event.timeZone ?? 'UTC',

[CQ-009] packages/features/calendar-subscription/lib/sync/__tests__/CalendarSyncService.test.ts:3 (Style · LOW)

Issue: Line exceeds 120 characters (131 chars)
Suggestion: Break long lines into multiple lines for readability

import type { CalendarSubscriptionEventItem } from "@calcom/features/calendar-subscription/lib/CalendarSubscriptionPort....

[CQ-008] packages/features/calendar-subscription/lib/sync/__tests__/CalendarSyncService.test.ts:49 (Maintainability · LOW)

Issue: Magic number 86400000 in code
Suggestion: Extract to a named constant

channelExpiration: new Date(Date.now() + 86400000),

[CQ-008] packages/features/calendar-subscription/lib/sync/__tests__/CalendarSyncService.test.ts:62 (Maintainability · LOW)

Issue: Magic number 2023 in code
Suggestion: Extract to a named constant

startTime: new Date("2023-12-01T10:00:00Z"),

[CQ-008] packages/features/calendar-subscription/lib/sync/__tests__/CalendarSyncService.test.ts:63 (Maintainability · LOW)

Issue: Magic number 2023 in code
Suggestion: Extract to a named constant

endTime: new Date("2023-12-01T11:00:00Z"),

[CQ-008] packages/features/calendar-subscription/lib/sync/__tests__/CalendarSyncService.test.ts:74 (Maintainability · LOW)

Issue: Magic number 2023 in code
Suggestion: Extract to a named constant

start: new Date("2023-12-01T10:00:00Z"),

Per-File Breakdown

File Critical High Medium Low Total
packages/features/bookings/lib/handleCancelBooking.ts 0 1 1 0 2
packages/features/calendar-subscription/lib/sync/CalendarSyncService.ts 0 0 1 1 2
packages/features/calendar-subscription/lib/sync/__tests__/CalendarSyncService.test.ts 0 0 1 14 15

Recommendations

  1. Resolve High severity issues, especially error handling gaps and performance bottlenecks.
  • Run automated tests after applying fixes to verify no regressions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants