UNPKG

@edirect/template

Version:

`@edirect/template` is a flexible library for transforming payloads based on declarative templates. It supports mapping fields, applying custom transformers, setting default values, handling nested objects, and working with arrays.

293 lines (292 loc) 13.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TemplateModule = void 0; const lodash_1 = require("lodash"); class TemplateModule { context = {}; template = {}; transformers = {}; options = {}; setContext(context) { this.context = context; } setTemplate(template) { this.template = template; } setTransformers(transformers) { this.transformers = transformers; } setOptions(options) { this.options = options; } verifyTransformer(transformer, methodName) { return !!(transformer && transformer[methodName] && typeof transformer[methodName] === 'function'); } runTransformer(transformer, value) { if (this.verifyTransformer(this.transformers, transformer)) { return this.transformers[transformer]({ context: this.context, value }); } return undefined; } runTransformerWithParams(transformer, params, value) { if (this.verifyTransformer(this.transformers, transformer)) { return this.transformers[transformer]({ context: this.context, value }, ...params); } return null; } setValueByCondition(object, key, value, allowNull = false) { return this.checkValue(value, allowNull) ? { ...object, [key]: value, } : object; } isTransformer(object) { return !!((0, lodash_1.has)(object, 'transformer') || (0, lodash_1.has)(object, 'fields') || (0, lodash_1.has)(object, 'defaultValue') || (0, lodash_1.has)(object, 'transformers') || ((0, lodash_1.has)(object, 'transformerParams') && (0, lodash_1.has)(object, 'transformer'))); } isTransformers(object) { return !!((0, lodash_1.has)(object, 'transformers') && Array.isArray(object.transformers)); } isStaticArrayMapper(object) { return Array.isArray(object); } isDynamicArrayMapper(object) { return !!((0, lodash_1.has)(object, 'arraySource') && (0, lodash_1.has)(object, 'arrayTemplate')); } isValidObject(object) { return (!!object && typeof object === 'object' && Object.keys(object).length > 0 && Object.getPrototypeOf(object) !== null && Object.getPrototypeOf(object) === Object.prototype); } checkValue(value, allowNull = false) { const omitEmptyFields = this.options?.omitEmptyFields ?? false; const omitEmptyCollections = this.options?.omitEmptyCollections ?? false; const isEmptyArray = Array.isArray(value) && value.length === 0; const isEmptyObject = value !== null && typeof value === 'object' && !Array.isArray(value) && Object.keys(value).length === 0; if ((omitEmptyCollections || allowNull) && (isEmptyArray || isEmptyObject)) { return false; } return !!(value || allowNull || typeof value === 'boolean' || typeof value === 'number' || (omitEmptyFields ? typeof value === 'string' && value !== '' : typeof value === 'string')); } containsVar(value) { return !!value.match(/^\$\{[a-z_0-9\-.]+\}$/gi); } getVarOnConst(varName, context) { varName = varName.substring(2, varName.length - 1); return (0, lodash_1.get)(context, varName); } simpleValueToObject(value) { return { value }; } transformPayload(dataSource, template = this.template, rootData) { if (!template) return {}; if (rootData === undefined) rootData = dataSource; return Object.keys(template).reduce((acc, key) => { const target = template[key]; let value = (0, lodash_1.get)(dataSource, target); // handle static array mapper if (this.isStaticArrayMapper(target)) { const finalArray = []; const arrayTargets = target; for (const arrayTemplate of arrayTargets) { if (this.isValidObject(arrayTemplate)) { value = this.transformPayload(dataSource, arrayTemplate); if (this.checkValue(value) && typeof value === 'object' && value !== null && this.isValidObject(value)) finalArray.push(value); } } return this.setValueByCondition(acc, key, finalArray); } // handle dynamic array mapper if (this.isDynamicArrayMapper(target)) { const { arraySource, arrayTemplate, arrayOmitIfEmpty, ignoreIndexs } = target; value = (0, lodash_1.get)(dataSource, arraySource); if ((0, lodash_1.isArray)(value)) { const finalArray = []; for (const [index, arrayItem] of value.entries()) { if (ignoreIndexs && Array.isArray(ignoreIndexs) && ignoreIndexs.includes(index)) continue; if (target.simpleArray) { const source = this.simpleValueToObject(arrayItem); value = this.transformPayload(source, arrayTemplate); } else { value = this.transformPayload(arrayItem, arrayTemplate, rootData); } if (this.checkValue(value) && typeof value === 'object' && value !== null && this.isValidObject(value)) finalArray.push(value); } return this.setValueByCondition(acc, key, finalArray, arrayOmitIfEmpty); } return this.setValueByCondition(acc, key, [], arrayOmitIfEmpty); } // handle nested object + transformers if (this.isValidObject(target)) { // identify a nested object if (!this.isTransformer(target)) { return this.setValueByCondition(acc, key, this.transformPayload(dataSource, target)); } // Handle array of transformers if ((0, lodash_1.has)(target, 'transformers') && Array.isArray(target.transformers)) { value = this.applySequentialTransformers(target, dataSource, rootData); } else { // Handle single transformer (backward compatibility) const { fields, transformer, transformerParams, defaultValue, allowNull, } = target; const transformedTransformParams = []; if (transformerParams && Array.isArray(transformerParams)) { transformedTransformParams.push(...transformerParams.map(param => { if (!(0, lodash_1.isString)(param) || !this.containsVar(param)) return param; return (this.getVarOnConst(param, dataSource) || this.getVarOnConst(param, rootData)); })); } if (fields && Array.isArray(fields)) { for (const field of fields) { value = (0, lodash_1.get)(dataSource, field); if (transformer && transformedTransformParams.length) value = this.runTransformerWithParams(transformer, transformedTransformParams, value); else if (transformer) value = this.runTransformer(transformer, value); if (this.checkValue(value, !!allowNull)) break; } } else { if (transformer && transformedTransformParams.length) value = this.runTransformerWithParams(transformer, transformedTransformParams); else if (transformer) value = this.runTransformer(transformer); } value = this.checkValue(value, !!allowNull) ? value : defaultValue; } // Use the allowNull value from the last transformer or target object const useAllowNull = (0, lodash_1.has)(target, 'transformers') && Array.isArray(target.transformers) ? // Find the last transformer that's not a DynamicArrayMapper !!target.transformers .filter(t => !this.isDynamicArrayMapper(t)) .slice(-1)[0] && 'allowNull' in target.transformers .filter(t => !this.isDynamicArrayMapper(t)) .slice(-1)[0] ? target.transformers .filter(t => !this.isDynamicArrayMapper(t)) .slice(-1)[0].allowNull : false : !!target.allowNull; return this.setValueByCondition(acc, key, value, useAllowNull); } // handle simple mapper return this.setValueByCondition(acc, key, value); }, {}); } applySequentialTransformers(target, dataSource, rootData) { let transformedValue = null; const transformers = target.transformers; for (const transformer of transformers) { // Handle DynamicArrayMapper within the transformers array if (this.isDynamicArrayMapper(transformer)) { const dynamicArrayMapper = transformer; const { arraySource, arrayTemplate, ignoreIndexs, simpleArray } = dynamicArrayMapper; const arrayData = (0, lodash_1.get)(dataSource, arraySource); if ((0, lodash_1.isArray)(arrayData)) { const finalArray = []; for (const [index, arrayItem] of arrayData.entries()) { if (ignoreIndexs && ignoreIndexs.includes(index)) continue; let processedItem; if (simpleArray) { const source = this.simpleValueToObject(arrayItem); processedItem = this.transformPayload(source, arrayTemplate); } else { processedItem = this.transformPayload(arrayItem, arrayTemplate, rootData); } if (this.checkValue(processedItem) && typeof processedItem === 'object' && processedItem !== null && this.isValidObject(processedItem)) { finalArray.push(processedItem); } } transformedValue = finalArray; } else { transformedValue = []; } continue; } // Handle standard transformer const { fields, transformer: transformerName, transformerParams, defaultValue, allowNull, } = transformer; const transformedTransformParams = []; if (transformerParams && Array.isArray(transformerParams)) { transformedTransformParams.push(...transformerParams.map(param => { if (!(0, lodash_1.isString)(param) || !this.containsVar(param)) return param; return (this.getVarOnConst(param, dataSource) || this.getVarOnConst(param, rootData)); })); } if (fields && Array.isArray(fields)) { for (const field of fields) { transformedValue = transformedValue === null ? (0, lodash_1.get)(dataSource, field) : transformedValue; if (transformerName && transformedTransformParams.length) { transformedValue = this.runTransformerWithParams(transformerName, transformedTransformParams, transformedValue); } else if (transformerName) { transformedValue = this.runTransformer(transformerName, transformedValue); } if (this.checkValue(transformedValue, !!allowNull)) break; } } else { if (transformerName && transformedTransformParams.length) { transformedValue = this.runTransformerWithParams(transformerName, transformedTransformParams, transformedValue); } else if (transformerName) { transformedValue = this.runTransformer(transformerName, transformedValue); } } if (!this.checkValue(transformedValue, !!allowNull)) { transformedValue = defaultValue; } } return transformedValue; } } exports.TemplateModule = TemplateModule;