UNPKG

@decaf-ts/for-nano

Version:

decaf-ts persistence adapter for CouchDB via nano

391 lines (390 loc) 16.6 kB
import { Context, OperationKeys } from "@decaf-ts/db-decorators"; import "reflect-metadata"; import { CouchDBAdapter, MangoQuery } from "@decaf-ts/for-couchdb"; import { DocumentScope, ServerScope } from "nano"; import { Constructor, Model } from "@decaf-ts/decorator-validation"; import { NanoFlags } from "./types"; import { RelationsMetadata } from "@decaf-ts/core"; import { NanoRepository } from "./NanoRepository"; import { NanoDispatch } from "./NanoDispatch"; /** * @description Sets the creator or updater field in a model based on the user in the context * @summary Callback function used in decorators to automatically set the created_by or updated_by fields * with the username from the context when a document is created or updated * @template M - Type extending Model * @template R - Type extending NanoRepository<M> * @template V - Type extending RelationsMetadata * @param {R} this - The repository instance * @param {Context<NanoFlags>} context - The operation context containing user information * @param {V} data - The relation metadata * @param key - The property key to set with the username * @param {M} model - The model instance being created or updated * @return {Promise<void>} A promise that resolves when the operation is complete * @function createdByOnNanoCreateUpdate * @memberOf module:for-nano * @mermaid * sequenceDiagram * participant F as createdByOnNanoCreateUpdate * participant C as Context * participant M as Model * F->>C: get("user") * C-->>F: user object * F->>M: set key to user.name * Note over F: If no user in context * F-->>F: throw UnsupportedError */ export declare function createdByOnNanoCreateUpdate<M extends Model, R extends NanoRepository<M>, V extends RelationsMetadata>(this: R, context: Context<NanoFlags>, data: V, key: keyof M, model: M): Promise<void>; /** * @description Adapter for interacting with Nano databases * @summary Provides a standardized interface for performing CRUD operations on Nano databases, * extending the CouchDB adapter with Nano-specific functionality. This adapter handles document * creation, reading, updating, and deletion, as well as bulk operations and index management. * @template DocumentScope - The Nano document scope type * @template NanoFlags - Configuration flags for Nano operations * @template Context - Context type for operations * @param {DocumentScope<any>} scope - The Nano document scope to use for database operations * @param {string} [alias] - Optional alias for the adapter * @class NanoAdapter * @example * ```typescript * // Connect to a Nano database * const server = NanoAdapter.connect('admin', 'password', 'localhost:5984'); * const db = server.db.use('my_database'); * * // Create an adapter instance * const adapter = new NanoAdapter(db); * * // Use the adapter for database operations * const document = await adapter.read('users', '123'); * ``` * @mermaid * classDiagram * class CouchDBAdapter { * +flags() * +Dispatch() * +index() * +create() * +read() * +update() * +delete() * } * class NanoAdapter { * +flags() * +Dispatch() * +index() * +create() * +createAll() * +read() * +readAll() * +update() * +updateAll() * +delete() * +deleteAll() * +raw() * +static connect() * +static createDatabase() * +static deleteDatabase() * +static createUser() * +static deleteUser() * +static decoration() * } * CouchDBAdapter <|-- NanoAdapter */ export declare class NanoAdapter extends CouchDBAdapter<DocumentScope<any>, NanoFlags, Context<NanoFlags>> { constructor(scope: DocumentScope<any>, alias?: string); /** * @description Generates flags for database operations * @summary Creates a set of flags for a specific operation, including user information * @template M - Type extending Model * @param {OperationKeys} operation - The operation being performed (create, read, update, delete) * @param {Constructor<M>} model - The model constructor * @param {Partial<NanoFlags>} flags - Partial flags to be merged * @return {Promise<NanoFlags>} Complete flags for the operation */ protected flags<M extends Model>(operation: OperationKeys, model: Constructor<M>, flags: Partial<NanoFlags>): Promise<NanoFlags>; /** * @description Creates a new NanoDispatch instance * @summary Returns a dispatcher for handling Nano-specific operations * @return {NanoDispatch} A new NanoDispatch instance */ protected Dispatch(): NanoDispatch; /** * @description Creates database indexes for models * @summary Generates and creates indexes in the Nano database based on the provided models * @template M - Type extending Model * @param models - Model constructors to create indexes for * @return {Promise<void>} A promise that resolves when all indexes are created * @mermaid * sequenceDiagram * participant A as NanoAdapter * participant G as generateIndexes * participant DB as Nano Database * A->>G: generateIndexes(models) * G-->>A: indexes * loop For each index * A->>DB: createIndex(index) * DB-->>A: response * Note over A: Check if index already exists * alt Index exists * A-->>A: throw ConflictError * end * end */ protected index<M extends Model>(...models: Constructor<M>[]): Promise<void>; /** * @description Creates a new document in the database * @summary Inserts a new document into the Nano database with the provided data * @param {string} tableName - The name of the table/collection * @param {string | number} id - The document identifier * @param {Record<string, any>} model - The document data to insert * @return {Promise<Record<string, any>>} A promise that resolves to the created document with metadata * @mermaid * sequenceDiagram * participant A as NanoAdapter * participant DB as Nano Database * A->>DB: insert(model) * alt Success * DB-->>A: response with ok=true * A->>A: assignMetadata(model, response.rev) * A-->>A: return document with metadata * else Error * DB-->>A: error * A-->>A: throw parseError(e) * else Not OK * DB-->>A: response with ok=false * A-->>A: throw InternalError * end */ create(tableName: string, id: string | number, model: Record<string, any>): Promise<Record<string, any>>; /** * @description Creates multiple documents in the database * @summary Inserts multiple documents into the Nano database in a single bulk operation * @param {string} tableName - The name of the table/collection * @param {string[] | number[]} ids - Array of document identifiers * @param models - Array of document data to insert * @return A promise that resolves to an array of created documents with metadata * @mermaid * sequenceDiagram * participant A as NanoAdapter * participant DB as Nano Database * A->>DB: bulk({docs: models}) * alt Success * DB-->>A: response array * A->>A: Check if all responses have no errors * alt All OK * A->>A: assignMultipleMetadata(models, revs) * A-->>A: return documents with metadata * else Some errors * A->>A: Collect error messages * A-->>A: throw InternalError with collected messages * end * else Error * DB-->>A: error * A-->>A: throw parseError(e) * end */ createAll(tableName: string, ids: string[] | number[], models: Record<string, any>[]): Promise<Record<string, any>[]>; /** * @description Retrieves a document from the database * @summary Fetches a single document from the Nano database by its ID * @param {string} tableName - The name of the table/collection * @param {string | number} id - The document identifier * @return {Promise<Record<string, any>>} A promise that resolves to the retrieved document with metadata * @mermaid * sequenceDiagram * participant A as NanoAdapter * participant DB as Nano Database * A->>A: generateId(tableName, id) * A->>DB: get(_id) * alt Success * DB-->>A: record * A->>A: assignMetadata(record, record._rev) * A-->>A: return document with metadata * else Error * DB-->>A: error * A-->>A: throw parseError(e) * end */ read(tableName: string, id: string | number): Promise<Record<string, any>>; /** * @description Retrieves multiple documents from the database * @summary Fetches multiple documents from the Nano database by their IDs in a single operation * @param {string} tableName - The name of the table/collection * @param {Array<string | number | bigint>} ids - Array of document identifiers * @return A promise that resolves to an array of retrieved documents with metadata * @mermaid * sequenceDiagram * participant A as NanoAdapter * participant DB as Nano Database * A->>A: Map ids to generateId(tableName, id) * A->>DB: fetch({keys: mappedIds}, {}) * DB-->>A: results * A->>A: Process each result row * loop For each row * alt Row has error * A-->>A: throw InternalError * else Row has document * A->>A: assignMetadata(doc, doc._rev) * else No document * A-->>A: throw InternalError * end * end * A-->>A: return documents with metadata */ readAll(tableName: string, ids: (string | number | bigint)[]): Promise<Record<string, any>[]>; /** * @description Updates a document in the database * @summary Updates an existing document in the Nano database with the provided data * @param {string} tableName - The name of the table/collection * @param {string | number} id - The document identifier * @param {Record<string, any>} model - The updated document data * @return {Promise<Record<string, any>>} A promise that resolves to the updated document with metadata * @mermaid * sequenceDiagram * participant A as NanoAdapter * participant DB as Nano Database * A->>DB: insert(model) * alt Success * DB-->>A: response with ok=true * A->>A: assignMetadata(model, response.rev) * A-->>A: return document with metadata * else Error * DB-->>A: error * A-->>A: throw parseError(e) * else Not OK * DB-->>A: response with ok=false * A-->>A: throw InternalError * end */ update(tableName: string, id: string | number, model: Record<string, any>): Promise<Record<string, any>>; updateAll(tableName: string, ids: string[] | number[], models: Record<string, any>[]): Promise<Record<string, any>[]>; delete(tableName: string, id: string | number): Promise<Record<string, any>>; deleteAll(tableName: string, ids: (string | number | bigint)[]): Promise<Record<string, any>[]>; raw<R>(rawInput: MangoQuery, docsOnly?: boolean): Promise<R>; static connect(user: string, pass: string, host?: string, protocol?: "http" | "https"): ServerScope; /** * @description Creates a new database on the Nano server * @summary Creates a new database with the specified name on the connected Nano server * @param {ServerScope} con - The Nano server connection * @param {string} name - The name of the database to create * @return {Promise<void>} A promise that resolves when the database is created * @mermaid * sequenceDiagram * participant A as NanoAdapter * participant DB as Nano Server * A->>DB: db.create(name) * alt Success * DB-->>A: result with ok=true * else Error * DB-->>A: error * A-->>A: throw parseError(e) * else Not OK * DB-->>A: result with ok=false * A-->>A: throw parseError(error, reason) * end */ static createDatabase(con: ServerScope, name: string): Promise<void>; /** * @description Deletes a database from the Nano server * @summary Removes an existing database with the specified name from the connected Nano server * @param {ServerScope} con - The Nano server connection * @param {string} name - The name of the database to delete * @return {Promise<void>} A promise that resolves when the database is deleted * @mermaid * sequenceDiagram * participant A as NanoAdapter * participant DB as Nano Server * A->>DB: db.destroy(name) * alt Success * DB-->>A: result with ok=true * else Error * DB-->>A: error * A-->>A: throw parseError(e) * else Not OK * DB-->>A: result with ok=false * A-->>A: throw InternalError * end */ static deleteDatabase(con: ServerScope, name: string): Promise<void>; /** * @description Creates a new user and grants access to a database * @summary Creates a new user in the Nano server and configures security to grant the user access to a specific database * @param {ServerScope} con - The Nano server connection * @param {string} dbName - The name of the database to grant access to * @param {string} user - The username to create * @param {string} pass - The password for the new user * @param {string[]} [roles=["reader", "writer"]] - The roles to assign to the user * @return {Promise<void>} A promise that resolves when the user is created and granted access * @mermaid * sequenceDiagram * participant A as NanoAdapter * participant U as _users Database * participant S as Security API * A->>A: Create user object * A->>U: insert(user) * alt Success * U-->>A: response with ok=true * A->>S: PUT _security with user permissions * alt Security Success * S-->>A: security response with ok=true * else Security Failure * S-->>A: security response with ok=false * A-->>A: throw InternalError * end * else Error * U-->>A: error * A-->>A: throw parseError(e) * else Not OK * U-->>A: response with ok=false * A-->>A: throw InternalError * end */ static createUser(con: ServerScope, dbName: string, user: string, pass: string, roles?: string[]): Promise<void>; /** * @description Deletes a user from the Nano server * @summary Removes an existing user from the Nano server * @param {ServerScope} con - The Nano server connection * @param {string} dbName - The name of the database (used for logging purposes) * @param {string} user - The username to delete * @return {Promise<void>} A promise that resolves when the user is deleted * @mermaid * sequenceDiagram * participant A as NanoAdapter * participant U as _users Database * A->>A: Generate user ID * A->>U: get(id) * U-->>A: user document * A->>U: destroy(id, user._rev) * alt Success * U-->>A: success response * else Error * U-->>A: error * A-->>A: throw parseError(e) * end */ static deleteUser(con: ServerScope, dbName: string, user: string): Promise<void>; /** * @description Sets up decorations for Nano-specific model properties * @summary Configures decorators for created_by and updated_by fields in models to be automatically * populated with the user from the context when documents are created or updated * @return {void} * @mermaid * sequenceDiagram * participant A as NanoAdapter * participant D as Decoration * participant R as Repository * A->>R: key(PersistenceKeys.CREATED_BY) * R-->>A: createdByKey * A->>D: flavouredAs("nano") * A->>D: for(createdByKey) * A->>D: define(onCreate(createdByOnNanoCreateUpdate), propMetadata) * A->>D: apply() * A->>R: key(PersistenceKeys.UPDATED_BY) * R-->>A: updatedByKey * A->>D: flavouredAs("nano") * A->>D: for(updatedByKey) * A->>D: define(onCreate(createdByOnNanoCreateUpdate), propMetadata) * A->>D: apply() */ static decoration(): void; }