Allow to insert into loop step ids (#14425)

- Create empty node on iterator creation
- Add options on step creation to insert in loop
- Handle properly when a step is removed from loop
- Remove loopNextStepIds from frontend action
- Add a frontend skeleton for empty action



https://github.com/user-attachments/assets/281a8c15-8062-4702-afb4-0d9a50902252
This commit is contained in:
Thomas Trompette
2025-09-12 09:30:28 +02:00
committed by GitHub
parent bdf60b1680
commit 231d1a98af
23 changed files with 538 additions and 92 deletions
@@ -50,11 +50,6 @@ export class IteratorWorkflowAction implements WorkflowActionInterface {
const { items, initialLoopStepIds } = iteratorInput;
// TODO: remove once the UI is implemented
const parsedInitialLoopStepIds = isString(initialLoopStepIds)
? JSON.parse(initialLoopStepIds)
: initialLoopStepIds;
const parsedItems = isString(items) ? JSON.parse(items) : items;
if (!Array.isArray(parsedItems)) {
@@ -64,7 +59,11 @@ export class IteratorWorkflowAction implements WorkflowActionInterface {
);
}
if (parsedInitialLoopStepIds.length === 0 || parsedItems.length === 0) {
if (
!isDefined(initialLoopStepIds) ||
initialLoopStepIds.length === 0 ||
parsedItems.length === 0
) {
return {
result: {
currentItemIndex: 0,
@@ -109,7 +108,7 @@ export class IteratorWorkflowAction implements WorkflowActionInterface {
if (!hasProcessedAllItems) {
await this.resetStepsInLoop({
iteratorStepId,
initialLoopStepIds: parsedInitialLoopStepIds,
initialLoopStepIds,
workflowRunId: runInfo.workflowRunId,
workspaceId: runInfo.workspaceId,
steps,
@@ -3,8 +3,7 @@ import { type BaseWorkflowActionSettings } from 'src/modules/workflow/workflow-e
export type WorkflowIteratorActionInput = {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
items?: Array<any> | string;
// testing purpose, should never be a string nor undefined
initialLoopStepIds?: string[] | string;
initialLoopStepIds?: string[];
};
export type WorkflowIteratorActionSettings = BaseWorkflowActionSettings & {
@@ -22,7 +22,7 @@ const { getAllStepIdsInLoop } = jest.requireMock(
describe('canExecuteIteratorStep', () => {
const createMockIteratorStep = (
id: string,
initialLoopStepIds: string[] | string = [],
initialLoopStepIds: string[] = [],
): WorkflowIteratorAction => ({
id,
name: 'Iterator Step',
@@ -1,4 +1,3 @@
import { isString } from 'class-validator';
import { isDefined } from 'twenty-shared/utils';
import { StepStatus, type WorkflowRunStepInfos } from 'twenty-shared/workflow';
@@ -29,19 +28,15 @@ export const canExecuteIteratorStep = ({
(step) => stepInfos[step.id]?.status === StepStatus.SUCCESS,
);
} else {
// On the first iteration, the loop steps will not have been started yet.
// TODO: remove parsing once the UI is implemented
const parsedInitialLoopStepIds = isString(
step.settings.input.initialLoopStepIds,
)
? JSON.parse(step.settings.input.initialLoopStepIds)
: step.settings.input.initialLoopStepIds;
const initialLoopStepIds = step.settings.input.initialLoopStepIds;
const stepIdsInLoop = getAllStepIdsInLoop({
iteratorStepId: step.id,
initialLoopStepIds: parsedInitialLoopStepIds,
steps,
});
const stepIdsInLoop = isDefined(initialLoopStepIds)
? getAllStepIdsInLoop({
iteratorStepId: step.id,
initialLoopStepIds,
steps,
})
: [];
const parentSteps = stepsTargetingIterator.filter(
(step) => !stepIdsInLoop.includes(step.id),
@@ -33,11 +33,7 @@ const traverseSteps = ({
const nestedIteratorInput = step.settings
.input as WorkflowIteratorActionInput;
if (
nestedIteratorInput.initialLoopStepIds &&
// TODO: To remove once we remove the string input for initialLoopStepIds
Array.isArray(nestedIteratorInput.initialLoopStepIds)
) {
if (nestedIteratorInput.initialLoopStepIds) {
const nestedLoopStepIds = getAllStepIdsInLoop({
iteratorStepId: stepId,
initialLoopStepIds: nestedIteratorInput.initialLoopStepIds,