From f8f584c136da8ad8b17f82f6f4e95f0d69014b40 Mon Sep 17 00:00:00 2001 From: Mutasem Aldmour <4711238+mutdmour@users.noreply.github.com> Date: Thu, 30 Mar 2023 15:50:47 +0200 Subject: [PATCH] fix(editor): Fix mapping with special characters (#5837) * fix: Fix mapping with special characters * refactor: rename var * test: update more unit tests * test: update mapping test * test: update mapping test --- cypress/e2e/14-mapping.cy.ts | 8 ++--- .../ExpressionEditorModalOutput.vue | 4 ++- .../InlineExpressionEditorOutput.vue | 4 ++- .../__snapshots__/RunDataSchema.test.ts.snap | 20 ++++++------- .../src/utils/__tests__/mappingUtils.test.ts | 29 +++++++++++++++++-- .../src/utils/__tests__/typesUtils.test.ts | 4 +-- packages/editor-ui/src/utils/mappingUtils.ts | 7 +++-- 7 files changed, 53 insertions(+), 23 deletions(-) diff --git a/cypress/e2e/14-mapping.cy.ts b/cypress/e2e/14-mapping.cy.ts index 73e628216e..7e145887f2 100644 --- a/cypress/e2e/14-mapping.cy.ts +++ b/cypress/e2e/14-mapping.cy.ts @@ -56,7 +56,7 @@ describe('Data mapping', () => { ndv.actions.mapDataFromHeader(2, 'value'); ndv.getters .inlineExpressionEditorInput() - .should('have.text', '{{ $json.timestamp }} {{ $json["Readable date"] }}'); + .should('have.text', "{{ $json.timestamp }} {{ $json['Readable date'] }}"); }); it('maps expressions from table json, and resolves value based on hover', () => { @@ -193,7 +193,7 @@ describe('Data mapping', () => { ndv.actions.mapToParameter('value'); ndv.getters .inlineExpressionEditorInput() - .should('have.text', `{{ $node["${SCHEDULE_TRIGGER_NODE_NAME}"].json.input[0].count }}`); + .should('have.text', `{{ $node['${SCHEDULE_TRIGGER_NODE_NAME}'].json.input[0].count }}`); ndv.getters.parameterExpressionPreview('value').should('not.exist'); ndv.actions.switchInputMode('Table'); @@ -202,7 +202,7 @@ describe('Data mapping', () => { .inlineExpressionEditorInput() .should( 'have.text', - `{{ $node["${SCHEDULE_TRIGGER_NODE_NAME}"].json.input[0].count }} {{ $node["${SCHEDULE_TRIGGER_NODE_NAME}"].json.input }}`, + `{{ $node['${SCHEDULE_TRIGGER_NODE_NAME}'].json.input[0].count }} {{ $node['${SCHEDULE_TRIGGER_NODE_NAME}'].json.input }}`, ); ndv.getters.parameterExpressionPreview('value').should('not.exist'); @@ -259,7 +259,7 @@ describe('Data mapping', () => { ndv.getters .parameterInput('fieldName') .find('input') - .should('have.value', 'input[0]["hello.world"]["my count"]'); + .should('have.value', "input[0]['hello.world']['my count']"); }); it('maps expressions to updated fields correctly', () => { diff --git a/packages/editor-ui/src/components/ExpressionEditorModal/ExpressionEditorModalOutput.vue b/packages/editor-ui/src/components/ExpressionEditorModal/ExpressionEditorModalOutput.vue index c2c2dc1a95..642773e908 100644 --- a/packages/editor-ui/src/components/ExpressionEditorModal/ExpressionEditorModalOutput.vue +++ b/packages/editor-ui/src/components/ExpressionEditorModal/ExpressionEditorModalOutput.vue @@ -77,7 +77,9 @@ export default Vue.extend({ segment.kind === 'plaintext' ? segment.plaintext.length : // eslint-disable-next-line @typescript-eslint/no-explicit-any - (segment.resolved as any).toString().length; + segment.resolved + ? (segment.resolved as any).toString().length + : 0; segment.to = cursor; diff --git a/packages/editor-ui/src/components/InlineExpressionEditor/InlineExpressionEditorOutput.vue b/packages/editor-ui/src/components/InlineExpressionEditor/InlineExpressionEditorOutput.vue index b7a8086271..89aa083352 100644 --- a/packages/editor-ui/src/components/InlineExpressionEditor/InlineExpressionEditorOutput.vue +++ b/packages/editor-ui/src/components/InlineExpressionEditor/InlineExpressionEditorOutput.vue @@ -68,7 +68,9 @@ export default Vue.extend({ segment.kind === 'plaintext' ? segment.plaintext.length : // eslint-disable-next-line @typescript-eslint/no-explicit-any - (segment.resolved as any).toString().length; + segment.resolved + ? (segment.resolved as any).toString().length + : 0; segment.to = cursor; return segment; }) diff --git a/packages/editor-ui/src/components/__snapshots__/RunDataSchema.test.ts.snap b/packages/editor-ui/src/components/__snapshots__/RunDataSchema.test.ts.snap index 1356f49ca8..317edebadb 100644 --- a/packages/editor-ui/src/components/__snapshots__/RunDataSchema.test.ts.snap +++ b/packages/editor-ui/src/components/__snapshots__/RunDataSchema.test.ts.snap @@ -301,9 +301,9 @@ exports[`RunDataJsonSchema.vue > renders schema with spaces and dots 1`] = ` class="label" data-depth="1" data-name="hello world" - data-path="[\\"hello world\\"]" + data-path="['hello world']" data-target="mappable" - data-value="{{ $json[\\"hello world\\"] }}" + data-value="{{ $json['hello world'] }}" > renders schema with spaces and dots 1`] = ` class="label" data-depth="2" data-name="object[0]" - data-path="[\\"hello world\\"][0]" + data-path="['hello world'][0]" data-target="mappable" - data-value="{{ $json[\\"hello world\\"][0] }}" + data-value="{{ $json['hello world'][0] }}" > renders schema with spaces and dots 1`] = ` class="label" data-depth="3" data-name="test" - data-path="[\\"hello world\\"][0].test" + data-path="['hello world'][0].test" data-target="mappable" - data-value="{{ $json[\\"hello world\\"][0].test }}" + data-value="{{ $json['hello world'][0].test }}" > renders schema with spaces and dots 1`] = ` class="label" data-depth="4" data-name="more to think about" - data-path="[\\"hello world\\"][0].test[\\"more to think about\\"]" + data-path="['hello world'][0].test['more to think about']" data-target="mappable" - data-value="{{ $json[\\"hello world\\"][0].test[\\"more to think about\\"] }}" + data-value="{{ $json['hello world'][0].test['more to think about'] }}" > renders schema with spaces and dots 1`] = ` class="label" data-depth="3" data-name="test.how" - data-path="[\\"hello world\\"][0][\\"test.how\\"]" + data-path="['hello world'][0]['test.how']" data-target="mappable" - data-value="{{ $json[\\"hello world\\"][0][\\"test.how\\"] }}" + data-value="{{ $json['hello world'][0]['test.how'] }}" > { }; const result = getMappedExpression(input); expect(result).toBe( - '{{ $node.nodeName.json.sample["path with-space"]["path-with-hyphen"] }}', + "{{ $node.nodeName.json.sample['path with-space']['path-with-hyphen'] }}", + ); + }); + + it('should handle paths with special characters', () => { + const input = { + nodeName: 'nodeName', + distanceFromActive: 2, + path: [ + 'sample', + '"Execute"', + '`Execute`', + "'Execute'", + '[Execute]', + '{Execute}', + 'execute?', + 'test,', + 'test:', + 'path.', + ], + }; + const result = getMappedExpression(input); + expect(result).toBe( + "{{ $node.nodeName.json.sample['\"Execute\"']['`Execute`']['\\'Execute\\'']['[Execute]']['{Execute}']['execute?']['test,']['test:']['path.'] }}", ); }); @@ -239,7 +262,7 @@ describe('Mapping Utils', () => { path: ['propertyName', 'capitalizedName', 'hyphen-prop'], }; const result = getMappedExpression(input); - expect(result).toBe('{{ $json.propertyName.capitalizedName["hyphen-prop"] }}'); + expect(result).toBe("{{ $json.propertyName.capitalizedName['hyphen-prop'] }}"); }); it('should generate a mapped expression with a complex path', () => { @@ -250,7 +273,7 @@ describe('Mapping Utils', () => { }; const result = getMappedExpression(input); expect(result).toBe( - '{{ $json.propertyName.capitalizedName.stringVal["some-value"].capitalizedProp }}', + "{{ $json.propertyName.capitalizedName.stringVal['some-value'].capitalizedProp }}", ); }); }); diff --git a/packages/editor-ui/src/utils/__tests__/typesUtils.test.ts b/packages/editor-ui/src/utils/__tests__/typesUtils.test.ts index 0f43a8c0ac..405358ad84 100644 --- a/packages/editor-ui/src/utils/__tests__/typesUtils.test.ts +++ b/packages/editor-ui/src/utils/__tests__/typesUtils.test.ts @@ -275,13 +275,13 @@ describe('Types Utils', () => { type: 'array', key: 'with space', value: [], - path: '["with space"]', + path: "['with space']", }, { type: 'string', key: 'with.dot', value: 'test', - path: '["with.dot"]', + path: "['with.dot']", }, ], path: '', diff --git a/packages/editor-ui/src/utils/mappingUtils.ts b/packages/editor-ui/src/utils/mappingUtils.ts index 358ceed313..bf07289b25 100644 --- a/packages/editor-ui/src/utils/mappingUtils.ts +++ b/packages/editor-ui/src/utils/mappingUtils.ts @@ -6,8 +6,11 @@ export function generatePath(root: string, path: Array): string return `${accu}[${part}]`; } - if (part.includes('-') || part.includes(' ') || part.includes('.')) { - return `${accu}["${part}"]`; + const special = ['-', ' ', '.', "'", '"', '`', '[', ']', '{', '}', '(', ')', ':', ',', '?']; + const hasSpecial = !!special.find((s) => part.includes(s)); + if (hasSpecial) { + const escaped = part.replaceAll("'", "\\'"); + return `${accu}['${escaped}']`; } return `${accu}.${part}`;