@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
JavaScript
"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;