965 flow control arrow menu 1/3 add insert step button (#12519)
Add insert step button to workflow edges https://github.com/user-attachments/assets/7144f722-f1c7-450f-a8eb-c902071986a1 Also fixes `iconButtonGroup` UI component ## Before https://github.com/user-attachments/assets/7b5f0245-d0e8-48af-9aa5-a29388a1caea ## After https://github.com/user-attachments/assets/1820874f-aa99-41ae-8254-c76c275ee3ae
This commit is contained in:
@ -38,7 +38,8 @@ describe('insertStep', () => {
|
||||
insertedStep: newStep,
|
||||
});
|
||||
|
||||
expect(result).toEqual([step1, step2, newStep]);
|
||||
expect(result.updatedSteps).toEqual([step1, step2, newStep]);
|
||||
expect(result.updatedInsertedStep).toEqual(newStep);
|
||||
});
|
||||
|
||||
it('should update parent step nextStepIds when inserting a step between two steps', () => {
|
||||
@ -53,7 +54,7 @@ describe('insertStep', () => {
|
||||
nextStepId: '2',
|
||||
});
|
||||
|
||||
expect(result).toEqual([
|
||||
expect(result.updatedSteps).toEqual([
|
||||
{ ...step1, nextStepIds: ['new'] },
|
||||
step2,
|
||||
{ ...newStep, nextStepIds: ['2'] },
|
||||
@ -71,7 +72,10 @@ describe('insertStep', () => {
|
||||
nextStepId: '1',
|
||||
});
|
||||
|
||||
expect(result).toEqual([step1, { ...newStep, nextStepIds: ['1'] }]);
|
||||
expect(result.updatedSteps).toEqual([
|
||||
step1,
|
||||
{ ...newStep, nextStepIds: ['1'] },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle inserting a step at the end of the workflow', () => {
|
||||
@ -85,7 +89,10 @@ describe('insertStep', () => {
|
||||
nextStepId: undefined,
|
||||
});
|
||||
|
||||
expect(result).toEqual([{ ...step1, nextStepIds: ['new'] }, newStep]);
|
||||
expect(result.updatedSteps).toEqual([
|
||||
{ ...step1, nextStepIds: ['new'] },
|
||||
newStep,
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle inserting a step between two steps with multiple nextStepIds', () => {
|
||||
@ -101,7 +108,7 @@ describe('insertStep', () => {
|
||||
nextStepId: '2',
|
||||
});
|
||||
|
||||
expect(result).toEqual([
|
||||
expect(result.updatedSteps).toEqual([
|
||||
{ ...step1, nextStepIds: ['3', 'new'] },
|
||||
step2,
|
||||
step3,
|
||||
|
||||
@ -10,7 +10,7 @@ export const insertStep = ({
|
||||
insertedStep: WorkflowAction;
|
||||
parentStepId?: string;
|
||||
nextStepId?: string;
|
||||
}): WorkflowAction[] => {
|
||||
}): { updatedSteps: WorkflowAction[]; updatedInsertedStep: WorkflowAction } => {
|
||||
const updatedExistingSteps = existingSteps.map((existingStep) => {
|
||||
if (existingStep.id === parentStepId) {
|
||||
return {
|
||||
@ -28,11 +28,13 @@ export const insertStep = ({
|
||||
return existingStep;
|
||||
});
|
||||
|
||||
return [
|
||||
...updatedExistingSteps,
|
||||
{
|
||||
...insertedStep,
|
||||
nextStepIds: nextStepId ? [nextStepId] : undefined,
|
||||
},
|
||||
];
|
||||
const updatedInsertedStep = {
|
||||
...insertedStep,
|
||||
nextStepIds: nextStepId ? [nextStepId] : undefined,
|
||||
};
|
||||
|
||||
return {
|
||||
updatedSteps: [...updatedExistingSteps, updatedInsertedStep],
|
||||
updatedInsertedStep,
|
||||
};
|
||||
};
|
||||
|
||||
@ -96,7 +96,8 @@ export class WorkflowVersionStepWorkspaceService {
|
||||
assertWorkflowVersionIsDraft(workflowVersion);
|
||||
|
||||
const existingSteps = workflowVersion.steps || [];
|
||||
const updatedSteps = insertStep({
|
||||
|
||||
const { updatedSteps, updatedInsertedStep } = insertStep({
|
||||
existingSteps,
|
||||
insertedStep: enrichedNewStep,
|
||||
parentStepId,
|
||||
@ -107,7 +108,7 @@ export class WorkflowVersionStepWorkspaceService {
|
||||
steps: updatedSteps,
|
||||
});
|
||||
|
||||
return enrichedNewStep;
|
||||
return updatedInsertedStep;
|
||||
}
|
||||
|
||||
async updateWorkflowVersionStep({
|
||||
|
||||
@ -8,6 +8,7 @@ export class WorkflowRunException extends CustomException {
|
||||
|
||||
export enum WorkflowRunExceptionCode {
|
||||
WORKFLOW_RUN_NOT_FOUND = 'WORKFLOW_RUN_NOT_FOUND',
|
||||
WORKFLOW_ROOT_STEP_NOT_FOUND = 'WORKFLOW_ROOT_STEP_NOT_FOUND',
|
||||
INVALID_OPERATION = 'INVALID_OPERATION',
|
||||
INVALID_INPUT = 'INVALID_INPUT',
|
||||
WORKFLOW_RUN_LIMIT_REACHED = 'WORKFLOW_RUN_LIMIT_REACHED',
|
||||
|
||||
@ -14,6 +14,7 @@ import {
|
||||
WorkflowRunExceptionCode,
|
||||
} from 'src/modules/workflow/workflow-runner/exceptions/workflow-run.exception';
|
||||
import { WorkflowRunWorkspaceService } from 'src/modules/workflow/workflow-runner/workflow-run/workflow-run.workspace-service';
|
||||
import { getRootSteps } from 'src/modules/workflow/workflow-runner/utils/getRootSteps.utils';
|
||||
|
||||
export type RunWorkflowJobData = {
|
||||
workspaceId: string;
|
||||
@ -114,9 +115,11 @@ export class RunWorkflowJob {
|
||||
|
||||
await this.throttleExecution(workflowVersion.workflowId);
|
||||
|
||||
const rootSteps = getRootSteps(workflowVersion.steps);
|
||||
|
||||
await this.executeWorkflow({
|
||||
workflowRunId,
|
||||
currentStepId: workflowVersion.steps[0].id,
|
||||
currentStepId: rootSteps[0].id,
|
||||
steps: workflowVersion.steps,
|
||||
context,
|
||||
workspaceId,
|
||||
|
||||
@ -0,0 +1,85 @@
|
||||
import { getRootSteps } from 'src/modules/workflow/workflow-runner/utils/getRootSteps.utils';
|
||||
import { WorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
|
||||
|
||||
describe('getRootSteps', () => {
|
||||
it('should return the root steps', () => {
|
||||
const steps = [
|
||||
{
|
||||
id: 'step1',
|
||||
nextStepIds: ['step2'],
|
||||
},
|
||||
{ id: 'step2', nextStepIds: undefined },
|
||||
] as WorkflowAction[];
|
||||
|
||||
const expectedRootSteps = [
|
||||
{
|
||||
id: 'step1',
|
||||
nextStepIds: ['step2'],
|
||||
},
|
||||
] as WorkflowAction[];
|
||||
|
||||
expect(getRootSteps(steps)).toEqual(expectedRootSteps);
|
||||
});
|
||||
|
||||
it('should not consider step order', () => {
|
||||
const steps = [
|
||||
{ id: 'step2', nextStepIds: undefined },
|
||||
{
|
||||
id: 'step1',
|
||||
nextStepIds: ['step2'],
|
||||
},
|
||||
] as WorkflowAction[];
|
||||
|
||||
const expectedRootSteps = [
|
||||
{
|
||||
id: 'step1',
|
||||
nextStepIds: ['step2'],
|
||||
},
|
||||
] as WorkflowAction[];
|
||||
|
||||
expect(getRootSteps(steps)).toEqual(expectedRootSteps);
|
||||
});
|
||||
|
||||
it('should handle multiple root steps', () => {
|
||||
const steps = [
|
||||
{
|
||||
id: 'step1',
|
||||
nextStepIds: ['step3'],
|
||||
},
|
||||
{
|
||||
id: 'step2',
|
||||
nextStepIds: ['step3'],
|
||||
},
|
||||
{ id: 'step3', nextStepIds: ['step4'] },
|
||||
{ id: 'step4', nextStepIds: undefined },
|
||||
] as WorkflowAction[];
|
||||
|
||||
const expectedRootSteps = [
|
||||
{
|
||||
id: 'step1',
|
||||
nextStepIds: ['step3'],
|
||||
},
|
||||
{
|
||||
id: 'step2',
|
||||
nextStepIds: ['step3'],
|
||||
},
|
||||
] as WorkflowAction[];
|
||||
|
||||
expect(getRootSteps(steps)).toEqual(expectedRootSteps);
|
||||
});
|
||||
|
||||
it('should throw if buggy steps provided', () => {
|
||||
const steps = [
|
||||
{
|
||||
id: 'step1',
|
||||
nextStepIds: ['step2'],
|
||||
},
|
||||
{
|
||||
id: 'step2',
|
||||
nextStepIds: ['step1'],
|
||||
},
|
||||
] as WorkflowAction[];
|
||||
|
||||
expect(() => getRootSteps(steps)).toThrow('No root step found');
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,24 @@
|
||||
import { WorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
|
||||
import {
|
||||
WorkflowRunException,
|
||||
WorkflowRunExceptionCode,
|
||||
} from 'src/modules/workflow/workflow-runner/exceptions/workflow-run.exception';
|
||||
|
||||
export const getRootSteps = (steps: WorkflowAction[]): WorkflowAction[] => {
|
||||
const childIds = new Set<string>();
|
||||
|
||||
for (const step of steps) {
|
||||
step.nextStepIds?.forEach((id) => childIds.add(id));
|
||||
}
|
||||
|
||||
const rootSteps = steps.filter((step) => !childIds.has(step.id));
|
||||
|
||||
if (rootSteps.length === 0) {
|
||||
throw new WorkflowRunException(
|
||||
'No root step found',
|
||||
WorkflowRunExceptionCode.WORKFLOW_ROOT_STEP_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
return rootSteps;
|
||||
};
|
||||
Reference in New Issue
Block a user