UNPKG

@decaf-ts/db-decorators

Version:

Agnostic database decorators and repository

270 lines 12.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.generated = generated; exports.hashOnCreateUpdate = hashOnCreateUpdate; exports.hash = hash; exports.composedFromCreateUpdate = composedFromCreateUpdate; exports.composedFromKeys = composedFromKeys; exports.composed = composed; exports.versionCreateUpdate = versionCreateUpdate; exports.version = version; exports.transient = transient; const constants_1 = require("./constants.cjs"); const decorator_validation_1 = require("@decaf-ts/decorator-validation"); const decorators_1 = require("./../operations/decorators.cjs"); const errors_1 = require("./../repository/errors.cjs"); const operations_1 = require("./../operations/index.cjs"); const decoration_1 = require("@decaf-ts/decoration"); function generated(type) { return function generated(target, prop) { return (0, decoration_1.propMetadata)(decoration_1.Metadata.key(constants_1.DBKeys.GENERATED, prop), type || true)(target, prop); }; } /** * @description Hashes a property value during create or update operations * @summary Callback function used by the hash decorator to apply hashing to a property value * @template M - Type extending Model * @template R - Type extending IRepository * @template V - Type for metadata * @template F - Type extending RepositoryFlags * @template C - Type extending Context * @param {C} context - The operation context * @param {V} data - Metadata for the operation * @param key - The property key to hash * @param {M} model - The model being processed * @param {M} [oldModel] - The previous model state (for updates) * @return {void} * @function hashOnCreateUpdate * @memberOf module:db-decorators */ function hashOnCreateUpdate(context, data, key, model, oldModel) { if (typeof model[key] === "undefined") return; const hash = decorator_validation_1.Hashing.hash(model[key]); if (oldModel && model[key] === hash) return; model[key] = hash; } /** * @description Creates a decorator that hashes a property value * @summary Decorator that automatically hashes a property value during create and update operations * @return {PropertyDecorator} A decorator that can be applied to class properties * @function hash * @category Property Decorators */ function hash() { return (0, decoration_1.apply)((0, decorators_1.onCreateUpdate)(hashOnCreateUpdate), (0, decoration_1.propMetadata)(constants_1.DBKeys.HASH, {})); } /** * @description Composes a property value from other properties during create or update operations * @summary Callback function used by composed decorators to generate a property value from other properties * @template M - Type extending Model * @template R - Type extending IRepository * @template V - Type extending ComposedFromMetadata * @template F - Type extending RepositoryFlags * @template C - Type extending Context * @param {C} context - The operation context * @param {V} data - Metadata for the composition * @param key - The property key to set the composed value on * @param {M} model - The model being processed * @return {void} * @function composedFromCreateUpdate * @memberOf module:db-decorators */ function composedFromCreateUpdate(context, data, key, model) { try { const { args, type, prefix, suffix, separator, filterEmpty, hashResult } = data; const composed = args .map((arg) => { if (!(arg in model)) throw new errors_1.InternalError(`Property ${arg} not found to compose from`); if (type === "keys") return arg; if (typeof model[arg] === "undefined") { if (filterEmpty) { if (!Array.isArray(filterEmpty)) return undefined; if (filterEmpty.includes(arg)) return undefined; } throw new errors_1.InternalError(`Property ${args} does not contain a value to compose from`); } return model[arg].toString(); }) .filter((a) => (filterEmpty ? !!a : true)); if (prefix) composed.unshift(prefix); if (suffix) composed.push(suffix); const str = composed .map((c) => { return typeof c === "object" && c.toString() === "[object Object]" ? JSON.stringify(c) : c; }) .join(separator); model[key] = hashResult ? decorator_validation_1.Hashing.hash(str) : str; } catch (e) { throw new errors_1.InternalError(`Failed to compose value: ${e}`); } } /** * @description Creates a decorator that composes a property value from other properties * @summary Base function for creating property composition decorators * @param {string[]} args - Property names to compose from * @param {boolean} [hashResult=false] - Whether to hash the composed result * @param {string} [separator=DefaultSeparator] - Character used to join the composed values * @param {"keys"|"values"} [type="values"] - Whether to use property keys or values * @param {string} [prefix=""] - Optional prefix to add to the composed value * @param {string} [suffix=""] - Optional suffix to add to the composed value * @param {GroupSort} groupsort - GroupSort configuration * @return {PropertyDecorator} A decorator that can be applied to class properties * @function composedFrom * @category PropertyDecorators */ function composedFrom(args, hashResult = false, separator = constants_1.DefaultSeparator, filterEmpty = false, type = "values", prefix = "", suffix = "", groupsort = { priority: 55 }) { function composedFrom(args, hashResult, separator, type, prefix, suffix, groupsort) { return function composeFrom(target, property) { const data = { args: args, hashResult: hashResult, separator: separator, type: type, prefix: prefix, suffix: suffix, filterEmpty: filterEmpty, }; const decorators = [ generated(constants_1.DBKeys.COMPOSED), (0, decorators_1.onCreateUpdate)(composedFromCreateUpdate, data, groupsort), (0, decoration_1.propMetadata)(decoration_1.Metadata.key(constants_1.DBKeys.COMPOSED, property), data), ]; if (hashResult) decorators.push(hash()); return (0, decoration_1.apply)(...decorators)(target, property); }; } return decoration_1.Decoration.for(constants_1.DBKeys.COMPOSED) .define({ decorator: composedFrom, args: [args, hashResult, separator, type, prefix, suffix, groupsort], }) .apply(); } /** * @description Creates a decorator that composes a property value from property keys * @summary Decorator that generates a property value by joining the names of other properties * @param {string[]} args - Property names to compose from * @param {string} [separator=DefaultSeparator] - Character used to join the property names * @param {boolean} [hash=false] - Whether to hash the composed result * @param {string} [prefix=""] - Optional prefix to add to the composed value * @param {string} [suffix=""] - Optional suffix to add to the composed value * @param {GroupSort} groupsort - GroupSort configuration * @return {PropertyDecorator} A decorator that can be applied to class properties * @function composedFromKeys * @category PropertyDecorators */ function composedFromKeys(args, separator = constants_1.DefaultSeparator, filterEmpty = false, hash = false, prefix = "", suffix = "", groupsort = { priority: 55 }) { return composedFrom(args, hash, separator, filterEmpty, "keys", prefix, suffix, groupsort); } /** * @description Creates a decorator that composes a property value from property values * @summary Decorator that generates a property value by joining the values of other properties * @param {string[]} args - Property names whose values will be composed * @param {string} [separator=DefaultSeparator] - Character used to join the property values * @param {boolean} [hash=false] - Whether to hash the composed result * @param {string} [prefix=""] - Optional prefix to add to the composed value * @param {string} [suffix=""] - Optional suffix to add to the composed value * @param {GroupSort} groupsort - GroupSort configuration * @return {PropertyDecorator} A decorator that can be applied to class properties * @function composed * @category PropertyDecorators */ function composed(args, separator = constants_1.DefaultSeparator, filterEmpty = false, hash = false, prefix = "", suffix = "", groupsort = { priority: 55 }) { return composedFrom(args, hash, separator, filterEmpty, "values", prefix, suffix, groupsort); } /** * @description Creates a function that updates a version property during operations * @summary Factory function that generates a callback for incrementing version numbers * @param {CrudOperations} operation - The type of operation (CREATE or UPDATE) * @return {Function} A callback function that updates the version property * @template M - Type extending Model * @template R - Type extending IRepository * @template V - Type for metadata * @template F - Type extending RepositoryFlags * @template C - Type extending Context * @function versionCreateUpdate * @memberOf module:db-decorators * @mermaid * sequenceDiagram * participant Caller * participant versionCreateUpdate * * Caller->>versionCreateUpdate: operation * versionCreateUpdate-->>Caller: callback function * Note over Caller,versionCreateUpdate: When callback is executed: * Caller->>versionCreateUpdate: context, data, key, model * alt operation is CREATE * versionCreateUpdate->>versionCreateUpdate: set version to 1 * else operation is UPDATE * versionCreateUpdate->>versionCreateUpdate: increment version * else invalid operation * versionCreateUpdate->>versionCreateUpdate: throw error * end * versionCreateUpdate-->>Caller: void */ function versionCreateUpdate(operation) { return function versionCreateUpdate(context, data, key, model, oldModel) { if (!decorator_validation_1.Model.shouldGenerate(model, key, context)) return; try { switch (operation) { case operations_1.OperationKeys.CREATE: model[key] = 1; break; case operations_1.OperationKeys.UPDATE: if (context.get("applyUpdateValidation") && oldModel && model[key] !== oldModel[key]) throw new errors_1.ValidationError(`Version mismatch: ${model[key]} !== ${oldModel[key]}`); model[key]++; break; default: throw new errors_1.InternalError(`Invalid operation: ${operation}`); } } catch (e) { throw new errors_1.InternalError(`Failed to update version: ${e}`); } }; } /** * @description Creates a decorator for versioning a property in a model * @summary This decorator applies multiple sub-decorators to handle version management during create and update operations * @return {PropertyDecorator} A composite decorator that sets the type to Number, manages version updates, and adds versioning metadata * @function version * @category PropertyDecorators */ function version() { const key = constants_1.DBKeys.VERSION; return decoration_1.Decoration.for(key) .define(generated(constants_1.DBKeys.VERSION), (0, decorator_validation_1.type)(Number), (0, decorators_1.onCreate)(versionCreateUpdate(operations_1.OperationKeys.CREATE)), (0, decorators_1.onUpdate)(versionCreateUpdate(operations_1.OperationKeys.UPDATE)), (0, decoration_1.propMetadata)(key, true)) .apply(); } /** * @description Creates a decorator that marks a property as transient * @summary Decorator that indicates a property should not be persisted to the database * @return {PropertyDecorator} A decorator that can be applied to class properties * @function transient * @category PropertyDecorators */ function transient() { return decoration_1.Decoration.for(constants_1.DBKeys.TRANSIENT) .define(function transient(model, attribute) { (0, decoration_1.propMetadata)(constants_1.DBKeys.TRANSIENT, true)(model.constructor); (0, decoration_1.propMetadata)(decoration_1.Metadata.key(constants_1.DBKeys.TRANSIENT, attribute), {})(model, attribute); }) .apply(); } //# sourceMappingURL=decorators.js.map