-
Notifications
You must be signed in to change notification settings - Fork 38.8k
refactor(core): Split WorkflowExecute.runNode
into smaller methods
#17864
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
7b3dbf4
to
4c576f0
Compare
4c576f0
to
230d521
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cubic analysis
1 issue found across 3 files • Review in cubic
React with 👍 or 👎 to teach cubic. You can also tag @cubic-dev-ai
to give feedback, ask questions, or re-run the review.
break; | ||
} | ||
// We always use the data of main input and the first input for execute | ||
let connectionInputData = inputData.main[0] as INodeExecutionData[]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rule violated: Prefer Typeguards over Type casting
Avoid using as
for type narrowing – replace the cast with a proper type annotation.
Rule "Prefer Typeguards over Type casting" forbids as
in this context since it is not a DOM/event/const/generic-constraint assertion.
Prompt for AI agents
Address the following comment on packages/core/src/execution-engine/workflow-execute.ts at line 1129:
<comment>Avoid using `as` for type narrowing – replace the cast with a proper type annotation.
Rule "Prefer Typeguards over Type casting" forbids `as` in this context since it is not a DOM/event/const/generic-constraint assertion.</comment>
<file context>
@@ -1089,72 +1089,68 @@ export class WorkflowExecute {
return customOperation;
}
- /** Executes the given node */
- // eslint-disable-next-line complexity
- async runNode(
- workflow: Workflow,
- executionData: IExecuteData,
- runExecutionData: IRunExecutionData,
</file context>
let connectionInputData = inputData.main[0] as INodeExecutionData[]; | |
let connectionInputData: INodeExecutionData[] = inputData.main[0]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense to me as a first refactor, a great improvement to understand what's happening. Also besides the heavy mocking I think the additional test coverage is quite useful.
I'll let Ivan do the final approval so we have another pair of eyes on it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Quick pass, more thorough look with fresh eyes tomorrow.
break; | ||
} | ||
// We always use the data of main input and the first input for execute | ||
let connectionInputData = inputData.main[0] as INodeExecutionData[]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could do inputData.main.at(0)
, check it for the empty case, and so allow the typechecker to recognize that connectionInputData
is INodeExecutionData[]
without overriding TS.
!nodeType.execute && | ||
!customOperation && | ||
(nodeType.poll || nodeType.trigger || nodeType.webhook) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found this condition a bit easier to read beforehand.
Curious why we need to check that a node type does not have an execute
method and does not have a custom node operation in addition to checking that it has a method that identifies it as a trigger, poller or webhook? I'd expect the latter should be enough?
|
||
let connectionInputData: INodeExecutionData[] = []; | ||
/** | ||
* Prepares input data for node execution based on node type and workflow settings |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This docline could use some love. What does it mean to "prepare" input data? The args mentioned are already clear from the signature. Let's either improve or remove?
// We always use the data of main input and the first input for execute | ||
let connectionInputData = inputData.main[0] as INodeExecutionData[]; | ||
|
||
const forceInputNodeExecution = workflow.settings.executionOrder !== 'v1'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const forceInputNodeExecution = workflow.settings.executionOrder !== 'v1'; | |
const legacyExecutionOrder = workflow.settings.executionOrder !== 'v1'; |
if (connectionInputData.length === 0) { | ||
return null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing context so this might be intended, but I notice the return value differs?
/** | ||
* Executes a declarative node using RoutingNode | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This docline doesn't add much.
/** | ||
* Executes a declarative node using RoutingNode | ||
*/ | ||
private async executeDeclarativeNode( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
private async executeDeclarativeNode( | |
private async executeDeclarativeNodeInTest( |
* @param abortSignal - Signal for canceling execution | ||
* @returns Promise resolving to node execution result | ||
*/ | ||
async runNode( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have a runNode
(which orchestrates) and an executeNode
(which actually executes). I find the above docline mostly redundant of the code (does it add much?), but it'd be a good opportunity to document this so folks can clearly differentiate between them. Or maybe finding better names that make this clear outright.
|
||
const response = await triggerResponse.manualTriggerResponse!; | ||
const nodeType = workflow.nodeTypes.getByNameAndVersion(node.type, node.typeVersion); | ||
const isDeclarativeNode = nodeType.description.requestDefaults !== undefined; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can move this after the connectionInputData
check.
executionData, | ||
abortSignal, | ||
); | ||
} else if (nodeType.poll) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For a minimal readability gain in this nightmare file, since all these cases return, can we remove the unnecessary elses and spread out these cases?
Summary
Review commit by commit.
This splits
WorkflowExecute.runNode
. This PR should not change any behaviour.This allows removing the eslint warning about cyclomatic complexity as well as making it clear which part of that method needs access to what bindings.
It also makes the method more readable and descriptive.
This is done to prepare the work of moving AI tool executions into the engine.
Note:
The additional tests were created by claude code, but I checked that they work via code coverage reports and mutation testing (e.g. willfully breaking the implementation to check if the test works)
The tests mock way to much and are mainly here to prove that moving the code around didn't change any logic.
With further refactors the tests will gradually be removed and replaced with handwritten tests that document that intention of the behaviour as well.
Related Linear tickets, Github issues, and Community forum posts
Review / Merge checklist
Docs updated or follow-up ticket created.PR Labeled withrelease/backport
(if the PR is an urgent fix that needs to be backported)