import { EncryptedMetadataManager, SettingsManager } from "@saleor/app-sdk/settings-manager"; import { ZodSchema, z } from "zod"; import { Logger, createLogger } from "../../lib/logger"; import { logUtils } from "./log-utils"; export interface MetadataLogs { getAll(): Promise; push(payload: unknown): Promise; } type ClientLogsMetadataRepositoryOptions = { limit: number; }; export type ClientLogsMetadataRepositoryParams = { settingsManager: EncryptedMetadataManager; schema: ZodSchema; metadataKey: string; options: ClientLogsMetadataRepositoryOptions; }; /** * ClientLogsMetadataRepository is a generic class that can be used to store logs in the app metadata. * The name "clientLogs" is used to distinguish it from the server logs. Client logs can appear in the app UI. */ export class ClientLogsMetadataRepository implements MetadataLogs { private readonly settingsManager: EncryptedMetadataManager; private readonly schema: ZodSchema; private readonly metadataKey: string; private readonly options: ClientLogsMetadataRepositoryOptions; private readonly logger: Logger; private logs: TLog[] = []; constructor({ settingsManager, schema, metadataKey, options, }: ClientLogsMetadataRepositoryParams) { this.settingsManager = settingsManager; this.schema = schema; this.metadataKey = metadataKey; this.options = options; this.logger = createLogger({ name: "ClientLogsMetadataRepository", }); } async getAll() { if (this.logs.length) { this.logger.debug(`Returning cached logs for key ${this.metadataKey}`); return this.logs; } const metadata = await this.settingsManager.get(this.metadataKey); if (!metadata) { this.logger.debug(`No metadata found for key ${this.metadataKey}`); return []; } const parsedMetadata = JSON.parse(metadata); const validation = z.array(this.schema).safeParse(parsedMetadata); if (!validation.success) { throw new Error(validation.error.message); } const logs = validation.data; this.logs = logs; this.logger.debug(`Returning logs for key ${this.metadataKey}`); return logs; } async push(payload: unknown) { const validation = this.schema.safeParse(payload); if (!validation.success) { throw new Error(validation.error.message); } const log = validation.data; const logs = await this.getAll(); const nextLogs = logUtils.unshiftItemToLimitedArray(logs, log, this.options.limit); this.logs = nextLogs; this.logger.debug(`Pushing log to metadata for key ${this.metadataKey}`); await this.settingsManager.set({ key: this.metadataKey, value: JSON.stringify(nextLogs), }); } }