diff --git a/packages/cli/src/execution-lifecycle/__tests__/execution-lifecycle-hooks.test.ts b/packages/cli/src/execution-lifecycle/__tests__/execution-lifecycle-hooks.test.ts index 916f077480..19e57e5f88 100644 --- a/packages/cli/src/execution-lifecycle/__tests__/execution-lifecycle-hooks.test.ts +++ b/packages/cli/src/execution-lifecycle/__tests__/execution-lifecycle-hooks.test.ts @@ -84,6 +84,12 @@ describe('Execution Lifecycle Hooks', () => { const taskStartedData = mock(); const taskData = mock(); const runExecutionData = mock(); + + const successfulRunWithRewiredDestination = mock({ + status: 'success', + finished: true, + waitTill: undefined, + }); const successfulRun = mock({ status: 'success', finished: true, @@ -123,6 +129,15 @@ describe('Execution Lifecycle Hooks', () => { error: expressionError, }, }; + successfulRunWithRewiredDestination.data = { + startData: { + destinationNode: 'PartialExecutionToolExecutor', + originalDestinationNode: nodeName, + }, + resultData: { + runData: {}, + }, + }; }); const workflowEventTests = (expectedUserId?: string) => { @@ -154,6 +169,25 @@ describe('Execution Lifecycle Hooks', () => { expect(eventService.emit).not.toHaveBeenCalledWith('workflow-post-execute'); }); + + it('should reset destination node to original destination', async () => { + await lifecycleHooks.runHook('workflowExecuteAfter', [ + successfulRunWithRewiredDestination, + {}, + ]); + + expect(eventService.emit).toHaveBeenCalledWith('workflow-post-execute', { + executionId, + runData: successfulRunWithRewiredDestination, + workflow: workflowData, + userId: expectedUserId, + }); + + expect(successfulRunWithRewiredDestination.data.startData?.destinationNode).toBe(nodeName); + expect( + successfulRunWithRewiredDestination.data.startData?.originalDestinationNode, + ).toBeUndefined(); + }); }); }; diff --git a/packages/cli/src/execution-lifecycle/execution-lifecycle-hooks.ts b/packages/cli/src/execution-lifecycle/execution-lifecycle-hooks.ts index e37d33d6ed..76548b48b9 100644 --- a/packages/cli/src/execution-lifecycle/execution-lifecycle-hooks.ts +++ b/packages/cli/src/execution-lifecycle/execution-lifecycle-hooks.ts @@ -44,6 +44,15 @@ function hookFunctionsWorkflowEvents(hooks: ExecutionLifecycleHooks, userId?: st if (runData.status === 'waiting') return; const { executionId, workflowData: workflow } = this; + + if (runData.data.startData) { + const originalDestination = runData.data.startData.originalDestinationNode; + if (originalDestination) { + runData.data.startData.destinationNode = originalDestination; + runData.data.startData.originalDestinationNode = undefined; + } + } + eventService.emit('workflow-post-execute', { executionId, runData, workflow, userId }); }); } diff --git a/packages/cli/src/manual-execution.service.ts b/packages/cli/src/manual-execution.service.ts index 39b4a77d85..5bc3e6ac1b 100644 --- a/packages/cli/src/manual-execution.service.ts +++ b/packages/cli/src/manual-execution.service.ts @@ -1,4 +1,5 @@ import { Logger } from '@n8n/backend-common'; +import { TOOL_EXECUTOR_NODE_NAME } from '@n8n/constants'; import { Service } from '@n8n/di'; import * as a from 'assert/strict'; import { @@ -151,7 +152,14 @@ export class ManualExecutionService { workflow = graph.toWorkflow({ ...workflow, }); - data.destinationNode = graph.getDirectChildConnections(destinationNode).at(0)?.to?.name; + + // Save original destination + if (data.executionData) { + data.executionData.startData = data.executionData.startData ?? {}; + data.executionData.startData.originalDestinationNode = data.destinationNode; + } + // Set destination to Tool Executor + data.destinationNode = TOOL_EXECUTOR_NODE_NAME; } } diff --git a/packages/core/src/execution-engine/workflow-execute.ts b/packages/core/src/execution-engine/workflow-execute.ts index f85f539c2c..ab8231a226 100644 --- a/packages/core/src/execution-engine/workflow-execute.ts +++ b/packages/core/src/execution-engine/workflow-execute.ts @@ -358,6 +358,7 @@ export class WorkflowExecute { destinationNodeName, 'a destinationNodeName is required for the new partial execution flow', ); + const originalDestination = destinationNodeName; let destination = workflow.getNode(destinationNodeName); assert.ok( @@ -456,6 +457,7 @@ export class WorkflowExecute { this.runExecutionData = { startData: { destinationNode: destinationNodeName, + originalDestinationNode: originalDestination, runNodeFilter: Array.from(filteredNodes.values()).map((node) => node.name), }, resultData: { diff --git a/packages/workflow/src/interfaces.ts b/packages/workflow/src/interfaces.ts index 570d5691ae..ce77b145de 100644 --- a/packages/workflow/src/interfaces.ts +++ b/packages/workflow/src/interfaces.ts @@ -2152,6 +2152,7 @@ export interface IRunExecutionData { startData?: { startNodes?: StartNodeData[]; destinationNode?: string; + originalDestinationNode?: string; runNodeFilter?: string[]; }; resultData: {