UNPKG

@decaf-ts/db-decorators

Version:

Agnostic database decorators and repository

174 lines (173 loc) 7.42 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getAllPropertyDecoratorsRecursive = exports.getHandlerArgs = void 0; exports.enforceDBDecorators = enforceDBDecorators; exports.getDbDecorators = getDbDecorators; const Operations_1 = require("../operations/Operations.cjs"); const constants_1 = require("../operations/constants.cjs"); const reflection_1 = require("@decaf-ts/reflection"); const errors_1 = require("./errors.cjs"); const decorator_validation_1 = require("@decaf-ts/decorator-validation"); /** * @summary retrieves the arguments for the handler * @param {any} dec the decorator * @param {string} prop the property name * @param {{}} m the model * @param {{}} [accum] accumulator used for internal recursiveness * * @function getHandlerArgs * @memberOf module:db-decorators.Repository */ const getHandlerArgs = function (dec, prop, m, accum) { const name = m.constructor.name; if (!name) throw new errors_1.InternalError("Could not determine model class"); accum = accum || {}; if (dec.props.handlers[name] && dec.props.handlers[name][prop]) accum = { ...dec.props.handlers[name][prop], ...accum }; let proto = Object.getPrototypeOf(m); if (proto === Object.prototype) return accum; if (proto.constructor.name === name) proto = Object.getPrototypeOf(proto); return (0, exports.getHandlerArgs)(dec, prop, proto, accum); }; exports.getHandlerArgs = getHandlerArgs; /** * * @param {IRepository<T>} repo * @param context * @param {T} model * @param operation * @param prefix * * @param oldModel * @function enforceDBPropertyDecoratorsAsync * * @memberOf db-decorators.utils */ async function enforceDBDecorators(repo, context, model, operation, prefix, oldModel) { const decorators = getDbDecorators(model, operation, prefix); if (!decorators) return; for (const prop in decorators) { const decs = decorators[prop]; for (const dec of decs) { const { key } = dec; const handlers = Operations_1.Operations.get(model, prop, prefix + key); if (!handlers || !handlers.length) throw new errors_1.InternalError(`Could not find registered handler for the operation ${prefix + key} under property ${prop}`); const handlerArgs = (0, exports.getHandlerArgs)(dec, prop, model); if (!handlerArgs || Object.values(handlerArgs).length !== handlers.length) throw new errors_1.InternalError((0, decorator_validation_1.sf)("Args and handlers length do not match")); let handler; let data; for (let i = 0; i < handlers.length; i++) { handler = handlers[i]; data = Object.values(handlerArgs)[i]; const args = [context, data.data, prop, model]; if (operation === constants_1.OperationKeys.UPDATE && prefix === constants_1.OperationKeys.ON) { if (!oldModel) throw new errors_1.InternalError("Missing old model for update operation"); args.push(oldModel); } await handler.apply(repo, args); } } } } /** * Specific for DB Decorators * @param {T} model * @param {string} operation CRUD {@link OperationKeys} * @param {string} [extraPrefix] * * @function getDbPropertyDecorators * * @memberOf db-decorators.utils */ function getDbDecorators(model, operation, extraPrefix) { const decorators = reflection_1.Reflection.getAllPropertyDecorators(model, // undefined, constants_1.OperationKeys.REFLECT + (extraPrefix ? extraPrefix : "")); if (!decorators) return; return Object.keys(decorators).reduce((accum, decorator) => { const dec = decorators[decorator].filter((d) => d.key === operation); if (dec && dec.length) { if (!accum) accum = {}; accum[decorator] = dec; } return accum; }, undefined); } /** * @summary Retrieves the decorators for an object's properties prefixed by {@param prefixes} recursively * @param model * @param accum * @param prefixes * * @function getAllPropertyDecoratorsRecursive * @memberOf module:db-decorators.Repository */ const getAllPropertyDecoratorsRecursive = function (model, accum, ...prefixes) { const accumulator = accum || {}; const mergeDecorators = function (decs) { const pushOrSquash = (key, ...values) => { values.forEach((val) => { let match; if (!(match = accumulator[key].find((e) => e.key === val.key)) || match.props.operation !== val.props.operation) { accumulator[key].push(val); return; } if (val.key === decorator_validation_1.ModelKeys.TYPE) return; const { handlers, operation } = val.props; if (!operation || !operation.match(new RegExp(`^(:?${constants_1.OperationKeys.ON}|${constants_1.OperationKeys.AFTER})(:?${constants_1.OperationKeys.CREATE}|${constants_1.OperationKeys.READ}|${constants_1.OperationKeys.UPDATE}|${constants_1.OperationKeys.DELETE})$`))) { accumulator[key].push(val); return; } const accumHandlers = match.props.handlers; Object.entries(handlers).forEach(([clazz, handlerDef]) => { if (!(clazz in accumHandlers)) { accumHandlers[clazz] = handlerDef; return; } Object.entries(handlerDef).forEach(([handlerProp, handler]) => { if (!(handlerProp in accumHandlers[clazz])) { accumHandlers[clazz][handlerProp] = handler; return; } Object.entries(handler).forEach(([handlerKey, argsObj]) => { if (!(handlerKey in accumHandlers[clazz][handlerProp])) { accumHandlers[clazz][handlerProp][handlerKey] = argsObj; return; } console.warn((0, decorator_validation_1.sf)("Skipping handler registration for {0} under prop {0} because handler is the same", clazz, handlerProp)); }); }); }); }); }; Object.entries(decs).forEach(([key, value]) => { accumulator[key] = accumulator[key] || []; pushOrSquash(key, ...value); }); }; const decs = reflection_1.Reflection.getAllPropertyDecorators(model, ...prefixes); if (decs) mergeDecorators(decs); if (Object.getPrototypeOf(model) === Object.prototype) return accumulator; // const name = model.constructor.name; const proto = Object.getPrototypeOf(model); if (!proto) return accumulator; // if (proto.constructor && proto.constructor.name === name) // proto = Object.getPrototypeOf(proto) return (0, exports.getAllPropertyDecoratorsRecursive)(proto, accumulator, ...prefixes); }; exports.getAllPropertyDecoratorsRecursive = getAllPropertyDecoratorsRecursive;