Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,25 @@ import type { ICalendarSubscriptionPort } from "@calcom/features/calendar-subscr

export type CalendarSubscriptionProvider = "google_calendar" | "office365_calendar";

/**
* Generic calendar suffixes that should be excluded from subscription.
* These are special calendars (holidays, contacts, shared, imported, resources)
* that are not user's personal calendars and shouldn't be subscribed to for sync.
*/
export const GENERIC_CALENDAR_SUFFIXES: Record<CalendarSubscriptionProvider, string[]> = {
google_calendar: [
"@group.v.calendar.google.com",
"@group.calendar.google.com",
"@import.calendar.google.com",
"@resource.calendar.google.com",
],
office365_calendar: [],
};

export interface AdapterFactory {
get(provider: CalendarSubscriptionProvider): ICalendarSubscriptionPort;
getProviders(): CalendarSubscriptionProvider[];
getGenericCalendarSuffixes(): string[];
}

/**
Expand Down Expand Up @@ -41,4 +57,14 @@ export class DefaultAdapterFactory implements AdapterFactory {
const providers: CalendarSubscriptionProvider[] = ["google_calendar"];
return providers;
}

/**
* Returns all generic calendar suffixes that should be excluded from subscription
* across all supported providers.
*
* @returns
*/
getGenericCalendarSuffixes(): string[] {
return this.getProviders().flatMap((provider) => GENERIC_CALENDAR_SUFFIXES[provider]);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,7 @@ export class CalendarSubscriptionService {
take: 100,
integrations: this.deps.adapterFactory.getProviders(),
teamIds,
genericCalendarSuffixes: this.deps.adapterFactory.getGenericCalendarSuffixes(),
});
log.debug("checkForNewSubscriptions", { count: rows.length });
await Promise.allSettled(rows.map(({ id }) => this.subscribe(id)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,12 @@ describe("CalendarSubscriptionService", () => {
mockAdapterFactory = {
get: vi.fn().mockReturnValue(mockAdapter),
getProviders: vi.fn().mockReturnValue(["google_calendar", "office365_calendar"]),
getGenericCalendarSuffixes: vi.fn().mockReturnValue([
"@group.v.calendar.google.com",
"@group.calendar.google.com",
"@import.calendar.google.com",
"@resource.calendar.google.com",
]),
};

mockSelectedCalendarRepository = {
Expand Down Expand Up @@ -385,6 +391,12 @@ describe("CalendarSubscriptionService", () => {
take: 100,
integrations: ["google_calendar", "office365_calendar"],
teamIds: [1, 2, 3],
genericCalendarSuffixes: [
"@group.v.calendar.google.com",
"@group.calendar.google.com",
"@import.calendar.google.com",
"@resource.calendar.google.com",
],
});
expect(subscribeSpy).toHaveBeenCalledWith(mockSelectedCalendar.id);
});
Expand All @@ -411,6 +423,12 @@ describe("CalendarSubscriptionService", () => {
take: 100,
integrations: ["google_calendar", "office365_calendar"],
teamIds: [10, 20],
genericCalendarSuffixes: [
"@group.v.calendar.google.com",
"@group.calendar.google.com",
"@import.calendar.google.com",
"@resource.calendar.google.com",
],
});
expect(subscribeSpy).toHaveBeenCalledTimes(2);
expect(subscribeSpy).toHaveBeenCalledWith("calendar-with-cache");
Expand All @@ -437,6 +455,12 @@ describe("CalendarSubscriptionService", () => {
take: 100,
integrations: ["google_calendar", "office365_calendar"],
teamIds: [teamId],
genericCalendarSuffixes: [
"@group.v.calendar.google.com",
"@group.calendar.google.com",
"@import.calendar.google.com",
"@resource.calendar.google.com",
],
});
expect(mockSelectedCalendarRepository.findNextSubscriptionBatch).not.toHaveBeenCalledWith(
expect.objectContaining({
Expand All @@ -463,6 +487,12 @@ describe("CalendarSubscriptionService", () => {
take: 100,
integrations: ["google_calendar", "office365_calendar"],
teamIds: [],
genericCalendarSuffixes: [
"@group.v.calendar.google.com",
"@group.calendar.google.com",
"@import.calendar.google.com",
"@resource.calendar.google.com",
],
});
expect(subscribeSpy).not.toHaveBeenCalled();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,18 @@ export interface ISelectedCalendarRepository {
*
* @param take the number of calendars to take
* @param integrations the list of integrations
* @param genericCalendarSuffixes the list of generic calendar suffixes to exclude
*/
findNextSubscriptionBatch({
take,
teamIds,
integrations,
genericCalendarSuffixes,
}: {
take: number;
teamIds: number[];
integrations?: string[];
genericCalendarSuffixes?: string[];
}): Promise<SelectedCalendar[]>;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ describe("SelectedCalendarRepository", () => {
},
},
},
AND: undefined,
},
take: 10,
});
Expand Down Expand Up @@ -160,6 +161,7 @@ describe("SelectedCalendarRepository", () => {
},
},
},
AND: undefined,
Copy link
Contributor

Choose a reason for hiding this comment

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

Here too

},
take: 5,
});
Expand Down Expand Up @@ -189,6 +191,43 @@ describe("SelectedCalendarRepository", () => {
},
},
},
AND: undefined,
},
take: 10,
});

expect(result).toEqual(mockCalendars);
});

test("should filter out generic calendars when genericCalendarSuffixes is provided", async () => {
const mockCalendars = [mockSelectedCalendar];
vi.mocked(mockPrismaClient.selectedCalendar.findMany).mockResolvedValue(mockCalendars);

const genericSuffixes = ["@group.v.calendar.google.com", "@group.calendar.google.com"];

const result = await repository.findNextSubscriptionBatch({
take: 10,
teamIds: [1, 2],
integrations: ["google_calendar"],
genericCalendarSuffixes: genericSuffixes,
});

expect(mockPrismaClient.selectedCalendar.findMany).toHaveBeenCalledWith({
where: {
integration: { in: ["google_calendar"] },
OR: [{ syncSubscribedAt: null }, { channelExpiration: { lte: expect.any(Date) } }],
user: {
teams: {
some: {
teamId: { in: [1, 2] },
accepted: true,
},
},
},
AND: [
{ NOT: { externalId: { endsWith: "@group.v.calendar.google.com" } } },
{ NOT: { externalId: { endsWith: "@group.calendar.google.com" } } },
],
},
take: 10,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ export class SelectedCalendarRepository implements ISelectedCalendarRepository {
take,
teamIds,
integrations,
genericCalendarSuffixes,
}: {
take: number;
teamIds: number[];
integrations: string[];
genericCalendarSuffixes?: string[];
}) {
return this.prismaClient.selectedCalendar.findMany({
where: {
Expand All @@ -36,6 +38,9 @@ export class SelectedCalendarRepository implements ISelectedCalendarRepository {
},
},
},
AND: genericCalendarSuffixes?.map((suffix) => ({
NOT: { externalId: { endsWith: suffix } },
})),
},
take,
});
Expand Down
Loading