diff --git a/apps/web/app/api/cron/selected-calendars/__tests__/cron.test.ts b/apps/web/app/api/cron/selected-calendars/__tests__/cron.test.ts index 2d2405cad3..0157e420d1 100644 --- a/apps/web/app/api/cron/selected-calendars/__tests__/cron.test.ts +++ b/apps/web/app/api/cron/selected-calendars/__tests__/cron.test.ts @@ -8,11 +8,11 @@ import { CalendarAppDelegationCredentialInvalidGrantError } from "@calcom/lib/Ca import { handleCreateSelectedCalendars, isSameEmail } from "../route"; // Mock GoogleCalendarService -const fetchPrimaryCalendarMock = vi.fn(); +const getPrimaryCalendarMock = vi.fn(); vi.mock("@calcom/app-store/googlecalendar/lib/CalendarService", () => { return { default: vi.fn().mockImplementation(() => ({ - fetchPrimaryCalendar: fetchPrimaryCalendarMock, + getPrimaryCalendar: getPrimaryCalendarMock, })), }; }); @@ -116,7 +116,7 @@ describe("handleCreateSelectedCalendars integration", () => { prismock.user.deleteMany(); prismock.team.deleteMany(); prismock.workspacePlatform.deleteMany(); - fetchPrimaryCalendarMock.mockReset(); + getPrimaryCalendarMock.mockReset(); }); it("shows a helpful message when no Delegation Credentials are set up in the system", async () => { @@ -138,7 +138,7 @@ describe("handleCreateSelectedCalendars integration", () => { domain: "example.com", }); await createCredential({ id: 1, userId: user.id, delegationCredentialId: delegationCredential.id }); - fetchPrimaryCalendarMock.mockResolvedValue({ id: "user1@example.com" }); + getPrimaryCalendarMock.mockResolvedValue({ id: "user1@example.com" }); const result = await handleCreateSelectedCalendars(); expect(result.success).toBe(1); @@ -159,7 +159,7 @@ describe("handleCreateSelectedCalendars integration", () => { domain: "example.com", }); await createCredential({ id: 1, userId: user.id, delegationCredentialId: delegationCredential.id }); - fetchPrimaryCalendarMock.mockResolvedValue({ id: "notuser@example.com" }); + getPrimaryCalendarMock.mockResolvedValue({ id: "notuser@example.com" }); const result = await handleCreateSelectedCalendars(); expect(result.success).toBe(1); @@ -183,7 +183,7 @@ describe("handleCreateSelectedCalendars integration", () => { domain: "example.com", }); await createCredential({ id: 1, userId: user.id, delegationCredentialId: delegationCredential.id }); - fetchPrimaryCalendarMock.mockResolvedValue({ id: "user1@example.com" }); + getPrimaryCalendarMock.mockResolvedValue({ id: "user1@example.com" }); const result = await handleCreateSelectedCalendars(); expect(result.success).toBe(1); @@ -210,7 +210,7 @@ describe("handleCreateSelectedCalendars integration", () => { delegationCredentialId: delegationCredential.id, credentialId: 1, }); - fetchPrimaryCalendarMock.mockResolvedValue({ id: "user1@example.com" }); + getPrimaryCalendarMock.mockResolvedValue({ id: "user1@example.com" }); const result = await handleCreateSelectedCalendars(); expect(result.success).toBe(0); @@ -247,7 +247,7 @@ describe("handleCreateSelectedCalendars integration", () => { error: "some error" as unknown as null, credentialId: regularCredential.id, }); - fetchPrimaryCalendarMock.mockResolvedValue({ id: "user1@example.com" }); + getPrimaryCalendarMock.mockResolvedValue({ id: "user1@example.com" }); const result = await handleCreateSelectedCalendars(); expect(result.success).toBe(1); @@ -272,7 +272,7 @@ describe("handleCreateSelectedCalendars integration", () => { domain: "example.com", }); await createCredential({ id: 1, userId: user.id, delegationCredentialId: delegationCredential.id }); - fetchPrimaryCalendarMock.mockRejectedValue( + getPrimaryCalendarMock.mockRejectedValue( new CalendarAppDelegationCredentialInvalidGrantError("some error") ); @@ -293,7 +293,7 @@ describe("handleCreateSelectedCalendars integration", () => { domain: "example.com", }); await createCredential({ id: 1, userId: user.id, delegationCredentialId: delegationCredential.id }); - fetchPrimaryCalendarMock.mockRejectedValue(new Error("some error")); + getPrimaryCalendarMock.mockRejectedValue(new Error("some error")); const result = await handleCreateSelectedCalendars(); expect(result.success).toBe(0); diff --git a/apps/web/app/api/cron/selected-calendars/route.ts b/apps/web/app/api/cron/selected-calendars/route.ts index a0ddb4c3d9..e015b31f1c 100644 --- a/apps/web/app/api/cron/selected-calendars/route.ts +++ b/apps/web/app/api/cron/selected-calendars/route.ts @@ -153,7 +153,7 @@ async function fetchPrimaryCalendarId({ const userEmail = delegationUserCredential.user.email; try { - const primaryCalendar = await googleCalendarService.fetchPrimaryCalendar(); + const primaryCalendar = await googleCalendarService.getPrimaryCalendar(); primaryCalendarId = primaryCalendar?.id; } catch (error) { log.error( diff --git a/packages/app-store/googlecalendar/lib/CalendarService.ts b/packages/app-store/googlecalendar/lib/CalendarService.ts index 0a0d4d2aae..6c445e2a9c 100644 --- a/packages/app-store/googlecalendar/lib/CalendarService.ts +++ b/packages/app-store/googlecalendar/lib/CalendarService.ts @@ -678,15 +678,6 @@ export default class GoogleCalendarService implements Calendar { } } - /** - * Better alternative to `getPrimaryCalendar` that doesn't require any argument - */ - async fetchPrimaryCalendar() { - const calendar = await this.authedCalendar(); - const primaryCalendar = await this.getPrimaryCalendar(calendar); - return primaryCalendar; - } - async listCalendars(): Promise { this.log.debug("Listing calendars"); const calendar = await this.authedCalendar(); @@ -1009,39 +1000,16 @@ export default class GoogleCalendarService implements Calendar { } } - async getPrimaryCalendar( - calendar: calendar_v3.Calendar, - fields: string[] = ["id", "summary", "primary", "accessRole"] - ): Promise { - let pageToken: string | undefined; - let firstCalendar: calendar_v3.Schema$CalendarListEntry | undefined; - + async getPrimaryCalendar(_calendar?: calendar_v3.Calendar): Promise { try { - do { - const response: any = await calendar.calendarList.list({ - fields: `items(${fields.join(",")}),nextPageToken`, - pageToken, - maxResults: 250, // 250 is max - }); - - const cals = response.data.items ?? []; - const primaryCal = cals.find((cal: calendar_v3.Schema$CalendarListEntry) => cal.primary); - if (primaryCal) { - return primaryCal; - } - - // Store the first calendar in case no primary is found - if (cals.length > 0 && !firstCalendar) { - firstCalendar = cals[0]; - } - - pageToken = response.data.nextPageToken; - } while (pageToken); - - // should not be reached because Google Cal always has a primary cal - return firstCalendar ?? null; + const calendar = _calendar ?? (await this.authedCalendar()); + const response = await calendar.calendars.get({ + calendarId: "primary", + }); + return response.data; } catch (error) { - logger.error("Error in `getPrimaryCalendar`", { error }); + // should not be reached because Google Cal always has a primary cal + logger.error("Error getting primary calendar", { error }); throw error; } } diff --git a/packages/app-store/googlecalendar/lib/__mocks__/googleapis.ts b/packages/app-store/googlecalendar/lib/__mocks__/googleapis.ts index dfed81fba5..6a736464a1 100644 --- a/packages/app-store/googlecalendar/lib/__mocks__/googleapis.ts +++ b/packages/app-store/googlecalendar/lib/__mocks__/googleapis.ts @@ -3,6 +3,7 @@ import { vi } from "vitest"; const setCredentialsMock = vi.fn(); const freebusyQueryMock = vi.fn(); const calendarListMock = vi.fn(); +const calendarsGetMock = vi.fn(); const calendarMockImplementation = { channels: { stop: vi.fn().mockResolvedValue(undefined), @@ -10,6 +11,9 @@ const calendarMockImplementation = { calendarList: { list: calendarListMock, }, + calendars: { + get: calendarsGetMock, + }, events: { watch: vi.fn().mockResolvedValue({ data: { diff --git a/packages/app-store/googlecalendar/lib/__tests__/CalendarService.test.ts b/packages/app-store/googlecalendar/lib/__tests__/CalendarService.test.ts index e49bdcb72f..5b33c09e52 100644 --- a/packages/app-store/googlecalendar/lib/__tests__/CalendarService.test.ts +++ b/packages/app-store/googlecalendar/lib/__tests__/CalendarService.test.ts @@ -885,3 +885,27 @@ describe("getAvailability", () => { expect(availabilityWithAllCalendarsAsFallback).toEqual([...mockedBusyTimes1, ...mockedBusyTimes2]); }); }); + +describe("getPrimaryCalendar", () => { + test("should fetch primary calendar using 'primary' keyword", async () => { + const credential = await createCredentialForCalendarService(); + const calendarService = new CalendarService(credential); + setFullMockOAuthManagerRequest(); + const mockPrimaryCalendar = { + id: "user@example.com", + summary: "Primary Calendar", + description: "Primary calendar for user@example.com", + timeZone: "America/New_York", + }; + const calendarsGetMock = vi.fn().mockResolvedValue({ + data: mockPrimaryCalendar, + }); + calendarMock.calendar_v3.Calendar().calendars.get = calendarsGetMock; + const result = await calendarService.getPrimaryCalendar(); + expect(calendarsGetMock).toHaveBeenCalledTimes(1); + expect(calendarsGetMock).toHaveBeenCalledWith({ + calendarId: "primary", + }); + expect(result).toEqual(mockPrimaryCalendar); + }); +});