Skip to content
Open
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,16 @@ 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 Object.keys(GENERIC_CALENDAR_SUFFIXES).flatMap(
(provider) => GENERIC_CALENDAR_SUFFIXES[provider as CalendarSubscriptionProvider]
);
}
}
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,
},
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 @@ -2,7 +2,7 @@ import type { ISelectedCalendarRepository } from "@calcom/features/selectedCalen
import type { PrismaClient } from "@calcom/prisma";
import type { Prisma } from "@calcom/prisma/client";

export class SelectedCalendarRepository implements ISelectedCalendarRepository {
export class PrismaSelectedCalendarRepository implements ISelectedCalendarRepository {
constructor(private prismaClient: PrismaClient) {}
Comment on lines +5 to 6

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how SelectedCalendarRepository is exported and imported across the codebase
echo "=== Checking exports from the repository file ==="
cat packages/features/selectedCalendar/repositories/SelectedCalendarRepository.ts | head -20

echo -e "\n=== Checking all imports of SelectedCalendarRepository ==="
rg -n "import.*SelectedCalendarRepository" --type=ts

echo -e "\n=== Checking if there's an index file with re-exports ==="
fd -t f "index.ts" packages/features/selectedCalendar

Repository: agentic-review-benchmarks/cal.com-combined-coderabbit

Length of output: 3825


🏁 Script executed:

#!/bin/bash
# Check the re-export file at packages/lib/server/repository/selectedCalendar
echo "=== Checking re-export file ==="
cat packages/lib/server/repository/selectedCalendar.ts

echo -e "\n=== Checking what the test file actually imports ==="
cat packages/features/selectedCalendar/repositories/SelectedCalendarRepository.test.ts | head -10

Repository: agentic-review-benchmarks/cal.com-combined-coderabbit

Length of output: 14208


Fix test import for renamed class.

The class has been renamed from SelectedCalendarRepository to PrismaSelectedCalendarRepository in packages/features/selectedCalendar/repositories/SelectedCalendarRepository.ts. The test file at packages/features/selectedCalendar/repositories/SelectedCalendarRepository.test.ts imports the old name and will fail:

import { SelectedCalendarRepository } from "@calcom/features/selectedCalendar/repositories/SelectedCalendarRepository";

Update the test import to use the new class name PrismaSelectedCalendarRepository.

🤖 Prompt for AI Agents
In
`@packages/features/selectedCalendar/repositories/SelectedCalendarRepository.ts`
around lines 5 - 6, Update the test that still imports the old class name:
replace the import of SelectedCalendarRepository in the test file
(packages/features/selectedCalendar/repositories/SelectedCalendarRepository.test.ts)
with the renamed class PrismaSelectedCalendarRepository so the test imports {
PrismaSelectedCalendarRepository } from
"@calcom/features/selectedCalendar/repositories/SelectedCalendarRepository";
this will match the exported class name in PrismaSelectedCalendarRepository and
fix the failing import.


async findById(id: string) {
Expand All @@ -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,11 @@ export class SelectedCalendarRepository implements ISelectedCalendarRepository {
},
},
},
AND: genericCalendarSuffixes?.length
? genericCalendarSuffixes?.map((suffix) => ({
NOT: { externalId: { endsWith: suffix } },
}))
: undefined,
},
take,
});
Expand Down