perf: optimize fetch for primary google calendar (#22041)
* perf: optimize getting primary google calendar * add test * use calendars * safer
This commit is contained in:
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);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user