-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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
Build separated executors #10370
Build separated executors #10370
Conversation
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.
PR Summary
This PR introduces a significant architectural change to the workflow execution system by separating concerns into distinct WorkflowExecutor and WorkflowActionExecutor components.
- Added new
WorkflowStep
type in/packages/twenty-server/src/modules/workflow/workflow-executor/types/workflow-step.type.ts
to support V2 workflow system - Introduced
WorkflowStepResult
in/packages/twenty-server/src/modules/workflow/workflow-executor/types/workflow-step-result.type.ts
replacingWorkflowActionResult
for standardized output handling - Added type guards
isWorkflowStep
andisWorkflowAction
in/packages/twenty-server/src/modules/workflow/workflow-executor/guards/
for step validation - Implemented dual execution paths in
workflow-action-executor.workspace-service.ts
to maintain backward compatibility while introducing new step format - Restructured modules to better separate concerns, moving action-specific logic into
WorkflowActionExecutorModule
25 file(s) reviewed, 13 comment(s)
Edit PR Review Bot Settings | Greptile
packages/twenty-server/src/modules/workflow/workflow-executor/guards/is-workflow-step.guard.ts
Show resolved
Hide resolved
.../twenty-server/src/modules/workflow/common/standard-objects/workflow-run.workspace-entity.ts
Show resolved
Hide resolved
.../twenty-server/src/modules/workflow/common/standard-objects/workflow-run.workspace-entity.ts
Show resolved
Hide resolved
...ages/twenty-server/src/modules/workflow/workflow-executor/types/workflow-step-result.type.ts
Show resolved
Hide resolved
packages/twenty-server/src/modules/workflow/workflow-executor/types/workflow-step.type.ts
Outdated
Show resolved
Hide resolved
...ules/workflow/workflow-executor/workflow-actions/record-crud/find-records.workflow-action.ts
Outdated
Show resolved
Hide resolved
packages/twenty-server/src/modules/workflow/workflow-runner/jobs/run-workflow.job.ts
Show resolved
Hide resolved
packages/twenty-server/src/modules/workflow/workflow-runner/jobs/run-workflow.job.ts
Show resolved
Hide resolved
...modules/workflow/workflow-executor/workspace-services/workflow-executor.workspace-service.ts
Show resolved
Hide resolved
...modules/workflow/workflow-executor/workspace-services/workflow-executor.workspace-service.ts
Outdated
Show resolved
Hide resolved
9fabd21
to
3b9c156
Compare
ed7a80f
to
9ee4d49
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.
I'm really excited by these changes! I wrote a few comments, let's discuss about that. I'm already approving the PR as there is nothing critical.
Great job on the retrocompatibility! We can still execute workflows properly.
export const isWorkflowAction = ( | ||
step: WorkflowStep | WorkflowAction, | ||
): step is WorkflowAction => { | ||
return 'settings' in step; | ||
}; |
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 love these guards 👌
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.
Maybe we can check the value of the type
property. It seems to be the most substantial separation between the two types.
export const isWorkflowAction = ( | |
step: WorkflowStep | WorkflowAction, | |
): step is WorkflowAction => { | |
return 'settings' in step; | |
}; | |
export const isWorkflowAction = ( | |
step: WorkflowStep | WorkflowAction, | |
): step is WorkflowAction => { | |
return Object.values(WorkflowActionType).includes( | |
step.type as WorkflowActionType, | |
); | |
}; |
I'm unsure how we usually do that in the back-end codebase.
export const isWorkflowStep = ( | ||
step: WorkflowStep | WorkflowAction, | ||
): step is WorkflowStep => { | ||
return 'stepSettings' in step; | ||
}; |
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.
Same thing here. I would check the type
property as it's the most significant difference between the two types.
export const isWorkflowStep = ( | |
step: WorkflowStep | WorkflowAction, | |
): step is WorkflowStep => { | |
return 'stepSettings' in step; | |
}; | |
export const isWorkflowStep = ( | |
step: WorkflowStep | WorkflowAction, | |
): step is WorkflowStep => { | |
return Object.values(WorkflowStepType).includes( | |
step.type as WorkflowStepType, | |
); | |
}; |
/** | ||
* To migrate to WorkflowActionStepSettings once the V2 workflow is released. | ||
*/ |
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 updatedStepsOutput = { | ||
...workflowExecutorState.stepsOutput, | ||
[step.id]: updatedStepOutput, | ||
if (result.result) { |
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.
🤓 nitpick: result.result
. Maybe we can rename the result
variable to read stepExecution.result
return this.execute({ | ||
workflowRunId, | ||
currentStepIndex: currentStepIndex + 1, | ||
steps, | ||
context, | ||
}); | ||
} |
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.
Excellent 👍
|
||
let result: WorkflowActionResult; | ||
const stepExecutor = this.getStepExecutor(step); | ||
let result; |
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.
The result
variable was typed in the previous version. Why did you remove the explicit type?
); | ||
} | ||
|
||
let result; |
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 think it would be great to strictly type the result
variable.
import { WorkflowExecutorInput } from 'src/modules/workflow/workflow-executor/types/workflow-executor-input'; | ||
import { WorkflowStepResult } from 'src/modules/workflow/workflow-executor/types/workflow-step-result.type'; | ||
|
||
export interface WorkflowExecutor { |
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 like this interface 💪
} else { | ||
return this.executeStep({ | ||
currentStepIndex, | ||
steps, | ||
context, | ||
attemptCount, | ||
workflowRunId, | ||
}); | ||
} |
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.
🤓 nitpick: I think it would be "safer" to use else if (isWorkflowStep(step))
to not fall in an unwanted case.
settings?: object; | ||
|
||
@Field(() => graphqlTypeJson) | ||
stepSettings?: object; |
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.
Could you please remind me why we split the settings into two properties?
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.
settings is the previous field that will be copied into actionSettings, subfield of stepSettings
We now have two executors. WorkflowExecutor is responsible for:
WorkflowActionExecutor finds the right action to execute.
Both returns a base output so the parent caller can also build its output.
I also duplicated the execution functions so we keep on handling the old step format.
Next steps: