From 381c09fa47212a12f6c7bc166ac641afd97c9681 Mon Sep 17 00:00:00 2001 From: Michael Kret <88898367+michael-radency@users.noreply.github.com> Date: Tue, 13 Sep 2022 18:09:16 +0300 Subject: [PATCH] fix(core): Fix node renaming in expressions --- packages/workflow/src/Workflow.ts | 53 +++++++++-- packages/workflow/test/Workflow.test.ts | 111 ++++++++++++++++++++++++ 2 files changed, 157 insertions(+), 7 deletions(-) diff --git a/packages/workflow/src/Workflow.ts b/packages/workflow/src/Workflow.ts index bb460df3878f4..76bbd7ced9157 100644 --- a/packages/workflow/src/Workflow.ts +++ b/packages/workflow/src/Workflow.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-use-before-define */ /* eslint-disable no-await-in-loop */ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ @@ -444,19 +445,41 @@ export class Workflow { // Reached the actual value if (typeof parameterValue === 'string' && parameterValue.charAt(0) === '=') { // Is expression so has to be rewritten - // To not run the "expensive" regex stuff when it is not needed // make a simple check first if it really contains the the node-name if (parameterValue.includes(currentName)) { // Really contains node-name (even though we do not know yet if really as $node-expression) - // In case some special characters are used in name escape them - const currentNameEscaped = currentName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + const escapedOldName = backslashEscape(currentName); // for match + const escapedNewName = dollarEscape(newName); // for replacement + + const setNewName = (expression: string, oldPattern: string) => + expression.replace(new RegExp(oldPattern, 'g'), `$1${escapedNewName}$2`); + + if (parameterValue.includes('$(')) { + const oldPattern = String.raw`(\$\(['"])${escapedOldName}(['"]\))`; + parameterValue = setNewName(parameterValue, oldPattern); + } + + if (parameterValue.includes('$node[')) { + const oldPattern = String.raw`(\$node\[['"])${escapedOldName}(['"]\])`; + parameterValue = setNewName(parameterValue, oldPattern); + } + + if (parameterValue.includes('$node.')) { + const oldPattern = String.raw`(\$node\.)${escapedOldName}(\.?)`; + parameterValue = setNewName(parameterValue, oldPattern); + + if (hasDotNotationBannedChar(newName)) { + const regex = new RegExp(`.${backslashEscape(newName)}( |\\.)`, 'g'); + parameterValue = parameterValue.replace(regex, `["${escapedNewName}"]$1`); + } + } - parameterValue = parameterValue.replace( - new RegExp(`(\\$node(\\.|\\["|\\['))${currentNameEscaped}((\\.|"\\]|'\\]))`, 'g'), - `$1${newName}$3`, - ); + if (parameterValue.includes('$items(')) { + const oldPattern = String.raw`(\$items\(['"])${escapedOldName}(['"],|['"]\))`; + parameterValue = setNewName(parameterValue, oldPattern); + } } } @@ -1397,3 +1420,19 @@ export class Workflow { return { data: null }; } } + +function hasDotNotationBannedChar(nodeName: string) { + const DOT_NOTATION_BANNED_CHARS = /^(\d)|[\\ `!@#$%^&*()_+\-=[\]{};':"\\|,.<>?~]/g; + + return DOT_NOTATION_BANNED_CHARS.test(nodeName); +} + +function backslashEscape(nodeName: string) { + const BACKSLASH_ESCAPABLE_CHARS = /[.*+?^${}()|[\]\\]/g; + + return nodeName.replace(BACKSLASH_ESCAPABLE_CHARS, (char) => `\\${char}`); +} + +function dollarEscape(nodeName: string) { + return nodeName.replace(new RegExp('\\$', 'g'), '$$$$'); +} diff --git a/packages/workflow/test/Workflow.test.ts b/packages/workflow/test/Workflow.test.ts index 20d2d2f7f5c22..5922b68a151a6 100644 --- a/packages/workflow/test/Workflow.test.ts +++ b/packages/workflow/test/Workflow.test.ts @@ -66,6 +66,117 @@ describe('Workflow', () => { '={{$node["NewName"]["data"]["value2"] + \' - \' + $node["NewName"]["data"]["value2"]}}', }, }, + { + description: 'should work with $("Node1")', + input: { + currentName: 'Node1', + newName: 'NewName', + parameters: { + value1: '={{$("Node1")["data"]["value1"] + \'Node1\'}}', + value2: '={{$("Node1")["data"]["value2"] + \' - \' + $("Node1")["data"]["value2"]}}', + }, + }, + output: { + value1: '={{$("NewName")["data"]["value1"] + \'Node1\'}}', + value2: '={{$("NewName")["data"]["value2"] + \' - \' + $("NewName")["data"]["value2"]}}', + }, + }, + { + description: 'should work with $items("Node1")', + input: { + currentName: 'Node1', + newName: 'NewName', + parameters: { + value1: '={{$items("Node1")["data"]["value1"] + \'Node1\'}}', + value2: + '={{$items("Node1")["data"]["value2"] + \' - \' + $items("Node1")["data"]["value2"]}}', + }, + }, + output: { + value1: '={{$items("NewName")["data"]["value1"] + \'Node1\'}}', + value2: + '={{$items("NewName")["data"]["value2"] + \' - \' + $items("NewName")["data"]["value2"]}}', + }, + }, + { + description: 'should work with $items("Node1", 0, 1)', + input: { + currentName: 'Node1', + newName: 'NewName', + parameters: { + value1: '={{$items("Node1", 0, 1)["data"]["value1"] + \'Node1\'}}', + value2: + '={{$items("Node1", 0, 1)["data"]["value2"] + \' - \' + $items("Node1", 0, 1)["data"]["value2"]}}', + }, + }, + output: { + value1: '={{$items("NewName", 0, 1)["data"]["value1"] + \'Node1\'}}', + value2: + '={{$items("NewName", 0, 1)["data"]["value2"] + \' - \' + $items("NewName", 0, 1)["data"]["value2"]}}', + }, + }, + { + description: 'should work with dot notation that contains space and special character', + input: { + currentName: 'Node1', + newName: 'New $ Name', + parameters: { + value1: "={{$node.Node1.data.value1 + 'Node1'}}", + value2: "={{$node.Node1.data.value2 + ' - ' + $node.Node1.data.value2}}", + }, + }, + output: { + value1: '={{$node["New $ Name"].data.value1 + \'Node1\'}}', + value2: + '={{$node["New $ Name"].data.value2 + \' - \' + $node["New $ Name"].data.value2}}', + }, + }, + { + description: 'should work with dot notation that contains space and trailing $', + input: { + currentName: 'Node1', + newName: 'NewName$', + parameters: { + value1: "={{$node.Node1.data.value1 + 'Node1'}}", + value2: "={{$node.Node1.data.value2 + ' - ' + $node.Node1.data.value2}}", + }, + }, + output: { + value1: '={{$node["NewName$"].data.value1 + \'Node1\'}}', + value2: '={{$node["NewName$"].data.value2 + \' - \' + $node["NewName$"].data.value2}}', + }, + }, + { + description: 'should work with dot notation that contains space and special character', + input: { + currentName: 'Node1', + newName: 'NewName $ $& $` $$$', + parameters: { + value1: "={{$node.Node1.data.value1 + 'Node1'}}", + value2: "={{$node.Node1.data.value2 + ' - ' + $node.Node1.data.value2}}", + }, + }, + output: { + value1: '={{$node["NewName $ $& $` $$$"].data.value1 + \'Node1\'}}', + value2: + '={{$node["NewName $ $& $` $$$"].data.value2 + \' - \' + $node["NewName $ $& $` $$$"].data.value2}}', + }, + }, + { + description: 'should work with dot notation without trailing dot', + input: { + currentName: 'Node1', + newName: 'NewName', + parameters: { + value1: "={{$node.Node1 + 'Node1'}}", + value2: "={{$node.Node1 + ' - ' + $node.Node1}}", + }, + }, + output: { + value1: "={{$node.NewName + 'Node1'}}", + value2: "={{$node.NewName + ' - ' + $node.NewName}}", + }, + }, { description: "should work with ['nodeName']", input: {