From 96d7f1d024bbbe94a70f09823ff922068293c8d7 Mon Sep 17 00:00:00 2001 From: Niva Vaani Sivakumar Date: Thu, 26 Jun 2025 14:39:26 -0400 Subject: [PATCH] first draft --- .../src/compass-query-storage.ts | 130 ++++++++++++------ 1 file changed, 90 insertions(+), 40 deletions(-) diff --git a/packages/my-queries-storage/src/compass-query-storage.ts b/packages/my-queries-storage/src/compass-query-storage.ts index e9b6b3b6169..4b64166a83f 100644 --- a/packages/my-queries-storage/src/compass-query-storage.ts +++ b/packages/my-queries-storage/src/compass-query-storage.ts @@ -12,69 +12,119 @@ export type QueryStorageOptions = { basepath?: string; }; -export interface QueryStorageBackend { - loadAll(namespace?: string): Promise; +export interface QueryDocumentBase { + _id: string; + _lastExecuted: Date; + _ns?: string; +} + +// this will be either UserDataBackend (for desktop filesystem storage) or +// ApiBackend (for Compass-Web cloud storage) +export interface QueryStorageBackend { + loadAll(): Promise; + readOne(id: string): Promise; updateAttributes(id: string, data: Partial): Promise; delete(id: string): Promise; - saveQuery(data: Omit): Promise; + saveQuery(data: TData): Promise; +} + +export abstract class CompassQueryStorage + implements QueryStorageBackend +{ + protected backend: QueryStorageBackend; + + constructor(backend: QueryStorageBackend) { + this.backend = backend; + } + + async loadAll(namespace?: string): Promise { + const items = await this.backend.loadAll(); + return items + .sort((a, b) => b._lastExecuted.getTime() - a._lastExecuted.getTime()) + .filter((x) => !namespace || x._ns === namespace); + } + + async readOne(id: string): Promise { + return this.backend.readOne(id); + } + + async updateAttributes(id: string, patch: Partial): Promise { + return this.backend.updateAttributes(id, patch); + } + + async delete(id: string): Promise { + return this.backend.delete(id); + } + + abstract saveQuery(data: Omit): Promise; } -export abstract class CompassQueryStorage< +// UserDataBackend preserves all major functionality and adapts UserData to the QueryStorageBackend interface +export class UserDataBackend< TSchema extends z.Schema, - TData extends z.output = z.output + TData extends z.output & QueryDocumentBase > implements QueryStorageBackend { - protected readonly userData: UserData; - constructor( - schemaValidator: TSchema, - protected readonly folder: string, - protected readonly options: QueryStorageOptions - ) { - this.userData = new UserData(schemaValidator, { - subdir: folder, - basePath: options.basepath, - serialize: (content) => EJSON.stringify(content, undefined, 2), - deserialize: (content) => EJSON.parse(content), - }); + private userData: UserData; + + constructor(userData: UserData) { + this.userData = userData; } - async loadAll(namespace?: string): Promise { + async loadAll(): Promise { try { const { data } = await this.userData.readAll(); - const sortedData = data - .sort((a, b) => { - return b._lastExecuted.getTime() - a._lastExecuted.getTime(); - }) - .filter((x) => !namespace || x._ns === namespace); - return sortedData; + return data as TData[]; } catch { return []; } } - async updateAttributes(id: string, data: Partial): Promise { - await this.userData.write(id, { - ...((await this.userData.readOne(id)) ?? {}), - ...data, - }); - return await this.userData.readOne(id); + async readOne(id: string): Promise { + try { + const result = await this.userData.readOne(id); + return result as TData | undefined; + } catch { + // not sure if this is what I should be returning here + return undefined; + } } - async delete(id: string) { + async updateAttributes(id: string, patch: Partial): Promise { + // this is kind of a different approach than the original UserData + // just more error handling + const current = await this.readOne(id); + if (!current) { + // will throwing Errors here be bubbled up properly? + // or should I return undefined or null? + throw new Error(`Record not found for id ${id}`); + } + const updated = { ...current, ...patch }; + await this.userData.write(id, updated as z.input); + const after = await this.readOne(id); + if (!after) { + throw new Error(`Failed to update record for id ${id}`); + } + return after; + } + + async delete(id: string): Promise { return await this.userData.delete(id); } - abstract saveQuery(data: any): Promise; + async saveQuery(data: TData): Promise { + await this.userData.write(data._id, data as z.input); + } } export class CompassRecentQueryStorage - extends CompassQueryStorage + extends CompassQueryStorage implements RecentQueryStorage { private readonly maxAllowedQueries = 30; - constructor(options: QueryStorageOptions = {}) { - super(RecentQuerySchema, 'RecentQueries', options); + constructor(backend: QueryStorageBackend) { + super(backend); } async saveQuery( @@ -93,16 +143,16 @@ export class CompassRecentQueryStorage _id, _lastExecuted: new Date(), }; - await this.userData.write(_id, recentQuery); + await this.backend.saveQuery(recentQuery); } } export class CompassFavoriteQueryStorage - extends CompassQueryStorage + extends CompassQueryStorage implements FavoriteQueryStorage { - constructor(options: QueryStorageOptions = {}) { - super(FavoriteQuerySchema, 'FavoriteQueries', options); + constructor(backend: QueryStorageBackend) { + super(backend); } async saveQuery( @@ -118,6 +168,6 @@ export class CompassFavoriteQueryStorage _lastExecuted: new Date(), _dateSaved: new Date(), }; - await this.userData.write(_id, favoriteQuery); + await this.backend.saveQuery(favoriteQuery); } }