App logic function as step (#17525)
- Add logic function as step - Rename previous one as Code <img width="494" height="573" alt="Capture d’écran 2026-01-28 à 16 15 29" src="https://github.com/user-attachments/assets/82f7e720-20da-4926-b5bc-94a33cd1f53a" /> <img width="494" height="264" alt="Capture d’écran 2026-01-28 à 16 15 39" src="https://github.com/user-attachments/assets/a0444ae9-8c50-418d-8c5f-68c4da08fce7" /> <img width="494" height="264" alt="Capture d’écran 2026-01-28 à 16 15 58" src="https://github.com/user-attachments/assets/3025db09-4e59-421e-8dc5-f3a7a7326554" /> --------- Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
This commit is contained in:
+1
-1
@@ -535,7 +535,7 @@ export class WorkflowVersionEdgeWorkspaceService {
|
||||
input: {
|
||||
...sourceStep.settings.input,
|
||||
initialLoopStepIds: currentInitialLoopStepIds.filter(
|
||||
(id) => id !== target,
|
||||
(id: string) => id !== target,
|
||||
),
|
||||
},
|
||||
},
|
||||
|
||||
+1
-1
@@ -198,7 +198,7 @@ const updateStepsWithOptions = ({
|
||||
initialLoopStepIds: [
|
||||
...new Set([
|
||||
...(step.settings.input.initialLoopStepIds?.filter(
|
||||
(id) => id !== nextStepId,
|
||||
(id: string) => id !== nextStepId,
|
||||
) || []),
|
||||
insertedStepId,
|
||||
]),
|
||||
|
||||
+5
-1
@@ -11,8 +11,8 @@ import {
|
||||
import { computeWorkflowVersionStepChanges } from 'src/modules/workflow/workflow-builder/utils/compute-workflow-version-step-updates.util';
|
||||
import { WorkflowSchemaWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.workspace-service';
|
||||
import { insertStep } from 'src/modules/workflow/workflow-builder/workflow-version-step/utils/insert-step';
|
||||
import { WorkflowVersionStepOperationsWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step-operations.workspace-service';
|
||||
import { WorkflowVersionStepHelpersWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step-helpers.workspace-service';
|
||||
import { WorkflowVersionStepOperationsWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step-operations.workspace-service';
|
||||
import { type WorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
|
||||
|
||||
@Injectable()
|
||||
@@ -38,6 +38,8 @@ export class WorkflowVersionStepCreationWorkspaceService {
|
||||
position,
|
||||
parentStepConnectionOptions,
|
||||
id,
|
||||
logicFunctionId,
|
||||
name,
|
||||
} = input;
|
||||
|
||||
const workflowVersion =
|
||||
@@ -59,6 +61,8 @@ export class WorkflowVersionStepCreationWorkspaceService {
|
||||
position,
|
||||
workflowVersionId,
|
||||
id,
|
||||
logicFunctionId,
|
||||
name,
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
+28
-1
@@ -20,9 +20,9 @@ import { type WorkflowStepPositionInput } from 'src/engine/core-modules/workflow
|
||||
import { AiAgentRoleService } from 'src/engine/metadata-modules/ai/ai-agent-role/ai-agent-role.service';
|
||||
import { AgentEntity } from 'src/engine/metadata-modules/ai/ai-agent/entities/agent.entity';
|
||||
import { DEFAULT_SMART_MODEL } from 'src/engine/metadata-modules/ai/ai-models/constants/ai-models.const';
|
||||
import { LogicFunctionService } from 'src/engine/metadata-modules/logic-function/logic-function.service';
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { RoleTargetEntity } from 'src/engine/metadata-modules/role-target/role-target.entity';
|
||||
import { LogicFunctionService } from 'src/engine/metadata-modules/logic-function/logic-function.service';
|
||||
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
|
||||
import { buildSystemAuthContext } from 'src/engine/twenty-orm/utils/build-system-auth-context.util';
|
||||
import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service';
|
||||
@@ -135,12 +135,16 @@ export class WorkflowVersionStepOperationsWorkspaceService {
|
||||
workflowVersionId,
|
||||
position,
|
||||
id,
|
||||
logicFunctionId,
|
||||
name,
|
||||
}: {
|
||||
type: WorkflowActionType;
|
||||
workspaceId: string;
|
||||
workflowVersionId: string;
|
||||
position?: WorkflowStepPositionInput;
|
||||
id?: string;
|
||||
logicFunctionId?: string;
|
||||
name?: string;
|
||||
}): Promise<{
|
||||
builtStep: WorkflowAction;
|
||||
additionalCreatedSteps?: WorkflowAction[];
|
||||
@@ -195,6 +199,29 @@ export class WorkflowVersionStepOperationsWorkspaceService {
|
||||
},
|
||||
};
|
||||
}
|
||||
case WorkflowActionType.LOGIC_FUNCTION: {
|
||||
if (!logicFunctionId) {
|
||||
throw new WorkflowVersionStepException(
|
||||
'Logic function ID is required for LOGIC_FUNCTION step',
|
||||
WorkflowVersionStepExceptionCode.INVALID_REQUEST,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
builtStep: {
|
||||
...baseStep,
|
||||
name: name ?? 'Logic Function',
|
||||
type: WorkflowActionType.LOGIC_FUNCTION,
|
||||
settings: {
|
||||
...BASE_STEP_DEFINITION,
|
||||
input: {
|
||||
logicFunctionId,
|
||||
logicFunctionInput: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
case WorkflowActionType.SEND_EMAIL: {
|
||||
return {
|
||||
builtStep: {
|
||||
|
||||
+1
-1
@@ -288,7 +288,7 @@ export class WorkflowVersionWorkspaceService {
|
||||
...remappedStep.settings.input,
|
||||
initialLoopStepIds:
|
||||
source.settings.input.initialLoopStepIds.map(
|
||||
(oldId) => oldToNewIdMap.get(oldId) ?? oldId,
|
||||
(oldId: string) => oldToNewIdMap.get(oldId) ?? oldId,
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
+4
@@ -14,6 +14,7 @@ import { FilterWorkflowAction } from 'src/modules/workflow/workflow-executor/wor
|
||||
import { FormWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/form/form.workflow-action';
|
||||
import { IfElseWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/if-else/if-else.workflow-action';
|
||||
import { IteratorWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/iterator/iterator.workflow-action';
|
||||
import { LogicFunctionWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/logic-function/logic-function.workflow-action';
|
||||
import { CreateRecordWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/create-record.workflow-action';
|
||||
import { DeleteRecordWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/delete-record.workflow-action';
|
||||
import { FindRecordsWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/find-records.workflow-action';
|
||||
@@ -26,6 +27,7 @@ import { WorkflowActionType } from 'src/modules/workflow/workflow-executor/workf
|
||||
export class WorkflowActionFactory {
|
||||
constructor(
|
||||
private readonly codeWorkflowAction: CodeWorkflowAction,
|
||||
private readonly logicFunctionWorkflowAction: LogicFunctionWorkflowAction,
|
||||
private readonly createRecordWorkflowAction: CreateRecordWorkflowAction,
|
||||
private readonly upsertRecordWorkflowAction: UpsertRecordWorkflowAction,
|
||||
private readonly updateRecordWorkflowAction: UpdateRecordWorkflowAction,
|
||||
@@ -45,6 +47,8 @@ export class WorkflowActionFactory {
|
||||
switch (stepType) {
|
||||
case WorkflowActionType.CODE:
|
||||
return this.codeWorkflowAction;
|
||||
case WorkflowActionType.LOGIC_FUNCTION:
|
||||
return this.logicFunctionWorkflowAction;
|
||||
case WorkflowActionType.SEND_EMAIL:
|
||||
return this.toolExecutorWorkflowAction;
|
||||
case WorkflowActionType.CREATE_RECORD:
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import {
|
||||
type WorkflowAction,
|
||||
WorkflowActionType,
|
||||
} from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type';
|
||||
|
||||
export const isWorkflowLogicFunctionAction = (
|
||||
action: WorkflowAction,
|
||||
): action is WorkflowAction & { type: WorkflowActionType.LOGIC_FUNCTION } => {
|
||||
return action.type === WorkflowActionType.LOGIC_FUNCTION;
|
||||
};
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module';
|
||||
import { LogicFunctionModule } from 'src/engine/metadata-modules/logic-function/logic-function.module';
|
||||
import { LogicFunctionWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/logic-function/logic-function.workflow-action';
|
||||
|
||||
@Module({
|
||||
imports: [LogicFunctionModule, WorkspaceManyOrAllFlatEntityMapsCacheModule],
|
||||
providers: [LogicFunctionWorkflowAction],
|
||||
exports: [LogicFunctionWorkflowAction],
|
||||
})
|
||||
export class LogicFunctionActionModule {}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { resolveInput } from 'twenty-shared/utils';
|
||||
|
||||
import { type WorkflowAction } from 'src/modules/workflow/workflow-executor/interfaces/workflow-action.interface';
|
||||
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
import { LogicFunctionService } from 'src/engine/metadata-modules/logic-function/logic-function.service';
|
||||
import {
|
||||
WorkflowStepExecutorException,
|
||||
WorkflowStepExecutorExceptionCode,
|
||||
} from 'src/modules/workflow/workflow-executor/exceptions/workflow-step-executor.exception';
|
||||
import { type WorkflowActionInput } from 'src/modules/workflow/workflow-executor/types/workflow-action-input';
|
||||
import { type WorkflowActionOutput } from 'src/modules/workflow/workflow-executor/types/workflow-action-output.type';
|
||||
import { findStepOrThrow } from 'src/modules/workflow/workflow-executor/utils/find-step-or-throw.util';
|
||||
import { isWorkflowLogicFunctionAction } from 'src/modules/workflow/workflow-executor/workflow-actions/logic-function/guards/is-workflow-logic-function-action.guard';
|
||||
import { WorkflowLogicFunctionActionInput } from 'src/modules/workflow/workflow-executor/workflow-actions/logic-function/types/workflow-logic-function-action-input.type';
|
||||
|
||||
@Injectable()
|
||||
export class LogicFunctionWorkflowAction implements WorkflowAction {
|
||||
constructor(
|
||||
private readonly logicFunctionService: LogicFunctionService,
|
||||
private readonly flatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
) {}
|
||||
|
||||
async execute({
|
||||
currentStepId,
|
||||
steps,
|
||||
context,
|
||||
runInfo,
|
||||
}: WorkflowActionInput): Promise<WorkflowActionOutput> {
|
||||
const step = findStepOrThrow({
|
||||
stepId: currentStepId,
|
||||
steps,
|
||||
});
|
||||
|
||||
if (!isWorkflowLogicFunctionAction(step)) {
|
||||
throw new WorkflowStepExecutorException(
|
||||
'Step is not a logic function action',
|
||||
WorkflowStepExecutorExceptionCode.INVALID_STEP_TYPE,
|
||||
);
|
||||
}
|
||||
|
||||
const workflowActionInput = resolveInput(
|
||||
step.settings.input,
|
||||
context,
|
||||
) as WorkflowLogicFunctionActionInput;
|
||||
|
||||
try {
|
||||
const { workspaceId } = runInfo;
|
||||
|
||||
const { flatLogicFunctionMaps } =
|
||||
await this.flatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatLogicFunctionMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
const logicFunction =
|
||||
flatLogicFunctionMaps.byId[workflowActionInput.logicFunctionId];
|
||||
|
||||
if (!logicFunction) {
|
||||
throw new WorkflowStepExecutorException(
|
||||
`Logic function with id ${workflowActionInput.logicFunctionId} not found`,
|
||||
WorkflowStepExecutorExceptionCode.INVALID_STEP_TYPE,
|
||||
);
|
||||
}
|
||||
|
||||
const version = logicFunction.latestVersion ?? 'draft';
|
||||
|
||||
const result = await this.logicFunctionService.executeOneLogicFunction({
|
||||
id: workflowActionInput.logicFunctionId,
|
||||
workspaceId,
|
||||
payload: workflowActionInput.logicFunctionInput,
|
||||
version,
|
||||
});
|
||||
|
||||
if (result.error) {
|
||||
return { error: result.error.errorMessage };
|
||||
}
|
||||
|
||||
return { result: result.data || {} };
|
||||
} catch (error) {
|
||||
return { error: error.message };
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
export type WorkflowLogicFunctionActionInput = {
|
||||
logicFunctionId: string;
|
||||
logicFunctionInput: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[key: string]: any;
|
||||
};
|
||||
};
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
import { type WorkflowLogicFunctionActionInput } from 'src/modules/workflow/workflow-executor/workflow-actions/logic-function/types/workflow-logic-function-action-input.type';
|
||||
import { type BaseWorkflowActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action-settings.type';
|
||||
|
||||
export type WorkflowLogicFunctionActionSettings = BaseWorkflowActionSettings & {
|
||||
input: WorkflowLogicFunctionActionInput;
|
||||
};
|
||||
+1
@@ -1,5 +1,6 @@
|
||||
export enum WorkflowActionType {
|
||||
CODE = 'CODE',
|
||||
LOGIC_FUNCTION = 'LOGIC_FUNCTION',
|
||||
SEND_EMAIL = 'SEND_EMAIL',
|
||||
CREATE_RECORD = 'CREATE_RECORD',
|
||||
UPDATE_RECORD = 'UPDATE_RECORD',
|
||||
|
||||
+7
@@ -6,6 +6,7 @@ import { type WorkflowFormActionSettings } from 'src/modules/workflow/workflow-e
|
||||
import { type WorkflowHttpRequestActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/http-request/types/workflow-http-request-action-settings.type';
|
||||
import { type WorkflowIfElseActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/if-else/types/workflow-if-else-action-settings.type';
|
||||
import { type WorkflowIteratorActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/iterator/types/workflow-iterator-action-settings.type';
|
||||
import { type WorkflowLogicFunctionActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/logic-function/types/workflow-logic-function-action-settings.type';
|
||||
import { type WorkflowSendEmailActionSettings } from 'src/modules/workflow/workflow-executor/workflow-actions/mail-sender/types/workflow-send-email-action-settings.type';
|
||||
import {
|
||||
type WorkflowCreateRecordActionSettings,
|
||||
@@ -40,6 +41,11 @@ export type WorkflowCodeAction = BaseWorkflowAction & {
|
||||
settings: WorkflowCodeActionSettings;
|
||||
};
|
||||
|
||||
export type WorkflowLogicFunctionAction = BaseWorkflowAction & {
|
||||
type: WorkflowActionType.LOGIC_FUNCTION;
|
||||
settings: WorkflowLogicFunctionActionSettings;
|
||||
};
|
||||
|
||||
export type WorkflowSendEmailAction = BaseWorkflowAction & {
|
||||
type: WorkflowActionType.SEND_EMAIL;
|
||||
settings: WorkflowSendEmailActionSettings;
|
||||
@@ -111,6 +117,7 @@ export type WorkflowDelayAction = BaseWorkflowAction & {
|
||||
|
||||
export type WorkflowAction =
|
||||
| WorkflowCodeAction
|
||||
| WorkflowLogicFunctionAction
|
||||
| WorkflowSendEmailAction
|
||||
| WorkflowCreateRecordAction
|
||||
| WorkflowUpdateRecordAction
|
||||
|
||||
+2
@@ -13,6 +13,7 @@ import { FilterActionModule } from 'src/modules/workflow/workflow-executor/workf
|
||||
import { FormActionModule } from 'src/modules/workflow/workflow-executor/workflow-actions/form/form-action.module';
|
||||
import { IfElseActionModule } from 'src/modules/workflow/workflow-executor/workflow-actions/if-else/if-else-action.module';
|
||||
import { IteratorActionModule } from 'src/modules/workflow/workflow-executor/workflow-actions/iterator/iterator-action.module';
|
||||
import { LogicFunctionActionModule } from 'src/modules/workflow/workflow-executor/workflow-actions/logic-function/logic-function-action.module';
|
||||
import { RecordCRUDActionModule } from 'src/modules/workflow/workflow-executor/workflow-actions/record-crud/record-crud-action.module';
|
||||
import { ToolExecutorWorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/tool-executor-workflow-action';
|
||||
import { WorkflowExecutorWorkspaceService } from 'src/modules/workflow/workflow-executor/workspace-services/workflow-executor.workspace-service';
|
||||
@@ -23,6 +24,7 @@ import { WorkflowRunModule } from 'src/modules/workflow/workflow-runner/workflow
|
||||
WorkflowCommonModule,
|
||||
WorkflowRunModule,
|
||||
CodeActionModule,
|
||||
LogicFunctionActionModule,
|
||||
DelayActionModule,
|
||||
RecordCRUDActionModule,
|
||||
FormActionModule,
|
||||
|
||||
Reference in New Issue
Block a user