packages/@aws-cdk/toolkit-lib/lib/api/notices/cached-data-source.ts (65 lines of code) (raw):

import * as fs from 'fs-extra'; import type { Notice, NoticeDataSource } from './types'; import type { IoDefaultMessages } from '../io/private'; interface CachedNotices { expiration: number; notices: Notice[]; } const TIME_TO_LIVE_SUCCESS = 60 * 60 * 1000; // 1 hour const TIME_TO_LIVE_ERROR = 1 * 60 * 1000; // 1 minute export class CachedDataSource implements NoticeDataSource { constructor( private readonly ioMessages: IoDefaultMessages, private readonly fileName: string, private readonly dataSource: NoticeDataSource, private readonly skipCache?: boolean) { } async fetch(): Promise<Notice[]> { const cachedData = await this.load(); const data = cachedData.notices; const expiration = cachedData.expiration ?? 0; if (Date.now() > expiration || this.skipCache) { const freshData = await this.fetchInner(); await this.save(freshData); return freshData.notices; } else { this.ioMessages.debug(`Reading cached notices from ${this.fileName}`); return data; } } private async fetchInner(): Promise<CachedNotices> { try { return { expiration: Date.now() + TIME_TO_LIVE_SUCCESS, notices: await this.dataSource.fetch(), }; } catch (e) { this.ioMessages.debug(`Could not refresh notices: ${e}`); return { expiration: Date.now() + TIME_TO_LIVE_ERROR, notices: [], }; } } private async load(): Promise<CachedNotices> { const defaultValue = { expiration: 0, notices: [], }; try { return fs.existsSync(this.fileName) ? await fs.readJSON(this.fileName) as CachedNotices : defaultValue; } catch (e) { this.ioMessages.debug(`Failed to load notices from cache: ${e}`); return defaultValue; } } private async save(cached: CachedNotices): Promise<void> { try { await fs.writeJSON(this.fileName, cached); } catch (e) { this.ioMessages.debug(`Failed to store notices in the cache: ${e}`); } } }