perf: optimize fetch for primary google calendar (#22041)

* perf: optimize getting primary google calendar

* add test

* use calendars

* safer
This commit is contained in:
Benny Joo authored and GitHub committed 2025-06-25 23:10:25 +01:00
1 parent 29aad2c535
commit 801b0be414
5 files changed
+47 -51

No files matched your search

@@ -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);
@@ -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(
@@ -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<IntegrationCalendar[]> {
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<calendar_v3.Schema$CalendarListEntry | null> {
let pageToken: string | undefined;
let firstCalendar: calendar_v3.Schema$CalendarListEntry | undefined;
async getPrimaryCalendar(_calendar?: calendar_v3.Calendar): Promise<calendar_v3.Schema$Calendar | null> {
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;
}
}
@@ -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: {
@@ -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);
});
});