Files
cal.diy/packages/lib/holidays/HolidayServiceCachingProxy.ts
T
Benny JooandDevin AI 96bec9f7b9 refactor: Move repositories from @calcom/lib to @calcom/features domain folders (#27570)
* refactor: move repositories from lib to features domain folders

- Move HolidayRepository to features/holidays/repositories
- Move PrismaTrackingRepository to features/bookings/repositories
- Move PrismaBookingPaymentRepository to features/bookings/repositories
- Move PrismaRoutingFormResponseRepository to features/routing-forms/repositories
- Move PrismaAssignmentReasonRepository to features/assignment-reason/repositories
- Move VerificationTokenRepository to features/auth/repositories
- Move WorkspacePlatformRepository to features/workspace-platform/repositories
- Move DTO files to their respective feature domains
- Merge lib DestinationCalendarRepository into features version
- Merge lib SelectedCalendarRepository into features version
- Update all import paths across the codebase

This follows the vertical slice architecture pattern by organizing
repositories by domain rather than by technical layer.

Co-Authored-By: benny@cal.com <sldisek783@gmail.com>

* fix: update VerificationTokenService import path to new location

Co-Authored-By: benny@cal.com <sldisek783@gmail.com>

* fix: update test file imports to use new repository locations

Co-Authored-By: benny@cal.com <sldisek783@gmail.com>

* mv

* fix structure

* fix

* refactor: merge unit tests for SelectedCalendarRepository into single file

Co-Authored-By: benny@cal.com <sldisek783@gmail.com>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-02-09 08:41:04 -03:00

117 lines
3.2 KiB
TypeScript

import dayjs from "@calcom/dayjs";
import { HolidayRepository } from "@calcom/features/holidays/repositories/HolidayRepository";
import { GOOGLE_HOLIDAY_CALENDARS, HOLIDAY_CACHE_DAYS } from "./constants";
import { getGoogleCalendarClient, type GoogleCalendarClient, type GoogleCalendarHoliday } from "./GoogleCalendarClient";
export interface CachedHoliday {
id: string;
countryCode: string;
eventId: string;
name: string;
date: Date;
year: number;
}
export class HolidayServiceCachingProxy {
private calendarClient: GoogleCalendarClient;
constructor(calendarClient?: GoogleCalendarClient) {
this.calendarClient = calendarClient || getGoogleCalendarClient();
}
private async isCacheStale(countryCode: string, year: number): Promise<boolean> {
const staleDate = dayjs().subtract(HOLIDAY_CACHE_DAYS, "days").toDate();
const cachedEntry = await HolidayRepository.findFirstCacheEntry({ countryCode, year });
if (!cachedEntry) {
return true;
}
return cachedEntry.updatedAt < staleDate;
}
private async refreshCache(countryCode: string, year: number): Promise<void> {
const calendarConfig = GOOGLE_HOLIDAY_CALENDARS[countryCode];
if (!calendarConfig) {
return;
}
try {
const holidays = await this.calendarClient.fetchHolidays(countryCode, year);
await HolidayRepository.refreshCache({
countryCode,
year,
holidays: holidays.map((h: GoogleCalendarHoliday) => ({
countryCode: h.countryCode,
calendarId: calendarConfig.calendarId,
eventId: h.eventId,
name: h.name,
date: h.date,
year: h.year,
})),
});
} catch (error) {
console.error(`Failed to refresh holiday cache for ${countryCode}:`, error);
}
}
async getHolidaysForCountry(countryCode: string, year: number): Promise<CachedHoliday[]> {
const isStale = await this.isCacheStale(countryCode, year);
if (isStale) {
await this.refreshCache(countryCode, year);
}
const cached = await HolidayRepository.findManyCachedHolidays({ countryCode, year });
return cached.map((h): CachedHoliday => ({
id: h.id,
countryCode: h.countryCode,
eventId: h.eventId,
name: h.name,
date: h.date,
year: h.year,
}));
}
async getHolidaysInRange(countryCode: string, startDate: Date, endDate: Date): Promise<CachedHoliday[]> {
const startYear = dayjs(startDate).year();
const endYear = dayjs(endDate).year();
for (let year = startYear; year <= endYear; year++) {
const isStale = await this.isCacheStale(countryCode, year);
if (isStale) {
await this.refreshCache(countryCode, year);
}
}
const cached = await HolidayRepository.findCachedHolidaysInRange({
countryCode,
startDate,
endDate,
});
return cached.map((h): CachedHoliday => ({
id: h.id,
countryCode: h.countryCode,
eventId: h.eventId,
name: h.name,
date: h.date,
year: h.year,
}));
}
}
let defaultProxy: HolidayServiceCachingProxy | null = null;
export function getHolidayServiceCachingProxy(): HolidayServiceCachingProxy {
if (!defaultProxy) {
defaultProxy = new HolidayServiceCachingProxy();
}
return defaultProxy;
}