feat: filter generic calendars from subscription batch (#26774)
Add filtering to SelectedCalendarRepository.findNextSubscriptionBatch to exclude generic calendars (holidays, contacts, shared, imported, resources) based on their externalId suffixes. Changes: - Add GENERIC_CALENDAR_SUFFIXES constant mapping providers to their generic calendar suffixes in AdaptersFactory - Add getGenericCalendarSuffixes method to AdapterFactory interface - Update findNextSubscriptionBatch to accept and use genericCalendarSuffixes parameter to filter out calendars with matching externalId suffixes - Update CalendarSubscriptionService.checkForNewSubscriptions to pass generic calendar suffixes from the adapter factory - Update tests to cover the new filtering logic Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
Devin AI
parent
a6d7e4f107
commit
a20ad44209
@@ -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[];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)));
|
||||
|
||||
+30
@@ -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 = {
|
||||
@@ -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);
|
||||
});
|
||||
@@ -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");
|
||||
@@ -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({
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
+3
@@ -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[]>;
|
||||
|
||||
/**
|
||||
|
||||
@@ -131,6 +131,7 @@ describe("SelectedCalendarRepository", () => {
|
||||
},
|
||||
},
|
||||
},
|
||||
AND: undefined,
|
||||
},
|
||||
take: 10,
|
||||
});
|
||||
@@ -160,6 +161,7 @@ describe("SelectedCalendarRepository", () => {
|
||||
},
|
||||
},
|
||||
},
|
||||
AND: undefined,
|
||||
},
|
||||
take: 5,
|
||||
});
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
@@ -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: {
|
||||
@@ -36,6 +38,9 @@ export class SelectedCalendarRepository implements ISelectedCalendarRepository {
|
||||
},
|
||||
},
|
||||
},
|
||||
AND: genericCalendarSuffixes?.map((suffix) => ({
|
||||
NOT: { externalId: { endsWith: suffix } },
|
||||
})),
|
||||
},
|
||||
take,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user