diff --git a/packages/core/src/execution-engine/__tests__/workflow-execute.test.ts b/packages/core/src/execution-engine/__tests__/workflow-execute.test.ts index 24ca096f38..82f8db0b84 100644 --- a/packages/core/src/execution-engine/__tests__/workflow-execute.test.ts +++ b/packages/core/src/execution-engine/__tests__/workflow-execute.test.ts @@ -970,6 +970,47 @@ describe('WorkflowExecute', () => { expect.objectContaining({ executionIndex: 1 }), ]); }); + + // ►► + // ┌─────┐1 ┌─────┐ + // │node1├──────►node2│ + // └─────┘ └─────┘ + test('should find closest parent with run data when no trigger exists', async () => { + // ARRANGE + const waitPromise = createDeferredPromise(); + const additionalData = Helpers.WorkflowExecuteAdditionalData(waitPromise); + const workflowExecute = new WorkflowExecute(additionalData, 'manual'); + + const node1 = createNodeData({ name: 'node1' }); + const node2 = createNodeData({ name: 'node2' }); + const workflow = new DirectedGraph() + .addNodes(node1, node2) + .addConnections({ from: node1, to: node2 }) + .toWorkflow({ name: '', active: false, nodeTypes }); + + const pinData: IPinData = {}; + const runData: IRunData = { + [node1.name]: [toITaskData([{ data: { name: node1.name } }])], + }; + const dirtyNodeNames: string[] = []; + const destinationNode = node2.name; + + const processRunExecutionDataSpy = jest + .spyOn(workflowExecute, 'processRunExecutionData') + .mockImplementationOnce(jest.fn()); + + // ACT + await workflowExecute.runPartialWorkflow2( + workflow, + runData, + pinData, + dirtyNodeNames, + destinationNode, + ); + + // ASSERT + expect(processRunExecutionDataSpy).toHaveBeenCalledTimes(1); + }); }); describe('checkReadyForExecution', () => { diff --git a/packages/core/src/execution-engine/workflow-execute.ts b/packages/core/src/execution-engine/workflow-execute.ts index 8f7cee00c5..3f304795b9 100644 --- a/packages/core/src/execution-engine/workflow-execute.ts +++ b/packages/core/src/execution-engine/workflow-execute.ts @@ -424,9 +424,27 @@ export class WorkflowExecute { } // 1. Find the Trigger - const trigger = findTriggerForPartialExecution(workflow, destinationNodeName, runData); + let trigger = findTriggerForPartialExecution(workflow, destinationNodeName, runData); if (trigger === undefined) { - throw new UserError('Connect a trigger to run this node'); + // destination has parents but none of them are triggers, so find the closest + // parent node that has run data, and treat that parent as starting point + + let startNode; + + const parentNodes = workflow.getParentNodes(destinationNodeName); + + for (const nodeName of parentNodes) { + if (runData[nodeName]) { + startNode = workflow.getNode(nodeName); + break; + } + } + + if (!startNode) { + throw new UserError('Connect a trigger to run this node'); + } + + trigger = startNode; } // 2. Find the Subgraph