UNPKG

@decaf-ts/for-couchdb

Version:
263 lines (262 loc) 13.2 kB
import { Adapter, Sequence, type SequenceOptions } from "@decaf-ts/core"; import { BaseError, Context, RepositoryFlags } from "@decaf-ts/db-decorators"; import "reflect-metadata"; import { Constructor, Model } from "@decaf-ts/decorator-validation"; import { MangoQuery } from "./types"; import { CouchDBStatement } from "./query"; /** * @description Abstract adapter for CouchDB database operations * @summary Provides a base implementation for CouchDB database operations, including CRUD operations, sequence management, and error handling * @template Y - The scope type * @template F - The repository flags type * @template C - The context type * @param {Y} scope - The scope for the adapter * @param {string} flavour - The flavour of the adapter * @param {string} [alias] - Optional alias for the adapter * @class * @example * // Example of extending CouchDBAdapter * class MyCouchDBAdapter extends CouchDBAdapter<MyScope, MyFlags, MyContext> { * constructor(scope: MyScope) { * super(scope, 'my-couchdb', 'my-alias'); * } * * // Implement abstract methods * async index<M extends Model>(...models: Constructor<M>[]): Promise<void> { * // Implementation * } * * async raw<R>(rawInput: MangoQuery, docsOnly: boolean): Promise<R> { * // Implementation * } * * async create(tableName: string, id: string | number, model: Record<string, any>, ...args: any[]): Promise<Record<string, any>> { * // Implementation * } * * async read(tableName: string, id: string | number, ...args: any[]): Promise<Record<string, any>> { * // Implementation * } * * async update(tableName: string, id: string | number, model: Record<string, any>, ...args: any[]): Promise<Record<string, any>> { * // Implementation * } * * async delete(tableName: string, id: string | number, ...args: any[]): Promise<Record<string, any>> { * // Implementation * } * } */ export declare abstract class CouchDBAdapter<Y, F extends RepositoryFlags, C extends Context<F>> extends Adapter<Y, MangoQuery, F, C> { protected constructor(scope: Y, flavour: string, alias?: string); /** * @description Creates a new CouchDB statement for querying * @summary Factory method that creates a new CouchDBStatement instance for building queries * @template M - The model type * @return {CouchDBStatement<M, any>} A new CouchDBStatement instance */ Statement<M extends Model>(): CouchDBStatement<M, any>; /** * @description Creates a new CouchDB sequence * @summary Factory method that creates a new CouchDBSequence instance for managing sequences * @param {SequenceOptions} options - The options for the sequence * @return {Promise<Sequence>} A promise that resolves to a new Sequence instance */ Sequence(options: SequenceOptions): Promise<Sequence>; /** * @description Initializes the adapter by creating indexes for all managed models * @summary Sets up the necessary database indexes for all models managed by this adapter * @return {Promise<void>} A promise that resolves when initialization is complete */ initialize(): Promise<void>; /** * @description Creates indexes for the given models * @summary Abstract method that must be implemented to create database indexes for the specified models * @template M - The model type * @param {...Constructor<M>} models - The model constructors to create indexes for * @return {Promise<void>} A promise that resolves when all indexes are created */ protected abstract index<M extends Model>(...models: Constructor<M>[]): Promise<void>; /** * @description Executes a raw Mango query against the database * @summary Abstract method that must be implemented to execute raw Mango queries * @template R - The result type * @param {MangoQuery} rawInput - The raw Mango query to execute * @param {boolean} docsOnly - Whether to return only the documents or the full response * @return {Promise<R>} A promise that resolves to the query result */ abstract raw<R>(rawInput: MangoQuery, docsOnly: boolean): Promise<R>; /** * @description Assigns metadata to a model * @summary Adds revision metadata to a model as a non-enumerable property * @param {Record<string, any>} model - The model to assign metadata to * @param {string} rev - The revision string to assign * @return {Record<string, any>} The model with metadata assigned */ protected assignMetadata(model: Record<string, any>, rev: string): Record<string, any>; /** * @description Assigns metadata to multiple models * @summary Adds revision metadata to multiple models as non-enumerable properties * @param models - The models to assign metadata to * @param {string[]} revs - The revision strings to assign * @return The models with metadata assigned */ protected assignMultipleMetadata(models: Record<string, any>[], revs: string[]): Record<string, any>[]; /** * @description Prepares a record for creation * @summary Adds necessary CouchDB fields to a record before creation * @param {string} tableName - The name of the table * @param {string|number} id - The ID of the record * @param {Record<string, any>} model - The model to prepare * @return A tuple containing the tableName, id, and prepared record */ protected createPrefix(tableName: string, id: string | number, model: Record<string, any>): (string | number | Record<string, any>)[]; /** * @description Creates a new record in the database * @summary Abstract method that must be implemented to create a new record * @param {string} tableName - The name of the table * @param {string|number} id - The ID of the record * @param {Record<string, any>} model - The model to create * @param {...any[]} args - Additional arguments * @return {Promise<Record<string, any>>} A promise that resolves to the created record */ abstract create(tableName: string, id: string | number, model: Record<string, any>, ...args: any[]): Promise<Record<string, any>>; /** * @description Prepares multiple records for creation * @summary Adds necessary CouchDB fields to multiple records before creation * @param {string} tableName - The name of the table * @param {string[]|number[]} ids - The IDs of the records * @param models - The models to prepare * @return A tuple containing the tableName, ids, and prepared records * @throws {InternalError} If ids and models arrays have different lengths */ protected createAllPrefix(tableName: string, ids: string[] | number[], models: Record<string, any>[]): (string | string[] | number[] | Record<string, any>[])[]; /** * @description Reads a record from the database * @summary Abstract method that must be implemented to read a record * @param {string} tableName - The name of the table * @param {string|number} id - The ID of the record * @param {...any[]} args - Additional arguments * @return {Promise<Record<string, any>>} A promise that resolves to the read record */ abstract read(tableName: string, id: string | number, ...args: any[]): Promise<Record<string, any>>; /** * @description Prepares a record for update * @summary Adds necessary CouchDB fields to a record before update * @param {string} tableName - The name of the table * @param {string|number} id - The ID of the record * @param model - The model to prepare * @return A tuple containing the tableName, id, and prepared record * @throws {InternalError} If no revision number is found in the model */ updatePrefix(tableName: string, id: string | number, model: Record<string, any>): (string | number | Record<string, any>)[]; /** * @description Updates a record in the database * @summary Abstract method that must be implemented to update a record * @param {string} tableName - The name of the table * @param {string|number} id - The ID of the record * @param {Record<string, any>} model - The model to update * @param {any[]} args - Additional arguments * @return A promise that resolves to the updated record */ abstract update(tableName: string, id: string | number, model: Record<string, any>, ...args: any[]): Promise<Record<string, any>>; /** * @description Prepares multiple records for update * @summary Adds necessary CouchDB fields to multiple records before update * @param {string} tableName - The name of the table * @param {string[]|number[]} ids - The IDs of the records * @param models - The models to prepare * @return A tuple containing the tableName, ids, and prepared records * @throws {InternalError} If ids and models arrays have different lengths or if no revision number is found in a model */ protected updateAllPrefix(tableName: string, ids: string[] | number[], models: Record<string, any>[]): (string | string[] | number[] | Record<string, any>[])[]; /** * @description Deletes a record from the database * @summary Abstract method that must be implemented to delete a record * @param {string} tableName - The name of the table * @param {string|number} id - The ID of the record * @param {any[]} args - Additional arguments * @return A promise that resolves to the deleted record */ abstract delete(tableName: string, id: string | number, ...args: any[]): Promise<Record<string, any>>; /** * @description Generates a CouchDB document ID * @summary Combines the table name and ID to create a CouchDB document ID * @param {string} tableName - The name of the table * @param {string|number} id - The ID of the record * @return {string} The generated CouchDB document ID */ protected generateId(tableName: string, id: string | number): string; /** * @description Parses an error and converts it to a BaseError * @summary Converts various error types to appropriate BaseError subtypes * @param {Error|string} err - The error to parse * @param {string} [reason] - Optional reason for the error * @return {BaseError} The parsed error as a BaseError */ parseError(err: Error | string, reason?: string): BaseError; /** * @description Checks if an attribute is reserved * @summary Determines if an attribute name is reserved in CouchDB * @param {string} attr - The attribute name to check * @return {boolean} True if the attribute is reserved, false otherwise */ protected isReserved(attr: string): boolean; /** * @description Static method to parse an error and convert it to a BaseError * @summary Converts various error types to appropriate BaseError subtypes based on error codes and messages * @param {Error|string} err - The error to parse * @param {string} [reason] - Optional reason for the error * @return {BaseError} The parsed error as a BaseError * @mermaid * sequenceDiagram * participant Caller * participant parseError * participant ErrorTypes * * Caller->>parseError: err, reason * Note over parseError: Check if err is already a BaseError * alt err is BaseError * parseError-->>Caller: return err * else err is string * Note over parseError: Extract code from string * alt code matches "already exist|update conflict" * parseError->>ErrorTypes: new ConflictError(code) * ErrorTypes-->>Caller: ConflictError * else code matches "missing|deleted" * parseError->>ErrorTypes: new NotFoundError(code) * ErrorTypes-->>Caller: NotFoundError * end * else err has code property * Note over parseError: Extract code and reason * else err has statusCode property * Note over parseError: Extract code and reason * else * Note over parseError: Use err.message as code * end * * Note over parseError: Switch on code * alt code is 401, 412, or 409 * parseError->>ErrorTypes: new ConflictError(reason) * ErrorTypes-->>Caller: ConflictError * else code is 404 * parseError->>ErrorTypes: new NotFoundError(reason) * ErrorTypes-->>Caller: NotFoundError * else code is 400 * alt code matches "No index exists" * parseError->>ErrorTypes: new IndexError(err) * ErrorTypes-->>Caller: IndexError * else * parseError->>ErrorTypes: new InternalError(err) * ErrorTypes-->>Caller: InternalError * end * else code matches "ECONNREFUSED" * parseError->>ErrorTypes: new ConnectionError(err) * ErrorTypes-->>Caller: ConnectionError * else * parseError->>ErrorTypes: new InternalError(err) * ErrorTypes-->>Caller: InternalError * end */ protected static parseError(err: Error | string, reason?: string): BaseError; }