Skip to content

Commit 0968bce

Browse files
fix(calendar): sync Cal.diy iCalUID events (calcom#29580)
Co-authored-by: Kartik <103111467+kartik-212004@users.noreply.github.com>
1 parent ca90ca2 commit 0968bce

2 files changed

Lines changed: 41 additions & 12 deletions

File tree

packages/features/calendar-subscription/lib/sync/CalendarSyncService.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,25 @@
11
import type { CreateRegularBookingData } from "@calcom/features/bookings/lib/dto/types";
2-
import { IdempotencyKeyService } from "@calcom/lib/idempotencyKey/idempotencyKeyService";
32
import handleCancelBooking from "@calcom/features/bookings/lib/handleCancelBooking";
43
import type { BookingRepository } from "@calcom/features/bookings/repositories/BookingRepository";
54
import type { CalendarSubscriptionEventItem } from "@calcom/features/calendar-subscription/lib/CalendarSubscriptionPort.interface";
5+
import { APP_NAME } from "@calcom/lib/constants";
6+
import { IdempotencyKeyService } from "@calcom/lib/idempotencyKey/idempotencyKeyService";
67
import logger from "@calcom/lib/logger";
78
import { safeStringify } from "@calcom/lib/safeStringify";
89
import type { SelectedCalendar } from "@calcom/prisma/client";
910
import { metrics } from "@sentry/nextjs";
1011

1112
const log = logger.getSubLogger({ prefix: ["CalendarSyncService"] });
13+
const CAL_MANAGED_ICAL_UID_SUFFIXES: ReadonlySet<string> = new Set(
14+
["cal.com", "cal.diy", APP_NAME].map((suffix) => suffix.toLowerCase())
15+
);
16+
17+
const isCalManagedICalUID = (iCalUID?: string | null): boolean => {
18+
const suffix = iCalUID?.split("@").at(-1)?.toLowerCase();
19+
if (!suffix) return false;
20+
21+
return CAL_MANAGED_ICAL_UID_SUFFIXES.has(suffix);
22+
};
1223

1324
/**
1425
* Service to handle synchronization of calendar events.
@@ -42,10 +53,7 @@ export class CalendarSyncService {
4253
},
4354
});
4455

45-
// only process cal.com calendar events
46-
const calEvents = calendarSubscriptionEvents.filter((e) =>
47-
e.iCalUID?.toLowerCase()?.endsWith("@cal.com")
48-
);
56+
const calEvents = calendarSubscriptionEvents.filter((e) => isCalManagedICalUID(e.iCalUID));
4957

5058
metrics.distribution("calendar.sync.handleEvents.events_count", calEvents.length, {
5159
attributes: {

packages/features/calendar-subscription/lib/sync/__tests__/CalendarSyncService.test.ts

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1-
import { beforeEach, describe, expect, test, vi } from "vitest";
2-
31
import type { CalendarSubscriptionEventItem } from "@calcom/features/calendar-subscription/lib/CalendarSubscriptionPort.interface";
42
import type { BookingRepository } from "@calcom/lib/server/repository/booking";
53
import type { SelectedCalendar } from "@calcom/prisma/client";
6-
4+
import { beforeEach, describe, expect, test, vi } from "vitest";
75
import { CalendarSyncService } from "../CalendarSyncService";
86

97
const { mockHandleCancelBooking, mockCreateBooking } = vi.hoisted(() => ({
@@ -206,6 +204,21 @@ describe("CalendarSyncService", () => {
206204
});
207205
});
208206

207+
test("should handle default Cal.diy iCalUID", async () => {
208+
const eventWithCalDiyUID: CalendarSubscriptionEventItem = {
209+
...mockCalComEvent,
210+
iCalUID: "test-booking-uid@Cal.diy",
211+
};
212+
213+
mockBookingRepository.findBookingByUidWithEventType = vi.fn().mockResolvedValue(mockBooking);
214+
215+
await service.handleEvents(mockSelectedCalendar, [eventWithCalDiyUID]);
216+
217+
expect(mockBookingRepository.findBookingByUidWithEventType).toHaveBeenCalledWith({
218+
bookingUid: "test-booking-uid",
219+
});
220+
});
221+
209222
test("should handle events with null iCalUID", async () => {
210223
const eventWithNullUID: CalendarSubscriptionEventItem = {
211224
...mockCalComEvent,
@@ -279,7 +292,9 @@ describe("CalendarSyncService", () => {
279292
mockHandleCancelBooking.mockRejectedValue(new Error("Cancellation failed"));
280293

281294
// Should not throw - errors are caught and logged
282-
await expect(service.cancelBooking(mockCancelledEvent, mockSelectedCalendar.userId)).resolves.not.toThrow();
295+
await expect(
296+
service.cancelBooking(mockCancelledEvent, mockSelectedCalendar.userId)
297+
).resolves.not.toThrow();
283298

284299
expect(mockBookingRepository.findBookingByUidWithEventType).toHaveBeenCalled();
285300
expect(mockHandleCancelBooking).toHaveBeenCalled();
@@ -290,7 +305,9 @@ describe("CalendarSyncService", () => {
290305
.fn()
291306
.mockRejectedValue(new Error("DB connection failed"));
292307

293-
await expect(service.cancelBooking(mockCancelledEvent, mockSelectedCalendar.userId)).resolves.not.toThrow();
308+
await expect(
309+
service.cancelBooking(mockCancelledEvent, mockSelectedCalendar.userId)
310+
).resolves.not.toThrow();
294311

295312
expect(mockBookingRepository.findBookingByUidWithEventType).toHaveBeenCalled();
296313
expect(mockHandleCancelBooking).not.toHaveBeenCalled();
@@ -489,7 +506,9 @@ describe("CalendarSyncService", () => {
489506
mockCreateBooking.mockRejectedValue(new Error("Rescheduling failed"));
490507

491508
// Should not throw - errors are caught and logged
492-
await expect(service.rescheduleBooking(eventWithDifferentStart, mockSelectedCalendar.userId)).resolves.not.toThrow();
509+
await expect(
510+
service.rescheduleBooking(eventWithDifferentStart, mockSelectedCalendar.userId)
511+
).resolves.not.toThrow();
493512

494513
expect(mockBookingRepository.findBookingByUidWithEventType).toHaveBeenCalled();
495514
expect(mockCreateBooking).toHaveBeenCalled();
@@ -500,7 +519,9 @@ describe("CalendarSyncService", () => {
500519
.fn()
501520
.mockRejectedValue(new Error("DB connection failed"));
502521

503-
await expect(service.rescheduleBooking(mockCalComEvent, mockSelectedCalendar.userId)).resolves.not.toThrow();
522+
await expect(
523+
service.rescheduleBooking(mockCalComEvent, mockSelectedCalendar.userId)
524+
).resolves.not.toThrow();
504525

505526
expect(mockBookingRepository.findBookingByUidWithEventType).toHaveBeenCalled();
506527
expect(mockCreateBooking).not.toHaveBeenCalled();

0 commit comments

Comments
 (0)