From 049a065307b82d39f7b7bda757241a60ec68e626 Mon Sep 17 00:00:00 2001 From: Thomas Trompette Date: Mon, 24 Mar 2025 17:42:15 +0100 Subject: [PATCH] Update workflow run step (#11125) Currently, when filling the form, values are not saved in the action settings. This is an issue because we do not see the response in the node settings, only in the output of the step. This PR: - adds a new endpoint to update a step in the run flow output - updates this flow when a step is updated https://github.com/user-attachments/assets/2e74a010-a0d2-4b87-bd1f-1c91f7ca6b60 --------- Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- .../twenty-front/src/generated/graphql.tsx | 59 ++++++++++++- .../mutations/updateWorkflowRunStep.ts | 13 +++ .../validation-schemas/workflowSchema.ts | 3 +- .../components/WorkflowRunStepNodeDetail.tsx | 2 - .../hooks/useUpdateWorkflowRunStep.ts | 86 +++++++++++++++++++ .../WorkflowEditActionFormFiller.tsx | 35 +++----- .../WorkflowEditActionFormFiller.stories.tsx | 6 +- .../update-workflow-run-step-input.dto.ts | 20 +++++ ....resolver.ts => workflow-step.resolver.ts} | 18 +++- .../workflow/workflow-api.module.ts | 8 +- ...workflow-version-step.workspace-service.ts | 4 +- .../workflow-run.workspace-service.ts | 49 +++++++++++ 12 files changed, 266 insertions(+), 37 deletions(-) create mode 100644 packages/twenty-front/src/modules/workflow/graphql/mutations/updateWorkflowRunStep.ts create mode 100644 packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useUpdateWorkflowRunStep.ts create mode 100644 packages/twenty-server/src/engine/core-modules/workflow/dtos/update-workflow-run-step-input.dto.ts rename packages/twenty-server/src/engine/core-modules/workflow/resolvers/{workflow-version-step.resolver.ts => workflow-step.resolver.ts} (81%) diff --git a/packages/twenty-front/src/generated/graphql.tsx b/packages/twenty-front/src/generated/graphql.tsx index 5a92c3868..0774b969a 100644 --- a/packages/twenty-front/src/generated/graphql.tsx +++ b/packages/twenty-front/src/generated/graphql.tsx @@ -1,5 +1,5 @@ -import * as Apollo from '@apollo/client'; import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; export type Maybe = T | null; export type InputMaybe = Maybe; export type Exact = { [K in keyof T]: T[K] }; @@ -856,6 +856,7 @@ export type Mutation = { updateOneRole: Role; updateOneServerlessFunction: ServerlessFunction; updatePasswordViaResetToken: InvalidatePassword; + updateWorkflowRunStep: WorkflowAction; updateWorkflowVersionStep: WorkflowAction; updateWorkspace: Workspace; updateWorkspaceFeatureFlag: Scalars['Boolean']; @@ -1113,6 +1114,11 @@ export type MutationUpdatePasswordViaResetTokenArgs = { }; +export type MutationUpdateWorkflowRunStepArgs = { + input: UpdateWorkflowRunStepInput; +}; + + export type MutationUpdateWorkflowVersionStepArgs = { input: UpdateWorkflowVersionStepInput; }; @@ -1954,6 +1960,13 @@ export type UpdateServerlessFunctionInput = { timeoutSeconds?: InputMaybe; }; +export type UpdateWorkflowRunStepInput = { + /** Step to update in JSON format */ + step: Scalars['JSON']; + /** Workflow run ID */ + workflowRunId: Scalars['String']; +}; + export type UpdateWorkflowVersionStepInput = { /** Step to update in JSON format */ step: Scalars['JSON']; @@ -2662,6 +2675,13 @@ export type RunWorkflowVersionMutationVariables = Exact<{ export type RunWorkflowVersionMutation = { __typename?: 'Mutation', runWorkflowVersion: { __typename?: 'WorkflowRun', workflowRunId: any } }; +export type UpdateWorkflowRunStepMutationVariables = Exact<{ + input: UpdateWorkflowRunStepInput; +}>; + + +export type UpdateWorkflowRunStepMutation = { __typename?: 'Mutation', updateWorkflowRunStep: { __typename?: 'WorkflowAction', id: any, name: string, type: string, settings: any, valid: boolean } }; + export type UpdateWorkflowVersionStepMutationVariables = Exact<{ input: UpdateWorkflowVersionStepInput; }>; @@ -5265,6 +5285,43 @@ export function useRunWorkflowVersionMutation(baseOptions?: Apollo.MutationHookO export type RunWorkflowVersionMutationHookResult = ReturnType; export type RunWorkflowVersionMutationResult = Apollo.MutationResult; export type RunWorkflowVersionMutationOptions = Apollo.BaseMutationOptions; +export const UpdateWorkflowRunStepDocument = gql` + mutation UpdateWorkflowRunStep($input: UpdateWorkflowRunStepInput!) { + updateWorkflowRunStep(input: $input) { + id + name + type + settings + valid + } +} + `; +export type UpdateWorkflowRunStepMutationFn = Apollo.MutationFunction; + +/** + * __useUpdateWorkflowRunStepMutation__ + * + * To run a mutation, you first call `useUpdateWorkflowRunStepMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useUpdateWorkflowRunStepMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [updateWorkflowRunStepMutation, { data, loading, error }] = useUpdateWorkflowRunStepMutation({ + * variables: { + * input: // value for 'input' + * }, + * }); + */ +export function useUpdateWorkflowRunStepMutation(baseOptions?: Apollo.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation(UpdateWorkflowRunStepDocument, options); + } +export type UpdateWorkflowRunStepMutationHookResult = ReturnType; +export type UpdateWorkflowRunStepMutationResult = Apollo.MutationResult; +export type UpdateWorkflowRunStepMutationOptions = Apollo.BaseMutationOptions; export const UpdateWorkflowVersionStepDocument = gql` mutation UpdateWorkflowVersionStep($input: UpdateWorkflowVersionStepInput!) { updateWorkflowVersionStep(input: $input) { diff --git a/packages/twenty-front/src/modules/workflow/graphql/mutations/updateWorkflowRunStep.ts b/packages/twenty-front/src/modules/workflow/graphql/mutations/updateWorkflowRunStep.ts new file mode 100644 index 000000000..8a4040d6b --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/graphql/mutations/updateWorkflowRunStep.ts @@ -0,0 +1,13 @@ +import { gql } from '@apollo/client'; + +export const UPDATE_WORKFLOW_RUN_STEP = gql` + mutation UpdateWorkflowRunStep($input: UpdateWorkflowRunStepInput!) { + updateWorkflowRunStep(input: $input) { + id + name + type + settings + valid + } + } +`; diff --git a/packages/twenty-front/src/modules/workflow/validation-schemas/workflowSchema.ts b/packages/twenty-front/src/modules/workflow/validation-schemas/workflowSchema.ts index 6f5b98adf..e5a442d38 100644 --- a/packages/twenty-front/src/modules/workflow/validation-schemas/workflowSchema.ts +++ b/packages/twenty-front/src/modules/workflow/validation-schemas/workflowSchema.ts @@ -1,5 +1,5 @@ -import { z } from 'zod'; import { FieldMetadataType } from 'twenty-shared/types'; +import { z } from 'zod'; // Base schemas export const objectRecordSchema = z.record(z.any()); @@ -95,6 +95,7 @@ export const workflowFormActionSettingsSchema = ]), placeholder: z.string().optional(), settings: z.record(z.any()).optional(), + value: z.any().optional(), }), ), }); diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/components/WorkflowRunStepNodeDetail.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/components/WorkflowRunStepNodeDetail.tsx index 644214621..427fd8fef 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/components/WorkflowRunStepNodeDetail.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/components/WorkflowRunStepNodeDetail.tsx @@ -171,8 +171,6 @@ export const WorkflowRunStepNodeDetail = ({ action={stepDefinition.definition} actionOptions={{ readonly: stepExecutionStatus !== 'running', - // TODO: Implement update worklfow run flow step - onActionUpdate: () => {}, }} /> ); diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useUpdateWorkflowRunStep.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useUpdateWorkflowRunStep.ts new file mode 100644 index 000000000..4100e6543 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useUpdateWorkflowRunStep.ts @@ -0,0 +1,86 @@ +import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem'; +import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; +import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; +import { useGetRecordFromCache } from '@/object-record/cache/hooks/useGetRecordFromCache'; +import { updateRecordFromCache } from '@/object-record/cache/utils/updateRecordFromCache'; +import { UPDATE_WORKFLOW_RUN_STEP } from '@/workflow/graphql/mutations/updateWorkflowRunStep'; +import { WorkflowRun } from '@/workflow/types/Workflow'; +import { useApolloClient, useMutation } from '@apollo/client'; +import { isDefined } from 'twenty-shared/utils'; +import { + UpdateWorkflowRunStepInput, + UpdateWorkflowRunStepMutation, + UpdateWorkflowRunStepMutationVariables, + WorkflowAction, +} from '~/generated/graphql'; + +export const useUpdateWorkflowRunStep = () => { + const apolloClient = useApolloClient(); + const { objectMetadataItems } = useObjectMetadataItems(); + const { objectMetadataItem } = useObjectMetadataItem({ + objectNameSingular: CoreObjectNameSingular.WorkflowRun, + }); + + const [mutate] = useMutation< + UpdateWorkflowRunStepMutation, + UpdateWorkflowRunStepMutationVariables + >(UPDATE_WORKFLOW_RUN_STEP, { + client: apolloClient, + }); + + const getRecordFromCache = useGetRecordFromCache({ + objectNameSingular: CoreObjectNameSingular.WorkflowRun, + }); + + const updateWorkflowRunStep = async (input: UpdateWorkflowRunStepInput) => { + const result = await mutate({ + variables: { + input: { workflowRunId: input.workflowRunId, step: input.step }, + }, + }); + const updatedStep = result?.data?.updateWorkflowRunStep; + if (!isDefined(updatedStep)) { + return; + } + + const cachedRecord = getRecordFromCache(input.workflowRunId); + + if ( + !isDefined(cachedRecord) || + !isDefined(cachedRecord?.output?.flow?.steps) + ) { + return; + } + + const newCachedRecord = { + ...cachedRecord, + output: { + ...cachedRecord.output, + flow: { + ...cachedRecord.output.flow, + steps: cachedRecord.output.flow.steps.map((step: WorkflowAction) => { + if (step.id === updatedStep.id) { + return updatedStep; + } + return step; + }), + }, + }, + }; + + const recordGqlFields = { + output: true, + }; + updateRecordFromCache({ + objectMetadataItems, + objectMetadataItem, + cache: apolloClient.cache, + record: newCachedRecord, + recordGqlFields, + }); + + return updatedStep; + }; + + return { updateWorkflowRunStep }; +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowEditActionFormFiller.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowEditActionFormFiller.tsx index 6fb284743..e07ef7bda 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowEditActionFormFiller.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowEditActionFormFiller.tsx @@ -7,25 +7,21 @@ import { useWorkflowStepContextOrThrow } from '@/workflow/states/context/Workflo import { WorkflowFormAction } from '@/workflow/types/Workflow'; import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody'; import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader'; +import { useUpdateWorkflowRunStep } from '@/workflow/workflow-steps/hooks/useUpdateWorkflowRunStep'; import { useSubmitFormStep } from '@/workflow/workflow-steps/workflow-actions/form-action/hooks/useSubmitFormStep'; import { WorkflowFormActionField } from '@/workflow/workflow-steps/workflow-actions/form-action/types/WorkflowFormActionField'; import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon'; import { useTheme } from '@emotion/react'; import { useEffect, useState } from 'react'; +import { isDefined } from 'twenty-shared/utils'; import { useIcons } from 'twenty-ui'; import { useDebouncedCallback } from 'use-debounce'; -import { isDefined } from 'twenty-shared/utils'; export type WorkflowEditActionFormFillerProps = { action: WorkflowFormAction; - actionOptions: - | { - readonly: true; - } - | { - readonly?: false; - onActionUpdate: (action: WorkflowFormAction) => void; - }; + actionOptions: { + readonly: boolean; + }; }; type FormData = WorkflowFormActionField[]; @@ -40,6 +36,7 @@ export const WorkflowEditActionFormFiller = ({ const [formData, setFormData] = useState(action.settings.input); const { workflowRunId } = useWorkflowStepContextOrThrow(); const { closeCommandMenu } = useCommandMenu(); + const { updateWorkflowRunStep } = useUpdateWorkflowRunStep(); if (!isDefined(workflowRunId)) { throw new Error('Form filler action must be used in a workflow run'); @@ -73,11 +70,11 @@ export const WorkflowEditActionFormFiller = ({ return; } - actionOptions.onActionUpdate({ - ...action, - settings: { - ...action.settings, - input: updatedFormData, + await updateWorkflowRunStep({ + workflowRunId, + step: { + ...action, + settings: { ...action.settings, input: updatedFormData }, }, }); }, 1_000); @@ -109,16 +106,6 @@ export const WorkflowEditActionFormFiller = ({ return ( <> { - if (actionOptions.readonly === true) { - return; - } - - actionOptions.onActionUpdate({ - ...action, - name: newName, - }); - }} Icon={getIcon(headerIcon)} iconColor={theme.font.color.tertiary} initialTitle={headerTitle} diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/__stories__/WorkflowEditActionFormFiller.stories.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/__stories__/WorkflowEditActionFormFiller.stories.tsx index e22c9e8a9..c98fda0bd 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/__stories__/WorkflowEditActionFormFiller.stories.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/__stories__/WorkflowEditActionFormFiller.stories.tsx @@ -1,6 +1,7 @@ import { WorkflowFormAction } from '@/workflow/types/Workflow'; import { Meta, StoryObj } from '@storybook/react'; -import { expect, fn, within } from '@storybook/test'; +import { expect, within } from '@storybook/test'; +import { FieldMetadataType } from 'twenty-shared/types'; import { ComponentDecorator, RouterDecorator } from 'twenty-ui'; import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator'; import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator'; @@ -9,7 +10,6 @@ import { WorkflowStepDecorator } from '~/testing/decorators/WorkflowStepDecorato import { WorkspaceDecorator } from '~/testing/decorators/WorkspaceDecorator'; import { graphqlMocks } from '~/testing/graphqlMocks'; import { WorkflowEditActionFormFiller } from '../WorkflowEditActionFormFiller'; -import { FieldMetadataType } from 'twenty-shared/types'; const meta: Meta = { title: 'Modules/Workflow/Actions/Form/WorkflowEditActionFormFiller', @@ -67,7 +67,7 @@ export const Default: Story = { args: { action: mockAction, actionOptions: { - onActionUpdate: fn(), + readonly: false, }, }, play: async ({ canvasElement }) => { diff --git a/packages/twenty-server/src/engine/core-modules/workflow/dtos/update-workflow-run-step-input.dto.ts b/packages/twenty-server/src/engine/core-modules/workflow/dtos/update-workflow-run-step-input.dto.ts new file mode 100644 index 000000000..ca432f061 --- /dev/null +++ b/packages/twenty-server/src/engine/core-modules/workflow/dtos/update-workflow-run-step-input.dto.ts @@ -0,0 +1,20 @@ +import { Field, InputType } from '@nestjs/graphql'; + +import graphqlTypeJson from 'graphql-type-json'; + +import { WorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type'; + +@InputType() +export class UpdateWorkflowRunStepInput { + @Field(() => String, { + description: 'Workflow run ID', + nullable: false, + }) + workflowRunId: string; + + @Field(() => graphqlTypeJson, { + description: 'Step to update in JSON format', + nullable: false, + }) + step: WorkflowAction; +} diff --git a/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-version-step.resolver.ts b/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-step.resolver.ts similarity index 81% rename from packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-version-step.resolver.ts rename to packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-step.resolver.ts index f6a010448..52867a53a 100644 --- a/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-version-step.resolver.ts +++ b/packages/twenty-server/src/engine/core-modules/workflow/resolvers/workflow-step.resolver.ts @@ -4,6 +4,7 @@ import { Args, Mutation, Resolver } from '@nestjs/graphql'; import { CreateWorkflowVersionStepInput } from 'src/engine/core-modules/workflow/dtos/create-workflow-version-step-input.dto'; import { DeleteWorkflowVersionStepInput } from 'src/engine/core-modules/workflow/dtos/delete-workflow-version-step-input.dto'; import { SubmitFormStepInput } from 'src/engine/core-modules/workflow/dtos/submit-form-step-input.dto'; +import { UpdateWorkflowRunStepInput } from 'src/engine/core-modules/workflow/dtos/update-workflow-run-step-input.dto'; import { UpdateWorkflowVersionStepInput } from 'src/engine/core-modules/workflow/dtos/update-workflow-version-step-input.dto'; import { WorkflowActionDTO } from 'src/engine/core-modules/workflow/dtos/workflow-step.dto'; import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity'; @@ -11,12 +12,14 @@ import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorat import { UserAuthGuard } from 'src/engine/guards/user-auth.guard'; import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard'; import { WorkflowVersionStepWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-step/workflow-version-step.workspace-service'; +import { WorkflowRunWorkspaceService } from 'src/modules/workflow/workflow-runner/workflow-run/workflow-run.workspace-service'; @Resolver() @UseGuards(WorkspaceAuthGuard, UserAuthGuard) -export class WorkflowVersionStepResolver { +export class WorkflowStepResolver { constructor( private readonly workflowVersionStepWorkspaceService: WorkflowVersionStepWorkspaceService, + private readonly workflowRunWorkspaceService: WorkflowRunWorkspaceService, ) {} @Mutation(() => WorkflowActionDTO) @@ -73,4 +76,17 @@ export class WorkflowVersionStepResolver { return true; } + + @Mutation(() => WorkflowActionDTO) + async updateWorkflowRunStep( + @Args('input') + { workflowRunId, step }: UpdateWorkflowRunStepInput, + ): Promise { + await this.workflowRunWorkspaceService.updateWorkflowRunStep({ + workflowRunId, + step, + }); + + return step; + } } diff --git a/packages/twenty-server/src/engine/core-modules/workflow/workflow-api.module.ts b/packages/twenty-server/src/engine/core-modules/workflow/workflow-api.module.ts index 0c4d57021..401dada11 100644 --- a/packages/twenty-server/src/engine/core-modules/workflow/workflow-api.module.ts +++ b/packages/twenty-server/src/engine/core-modules/workflow/workflow-api.module.ts @@ -1,14 +1,15 @@ import { Module } from '@nestjs/common'; +import { WorkflowTriggerController } from 'src/engine/core-modules/workflow/controllers/workflow-trigger.controller'; import { WorkflowBuilderResolver } from 'src/engine/core-modules/workflow/resolvers/workflow-builder.resolver'; +import { WorkflowStepResolver } from 'src/engine/core-modules/workflow/resolvers/workflow-step.resolver'; import { WorkflowTriggerResolver } from 'src/engine/core-modules/workflow/resolvers/workflow-trigger.resolver'; -import { WorkflowVersionStepResolver } from 'src/engine/core-modules/workflow/resolvers/workflow-version-step.resolver'; import { WorkflowVersionResolver } from 'src/engine/core-modules/workflow/resolvers/workflow-version.resolver'; import { WorkflowCommonModule } from 'src/modules/workflow/common/workflow-common.module'; import { WorkflowBuilderModule } from 'src/modules/workflow/workflow-builder/workflow-builder.module'; import { WorkflowVersionModule } from 'src/modules/workflow/workflow-builder/workflow-version/workflow-version.module'; +import { WorkflowRunModule } from 'src/modules/workflow/workflow-runner/workflow-run/workflow-run.module'; import { WorkflowTriggerModule } from 'src/modules/workflow/workflow-trigger/workflow-trigger.module'; -import { WorkflowTriggerController } from 'src/engine/core-modules/workflow/controllers/workflow-trigger.controller'; @Module({ imports: [ @@ -16,12 +17,13 @@ import { WorkflowTriggerController } from 'src/engine/core-modules/workflow/cont WorkflowBuilderModule, WorkflowCommonModule, WorkflowVersionModule, + WorkflowRunModule, ], controllers: [WorkflowTriggerController], providers: [ WorkflowTriggerResolver, WorkflowBuilderResolver, - WorkflowVersionStepResolver, + WorkflowStepResolver, WorkflowVersionResolver, ], }) diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-step/workflow-version-step.workspace-service.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-step/workflow-version-step.workspace-service.ts index 102b68224..3445dacf4 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-step/workflow-version-step.workspace-service.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-step/workflow-version-step.workspace-service.ts @@ -1,10 +1,10 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; -import { v4 } from 'uuid'; import { FieldMetadataType } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; +import { Repository } from 'typeorm'; +import { v4 } from 'uuid'; import { BASE_TYPESCRIPT_PROJECT_INPUT_SCHEMA } from 'src/engine/core-modules/serverless/drivers/constants/base-typescript-project-input-schema'; import { WorkflowActionDTO } from 'src/engine/core-modules/workflow/dtos/workflow-step.dto'; diff --git a/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-run/workflow-run.workspace-service.ts b/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-run/workflow-run.workspace-service.ts index 8ddb94086..9eaa57cab 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-run/workflow-run.workspace-service.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-runner/workflow-run/workflow-run.workspace-service.ts @@ -9,6 +9,7 @@ import { WorkflowRunWorkspaceEntity, } from 'src/modules/workflow/common/standard-objects/workflow-run.workspace-entity'; import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service'; +import { WorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type'; import { WorkflowRunException, WorkflowRunExceptionCode, @@ -164,6 +165,54 @@ export class WorkflowRunWorkspaceService { }); } + async updateWorkflowRunStep({ + workflowRunId, + step, + }: { + workflowRunId: string; + step: WorkflowAction; + }) { + const workflowRunRepository = + await this.twentyORMManager.getRepository( + 'workflowRun', + ); + + const workflowRunToUpdate = await workflowRunRepository.findOneBy({ + id: workflowRunId, + }); + + if (!workflowRunToUpdate) { + throw new WorkflowRunException( + 'No workflow run to update', + WorkflowRunExceptionCode.WORKFLOW_RUN_NOT_FOUND, + ); + } + + if ( + workflowRunToUpdate.status === WorkflowRunStatus.COMPLETED || + workflowRunToUpdate.status === WorkflowRunStatus.FAILED + ) { + throw new WorkflowRunException( + 'Cannot update steps of a completed or failed workflow run', + WorkflowRunExceptionCode.INVALID_OPERATION, + ); + } + + const updatedSteps = workflowRunToUpdate.output?.flow?.steps?.map( + (existingStep) => (step.id === existingStep.id ? step : existingStep), + ); + + return workflowRunRepository.update(workflowRunToUpdate.id, { + output: { + ...(workflowRunToUpdate.output ?? {}), + flow: { + ...(workflowRunToUpdate.output?.flow ?? {}), + steps: updatedSteps, + }, + }, + }); + } + async getWorkflowRunOrFail( workflowRunId: string, ): Promise {