Migrate update step changes to diff (#14663)
This PR should fix optimistic rendering issues on step updates: - compute a diff for trigger and steps on mutations - build an util that applies that diff (built a more robust version from https://github.com/AsyncBanana/micropatch) - apply diff in cache
This commit is contained in:
+183
-16
@@ -1,24 +1,191 @@
|
||||
import { computeWorkflowVersionStepChanges } from 'src/modules/workflow/workflow-builder/utils/compute-workflow-version-step-updates.util';
|
||||
import { type WorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
|
||||
import { type WorkflowTrigger } from 'src/modules/workflow/workflow-trigger/types/workflow-trigger.type';
|
||||
import {
|
||||
type WorkflowAction,
|
||||
WorkflowActionType,
|
||||
} from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
|
||||
import {
|
||||
type WorkflowTrigger,
|
||||
WorkflowTriggerType,
|
||||
} from 'src/modules/workflow/workflow-trigger/types/workflow-trigger.type';
|
||||
|
||||
describe('computeWorkflowVersionStepChanges', () => {
|
||||
it('should compute next step ids', () => {
|
||||
const input = {
|
||||
trigger: { nextStepIds: ['1', '2'] } as WorkflowTrigger,
|
||||
steps: [
|
||||
{ id: '1', nextStepIds: ['3'] },
|
||||
{ id: '2', nextStepIds: ['3'] },
|
||||
] as WorkflowAction[],
|
||||
deletedStepIds: ['5'],
|
||||
const mockTrigger: WorkflowTrigger = {
|
||||
name: 'Test Manual Trigger',
|
||||
type: WorkflowTriggerType.MANUAL,
|
||||
settings: {
|
||||
input: {},
|
||||
outputSchema: {},
|
||||
},
|
||||
nextStepIds: ['step-1'],
|
||||
};
|
||||
|
||||
const mockSteps: WorkflowAction[] = [
|
||||
{
|
||||
id: 'step-1',
|
||||
name: 'Form Step',
|
||||
type: WorkflowActionType.FORM,
|
||||
settings: {
|
||||
input: [],
|
||||
errorHandlingOptions: {
|
||||
continueOnFailure: { value: false },
|
||||
retryOnFailure: { value: false },
|
||||
},
|
||||
outputSchema: {},
|
||||
},
|
||||
valid: true,
|
||||
nextStepIds: ['step-2'],
|
||||
},
|
||||
{
|
||||
id: 'step-2',
|
||||
name: 'Email Step',
|
||||
type: WorkflowActionType.SEND_EMAIL,
|
||||
settings: {
|
||||
input: {
|
||||
connectedAccountId: '',
|
||||
email: '',
|
||||
subject: '',
|
||||
body: '',
|
||||
},
|
||||
errorHandlingOptions: {
|
||||
continueOnFailure: { value: false },
|
||||
retryOnFailure: { value: false },
|
||||
},
|
||||
outputSchema: {},
|
||||
},
|
||||
valid: true,
|
||||
nextStepIds: [],
|
||||
},
|
||||
];
|
||||
|
||||
it('should compute trigger diff when trigger is updated', () => {
|
||||
const existingTrigger = mockTrigger;
|
||||
const updatedTrigger = {
|
||||
...mockTrigger,
|
||||
nextStepIds: ['step-1', 'step-3'],
|
||||
};
|
||||
|
||||
const expectedResult = {
|
||||
triggerNextStepIds: ['1', '2'],
|
||||
stepsNextStepIds: { '1': ['3'], '2': ['3'] },
|
||||
deletedStepIds: ['5'],
|
||||
};
|
||||
const result = computeWorkflowVersionStepChanges({
|
||||
existingTrigger,
|
||||
existingSteps: mockSteps,
|
||||
updatedTrigger,
|
||||
updatedSteps: mockSteps,
|
||||
});
|
||||
|
||||
expect(computeWorkflowVersionStepChanges(input)).toEqual(expectedResult);
|
||||
expect(result.triggerDiff).toMatchObject([
|
||||
{ path: ['trigger', 'nextStepIds', 1], type: 'CREATE', value: 'step-3' },
|
||||
]);
|
||||
expect(result.stepsDiff).toMatchObject([]);
|
||||
expect(result.stepsDiff.length).toBe(0); // No steps changed
|
||||
|
||||
// Verify the trigger diff contains the nextStepIds change
|
||||
const nextStepIdsDiff = result.triggerDiff.find((diff) =>
|
||||
diff.path.includes('nextStepIds'),
|
||||
);
|
||||
|
||||
expect(nextStepIdsDiff).toMatchObject({
|
||||
type: 'CREATE',
|
||||
path: ['trigger', 'nextStepIds', 1],
|
||||
value: 'step-3',
|
||||
});
|
||||
});
|
||||
|
||||
it('should compute steps diff when steps are updated', () => {
|
||||
const existingSteps = mockSteps;
|
||||
const updatedSteps = [
|
||||
...mockSteps,
|
||||
{
|
||||
id: 'step-3',
|
||||
name: 'Code Step',
|
||||
type: WorkflowActionType.CODE,
|
||||
settings: {
|
||||
input: {
|
||||
serverlessFunctionId: '',
|
||||
serverlessFunctionVersion: '',
|
||||
serverlessFunctionInput: {},
|
||||
},
|
||||
errorHandlingOptions: {
|
||||
continueOnFailure: { value: false },
|
||||
retryOnFailure: { value: false },
|
||||
},
|
||||
outputSchema: {},
|
||||
},
|
||||
valid: true,
|
||||
nextStepIds: [],
|
||||
} as WorkflowAction,
|
||||
];
|
||||
|
||||
const result = computeWorkflowVersionStepChanges({
|
||||
existingTrigger: mockTrigger,
|
||||
existingSteps,
|
||||
updatedTrigger: mockTrigger,
|
||||
updatedSteps,
|
||||
});
|
||||
|
||||
expect(result.stepsDiff).toBeDefined();
|
||||
expect(result.stepsDiff.length).toBeGreaterThan(0);
|
||||
expect(result.triggerDiff).toBeDefined();
|
||||
expect(result.triggerDiff.length).toBe(0); // No trigger changed
|
||||
|
||||
// Verify the steps diff contains the new step
|
||||
const createDiff = result.stepsDiff.find((diff) => diff.type === 'CREATE');
|
||||
|
||||
expect(createDiff).toBeDefined();
|
||||
});
|
||||
|
||||
it('should return empty diffs when no changes are made', () => {
|
||||
const result = computeWorkflowVersionStepChanges({
|
||||
existingTrigger: mockTrigger,
|
||||
existingSteps: mockSteps,
|
||||
updatedTrigger: mockTrigger,
|
||||
updatedSteps: mockSteps,
|
||||
});
|
||||
|
||||
expect(result.triggerDiff).toBeDefined();
|
||||
expect(result.triggerDiff.length).toBe(0);
|
||||
expect(result.stepsDiff).toBeDefined();
|
||||
expect(result.stepsDiff.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should handle null existing trigger and steps', () => {
|
||||
const updatedTrigger = mockTrigger;
|
||||
const updatedSteps = mockSteps;
|
||||
|
||||
const result = computeWorkflowVersionStepChanges({
|
||||
existingTrigger: null,
|
||||
existingSteps: null,
|
||||
updatedTrigger,
|
||||
updatedSteps,
|
||||
});
|
||||
|
||||
expect(result.triggerDiff).toBeDefined();
|
||||
expect(result.triggerDiff.length).toBeGreaterThan(0);
|
||||
expect(result.stepsDiff).toBeDefined();
|
||||
expect(result.stepsDiff.length).toBeGreaterThan(0);
|
||||
|
||||
// Verify change diffs are present
|
||||
const triggerChangeDiff = result.triggerDiff.find(
|
||||
(diff) => diff.type === 'CHANGE',
|
||||
);
|
||||
|
||||
expect(triggerChangeDiff).toBeDefined();
|
||||
|
||||
const stepsChangeDiff = result.stepsDiff.find(
|
||||
(diff) => diff.type === 'CHANGE',
|
||||
);
|
||||
|
||||
expect(stepsChangeDiff).toBeDefined();
|
||||
});
|
||||
|
||||
it('should return empty diffs when updated values are undefined', () => {
|
||||
const result = computeWorkflowVersionStepChanges({
|
||||
existingTrigger: mockTrigger,
|
||||
existingSteps: mockSteps,
|
||||
// updatedTrigger and updatedSteps are undefined
|
||||
});
|
||||
|
||||
expect(result.triggerDiff).toBeDefined();
|
||||
expect(result.triggerDiff.length).toBe(0);
|
||||
expect(result.stepsDiff).toBeDefined();
|
||||
expect(result.stepsDiff.length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
+22
-18
@@ -1,24 +1,28 @@
|
||||
import { type WorkflowVersionStepChangesDTO } from 'src/engine/core-modules/workflow/dtos/workflow-version-step-changes.dto';
|
||||
import { isDefined } from 'class-validator';
|
||||
import diff, { type Difference } from 'microdiff';
|
||||
|
||||
import { type WorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
|
||||
import { type WorkflowTrigger } from 'src/modules/workflow/workflow-trigger/types/workflow-trigger.type';
|
||||
|
||||
export const computeWorkflowVersionStepChanges = ({
|
||||
trigger,
|
||||
steps,
|
||||
createdStep,
|
||||
deletedStepIds,
|
||||
existingTrigger,
|
||||
existingSteps,
|
||||
updatedTrigger,
|
||||
updatedSteps,
|
||||
}: {
|
||||
trigger: WorkflowTrigger | null;
|
||||
steps: WorkflowAction[] | null;
|
||||
createdStep?: WorkflowAction;
|
||||
deletedStepIds?: string[];
|
||||
}): WorkflowVersionStepChangesDTO => {
|
||||
return {
|
||||
triggerNextStepIds: trigger?.nextStepIds,
|
||||
stepsNextStepIds: Object.fromEntries(
|
||||
(steps || []).map((step) => [step.id, step.nextStepIds]),
|
||||
),
|
||||
createdStep,
|
||||
deletedStepIds,
|
||||
};
|
||||
existingTrigger: WorkflowTrigger | null;
|
||||
existingSteps: WorkflowAction[] | null;
|
||||
updatedTrigger?: WorkflowTrigger | null;
|
||||
updatedSteps?: WorkflowAction[] | null;
|
||||
}) => {
|
||||
const triggerDiff: Difference[] =
|
||||
updatedTrigger !== undefined
|
||||
? diff({ trigger: existingTrigger }, { trigger: updatedTrigger })
|
||||
: [];
|
||||
|
||||
const stepsDiff: Difference[] = isDefined(updatedSteps)
|
||||
? diff({ steps: existingSteps }, { steps: updatedSteps })
|
||||
: [];
|
||||
|
||||
return { triggerDiff, stepsDiff };
|
||||
};
|
||||
|
||||
+103
-90
@@ -181,12 +181,14 @@ describe('WorkflowVersionEdgeWorkspaceService', () => {
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
triggerNextStepIds: ['step-1', 'step-3'],
|
||||
stepsNextStepIds: {
|
||||
'step-1': ['step-2'],
|
||||
'step-2': [],
|
||||
'step-3': [],
|
||||
},
|
||||
triggerDiff: [
|
||||
{
|
||||
path: ['trigger', 'nextStepIds', 1],
|
||||
type: 'CREATE',
|
||||
value: 'step-3',
|
||||
},
|
||||
],
|
||||
stepsDiff: [],
|
||||
});
|
||||
});
|
||||
|
||||
@@ -203,12 +205,8 @@ describe('WorkflowVersionEdgeWorkspaceService', () => {
|
||||
).not.toHaveBeenCalled();
|
||||
|
||||
expect(result).toEqual({
|
||||
triggerNextStepIds: ['step-1'],
|
||||
stepsNextStepIds: {
|
||||
'step-1': ['step-2'],
|
||||
'step-2': [],
|
||||
'step-3': [],
|
||||
},
|
||||
triggerDiff: [],
|
||||
stepsDiff: [],
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -272,13 +270,21 @@ describe('WorkflowVersionEdgeWorkspaceService', () => {
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
triggerNextStepIds: ['step-1'],
|
||||
stepsNextStepIds: {
|
||||
'step-1': ['step-2'],
|
||||
'step-2': [],
|
||||
'step-3': [],
|
||||
'iterator-step': ['step-2'],
|
||||
},
|
||||
stepsDiff: [
|
||||
{
|
||||
path: [
|
||||
'steps',
|
||||
3,
|
||||
'settings',
|
||||
'input',
|
||||
'initialLoopStepIds',
|
||||
1,
|
||||
],
|
||||
type: 'CREATE',
|
||||
value: 'step-3',
|
||||
},
|
||||
],
|
||||
triggerDiff: [],
|
||||
});
|
||||
});
|
||||
|
||||
@@ -301,13 +307,8 @@ describe('WorkflowVersionEdgeWorkspaceService', () => {
|
||||
).not.toHaveBeenCalled();
|
||||
|
||||
expect(result).toEqual({
|
||||
triggerNextStepIds: ['step-1'],
|
||||
stepsNextStepIds: {
|
||||
'step-1': ['step-2'],
|
||||
'step-2': [],
|
||||
'step-3': [],
|
||||
'iterator-step': ['step-2'],
|
||||
},
|
||||
stepsDiff: [],
|
||||
triggerDiff: [],
|
||||
});
|
||||
});
|
||||
|
||||
@@ -357,13 +358,14 @@ describe('WorkflowVersionEdgeWorkspaceService', () => {
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
triggerNextStepIds: ['step-1'],
|
||||
stepsNextStepIds: {
|
||||
'step-1': ['step-2'],
|
||||
'step-2': [],
|
||||
'step-3': [],
|
||||
'iterator-step': ['step-2', 'step-3'],
|
||||
},
|
||||
stepsDiff: [
|
||||
{
|
||||
path: ['steps', 3, 'nextStepIds', 1],
|
||||
type: 'CREATE',
|
||||
value: 'step-3',
|
||||
},
|
||||
],
|
||||
triggerDiff: [],
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -392,12 +394,14 @@ describe('WorkflowVersionEdgeWorkspaceService', () => {
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
triggerNextStepIds: ['step-1'],
|
||||
stepsNextStepIds: {
|
||||
'step-1': ['step-2'],
|
||||
'step-2': ['step-3'],
|
||||
'step-3': [],
|
||||
},
|
||||
stepsDiff: [
|
||||
{
|
||||
path: ['steps', 1, 'nextStepIds', 0],
|
||||
type: 'CREATE',
|
||||
value: 'step-3',
|
||||
},
|
||||
],
|
||||
triggerDiff: [],
|
||||
});
|
||||
});
|
||||
|
||||
@@ -414,12 +418,8 @@ describe('WorkflowVersionEdgeWorkspaceService', () => {
|
||||
).not.toHaveBeenCalled();
|
||||
|
||||
expect(result).toEqual({
|
||||
triggerNextStepIds: ['step-1'],
|
||||
stepsNextStepIds: {
|
||||
'step-1': ['step-2'],
|
||||
'step-2': [],
|
||||
'step-3': [],
|
||||
},
|
||||
stepsDiff: [],
|
||||
triggerDiff: [],
|
||||
});
|
||||
});
|
||||
|
||||
@@ -480,12 +480,14 @@ describe('WorkflowVersionEdgeWorkspaceService', () => {
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
triggerNextStepIds: [],
|
||||
stepsNextStepIds: {
|
||||
'step-1': ['step-2'],
|
||||
'step-2': [],
|
||||
'step-3': [],
|
||||
},
|
||||
triggerDiff: [
|
||||
{
|
||||
oldValue: 'step-1',
|
||||
path: ['trigger', 'nextStepIds', 0],
|
||||
type: 'REMOVE',
|
||||
},
|
||||
],
|
||||
stepsDiff: [],
|
||||
});
|
||||
});
|
||||
|
||||
@@ -509,12 +511,8 @@ describe('WorkflowVersionEdgeWorkspaceService', () => {
|
||||
).not.toHaveBeenCalled();
|
||||
|
||||
expect(result).toEqual({
|
||||
triggerNextStepIds: ['step-1'],
|
||||
stepsNextStepIds: {
|
||||
'step-1': ['step-2'],
|
||||
'step-2': [],
|
||||
'step-3': [],
|
||||
},
|
||||
stepsDiff: [],
|
||||
triggerDiff: [],
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -544,12 +542,14 @@ describe('WorkflowVersionEdgeWorkspaceService', () => {
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
triggerNextStepIds: ['step-1'],
|
||||
stepsNextStepIds: {
|
||||
'step-1': [],
|
||||
'step-2': [],
|
||||
'step-3': [],
|
||||
},
|
||||
stepsDiff: [
|
||||
{
|
||||
oldValue: 'step-2',
|
||||
path: ['steps', 0, 'nextStepIds', 0],
|
||||
type: 'REMOVE',
|
||||
},
|
||||
],
|
||||
triggerDiff: [],
|
||||
});
|
||||
});
|
||||
|
||||
@@ -566,12 +566,8 @@ describe('WorkflowVersionEdgeWorkspaceService', () => {
|
||||
).not.toHaveBeenCalledWith();
|
||||
|
||||
expect(result).toEqual({
|
||||
triggerNextStepIds: ['step-1'],
|
||||
stepsNextStepIds: {
|
||||
'step-1': ['step-2'],
|
||||
'step-2': [],
|
||||
'step-3': [],
|
||||
},
|
||||
stepsDiff: [],
|
||||
triggerDiff: [],
|
||||
});
|
||||
});
|
||||
|
||||
@@ -670,13 +666,34 @@ describe('WorkflowVersionEdgeWorkspaceService', () => {
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
triggerNextStepIds: ['step-1'],
|
||||
stepsNextStepIds: {
|
||||
'iterator-step': ['step-1'],
|
||||
'step-1': ['step-2'],
|
||||
'step-2': [],
|
||||
'step-3': [],
|
||||
},
|
||||
stepsDiff: [
|
||||
{
|
||||
oldValue: 'step-2',
|
||||
path: [
|
||||
'steps',
|
||||
0,
|
||||
'settings',
|
||||
'input',
|
||||
'initialLoopStepIds',
|
||||
0,
|
||||
],
|
||||
type: 'CHANGE',
|
||||
value: 'step-3',
|
||||
},
|
||||
{
|
||||
oldValue: 'step-3',
|
||||
path: [
|
||||
'steps',
|
||||
0,
|
||||
'settings',
|
||||
'input',
|
||||
'initialLoopStepIds',
|
||||
1,
|
||||
],
|
||||
type: 'REMOVE',
|
||||
},
|
||||
],
|
||||
triggerDiff: [],
|
||||
});
|
||||
});
|
||||
|
||||
@@ -699,13 +716,8 @@ describe('WorkflowVersionEdgeWorkspaceService', () => {
|
||||
).not.toHaveBeenCalled();
|
||||
|
||||
expect(result).toEqual({
|
||||
triggerNextStepIds: ['step-1'],
|
||||
stepsNextStepIds: {
|
||||
'iterator-step': ['step-1'],
|
||||
'step-1': ['step-2'],
|
||||
'step-2': [],
|
||||
'step-3': [],
|
||||
},
|
||||
stepsDiff: [],
|
||||
triggerDiff: [],
|
||||
});
|
||||
});
|
||||
|
||||
@@ -735,13 +747,14 @@ describe('WorkflowVersionEdgeWorkspaceService', () => {
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
triggerNextStepIds: ['step-1'],
|
||||
stepsNextStepIds: {
|
||||
'iterator-step': [],
|
||||
'step-1': ['step-2'],
|
||||
'step-2': [],
|
||||
'step-3': [],
|
||||
},
|
||||
stepsDiff: [
|
||||
{
|
||||
oldValue: 'step-1',
|
||||
path: ['steps', 0, 'nextStepIds', 0],
|
||||
type: 'REMOVE',
|
||||
},
|
||||
],
|
||||
triggerDiff: [],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+22
-18
@@ -176,8 +176,8 @@ export class WorkflowVersionEdgeWorkspaceService {
|
||||
|
||||
if (trigger.nextStepIds?.includes(target)) {
|
||||
return computeWorkflowVersionStepChanges({
|
||||
trigger,
|
||||
steps,
|
||||
existingTrigger: trigger,
|
||||
existingSteps: steps,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -191,8 +191,9 @@ export class WorkflowVersionEdgeWorkspaceService {
|
||||
});
|
||||
|
||||
return computeWorkflowVersionStepChanges({
|
||||
trigger: updatedTrigger,
|
||||
steps,
|
||||
existingTrigger: trigger,
|
||||
existingSteps: steps,
|
||||
updatedTrigger,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -227,8 +228,8 @@ export class WorkflowVersionEdgeWorkspaceService {
|
||||
!isDefined(sourceConnectionOptions)
|
||||
) {
|
||||
return computeWorkflowVersionStepChanges({
|
||||
trigger,
|
||||
steps,
|
||||
existingTrigger: trigger,
|
||||
existingSteps: steps,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -257,8 +258,9 @@ export class WorkflowVersionEdgeWorkspaceService {
|
||||
}
|
||||
|
||||
return computeWorkflowVersionStepChanges({
|
||||
trigger,
|
||||
steps: updatedSteps,
|
||||
existingTrigger: trigger,
|
||||
existingSteps: steps,
|
||||
updatedSteps,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -375,8 +377,8 @@ export class WorkflowVersionEdgeWorkspaceService {
|
||||
|
||||
if (!trigger.nextStepIds?.includes(target)) {
|
||||
return computeWorkflowVersionStepChanges({
|
||||
trigger,
|
||||
steps,
|
||||
existingTrigger: trigger,
|
||||
existingSteps: steps,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -392,8 +394,9 @@ export class WorkflowVersionEdgeWorkspaceService {
|
||||
});
|
||||
|
||||
return computeWorkflowVersionStepChanges({
|
||||
trigger: updatedTrigger,
|
||||
steps,
|
||||
existingTrigger: trigger,
|
||||
existingSteps: steps,
|
||||
updatedTrigger,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -428,8 +431,8 @@ export class WorkflowVersionEdgeWorkspaceService {
|
||||
!isDefined(sourceConnectionOptions)
|
||||
) {
|
||||
return computeWorkflowVersionStepChanges({
|
||||
trigger,
|
||||
steps,
|
||||
existingTrigger: trigger,
|
||||
existingSteps: steps,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -453,8 +456,8 @@ export class WorkflowVersionEdgeWorkspaceService {
|
||||
|
||||
if (!shouldPersist) {
|
||||
return computeWorkflowVersionStepChanges({
|
||||
trigger,
|
||||
steps,
|
||||
existingTrigger: trigger,
|
||||
existingSteps: steps,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -471,8 +474,9 @@ export class WorkflowVersionEdgeWorkspaceService {
|
||||
});
|
||||
|
||||
return computeWorkflowVersionStepChanges({
|
||||
trigger,
|
||||
steps: updatedSteps,
|
||||
existingTrigger: trigger,
|
||||
existingSteps: steps,
|
||||
updatedSteps,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -191,8 +191,8 @@ describe('WorkflowVersionStepOperationsWorkspaceService', () => {
|
||||
workflowVersionId: 'workflow-version-id',
|
||||
});
|
||||
|
||||
expect(result.type).toBe(WorkflowActionType.CODE);
|
||||
const codeResult = result as unknown as {
|
||||
expect(result.builtStep.type).toBe(WorkflowActionType.CODE);
|
||||
const codeResult = result.builtStep as unknown as {
|
||||
settings: {
|
||||
input: {
|
||||
serverlessFunctionId: string;
|
||||
@@ -214,8 +214,8 @@ describe('WorkflowVersionStepOperationsWorkspaceService', () => {
|
||||
workflowVersionId: 'workflow-version-id',
|
||||
});
|
||||
|
||||
expect(result.type).toBe(WorkflowActionType.FORM);
|
||||
expect(result.settings.input).toEqual([]);
|
||||
expect(result.builtStep.type).toBe(WorkflowActionType.FORM);
|
||||
expect(result.builtStep.settings.input).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+133
-72
@@ -15,6 +15,13 @@ import {
|
||||
} from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
|
||||
import { WorkflowTriggerType } from 'src/modules/workflow/workflow-trigger/types/workflow-trigger.type';
|
||||
|
||||
jest.mock(
|
||||
'src/modules/workflow/workflow-builder/utils/compute-workflow-version-step-updates.util',
|
||||
() => ({
|
||||
computeWorkflowVersionStepChanges: jest.fn(),
|
||||
}),
|
||||
);
|
||||
|
||||
type MockWorkspaceRepository = Partial<
|
||||
WorkspaceRepository<WorkflowVersionWorkspaceEntity>
|
||||
> & {
|
||||
@@ -78,8 +85,16 @@ describe('WorkflowVersionStepWorkspaceService', () => {
|
||||
let twentyORMGlobalManager: jest.Mocked<TwentyORMGlobalManager>;
|
||||
let service: WorkflowVersionStepWorkspaceService;
|
||||
let mockWorkflowVersionWorkspaceRepository: MockWorkspaceRepository;
|
||||
let mockComputeWorkflowVersionStepChanges: jest.Mock;
|
||||
|
||||
beforeEach(async () => {
|
||||
const {
|
||||
computeWorkflowVersionStepChanges,
|
||||
} = require('src/modules/workflow/workflow-builder/utils/compute-workflow-version-step-updates.util');
|
||||
|
||||
mockComputeWorkflowVersionStepChanges =
|
||||
computeWorkflowVersionStepChanges as jest.Mock;
|
||||
|
||||
mockWorkflowVersionWorkspaceRepository = {
|
||||
findOne: jest.fn(),
|
||||
update: jest.fn(),
|
||||
@@ -116,10 +131,13 @@ describe('WorkflowVersionStepWorkspaceService', () => {
|
||||
runStepCreationSideEffectsAndBuildStep: jest
|
||||
.fn()
|
||||
.mockImplementation(({ type }) => ({
|
||||
id: 'new-step-id',
|
||||
type,
|
||||
settings: {},
|
||||
nextStepIds: [],
|
||||
builtStep: {
|
||||
id: 'new-step-id',
|
||||
type,
|
||||
settings: {},
|
||||
nextStepIds: [],
|
||||
},
|
||||
additionalCreatedSteps: [],
|
||||
})),
|
||||
runWorkflowVersionStepDeletionSideEffects: jest.fn(),
|
||||
},
|
||||
@@ -140,6 +158,25 @@ describe('WorkflowVersionStepWorkspaceService', () => {
|
||||
|
||||
describe('createWorkflowVersionStep', () => {
|
||||
it('should create a step linked to trigger', async () => {
|
||||
const mockChanges = {
|
||||
triggerDiff: [
|
||||
{
|
||||
type: 'CHANGE',
|
||||
path: ['nextStepIds'],
|
||||
value: ['step-1', 'new-step-id'],
|
||||
},
|
||||
],
|
||||
stepsDiff: [
|
||||
{
|
||||
type: 'CREATE',
|
||||
path: [],
|
||||
value: { id: 'new-step-id', type: 'FORM' },
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
mockComputeWorkflowVersionStepChanges.mockReturnValue(mockChanges);
|
||||
|
||||
const result = await service.createWorkflowVersionStep({
|
||||
input: {
|
||||
stepType: WorkflowActionType.FORM,
|
||||
@@ -151,20 +188,33 @@ describe('WorkflowVersionStepWorkspaceService', () => {
|
||||
});
|
||||
|
||||
expect(mockWorkflowVersionWorkspaceRepository.update).toHaveBeenCalled();
|
||||
expect(mockComputeWorkflowVersionStepChanges).toHaveBeenCalled();
|
||||
|
||||
expect(result.createdStep).toBeDefined();
|
||||
|
||||
const createdStepId = result.createdStep?.id;
|
||||
|
||||
expect(result.triggerNextStepIds).toEqual(['step-1', createdStepId]);
|
||||
expect(result.stepsNextStepIds).toEqual({
|
||||
'step-1': ['step-2'],
|
||||
'step-2': [],
|
||||
'step-3': [],
|
||||
});
|
||||
expect(result).toEqual(mockChanges);
|
||||
expect(result.triggerDiff).toBeDefined();
|
||||
expect(result.stepsDiff).toBeDefined();
|
||||
});
|
||||
|
||||
it('should create a step between a trigger and a step', async () => {
|
||||
const mockChanges = {
|
||||
triggerDiff: [
|
||||
{ type: 'CHANGE', path: ['nextStepIds'], value: ['new-step-id'] },
|
||||
],
|
||||
stepsDiff: [
|
||||
{
|
||||
type: 'CREATE',
|
||||
path: [],
|
||||
value: {
|
||||
id: 'new-step-id',
|
||||
type: 'FORM',
|
||||
nextStepIds: ['step-1'],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
mockComputeWorkflowVersionStepChanges.mockReturnValue(mockChanges);
|
||||
|
||||
const result = await service.createWorkflowVersionStep({
|
||||
input: {
|
||||
stepType: WorkflowActionType.FORM,
|
||||
@@ -176,21 +226,32 @@ describe('WorkflowVersionStepWorkspaceService', () => {
|
||||
});
|
||||
|
||||
expect(mockWorkflowVersionWorkspaceRepository.update).toHaveBeenCalled();
|
||||
expect(mockComputeWorkflowVersionStepChanges).toHaveBeenCalled();
|
||||
|
||||
expect(result.createdStep).toBeDefined();
|
||||
|
||||
const createdStepId = result.createdStep?.id as string;
|
||||
|
||||
expect(result.triggerNextStepIds).toEqual([createdStepId]);
|
||||
expect(result.stepsNextStepIds).toEqual({
|
||||
[createdStepId]: ['step-1'],
|
||||
'step-1': ['step-2'],
|
||||
'step-2': [],
|
||||
'step-3': [],
|
||||
});
|
||||
expect(result).toEqual(mockChanges);
|
||||
expect(result.triggerDiff).toBeDefined();
|
||||
expect(result.stepsDiff).toBeDefined();
|
||||
});
|
||||
|
||||
it('should create a step between two steps', async () => {
|
||||
const mockChanges = {
|
||||
triggerDiff: [],
|
||||
stepsDiff: [
|
||||
{ type: 'CHANGE', path: [0, 'nextStepIds'], value: ['new-step-id'] },
|
||||
{
|
||||
type: 'CREATE',
|
||||
path: [],
|
||||
value: {
|
||||
id: 'new-step-id',
|
||||
type: 'FORM',
|
||||
nextStepIds: ['step-2'],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
mockComputeWorkflowVersionStepChanges.mockReturnValue(mockChanges);
|
||||
|
||||
const result = await service.createWorkflowVersionStep({
|
||||
input: {
|
||||
stepType: WorkflowActionType.FORM,
|
||||
@@ -202,21 +263,27 @@ describe('WorkflowVersionStepWorkspaceService', () => {
|
||||
});
|
||||
|
||||
expect(mockWorkflowVersionWorkspaceRepository.update).toHaveBeenCalled();
|
||||
expect(mockComputeWorkflowVersionStepChanges).toHaveBeenCalled();
|
||||
|
||||
expect(result.createdStep).toBeDefined();
|
||||
|
||||
const createdStepId = result.createdStep?.id as string;
|
||||
|
||||
expect(result.triggerNextStepIds).toEqual(['step-1']);
|
||||
expect(result.stepsNextStepIds).toEqual({
|
||||
'step-1': [createdStepId],
|
||||
[createdStepId]: ['step-2'],
|
||||
'step-2': [],
|
||||
'step-3': [],
|
||||
});
|
||||
expect(result).toEqual(mockChanges);
|
||||
expect(result.triggerDiff).toBeDefined();
|
||||
expect(result.stepsDiff).toBeDefined();
|
||||
});
|
||||
|
||||
it('should create a step without parent or children', async () => {
|
||||
const mockChanges = {
|
||||
triggerDiff: [],
|
||||
stepsDiff: [
|
||||
{
|
||||
type: 'CREATE',
|
||||
path: [],
|
||||
value: { id: 'new-step-id', type: 'FORM' },
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
mockComputeWorkflowVersionStepChanges.mockReturnValue(mockChanges);
|
||||
|
||||
const result = await service.createWorkflowVersionStep({
|
||||
input: {
|
||||
stepType: WorkflowActionType.FORM,
|
||||
@@ -228,65 +295,59 @@ describe('WorkflowVersionStepWorkspaceService', () => {
|
||||
});
|
||||
|
||||
expect(mockWorkflowVersionWorkspaceRepository.update).toHaveBeenCalled();
|
||||
expect(mockComputeWorkflowVersionStepChanges).toHaveBeenCalled();
|
||||
|
||||
expect(result.createdStep).toBeDefined();
|
||||
|
||||
expect(result.triggerNextStepIds).toEqual(['step-1']);
|
||||
expect(result.stepsNextStepIds).toEqual({
|
||||
'step-1': ['step-2'],
|
||||
'step-2': [],
|
||||
'step-3': [],
|
||||
});
|
||||
expect(result).toEqual(mockChanges);
|
||||
expect(result.triggerDiff).toBeDefined();
|
||||
expect(result.stepsDiff).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('deleteWorkflowVersionStep', () => {
|
||||
it('should delete step linked to trigger', async () => {
|
||||
const mockChanges = {
|
||||
triggerDiff: [
|
||||
{ type: 'CHANGE', path: ['nextStepIds'], value: ['step-2'] },
|
||||
],
|
||||
stepsDiff: [{ type: 'REMOVE', path: [0] }],
|
||||
};
|
||||
|
||||
mockComputeWorkflowVersionStepChanges.mockReturnValue(mockChanges);
|
||||
|
||||
const result = await service.deleteWorkflowVersionStep({
|
||||
stepIdToDelete: 'step-1',
|
||||
workflowVersionId: mockWorkflowVersionId,
|
||||
workspaceId: mockWorkspaceId,
|
||||
});
|
||||
|
||||
expect(
|
||||
mockWorkflowVersionWorkspaceRepository.update,
|
||||
).toHaveBeenCalledWith(mockWorkflowVersionId, {
|
||||
trigger: { ...mockTrigger, nextStepIds: ['step-2'] },
|
||||
steps: mockSteps.filter((step) => step.id !== 'step-1'),
|
||||
});
|
||||
expect(mockWorkflowVersionWorkspaceRepository.update).toHaveBeenCalled();
|
||||
expect(mockComputeWorkflowVersionStepChanges).toHaveBeenCalled();
|
||||
|
||||
expect(result).toEqual({
|
||||
triggerNextStepIds: ['step-2'],
|
||||
stepsNextStepIds: {
|
||||
'step-2': [],
|
||||
'step-3': [],
|
||||
},
|
||||
deletedStepIds: ['step-1'],
|
||||
});
|
||||
expect(result).toEqual(mockChanges);
|
||||
expect(result.triggerDiff).toBeDefined();
|
||||
expect(result.stepsDiff).toBeDefined();
|
||||
});
|
||||
|
||||
it('should delete trigger', async () => {
|
||||
const mockChanges = {
|
||||
triggerDiff: [{ type: 'REMOVE', path: [] }],
|
||||
stepsDiff: [],
|
||||
};
|
||||
|
||||
mockComputeWorkflowVersionStepChanges.mockReturnValue(mockChanges);
|
||||
|
||||
const result = await service.deleteWorkflowVersionStep({
|
||||
stepIdToDelete: TRIGGER_STEP_ID,
|
||||
workflowVersionId: mockWorkflowVersionId,
|
||||
workspaceId: mockWorkspaceId,
|
||||
});
|
||||
|
||||
expect(
|
||||
mockWorkflowVersionWorkspaceRepository.update,
|
||||
).toHaveBeenCalledWith(mockWorkflowVersionId, {
|
||||
trigger: null,
|
||||
steps: mockSteps,
|
||||
});
|
||||
expect(mockWorkflowVersionWorkspaceRepository.update).toHaveBeenCalled();
|
||||
expect(mockComputeWorkflowVersionStepChanges).toHaveBeenCalled();
|
||||
|
||||
expect(result).toEqual({
|
||||
stepsNextStepIds: {
|
||||
'step-1': ['step-2'],
|
||||
'step-2': [],
|
||||
'step-3': [],
|
||||
},
|
||||
deletedStepIds: [TRIGGER_STEP_ID],
|
||||
});
|
||||
expect(result).toEqual(mockChanges);
|
||||
expect(result.triggerDiff).toBeDefined();
|
||||
expect(result.stepsDiff).toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+131
-105
@@ -101,16 +101,19 @@ export class WorkflowVersionStepOperationsWorkspaceService {
|
||||
workspaceId,
|
||||
workflowVersionId,
|
||||
position,
|
||||
id,
|
||||
}: {
|
||||
type: WorkflowActionType;
|
||||
workspaceId: string;
|
||||
workflowVersionId: string;
|
||||
position?: WorkflowStepPositionInput;
|
||||
}): Promise<WorkflowAction> {
|
||||
const newStepId = v4();
|
||||
|
||||
id?: string;
|
||||
}): Promise<{
|
||||
builtStep: WorkflowAction;
|
||||
additionalCreatedSteps?: WorkflowAction[];
|
||||
}> {
|
||||
const baseStep = {
|
||||
id: newStepId,
|
||||
id: id || v4(),
|
||||
position,
|
||||
valid: false,
|
||||
nextStepIds: [],
|
||||
@@ -135,40 +138,44 @@ export class WorkflowVersionStepOperationsWorkspaceService {
|
||||
}
|
||||
|
||||
return {
|
||||
...baseStep,
|
||||
name: 'Code - Serverless Function',
|
||||
type: WorkflowActionType.CODE,
|
||||
settings: {
|
||||
...BASE_STEP_DEFINITION,
|
||||
outputSchema: {
|
||||
link: {
|
||||
isLeaf: true,
|
||||
icon: 'IconVariable',
|
||||
tab: 'test',
|
||||
label: 'Generate Function Output',
|
||||
builtStep: {
|
||||
...baseStep,
|
||||
name: 'Code - Serverless Function',
|
||||
type: WorkflowActionType.CODE,
|
||||
settings: {
|
||||
...BASE_STEP_DEFINITION,
|
||||
outputSchema: {
|
||||
link: {
|
||||
isLeaf: true,
|
||||
icon: 'IconVariable',
|
||||
tab: 'test',
|
||||
label: 'Generate Function Output',
|
||||
},
|
||||
_outputSchemaType: 'LINK',
|
||||
},
|
||||
input: {
|
||||
serverlessFunctionId: newServerlessFunction.id,
|
||||
serverlessFunctionVersion: 'draft',
|
||||
serverlessFunctionInput: BASE_TYPESCRIPT_PROJECT_INPUT_SCHEMA,
|
||||
},
|
||||
_outputSchemaType: 'LINK',
|
||||
},
|
||||
input: {
|
||||
serverlessFunctionId: newServerlessFunction.id,
|
||||
serverlessFunctionVersion: 'draft',
|
||||
serverlessFunctionInput: BASE_TYPESCRIPT_PROJECT_INPUT_SCHEMA,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
case WorkflowActionType.SEND_EMAIL: {
|
||||
return {
|
||||
...baseStep,
|
||||
name: 'Send Email',
|
||||
type: WorkflowActionType.SEND_EMAIL,
|
||||
settings: {
|
||||
...BASE_STEP_DEFINITION,
|
||||
input: {
|
||||
connectedAccountId: '',
|
||||
email: '',
|
||||
subject: '',
|
||||
body: '',
|
||||
builtStep: {
|
||||
...baseStep,
|
||||
name: 'Send Email',
|
||||
type: WorkflowActionType.SEND_EMAIL,
|
||||
settings: {
|
||||
...BASE_STEP_DEFINITION,
|
||||
input: {
|
||||
connectedAccountId: '',
|
||||
email: '',
|
||||
subject: '',
|
||||
body: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -180,14 +187,16 @@ export class WorkflowVersionStepOperationsWorkspaceService {
|
||||
});
|
||||
|
||||
return {
|
||||
...baseStep,
|
||||
name: 'Create Record',
|
||||
type: WorkflowActionType.CREATE_RECORD,
|
||||
settings: {
|
||||
...BASE_STEP_DEFINITION,
|
||||
input: {
|
||||
objectName: activeObjectMetadataItem?.nameSingular || '',
|
||||
objectRecord: {},
|
||||
builtStep: {
|
||||
...baseStep,
|
||||
name: 'Create Record',
|
||||
type: WorkflowActionType.CREATE_RECORD,
|
||||
settings: {
|
||||
...BASE_STEP_DEFINITION,
|
||||
input: {
|
||||
objectName: activeObjectMetadataItem?.nameSingular || '',
|
||||
objectRecord: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -199,16 +208,18 @@ export class WorkflowVersionStepOperationsWorkspaceService {
|
||||
});
|
||||
|
||||
return {
|
||||
...baseStep,
|
||||
name: 'Update Record',
|
||||
type: WorkflowActionType.UPDATE_RECORD,
|
||||
settings: {
|
||||
...BASE_STEP_DEFINITION,
|
||||
input: {
|
||||
objectName: activeObjectMetadataItem?.nameSingular || '',
|
||||
objectRecord: {},
|
||||
objectRecordId: '',
|
||||
fieldsToUpdate: [],
|
||||
builtStep: {
|
||||
...baseStep,
|
||||
name: 'Update Record',
|
||||
type: WorkflowActionType.UPDATE_RECORD,
|
||||
settings: {
|
||||
...BASE_STEP_DEFINITION,
|
||||
input: {
|
||||
objectName: activeObjectMetadataItem?.nameSingular || '',
|
||||
objectRecord: {},
|
||||
objectRecordId: '',
|
||||
fieldsToUpdate: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -220,14 +231,16 @@ export class WorkflowVersionStepOperationsWorkspaceService {
|
||||
});
|
||||
|
||||
return {
|
||||
...baseStep,
|
||||
name: 'Delete Record',
|
||||
type: WorkflowActionType.DELETE_RECORD,
|
||||
settings: {
|
||||
...BASE_STEP_DEFINITION,
|
||||
input: {
|
||||
objectName: activeObjectMetadataItem?.nameSingular || '',
|
||||
objectRecordId: '',
|
||||
builtStep: {
|
||||
...baseStep,
|
||||
name: 'Delete Record',
|
||||
type: WorkflowActionType.DELETE_RECORD,
|
||||
settings: {
|
||||
...BASE_STEP_DEFINITION,
|
||||
input: {
|
||||
objectName: activeObjectMetadataItem?.nameSingular || '',
|
||||
objectRecordId: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -239,69 +252,79 @@ export class WorkflowVersionStepOperationsWorkspaceService {
|
||||
});
|
||||
|
||||
return {
|
||||
...baseStep,
|
||||
name: 'Search Records',
|
||||
type: WorkflowActionType.FIND_RECORDS,
|
||||
settings: {
|
||||
...BASE_STEP_DEFINITION,
|
||||
input: {
|
||||
objectName: activeObjectMetadataItem?.nameSingular || '',
|
||||
limit: 1,
|
||||
builtStep: {
|
||||
...baseStep,
|
||||
name: 'Search Records',
|
||||
type: WorkflowActionType.FIND_RECORDS,
|
||||
settings: {
|
||||
...BASE_STEP_DEFINITION,
|
||||
input: {
|
||||
objectName: activeObjectMetadataItem?.nameSingular || '',
|
||||
limit: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
case WorkflowActionType.FORM: {
|
||||
return {
|
||||
...baseStep,
|
||||
name: 'Form',
|
||||
type: WorkflowActionType.FORM,
|
||||
settings: {
|
||||
...BASE_STEP_DEFINITION,
|
||||
input: [],
|
||||
builtStep: {
|
||||
...baseStep,
|
||||
name: 'Form',
|
||||
type: WorkflowActionType.FORM,
|
||||
settings: {
|
||||
...BASE_STEP_DEFINITION,
|
||||
input: [],
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
case WorkflowActionType.FILTER: {
|
||||
return {
|
||||
...baseStep,
|
||||
name: 'Filter',
|
||||
type: WorkflowActionType.FILTER,
|
||||
settings: {
|
||||
...BASE_STEP_DEFINITION,
|
||||
input: {
|
||||
stepFilterGroups: [],
|
||||
stepFilters: [],
|
||||
builtStep: {
|
||||
...baseStep,
|
||||
name: 'Filter',
|
||||
type: WorkflowActionType.FILTER,
|
||||
settings: {
|
||||
...BASE_STEP_DEFINITION,
|
||||
input: {
|
||||
stepFilterGroups: [],
|
||||
stepFilters: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
case WorkflowActionType.HTTP_REQUEST: {
|
||||
return {
|
||||
...baseStep,
|
||||
name: 'HTTP Request',
|
||||
type: WorkflowActionType.HTTP_REQUEST,
|
||||
settings: {
|
||||
...BASE_STEP_DEFINITION,
|
||||
input: {
|
||||
url: '',
|
||||
method: 'GET',
|
||||
headers: {},
|
||||
body: {},
|
||||
builtStep: {
|
||||
...baseStep,
|
||||
name: 'HTTP Request',
|
||||
type: WorkflowActionType.HTTP_REQUEST,
|
||||
settings: {
|
||||
...BASE_STEP_DEFINITION,
|
||||
input: {
|
||||
url: '',
|
||||
method: 'GET',
|
||||
headers: {},
|
||||
body: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
case WorkflowActionType.AI_AGENT: {
|
||||
return {
|
||||
...baseStep,
|
||||
name: 'AI Agent',
|
||||
type: WorkflowActionType.AI_AGENT,
|
||||
settings: {
|
||||
...BASE_STEP_DEFINITION,
|
||||
input: {
|
||||
agentId: '',
|
||||
prompt: '',
|
||||
builtStep: {
|
||||
...baseStep,
|
||||
name: 'AI Agent',
|
||||
type: WorkflowActionType.AI_AGENT,
|
||||
settings: {
|
||||
...BASE_STEP_DEFINITION,
|
||||
input: {
|
||||
agentId: '',
|
||||
prompt: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -315,16 +338,19 @@ export class WorkflowVersionStepOperationsWorkspaceService {
|
||||
});
|
||||
|
||||
return {
|
||||
...baseStep,
|
||||
name: 'Iterator',
|
||||
type: WorkflowActionType.ITERATOR,
|
||||
settings: {
|
||||
...BASE_STEP_DEFINITION,
|
||||
input: {
|
||||
items: [],
|
||||
initialLoopStepIds: [emptyNodeStep.id],
|
||||
builtStep: {
|
||||
...baseStep,
|
||||
name: 'Iterator',
|
||||
type: WorkflowActionType.ITERATOR,
|
||||
settings: {
|
||||
...BASE_STEP_DEFINITION,
|
||||
input: {
|
||||
items: [],
|
||||
initialLoopStepIds: [emptyNodeStep.id],
|
||||
},
|
||||
},
|
||||
},
|
||||
additionalCreatedSteps: [emptyNodeStep],
|
||||
};
|
||||
}
|
||||
default:
|
||||
|
||||
+42
-32
@@ -4,6 +4,7 @@ import { isDefined } from 'twenty-shared/utils';
|
||||
import { TRIGGER_STEP_ID } from 'twenty-shared/workflow';
|
||||
|
||||
import { type CreateWorkflowVersionStepInput } from 'src/engine/core-modules/workflow/dtos/create-workflow-version-step-input.dto';
|
||||
import { WorkflowActionDTO } from 'src/engine/core-modules/workflow/dtos/workflow-action.dto';
|
||||
import { type WorkflowVersionStepChangesDTO } from 'src/engine/core-modules/workflow/dtos/workflow-version-step-changes.dto';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import {
|
||||
@@ -43,24 +44,9 @@ export class WorkflowVersionStepWorkspaceService {
|
||||
nextStepId,
|
||||
position,
|
||||
parentStepConnectionOptions,
|
||||
id,
|
||||
} = input;
|
||||
|
||||
const newStep =
|
||||
await this.workflowVersionStepOperationsWorkspaceService.runStepCreationSideEffectsAndBuildStep(
|
||||
{
|
||||
type: stepType,
|
||||
workspaceId,
|
||||
position,
|
||||
workflowVersionId,
|
||||
},
|
||||
);
|
||||
|
||||
const enrichedNewStep =
|
||||
await this.workflowSchemaWorkspaceService.enrichOutputSchema({
|
||||
step: newStep,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
const workflowVersion =
|
||||
await this.workflowCommonWorkspaceService.getWorkflowVersionOrFail({
|
||||
workflowVersionId,
|
||||
@@ -69,12 +55,29 @@ export class WorkflowVersionStepWorkspaceService {
|
||||
|
||||
assertWorkflowVersionIsDraft(workflowVersion);
|
||||
|
||||
const existingSteps = workflowVersion.steps || [];
|
||||
const existingSteps = workflowVersion.steps;
|
||||
|
||||
const existingTrigger = workflowVersion.trigger;
|
||||
|
||||
const { updatedSteps, updatedInsertedStep, updatedTrigger } = insertStep({
|
||||
existingSteps,
|
||||
const { builtStep, additionalCreatedSteps } =
|
||||
await this.workflowVersionStepOperationsWorkspaceService.runStepCreationSideEffectsAndBuildStep(
|
||||
{
|
||||
type: stepType,
|
||||
workspaceId,
|
||||
position,
|
||||
workflowVersionId,
|
||||
id,
|
||||
},
|
||||
);
|
||||
|
||||
const enrichedNewStep =
|
||||
await this.workflowSchemaWorkspaceService.enrichOutputSchema({
|
||||
step: builtStep,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
const { updatedSteps, updatedTrigger } = insertStep({
|
||||
existingSteps: existingSteps ?? [],
|
||||
existingTrigger,
|
||||
insertedStep: enrichedNewStep,
|
||||
parentStepId,
|
||||
@@ -82,6 +85,10 @@ export class WorkflowVersionStepWorkspaceService {
|
||||
parentStepConnectionOptions,
|
||||
});
|
||||
|
||||
if (isDefined(additionalCreatedSteps)) {
|
||||
updatedSteps.push(...additionalCreatedSteps);
|
||||
}
|
||||
|
||||
const workflowVersionRepository =
|
||||
await this.twentyORMGlobalManager.getRepositoryForWorkspace<WorkflowVersionWorkspaceEntity>(
|
||||
workspaceId,
|
||||
@@ -95,9 +102,10 @@ export class WorkflowVersionStepWorkspaceService {
|
||||
});
|
||||
|
||||
return computeWorkflowVersionStepChanges({
|
||||
createdStep: updatedInsertedStep,
|
||||
trigger: updatedTrigger,
|
||||
steps: updatedSteps,
|
||||
existingTrigger,
|
||||
existingSteps,
|
||||
updatedTrigger,
|
||||
updatedSteps,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -109,7 +117,7 @@ export class WorkflowVersionStepWorkspaceService {
|
||||
workspaceId: string;
|
||||
workflowVersionId: string;
|
||||
step: WorkflowAction;
|
||||
}): Promise<WorkflowAction> {
|
||||
}): Promise<WorkflowActionDTO> {
|
||||
const workflowVersion =
|
||||
await this.workflowCommonWorkspaceService.getWorkflowVersionOrFail({
|
||||
workflowVersionId,
|
||||
@@ -252,9 +260,10 @@ export class WorkflowVersionStepWorkspaceService {
|
||||
);
|
||||
|
||||
return computeWorkflowVersionStepChanges({
|
||||
steps: updatedSteps,
|
||||
trigger: updatedTrigger,
|
||||
deletedStepIds: removedStepIds,
|
||||
existingTrigger,
|
||||
existingSteps: workflowVersion.steps,
|
||||
updatedTrigger,
|
||||
updatedSteps,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -294,7 +303,7 @@ export class WorkflowVersionStepWorkspaceService {
|
||||
},
|
||||
);
|
||||
|
||||
const { updatedSteps, updatedInsertedStep, updatedTrigger } = insertStep({
|
||||
const { updatedSteps, updatedTrigger } = insertStep({
|
||||
existingSteps: workflowVersion.steps ?? [],
|
||||
existingTrigger: workflowVersion.trigger,
|
||||
insertedStep: duplicatedStep,
|
||||
@@ -313,9 +322,10 @@ export class WorkflowVersionStepWorkspaceService {
|
||||
});
|
||||
|
||||
return computeWorkflowVersionStepChanges({
|
||||
createdStep: updatedInsertedStep,
|
||||
trigger: updatedTrigger,
|
||||
steps: updatedSteps,
|
||||
existingTrigger: workflowVersion.trigger,
|
||||
existingSteps: workflowVersion.steps,
|
||||
updatedTrigger,
|
||||
updatedSteps,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -350,7 +360,7 @@ export class WorkflowVersionStepWorkspaceService {
|
||||
},
|
||||
);
|
||||
|
||||
const defaultStep =
|
||||
const { builtStep } =
|
||||
await this.workflowVersionStepOperationsWorkspaceService.runStepCreationSideEffectsAndBuildStep(
|
||||
{
|
||||
type: newStep.type,
|
||||
@@ -362,7 +372,7 @@ export class WorkflowVersionStepWorkspaceService {
|
||||
|
||||
return this.workflowSchemaWorkspaceService.enrichOutputSchema({
|
||||
step: {
|
||||
...defaultStep,
|
||||
...builtStep,
|
||||
id: existingStep.id,
|
||||
nextStepIds: existingStep.nextStepIds,
|
||||
position: existingStep.position,
|
||||
|
||||
Reference in New Issue
Block a user