@decaf-ts/db-decorators
Version:
Agnostic database decorators and repository
174 lines (173 loc) • 7.42 kB
JavaScript
;
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;