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 {