diff --git a/src/engine.ts b/src/engine.ts index 3a620a8..d02f4a6 100644 --- a/src/engine.ts +++ b/src/engine.ts @@ -87,12 +87,13 @@ export class JsonTemplateEngine { mappings: FlatMappingPaths[], options?: EngineOptions, ): FlatMappingAST[] { + const newOptions = { ...options, mappings: true }; 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], + inputExpr: JsonTemplateEngine.parse(mapping.input, newOptions).statements[0], + outputExpr: JsonTemplateEngine.parse(mapping.output, newOptions).statements[0], })); } @@ -131,7 +132,7 @@ export class JsonTemplateEngine { const translator = new JsonTemplateReverseTranslator(options); let newExpr = expr; if (Array.isArray(expr)) { - newExpr = JsonTemplateEngine.parseMappingPaths(expr as FlatMappingPaths[], options); + newExpr = JsonTemplateEngine.parseMappingPaths(expr, options); } return translator.translate(newExpr as Expression); } diff --git a/src/parser.ts b/src/parser.ts index e6deb02..af77631 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -355,7 +355,7 @@ export class JsonTemplateParser { if (!expr.parts.length) { return JsonTemplateParser.setPathTypeIfNotJSON(expr, PathType.SIMPLE); } - return JsonTemplateParser.updatePathExpr(expr); + return this.updatePathExpr(expr); } private static createArrayIndexFilterExpr(expr: Expression): IndexFilterExpression { @@ -1443,10 +1443,15 @@ export class JsonTemplateParser { return this.lexer.throwUnexpectedToken(); } - private static shouldPathBeConvertedAsBlock(parts: Expression[]): boolean { - return parts - .filter((part, index) => part.type === SyntaxType.PATH_OPTIONS && index < parts.length - 1) - .some((part) => part.options?.index ?? part.options?.item); + private shouldPathBeConvertedAsBlock(parts: Expression[]): boolean { + return ( + !this.options?.mappings && + parts + .filter( + (part, index) => part.type === SyntaxType.PATH_OPTIONS && index !== parts.length - 1, + ) + .some((part) => part.options?.index ?? part.options?.item) + ); } private static convertToBlockExpr(expr: Expression): FunctionExpression { @@ -1543,14 +1548,14 @@ export class JsonTemplateParser { return newPathExpr; } - private static updatePathExpr(pathExpr: PathExpression): Expression { + private updatePathExpr(pathExpr: PathExpression): Expression { const newPathExpr = pathExpr; if (newPathExpr.parts.length > 1 && newPathExpr.parts[0].type === SyntaxType.PATH_OPTIONS) { newPathExpr.options = newPathExpr.parts[0].options; newPathExpr.parts.shift(); } - const shouldConvertAsBlock = JsonTemplateParser.shouldPathBeConvertedAsBlock(newPathExpr.parts); + const shouldConvertAsBlock = this.shouldPathBeConvertedAsBlock(newPathExpr.parts); let lastPart = getLastElement(newPathExpr.parts); let fnExpr: FunctionCallExpression | undefined; if (lastPart?.type === SyntaxType.FUNCTION_CALL_EXPR) { @@ -1575,7 +1580,7 @@ export class JsonTemplateParser { if (shouldConvertAsBlock) { expr = JsonTemplateParser.convertToBlockExpr(expr); JsonTemplateParser.setPathTypeIfNotJSON(newPathExpr, PathType.RICH); - } else if (this.isRichPath(newPathExpr)) { + } else if (JsonTemplateParser.isRichPath(newPathExpr)) { JsonTemplateParser.setPathTypeIfNotJSON(newPathExpr, PathType.RICH); } return expr; diff --git a/src/types.ts b/src/types.ts index 45f7bf6..0f2bd4a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -107,6 +107,7 @@ export enum PathType { export interface EngineOptions { compileTimeBindings?: Record; defaultPathType?: PathType; + mappings?: boolean; } export type Token = { diff --git a/src/utils/converter.ts b/src/utils/converter.ts index d83cb5c..c95c74e 100644 --- a/src/utils/converter.ts +++ b/src/utils/converter.ts @@ -110,6 +110,22 @@ function addToArrayToExpression(expr: Expression) { }; } +function mergeObjectFilterParts(existParts: Expression[], newParts: Expression[]) { + for (let index = 0; index < newParts.length; index++) { + if ( + newParts[index].type === SyntaxType.OBJECT_FILTER_EXPR && + existParts[index].type === SyntaxType.OBJECT_FILTER_EXPR + ) { + if (newParts[index].options?.index) { + existParts[index].options = { ...existParts[index].options, ...newParts[index].options }; + } + if (newParts[index].filter.type !== SyntaxType.ALL_FILTER_EXPR) { + existParts[index].filter = newParts[index].filter; + } + } + } +} + function handleAllFilterIndexFound( currentInputAST: Expression, currentOutputPropAST: ObjectPropExpression, @@ -130,6 +146,8 @@ function handleAllFilterIndexFound( currentInputAST.root, matchedInputParts, ); + } else { + mergeObjectFilterParts(currentOutputPropAST.value.parts, matchedInputParts); } currentInputAST.root = undefined; } @@ -391,10 +409,7 @@ function validateMappingsForIndexVar(flatMapping: FlatMappingAST, indexVar: stri ); } const foundIndexVar = flatMapping.inputExpr.parts.some( - (item) => - item?.type === SyntaxType.OBJECT_FILTER_EXPR && - item.filter.type === SyntaxType.ALL_FILTER_EXPR && - item.options?.index === indexVar, + (item) => item?.type === SyntaxType.OBJECT_FILTER_EXPR && item.options?.index === indexVar, ); if (!foundIndexVar) { throw new JsonTemplateMappingError( diff --git a/test/scenarios/mappings/all_features.json b/test/scenarios/mappings/all_features.json index 990207c..23f7883 100644 --- a/test/scenarios/mappings/all_features.json +++ b/test/scenarios/mappings/all_features.json @@ -20,19 +20,19 @@ "output": "$.events[0].name" }, { - "input": "$.products[?(@.category)].name", + "input": "$.products[*].name", "output": "$.events[0].items[*].product_name" }, { - "from": "$.products[?(@.category)].category", + "from": "$.products[*].category", "to": "$.events[0].items[*].product_category" }, { - "input": "$.products[?(@.category)].variations[*].size", + "input": "$.products[*].variations[*].size", "output": "$.events[0].items[*].options[*].s" }, { - "input": "$.products[?(@.category)].(@.price * @.quantity * (1 - $.discount / 100))", + "input": "$.products[*].(@.price * @.quantity * (1 - $.discount / 100))", "output": "$.events[0].items[*].value" }, { @@ -40,19 +40,19 @@ "output": "$.events[0].revenue" }, { - "input": "$.products[?(@.category)].variations[*].length", + "input": "$.products[*].variations[*].length", "output": "$.events[0].items[*].options[*].l" }, { - "input": "$.products[?(@.category)].variations[*].width", + "input": "$.products[*].variations[*].width", "output": "$.events[0].items[*].options[*].w" }, { - "input": "$.products[?(@.category)].variations[*].color", + "input": "$.products[*].variations[*].color", "output": "$.events[0].items[*].options[*].c" }, { - "input": "$.products[?(@.category)].variations[*].height", + "input": "$.products[*].variations[*].height", "output": "$.events[0].items[*].options[*].h" } ] diff --git a/test/scenarios/mappings/context_vars_mapping.json b/test/scenarios/mappings/context_vars_mapping.json index cc9d6fe..be9e2f3 100644 --- a/test/scenarios/mappings/context_vars_mapping.json +++ b/test/scenarios/mappings/context_vars_mapping.json @@ -4,7 +4,7 @@ "to": "$.b[*].#index" }, { - "from": "$.a[*].foo", + "from": "$.a[*].#index.foo", "to": "$.b[*].bar" } ] diff --git a/test/scenarios/mappings/filters.json b/test/scenarios/mappings/filters.json index 34106f2..b49bc97 100644 --- a/test/scenarios/mappings/filters.json +++ b/test/scenarios/mappings/filters.json @@ -4,11 +4,11 @@ "output": "$.items[*].product_id" }, { - "input": "$.products[?(@.category)].name", + "input": "$.products[*].name", "output": "$.items[*].product_name" }, { - "input": "$.products[?(@.category)].category", + "input": "$.products[*].category", "output": "$.items[*].product_category" } ]