From 6b1bb18f28c6e0de387c44e041e80d9db9432d1e Mon Sep 17 00:00:00 2001 From: Mutasem Date: Thu, 12 Jan 2023 16:22:28 +0100 Subject: [PATCH 01/15] feat: add data path flag --- .../nodes/CompareDatasets/CompareDatasets.node.ts | 2 ++ packages/nodes-base/nodes/ItemLists/ItemLists.node.ts | 7 +++++++ packages/nodes-base/nodes/Merge/v2/MergeV2.node.ts | 2 ++ 3 files changed, 11 insertions(+) diff --git a/packages/nodes-base/nodes/CompareDatasets/CompareDatasets.node.ts b/packages/nodes-base/nodes/CompareDatasets/CompareDatasets.node.ts index 3bb53f0bd510b..7313601586b6a 100644 --- a/packages/nodes-base/nodes/CompareDatasets/CompareDatasets.node.ts +++ b/packages/nodes-base/nodes/CompareDatasets/CompareDatasets.node.ts @@ -47,6 +47,7 @@ export class CompareDatasets implements INodeType { // eslint-disable-next-line n8n-nodes-base/node-param-placeholder-miscased-id placeholder: 'e.g. id', hint: ' Enter the field name as text', + requiresDataPath: true, }, { displayName: 'Input B Field', @@ -56,6 +57,7 @@ export class CompareDatasets implements INodeType { // eslint-disable-next-line n8n-nodes-base/node-param-placeholder-miscased-id placeholder: 'e.g. id', hint: ' Enter the field name as text', + requiresDataPath: true, }, ], }, diff --git a/packages/nodes-base/nodes/ItemLists/ItemLists.node.ts b/packages/nodes-base/nodes/ItemLists/ItemLists.node.ts index 26681c80c6a10..2bc5494512717 100644 --- a/packages/nodes-base/nodes/ItemLists/ItemLists.node.ts +++ b/packages/nodes-base/nodes/ItemLists/ItemLists.node.ts @@ -153,6 +153,7 @@ export class ItemLists implements INodeType { // eslint-disable-next-line n8n-nodes-base/node-param-placeholder-miscased-id placeholder: 'e.g. id', hint: ' Enter the field name as text', + requiresDataPath: true, }, ], }, @@ -212,6 +213,7 @@ export class ItemLists implements INodeType { // eslint-disable-next-line n8n-nodes-base/node-param-placeholder-miscased-id placeholder: 'e.g. id', hint: ' Enter the field name as text', + requiresDataPath: true, }, { displayName: 'Rename Field', @@ -302,6 +304,7 @@ export class ItemLists implements INodeType { // eslint-disable-next-line n8n-nodes-base/node-param-placeholder-miscased-id placeholder: 'e.g. id', hint: ' Enter the field name as text', + requiresDataPath: true, }, ], }, @@ -338,6 +341,7 @@ export class ItemLists implements INodeType { // eslint-disable-next-line n8n-nodes-base/node-param-placeholder-miscased-id placeholder: 'e.g. id', hint: ' Enter the field name as text', + requiresDataPath: true, }, ], }, @@ -409,6 +413,7 @@ export class ItemLists implements INodeType { // eslint-disable-next-line n8n-nodes-base/node-param-placeholder-miscased-id placeholder: 'e.g. id', hint: ' Enter the field name as text', + requiresDataPath: true, }, ], }, @@ -444,6 +449,7 @@ export class ItemLists implements INodeType { // eslint-disable-next-line n8n-nodes-base/node-param-placeholder-miscased-id placeholder: 'e.g. id', hint: ' Enter the field name as text', + requiresDataPath: true, }, ], }, @@ -500,6 +506,7 @@ export class ItemLists implements INodeType { // eslint-disable-next-line n8n-nodes-base/node-param-placeholder-miscased-id placeholder: 'e.g. id', hint: ' Enter the field name as text', + requiresDataPath: true, }, { displayName: 'Order', diff --git a/packages/nodes-base/nodes/Merge/v2/MergeV2.node.ts b/packages/nodes-base/nodes/Merge/v2/MergeV2.node.ts index ef41955aa418c..07390c1656fb1 100644 --- a/packages/nodes-base/nodes/Merge/v2/MergeV2.node.ts +++ b/packages/nodes-base/nodes/Merge/v2/MergeV2.node.ts @@ -120,6 +120,7 @@ const versionDescription: INodeTypeDescription = { // eslint-disable-next-line n8n-nodes-base/node-param-placeholder-miscased-id placeholder: 'e.g. id', hint: ' Enter the field name as text', + requiresDataPath: true, }, { displayName: 'Input 2 Field', @@ -129,6 +130,7 @@ const versionDescription: INodeTypeDescription = { // eslint-disable-next-line n8n-nodes-base/node-param-placeholder-miscased-id placeholder: 'e.g. id', hint: ' Enter the field name as text', + requiresDataPath: true, }, ], }, From 3c9e7407aeab523b3acbace405924c72a5e1bfac Mon Sep 17 00:00:00 2001 From: Mutasem Date: Thu, 12 Jan 2023 16:25:05 +0100 Subject: [PATCH 02/15] chore: update types --- packages/workflow/src/Interfaces.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/workflow/src/Interfaces.ts b/packages/workflow/src/Interfaces.ts index 94a1b6bad48eb..6a12abf2b2580 100644 --- a/packages/workflow/src/Interfaces.ts +++ b/packages/workflow/src/Interfaces.ts @@ -1039,6 +1039,7 @@ export interface INodeProperties { >; extractValue?: INodePropertyValueExtractor; modes?: INodePropertyMode[]; + requiresDataPath?: true; } export interface INodePropertyModeTypeOptions { From 01030927bfbe7ac256860359c375833662116971 Mon Sep 17 00:00:00 2001 From: Mutasem Date: Thu, 12 Jan 2023 16:44:21 +0100 Subject: [PATCH 03/15] feat: use path for data --- packages/editor-ui/src/components/ParameterInputFull.vue | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/editor-ui/src/components/ParameterInputFull.vue b/packages/editor-ui/src/components/ParameterInputFull.vue index 9da5da378144d..2ac543a5a8896 100644 --- a/packages/editor-ui/src/components/ParameterInputFull.vue +++ b/packages/editor-ui/src/components/ParameterInputFull.vue @@ -217,12 +217,16 @@ export default mixins(showMessage).extend({ } }, onDrop(data: string) { - this.forceShowExpression = true; + if (!this.parameter.requiresDataPath) { + this.forceShowExpression = true; + } setTimeout(() => { if (this.node) { const prevValue = this.isResourceLocator ? this.value.value : this.value; let updatedValue: string; - if (typeof prevValue === 'string' && prevValue.startsWith('=') && prevValue.length > 1) { + if (this.parameter.requiresDataPath) { + updatedValue = data.replace('{{ $json', '').replace(new RegExp('}}$'), '').trim(); + } else if (typeof prevValue === 'string' && prevValue.startsWith('=') && prevValue.length > 1) { updatedValue = `${prevValue} ${data}`; } else { updatedValue = `=${data}`; From faeffe72e2d0861a52b2f2bdce26f8d0bb5a5e44 Mon Sep 17 00:00:00 2001 From: Mutasem Date: Tue, 17 Jan 2023 14:05:32 +0100 Subject: [PATCH 04/15] feat: add support for multiple values --- .../src/components/ParameterInputFull.vue | 13 +++++++++++-- .../nodes/CompareDatasets/CompareDatasets.node.ts | 5 +++-- .../nodes-base/nodes/ItemLists/ItemLists.node.ts | 14 +++++++------- packages/nodes-base/nodes/Merge/v2/MergeV2.node.ts | 4 ++-- packages/workflow/src/Interfaces.ts | 2 +- 5 files changed, 24 insertions(+), 14 deletions(-) diff --git a/packages/editor-ui/src/components/ParameterInputFull.vue b/packages/editor-ui/src/components/ParameterInputFull.vue index 5594328925269..4df0a1d180742 100644 --- a/packages/editor-ui/src/components/ParameterInputFull.vue +++ b/packages/editor-ui/src/components/ParameterInputFull.vue @@ -231,8 +231,17 @@ export default mixins(showMessage).extend({ const prevValue = this.isResourceLocator ? this.value.value : this.value; let updatedValue: string; if (this.parameter.requiresDataPath) { - updatedValue = data.replace('{{ $json', '').replace(new RegExp('}}$'), '').trim(); - } else if (typeof prevValue === 'string' && prevValue.startsWith('=') && prevValue.length > 1) { + const newValue = data.replace('{{ $json', '').replace(new RegExp('}}$'), '').trim(); + if (prevValue && this.parameter.requiresDataPath === 'multiple') { + updatedValue = `${prevValue}, ${newValue}`; + } else { + updatedValue = newValue; + } + } else if ( + typeof prevValue === 'string' && + prevValue.startsWith('=') && + prevValue.length > 1 + ) { updatedValue = `${prevValue} ${data}`; } else { updatedValue = `=${data}`; diff --git a/packages/nodes-base/nodes/CompareDatasets/CompareDatasets.node.ts b/packages/nodes-base/nodes/CompareDatasets/CompareDatasets.node.ts index 7313601586b6a..c5e571da795df 100644 --- a/packages/nodes-base/nodes/CompareDatasets/CompareDatasets.node.ts +++ b/packages/nodes-base/nodes/CompareDatasets/CompareDatasets.node.ts @@ -47,7 +47,7 @@ export class CompareDatasets implements INodeType { // eslint-disable-next-line n8n-nodes-base/node-param-placeholder-miscased-id placeholder: 'e.g. id', hint: ' Enter the field name as text', - requiresDataPath: true, + requiresDataPath: 'single', }, { displayName: 'Input B Field', @@ -57,7 +57,7 @@ export class CompareDatasets implements INodeType { // eslint-disable-next-line n8n-nodes-base/node-param-placeholder-miscased-id placeholder: 'e.g. id', hint: ' Enter the field name as text', - requiresDataPath: true, + requiresDataPath: 'single', }, ], }, @@ -140,6 +140,7 @@ export class CompareDatasets implements INodeType { hint: 'Enter the field names as text, separated by commas', description: "Fields that shouldn't be included when checking whether two items are the same", + requiresDataPath: "multiple", }, { displayName: 'Fuzzy Compare', diff --git a/packages/nodes-base/nodes/ItemLists/ItemLists.node.ts b/packages/nodes-base/nodes/ItemLists/ItemLists.node.ts index be6f6f18640b5..62b83d207cc3c 100644 --- a/packages/nodes-base/nodes/ItemLists/ItemLists.node.ts +++ b/packages/nodes-base/nodes/ItemLists/ItemLists.node.ts @@ -189,7 +189,7 @@ export class ItemLists implements INodeType { // eslint-disable-next-line n8n-nodes-base/node-param-placeholder-miscased-id placeholder: 'e.g. id', hint: ' Enter the field name as text', - requiresDataPath: true, + requiresDataPath: 'single', }, ], }, @@ -249,7 +249,7 @@ export class ItemLists implements INodeType { // eslint-disable-next-line n8n-nodes-base/node-param-placeholder-miscased-id placeholder: 'e.g. id', hint: ' Enter the field name as text', - requiresDataPath: true, + requiresDataPath: 'single', }, { displayName: 'Rename Field', @@ -340,7 +340,7 @@ export class ItemLists implements INodeType { // eslint-disable-next-line n8n-nodes-base/node-param-placeholder-miscased-id placeholder: 'e.g. id', hint: ' Enter the field name as text', - requiresDataPath: true, + requiresDataPath: 'single', }, ], }, @@ -377,7 +377,7 @@ export class ItemLists implements INodeType { // eslint-disable-next-line n8n-nodes-base/node-param-placeholder-miscased-id placeholder: 'e.g. id', hint: ' Enter the field name as text', - requiresDataPath: true, + requiresDataPath: 'single', }, ], }, @@ -449,7 +449,7 @@ export class ItemLists implements INodeType { // eslint-disable-next-line n8n-nodes-base/node-param-placeholder-miscased-id placeholder: 'e.g. id', hint: ' Enter the field name as text', - requiresDataPath: true, + requiresDataPath: 'single', }, ], }, @@ -485,7 +485,7 @@ export class ItemLists implements INodeType { // eslint-disable-next-line n8n-nodes-base/node-param-placeholder-miscased-id placeholder: 'e.g. id', hint: ' Enter the field name as text', - requiresDataPath: true, + requiresDataPath: 'single', }, ], }, @@ -542,7 +542,7 @@ export class ItemLists implements INodeType { // eslint-disable-next-line n8n-nodes-base/node-param-placeholder-miscased-id placeholder: 'e.g. id', hint: ' Enter the field name as text', - requiresDataPath: true, + requiresDataPath: 'single', }, { displayName: 'Order', diff --git a/packages/nodes-base/nodes/Merge/v2/MergeV2.node.ts b/packages/nodes-base/nodes/Merge/v2/MergeV2.node.ts index 07390c1656fb1..787aa9d8076d6 100644 --- a/packages/nodes-base/nodes/Merge/v2/MergeV2.node.ts +++ b/packages/nodes-base/nodes/Merge/v2/MergeV2.node.ts @@ -120,7 +120,7 @@ const versionDescription: INodeTypeDescription = { // eslint-disable-next-line n8n-nodes-base/node-param-placeholder-miscased-id placeholder: 'e.g. id', hint: ' Enter the field name as text', - requiresDataPath: true, + requiresDataPath: 'single', }, { displayName: 'Input 2 Field', @@ -130,7 +130,7 @@ const versionDescription: INodeTypeDescription = { // eslint-disable-next-line n8n-nodes-base/node-param-placeholder-miscased-id placeholder: 'e.g. id', hint: ' Enter the field name as text', - requiresDataPath: true, + requiresDataPath: 'single', }, ], }, diff --git a/packages/workflow/src/Interfaces.ts b/packages/workflow/src/Interfaces.ts index 6a12abf2b2580..4d347a04c2607 100644 --- a/packages/workflow/src/Interfaces.ts +++ b/packages/workflow/src/Interfaces.ts @@ -1039,7 +1039,7 @@ export interface INodeProperties { >; extractValue?: INodePropertyValueExtractor; modes?: INodePropertyMode[]; - requiresDataPath?: true; + requiresDataPath?: 'single' | 'multiple'; } export interface INodePropertyModeTypeOptions { From e691989142d413b7d85de7fc61ba5433d7f57aeb Mon Sep 17 00:00:00 2001 From: Mutasem Date: Tue, 17 Jan 2023 14:28:10 +0100 Subject: [PATCH 05/15] fix: handle if not prev node --- packages/editor-ui/src/components/ParameterInputFull.vue | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/editor-ui/src/components/ParameterInputFull.vue b/packages/editor-ui/src/components/ParameterInputFull.vue index 4df0a1d180742..2e2e845c1ccd7 100644 --- a/packages/editor-ui/src/components/ParameterInputFull.vue +++ b/packages/editor-ui/src/components/ParameterInputFull.vue @@ -223,14 +223,15 @@ export default mixins(showMessage).extend({ } }, onDrop(data: string) { - if (!this.parameter.requiresDataPath) { + const useDataPath = !!this.parameter.requiresDataPath && data.startsWith('{{ $json'); + if (!useDataPath) { this.forceShowExpression = true; } setTimeout(() => { if (this.node) { const prevValue = this.isResourceLocator ? this.value.value : this.value; let updatedValue: string; - if (this.parameter.requiresDataPath) { + if (useDataPath) { const newValue = data.replace('{{ $json', '').replace(new RegExp('}}$'), '').trim(); if (prevValue && this.parameter.requiresDataPath === 'multiple') { updatedValue = `${prevValue}, ${newValue}`; From 7cd4722feb0647608810a4ea9555e9c90039edd9 Mon Sep 17 00:00:00 2001 From: Mutasem Date: Tue, 17 Jan 2023 14:40:38 +0100 Subject: [PATCH 06/15] fix: update node --- packages/nodes-base/nodes/ItemLists/ItemLists.node.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/nodes-base/nodes/ItemLists/ItemLists.node.ts b/packages/nodes-base/nodes/ItemLists/ItemLists.node.ts index 62b83d207cc3c..e64e837366397 100644 --- a/packages/nodes-base/nodes/ItemLists/ItemLists.node.ts +++ b/packages/nodes-base/nodes/ItemLists/ItemLists.node.ts @@ -131,6 +131,7 @@ export class ItemLists implements INodeType { }, }, description: 'The name of the input field to break out into separate items', + requiresDataPath: 'single', }, { displayName: 'Include', From 3b17dd154718d3d639c069749db9b385d07a0406 Mon Sep 17 00:00:00 2001 From: Mutasem Date: Tue, 17 Jan 2023 14:52:00 +0100 Subject: [PATCH 07/15] fix: handle multi part path --- packages/editor-ui/src/components/ParameterInputFull.vue | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/editor-ui/src/components/ParameterInputFull.vue b/packages/editor-ui/src/components/ParameterInputFull.vue index 2e2e845c1ccd7..060245cb7a436 100644 --- a/packages/editor-ui/src/components/ParameterInputFull.vue +++ b/packages/editor-ui/src/components/ParameterInputFull.vue @@ -232,7 +232,11 @@ export default mixins(showMessage).extend({ const prevValue = this.isResourceLocator ? this.value.value : this.value; let updatedValue: string; if (useDataPath) { - const newValue = data.replace('{{ $json', '').replace(new RegExp('}}$'), '').trim(); + let newValue = data.replace('{{ $json', '').replace(new RegExp('}}$'), '').trim(); + const isMultiPartPath = [...newValue.matchAll(new RegExp('\\"\]', 'g'))].length > 1; + if (!isMultiPartPath) { + newValue = newValue.replace('["', '').replace('"]', ''); + } if (prevValue && this.parameter.requiresDataPath === 'multiple') { updatedValue = `${prevValue}, ${newValue}`; } else { From 69ada01214680122d51b9623429f898823e9685f Mon Sep 17 00:00:00 2001 From: Mutasem Date: Wed, 18 Jan 2023 15:56:13 +0100 Subject: [PATCH 08/15] feat: add support for multiple vals for field --- .../nodes-base/nodes/CompareDatasets/CompareDatasets.node.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/nodes-base/nodes/CompareDatasets/CompareDatasets.node.ts b/packages/nodes-base/nodes/CompareDatasets/CompareDatasets.node.ts index c5e571da795df..b68e2f4da7ebc 100644 --- a/packages/nodes-base/nodes/CompareDatasets/CompareDatasets.node.ts +++ b/packages/nodes-base/nodes/CompareDatasets/CompareDatasets.node.ts @@ -123,6 +123,7 @@ export class CompareDatasets implements INodeType { resolve: ['mix'], }, }, + requiresDataPath: 'multiple', }, { displayName: 'Options', @@ -140,7 +141,7 @@ export class CompareDatasets implements INodeType { hint: 'Enter the field names as text, separated by commas', description: "Fields that shouldn't be included when checking whether two items are the same", - requiresDataPath: "multiple", + requiresDataPath: 'multiple', }, { displayName: 'Fuzzy Compare', From 932a0a055fc7021d14624b08abeca3ca2e916fe6 Mon Sep 17 00:00:00 2001 From: Mutasem Date: Wed, 18 Jan 2023 16:01:37 +0100 Subject: [PATCH 09/15] feat: add support for table transforms --- packages/nodes-base/nodes/ItemLists/ItemLists.node.ts | 1 + packages/nodes-base/nodes/ItemLists/summarize.operation.ts | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/packages/nodes-base/nodes/ItemLists/ItemLists.node.ts b/packages/nodes-base/nodes/ItemLists/ItemLists.node.ts index 8405fe2a1674d..76b8f3cb73d83 100644 --- a/packages/nodes-base/nodes/ItemLists/ItemLists.node.ts +++ b/packages/nodes-base/nodes/ItemLists/ItemLists.node.ts @@ -278,6 +278,7 @@ export class ItemLists implements INodeType { default: '', description: 'The name of the field to put the aggregated data in. Leave blank to use the input field name.', + requiresDataPath: 'single', }, ], }, diff --git a/packages/nodes-base/nodes/ItemLists/summarize.operation.ts b/packages/nodes-base/nodes/ItemLists/summarize.operation.ts index 69bbe86976946..d64df3f170ae6 100644 --- a/packages/nodes-base/nodes/ItemLists/summarize.operation.ts +++ b/packages/nodes-base/nodes/ItemLists/summarize.operation.ts @@ -123,6 +123,7 @@ export const description: INodeProperties[] = [ aggregation: [...NUMERICAL_AGGREGATIONS, 'countUnique', 'count'], }, }, + requiresDataPath: 'single', }, { displayName: 'Field', @@ -138,6 +139,7 @@ export const description: INodeProperties[] = [ aggregation: NUMERICAL_AGGREGATIONS, }, }, + requiresDataPath: 'single', }, { displayName: 'Field', @@ -153,6 +155,7 @@ export const description: INodeProperties[] = [ aggregation: ['countUnique', 'count'], }, }, + requiresDataPath: 'single', }, // ---------------------------------------------------------------------------------------------------------- { @@ -245,6 +248,7 @@ export const description: INodeProperties[] = [ '/options.outputFormat': ['singleItem'], }, }, + requiresDataPath: 'multiple', }, { displayName: 'Fields to Group By', @@ -261,6 +265,7 @@ export const description: INodeProperties[] = [ '/options.outputFormat': ['singleItem'], }, }, + requiresDataPath: 'multiple', }, // ---------------------------------------------------------------------------------------------------------- { From 14f6522533b0f672847eab710c077ab9b3a11ffd Mon Sep 17 00:00:00 2001 From: Mutasem Date: Tue, 24 Jan 2023 08:43:58 +0300 Subject: [PATCH 10/15] feat: use dot notation --- .../editor-ui/src/components/RunDataJson.vue | 13 +++++--- .../src/components/RunDataSchemaItem.vue | 8 ++++- .../editor-ui/src/components/RunDataTable.vue | 30 +++++++----------- packages/editor-ui/src/utils/mappingUtils.ts | 31 +++++++++++++++++++ packages/editor-ui/src/utils/typesUtils.ts | 3 +- 5 files changed, 59 insertions(+), 26 deletions(-) create mode 100644 packages/editor-ui/src/utils/mappingUtils.ts diff --git a/packages/editor-ui/src/components/RunDataJson.vue b/packages/editor-ui/src/components/RunDataJson.vue index e7981a72ca186..15a6c0289ca0a 100644 --- a/packages/editor-ui/src/components/RunDataJson.vue +++ b/packages/editor-ui/src/components/RunDataJson.vue @@ -79,6 +79,7 @@ import { externalHooks } from '@/mixins/externalHooks'; import { mapStores } from 'pinia'; import { useNDVStore } from '@/stores/ndv'; import MappingPill from './MappingPill.vue'; +import { getMappedExpression } from '@/utils/mappingUtils'; const runDataJsonActions = () => import('@/components/RunDataJsonActions.vue'); @@ -169,11 +170,13 @@ export default mixins(externalHooks).extend({ return shorten(el.dataset.name || '', 16, 2); }, getJsonParameterPath(path: string): string { - const convertedPath = convertPath(path); - return `{{ ${convertedPath.replace( - /^(\["?\d"?])/, - this.distanceFromActive === 1 ? '$json' : `$node["${this.node!.name}"].json`, - )} }}`; + const subPath = path.replace(/^(\["?\d"?])/, ''); // remove item position + + return getMappedExpression({ + nodeName: this.node.name, + distanceFromActive: this.distanceFromActive, + path: subPath, + }); }, onDragStart(el: HTMLElement) { if (el && el.dataset.path) { diff --git a/packages/editor-ui/src/components/RunDataSchemaItem.vue b/packages/editor-ui/src/components/RunDataSchemaItem.vue index fde51fed48f05..e6457fe93ab50 100644 --- a/packages/editor-ui/src/components/RunDataSchemaItem.vue +++ b/packages/editor-ui/src/components/RunDataSchemaItem.vue @@ -2,6 +2,7 @@ import { computed } from 'vue'; import { INodeUi, Schema } from '@/Interface'; import { checkExhaustive, shorten } from '@/utils'; +import { getMappedExpression } from '@/utils/mappingUtils'; type Props = { schema: Schema; @@ -35,7 +36,12 @@ const text = computed(() => ); const getJsonParameterPath = (path: string): string => - `{{ ${props.distanceFromActive === 1 ? '$json' : `$node["${props.node!.name}"].json`}${path} }}`; + getMappedExpression({ + nodeName: props.node!.name, + distanceFromActive: props.distanceFromActive, + path, + }); + const transitionDelay = (i: number) => `${i * 0.033}s`; const getIconBySchemaType = (type: Schema['type']): string => { diff --git a/packages/editor-ui/src/components/RunDataTable.vue b/packages/editor-ui/src/components/RunDataTable.vue index fc103f6e27bff..3f2c1e5b7c5b0 100644 --- a/packages/editor-ui/src/components/RunDataTable.vue +++ b/packages/editor-ui/src/components/RunDataTable.vue @@ -170,6 +170,7 @@ import { mapStores } from 'pinia'; import { useWorkflowsStore } from '@/stores/workflows'; import { useNDVStore } from '@/stores/ndv'; import MappingPill from './MappingPill.vue'; +import { getMappedExpression } from '@/utils/mappingUtils'; const MAX_COLUMNS_LIMIT = 40; @@ -315,11 +316,11 @@ export default mixins(externalHooks).extend({ return ''; } - if (this.distanceFromActive === 1) { - return `{{ $json["${column}"] }}`; - } - - return `{{ $node["${this.node.name}"].json["${column}"] }}`; + return getMappedExpression({ + nodeName: this.node.name, + distanceFromActive: this.distanceFromActive, + path: [column], + }); }, getPathNameFromTarget(el: HTMLElement) { if (!el) { @@ -343,21 +344,12 @@ export default mixins(externalHooks).extend({ if (!this.node) { return ''; } - - const expr = path.reduce((accu: string, key: string | number) => { - if (typeof key === 'number') { - return `${accu}[${key}]`; - } - - return `${accu}["${key}"]`; - }, ''); const column = this.tableData.columns[colIndex]; - - if (this.distanceFromActive === 1) { - return `{{ $json["${column}"]${expr} }}`; - } - - return `{{ $node["${this.node.name}"].json["${column}"]${expr} }}`; + return getMappedExpression({ + nodeName: this.node.name, + distanceFromActive: this.distanceFromActive, + path: [column, ...path], + }); }, isEmpty(value: unknown): boolean { return ( diff --git a/packages/editor-ui/src/utils/mappingUtils.ts b/packages/editor-ui/src/utils/mappingUtils.ts new file mode 100644 index 0000000000000..6d94505cd212e --- /dev/null +++ b/packages/editor-ui/src/utils/mappingUtils.ts @@ -0,0 +1,31 @@ +export function generatePath(root: string, path: Array): string { + return path.reduce((accu: string, part: string | number) => { + if (typeof part === 'number') { + return `${accu}[${part}]`; + } + + if (part.includes(' ')) { + return `${accu}["${part}"]`; + } + + return `${accu}.${part}`; + }, root); +} + +export function getMappedExpression({ + nodeName, + distanceFromActive, + path, +}: { + nodeName: string; + distanceFromActive: number; + path: Array | string; +}) { + const root = distanceFromActive === 1 ? '$json' : generatePath('$node', [nodeName, 'json']); + + if (typeof path === 'string') { + return `{{ ${root}${path} }}`; + } + + return `{{ ${generatePath(root, path)} }}`; +} diff --git a/packages/editor-ui/src/utils/typesUtils.ts b/packages/editor-ui/src/utils/typesUtils.ts index b1692cb51283e..52f3010b1c3a7 100644 --- a/packages/editor-ui/src/utils/typesUtils.ts +++ b/packages/editor-ui/src/utils/typesUtils.ts @@ -2,6 +2,7 @@ import dateformat from 'dateformat'; import { IDataObject, jsonParse } from 'n8n-workflow'; import { Schema, Optional, Primitives } from '@/Interface'; import { isObj } from '@/utils/typeGuards'; +import { generatePath } from '@/utils/mappingUtils'; /* Constants and utility functions than can be used to manipulate different data types and objects @@ -231,7 +232,7 @@ export const getSchema = (input: Optional, path = ''): Sche type: 'object', value: Object.entries(input).map(([k, v]) => ({ key: k, - ...getSchema(v, path + `["${k}"]`), + ...getSchema(v, generatePath(path, [k])), })), path, }; From 9a9fa7fec0c948b3319990435ae7609d917f2e6a Mon Sep 17 00:00:00 2001 From: Mutasem Date: Tue, 24 Jan 2023 16:00:21 +0300 Subject: [PATCH 11/15] feat: fix bug where brackets removed --- .../editor-ui/src/components/ParameterInputFull.vue | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/editor-ui/src/components/ParameterInputFull.vue b/packages/editor-ui/src/components/ParameterInputFull.vue index 51b90984cbdb4..f286a7e82f7ed 100644 --- a/packages/editor-ui/src/components/ParameterInputFull.vue +++ b/packages/editor-ui/src/components/ParameterInputFull.vue @@ -238,11 +238,12 @@ export default mixins(showMessage).extend({ const prevValue = this.isResourceLocator ? this.value.value : this.value; let updatedValue: string; if (useDataPath) { - let newValue = data.replace('{{ $json', '').replace(new RegExp('}}$'), '').trim(); - const isMultiPartPath = [...newValue.matchAll(new RegExp('\\"\]', 'g'))].length > 1; - if (!isMultiPartPath) { - newValue = newValue.replace('["', '').replace('"]', ''); - } + const newValue = data + .replace('{{ $json', '') + .replace(new RegExp('^\\.'), '') + .replace(new RegExp('}}$'), '') + .trim(); + if (prevValue && this.parameter.requiresDataPath === 'multiple') { updatedValue = `${prevValue}, ${newValue}`; } else { From 391b8f889da7b89df146d62695783abe42af9222 Mon Sep 17 00:00:00 2001 From: Mutasem Date: Tue, 24 Jan 2023 16:10:08 +0300 Subject: [PATCH 12/15] fix: handle dots, fix unit tests --- .../src/components/RunDataSchema.test.ts | 19 ++ .../__snapshots__/RunDataSchema.test.ts.snap | 270 +++++++++++++++++- packages/editor-ui/src/utils/mappingUtils.ts | 2 +- 3 files changed, 280 insertions(+), 11 deletions(-) diff --git a/packages/editor-ui/src/components/RunDataSchema.test.ts b/packages/editor-ui/src/components/RunDataSchema.test.ts index f9c5fe295db44..ff71b2aa00346 100644 --- a/packages/editor-ui/src/components/RunDataSchema.test.ts +++ b/packages/editor-ui/src/components/RunDataSchema.test.ts @@ -60,4 +60,23 @@ describe('RunDataJsonSchema.vue', () => { }); expect(container).toMatchSnapshot(); }); + + it('renders schema with spaces and dots', () => { + renderOptions.props.data = [ + { + 'hello world': [ + { + test: { + 'more to think about': 1, + }, + 'test.how': 'ignore', + }, + ], + }, + ]; + const { container } = render(RunDataJsonSchema, renderOptions, (vue) => { + vue.use(PiniaVuePlugin); + }); + expect(container).toMatchSnapshot(); + }); }); 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 848fae834fc14..9c17a7772641c 100644 --- a/packages/editor-ui/src/components/__snapshots__/RunDataSchema.test.ts.snap +++ b/packages/editor-ui/src/components/__snapshots__/RunDataSchema.test.ts.snap @@ -33,9 +33,9 @@ exports[`RunDataJsonSchema.vue > renders schema for data 1`] = ` class="_label_14xdy_89" data-depth="1" data-name="name" - data-path="[\\"name\\"]" + data-path=".name" data-target="mappable" - data-value="{{ $json[\\"name\\"] }}" + data-value="{{ $json.name }}" > renders schema for data 1`] = ` class="_label_14xdy_89" data-depth="1" data-name="age" - data-path="[\\"age\\"]" + data-path=".age" data-target="mappable" - data-value="{{ $json[\\"age\\"] }}" + data-value="{{ $json.age }}" > renders schema for data 1`] = ` class="_label_14xdy_89" data-depth="1" data-name="hobbies" - data-path="[\\"hobbies\\"]" + data-path=".hobbies" data-target="mappable" - data-value="{{ $json[\\"hobbies\\"] }}" + data-value="{{ $json.hobbies }}" > renders schema for data 1`] = ` class="_label_14xdy_89" data-depth="2" data-name="string[0]" - data-path="[\\"hobbies\\"][0]" + data-path=".hobbies[0]" data-target="mappable" - data-value="{{ $json[\\"hobbies\\"][0] }}" + data-value="{{ $json.hobbies[0] }}" > renders schema for data 1`] = ` class="_label_14xdy_89" data-depth="2" data-name="string[1]" - data-path="[\\"hobbies\\"][1]" + data-path=".hobbies[1]" data-target="mappable" - data-value="{{ $json[\\"hobbies\\"][1] }}" + data-value="{{ $json.hobbies[1] }}" > renders schema for empty data 1`] = ` `; + +exports[`RunDataJsonSchema.vue > renders schema with spaces 1`] = ` +
+
+
+
+
+ + + + +
+
+
+ + + + + hello world + + +
+ + + +
+
+
+ + + + hello world + + + [0] + + +
+ + + +
+
+
+ + + + + test + + +
+ + + +
+
+
+ + + + + more to think about + + +
+ + 1 + + + + +
+
+
+
+
+ + + + + test.how + + +
+ + ignore + + + + +
+
+
+
+
+
+
+
+ +
+
+`; diff --git a/packages/editor-ui/src/utils/mappingUtils.ts b/packages/editor-ui/src/utils/mappingUtils.ts index 6d94505cd212e..32889597f8f16 100644 --- a/packages/editor-ui/src/utils/mappingUtils.ts +++ b/packages/editor-ui/src/utils/mappingUtils.ts @@ -4,7 +4,7 @@ export function generatePath(root: string, path: Array): string return `${accu}[${part}]`; } - if (part.includes(' ')) { + if (part.includes(' ') || part.includes('.')) { return `${accu}["${part}"]`; } From a5c0725d002d670de5b2e588032762b5ebc1cd75 Mon Sep 17 00:00:00 2001 From: Mutasem Date: Tue, 24 Jan 2023 16:15:50 +0300 Subject: [PATCH 13/15] test: update snapshot --- .../__snapshots__/RunDataSchema.test.ts.snap | 250 ++++++++++++++++++ 1 file changed, 250 insertions(+) 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 9c17a7772641c..6b1b2f0c6b1d2 100644 --- a/packages/editor-ui/src/components/__snapshots__/RunDataSchema.test.ts.snap +++ b/packages/editor-ui/src/components/__snapshots__/RunDataSchema.test.ts.snap @@ -509,3 +509,253 @@ exports[`RunDataJsonSchema.vue > renders schema with spaces 1`] = `
`; + +exports[`RunDataJsonSchema.vue > renders schema with spaces and dots 1`] = ` +
+
+
+
+
+ + + + +
+
+
+ + + + + hello world + + +
+ + + +
+
+
+ + + + hello world + + + [0] + + +
+ + + +
+
+
+ + + + + test + + +
+ + + +
+
+
+ + + + + more to think about + + +
+ + 1 + + + + +
+
+
+
+
+ + + + + test.how + + +
+ + ignore + + + + +
+
+
+
+
+
+
+
+ +
+
+`; From a1bf8cabe24595402ac6a9d5cb27ff0cd8ca0a4d Mon Sep 17 00:00:00 2001 From: Mutasem Date: Tue, 24 Jan 2023 16:32:10 +0300 Subject: [PATCH 14/15] test: fix tests --- .../src/utils/__tests__/typesUtils.test.ts | 56 +++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/packages/editor-ui/src/utils/__tests__/typesUtils.test.ts b/packages/editor-ui/src/utils/__tests__/typesUtils.test.ts index 3807d42f6b86d..dc52e7e0bd242 100644 --- a/packages/editor-ui/src/utils/__tests__/typesUtils.test.ts +++ b/packages/editor-ui/src/utils/__tests__/typesUtils.test.ts @@ -257,10 +257,10 @@ describe('Utils', () => { type: 'array', key: 'people', value: [ - { type: 'string', value: 'Joe', key: '0', path: '["people"][0]' }, - { type: 'string', value: 'John', key: '1', path: '["people"][1]' }, + { type: 'string', value: 'Joe', key: '0', path: '.people[0]' }, + { type: 'string', value: 'John', key: '1', path: '.people[1]' }, ], - path: '["people"]', + path: '.people', }, ], path: '', @@ -278,8 +278,8 @@ describe('Utils', () => { type: 'object', key: '0', value: [ - { type: 'string', key: 'name', value: 'John', path: '[0]["name"]' }, - { type: 'number', key: 'age', value: '22', path: '[0]["age"]' }, + { type: 'string', key: 'name', value: 'John', path: '[0].name' }, + { type: 'number', key: 'age', value: '22', path: '[0].age' }, ], path: '[0]', }, @@ -287,8 +287,8 @@ describe('Utils', () => { type: 'object', key: '1', value: [ - { type: 'string', key: 'name', value: 'Joe', path: '[1]["name"]' }, - { type: 'number', key: 'age', value: '33', path: '[1]["age"]' }, + { type: 'string', key: 'name', value: 'Joe', path: '[1].name' }, + { type: 'number', key: 'age', value: '33', path: '[1].age' }, ], path: '[1]', }, @@ -308,16 +308,16 @@ describe('Utils', () => { type: 'object', key: '0', value: [ - { type: 'string', key: 'name', value: 'John', path: '[0]["name"]' }, - { type: 'number', key: 'age', value: '22', path: '[0]["age"]' }, + { type: 'string', key: 'name', value: 'John', path: '[0].name' }, + { type: 'number', key: 'age', value: '22', path: '[0].age' }, { type: 'array', key: 'hobbies', value: [ - { type: 'string', key: '0', value: 'surfing', path: '[0]["hobbies"][0]' }, - { type: 'string', key: '1', value: 'traveling', path: '[0]["hobbies"][1]' }, + { type: 'string', key: '0', value: 'surfing', path: '[0].hobbies[0]' }, + { type: 'string', key: '1', value: 'traveling', path: '[0].hobbies[1]' }, ], - path: '[0]["hobbies"]', + path: '[0].hobbies', }, ], path: '[0]', @@ -326,16 +326,16 @@ describe('Utils', () => { type: 'object', key: '1', value: [ - { type: 'string', key: 'name', value: 'Joe', path: '[1]["name"]' }, - { type: 'number', key: 'age', value: '33', path: '[1]["age"]' }, + { type: 'string', key: 'name', value: 'Joe', path: '[1].name' }, + { type: 'number', key: 'age', value: '33', path: '[1].age' }, { type: 'array', key: 'hobbies', value: [ - { type: 'string', key: '0', value: 'skateboarding', path: '[1]["hobbies"][0]' }, - { type: 'string', key: '1', value: 'gaming', path: '[1]["hobbies"][1]' }, + { type: 'string', key: '0', value: 'skateboarding', path: '[1].hobbies[0]' }, + { type: 'string', key: '1', value: 'gaming', path: '[1].hobbies[1]' }, ], - path: '[1]["hobbies"]', + path: '[1].hobbies', }, ], path: '[1]', @@ -381,8 +381,8 @@ describe('Utils', () => { type: 'object', key: '0', value: [ - { type: 'string', key: 'name', value: 'John', path: '[0][0]["name"]' }, - { type: 'number', key: 'age', value: '22', path: '[0][0]["age"]' }, + { type: 'string', key: 'name', value: 'John', path: '[0][0].name' }, + { type: 'number', key: 'age', value: '22', path: '[0][0].age' }, ], path: '[0][0]', }, @@ -390,8 +390,8 @@ describe('Utils', () => { type: 'object', key: '1', value: [ - { type: 'string', key: 'name', value: 'Joe', path: '[0][1]["name"]' }, - { type: 'number', key: 'age', value: '33', path: '[0][1]["age"]' }, + { type: 'string', key: 'name', value: 'Joe', path: '[0][1].name' }, + { type: 'number', key: 'age', value: '33', path: '[0][1].age' }, ], path: '[0][1]', }, @@ -430,16 +430,16 @@ describe('Utils', () => { type: 'string', key: '0', value: '2022-11-22T00:00:00.000Z', - path: '[0]["dates"][0][0]', + path: '[0].dates[0][0]', }, { type: 'string', key: '1', value: '2022-11-23T00:00:00.000Z', - path: '[0]["dates"][0][1]', + path: '[0].dates[0][1]', }, ], - path: '[0]["dates"][0]', + path: '[0].dates[0]', }, { type: 'array', @@ -449,19 +449,19 @@ describe('Utils', () => { type: 'string', key: '0', value: '2022-12-22T00:00:00.000Z', - path: '[0]["dates"][1][0]', + path: '[0].dates[1][0]', }, { type: 'string', key: '1', value: '2022-12-23T00:00:00.000Z', - path: '[0]["dates"][1][1]', + path: '[0].dates[1][1]', }, ], - path: '[0]["dates"][1]', + path: '[0].dates[1]', }, ], - path: '[0]["dates"]', + path: '[0].dates', }, ], path: '[0]', From 5361ce523b33596da98784248148c7a508a9d90d Mon Sep 17 00:00:00 2001 From: Mutasem Date: Tue, 24 Jan 2023 16:48:28 +0300 Subject: [PATCH 15/15] test: add test for edge case --- .../src/utils/__tests__/typesUtils.test.ts | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/packages/editor-ui/src/utils/__tests__/typesUtils.test.ts b/packages/editor-ui/src/utils/__tests__/typesUtils.test.ts index dc52e7e0bd242..569b98d4085d5 100644 --- a/packages/editor-ui/src/utils/__tests__/typesUtils.test.ts +++ b/packages/editor-ui/src/utils/__tests__/typesUtils.test.ts @@ -266,6 +266,27 @@ describe('Utils', () => { path: '', }, ], + [ + { 'with space': [], 'with.dot': 'test' }, + { + type: 'object', + value: [ + { + type: 'array', + key: 'with space', + value: [], + path: '["with space"]', + }, + { + type: 'string', + key: 'with.dot', + value: 'test', + path: '["with.dot"]', + }, + ], + path: '', + }, + ], [ [ { name: 'John', age: 22 },