@decaf-ts/db-decorators
Version:
Agnostic database decorators and repository
209 lines • 9.55 kB
JavaScript
;
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 reflection_1 = require("@decaf-ts/reflection");
const repository_1 = require("./../repository/index.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 = decorator_validation_1.Validation.updateKey(constants_1.DBKeys.READONLY);
return decorator_validation_1.Decoration.for(key)
.define((0, decorator_validation_1.propMetadata)(key, {
message: message,
}))
.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) {
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 key = decorator_validation_1.Validation.updateKey(constants_1.DBKeys.TIMESTAMP);
function ts(operation, format) {
const decorators = [
(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, decorator_validation_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, decorator_validation_1.propMetadata)(key, {
message: constants_2.DEFAULT_ERROR_MESSAGES.TIMESTAMP.INVALID,
}));
else
decorators.push(readonly());
return (0, reflection_1.apply)(...decorators);
}
return decorator_validation_1.Decoration.for(key)
.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] = JSON.stringify(model[key]);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
}
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] = 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() {
return (0, reflection_1.apply)((0, decorators_1.onCreateUpdate)(serializeOnCreateUpdate), (0, decorators_1.after)(constants_3.DBOperations.ALL, serializeAfterAll), (0, decorator_validation_1.type)([String.name, Object.name]), (0, reflection_1.metadata)(repository_1.Repository.key(constants_1.DBKeys.SERIALIZE), {}));
}
//# sourceMappingURL=decorators.js.map