Skip to content

Commit

Permalink
feat: merge filters and indexes (#113)
Browse files Browse the repository at this point in the history
* fix: context vars mappings

* feat: merge filters and indexes

* fix: sonar issues

* refactor: mapping test cases filter.json
  • Loading branch information
koladilip authored Jul 2, 2024
1 parent 5f1d213 commit ab652ff
Show file tree
Hide file tree
Showing 7 changed files with 48 additions and 26 deletions.
7 changes: 4 additions & 3 deletions src/engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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],
}));
}

Expand Down Expand Up @@ -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);
}
Expand Down
21 changes: 13 additions & 8 deletions src/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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) {
Expand All @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ export enum PathType {
export interface EngineOptions {
compileTimeBindings?: Record<string, any>;
defaultPathType?: PathType;
mappings?: boolean;
}

export type Token = {
Expand Down
23 changes: 19 additions & 4 deletions src/utils/converter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -130,6 +146,8 @@ function handleAllFilterIndexFound(
currentInputAST.root,
matchedInputParts,
);
} else {
mergeObjectFilterParts(currentOutputPropAST.value.parts, matchedInputParts);
}
currentInputAST.root = undefined;
}
Expand Down Expand Up @@ -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(
Expand Down
16 changes: 8 additions & 8 deletions test/scenarios/mappings/all_features.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,39 +20,39 @@
"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"
},
{
"input": "$.products[?(@.category)].(@.price * @.quantity * (1 - $.discount / 100)).sum()",
"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"
}
]
2 changes: 1 addition & 1 deletion test/scenarios/mappings/context_vars_mapping.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"to": "$.b[*].#index"
},
{
"from": "$.a[*].foo",
"from": "$.a[*].#index.foo",
"to": "$.b[*].bar"
}
]
4 changes: 2 additions & 2 deletions test/scenarios/mappings/filters.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
]

0 comments on commit ab652ff

Please sign in to comment.