mirror of
https://github.com/n8n-io/n8n.git
synced 2026-09-24 23:22:38 +08:00
fix(core): During partial execution don't include loop as start node if the loop isn't closed (#22555)
Co-authored-by: Danny Martini <danny@n8n.io>
This commit is contained in:
+41
@@ -624,5 +624,46 @@ describe('findStartNodes', () => {
|
||||
expect(startNodes.size).toBe(1);
|
||||
expect(startNodes).toContainEqual(afterLoop);
|
||||
});
|
||||
|
||||
// done (empty)
|
||||
// ┌────►
|
||||
// ┌─────────┐1 ┌────┐1 │
|
||||
// │ trigger ┼──►loop┼─┬┘ ►►
|
||||
// └─────────┘ └────┘ │ ┌────────┐
|
||||
// └─►│in loop │
|
||||
// └────────┘
|
||||
test('if a loop node does not actually form a loop in the graph, it uses loop output instead of done output', () => {
|
||||
// ARRANGE
|
||||
const trigger = createNodeData({ name: 'trigger' });
|
||||
const loop = createNodeData({ name: 'loop', type: 'n8n-nodes-base.splitInBatches' });
|
||||
const inLoop = createNodeData({ name: 'inLoop' });
|
||||
const graph = new DirectedGraph().addNodes(trigger, loop, inLoop).addConnections(
|
||||
{ from: trigger, to: loop },
|
||||
// Note: loop connects to inLoop via output 1, but there's no connection
|
||||
// back to loop, so it's not actually a loop
|
||||
{ from: loop, outputIndex: 1, to: inLoop },
|
||||
);
|
||||
const runData: IRunData = {
|
||||
[trigger.name]: [toITaskData([{ data: { name: 'trigger' } }])],
|
||||
// The loop node has data on output 1 (the first output), but not on output 0 (done)
|
||||
[loop.name]: [toITaskData([{ outputIndex: 1, data: { name: 'loop' } }])],
|
||||
};
|
||||
|
||||
// ACT
|
||||
const startNodes = findStartNodes({
|
||||
graph,
|
||||
trigger,
|
||||
destination: inLoop,
|
||||
runData,
|
||||
pinData: {},
|
||||
});
|
||||
|
||||
// ASSERT
|
||||
// Because the loop node doesn't form an actual loop, it should check output 1
|
||||
// for run data (not output 0). Since output 1 has data, the loop node should
|
||||
// not be a start node, and we should continue to inLoop.
|
||||
expect(startNodes.size).toBe(1);
|
||||
expect(startNodes).toContainEqual(inLoop);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -83,7 +83,11 @@ function findStartNodesRecursive(
|
||||
// last run
|
||||
-1,
|
||||
NodeConnectionTypes.Main,
|
||||
0,
|
||||
// Although this is a Loop node, the graph may not actually have a loop here e.g.,
|
||||
// while the workflow is under development. If there's not a loop, we treat the loop
|
||||
// node as a normal node and take the data from the first output at index 1.
|
||||
// If there *is* a loop, we take the data from the `done` output at index 0.
|
||||
isALoop(graph, current) ? 0 : 1,
|
||||
);
|
||||
|
||||
if (nodeRunData === null || nodeRunData.length === 0) {
|
||||
@@ -130,6 +134,10 @@ function findStartNodesRecursive(
|
||||
return startNodes;
|
||||
}
|
||||
|
||||
function isALoop(graph: DirectedGraph, node: INode): boolean {
|
||||
return graph.getChildren(node).has(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* The start node is the node from which a partial execution starts. The start
|
||||
* node will be executed or re-executed.
|
||||
|
||||
Reference in New Issue
Block a user