UNPKG

@decaf-ts/db-decorators

Version:

Agnostic database decorators and repository

137 lines 6.02 kB
import { Model, validate, } from "@decaf-ts/decorator-validation"; import { validateCompare } from "./../model/validation.js"; import { Metadata } from "@decaf-ts/decoration"; import { DBKeys } from "./../model/constants.js"; import { ModelOperations } from "./../operations/constants.js"; import { SerializationError } from "./../repository/errors.js"; Model.prototype.isTransient = function () { return Metadata.isTransient(this); }; /** * @description Validates the model and checks for errors * @summary Validates the current model state and optionally compares with a previous version * @template M - Type extending Model * @param {M|any} [previousVersion] - Optional previous version of the model for comparison * @param {...any[]} exclusions - Properties to exclude from validation * @return {ModelErrorDefinition|undefined} Error definition if validation fails, undefined otherwise * @function hasErrors * @memberOf module:db-decorators */ Model.prototype.hasErrors = function (previousVersion, ...exclusions) { if (previousVersion && !(previousVersion instanceof Model)) { exclusions.unshift(previousVersion); previousVersion = undefined; } const async = this.isAsync(); const errs = validate(this, async, ...exclusions); if (async) { return Promise.resolve(errs).then((resolvedErrs) => { if (resolvedErrs || !previousVersion) { return resolvedErrs; } return validateCompare(previousVersion, this, async, ...exclusions); }); } if (errs || !previousVersion) return errs; // @ts-expect-error Overriding Model prototype method with dynamic conditional return type. return validateCompare(previousVersion, this, async, ...exclusions); }; Model.prototype.segregate = function segregate() { return Model.segregate(this); }; Model.segregate = function segregate(model) { if (!Metadata.isTransient(model)) return { model: model }; const decoratedProperties = Metadata.validatableProperties(model.constructor); const transientProps = Metadata.get(model.constructor, DBKeys.TRANSIENT); const result = { model: {}, transient: {}, }; for (const key of decoratedProperties) { const isTransient = Object.keys(transientProps).includes(key); if (isTransient) { result.transient = result.transient || {}; try { result.transient[key] = model[key]; } catch (e) { throw new SerializationError(`Failed to serialize transient property ${key}: ${e}`); } } else { result.model = result.model || {}; result.model[key] = model[key]; } } result.model = Model.build(result.model, model.constructor.name); return result; }; Model.pk = function pk(model, keyValue = false) { if (!model) throw new Error("No model was provided"); const constr = model instanceof Model ? model.constructor : model; const idProp = Metadata.get(constr, DBKeys.ID); if (!idProp) { throw new Error(`No Id property defined for model ${constr?.name || "Unknown Model"}`); } const key = Object.keys(idProp)[0]; if (!keyValue) return key; if (model instanceof Model) return model[key]; throw new Error("Cannot get the value of the pk from the constructor"); }.bind(Model); Model.pkProps = function pkProps(model) { return Metadata.get(model, Metadata.key(DBKeys.ID, Model.pk(model))); }.bind(Model); Model.isTransient = function isTransient(model) { return !!Metadata.get(typeof model !== "function" ? model.constructor : model, DBKeys.TRANSIENT); }.bind(Model); Model.composed = function composed(model, prop) { const constr = model instanceof Model ? model.constructor : model; if (prop) return Metadata.get(constr, Metadata.key(DBKeys.COMPOSED, prop)); return !!Metadata.get(constr, DBKeys.COMPOSED); }.bind(Model); /** * @description Merges two model instances into a new instance. * @summary Creates a new model instance by combining properties from an old model and a new model. * Properties from the new model override properties from the old model if they are defined. * @template {M} - Type extending Model * @param {M} oldModel - The original model instance * @param {M} model - The new model instance with updated properties * @return {M} A new model instance with merged properties */ Model.merge = function merge(oldModel, newModel, constructor) { constructor = constructor || oldModel.constructor; const extract = (model) => Object.entries(model).reduce((accum, [key, val]) => { if (typeof val !== "undefined") accum[key] = val; return accum; }, {}); return new constructor(Object.assign({}, extract(oldModel), extract(newModel))); }.bind(Model); Metadata.saveOperation = function saveOperation(model, propertyKey, operation, metadata) { if (!propertyKey) return; Metadata.set(model, Metadata.key(ModelOperations.OPERATIONS, propertyKey, operation), metadata); }.bind(Metadata); Metadata.readOperation = function readOperation(model, propertyKey, operation) { if (!propertyKey || !operation) return; return Metadata.get(model, Metadata.key(ModelOperations.OPERATIONS, propertyKey, operation)); }.bind(Metadata); Metadata.isTransient = function isTransient(model) { return !!Metadata.get(typeof model !== "function" ? model.constructor : model, DBKeys.TRANSIENT); }.bind(Metadata); Model.generated = function generated(model, prop) { return !!Metadata.get(typeof model !== "function" ? model.constructor : model, Metadata.key(DBKeys.GENERATED, prop)); }.bind(Model); Model.shouldGenerate = function shouldGenerate(model, prop, ctx) { if (ctx.get("allowGenerationOverride") && typeof model[prop] !== "undefined") return false; return true; }.bind(Model); //# sourceMappingURL=overrides.js.map