diff --git a/packages/core/src/WorkflowExecute.ts b/packages/core/src/WorkflowExecute.ts index 82418016d5..65361586fe 100644 --- a/packages/core/src/WorkflowExecute.ts +++ b/packages/core/src/WorkflowExecute.ts @@ -582,7 +582,14 @@ export class WorkflowExecute { const startedAt = new Date(); - const workflowIssues = workflow.checkReadyForExecution(); + const startNode = this.runExecutionData.executionData!.nodeExecutionStack[0].node.name; + + let destinationNode: string | undefined; + if (this.runExecutionData.startData && this.runExecutionData.startData.destinationNode) { + destinationNode = this.runExecutionData.startData.destinationNode; + } + + const workflowIssues = workflow.checkReadyForExecution({ startNode, destinationNode }); if (workflowIssues !== null) { throw new Error( 'The workflow has issues and can for that reason not be executed. Please fix them first.', diff --git a/packages/editor-ui/src/components/mixins/workflowHelpers.ts b/packages/editor-ui/src/components/mixins/workflowHelpers.ts index 23c7e53f4b..907f5bac02 100644 --- a/packages/editor-ui/src/components/mixins/workflowHelpers.ts +++ b/packages/editor-ui/src/components/mixins/workflowHelpers.ts @@ -1,6 +1,9 @@ import { + ERROR_TRIGGER_NODE_NAME, PLACEHOLDER_FILLED_AT_EXECUTION_TIME, PLACEHOLDER_EMPTY_WORKFLOW_ID, + START_NODE_TYPE, + WEBHOOK_NODE_NAME, } from '@/constants'; import { @@ -144,13 +147,39 @@ export const workflowHelpers = mixins( }, // Checks if everything in the workflow is complete and ready to be executed - checkReadyForExecution (workflow: Workflow) { + checkReadyForExecution (workflow: Workflow, lastNodeName?: string) { let node: INode; let nodeType: INodeType | undefined; let nodeIssues: INodeIssues | null = null; const workflowIssues: IWorfklowIssues = {}; - for (const nodeName of Object.keys(workflow.nodes)) { + let checkNodes = Object.keys(workflow.nodes); + if (lastNodeName) { + checkNodes = workflow.getParentNodes(lastNodeName); + checkNodes.push(lastNodeName); + } else { + // As webhook nodes always take presidence check first + // if there are any + let checkWebhook: string[] = []; + for (const nodeName of Object.keys(workflow.nodes)) { + if (workflow.nodes[nodeName].disabled !== true && workflow.nodes[nodeName].type === WEBHOOK_NODE_NAME) { + checkWebhook = [nodeName, ...checkWebhook, ...workflow.getChildNodes(nodeName)]; + } + } + + if (checkWebhook.length) { + checkNodes = checkWebhook; + } else { + // If no webhook nodes got found try to find another trigger node + const startNode = workflow.getStartNode(); + if (startNode !== undefined) { + checkNodes = workflow.getChildNodes(startNode.name); + checkNodes.push(startNode.name); + } + } + } + + for (const nodeName of checkNodes) { nodeIssues = null; node = workflow.nodes[nodeName]; @@ -214,6 +243,10 @@ export const workflowHelpers = mixins( return { description: nodeTypeDescription, + // As we do not have the trigger/poll functions available in the frontend + // we use the information available to figure out what are trigger nodes + // @ts-ignore + trigger: ![ERROR_TRIGGER_NODE_NAME, START_NODE_TYPE].includes(nodeType) && nodeTypeDescription.inputs.length === 0 && !nodeTypeDescription.webhooks || undefined, }; }, }; @@ -498,7 +531,7 @@ export const workflowHelpers = mixins( } as IUpdateInformation; this.$store.commit('setNodeValue', changes); }); - + const createdTags = (workflowData.tags || []) as ITag[]; const tagIds = createdTags.map((tag: ITag): string => tag.id); this.$store.commit('setWorkflowTagIds', tagIds); diff --git a/packages/editor-ui/src/components/mixins/workflowRun.ts b/packages/editor-ui/src/components/mixins/workflowRun.ts index ff5b0af998..5196ba573b 100644 --- a/packages/editor-ui/src/components/mixins/workflowRun.ts +++ b/packages/editor-ui/src/components/mixins/workflowRun.ts @@ -55,7 +55,7 @@ export const workflowRun = mixins( return response; }, - async runWorkflow (nodeName: string, source?: string): Promise { + async runWorkflow (nodeName?: string, source?: string): Promise { if (this.$store.getters.isActionActive('workflowRunning') === true) { return; } @@ -70,7 +70,7 @@ export const workflowRun = mixins( const issuesExist = this.$store.getters.nodesIssuesExist; if (issuesExist === true) { // If issues exist get all of the issues of all nodes - const workflowIssues = this.checkReadyForExecution(workflow); + const workflowIssues = this.checkReadyForExecution(workflow, nodeName); if (workflowIssues !== null) { const errorMessages = []; let nodeIssues: string[]; @@ -94,7 +94,10 @@ export const workflowRun = mixins( } // Get the direct parents of the node - const directParentNodes = workflow.getParentNodes(nodeName, 'main', 1); + let directParentNodes: string[] = []; + if (nodeName !== undefined) { + directParentNodes = workflow.getParentNodes(nodeName, 'main', 1); + } const runData = this.$store.getters.getWorkflowRunData; @@ -133,7 +136,7 @@ export const workflowRun = mixins( } } - if (startNodes.length === 0) { + if (startNodes.length === 0 && nodeName !== undefined) { startNodes.push(nodeName); } diff --git a/packages/editor-ui/src/constants.ts b/packages/editor-ui/src/constants.ts index fff33d7822..8bcd3d4d5f 100644 --- a/packages/editor-ui/src/constants.ts +++ b/packages/editor-ui/src/constants.ts @@ -54,6 +54,7 @@ export const ALL_NODE_FILTER = 'All'; export const UNCATEGORIZED_CATEGORY = 'Miscellaneous'; export const UNCATEGORIZED_SUBCATEGORY = 'Helpers'; export const HIDDEN_NODES = ['n8n-nodes-base.start']; +export const ERROR_TRIGGER_NODE_NAME = 'n8n-nodes-base.errorTrigger'; export const WEBHOOK_NODE_NAME = 'n8n-nodes-base.webhook'; export const HTTP_REQUEST_NODE_NAME = 'n8n-nodes-base.httpRequest'; export const REQUEST_NODE_FORM_URL = 'https://n8n-community.typeform.com/to/K1fBVTZ3'; diff --git a/packages/workflow/src/Workflow.ts b/packages/workflow/src/Workflow.ts index 36ed377bb4..d0b2e1efa4 100644 --- a/packages/workflow/src/Workflow.ts +++ b/packages/workflow/src/Workflow.ts @@ -231,13 +231,29 @@ export class Workflow { * @returns {(IWorfklowIssues | null)} * @memberof Workflow */ - checkReadyForExecution(): IWorfklowIssues | null { + checkReadyForExecution(inputData: { + startNode?: string; + destinationNode?: string; + }): IWorfklowIssues | null { let node: INode; let nodeType: INodeType | undefined; let nodeIssues: INodeIssues | null = null; const workflowIssues: IWorfklowIssues = {}; - for (const nodeName of Object.keys(this.nodes)) { + let checkNodes: string[] = []; + if (inputData.destinationNode) { + // If a destination node is given we have to check all the nodes + // leading up to it + checkNodes = this.getParentNodes(inputData.destinationNode); + checkNodes.push(inputData.destinationNode); + } else if (inputData.startNode) { + // If a start node is given we have to check all nodes which + // come after it + checkNodes = this.getChildNodes(inputData.startNode); + checkNodes.push(inputData.startNode); + } + + for (const nodeName of checkNodes) { nodeIssues = null; node = this.nodes[nodeName];