-
Notifications
You must be signed in to change notification settings - Fork 2.4k
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
Execute variables in action input #7715
Conversation
thomtrp
commented
Oct 15, 2024
•
edited
Loading
edited
- send context from all previous steps rather than unique payload
- wrap input data in settings into input field
- add email into send email action settings
- update output shape
48367c5
to
126dcea
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.
Hey, good job, some comments
packages/twenty-server/src/modules/mail-sender/workflow-actions/send-email.workflow-action.ts
Show resolved
Hide resolved
packages/twenty-server/src/modules/mail-sender/workflow-actions/send-email.workflow-action.ts
Show resolved
Hide resolved
packages/twenty-server/src/modules/workflow/workflow-executor/utils/variable-resolver.util.ts
Outdated
Show resolved
Hide resolved
...modules/workflow/workflow-executor/workspace-services/workflow-executor.workspace-service.ts
Outdated
Show resolved
Hide resolved
...modules/workflow/workflow-executor/workspace-services/workflow-executor.workspace-service.ts
Outdated
Show resolved
Hide resolved
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 pull request introduces significant changes to the workflow execution process, focusing on variable handling and input structure for workflow actions. Here's a concise summary of the major changes:
- Restructured workflow step settings to wrap input data in an 'input' field for both code and email actions
- Added email field to send email action settings for improved flexibility
- Implemented variable resolution using Handlebars templating for dynamic content in action inputs
- Updated workflow execution to use a context object containing data from all previous steps
- Modified workflow run output structure to track multiple execution attempts per step
- Introduced a new variable resolver utility for handling different input types and error scenarios
Key points to consider:
- The changes may require updates to existing code that relies on the previous input structure
- New error handling for variable evaluation failures has been added
- The modifications aim to enhance flexibility and tracking in workflow execution
20 file(s) reviewed, 25 comment(s)
Edit PR Review Bot Settings | Greptile
packages/twenty-front/src/modules/workflow/components/WorkflowEditActionFormSendEmail.tsx
Show resolved
Hide resolved
...es/twenty-front/src/modules/workflow/components/WorkflowEditActionFormServerlessFunction.tsx
Show resolved
Hide resolved
...es/twenty-front/src/modules/workflow/components/WorkflowEditActionFormServerlessFunction.tsx
Show resolved
Hide resolved
...es/twenty-front/src/modules/workflow/components/WorkflowEditActionFormServerlessFunction.tsx
Show resolved
Hide resolved
packages/twenty-server/src/modules/mail-sender/workflow-actions/send-email.workflow-action.ts
Outdated
Show resolved
Hide resolved
packages/twenty-server/src/modules/workflow/workflow-executor/utils/variable-resolver.util.ts
Show resolved
Hide resolved
...modules/workflow/workflow-executor/workspace-services/workflow-executor.workspace-service.ts
Outdated
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
Show resolved
Hide resolved
packages/twenty-server/src/modules/workflow/workflow-runner/jobs/run-workflow.job.ts
Show resolved
Hide resolved
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.
Great job, @thomtrp! I left some comments to discuss a few things in your implementation. That looks great! 😉
attemptCount: number; | ||
result: object | undefined; | ||
error: string | undefined; | ||
}[]; | ||
}; | ||
|
||
export type WorkflowRunOutput = { | ||
steps: Record<string, StepRunOutput>; |
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 consequence of storing the output of the steps in an object is that their order is not guaranteed to be preserved. That's not an issue on its own, but the frontend will have to fetch the steps definition to print the output in order.
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.
Indeed. In V1, the frontend may have to re-order the output if we go with the figma. But most likely, we will end with a drawer and each step will look for its output. That's where a map with the step id as key will be useful!
}: { | ||
step: WorkflowStep; | ||
payload?: object; | ||
context?: Record<string, any>; |
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 to use unknown
instead of any
when I don't know the data type that will be there. It forces me to type-check it explicitly later.
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.
Also, we might want to create a specific type for the context so we can reuse it easily.
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 use Record<string, any>
in a lot of places actually. This is because we do not know the shape of the objects. And same goes for context, since it could contain anything. Not sure what specific type we would create...
About the unknown you are right, I updated the code 👍
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.
Perfect for unknown
👌
I wasn't clear, but I meant that we could create a WorkflowStepInputContext
type or another name not to repeat Record<string, unknown>
many times.
...es/twenty-server/src/modules/workflow/workflow-executor/types/workflow-step-settings.type.ts
Outdated
Show resolved
Hide resolved
const VARIABLE_PATTERN = RegExp('\\{\\{(.*?)\\}\\}', 'g'); | ||
|
||
export const resolve = ( | ||
unresolvedInput: any, |
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.
Is it possible for us to predict a more specific type for the unresolvedInput
parameter? If it's not, I would use unknown
to be forced to explicitly check the type of the arguments.
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.
nope as explained above. So using unknown!
return input.replace(VARIABLE_PATTERN, (matchedToken, _) => { | ||
const processedToken = evalFromContext(matchedToken, context); | ||
|
||
return processedToken; | ||
}); |
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.
Aren't we redoing the job of Handlebars here? Couldn't we call evalFromContext
on the whole input
string and let Handlebars replace the variables? i might misunderstand something.
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 want to focus on variables only there. Giving the whole string to handlebars can make it translate pretty much anything. See different templates here https://handlebarsjs.com/guide/#nested-input-objects
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.
Indeed. Having loops and conditions might be interesting when writing the body of the email, though!
9dc1752
to
0906d12
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.
Two small comment about naming and typing but LGTM though. Nice work!
step: WorkflowStep; | ||
payload?: object; | ||
}): Promise<WorkflowActionResult>; | ||
execute(payload: unknown): Promise<WorkflowActionResult>; |
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 know that payload is at least an object isn't 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.
We cannot be sure. The step could have an input null
. Or the inferred variables could provide anything
step: WorkflowCodeStep; | ||
payload?: object; | ||
}): Promise<WorkflowActionResult> { | ||
async execute(payload: WorkflowCodeStepInput): Promise<WorkflowActionResult> { |
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.
payload
might not be the proper naming finally. What about workflowStepInput
or actionInput
or just input
?
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 will go with workflowStepInput there!
it('should throw an error for invalid expressions', () => { | ||
expect(() => resolveInput('{{invalidFunction()}}', context)).toThrow(); | ||
}); | ||
}); |
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.
😍
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.
Great job, @thomtrp, as usual! We can merge.
return input.replace(VARIABLE_PATTERN, (matchedToken, _) => { | ||
const processedToken = evalFromContext(matchedToken, context); | ||
|
||
return processedToken; | ||
}); |
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.
Indeed. Having loops and conditions might be interesting when writing the body of the email, though!
c42303c
to
a72ebcc
Compare