@decaf-ts/db-decorators
Version:
Agnostic database decorators and repository
270 lines • 12.8 kB
JavaScript
;
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