Add workflow email action (#7279)
- Add the SAVE_EMAIL action. This action requires more setting parameters than the Serverless Function action. - Changed the way we computed the workflow diagram. It now preserves some properties, like the `selected` property. That's necessary to not close the right drawer when the workflow back-end data change. - Added the possibility to set a label to a TextArea. This uses a `<label>` HTML element and the `useId()` hook to create an id linking the label with the input.
This commit is contained in:
committed by
GitHub
parent
0d570caff5
commit
cde255a031
@ -72,6 +72,7 @@ describe('generateWorkflowDiagram', () => {
|
||||
for (const [index, step] of steps.entries()) {
|
||||
expect(stepNodes[index].data).toEqual({
|
||||
nodeType: 'action',
|
||||
actionType: 'CODE',
|
||||
label: step.name,
|
||||
});
|
||||
}
|
||||
|
||||
@ -0,0 +1,72 @@
|
||||
import { WorkflowDiagram } from '@/workflow/types/WorkflowDiagram';
|
||||
import { mergeWorkflowDiagrams } from '../mergeWorkflowDiagrams';
|
||||
|
||||
it('Preserves the properties defined in the previous version but not in the next one', () => {
|
||||
const previousDiagram: WorkflowDiagram = {
|
||||
nodes: [
|
||||
{
|
||||
data: { nodeType: 'action', label: '', actionType: 'CODE' },
|
||||
id: '1',
|
||||
position: { x: 0, y: 0 },
|
||||
selected: true,
|
||||
},
|
||||
],
|
||||
edges: [],
|
||||
};
|
||||
const nextDiagram: WorkflowDiagram = {
|
||||
nodes: [
|
||||
{
|
||||
data: { nodeType: 'action', label: '', actionType: 'CODE' },
|
||||
id: '1',
|
||||
position: { x: 0, y: 0 },
|
||||
},
|
||||
],
|
||||
edges: [],
|
||||
};
|
||||
|
||||
expect(mergeWorkflowDiagrams(previousDiagram, nextDiagram)).toEqual({
|
||||
nodes: [
|
||||
{
|
||||
data: { nodeType: 'action', label: '', actionType: 'CODE' },
|
||||
id: '1',
|
||||
position: { x: 0, y: 0 },
|
||||
selected: true,
|
||||
},
|
||||
],
|
||||
edges: [],
|
||||
});
|
||||
});
|
||||
|
||||
it('Replaces duplicated properties with the next value', () => {
|
||||
const previousDiagram: WorkflowDiagram = {
|
||||
nodes: [
|
||||
{
|
||||
data: { nodeType: 'action', label: '', actionType: 'CODE' },
|
||||
id: '1',
|
||||
position: { x: 0, y: 0 },
|
||||
},
|
||||
],
|
||||
edges: [],
|
||||
};
|
||||
const nextDiagram: WorkflowDiagram = {
|
||||
nodes: [
|
||||
{
|
||||
data: { nodeType: 'action', label: '2', actionType: 'CODE' },
|
||||
id: '1',
|
||||
position: { x: 0, y: 0 },
|
||||
},
|
||||
],
|
||||
edges: [],
|
||||
};
|
||||
|
||||
expect(mergeWorkflowDiagrams(previousDiagram, nextDiagram)).toEqual({
|
||||
nodes: [
|
||||
{
|
||||
data: { nodeType: 'action', label: '2', actionType: 'CODE' },
|
||||
id: '1',
|
||||
position: { x: 0, y: 0 },
|
||||
},
|
||||
],
|
||||
edges: [],
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,3 @@
|
||||
export const assertUnreachable = (x: never, errorMessage?: string): never => {
|
||||
throw new Error(errorMessage ?? "Didn't expect to get here.");
|
||||
};
|
||||
@ -33,6 +33,7 @@ export const generateWorkflowDiagram = ({
|
||||
id: nodeId,
|
||||
data: {
|
||||
nodeType: 'action',
|
||||
actionType: step.type,
|
||||
label: step.name,
|
||||
},
|
||||
position: {
|
||||
|
||||
@ -26,6 +26,27 @@ export const getStepDefaultDefinition = (
|
||||
},
|
||||
};
|
||||
}
|
||||
case 'SEND_EMAIL': {
|
||||
return {
|
||||
id: newStepId,
|
||||
name: 'Send Email',
|
||||
type: 'SEND_EMAIL',
|
||||
valid: false,
|
||||
settings: {
|
||||
subject: 'hello',
|
||||
title: 'hello',
|
||||
template: '{{title}}',
|
||||
errorHandlingOptions: {
|
||||
continueOnFailure: {
|
||||
value: false,
|
||||
},
|
||||
retryOnFailure: {
|
||||
value: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
default: {
|
||||
throw new Error(`Unknown type: ${type}`);
|
||||
}
|
||||
|
||||
@ -0,0 +1,33 @@
|
||||
import {
|
||||
WorkflowDiagram,
|
||||
WorkflowDiagramNode,
|
||||
} from '@/workflow/types/WorkflowDiagram';
|
||||
|
||||
const nodePropertiesToPreserve: Array<keyof WorkflowDiagramNode> = ['selected'];
|
||||
|
||||
export const mergeWorkflowDiagrams = (
|
||||
previousDiagram: WorkflowDiagram,
|
||||
nextDiagram: WorkflowDiagram,
|
||||
): WorkflowDiagram => {
|
||||
const lastNodes = nextDiagram.nodes.map((nextNode) => {
|
||||
const previousNode = previousDiagram.nodes.find(
|
||||
(previousNode) => previousNode.id === nextNode.id,
|
||||
);
|
||||
|
||||
const nodeWithPreservedProperties = nodePropertiesToPreserve.reduce(
|
||||
(nodeToSet, propertyToPreserve) => {
|
||||
return Object.assign(nodeToSet, {
|
||||
[propertyToPreserve]: previousNode?.[propertyToPreserve],
|
||||
});
|
||||
},
|
||||
{} as Partial<WorkflowDiagramNode>,
|
||||
);
|
||||
|
||||
return Object.assign(nodeWithPreservedProperties, nextNode);
|
||||
});
|
||||
|
||||
return {
|
||||
nodes: lastNodes,
|
||||
edges: nextDiagram.edges,
|
||||
};
|
||||
};
|
||||
@ -1,14 +1,14 @@
|
||||
import { WorkflowStep } from '@/workflow/types/Workflow';
|
||||
import { findStepPositionOrThrow } from '@/workflow/utils/findStepPositionOrThrow';
|
||||
|
||||
export const replaceStep = ({
|
||||
export const replaceStep = <T extends WorkflowStep>({
|
||||
steps: stepsInitial,
|
||||
stepId,
|
||||
stepToReplace,
|
||||
}: {
|
||||
steps: Array<WorkflowStep>;
|
||||
stepId: string;
|
||||
stepToReplace: Partial<Omit<WorkflowStep, 'id'>>;
|
||||
stepToReplace: Partial<Omit<T, 'id'>>;
|
||||
}) => {
|
||||
const steps = structuredClone(stepsInitial);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user