Compare commits

...

1 Commits

Author SHA1 Message Date
Charles Bochet 3e0e1ccb86 refactor(server): extract static step builders from workflow-version-step-operations
The `runStepCreationSideEffectsAndBuildStep` method mixed 12 pure/static
step builders with 5 async ones that have side effects. Extract the static
builders into a `buildStaticWorkflowStep` utility and shared constants.

| File | Before | After |
|---|---|---|
| `workflow-version-step-operations.workspace-service.ts` | 1005 | 782 |
| `build-static-workflow-step.util.ts` | — | 237 |
| `workflow-step.constants.ts` | — | 21 |

No behavior change.
2026-06-04 03:03:19 +02:00
3 changed files with 286 additions and 253 deletions
@@ -0,0 +1,20 @@
import { type BaseWorkflowActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action-settings.type';
export const BASE_STEP_DEFINITION: BaseWorkflowActionSettings = {
outputSchema: {},
errorHandlingOptions: {
continueOnFailure: {
value: false,
},
retryOnFailure: {
value: false,
},
},
};
export const DUPLICATED_STEP_POSITION_OFFSET = 50;
export const ITERATOR_EMPTY_STEP_POSITION_OFFSET = {
x: 174,
y: 83,
};
@@ -0,0 +1,236 @@
import { type WorkflowStepPositionInput } from 'src/engine/core-modules/workflow/dtos/update-workflow-step-position.input';
import { type ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { BASE_STEP_DEFINITION } from 'src/modules/workflow/workflow-builder/workflow-version-step/constants/workflow-step.constants';
import {
WorkflowActionType,
type WorkflowAction,
} from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
export const buildStaticWorkflowStep = ({
baseStep,
type,
activeObjectMetadataItem,
}: {
baseStep: {
id: string;
position?: WorkflowStepPositionInput;
valid: boolean;
nextStepIds: string[];
};
type: WorkflowActionType;
activeObjectMetadataItem?: ObjectMetadataEntity | null;
}): { builtStep: WorkflowAction } | undefined => {
switch (type) {
case WorkflowActionType.SEND_EMAIL: {
return {
builtStep: {
...baseStep,
name: 'Send Email',
type: WorkflowActionType.SEND_EMAIL,
settings: {
...BASE_STEP_DEFINITION,
input: {
connectedAccountId: '',
recipients: {
to: '',
cc: '',
bcc: '',
},
subject: '',
body: '',
},
},
},
};
}
case WorkflowActionType.DRAFT_EMAIL: {
return {
builtStep: {
...baseStep,
name: 'Draft Email',
type: WorkflowActionType.DRAFT_EMAIL,
settings: {
...BASE_STEP_DEFINITION,
input: {
connectedAccountId: '',
recipients: {
to: '',
cc: '',
bcc: '',
},
subject: '',
body: '',
},
},
},
};
}
case WorkflowActionType.CREATE_RECORD: {
return {
builtStep: {
...baseStep,
name: 'Create Record',
type: WorkflowActionType.CREATE_RECORD,
settings: {
...BASE_STEP_DEFINITION,
input: {
objectName: activeObjectMetadataItem?.nameSingular || '',
objectRecord: {},
},
},
},
};
}
case WorkflowActionType.UPDATE_RECORD: {
return {
builtStep: {
...baseStep,
name: 'Update Record',
type: WorkflowActionType.UPDATE_RECORD,
settings: {
...BASE_STEP_DEFINITION,
input: {
objectName: activeObjectMetadataItem?.nameSingular || '',
objectRecord: {},
objectRecordId: '',
fieldsToUpdate: [],
},
},
},
};
}
case WorkflowActionType.DELETE_RECORD: {
return {
builtStep: {
...baseStep,
name: 'Delete Record',
type: WorkflowActionType.DELETE_RECORD,
settings: {
...BASE_STEP_DEFINITION,
input: {
objectName: activeObjectMetadataItem?.nameSingular || '',
objectRecordId: '',
},
},
},
};
}
case WorkflowActionType.UPSERT_RECORD: {
return {
builtStep: {
...baseStep,
name: 'Create or Update Record',
type: WorkflowActionType.UPSERT_RECORD,
settings: {
...BASE_STEP_DEFINITION,
input: {
objectName: activeObjectMetadataItem?.nameSingular || '',
objectRecord: {},
fieldsToUpdate: [],
},
},
},
};
}
case WorkflowActionType.FIND_RECORDS: {
return {
builtStep: {
...baseStep,
name: 'Search Records',
type: WorkflowActionType.FIND_RECORDS,
settings: {
...BASE_STEP_DEFINITION,
input: {
objectName: activeObjectMetadataItem?.nameSingular || '',
limit: 1,
},
},
},
};
}
case WorkflowActionType.FORM: {
return {
builtStep: {
...baseStep,
name: 'Form',
type: WorkflowActionType.FORM,
settings: {
...BASE_STEP_DEFINITION,
input: [],
},
},
};
}
case WorkflowActionType.FILTER: {
return {
builtStep: {
...baseStep,
name: 'Filter',
type: WorkflowActionType.FILTER,
settings: {
...BASE_STEP_DEFINITION,
input: {
stepFilterGroups: [],
stepFilters: [],
},
},
},
};
}
case WorkflowActionType.HTTP_REQUEST: {
return {
builtStep: {
...baseStep,
name: 'HTTP Request',
type: WorkflowActionType.HTTP_REQUEST,
settings: {
...BASE_STEP_DEFINITION,
input: {
url: '',
method: 'GET',
headers: {},
body: {},
},
},
},
};
}
case WorkflowActionType.DELAY: {
return {
builtStep: {
...baseStep,
name: 'Delay',
type: WorkflowActionType.DELAY,
settings: {
...BASE_STEP_DEFINITION,
input: {
delayType: 'DURATION',
duration: {
days: 0,
hours: 0,
minutes: 0,
seconds: 0,
},
},
},
},
};
}
case WorkflowActionType.EMPTY: {
return {
builtStep: {
...baseStep,
name: 'Add an Action',
type: WorkflowActionType.EMPTY,
valid: true,
settings: {
...BASE_STEP_DEFINITION,
input: {},
},
},
};
}
default:
return undefined;
}
};
@@ -38,7 +38,12 @@ import { type WorkflowVersionWorkspaceEntity } from 'src/modules/workflow/common
import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service';
import { type OutputSchema } from 'src/modules/workflow/workflow-builder/workflow-schema/types/output-schema.type';
import { CodeStepBuildService } from 'src/modules/workflow/workflow-builder/workflow-version-step/code-step/services/code-step-build.service';
import { type BaseWorkflowActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action-settings.type';
import {
BASE_STEP_DEFINITION,
DUPLICATED_STEP_POSITION_OFFSET,
ITERATOR_EMPTY_STEP_POSITION_OFFSET,
} from 'src/modules/workflow/workflow-builder/workflow-version-step/constants/workflow-step.constants';
import { buildStaticWorkflowStep } from 'src/modules/workflow/workflow-builder/workflow-version-step/utils/build-static-workflow-step.util';
import {
WorkflowActionType,
type WorkflowAction,
@@ -46,24 +51,6 @@ import {
type WorkflowFormAction,
} from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
import { AUTO_SELECT_SMART_MODEL_ID } from 'twenty-shared/constants';
const BASE_STEP_DEFINITION: BaseWorkflowActionSettings = {
outputSchema: {},
errorHandlingOptions: {
continueOnFailure: {
value: false,
},
retryOnFailure: {
value: false,
},
},
};
const DUPLICATED_STEP_POSITION_OFFSET = 50;
const ITERATOR_EMPTY_STEP_POSITION_OFFSET = {
x: 174,
y: 83,
};
@Injectable()
export class WorkflowVersionStepOperationsWorkspaceService {
@@ -153,6 +140,30 @@ export class WorkflowVersionStepOperationsWorkspaceService {
nextStepIds: [],
};
const recordRelatedTypes = new Set([
WorkflowActionType.CREATE_RECORD,
WorkflowActionType.UPDATE_RECORD,
WorkflowActionType.DELETE_RECORD,
WorkflowActionType.UPSERT_RECORD,
WorkflowActionType.FIND_RECORDS,
]);
const activeObjectMetadataItem = recordRelatedTypes.has(type)
? await this.objectMetadataRepository.findOne({
where: { workspaceId, isActive: true, isSystem: false },
})
: undefined;
const staticResult = buildStaticWorkflowStep({
baseStep,
type,
activeObjectMetadataItem,
});
if (isDefined(staticResult)) {
return staticResult;
}
switch (type) {
case WorkflowActionType.CODE: {
const logicFunctionId = id ?? v4();
@@ -268,205 +279,6 @@ export class WorkflowVersionStepOperationsWorkspaceService {
},
};
}
case WorkflowActionType.SEND_EMAIL: {
return {
builtStep: {
...baseStep,
name: 'Send Email',
type: WorkflowActionType.SEND_EMAIL,
settings: {
...BASE_STEP_DEFINITION,
input: {
connectedAccountId: '',
recipients: {
to: '',
cc: '',
bcc: '',
},
subject: '',
body: '',
},
},
},
};
}
case WorkflowActionType.DRAFT_EMAIL: {
return {
builtStep: {
...baseStep,
name: 'Draft Email',
type: WorkflowActionType.DRAFT_EMAIL,
settings: {
...BASE_STEP_DEFINITION,
input: {
connectedAccountId: '',
recipients: {
to: '',
cc: '',
bcc: '',
},
subject: '',
body: '',
},
},
},
};
}
case WorkflowActionType.CREATE_RECORD: {
const activeObjectMetadataItem =
await this.objectMetadataRepository.findOne({
where: { workspaceId, isActive: true, isSystem: false },
});
return {
builtStep: {
...baseStep,
name: 'Create Record',
type: WorkflowActionType.CREATE_RECORD,
settings: {
...BASE_STEP_DEFINITION,
input: {
objectName: activeObjectMetadataItem?.nameSingular || '',
objectRecord: {},
},
},
},
};
}
case WorkflowActionType.UPDATE_RECORD: {
const activeObjectMetadataItem =
await this.objectMetadataRepository.findOne({
where: { workspaceId, isActive: true, isSystem: false },
});
return {
builtStep: {
...baseStep,
name: 'Update Record',
type: WorkflowActionType.UPDATE_RECORD,
settings: {
...BASE_STEP_DEFINITION,
input: {
objectName: activeObjectMetadataItem?.nameSingular || '',
objectRecord: {},
objectRecordId: '',
fieldsToUpdate: [],
},
},
},
};
}
case WorkflowActionType.DELETE_RECORD: {
const activeObjectMetadataItem =
await this.objectMetadataRepository.findOne({
where: { workspaceId, isActive: true, isSystem: false },
});
return {
builtStep: {
...baseStep,
name: 'Delete Record',
type: WorkflowActionType.DELETE_RECORD,
settings: {
...BASE_STEP_DEFINITION,
input: {
objectName: activeObjectMetadataItem?.nameSingular || '',
objectRecordId: '',
},
},
},
};
}
case WorkflowActionType.UPSERT_RECORD: {
const activeObjectMetadataItem =
await this.objectMetadataRepository.findOne({
where: { workspaceId, isActive: true, isSystem: false },
});
return {
builtStep: {
...baseStep,
name: 'Create or Update Record',
type: WorkflowActionType.UPSERT_RECORD,
settings: {
...BASE_STEP_DEFINITION,
input: {
objectName: activeObjectMetadataItem?.nameSingular || '',
objectRecord: {},
fieldsToUpdate: [],
},
},
},
};
}
case WorkflowActionType.FIND_RECORDS: {
const activeObjectMetadataItem =
await this.objectMetadataRepository.findOne({
where: { workspaceId, isActive: true, isSystem: false },
});
return {
builtStep: {
...baseStep,
name: 'Search Records',
type: WorkflowActionType.FIND_RECORDS,
settings: {
...BASE_STEP_DEFINITION,
input: {
objectName: activeObjectMetadataItem?.nameSingular || '',
limit: 1,
},
},
},
};
}
case WorkflowActionType.FORM: {
return {
builtStep: {
...baseStep,
name: 'Form',
type: WorkflowActionType.FORM,
settings: {
...BASE_STEP_DEFINITION,
input: [],
},
},
};
}
case WorkflowActionType.FILTER: {
return {
builtStep: {
...baseStep,
name: 'Filter',
type: WorkflowActionType.FILTER,
settings: {
...BASE_STEP_DEFINITION,
input: {
stepFilterGroups: [],
stepFilters: [],
},
},
},
};
}
case WorkflowActionType.HTTP_REQUEST: {
return {
builtStep: {
...baseStep,
name: 'HTTP Request',
type: WorkflowActionType.HTTP_REQUEST,
settings: {
...BASE_STEP_DEFINITION,
input: {
url: '',
method: 'GET',
headers: {},
body: {},
},
},
},
};
}
case WorkflowActionType.AI_AGENT: {
const newAgent = await this.agentService.createOneAgent(
{
@@ -564,41 +376,6 @@ export class WorkflowVersionStepOperationsWorkspaceService {
additionalCreatedSteps: [ifEmptyNode, elseEmptyNode],
};
}
case WorkflowActionType.DELAY: {
return {
builtStep: {
...baseStep,
name: 'Delay',
type: WorkflowActionType.DELAY,
settings: {
...BASE_STEP_DEFINITION,
input: {
delayType: 'DURATION',
duration: {
days: 0,
hours: 0,
minutes: 0,
seconds: 0,
},
},
},
},
};
}
case WorkflowActionType.EMPTY: {
return {
builtStep: {
...baseStep,
name: 'Add an Action',
type: WorkflowActionType.EMPTY,
valid: true,
settings: {
...BASE_STEP_DEFINITION,
input: {},
},
},
};
}
default:
throw new WorkflowVersionStepException(
`WorkflowActionType '${type}' unknown`,