Files
calendar/packages/lib/holidays/HolidayServiceCachingProxy.ts
T
e2119879ae refactor: apply biome formatting to packages/sms, prisma, emails, lib (#27880)
* refactor: apply biome formatting to small packages + packages/lib

Format packages/sms, packages/prisma, packages/platform/libraries,
packages/platform/examples, packages/platform/types, packages/emails,
and packages/lib.

Excludes packages/platform/examples/base/src/pages/[bookingUid].tsx
due to pre-existing lint errors.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* revert: remove packages/platform formatting changes

Revert biome formatting for packages/platform as requested.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 07:39:01 -03:00

124 lines
3.3 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;
}