import SDKAdapter from '../core/adapter.js' import { SDKCollection } from '../core/collection.js' import { SDKModel } from '../core/model.js' import { SDKModelSchema } from '../core/schema.js' import { nanoid } from '../core/transport.js' import collectionSchema from '../schemas/collection.json' assert { type: 'json' } import type { SDK } from '../sdk.js' import { Atleast, RequiredKeys } from '../utils/typescript.js' import type { ComponentModel } from './components.js' import { LibraryModel, NanoId } from './libraries.js' /** Defines the schema for Collection. */ export interface Collection { /** The identifier of the Library that Collection belongs to */ libraryId: NanoId /** The identifier of the Collection */ id: NanoId /** * The name of the Collection. * * @minLength 1 * @maxLength 128 */ name: string /** Specifies whether the Collection is the default one or not, each Library can have only one default Collection */ isDefault: boolean /** * Specifies the order of appearance of the Collection. * * @minLength 0 * @maxLength 64 * @nullable */ orderIdx?: string /** The timestamp the Collection was created at */ createdAt?: Date /** The timestamp when the Collection was last modified at */ modifiedAt?: Date } export class Collections extends SDKAdapter { fetch(collection: Pick): Promise { const { libraryId } = collection return this.sdk.fetchJSON(`/libraries/${libraryId}/collections`) } get(collection: Pick): Promise { const { libraryId, id } = collection return this.sdk.fetchJSON(`/libraries/${libraryId}/collections/${id}`) } post(collection: Atleast): Promise { const { libraryId } = collection return this.sdk.fetchJSON(`/libraries/${libraryId}/collections`, { method: 'POST', body: JSON.stringify({ ...collection }) }) } put(collection: Atleast) { return this.post(collection) } delete(collection: Pick): Promise { const { libraryId, id } = collection return this.sdk.fetchJSON(`/libraries/${libraryId}/collections/${id}`, { method: 'DELETE' }) } } export function getCollectionDefaults(sdk: SDK) { const now = new Date() return { id: nanoid(10), isDefault: false, orderIdx: null as string, createdAt: now, modifiedAt: now } } export type CollectionMinimal = RequiredKeys< Collection, typeof getCollectionDefaults, T > export interface CollectionImplicit { libraryId: LibraryModel['id'] library?: LibraryModel components?: Partial[] } export interface CollectionParams extends Collection, CollectionImplicit {} export interface CollectionModel extends CollectionParams {} /** * Represents a CollectionModel, a groupping of components. * This class provides methods and properties for working with collections. */ export class CollectionModel extends SDKModel implements CollectionParams { /** * Gets the adapter for the collection. */ get adapter() { return this.sdk.Collections } components: ReturnType /** * Defines additional properties for the collection. */ defineProperties() { super.defineProperties() this.sdk.utils.defineCollectionAccessor(this, 'components', this.constructComponents(), {}, this.getHiddenProps()) } /** * Constructs the components for the collection. * * @returns The constructed components SDK collection. */ constructComponents() { return SDKCollection.construct(this.sdk.Component, () => ({ collection: this, collectionId: this.id, library: this.library, libraryId: this.libraryId })) } /** * Gets the hidden properties of the collection. These properties will be made non-iterable. * * @returns An array of hidden properties. */ getHiddenProps() { return ['components', 'library'] } /** * Gets the default values for the collection. These values will be used if not provided. * * @param sdk - The SDK instance. * @returns The default values. */ getDefaults(sdk: SDK) { return getCollectionDefaults(sdk) } /** * Compares the collection with another collection for sorting purposes. * * @param other - The other collection to compare. * @returns A number indicating the sort order. */ sortCompare(other: this): number { if ((this.orderIdx || '') < (other.orderIdx || '')) { return 1 } if ((this.orderIdx || '') > (other.orderIdx || '')) { return -1 } if (this.createdAt > other.createdAt) { return 1 } return 0 } /** * Checks if the collection matches the given search string. * * @param search - The search string. * @returns True if the collection matches the search string, false otherwise. */ matchesSearch(search: string) { return this.name.toLowerCase().includes(search.toLowerCase()) } /** * Gets the path of the collection. * * @returns The path of the collection. */ getPath() { return `/libraries/${this.libraryId}/collections/${this.id}` } serialize() { return { ...super.serialize(), components: this.components.map((component) => component.serialize()) } } /** * Copies the collection (with components by default) to another library. * * @param target - The target library to copy the collection to. * @param includeComponents - Optional. Specifies whether to include the components in the copy. Default is true. * @returns A promise that resolves to the copied collection. */ async copyToLibrary(target: LibraryModel, includeComponents = true) { // if current collection is default, use default collection in the target library as well const collection = (this.isDefault && target.collections.find((c) => c.isDefault)) || (await target.collections .upsertItem({ ...this, id: this.id }) .save()) if (includeComponents) { await Promise.all(this.components.map((component) => component.copyToCollection(collection))) } return collection } /** * Gets the schema for the CollectionModel class. * * @returns The schema for the CollectionModel class. */ static get schema(): SDKModelSchema { return new SDKModelSchema(CollectionModel, collectionSchema) } }