UNPKG

@nestjs-mod/common

Version:

A collection of utilities for unifying NestJS applications and modules

184 lines 7.73 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.configTransform = configTransform; exports.getConfigModelMetadata = getConfigModelMetadata; const common_1 = require("@nestjs/common"); const load_package_1 = require("../utils/load-package"); const constants_1 = require("./constants"); const errors_1 = require("./errors"); // ============= Validation Helpers ============= /** * Loads the class-validator package, using provided package or loading it dynamically */ function loadValidator(validatorPackage) { return validatorPackage || (0, load_package_1.loadPackage)('class-validator', () => require('class-validator')); } /** * Transforms and validates configuration data based on model metadata */ async function configTransform({ model, data, rootOptions }) { // Initialize root options const options = { logger: new common_1.ConsoleLogger('configTransform'), ...rootOptions, }; const validator = loadValidator(options?.validatorPackage); const { modelPropertyOptions, modelOptions } = getConfigModelMetadata(model); // Build info object for documentation and debugging const info = { modelPropertyOptions, modelOptions: modelOptions || {}, validations: {}, }; // Transform data with allowed fields const transformedData = transformDataFields(data, modelPropertyOptions, options, modelOptions); // Build validation info buildValidationInfo(transformedData, modelPropertyOptions, info); // Create model instances for validation const { emptyInstance, dataInstance } = createModelInstances(model, transformedData); // Validate the configuration await validateConfiguration(emptyInstance, dataInstance, validator, options, modelOptions, info); // Log debug information if enabled logDebugInfo(options, modelOptions, info); // Copy validated data back to original data object copyValidatedData(data, dataInstance, modelPropertyOptions); return { data, info }; } // ============= Transformation Helpers ============= /** * Transforms data fields based on model property options */ function transformDataFields(data, modelPropertyOptions, rootOptions, modelOptions) { const transformedData = {}; for (const propertyOptions of modelPropertyOptions) { const hasValue = data[propertyOptions.originalName] !== undefined; if (propertyOptions.transform?.transform) { transformedData[propertyOptions.originalName] = hasValue ? propertyOptions.transform.transform({ modelRootOptions: rootOptions, modelOptions: modelOptions || {}, obj: data, options: propertyOptions, value: data[propertyOptions.originalName], }) : propertyOptions.default; } else { transformedData[propertyOptions.originalName] = hasValue ? data[propertyOptions.originalName] : propertyOptions.default; } } return transformedData; } /** * Builds validation info from model property options */ function buildValidationInfo(data, modelPropertyOptions, info) { for (const propertyOptions of modelPropertyOptions) { info.validations[propertyOptions.originalName] = { constraints: {}, value: data?.[propertyOptions.originalName], }; } } /** * Creates empty and data-filled model instances */ function createModelInstances(model, data) { try { const emptyInstance = new model(); const dataInstance = Object.assign(new model(), data); return { emptyInstance, dataInstance }; } catch { return { emptyInstance: {}, dataInstance: {} }; } } // ============= Validation Logic ============= /** * Validates the configuration and populates validation constraints */ async function validateConfiguration(emptyInstance, dataInstance, validator, rootOptions, modelOptions, info) { const shouldSkipValidation = rootOptions?.skipValidation || modelOptions?.skipValidation; if (shouldSkipValidation) { return; } const validatorOptions = rootOptions?.validatorOptions || modelOptions?.validatorOptions || constants_1.CONFIG_MODEL_CLASS_VALIDATOR_OPTIONS; // Validate data instance for errors const validationErrors = (await validator.validate(dataInstance, validatorOptions)).filter((error) => error.property); // Validate empty instance to collect constraints for documentation const emptyValidationErrors = (await validator.validate(emptyInstance, validatorOptions)).filter((error) => error.property); // Collect constraints from empty instance validation for (const error of emptyValidationErrors) { if (info.validations[error.property]) { info.validations[error.property].constraints = error?.constraints || {}; } } // Throw error if validation failed if (validationErrors.length > 0) { throwError(validationErrors, info, rootOptions, modelOptions); } } /** * Throws validation error with proper logging */ function throwError(errors, info, rootOptions, modelOptions) { const error = new errors_1.ConfigModelValidationErrors(errors, info); const logger = rootOptions?.logger || modelOptions?.logger; if (logger?.error) { const debug = rootOptions?.debug || modelOptions?.debug || process.env['DEBUG']; const visibleValidations = buildVisibleValidations(info); logger.error(!debug ? error.message : { ...info, validations: visibleValidations, modelPropertyOptions: info.modelPropertyOptions.filter((o) => !o.hideValueFromOutputs && visibleValidations[o.originalName]), }); } throw error; } /** * Builds visible validations (excluding hidden values) */ function buildVisibleValidations(info) { return Object.fromEntries(Object.entries(info.validations).filter(([key, value]) => { return (Object.keys(value.constraints || {}).length > 0 && !info.modelPropertyOptions.some((o) => o.hideValueFromOutputs && o.originalName === key)); })); } /** * Logs debug information if debug mode is enabled */ function logDebugInfo(rootOptions, modelOptions, info) { const debug = rootOptions?.debug || modelOptions?.debug || process.env['DEBUG']; const logger = rootOptions?.logger || modelOptions?.logger; if (debug && logger?.debug) { logger.debug(JSON.stringify({ ...info, validations: Object.fromEntries(Object.entries(info.validations).filter(([key]) => { return !info.modelPropertyOptions.some((o) => o.hideValueFromOutputs && o.originalName === key); })), modelPropertyOptions: info.modelPropertyOptions.filter((o) => !o.hideValueFromOutputs), })); } } /** * Copies validated data from instance back to original data object */ function copyValidatedData(data, dataInstance, modelPropertyOptions) { for (const propertyMetadata of modelPropertyOptions) { data[propertyMetadata.originalName] = dataInstance[propertyMetadata.originalName]; } } // ============= Metadata Extraction ============= /** * Extracts configuration model metadata from the model class */ function getConfigModelMetadata(model) { const modelPropertyOptions = Reflect.getMetadata(constants_1.CONFIG_MODEL_PROPERTIES_METADATA, model) || []; const modelOptions = Reflect.getMetadata(constants_1.CONFIG_MODEL_METADATA, model); return { modelPropertyOptions, modelOptions }; } //# sourceMappingURL=utils.js.map