UNPKG

@decaf-ts/db-decorators

Version:

Agnostic database decorators and repository

221 lines 10 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.readonly = readonly; exports.timestampHandler = timestampHandler; exports.timestamp = timestamp; exports.serializeOnCreateUpdate = serializeOnCreateUpdate; exports.serializeAfterAll = serializeAfterAll; exports.serialize = serialize; require("./validation.cjs"); const decorator_validation_1 = require("@decaf-ts/decorator-validation"); const constants_1 = require("./../model/constants.cjs"); const constants_2 = require("./constants.cjs"); const constants_3 = require("./../operations/constants.cjs"); const decorators_1 = require("./../operations/decorators.cjs"); const errors_1 = require("./../repository/errors.cjs"); const decoration_1 = require("@decaf-ts/decoration"); const decorators_2 = require("./../model/decorators.cjs"); /** * @description Prevents a property from being modified after initial creation. * @summary Marks the property as readonly, causing validation errors if attempts are made to modify it during updates. * @param {string} [message] - The error message to display when validation fails. Defaults to {@link DEFAULT_ERROR_MESSAGES.READONLY.INVALID} * @return {PropertyDecorator} A decorator function that can be applied to class properties * @function readonly * @category Property Decorators */ function readonly(message = constants_2.DEFAULT_ERROR_MESSAGES.READONLY.INVALID) { const key = constants_1.DBKeys.READONLY; const meta = { message: message, description: `defines the attribute as readOnly`, async: false, }; return decoration_1.Decoration.for(key) .define({ decorator: decorator_validation_1.innerValidationDecorator, args: [readonly, key, meta], }) .apply(); } /** * @description Handler function that sets a timestamp property to the current timestamp. * @summary Updates a model property with the current timestamp from the repository context. * @template M - The model type extending Model * @template R - The repository type extending IRepository * @template V - The data type for the operation * @template F - The repository flags type * @template C - The context type * @param {C} context - The repository context containing the current timestamp * @param {V} data - The data being processed * @param key - The property key to update * @param {M} model - The model instance being updated * @return {Promise<void>} A promise that resolves when the timestamp has been set * @function timestampHandler */ async function timestampHandler(context, data, key, model) { if (decorator_validation_1.Model.shouldGenerate(model, key, context)) model[key] = context.timestamp; } /** * @description Automatically manages timestamp properties for tracking creation and update times. * @summary Marks the property as a timestamp, making it required and ensuring it's a valid date. The property will be automatically updated with the current timestamp during specified operations. * * Date Format: * * <pre> * Using similar formatting as Moment.js, Class DateTimeFormatter (Java), and Class SimpleDateFormat (Java), * I implemented a comprehensive solution formatDate(date, patternStr) where the code is easy to read and modify. * You can display date, time, AM/PM, etc. * * Date and Time Patterns * yy = 2-digit year; yyyy = full year * M = digit month; MM = 2-digit month; MMM = short month name; MMMM = full month name * EEEE = full weekday name; EEE = short weekday name * d = digit day; dd = 2-digit day * h = hours am/pm; hh = 2-digit hours am/pm; H = hours; HH = 2-digit hours * m = minutes; mm = 2-digit minutes; aaa = AM/PM * s = seconds; ss = 2-digit seconds * S = miliseconds * </pre> * * @param {OperationKeys[]} operation - The operations to act on. Defaults to {@link DBOperations.CREATE_UPDATE} * @param {string} [format] - The timestamp format. Defaults to {@link DEFAULT_TIMESTAMP_FORMAT} * @return {PropertyDecorator} A decorator function that can be applied to class properties * @function timestamp * @category Property Decorators * @mermaid * sequenceDiagram * participant C as Client * participant M as Model * participant T as TimestampDecorator * participant V as Validator * * C->>M: Create/Update model * M->>T: Process timestamp property * T->>M: Apply required validation * T->>M: Apply date format validation * * alt Update operation * T->>V: Register timestamp validator * V->>M: Validate timestamp is newer * end * * T->>M: Set current timestamp * M->>C: Return updated model */ function timestamp(operation = constants_3.DBOperations.CREATE_UPDATE, format = constants_1.DEFAULT_TIMESTAMP_FORMAT) { const decorationKey = constants_1.DBKeys.TIMESTAMP; const updateValidationKey = decorator_validation_1.Validation.updateKey(constants_1.DBKeys.TIMESTAMP); function ts(operation, format) { const decorators = [ (0, decorators_2.generated)(constants_1.DBKeys.TIMESTAMP), (0, decorator_validation_1.date)(format, constants_2.DEFAULT_ERROR_MESSAGES.TIMESTAMP.DATE), (0, decorator_validation_1.required)(constants_2.DEFAULT_ERROR_MESSAGES.TIMESTAMP.REQUIRED), (0, decoration_1.propMetadata)(decorator_validation_1.Validation.key(constants_1.DBKeys.TIMESTAMP), { operation: operation, format: format, }), (0, decorators_1.on)(operation, timestampHandler), ]; if (operation.indexOf(constants_3.OperationKeys.UPDATE) !== -1) decorators.push((0, decoration_1.propMetadata)(updateValidationKey, { message: constants_2.DEFAULT_ERROR_MESSAGES.TIMESTAMP.INVALID, })); else decorators.push(readonly()); return (0, decoration_1.apply)(...decorators); } return decoration_1.Decoration.for(decorationKey) .define({ decorator: ts, args: [operation, format], }) .apply(); } /** * @description Handler function that serializes a property to JSON string during create and update operations. * @summary Converts a complex object property to a JSON string before storing it in the database. * @template M - The model type extending Model * @template R - The repository type extending IRepository * @template V - The data type for the operation * @template F - The repository flags type * @template C - The context type * @param {C} context - The repository context * @param {V} data - The data being processed * @param key - The property key to serialize * @param {M} model - The model instance being processed * @return {Promise<void>} A promise that resolves when the property has been serialized * @function serializeOnCreateUpdate */ async function serializeOnCreateUpdate(context, data, key, model) { if (!model[key]) return; try { model[key] = data.serializer ? new data.serializer().serialize(model[key]) : JSON.stringify(model[key]); } catch (e) { throw new errors_1.SerializationError(`Failed to serialize ${key.toString()} property of model ${model.constructor.name}: ${e}`); } } /** * @description Handler function that deserializes a property from JSON string after database operations. * @summary Converts a JSON string property back to its original complex object form after retrieving it from the database. * @template M - The model type extending Model * @template R - The repository type extending IRepository * @template V - The data type for the operation * @template F - The repository flags type * @template C - The context type * @param {C} context - The repository context * @param {V} data - The data being processed * @param key - The property key to deserialize * @param {M} model - The model instance being processed * @return {Promise<void>} A promise that resolves when the property has been deserialized * @function serializeAfterAll */ async function serializeAfterAll(context, data, key, model) { if (!model[key]) return; if (typeof model[key] !== "string") return; try { model[key] = data.serializer ? new data.serializer().deserialize(model[key]) : JSON.parse(model[key]); } catch (e) { throw new errors_1.SerializationError(`Failed to deserialize ${key.toString()} property of model ${model.constructor.name}: ${e}`); } } /** * @description Enables automatic JSON serialization and deserialization for complex object properties. * @summary Decorator that automatically converts complex objects to JSON strings before storing in the database and back to objects when retrieving them. * @return {PropertyDecorator} A decorator function that can be applied to class properties * @function serialize * @category Property Decorators * @mermaid * sequenceDiagram * participant C as Client * participant M as Model * participant S as SerializeDecorator * participant DB as Database * * Note over C,DB: Create/Update Flow * C->>M: Set complex object property * M->>S: Process property (create/update) * S->>M: Convert to JSON string * M->>DB: Store serialized data * * Note over C,DB: Retrieval Flow * C->>M: Request model * M->>DB: Fetch data * DB->>M: Return with serialized property * M->>S: Process property (after all ops) * S->>M: Parse JSON back to object * M->>C: Return model with deserialized property */ function serialize(serializer) { return (0, decoration_1.apply)((0, decorators_1.onCreateUpdate)(serializeOnCreateUpdate, { serializer: serializer }), (0, decorators_1.after)(constants_3.DBOperations.ALL, serializeAfterAll, { serializer: serializer }), (0, decorator_validation_1.type)([String, Object]), (0, decoration_1.metadata)(constants_1.DBKeys.SERIALIZE, { serializer: serializer })); } //# sourceMappingURL=decorators.js.map