mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 10:02:05 +00:00
fix(core): Prefer triggers with run data during partial executions (#14691)
This commit is contained in:
@@ -1,8 +1,9 @@
|
|||||||
import { mock } from 'jest-mock-extended';
|
import { mock } from 'jest-mock-extended';
|
||||||
import type { IConnections, INode, INodeType, INodeTypes, IPinData } from 'n8n-workflow';
|
import type { IConnections, INode, INodeType, INodeTypes, IPinData, IRunData } from 'n8n-workflow';
|
||||||
import { Workflow } from 'n8n-workflow';
|
import { Workflow } from 'n8n-workflow';
|
||||||
|
|
||||||
import { toIConnections } from './helpers';
|
import { createNodeData, toIConnections, toITaskData } from './helpers';
|
||||||
|
import { DirectedGraph } from '../directed-graph';
|
||||||
import { findTriggerForPartialExecution } from '../find-trigger-for-partial-execution';
|
import { findTriggerForPartialExecution } from '../find-trigger-for-partial-execution';
|
||||||
|
|
||||||
describe('findTriggerForPartialExecution', () => {
|
describe('findTriggerForPartialExecution', () => {
|
||||||
@@ -188,7 +189,7 @@ describe('findTriggerForPartialExecution', () => {
|
|||||||
'$description',
|
'$description',
|
||||||
({ nodes, connections, destinationNodeName, expectedTrigger, pinData }) => {
|
({ nodes, connections, destinationNodeName, expectedTrigger, pinData }) => {
|
||||||
const workflow = createMockWorkflow(nodes, toIConnections(connections), pinData);
|
const workflow = createMockWorkflow(nodes, toIConnections(connections), pinData);
|
||||||
expect(findTriggerForPartialExecution(workflow, destinationNodeName)).toBe(
|
expect(findTriggerForPartialExecution(workflow, destinationNodeName, {})).toBe(
|
||||||
expectedTrigger,
|
expectedTrigger,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -199,19 +200,39 @@ describe('findTriggerForPartialExecution', () => {
|
|||||||
describe('Error and Edge Case Handling', () => {
|
describe('Error and Edge Case Handling', () => {
|
||||||
it('should handle non-existent destination node gracefully', () => {
|
it('should handle non-existent destination node gracefully', () => {
|
||||||
const workflow = createMockWorkflow([], {});
|
const workflow = createMockWorkflow([], {});
|
||||||
expect(findTriggerForPartialExecution(workflow, 'NonExistentNode')).toBeUndefined();
|
expect(findTriggerForPartialExecution(workflow, 'NonExistentNode', {})).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle empty workflow', () => {
|
it('should handle empty workflow', () => {
|
||||||
const workflow = createMockWorkflow([], {});
|
const workflow = createMockWorkflow([], {});
|
||||||
expect(findTriggerForPartialExecution(workflow, '')).toBeUndefined();
|
expect(findTriggerForPartialExecution(workflow, '', {})).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle workflow with no connections', () => {
|
it('should handle workflow with no connections', () => {
|
||||||
const workflow = createMockWorkflow([manualTriggerNode], {});
|
const workflow = createMockWorkflow([manualTriggerNode], {});
|
||||||
expect(findTriggerForPartialExecution(workflow, manualTriggerNode.name)).toBe(
|
expect(findTriggerForPartialExecution(workflow, manualTriggerNode.name, {})).toBe(
|
||||||
manualTriggerNode,
|
manualTriggerNode,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should prefer triggers that have run data', () => {
|
||||||
|
// ARRANGE
|
||||||
|
const trigger1 = createNodeData({ name: 'trigger1', type: 'n8n-nodes-base.manualTrigger' });
|
||||||
|
const trigger2 = createNodeData({ name: 'trigger2', type: 'n8n-nodes-base.manualTrigger' });
|
||||||
|
const node = createNodeData({ name: 'node' });
|
||||||
|
const workflow = new DirectedGraph()
|
||||||
|
.addNodes(trigger1, trigger2, node)
|
||||||
|
.addConnections({ from: trigger1, to: node }, { from: trigger2, to: node })
|
||||||
|
.toWorkflow({ name: '', active: false, nodeTypes });
|
||||||
|
const runData: IRunData = {
|
||||||
|
[trigger1.name]: [toITaskData([{ data: { nodeName: 'trigger1' } }])],
|
||||||
|
};
|
||||||
|
|
||||||
|
// ACT
|
||||||
|
const chosenTrigger = findTriggerForPartialExecution(workflow, node.name, runData);
|
||||||
|
|
||||||
|
// ASSERT
|
||||||
|
expect(chosenTrigger).toBe(trigger1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as assert from 'assert/strict';
|
import * as assert from 'assert/strict';
|
||||||
import type { INode, INodeType, Workflow } from 'n8n-workflow';
|
import type { INode, INodeType, IRunData, Workflow } from 'n8n-workflow';
|
||||||
|
|
||||||
const isTriggerNode = (nodeType: INodeType) => nodeType.description.group.includes('trigger');
|
const isTriggerNode = (nodeType: INodeType) => nodeType.description.group.includes('trigger');
|
||||||
|
|
||||||
@@ -29,6 +29,7 @@ function findAllParentTriggers(workflow: Workflow, destinationNodeName: string)
|
|||||||
export function findTriggerForPartialExecution(
|
export function findTriggerForPartialExecution(
|
||||||
workflow: Workflow,
|
workflow: Workflow,
|
||||||
destinationNodeName: string,
|
destinationNodeName: string,
|
||||||
|
runData: IRunData,
|
||||||
): INode | undefined {
|
): INode | undefined {
|
||||||
// First, check if the destination node itself is a trigger
|
// First, check if the destination node itself is a trigger
|
||||||
const destinationNode = workflow.getNode(destinationNodeName);
|
const destinationNode = workflow.getNode(destinationNodeName);
|
||||||
@@ -48,6 +49,13 @@ export function findTriggerForPartialExecution(
|
|||||||
(trigger) => !trigger.disabled,
|
(trigger) => !trigger.disabled,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// prefer triggers that have run data
|
||||||
|
for (const trigger of parentTriggers) {
|
||||||
|
if (runData[trigger.name]) {
|
||||||
|
return trigger;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Prioritize webhook triggers with pinned-data
|
// Prioritize webhook triggers with pinned-data
|
||||||
const pinnedTriggers = parentTriggers
|
const pinnedTriggers = parentTriggers
|
||||||
// TODO: add the other filters here from `findAllPinnedActivators`, see
|
// TODO: add the other filters here from `findAllPinnedActivators`, see
|
||||||
|
|||||||
@@ -394,7 +394,7 @@ export class WorkflowExecute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 1. Find the Trigger
|
// 1. Find the Trigger
|
||||||
const trigger = findTriggerForPartialExecution(workflow, destinationNodeName);
|
const trigger = findTriggerForPartialExecution(workflow, destinationNodeName, runData);
|
||||||
if (trigger === undefined) {
|
if (trigger === undefined) {
|
||||||
throw new UserError('Connect a trigger to run this node');
|
throw new UserError('Connect a trigger to run this node');
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user