File
Blob: src/worker/objects/common/collection-describe.ts
| 1 | import type { |
| 2 | CollectionDomainError, |
| 3 | DomainCollection, |
| 4 | DomainDescribeInput, |
| 5 | DomainObjectResources, |
| 6 | } from "@/worker/objects/common/collection-types"; |
| 7 | |
| 8 | export interface CollectionDescribeSpec< |
| 9 | TDb, |
| 10 | TCollection extends DomainCollection, |
| 11 | TObject, |
| 12 | TResource extends { href: string; syncToken?: string }, |
| 13 | TError extends CollectionDomainError, |
| 14 | > { |
| 15 | collectionKind: string; |
| 16 | ensureCollection(db: TDb, subjectId: string, nowMs: number, collectionName: string): TCollection | null; |
| 17 | homeResource(db: TDb): TResource; |
| 18 | listCollections(db: TDb, subjectId: string, nowMs: number): TCollection[]; |
| 19 | collectionResource(db: TDb, collection: TCollection): TResource; |
| 20 | objectByName(db: TDb, collectionId: string, objectName: string): TObject | undefined; |
| 21 | objectResource(db: TDb, collection: TCollection, object: TObject): TResource; |
| 22 | objectResources(db: TDb, collection: TCollection, maxResults: number): DomainObjectResources<TResource>; |
| 23 | syncToken(seq: number): string; |
| 24 | collectionNotFound(): TError; |
| 25 | objectNotFound(): TError; |
| 26 | } |
| 27 | |
| 28 | export function describeCollectionDomain< |
| 29 | TDb, |
| 30 | TCollection extends DomainCollection, |
| 31 | TObject, |
| 32 | TResource extends { href: string; syncToken?: string }, |
| 33 | TError extends CollectionDomainError, |
| 34 | >( |
| 35 | db: TDb, |
| 36 | input: DomainDescribeInput, |
| 37 | spec: CollectionDescribeSpec<TDb, TCollection, TObject, TResource, TError>, |
| 38 | ): { ok: true; resources: TResource[]; syncToken: string } | TError { |
| 39 | if (input.kind === "home") { |
| 40 | const collections = spec.listCollections(db, input.subjectId, input.nowMs); |
| 41 | return { |
| 42 | ok: true, |
| 43 | resources: [spec.homeResource(db), ...collections.map((collection) => spec.collectionResource(db, collection))], |
| 44 | syncToken: spec.syncToken(0), |
| 45 | }; |
| 46 | } |
| 47 | |
| 48 | const collection = spec.ensureCollection(db, input.subjectId, input.nowMs, input.collectionName ?? ""); |
| 49 | if (!collection) return spec.collectionNotFound(); |
| 50 | if (input.kind === spec.collectionKind) { |
| 51 | const resources = [spec.collectionResource(db, collection)]; |
| 52 | if (input.depth === "1") resources.push(...spec.objectResources(db, collection, input.maxResults).resources); |
| 53 | return { ok: true, resources, syncToken: resources[0]!.syncToken ?? spec.syncToken(0) }; |
| 54 | } |
| 55 | |
| 56 | const object = spec.objectByName(db, collection.id, input.objectName ?? ""); |
| 57 | if (!object) return spec.objectNotFound(); |
| 58 | return { ok: true, resources: [spec.objectResource(db, collection, object)], syncToken: spec.syncToken(0) }; |
| 59 | } |