diff --git a/src/engine.ts b/src/engine.ts index f8a3483..3a620a8 100644 --- a/src/engine.ts +++ b/src/engine.ts @@ -8,6 +8,7 @@ import { JsonTemplateTranslator } from './translator'; import { EngineOptions, Expression, + FlatMappingAST, FlatMappingPaths, PathType, SyntaxType, @@ -66,7 +67,7 @@ export class JsonTemplateEngine { })); } - static validateMappings(mappings: FlatMappingPaths[]) { + static validateMappings(mappings: FlatMappingPaths[], options?: EngineOptions) { JsonTemplateEngine.prepareMappings(mappings).forEach((mapping) => { if ( !JsonTemplateEngine.isValidJSONPath(mapping.input) || @@ -79,18 +80,24 @@ export class JsonTemplateEngine { ); } }); - JsonTemplateEngine.parseMappingPaths(mappings); + JsonTemplateEngine.parseMappingPaths(mappings, options); } - static parseMappingPaths(mappings: FlatMappingPaths[], options?: EngineOptions): Expression { - const flatMappingAST = JsonTemplateEngine.prepareMappings(mappings) + private static createFlatMappingsAST( + mappings: FlatMappingPaths[], + options?: EngineOptions, + ): FlatMappingAST[] { + return JsonTemplateEngine.prepareMappings(mappings) .filter((mapping) => mapping.input && mapping.output) .map((mapping) => ({ ...mapping, inputExpr: JsonTemplateEngine.parse(mapping.input, options).statements[0], outputExpr: JsonTemplateEngine.parse(mapping.output, options).statements[0], })); - return convertToObjectMapping(flatMappingAST); + } + + static parseMappingPaths(mappings: FlatMappingPaths[], options?: EngineOptions): Expression { + return convertToObjectMapping(JsonTemplateEngine.createFlatMappingsAST(mappings, options)); } static create(templateOrExpr: TemplateInput, options?: EngineOptions): JsonTemplateEngine { @@ -120,9 +127,13 @@ export class JsonTemplateEngine { return JsonTemplateEngine.translateExpression(JsonTemplateEngine.parse(template, options)); } - static reverseTranslate(expr: Expression, options?: EngineOptions): string { + static reverseTranslate(expr: Expression | FlatMappingPaths[], options?: EngineOptions): string { const translator = new JsonTemplateReverseTranslator(options); - return translator.translate(expr); + let newExpr = expr; + if (Array.isArray(expr)) { + newExpr = JsonTemplateEngine.parseMappingPaths(expr as FlatMappingPaths[], options); + } + return translator.translate(newExpr as Expression); } static convertMappingsToTemplate(mappings: FlatMappingPaths[], options?: EngineOptions): string { diff --git a/src/parser.ts b/src/parser.ts index 54e1ed2..e6deb02 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -1443,9 +1443,9 @@ export class JsonTemplateParser { return this.lexer.throwUnexpectedToken(); } - private static pathContainsVariables(parts: Expression[]): boolean { + private static shouldPathBeConvertedAsBlock(parts: Expression[]): boolean { return parts - .filter((part) => part.type === SyntaxType.PATH_OPTIONS) + .filter((part, index) => part.type === SyntaxType.PATH_OPTIONS && index < parts.length - 1) .some((part) => part.options?.index ?? part.options?.item); } @@ -1550,7 +1550,7 @@ export class JsonTemplateParser { newPathExpr.parts.shift(); } - const shouldConvertAsBlock = JsonTemplateParser.pathContainsVariables(newPathExpr.parts); + const shouldConvertAsBlock = JsonTemplateParser.shouldPathBeConvertedAsBlock(newPathExpr.parts); let lastPart = getLastElement(newPathExpr.parts); let fnExpr: FunctionCallExpression | undefined; if (lastPart?.type === SyntaxType.FUNCTION_CALL_EXPR) { @@ -1558,9 +1558,13 @@ export class JsonTemplateParser { } lastPart = getLastElement(newPathExpr.parts); - if (lastPart?.type === SyntaxType.PATH_OPTIONS) { - newPathExpr.parts.pop(); + if (lastPart?.type === SyntaxType.PATH_OPTIONS && lastPart.options?.toArray) { newPathExpr.returnAsArray = lastPart.options?.toArray; + if (!lastPart.options.item && !lastPart.options.index) { + newPathExpr.parts.pop(); + } else { + lastPart.options.toArray = false; + } } newPathExpr.parts = JsonTemplateParser.combinePathOptionParts(newPathExpr.parts); diff --git a/src/reverse_translator.test.ts b/src/reverse_translator.test.ts index 1ae0867..8fd70a7 100644 --- a/src/reverse_translator.test.ts +++ b/src/reverse_translator.test.ts @@ -1,4 +1,5 @@ import { JsonTemplateEngine } from './engine'; +import { PathType } from './types'; describe('reverse_translator', () => { it('should reverse translate with indentation', () => { @@ -12,7 +13,7 @@ describe('reverse_translator', () => { it('should reverse translate json mappings', () => { const template = JsonTemplateEngine.reverseTranslate( - JsonTemplateEngine.parse([ + [ { input: '$.userId', output: '$.user.id', @@ -41,10 +42,11 @@ describe('reverse_translator', () => { input: '$.products[?(@.category)].(@.price * @.quantity * (1 - $.discount / 100)).sum()', output: '$.events[0].revenue', }, - ]), + ], + { defaultPathType: PathType.JSON }, ); expect(template).toEqual( - '{\n "user": {\n "id": $.userId\n },\n "events": [{\n "items": $.products{.category}.({\n "discount": $.discount,\n "product_id": .id,\n "options": .variations[*].({\n "s": .size\n })[],\n "value": .price * .quantity * (1 - $.discount / 100)\n })[],\n "name": $.events[0],\n "revenue": $.products{.category}.(.price * .quantity * (1 - $.discount / 100)).sum()\n }]\n}', + '{\n "user": {\n "id": $.userId\n },\n "events": [{\n "items": $.products[?(@.category)].({\n "discount": $.discount,\n "product_id": @.id,\n "options": @.variations[*].({\n "s": @.size\n })[],\n "value": @.price * @.quantity * (1 - $.discount / 100)\n })[],\n "name": $.events[0],\n "revenue": $.products[?(@.category)].(@.price * @.quantity * (1 - $.discount / 100)).sum()\n }]\n}', ); }); }); diff --git a/src/translator.ts b/src/translator.ts index 1e2b99c..bd4ce61 100644 --- a/src/translator.ts +++ b/src/translator.ts @@ -310,6 +310,7 @@ export class JsonTemplateTranslator { code.push(JsonTemplateTranslator.covertToArrayValue(data)); if ( JsonTemplateTranslator.isArrayFilterExpr(expr.parts[partNum]) || + JsonTemplateTranslator.isAllFilterExpr(expr.parts[partNum]) || JsonTemplateTranslator.isToArray(expr, partNum) ) { code.push(`${data} = [${data}];`); @@ -317,10 +318,13 @@ export class JsonTemplateTranslator { return code.join(''); } + static isAllFilterExpr(expr: Expression): boolean { + return ( + expr.type === SyntaxType.OBJECT_FILTER_EXPR && expr.filter.type === SyntaxType.ALL_FILTER_EXPR + ); + } + private translatePathParts(expr: PathExpression, dest: string): string { - if (!expr.parts.length) { - return ''; - } const { parts } = expr; const code: string[] = []; const numParts = parts.length; @@ -384,8 +388,9 @@ export class JsonTemplateTranslator { } const code: string[] = []; code.push(this.translatePathRoot(expr, dest, ctx)); - code.push(this.translatePathParts(expr, dest)); - if (expr.returnAsArray && expr.parts.length === 0) { + if (expr.parts.length > 0) { + code.push(this.translatePathParts(expr, dest)); + } else if (expr.returnAsArray) { code.push(JsonTemplateTranslator.covertToArrayValue(dest)); } return code.join(''); @@ -786,11 +791,13 @@ export class JsonTemplateTranslator { ctx: string, ): string { const code: string[] = []; - const condition = this.acquireVar(); - code.push(JsonTemplateTranslator.generateAssignmentCode(condition, 'true')); - code.push(this.translateExpr(expr.filter, condition, ctx)); - code.push(`if(!${condition}) {${dest} = undefined;}`); - this.releaseVars(condition); + if (expr.filter.type !== SyntaxType.ALL_FILTER_EXPR) { + const condition = this.acquireVar(); + code.push(JsonTemplateTranslator.generateAssignmentCode(condition, 'true')); + code.push(this.translateExpr(expr.filter, condition, ctx)); + code.push(`if(!${condition}) {${dest} = undefined;}`); + this.releaseVars(condition); + } return code.join(''); } diff --git a/src/types.ts b/src/types.ts index 1badb02..45f7bf6 100644 --- a/src/types.ts +++ b/src/types.ts @@ -284,7 +284,7 @@ export type FlatMappingPaths = { }; export type FlatMappingAST = FlatMappingPaths & { - inputExpr: PathExpression; + inputExpr: Expression; outputExpr: PathExpression; }; diff --git a/src/utils/converter.ts b/src/utils/converter.ts index 404aa5e..d83cb5c 100644 --- a/src/utils/converter.ts +++ b/src/utils/converter.ts @@ -1,6 +1,5 @@ /* eslint-disable no-param-reassign */ import { JsonTemplateMappingError } from '../errors/mapping'; -import { EMPTY_EXPR } from '../constants'; import { SyntaxType, PathExpression, @@ -13,6 +12,7 @@ import { BlockExpression, TokenType, BinaryExpression, + PathType, } from '../types'; import { createBlockExpression, getLastElement } from './common'; @@ -40,20 +40,23 @@ function findOrCreateObjectPropExpression( } function processArrayIndexFilter( + flatMapping: FlatMappingAST, currrentOutputPropAST: ObjectPropExpression, filter: IndexFilterExpression, + isLastPart: boolean, ): ObjectExpression { const filterIndex = filter.indexes.elements[0].value; if (currrentOutputPropAST.value.type !== SyntaxType.ARRAY_EXPR) { const elements: Expression[] = []; - elements[filterIndex] = currrentOutputPropAST.value; + elements[filterIndex] = isLastPart ? flatMapping.inputExpr : currrentOutputPropAST.value; currrentOutputPropAST.value = { type: SyntaxType.ARRAY_EXPR, elements, }; } else if (!currrentOutputPropAST.value.elements[filterIndex]) { - (currrentOutputPropAST.value as ArrayExpression).elements[filterIndex] = - createObjectExpression(); + (currrentOutputPropAST.value as ArrayExpression).elements[filterIndex] = isLastPart + ? flatMapping.inputExpr + : createObjectExpression(); } return currrentOutputPropAST.value.elements[filterIndex]; } @@ -67,24 +70,27 @@ function isPathWithEmptyPartsAndObjectRoot(expr: Expression) { } function getPathExpressionForAllFilter( - currentInputAST: PathExpression, + currentInputAST: Expression, root: any, parts: Expression[] = [], ): PathExpression { return { type: SyntaxType.PATH, root, - pathType: currentInputAST.pathType, - inferredPathType: currentInputAST.inferredPathType, + pathType: currentInputAST.pathType || PathType.UNKNOWN, + inferredPathType: currentInputAST.inferredPathType || PathType.UNKNOWN, parts, returnAsArray: true, } as PathExpression; } -function validateResultOfAllFilter(objectExpr: Expression, flatMapping: FlatMappingAST) { +function validateResultOfAllFilter( + objectExpr: Expression | undefined, + flatMapping: FlatMappingAST, +) { if ( + !objectExpr?.props || objectExpr.type !== SyntaxType.OBJECT_EXPR || - !objectExpr.props || !Array.isArray(objectExpr.props) ) { throw new JsonTemplateMappingError( @@ -95,45 +101,95 @@ function validateResultOfAllFilter(objectExpr: Expression, flatMapping: FlatMapp } } +function addToArrayToExpression(expr: Expression) { + return { + type: SyntaxType.PATH, + root: expr, + returnAsArray: true, + parts: [], + }; +} + +function handleAllFilterIndexFound( + currentInputAST: Expression, + currentOutputPropAST: ObjectPropExpression, + filterIndex: number, + isLastPart: boolean, +) { + const matchedInputParts = currentInputAST.parts.splice(0, filterIndex + 1); + if (isPathWithEmptyPartsAndObjectRoot(currentOutputPropAST.value)) { + currentOutputPropAST.value = currentOutputPropAST.value.root; + } + + if (currentOutputPropAST.value.type !== SyntaxType.PATH) { + matchedInputParts.push( + createBlockExpression(isLastPart ? currentInputAST : currentOutputPropAST.value), + ); + currentOutputPropAST.value = getPathExpressionForAllFilter( + currentInputAST, + currentInputAST.root, + matchedInputParts, + ); + } + currentInputAST.root = undefined; +} + +function findAllFilterIndex(expr: Expression): number { + let filterIndex = -1; + if (expr.type === SyntaxType.PATH) { + filterIndex = expr.parts.findIndex((part) => part.type === SyntaxType.OBJECT_FILTER_EXPR); + } + return filterIndex; +} + +function handleAllFilterIndexNotFound( + currentInputAST: Expression, + currentOutputPropAST: ObjectPropExpression, + isLastPart: boolean, +): ObjectExpression | undefined { + if (currentOutputPropAST.value.type === SyntaxType.OBJECT_EXPR) { + const currObjectExpr = currentOutputPropAST.value as ObjectExpression; + currentOutputPropAST.value = isLastPart + ? addToArrayToExpression(currentInputAST) + : getPathExpressionForAllFilter(currentInputAST, currObjectExpr); + return currObjectExpr; + } + if (isPathWithEmptyPartsAndObjectRoot(currentOutputPropAST.value)) { + return currentOutputPropAST.value.root as ObjectExpression; + } +} + +function getNextObjectExpressionForAllFilter( + flatMapping: FlatMappingAST, + currentOutputPropAST: ObjectPropExpression, + isLastPart: boolean, +) { + const blockExpr = getLastElement(currentOutputPropAST.value.parts) as Expression; + const objectExpr = isLastPart ? createObjectExpression() : blockExpr?.statements?.[0]; + validateResultOfAllFilter(objectExpr, flatMapping); + return objectExpr; +} + function processAllFilter( flatMapping: FlatMappingAST, currentOutputPropAST: ObjectPropExpression, + isLastPart: boolean, ): ObjectExpression { const { inputExpr: currentInputAST } = flatMapping; - const filterIndex = currentInputAST.parts.findIndex( - (part) => part.type === SyntaxType.OBJECT_FILTER_EXPR, - ); - + const filterIndex = findAllFilterIndex(currentInputAST); if (filterIndex === -1) { - if (currentOutputPropAST.value.type === SyntaxType.OBJECT_EXPR) { - const currObjectExpr = currentOutputPropAST.value as ObjectExpression; - currentOutputPropAST.value = getPathExpressionForAllFilter(currentInputAST, currObjectExpr); - return currObjectExpr; - } - if (isPathWithEmptyPartsAndObjectRoot(currentOutputPropAST.value)) { - return currentOutputPropAST.value.root as ObjectExpression; + const objectExpr = handleAllFilterIndexNotFound( + currentInputAST, + currentOutputPropAST, + isLastPart, + ); + if (objectExpr) { + return objectExpr; } } else { - const matchedInputParts = currentInputAST.parts.splice(0, filterIndex + 1); - if (isPathWithEmptyPartsAndObjectRoot(currentOutputPropAST.value)) { - currentOutputPropAST.value = currentOutputPropAST.value.root; - } - - if (currentOutputPropAST.value.type !== SyntaxType.PATH) { - matchedInputParts.push(createBlockExpression(currentOutputPropAST.value)); - currentOutputPropAST.value = getPathExpressionForAllFilter( - currentInputAST, - currentInputAST.root, - matchedInputParts, - ); - } - currentInputAST.root = undefined; + handleAllFilterIndexFound(currentInputAST, currentOutputPropAST, filterIndex, isLastPart); } - - const blockExpr = getLastElement(currentOutputPropAST.value.parts) as Expression; - const objectExpr = blockExpr?.statements?.[0] || EMPTY_EXPR; - validateResultOfAllFilter(objectExpr, flatMapping); - return objectExpr; + return getNextObjectExpressionForAllFilter(flatMapping, currentOutputPropAST, isLastPart); } function isWildcardSelector(expr: Expression): boolean { @@ -201,12 +257,30 @@ function handleNextPart( ): ObjectExpression | undefined { const nextOutputPart = flatMapping.outputExpr.parts[partNum]; if (nextOutputPart.filter?.type === SyntaxType.ALL_FILTER_EXPR) { - return processAllFilter(flatMapping, currentOutputPropAST); + const objectExpr = processAllFilter( + flatMapping, + currentOutputPropAST, + partNum === flatMapping.outputExpr.parts.length - 1 && !nextOutputPart.options?.index, + ); + if (nextOutputPart.options?.index) { + objectExpr.props.push({ + type: SyntaxType.OBJECT_PROP_EXPR, + key: nextOutputPart.options?.index, + value: { + type: SyntaxType.PATH, + root: nextOutputPart.options?.index, + parts: [], + }, + }); + } + return objectExpr; } if (nextOutputPart.filter?.type === SyntaxType.ARRAY_INDEX_FILTER_EXPR) { return processArrayIndexFilter( + flatMapping, currentOutputPropAST, nextOutputPart.filter as IndexFilterExpression, + partNum === flatMapping.outputExpr.parts.length - 1, ); } if (isWildcardSelector(nextOutputPart)) { @@ -308,6 +382,29 @@ function handleRootOnlyOutputMapping(flatMapping: FlatMappingAST, outputAST: Obj } as ObjectPropExpression); } +function validateMappingsForIndexVar(flatMapping: FlatMappingAST, indexVar: string) { + if (flatMapping.inputExpr.type !== SyntaxType.PATH) { + throw new JsonTemplateMappingError( + 'Invalid mapping: input should be path expression', + flatMapping.input as string, + flatMapping.output as string, + ); + } + const foundIndexVar = flatMapping.inputExpr.parts.some( + (item) => + item?.type === SyntaxType.OBJECT_FILTER_EXPR && + item.filter.type === SyntaxType.ALL_FILTER_EXPR && + item.options?.index === indexVar, + ); + if (!foundIndexVar) { + throw new JsonTemplateMappingError( + `Invalid mapping: index variable:${indexVar} not found in input path`, + flatMapping.input as string, + flatMapping.output as string, + ); + } +} + function validateMapping(flatMapping: FlatMappingAST) { if (flatMapping.outputExpr.type !== SyntaxType.PATH) { throw new JsonTemplateMappingError( @@ -316,6 +413,10 @@ function validateMapping(flatMapping: FlatMappingAST) { flatMapping.output as string, ); } + const lastPart = getLastElement(flatMapping.outputExpr.parts); + if (lastPart?.options?.index) { + validateMappingsForIndexVar(flatMapping, lastPart.options.index); + } } function processFlatMappingParts(flatMapping: FlatMappingAST, objectExpr: ObjectExpression) { @@ -341,7 +442,7 @@ export function convertToObjectMapping( const objectPropExpr = { type: SyntaxType.OBJECT_PROP_EXPR, key: '', - value: objectExpr as Expression, + value: pathAST || objectExpr, }; objectExpr = handleNextParts(flatMapping, 0, objectPropExpr); pathAST = objectPropExpr.value as PathExpression; diff --git a/test/scenarios/context_variables/data.ts b/test/scenarios/context_variables/data.ts index cc66a9a..1a0f4e4 100644 --- a/test/scenarios/context_variables/data.ts +++ b/test/scenarios/context_variables/data.ts @@ -1,6 +1,23 @@ import { Scenario } from '../../types'; export const data: Scenario[] = [ + { + template: '.a.b@b[]', + description: 'context variable in last part', + input: { + a: { + b: [ + { + c: 1, + }, + { + c: 2, + }, + ], + }, + }, + output: [{ c: 1 }, { c: 2 }], + }, { templatePath: 'filter.jt', input: [[{ a: 1 }], [{ a: 2 }, { a: 3 }], [{ a: 4 }, { a: 5 }, { a: 6 }]], diff --git a/test/scenarios/mappings/context_vars_mapping.json b/test/scenarios/mappings/context_vars_mapping.json new file mode 100644 index 0000000..cc9d6fe --- /dev/null +++ b/test/scenarios/mappings/context_vars_mapping.json @@ -0,0 +1,10 @@ +[ + { + "from": "$.a[*].#index", + "to": "$.b[*].#index" + }, + { + "from": "$.a[*].foo", + "to": "$.b[*].bar" + } +] diff --git a/test/scenarios/mappings/data.ts b/test/scenarios/mappings/data.ts index 93e7cb0..aed7e80 100644 --- a/test/scenarios/mappings/data.ts +++ b/test/scenarios/mappings/data.ts @@ -115,6 +115,43 @@ export const data: Scenario[] = [ }, }, }, + { + mappingsPath: 'context_vars_mapping.json', + input: { + a: [ + { + foo: 1, + }, + { + foo: 2, + }, + ], + }, + output: { + b: [ + { bar: 1, index: 0 }, + { bar: 2, index: 1 }, + ], + }, + }, + { + mappings: [ + { + from: '$.a[*]', + to: '$.b[*].#index', + }, + ], + error: 'Invalid mapping', + }, + { + mappings: [ + { + from: '1', + to: '$.b[*].#index', + }, + ], + error: 'Invalid mapping', + }, { mappingsPath: 'filters.json', @@ -150,6 +187,21 @@ export const data: Scenario[] = [ ], }, }, + { + description: 'Index mappings in last part', + mappings: [ + { + from: '$.a[0]', + to: '$.b[0]', + }, + { + from: '$.a[1]', + to: '$.b[1]', + }, + ], + input: { a: [1, 2, 3] }, + output: { b: [1, 2] }, + }, { mappingsPath: 'invalid_array_mappings.json', error: 'Invalid mapping', @@ -380,6 +432,27 @@ export const data: Scenario[] = [ }, ], }, + { + mappingsPath: 'root_context_vars_mapping.json', + input: [ + { + foo: 1, + }, + { + foo: 2, + }, + ], + output: [ + { + bar: 1, + index: 0, + }, + { + bar: 2, + index: 1, + }, + ], + }, { mappingsPath: 'root_index_mappings.json', input: { @@ -454,6 +527,37 @@ export const data: Scenario[] = [ }, }, }, + { + description: 'array mappings in last part', + mappings: [ + { + from: '$.a[*]', + to: '$.b[*]', + }, + ], + input: { a: [1, 2, 3] }, + output: { b: [1, 2, 3] }, + }, + { + description: 'array mappings to scalar value', + mappings: [ + { + from: '1', + to: '$.a[*].b', + }, + ], + output: { a: [{ b: 1 }] }, + }, + { + description: 'array mappings to scalar value', + mappings: [ + { + from: '1', + to: '$.a[*]', + }, + ], + output: { a: [1] }, + }, { mappingsPath: 'simple_array_mappings.json', input: { diff --git a/test/scenarios/mappings/root_context_vars_mapping.json b/test/scenarios/mappings/root_context_vars_mapping.json new file mode 100644 index 0000000..3e9c068 --- /dev/null +++ b/test/scenarios/mappings/root_context_vars_mapping.json @@ -0,0 +1,10 @@ +[ + { + "from": "$[*].#index", + "to": "$[*].#index" + }, + { + "from": "$[*].foo", + "to": "$[*].bar" + } +] diff --git a/test/types.ts b/test/types.ts index d45672c..723ddd2 100644 --- a/test/types.ts +++ b/test/types.ts @@ -1,9 +1,10 @@ -import type { EngineOptions } from '../src'; +import type { EngineOptions, FlatMappingPaths } from '../src'; export type Scenario = { description?: string; input?: unknown; templatePath?: string; + mappings?: FlatMappingPaths[]; mappingsPath?: string; template?: string; options?: EngineOptions; diff --git a/test/utils/scenario.ts b/test/utils/scenario.ts index c3d4826..1fc2521 100644 --- a/test/utils/scenario.ts +++ b/test/utils/scenario.ts @@ -9,22 +9,28 @@ function getTemplate(scenarioDir: string, scenario: Scenario): string { } function getDefaultPathType(scenario: Scenario): PathType { - return scenario.mappingsPath ? PathType.JSON : PathType.SIMPLE; + return scenario.mappingsPath || scenario.mappings ? PathType.JSON : PathType.SIMPLE; } function initializeScenario(scenarioDir: string, scenario: Scenario) { scenario.options = scenario.options || {}; scenario.options.defaultPathType = scenario.options.defaultPathType || getDefaultPathType(scenario); - let template = scenario.template ?? getTemplate(scenarioDir, scenario); + if (scenario.mappingsPath) { - template = JsonTemplateEngine.convertMappingsToTemplate( - JSON.parse(template) as FlatMappingPaths[], + scenario.mappings = JSON.parse(getTemplate(scenarioDir, scenario)) as FlatMappingPaths[]; + } + if (scenario.mappings) { + scenario.template = JsonTemplateEngine.convertMappingsToTemplate( + scenario.mappings as FlatMappingPaths[], scenario.options, ); } + if (scenario.template === undefined) { + scenario.template = getTemplate(scenarioDir, scenario); + } scenario.template = JsonTemplateEngine.reverseTranslate( - JsonTemplateEngine.parse(template, scenario.options), + JsonTemplateEngine.parse(scenario.template, scenario.options), scenario.options, ); }