122 lines
4.9 KiB
TypeScript
122 lines
4.9 KiB
TypeScript
import { CalendarSubscriptionService } from "@calcom/features/calendar-subscription/lib/CalendarSubscriptionService";
|
|
import { CalendarCacheEventRepository } from "@calcom/features/calendar-subscription/lib/cache/CalendarCacheEventRepository";
|
|
import { CalendarCacheEventService } from "@calcom/features/calendar-subscription/lib/cache/CalendarCacheEventService";
|
|
import { CalendarCacheWrapper } from "@calcom/features/calendar-subscription/lib/cache/CalendarCacheWrapper";
|
|
import { CalendarTelemetryWrapper } from "@calcom/features/calendar-subscription/lib/telemetry/CalendarTelemetryWrapper";
|
|
import { FeaturesRepository } from "@calcom/features/flags/features.repository";
|
|
import logger from "@calcom/lib/logger";
|
|
import { isTelemetryEnabled } from "@calcom/lib/sentryWrapper";
|
|
import { prisma } from "@calcom/prisma";
|
|
import type { Calendar, CalendarFetchMode } from "@calcom/types/Calendar";
|
|
import type { CredentialForCalendarService } from "@calcom/types/Credential";
|
|
|
|
import { CalendarServiceMap } from "../calendar.services.generated";
|
|
|
|
const log = logger.getSubLogger({ prefix: ["CalendarManager"] });
|
|
|
|
export const getCalendar = async (
|
|
credential: CredentialForCalendarService | null,
|
|
mode: CalendarFetchMode = "none"
|
|
): Promise<Calendar | null> => {
|
|
if (!credential || !credential.key) return null;
|
|
let { type: calendarType } = credential;
|
|
if (calendarType?.endsWith("_other_calendar")) {
|
|
calendarType = calendarType.split("_other_calendar")[0];
|
|
}
|
|
// Backwards compatibility until CRM manager is created
|
|
if (calendarType?.endsWith("_crm")) {
|
|
calendarType = calendarType.split("_crm")[0];
|
|
}
|
|
|
|
const calendarAppImportFn =
|
|
CalendarServiceMap[calendarType.split("_").join("") as keyof typeof CalendarServiceMap];
|
|
|
|
if (!calendarAppImportFn) {
|
|
log.warn(`calendar of type ${calendarType} is not implemented`);
|
|
return null;
|
|
}
|
|
|
|
const calendarApp = await calendarAppImportFn;
|
|
|
|
const createCalendarService = calendarApp.default;
|
|
|
|
if (!createCalendarService || typeof createCalendarService !== "function") {
|
|
log.warn(`calendar of type ${calendarType} is not implemented`);
|
|
return null;
|
|
}
|
|
|
|
// Determine if we should use cache based on mode:
|
|
// - "slots": Check feature flags and use cache when available (for getting actual calendar availability)
|
|
// - "overlay": Don't use cache (for overlay calendar availability)
|
|
// - "booking": Don't use cache (for booking confirmation)
|
|
// - "none": Don't use cache (for operations that don't use getAvailability, e.g., deleteEvent, listCalendars)
|
|
let shouldServeCache = false;
|
|
if (mode === "slots") {
|
|
const featuresRepository = new FeaturesRepository(prisma);
|
|
const [isCalendarSubscriptionCacheEnabled, isCalendarSubscriptionCacheEnabledForUser] = await Promise.all(
|
|
[
|
|
featuresRepository.checkIfFeatureIsEnabledGlobally(
|
|
CalendarSubscriptionService.CALENDAR_SUBSCRIPTION_CACHE_FEATURE
|
|
),
|
|
featuresRepository.checkIfUserHasFeatureNonHierarchical(
|
|
credential.userId as number,
|
|
CalendarSubscriptionService.CALENDAR_SUBSCRIPTION_CACHE_FEATURE
|
|
),
|
|
]
|
|
);
|
|
shouldServeCache = isCalendarSubscriptionCacheEnabled && isCalendarSubscriptionCacheEnabledForUser;
|
|
log.debug("Cache feature flag check", {
|
|
credentialId: credential.id,
|
|
userId: credential.userId,
|
|
mode,
|
|
isCalendarSubscriptionCacheEnabled,
|
|
isCalendarSubscriptionCacheEnabledForUser,
|
|
shouldServeCache,
|
|
});
|
|
} else {
|
|
log.debug("Cache disabled for mode", {
|
|
credentialId: credential.id,
|
|
userId: credential.userId,
|
|
mode,
|
|
});
|
|
}
|
|
|
|
const isCacheSupported = CalendarCacheEventService.isCalendarTypeSupported(calendarType);
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
const originalCalendar = createCalendarService(credential as any);
|
|
|
|
// Determine if we should use cache
|
|
const useCache = isCacheSupported && shouldServeCache;
|
|
|
|
// Build the calendar chain: original -> cache (if enabled) -> telemetry (if enabled)
|
|
let calendar: Calendar = originalCalendar;
|
|
|
|
if (useCache) {
|
|
log.debug(`Calendar Cache is enabled, using CalendarCacheWrapper for credential ${credential.id}`);
|
|
const calendarCacheEventRepository = new CalendarCacheEventRepository(prisma);
|
|
calendar = new CalendarCacheWrapper({
|
|
originalCalendar: calendar,
|
|
calendarCacheEventRepository,
|
|
});
|
|
}
|
|
|
|
// Wrap ALL calendars with telemetry when telemetry is enabled
|
|
// This provides consistent metrics for all calendar types
|
|
if (isTelemetryEnabled()) {
|
|
log.debug(
|
|
`Using CalendarTelemetryWrapper for credential ${credential.id} (cacheSupported: ${isCacheSupported}, cacheEnabled: ${useCache})`
|
|
);
|
|
calendar = new CalendarTelemetryWrapper({
|
|
originalCalendar: calendar,
|
|
calendarType,
|
|
cacheSupported: isCacheSupported,
|
|
cacheEnabled: useCache,
|
|
credentialId: credential.id,
|
|
mode,
|
|
});
|
|
}
|
|
|
|
return calendar;
|
|
};
|