diff --git a/designer/client/src/components/graph/node-modal/aggregate/aggMapLikeParser.tsx b/designer/client/src/components/graph/node-modal/aggregate/aggMapLikeParser.tsx deleted file mode 100644 index c2a692568fb..00000000000 --- a/designer/client/src/components/graph/node-modal/aggregate/aggMapLikeParser.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import { parse } from "ts-spel"; -import { Ast } from "ts-spel/lib/lib/Ast"; - -export class AggMapLikeParser { - parseList = (input: string) => { - const ast = this.#parse(input); - return this.#parseList(input, ast); - }; - - parseObject = (input: string) => { - const ast = this.#parse(input); - return this.#parseObject(input, ast); - }; - - #parse(input: string) { - try { - return parse(input, true); - } catch (e) { - return null; - } - } - - #parseList(input: string, ast?: Ast) { - if (ast?.type !== "CompoundExpression") return null; - if (ast.expressionComponents[0].type !== "InlineList") return null; - if (ast.expressionComponents[1].type !== "PropertyReference") return null; - if (ast.expressionComponents[1].propertyName !== "toString") return null; - return ast.expressionComponents[0].elements.map((el) => this.#printFragment(input, el)); - } - - #printFragment( - text: string, - el: { - start: number; - end: number; - }, - ) { - return text.slice(el.start, el.end).trim(); - } - - #parseObject(input: string, ast?: Ast) { - switch (ast?.type) { - case "InlineList": - return ast.elements.length < 1 ? {} : null; - case "InlineMap": - return Object.fromEntries(Object.entries(ast.elements).map(([key, el]) => [key, this.#printFragment(input, el)])); - case "CompoundExpression": - if (ast.expressionComponents[0].type !== "VariableReference") return null; - if (ast.expressionComponents[0].variableName !== "AGG") return null; - if (ast.expressionComponents[1].type !== "MethodReference") return null; - if (ast.expressionComponents[1].methodName !== "map") return null; - return this.#parseObject(input, ast.expressionComponents[1].args[0]); - default: - return null; - } - } -} diff --git a/designer/client/src/components/graph/node-modal/aggregate/pareserHelpers.tsx b/designer/client/src/components/graph/node-modal/aggregate/pareserHelpers.tsx new file mode 100644 index 00000000000..3df53efde73 --- /dev/null +++ b/designer/client/src/components/graph/node-modal/aggregate/pareserHelpers.tsx @@ -0,0 +1,47 @@ +import { parse } from "ts-spel"; +import { Ast } from "ts-spel/lib/lib/Ast"; + +function getAst(input: string) { + try { + return parse(input, true); + } catch (e) { + return null; + } +} + +function printFragment(text: string, el: { start: number; end: number }): string { + return text.slice(el.start, el.end).trim(); +} + +function mapToList(input: string, ast?: Ast): string[] | null { + if (ast?.type !== "CompoundExpression") return null; + if (ast.expressionComponents[0].type !== "InlineList") return null; + if (ast.expressionComponents[1].type !== "PropertyReference") return null; + if (ast.expressionComponents[1].propertyName !== "toString") return null; + return ast.expressionComponents[0].elements.map((el) => printFragment(input, el)); +} + +function mapToObject(input: string, ast?: Ast): Record | null { + switch (ast?.type) { + case "InlineList": + return ast.elements.length < 1 ? {} : null; + case "InlineMap": + return Object.fromEntries(Object.entries(ast.elements).map(([key, el]) => [key, printFragment(input, el)])); + case "CompoundExpression": + if (ast.expressionComponents[0].type !== "VariableReference") return null; + if (ast.expressionComponents[0].variableName !== "AGG") return null; + if (ast.expressionComponents[1].type !== "MethodReference") return null; + if (ast.expressionComponents[1].methodName !== "map") return null; + return mapToObject(input, ast.expressionComponents[1].args[0]); + default: + return null; + } +} + +export function parseToList(input: string): string[] { + return mapToList(input, getAst(input)); +} + +export function parseToObject(input: string): Record { + return mapToObject(input, getAst(input)); +} diff --git a/designer/client/src/components/graph/node-modal/aggregate/parser.test.ts b/designer/client/src/components/graph/node-modal/aggregate/parser.test.ts deleted file mode 100644 index 9b915b7c4e6..00000000000 --- a/designer/client/src/components/graph/node-modal/aggregate/parser.test.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { AggMapLikeParser } from "./aggMapLikeParser"; - -describe("AggMapLikeParser", () => { - let parser: AggMapLikeParser; - - beforeEach(() => { - parser = new AggMapLikeParser(); - }); - - it.each([ - [`aaa`, null], - [`"aaa"`, null], - [`{`, null], - [`{}`, null], - [`{123}`, null], - [`{}.xxxx`, null], - [`{}.toString`, []], - [`#COLLECTION.join({}, "|")`, null], - [`#xxxx.yyyy({}, "|")`, null], - [`{123}.toString`, ["123"]], - [`{ 123 }.toString`, ["123"]], - [`{123,456}.toString`, ["123", "456"]], - [`{ 123, 456 }.toString`, ["123", "456"]], - [`{ 123, aaa, 456 }.toString`, ["123", "aaa", "456"]], - [`{ 123, aaa.bbb }.toString`, ["123", "aaa.bbb"]], - [`{ 123, aaa_bbb }.toString`, ["123", "aaa_bbb"]], - [`{ 123, aaa bbb, ccc }.toString`, ["123", "aaa", "bbb", "ccc"]], - [`{ 123, "aaa bbb" }.toString`, ["123", `"aaa bbb"`]], - [`{ 123, 'aaa bbb' }.toString`, ["123", `'aaa bbb'`]], - [`{ 123, #aaa.bbb }.toString`, ["123", `#aaa.bbb`]], - [`{ 123, 123.bbb }.toString`, ["123", `123.bbb`]], - [`{ 123, 123.456.bbb }.toString`, ["123", `123.456.bbb`]], - [`{ 123, "{ 123, 456 }" }.toString`, ["123", `"{ 123, 456 }"`]], - [`{ 123, { 123, "456" } }.toString`, ["123", `{ 123, "456" }`]], - [`{ 123, { aaa: 123, bbb: "456" } }.toString`, ["123", `{ aaa: 123, bbb: "456" }`]], - [`{ 123, {123,456}.^[2 + 5].test(123) }.toString`, ["123", `{123,456}.^[2 + 5].test(123)`]], - [`#COLLECTION.join({ 123, 456 }, "|")`, null], - ])("should parse list: %s => %s", (input, output) => { - expect(parser.parseList(input)).toEqual(output); - }); - - it.each([ - [`aaa`, null], - [`"aaa"`, null], - [`{}`, {}], - [`#AGG.map({})`, {}], - [`#AGG2.map({})`, null], - [`{aaa:123}`, { aaa: "123" }], - [`#AGG.map({aaa:123})`, { aaa: "123" }], - [`#AGG.map({ aaa:123, bbb:456 })`, { aaa: "123", bbb: "456" }], - [`{ aaa: 123, bbb: "456" }`, { aaa: "123", bbb: `"456"` }], - [`{ a: 123, b c: 123 }`, { a: "123" }], - [`{ aaa: 1 2 3 }`, { aaa: "1" }], - [`{ "a a a": 123 }`, { "a a a": "123" }], - [`{ aaa: #aaa.bbb }`, { aaa: "#aaa.bbb" }], - [`{ aaa: aaa.bbb }`, { aaa: "aaa.bbb" }], - [`{ aaa: "aaa bbb" }`, { aaa: `"aaa bbb"` }], - [`{ aaa: {} }`, { aaa: `{}` }], - [`{ aaa: { bbb: 123, ccc: "456" } }`, { aaa: `{ bbb: 123, ccc: "456" }` }], - [`{ aaa: { 123, "456" } }`, { aaa: `{ 123, "456" }` }], - [`{ aaa: "{ 123, '456' }" }`, { aaa: `"{ 123, '456' }"` }], - [`{ aaa: '{ 123, "456" }' }`, { aaa: `'{ 123, "456" }'` }], - [`{ aaa: {123,456}.![2 + 5] }`, { aaa: "{123,456}.![2 + 5]" }], - [`{ aaa: {123,456}.![2 + 5], bbb: 123 }`, { aaa: `{123,456}.![2 + 5]`, bbb: `123` }], - [`{ aaa: {123,456}.?[2 + 5] }`, { aaa: `{123,456}.?[2 + 5]` }], - [`{ aaa: {123,456}.^[2 + 5] }`, { aaa: `{123,456}.^[2 + 5]` }], - [`{ aaa: {123,456}.^[2 + 5].test(123) }`, { aaa: `{123,456}.^[2 + 5].test(123)` }], - [`{ aaa: 123 + 456 }`, { aaa: `123 + 456` }], - ])("should parse map: %s => %s", (input, output) => { - expect(parser.parseObject(input)).toEqual(output); - }); -}); diff --git a/designer/client/src/components/graph/node-modal/aggregate/parserHelpers.test.ts b/designer/client/src/components/graph/node-modal/aggregate/parserHelpers.test.ts new file mode 100644 index 00000000000..4ca17cd8a20 --- /dev/null +++ b/designer/client/src/components/graph/node-modal/aggregate/parserHelpers.test.ts @@ -0,0 +1,73 @@ +import { parseToList, parseToObject } from "./pareserHelpers"; + +describe("parseHelpers", () => { + describe("parseToList", () => { + it.each([ + [`aaa`, null], + [`"aaa"`, null], + [`{`, null], + [`{}`, null], + [`{123}`, null], + [`{}.xxxx`, null], + [`{}.toString`, []], + [`#COLLECTION.join({}, "|")`, null], + [`#xxxx.yyyy({}, "|")`, null], + [`{123}.toString`, ["123"]], + [`{ 123 }.toString`, ["123"]], + [`{123,456}.toString`, ["123", "456"]], + [`{ 123, 456 }.toString`, ["123", "456"]], + [`{ 123, aaa, 456 }.toString`, ["123", "aaa", "456"]], + [`{ 123, aaa.bbb }.toString`, ["123", "aaa.bbb"]], + [`{ 123, aaa_bbb }.toString`, ["123", "aaa_bbb"]], + [`{ 123, aaa bbb, ccc }.toString`, ["123", "aaa", "bbb", "ccc"]], + [`{ 123, "aaa bbb" }.toString`, ["123", `"aaa bbb"`]], + [`{ 123, 'aaa bbb' }.toString`, ["123", `'aaa bbb'`]], + [`{ 123, #aaa.bbb }.toString`, ["123", `#aaa.bbb`]], + [`{ 123, 123.bbb }.toString`, ["123", `123.bbb`]], + [`{ 123, 123.456.bbb }.toString`, ["123", `123.456.bbb`]], + [`{ 123, "{ 123, 456 }" }.toString`, ["123", `"{ 123, 456 }"`]], + [`{ 123, { 123, "456" } }.toString`, ["123", `{ 123, "456" }`]], + [`{ 123, { aaa: 123, bbb: "456" } }.toString`, ["123", `{ aaa: 123, bbb: "456" }`]], + [`{ 123, {123,456}.^[2 + 5].test(123) }.toString`, ["123", `{123,456}.^[2 + 5].test(123)`]], + [`#COLLECTION.join({ 123, 456 }, "|")`, null], + ])("should parse: %s to: %s", (input, output) => { + expect(parseToList(input)).toEqual(output); + }); + }); + + describe("parseToObject", () => { + it.each([ + [`aaa`, null], + [`"aaa"`, null], + [`{}`, {}], + [`#AGG.map({})`, {}], + [`#AGG.map({:})`, {}], + [`#AGG2.map({})`, null], + [`{aaa:123}`, { aaa: "123" }], + [`#AGG.map({aaa:123})`, { aaa: "123" }], + [`#AGG.map({ aaa:123, bbb:456 })`, { aaa: "123", bbb: "456" }], + [`{ aaa: 123, bbb: "456" }`, { aaa: "123", bbb: `"456"` }], + [`{ a: 123, b c: 123 }`, { a: "123" }], + [`{ aaa: 1 2 3 }`, { aaa: "1" }], + [`{ "a a a": 123 }`, { "a a a": "123" }], + [`{ aaa: #aaa.bbb }`, { aaa: "#aaa.bbb" }], + [`{ aaa: aaa.bbb }`, { aaa: "aaa.bbb" }], + [`{ aaa: "aaa bbb" }`, { aaa: `"aaa bbb"` }], + [`{ aaa: {} }`, { aaa: `{}` }], + [`{ aaa: { bbb: 123, ccc: "456" } }`, { aaa: `{ bbb: 123, ccc: "456" }` }], + [`{ aaa: { 123, "456" } }`, { aaa: `{ 123, "456" }` }], + [`{ aaa: "{ 123, '456' }" }`, { aaa: `"{ 123, '456' }"` }], + [`{ aaa: '{ 123, "456" }' }`, { aaa: `'{ 123, "456" }'` }], + [`{ aaa: '{ 123, "456" }' }`, { aaa: `'{ 123, "456" }'` }], + [`{ aaa:\n { 123,\n "456" } }`, { aaa: `{ 123,\n "456" }` }], + [`{ aaa: {123,456}.![2 + 5] }`, { aaa: "{123,456}.![2 + 5]" }], + [`{ aaa: {123,456}.![2 + 5], bbb: 123 }`, { aaa: `{123,456}.![2 + 5]`, bbb: `123` }], + [`{ aaa: {123,456}.?[2 + 5] }`, { aaa: `{123,456}.?[2 + 5]` }], + [`{ aaa: {123,456}.^[2 + 5] }`, { aaa: `{123,456}.^[2 + 5]` }], + [`{ aaa: {123,456}.^[2 + 5].test(123) }`, { aaa: `{123,456}.^[2 + 5].test(123)` }], + [`{ aaa: 123 + 456 }`, { aaa: `123 + 456` }], + ])("should parse: %s to: %s", (input, output) => { + expect(parseToObject(input)).toEqual(output); + }); + }); +}); diff --git a/designer/client/src/components/graph/node-modal/aggregate/useAggParamsSerializer.tsx b/designer/client/src/components/graph/node-modal/aggregate/useAggParamsSerializer.tsx index 44dc4b49a50..1ce7e3566dc 100644 --- a/designer/client/src/components/graph/node-modal/aggregate/useAggParamsSerializer.tsx +++ b/designer/client/src/components/graph/node-modal/aggregate/useAggParamsSerializer.tsx @@ -1,15 +1,11 @@ import { padStart } from "lodash"; -import { useCallback, useMemo } from "react"; -import { AggMapLikeParser } from "./aggMapLikeParser"; +import { useCallback } from "react"; +import { parseToList, parseToObject } from "./pareserHelpers"; export function useAggParamsSerializer(): [ (text: string) => Record, (paramName: string, map: Record) => string, ] { - const parser = useMemo(() => new AggMapLikeParser(), []); - - const deserialize = useCallback((input: string) => parser.parseObject(input), [parser]); - const serialize = useCallback((paramName: string, map: Record): string => { const entries = Object.entries(map || {}).map(([key, value]) => { const trimmedKey = key.trim(); @@ -27,14 +23,10 @@ export function useAggParamsSerializer(): [ } }, []); - return [deserialize, serialize]; + return [parseToObject, serialize]; } export function useGroupByParamsSerializer(): [(text: string) => string[], (paramName: string, arr: string[]) => string] { - const parser = useMemo(() => new AggMapLikeParser(), []); - - const deserialize = useCallback((input: string) => parser.parseList(input), [parser]); - const serialize = useCallback((paramName: string, arr: string[]): string => { const entries = arr.map((value) => { return value?.trim(); @@ -50,5 +42,5 @@ export function useGroupByParamsSerializer(): [(text: string) => string[], (para } }, []); - return [deserialize, serialize]; + return [parseToList, serialize]; }