From 969439ce5675c9e64284949048ee7d92a7ff5bad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E0=A4=95=E0=A4=BE=E0=A4=B0=E0=A4=A4=E0=A5=8B=E0=A4=AB?= =?UTF-8?q?=E0=A5=8D=E0=A4=AB=E0=A5=87=E0=A4=B2=E0=A4=B8=E0=A5=8D=E0=A4=95?= =?UTF-8?q?=E0=A5=8D=E0=A4=B0=E0=A4=BF=E0=A4=AA=E0=A5=8D=E0=A4=9F=E2=84=A2?= Date: Mon, 2 Dec 2024 16:55:01 +0100 Subject: [PATCH] fix(core): Fix .map calls on NodeExecutionOutput (no-changelog) (#11955) --- packages/core/src/WorkflowExecute.ts | 2 +- .../nodes/Merge/test/v3/operations.test.ts | 52 ++++++++++++------- .../nodes/Merge/v3/actions/mode/append.ts | 4 +- .../Merge/v3/actions/mode/chooseBranch.ts | 4 +- .../nodes/Merge/v3/actions/mode/combineAll.ts | 6 +-- .../Merge/v3/actions/mode/combineByFields.ts | 26 +++++----- .../v3/actions/mode/combineByPosition.ts | 4 +- .../Merge/v3/actions/mode/combineBySql.ts | 4 +- .../nodes/Merge/v3/actions/router.ts | 10 +--- packages/workflow/src/Interfaces.ts | 24 ++++++--- 10 files changed, 75 insertions(+), 61 deletions(-) diff --git a/packages/core/src/WorkflowExecute.ts b/packages/core/src/WorkflowExecute.ts index 5e1fecdfac..808bc17c9b 100644 --- a/packages/core/src/WorkflowExecute.ts +++ b/packages/core/src/WorkflowExecute.ts @@ -1194,7 +1194,7 @@ export class WorkflowExecute { } if (nodeSuccessData instanceof NodeExecutionOutput) { - const hints: NodeExecutionHint[] = nodeSuccessData.getHints(); + const hints = (nodeSuccessData as NodeExecutionOutput).getHints(); executionHints.push(...hints); } diff --git a/packages/nodes-base/nodes/Merge/test/v3/operations.test.ts b/packages/nodes-base/nodes/Merge/test/v3/operations.test.ts index 17502459f6..ae9dcfd954 100644 --- a/packages/nodes-base/nodes/Merge/test/v3/operations.test.ts +++ b/packages/nodes-base/nodes/Merge/test/v3/operations.test.ts @@ -100,7 +100,7 @@ describe('Test MergeV3, combineBySql operation', () => { inputsData, ); - expect(returnData[0].json).toEqual({ + expect(returnData[0][0].json).toEqual({ data_1: 'a', id: 1, data: 'aa', @@ -120,7 +120,9 @@ describe('Test MergeV3, combineBySql operation', () => { [inputsData[0], []], ); - expect(returnData[0].json).toEqual({ + expect(returnData.length).toEqual(1); + expect(returnData[0].length).toEqual(5); + expect(returnData[0][0].json).toEqual({ data: 'a', data_1: 'a', id: 1, @@ -179,13 +181,15 @@ describe('Test MergeV3, combineBySql operation', () => { inputsData, ); - expect(returnData[0].json).toEqual({ + expect(returnData.length).toEqual(1); + expect(returnData[0].length).toEqual(5); + expect(returnData[0][0].json).toEqual({ id: 1, data: 'aa', name: 'Sam', country: 'PL', }); - expect(returnData[4].json).toEqual({ + expect(returnData[0][4].json).toEqual({ id: 5, data: 'ff', country: 'ES', @@ -203,8 +207,9 @@ describe('Test MergeV3, combineBySql operation', () => { inputsData, ); - expect(returnData.length).toEqual(3); - expect(returnData[2].json).toEqual({ + expect(returnData.length).toEqual(1); + expect(returnData[0].length).toEqual(3); + expect(returnData[0][2].json).toEqual({ id: 3, data: 'cc', name: 'Jon', @@ -223,8 +228,9 @@ describe('Test MergeV3, combineBySql operation', () => { inputsData, ); - expect(returnData.length).toEqual(7); - expect(returnData[2].json).toEqual({ + expect(returnData.length).toEqual(1); + expect(returnData[0].length).toEqual(7); + expect(returnData[0][2].json).toEqual({ id: 3, data: 'cc', name: 'Jon', @@ -242,8 +248,9 @@ describe('Test MergeV3, combineBySql operation', () => { inputsData, ); - expect(returnData.length).toEqual(25); - expect(returnData[0].json).toEqual({ + expect(returnData.length).toEqual(1); + expect(returnData[0].length).toEqual(25); + expect(returnData[0][0].json).toEqual({ data_1: 'a', id: 1, data: 'aa', @@ -262,8 +269,9 @@ describe('Test MergeV3, append operation', () => { inputsData, ); - expect(returnData.length).toEqual(10); - expect(returnData[0].json).toEqual({ + expect(returnData.length).toEqual(1); + expect(returnData[0].length).toEqual(10); + expect(returnData[0][0].json).toEqual({ id: 1, data: 'a', name: 'Sam', @@ -283,8 +291,9 @@ describe('Test MergeV3, combineByFields operation', () => { inputsData, ); - expect(returnData.length).toEqual(3); - expect(returnData[1].json).toEqual({ + expect(returnData.length).toEqual(1); + expect(returnData[0].length).toEqual(3); + expect(returnData[0][1].json).toEqual({ id: 2, data: 'bb', name: 'Dan', @@ -302,8 +311,9 @@ describe('Test MergeV3, combineByPosition operation', () => { inputsData, ); - expect(returnData.length).toEqual(5); - expect(returnData[4].json).toEqual({ + expect(returnData.length).toEqual(1); + expect(returnData[0].length).toEqual(5); + expect(returnData[0][4].json).toEqual({ id: 5, data: 'ff', name: 'Joe', @@ -325,8 +335,9 @@ describe('Test MergeV3, chooseBranch operation', () => { inputsData, ); - expect(returnData.length).toEqual(5); - expect(returnData[0].json).toEqual({ + expect(returnData.length).toEqual(1); + expect(returnData[0].length).toEqual(5); + expect(returnData[0][0].json).toEqual({ id: 1, data: 'aa', country: 'PL', @@ -345,8 +356,9 @@ describe('Test MergeV3, combineAll operation', () => { inputsData, ); - expect(returnData.length).toEqual(25); - expect(returnData[0].json).toEqual({ + expect(returnData.length).toEqual(1); + expect(returnData[0].length).toEqual(25); + expect(returnData[0][0].json).toEqual({ id: 1, data: 'aa', name: 'Sam', diff --git a/packages/nodes-base/nodes/Merge/v3/actions/mode/append.ts b/packages/nodes-base/nodes/Merge/v3/actions/mode/append.ts index 1eaf58a0a8..5bf9896216 100644 --- a/packages/nodes-base/nodes/Merge/v3/actions/mode/append.ts +++ b/packages/nodes-base/nodes/Merge/v3/actions/mode/append.ts @@ -20,12 +20,12 @@ export const description = updateDisplayOptions(displayOptions, properties); export async function execute( this: IExecuteFunctions, inputsData: INodeExecutionData[][], -): Promise { +): Promise { const returnData: INodeExecutionData[] = []; for (let i = 0; i < inputsData.length; i++) { returnData.push.apply(returnData, inputsData[i]); } - return returnData; + return [returnData]; } diff --git a/packages/nodes-base/nodes/Merge/v3/actions/mode/chooseBranch.ts b/packages/nodes-base/nodes/Merge/v3/actions/mode/chooseBranch.ts index 5bd416ca18..053365e415 100644 --- a/packages/nodes-base/nodes/Merge/v3/actions/mode/chooseBranch.ts +++ b/packages/nodes-base/nodes/Merge/v3/actions/mode/chooseBranch.ts @@ -72,7 +72,7 @@ export const description = updateDisplayOptions(displayOptions, properties); export async function execute( this: IExecuteFunctions, inputsData: INodeExecutionData[][], -): Promise { +): Promise { const returnData: INodeExecutionData[] = []; const chooseBranchMode = this.getNodeParameter('chooseBranchMode', 0) as string; @@ -105,5 +105,5 @@ export async function execute( } } - return returnData; + return [returnData]; } diff --git a/packages/nodes-base/nodes/Merge/v3/actions/mode/combineAll.ts b/packages/nodes-base/nodes/Merge/v3/actions/mode/combineAll.ts index 504fab3d1f..089ecf9235 100644 --- a/packages/nodes-base/nodes/Merge/v3/actions/mode/combineAll.ts +++ b/packages/nodes-base/nodes/Merge/v3/actions/mode/combineAll.ts @@ -35,7 +35,7 @@ export const description = updateDisplayOptions(displayOptions, properties); export async function execute( this: IExecuteFunctions, inputsData: INodeExecutionData[][], -): Promise { +): Promise { const returnData: INodeExecutionData[] = []; const clashHandling = this.getNodeParameter( @@ -59,7 +59,7 @@ export async function execute( const mergeIntoSingleObject = selectMergeMethod(clashHandling); if (!input1 || !input2) { - return returnData; + return [returnData]; } let entry1: INodeExecutionData; @@ -79,5 +79,5 @@ export async function execute( } } - return returnData; + return [returnData]; } diff --git a/packages/nodes-base/nodes/Merge/v3/actions/mode/combineByFields.ts b/packages/nodes-base/nodes/Merge/v3/actions/mode/combineByFields.ts index 53620d3b05..bdbd40c444 100644 --- a/packages/nodes-base/nodes/Merge/v3/actions/mode/combineByFields.ts +++ b/packages/nodes-base/nodes/Merge/v3/actions/mode/combineByFields.ts @@ -260,7 +260,7 @@ export const description = updateDisplayOptions(displayOptions, properties); export async function execute( this: IExecuteFunctions, inputsData: INodeExecutionData[][], -): Promise { +): Promise { const returnData: INodeExecutionData[] = []; const advanced = this.getNodeParameter('advanced', 0) as boolean; let matchFields; @@ -297,7 +297,7 @@ export async function execute( options.disableDotNotation || false, 'Input 1', ); - if (!input1) return returnData; + if (!input1) return [returnData]; input2 = checkInput( this.getInputData(1), @@ -306,14 +306,14 @@ export async function execute( 'Input 2', ); } else { - if (!input1) return returnData; + if (!input1) return [returnData]; } if (input1.length === 0 || input2.length === 0) { if (!input1.length && joinMode === 'keepNonMatches' && outputDataFrom === 'input1') - return returnData; + return [returnData]; if (!input2.length && joinMode === 'keepNonMatches' && outputDataFrom === 'input2') - return returnData; + return [returnData]; if (joinMode === 'keepMatches') { // Stop the execution @@ -326,11 +326,11 @@ export async function execute( return []; } else { // Return the data of any of the inputs that contains data - return [...input1, ...input2]; + return [[...input1, ...input2]]; } } - if (!input1) return returnData; + if (!input1) return [returnData]; if (!input2 || !matchFields.length) { if ( @@ -338,9 +338,9 @@ export async function execute( joinMode === 'keepEverything' || joinMode === 'enrichInput2' ) { - return returnData; + return [returnData]; } - return input1; + return [input1]; } const matches = findMatches(input1, input2, matchFields, options); @@ -378,16 +378,16 @@ export async function execute( if (joinMode === 'keepNonMatches') { if (outputDataFrom === 'input1') { - return matches.unmatched1; + return [matches.unmatched1]; } if (outputDataFrom === 'input2') { - return matches.unmatched2; + return [matches.unmatched2]; } if (outputDataFrom === 'both') { let output: INodeExecutionData[] = []; output = output.concat(addSourceField(matches.unmatched1, 'input1')); output = output.concat(addSourceField(matches.unmatched2, 'input2')); - return output; + return [output]; } } @@ -415,5 +415,5 @@ export async function execute( } } - return returnData; + return [returnData]; } diff --git a/packages/nodes-base/nodes/Merge/v3/actions/mode/combineByPosition.ts b/packages/nodes-base/nodes/Merge/v3/actions/mode/combineByPosition.ts index 67ed47c15b..8cd7e6ed41 100644 --- a/packages/nodes-base/nodes/Merge/v3/actions/mode/combineByPosition.ts +++ b/packages/nodes-base/nodes/Merge/v3/actions/mode/combineByPosition.ts @@ -50,7 +50,7 @@ export const description = updateDisplayOptions(displayOptions, properties); export async function execute( this: IExecuteFunctions, inputsData: INodeExecutionData[][], -): Promise { +): Promise { const returnData: INodeExecutionData[] = []; const clashHandling = this.getNodeParameter( @@ -120,5 +120,5 @@ export async function execute( returnData.push({ json, binary, pairedItem }); } - return returnData; + return [returnData]; } diff --git a/packages/nodes-base/nodes/Merge/v3/actions/mode/combineBySql.ts b/packages/nodes-base/nodes/Merge/v3/actions/mode/combineBySql.ts index 055db7572d..9fd09abc73 100644 --- a/packages/nodes-base/nodes/Merge/v3/actions/mode/combineBySql.ts +++ b/packages/nodes-base/nodes/Merge/v3/actions/mode/combineBySql.ts @@ -41,7 +41,7 @@ export const description = updateDisplayOptions(displayOptions, properties); export async function execute( this: IExecuteFunctions, inputsData: INodeExecutionData[][], -): Promise { +): Promise { const nodeId = this.getNode().id; const returnData: INodeExecutionData[] = []; const pairedItem: IPairedItemData[] = []; @@ -132,5 +132,5 @@ export async function execute( delete alasql.databases[nodeId]; - return returnData; + return [returnData]; } diff --git a/packages/nodes-base/nodes/Merge/v3/actions/router.ts b/packages/nodes-base/nodes/Merge/v3/actions/router.ts index fe87969a6f..437ef1b3b6 100644 --- a/packages/nodes-base/nodes/Merge/v3/actions/router.ts +++ b/packages/nodes-base/nodes/Merge/v3/actions/router.ts @@ -1,4 +1,4 @@ -import { NodeExecutionOutput, type IExecuteFunctions } from 'n8n-workflow'; +import type { IExecuteFunctions } from 'n8n-workflow'; import { getNodeInputsData } from '../helpers/utils'; import type { MergeType } from './node.type'; import * as mode from './mode'; @@ -12,11 +12,5 @@ export async function router(this: IExecuteFunctions) { operationMode = combineBy; } - const returnData = await mode[operationMode as MergeType].execute.call(this, inputsData); - - if (returnData instanceof NodeExecutionOutput) { - return returnData; - } else { - return [returnData]; - } + return await mode[operationMode as MergeType].execute.call(this, inputsData); } diff --git a/packages/workflow/src/Interfaces.ts b/packages/workflow/src/Interfaces.ts index 0a7a5d0379..fbaea8f631 100644 --- a/packages/workflow/src/Interfaces.ts +++ b/packages/workflow/src/Interfaces.ts @@ -14,7 +14,7 @@ import type { URLSearchParams } from 'url'; import type { CODE_EXECUTION_MODES, CODE_LANGUAGES, LOG_LEVELS } from './Constants'; import type { IDeferredPromise } from './DeferredPromise'; -import type { ExecutionCancelledError } from './errors'; +import { ApplicationError, type ExecutionCancelledError } from './errors'; import type { ExpressionError } from './errors/expression.error'; import type { NodeApiError } from './errors/node-api.error'; import type { NodeOperationError } from './errors/node-operation.error'; @@ -1587,17 +1587,25 @@ export interface SupplyData { closeFunction?: CloseFunction; } -export class NodeExecutionOutput extends Array { - private hints: NodeExecutionHint[]; - +export class NodeExecutionOutput extends Array { constructor(data: INodeExecutionData[][], hints: NodeExecutionHint[] = []) { super(); - this.push(...data); - this.hints = hints; + // TODO: This is a temporary solution for NODE-1740, until we move away from extending native Array class + Object.defineProperty(data, 'getHints', { + value: () => hints, + enumerable: false, + writable: false, + configurable: false, + }); + return data as NodeExecutionOutput; } - public getHints(): NodeExecutionHint[] { - return this.hints; + static [Symbol.hasInstance](instance: unknown) { + return Array.isArray(instance) && 'getHints' in instance; + } + + getHints(): NodeExecutionHint[] { + throw new ApplicationError('This should not have been called'); } }