From 8c41c956d5713d0f763cf902fa9ed0a7d0d11478 Mon Sep 17 00:00:00 2001 From: Thomas Trompette Date: Thu, 7 Aug 2025 09:40:26 +0200 Subject: [PATCH] Remove filters following deleted trigger/steps (#13697) https://github.com/user-attachments/assets/e6ec591a-2064-4dfe-9048-2f234bef5c79 --- .../src/generated-metadata/graphql.ts | 6 +- .../twenty-front/src/generated/graphql.ts | 2 +- .../mutations/deleteWorkflowVersionStep.ts | 2 +- .../hooks/useDeleteWorkflowVersionStep.ts | 4 +- .../workflow/hooks/useStepsOutputSchema.ts | 24 ++--- .../workflow-steps/hooks/useDeleteStep.ts | 35 ++----- .../hooks/useUpdateWorkflowVersionCache.ts | 26 ++++-- .../dtos/workflow-version-step-changes.dto.ts | 4 +- ...workflow-version-step-changes.util.spec.ts | 6 +- ...pute-workflow-version-step-updates.util.ts | 8 +- ...low-version-step.workspace-service.spec.ts | 23 ++--- .../utils/__tests__/remove-step.spec.ts | 76 ++++++++++++--- .../utils/remove-step.ts | 93 +++++++++++++++++-- ...workflow-version-step.workspace-service.ts | 57 ++++++------ 14 files changed, 248 insertions(+), 118 deletions(-) diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 7b2629072a..8bd126a6db 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -3124,7 +3124,7 @@ export type WorkflowVersion = { export type WorkflowVersionStepChanges = { __typename?: 'WorkflowVersionStepChanges'; createdStep?: Maybe; - deletedStepId?: Maybe; + deletedStepIds?: Maybe>; stepsNextStepIds?: Maybe; triggerNextStepIds?: Maybe>; }; @@ -4208,7 +4208,7 @@ export type DeleteWorkflowVersionStepMutationVariables = Exact<{ }>; -export type DeleteWorkflowVersionStepMutation = { __typename?: 'Mutation', deleteWorkflowVersionStep: { __typename?: 'WorkflowVersionStepChanges', triggerNextStepIds?: Array | null, stepsNextStepIds?: any | null, deletedStepId?: string | null } }; +export type DeleteWorkflowVersionStepMutation = { __typename?: 'Mutation', deleteWorkflowVersionStep: { __typename?: 'WorkflowVersionStepChanges', triggerNextStepIds?: Array | null, stepsNextStepIds?: any | null, deletedStepIds?: Array | null } }; export type RunWorkflowVersionMutationVariables = Exact<{ input: RunWorkflowVersionInput; @@ -9535,7 +9535,7 @@ export const DeleteWorkflowVersionStepDocument = gql` deleteWorkflowVersionStep(input: $input) { triggerNextStepIds stepsNextStepIds - deletedStepId + deletedStepIds } } `; diff --git a/packages/twenty-front/src/generated/graphql.ts b/packages/twenty-front/src/generated/graphql.ts index 23354c512a..1a8e56a64d 100644 --- a/packages/twenty-front/src/generated/graphql.ts +++ b/packages/twenty-front/src/generated/graphql.ts @@ -2952,7 +2952,7 @@ export type WorkflowVersion = { export type WorkflowVersionStepChanges = { __typename?: 'WorkflowVersionStepChanges'; createdStep?: Maybe; - deletedStepId?: Maybe; + deletedStepIds?: Maybe>; stepsNextStepIds?: Maybe; triggerNextStepIds?: Maybe>; }; diff --git a/packages/twenty-front/src/modules/workflow/graphql/mutations/deleteWorkflowVersionStep.ts b/packages/twenty-front/src/modules/workflow/graphql/mutations/deleteWorkflowVersionStep.ts index af6bdcba18..7ea7f083ee 100644 --- a/packages/twenty-front/src/modules/workflow/graphql/mutations/deleteWorkflowVersionStep.ts +++ b/packages/twenty-front/src/modules/workflow/graphql/mutations/deleteWorkflowVersionStep.ts @@ -5,7 +5,7 @@ export const DELETE_WORKFLOW_VERSION_STEP = gql` deleteWorkflowVersionStep(input: $input) { triggerNextStepIds stepsNextStepIds - deletedStepId + deletedStepIds } } `; diff --git a/packages/twenty-front/src/modules/workflow/hooks/useDeleteWorkflowVersionStep.ts b/packages/twenty-front/src/modules/workflow/hooks/useDeleteWorkflowVersionStep.ts index 7fe82e56e1..de85b45dda 100644 --- a/packages/twenty-front/src/modules/workflow/hooks/useDeleteWorkflowVersionStep.ts +++ b/packages/twenty-front/src/modules/workflow/hooks/useDeleteWorkflowVersionStep.ts @@ -1,12 +1,12 @@ import { useApolloCoreClient } from '@/object-metadata/hooks/useApolloCoreClient'; import { DELETE_WORKFLOW_VERSION_STEP } from '@/workflow/graphql/mutations/deleteWorkflowVersionStep'; +import { useUpdateWorkflowVersionCache } from '@/workflow/workflow-steps/hooks/useUpdateWorkflowVersionCache'; import { useMutation } from '@apollo/client'; import { DeleteWorkflowVersionStepInput, DeleteWorkflowVersionStepMutation, DeleteWorkflowVersionStepMutationVariables, } from '~/generated-metadata/graphql'; -import { useUpdateWorkflowVersionCache } from '@/workflow/workflow-steps/hooks/useUpdateWorkflowVersionCache'; export const useDeleteWorkflowVersionStep = () => { const apolloCoreClient = useApolloCoreClient(); @@ -31,6 +31,8 @@ export const useDeleteWorkflowVersionStep = () => { workflowVersionStepChanges, workflowVersionId: input.workflowVersionId, }); + + return workflowVersionStepChanges; }; return { deleteWorkflowVersionStep }; diff --git a/packages/twenty-front/src/modules/workflow/hooks/useStepsOutputSchema.ts b/packages/twenty-front/src/modules/workflow/hooks/useStepsOutputSchema.ts index d728702401..303e5d94cc 100644 --- a/packages/twenty-front/src/modules/workflow/hooks/useStepsOutputSchema.ts +++ b/packages/twenty-front/src/modules/workflow/hooks/useStepsOutputSchema.ts @@ -3,8 +3,8 @@ import { WorkflowVersion } from '@/workflow/types/Workflow'; import { getStepOutputSchemaFamilyStateKey } from '@/workflow/utils/getStepOutputSchemaFamilyStateKey'; import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon'; import { TRIGGER_STEP_ID } from '@/workflow/workflow-trigger/constants/TriggerStepId'; -import { getTriggerIcon } from '@/workflow/workflow-trigger/utils/getTriggerIcon'; import { getTriggerDefaultLabel } from '@/workflow/workflow-trigger/utils/getTriggerDefaultLabel'; +import { getTriggerIcon } from '@/workflow/workflow-trigger/utils/getTriggerIcon'; import { OutputSchema, StepOutputSchema, @@ -60,27 +60,29 @@ export const useStepsOutputSchema = () => { [], ); - const deleteStepOutputSchema = useRecoilCallback( + const deleteStepsOutputSchema = useRecoilCallback( ({ set }) => ({ - stepId, + stepIds, workflowVersionId, }: { - stepId: string; + stepIds: string[]; workflowVersionId: string; }) => { - set( - stepsOutputSchemaFamilyState( - getStepOutputSchemaFamilyStateKey(workflowVersionId, stepId), - ), - null, - ); + stepIds.forEach((stepId) => { + set( + stepsOutputSchemaFamilyState( + getStepOutputSchemaFamilyStateKey(workflowVersionId, stepId), + ), + null, + ); + }); }, [], ); return { populateStepsOutputSchema, - deleteStepOutputSchema, + deleteStepsOutputSchema, }; }; diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useDeleteStep.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useDeleteStep.ts index c1e7e85683..0c026d78b8 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useDeleteStep.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useDeleteStep.ts @@ -1,15 +1,9 @@ import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu'; -import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; -import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord'; import { useDeleteWorkflowVersionStep } from '@/workflow/hooks/useDeleteWorkflowVersionStep'; import { useGetUpdatableWorkflowVersion } from '@/workflow/hooks/useGetUpdatableWorkflowVersion'; import { useStepsOutputSchema } from '@/workflow/hooks/useStepsOutputSchema'; -import { - WorkflowVersion, - WorkflowWithCurrentVersion, -} from '@/workflow/types/Workflow'; +import { WorkflowWithCurrentVersion } from '@/workflow/types/Workflow'; import { assertWorkflowWithCurrentVersionIsDefined } from '@/workflow/utils/assertWorkflowWithCurrentVersionIsDefined'; -import { TRIGGER_STEP_ID } from '@/workflow/workflow-trigger/constants/TriggerStepId'; import { isDefined } from 'twenty-shared/utils'; export const useDeleteStep = ({ @@ -18,11 +12,7 @@ export const useDeleteStep = ({ workflow: WorkflowWithCurrentVersion | undefined; }) => { const { deleteWorkflowVersionStep } = useDeleteWorkflowVersionStep(); - const { updateOneRecord: updateOneWorkflowVersion } = - useUpdateOneRecord({ - objectNameSingular: CoreObjectNameSingular.WorkflowVersion, - }); - const { deleteStepOutputSchema } = useStepsOutputSchema(); + const { deleteStepsOutputSchema } = useStepsOutputSchema(); const { getUpdatableWorkflowVersion } = useGetUpdatableWorkflowVersion(); const { closeCommandMenu } = useCommandMenu(); @@ -36,24 +26,15 @@ export const useDeleteStep = ({ throw new Error('Could not find workflow version'); } - if (stepId === TRIGGER_STEP_ID) { - await updateOneWorkflowVersion({ - idToUpdate: workflowVersionId, - updateOneRecordInput: { - trigger: null, - }, - }); - } else { - await deleteWorkflowVersionStep({ - workflowVersionId, - stepId, - }); - } + const workflowVersionStepChanges = await deleteWorkflowVersionStep({ + workflowVersionId, + stepId, + }); closeCommandMenu(); - deleteStepOutputSchema({ - stepId, + deleteStepsOutputSchema({ + stepIds: workflowVersionStepChanges?.deletedStepIds ?? [], workflowVersionId, }); }; diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useUpdateWorkflowVersionCache.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useUpdateWorkflowVersionCache.ts index 1873a075b0..9a3d5804a0 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useUpdateWorkflowVersionCache.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useUpdateWorkflowVersionCache.ts @@ -1,16 +1,16 @@ import { useApolloCoreClient } from '@/object-metadata/hooks/useApolloCoreClient'; -import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems'; -import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; 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 { isDefined } from 'twenty-shared/utils'; +import { updateRecordFromCache } from '@/object-record/cache/utils/updateRecordFromCache'; +import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions'; import { WorkflowVersion } from '@/workflow/types/Workflow'; +import { isDefined } from 'twenty-shared/utils'; import { WorkflowAction, WorkflowVersionStepChanges, } from '~/generated/graphql'; -import { updateRecordFromCache } from '@/object-record/cache/utils/updateRecordFromCache'; export const useUpdateWorkflowVersionCache = () => { const apolloCoreClient = useApolloCoreClient(); @@ -44,8 +44,12 @@ export const useUpdateWorkflowVersionCache = () => { return; } - const { triggerNextStepIds, stepsNextStepIds, createdStep, deletedStepId } = - workflowVersionStepChanges; + const { + triggerNextStepIds, + stepsNextStepIds, + createdStep, + deletedStepIds, + } = workflowVersionStepChanges; const newCachedRecord = { ...cachedRecord, @@ -70,10 +74,16 @@ export const useUpdateWorkflowVersionCache = () => { newCachedRecord.steps.push(formattedCreatedStep); } - if (isDefined(deletedStepId)) { + if (isDefined(deletedStepIds) && deletedStepIds.length > 0) { newCachedRecord.steps = newCachedRecord.steps.filter( - (step) => step.id !== deletedStepId, + (step: WorkflowAction) => !deletedStepIds.includes(step.id), ); + + const hasDeletedTrigger: boolean = deletedStepIds.includes('trigger'); + + if (hasDeletedTrigger) { + newCachedRecord.trigger = null; + } } const recordGqlFields = { diff --git a/packages/twenty-server/src/engine/core-modules/workflow/dtos/workflow-version-step-changes.dto.ts b/packages/twenty-server/src/engine/core-modules/workflow/dtos/workflow-version-step-changes.dto.ts index 893cb4d81d..50841a60d5 100644 --- a/packages/twenty-server/src/engine/core-modules/workflow/dtos/workflow-version-step-changes.dto.ts +++ b/packages/twenty-server/src/engine/core-modules/workflow/dtos/workflow-version-step-changes.dto.ts @@ -15,6 +15,6 @@ export class WorkflowVersionStepChangesDTO { @Field(() => WorkflowActionDTO, { nullable: true }) createdStep?: WorkflowActionDTO; - @Field(() => String, { nullable: true }) - deletedStepId?: string; + @Field(() => [String], { nullable: true }) + deletedStepIds?: string[]; } diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/utils/__tests__/compute-workflow-version-step-changes.util.spec.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/utils/__tests__/compute-workflow-version-step-changes.util.spec.ts index 7d194b0811..685a4ebc7a 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/utils/__tests__/compute-workflow-version-step-changes.util.spec.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/utils/__tests__/compute-workflow-version-step-changes.util.spec.ts @@ -1,6 +1,6 @@ import { computeWorkflowVersionStepChanges } from 'src/modules/workflow/workflow-builder/utils/compute-workflow-version-step-updates.util'; -import { WorkflowTrigger } from 'src/modules/workflow/workflow-trigger/types/workflow-trigger.type'; import { WorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type'; +import { WorkflowTrigger } from 'src/modules/workflow/workflow-trigger/types/workflow-trigger.type'; describe('computeWorkflowVersionStepChanges', () => { it('should compute next step ids', () => { @@ -10,13 +10,13 @@ describe('computeWorkflowVersionStepChanges', () => { { id: '1', nextStepIds: ['3'] }, { id: '2', nextStepIds: ['3'] }, ] as WorkflowAction[], - deletedStepId: '5', + deletedStepIds: ['5'], }; const expectedResult = { triggerNextStepIds: ['1', '2'], stepsNextStepIds: { '1': ['3'], '2': ['3'] }, - deletedStepId: '5', + deletedStepIds: ['5'], }; expect(computeWorkflowVersionStepChanges(input)).toEqual(expectedResult); diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/utils/compute-workflow-version-step-updates.util.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/utils/compute-workflow-version-step-updates.util.ts index 50f964c1fd..beac2b6ae8 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/utils/compute-workflow-version-step-updates.util.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/utils/compute-workflow-version-step-updates.util.ts @@ -1,17 +1,17 @@ import { WorkflowVersionStepChangesDTO } from 'src/engine/core-modules/workflow/dtos/workflow-version-step-changes.dto'; -import { WorkflowTrigger } from 'src/modules/workflow/workflow-trigger/types/workflow-trigger.type'; import { WorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type'; +import { WorkflowTrigger } from 'src/modules/workflow/workflow-trigger/types/workflow-trigger.type'; export const computeWorkflowVersionStepChanges = ({ trigger, steps, createdStep, - deletedStepId, + deletedStepIds, }: { trigger: WorkflowTrigger | null; steps: WorkflowAction[] | null; createdStep?: WorkflowAction; - deletedStepId?: string; + deletedStepIds?: string[]; }): WorkflowVersionStepChangesDTO => { return { triggerNextStepIds: trigger?.nextStepIds, @@ -19,6 +19,6 @@ export const computeWorkflowVersionStepChanges = ({ (steps || []).map((step) => [step.id, step.nextStepIds]), ), createdStep, - deletedStepId, + deletedStepIds, }; }; diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/__tests__/workflow-version-step.workspace-service.spec.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/__tests__/workflow-version-step.workspace-service.spec.ts index 056dbb02b8..577b16275b 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/__tests__/workflow-version-step.workspace-service.spec.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/__tests__/workflow-version-step.workspace-service.spec.ts @@ -1,22 +1,22 @@ import { Test, TestingModule } from '@nestjs/testing'; import { getRepositoryToken } from '@nestjs/typeorm'; -import { WorkflowVersionStepWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step.workspace-service'; -import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; -import { WorkflowSchemaWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.workspace-service'; -import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service'; import { AgentService } from 'src/engine/metadata-modules/agent/agent.service'; -import { WorkflowRunWorkspaceService } from 'src/modules/workflow/workflow-runner/workflow-run/workflow-run.workspace-service'; -import { WorkflowRunnerWorkspaceService } from 'src/modules/workflow/workflow-runner/workspace-services/workflow-runner.workspace-service'; -import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service'; -import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; -import { WorkflowVersionWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-version.workspace-entity'; +import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service'; +import { ScopedWorkspaceContextFactory } from 'src/engine/twenty-orm/factories/scoped-workspace-context.factory'; import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository'; +import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; +import { WorkflowVersionWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-version.workspace-entity'; +import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service'; +import { WorkflowSchemaWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-schema/workflow-schema.workspace-service'; +import { WorkflowVersionStepWorkspaceService } from 'src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step.workspace-service'; import { WorkflowAction, WorkflowActionType, } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type'; +import { WorkflowRunWorkspaceService } from 'src/modules/workflow/workflow-runner/workflow-run/workflow-run.workspace-service'; +import { WorkflowRunnerWorkspaceService } from 'src/modules/workflow/workflow-runner/workspace-services/workflow-runner.workspace-service'; import { WorkflowTriggerType } from 'src/modules/workflow/workflow-trigger/types/workflow-trigger.type'; type MockWorkspaceRepository = Partial< @@ -253,7 +253,7 @@ describe('WorkflowVersionStepWorkspaceService', () => { 'step-2': [], 'step-3': [], }, - deletedStepId: 'step-1', + deletedStepIds: ['step-1'], }); }); @@ -268,6 +268,7 @@ describe('WorkflowVersionStepWorkspaceService', () => { mockWorkflowVersionWorkspaceRepository.update, ).toHaveBeenCalledWith(mockWorkflowVersionId, { trigger: null, + steps: mockSteps, }); expect(result).toEqual({ @@ -276,7 +277,7 @@ describe('WorkflowVersionStepWorkspaceService', () => { 'step-2': [], 'step-3': [], }, - deletedStepId: 'trigger', + deletedStepIds: ['trigger'], }); }); }); diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/utils/__tests__/remove-step.spec.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/utils/__tests__/remove-step.spec.ts index 116598e094..23807ec83b 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/utils/__tests__/remove-step.spec.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/utils/__tests__/remove-step.spec.ts @@ -50,8 +50,8 @@ describe('removeStep', () => { stepIdToDelete: '2', }); - expect(result.steps).toEqual([step1, step3]); - expect(result.trigger).toEqual(mockTrigger); + expect(result.updatedSteps).toEqual([step1, step3]); + expect(result.updatedTrigger).toEqual(mockTrigger); }); it('should handle removing a step that has no next steps', () => { @@ -65,8 +65,8 @@ describe('removeStep', () => { stepIdToDelete: '2', }); - expect(result.steps).toEqual([{ ...step1, nextStepIds: [] }, step3]); - expect(result.trigger).toEqual(mockTrigger); + expect(result.updatedSteps).toEqual([{ ...step1, nextStepIds: [] }, step3]); + expect(result.updatedTrigger).toEqual(mockTrigger); }); it('should update nextStepIds of parent steps to include children of removed step', () => { @@ -81,8 +81,11 @@ describe('removeStep', () => { stepToDeleteChildrenIds: ['3'], }); - expect(result.steps).toEqual([{ ...step1, nextStepIds: ['3'] }, step3]); - expect(result.trigger).toEqual(mockTrigger); + expect(result.updatedSteps).toEqual([ + { ...step1, nextStepIds: ['3'] }, + step3, + ]); + expect(result.updatedTrigger).toEqual(mockTrigger); }); it('should handle multiple parent steps pointing to the same step', () => { @@ -98,12 +101,12 @@ describe('removeStep', () => { stepToDeleteChildrenIds: ['4'], }); - expect(result.steps).toEqual([ + expect(result.updatedSteps).toEqual([ { ...step1, nextStepIds: ['4'] }, { ...step2, nextStepIds: ['4'] }, step4, ]); - expect(result.trigger).toEqual(mockTrigger); + expect(result.updatedTrigger).toEqual(mockTrigger); }); it('should handle removing a step with multiple children', () => { @@ -119,12 +122,12 @@ describe('removeStep', () => { stepToDeleteChildrenIds: ['3', '4'], }); - expect(result.steps).toEqual([ + expect(result.updatedSteps).toEqual([ { ...step1, nextStepIds: ['3', '4'] }, step3, step4, ]); - expect(result.trigger).toEqual(mockTrigger); + expect(result.updatedTrigger).toEqual(mockTrigger); }); it('should handle removing a step linked to trigger', () => { @@ -139,7 +142,56 @@ describe('removeStep', () => { stepToDeleteChildrenIds: ['2'], }); - expect(result.steps).toEqual([step2, step3]); - expect(result.trigger).toEqual({ ...mockTrigger, nextStepIds: ['2'] }); + expect(result.updatedSteps).toEqual([step2, step3]); + expect(result.updatedTrigger).toEqual({ + ...mockTrigger, + nextStepIds: ['2'], + }); + }); + + it('should remove step child that is a filter', () => { + const step1 = createMockAction('1', ['2']); + const step2 = createMockAction('2', ['3']); + const step3 = { + id: '3', + name: 'Step 3', + type: WorkflowActionType.FILTER, + nextStepIds: ['4'], + } as WorkflowAction; + const step4 = createMockAction('4'); + + const result = removeStep({ + existingTrigger: mockTrigger, + existingSteps: [step1, step2, step3, step4], + stepIdToDelete: '2', + stepToDeleteChildrenIds: ['3'], + }); + + expect(result.updatedTrigger).toEqual(mockTrigger); + expect(result.updatedSteps).toEqual([ + { ...step1, nextStepIds: ['4'] }, + step4, + ]); + }); + + it('should remove trigger children that is a filter', () => { + const step1 = { + id: '1', + name: 'Step 1', + type: WorkflowActionType.FILTER, + nextStepIds: ['2'], + } as WorkflowAction; + const step2 = createMockAction('2', ['3']); + const step3 = createMockAction('3'); + + const result = removeStep({ + existingTrigger: mockTrigger, + existingSteps: [step1, step2, step3], + stepIdToDelete: 'trigger', + stepToDeleteChildrenIds: ['1'], + }); + + expect(result.updatedTrigger).toEqual(null); + expect(result.updatedSteps).toEqual([step2, step3]); }); }); diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/utils/remove-step.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/utils/remove-step.ts index bbbddb2e37..deed95d759 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/utils/remove-step.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/utils/remove-step.ts @@ -1,6 +1,9 @@ import { isDefined } from 'twenty-shared/utils'; -import { WorkflowAction } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type'; +import { + WorkflowAction, + WorkflowActionType, +} from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type'; import { WorkflowTrigger } from 'src/modules/workflow/workflow-trigger/types/workflow-trigger.type'; const computeUpdatedNextStepIds = ({ @@ -25,7 +28,7 @@ const computeUpdatedNextStepIds = ({ ]; }; -export const removeStep = ({ +const removeRegularStep = ({ existingTrigger, existingSteps, stepIdToDelete, @@ -35,9 +38,30 @@ export const removeStep = ({ existingSteps: WorkflowAction[]; stepIdToDelete: string; stepToDeleteChildrenIds?: string[]; -}): { steps: WorkflowAction[]; trigger: WorkflowTrigger | null } => { +}): { + updatedSteps: WorkflowAction[]; + updatedTrigger: WorkflowTrigger | null; + removedStepIds: string[]; +} => { + const stepIdsToRemove = [stepIdToDelete]; + const stepIdsToRemoveChildrenIds = + stepToDeleteChildrenIds + ?.map((id) => { + const step = existingSteps.find((step) => step.id === id); + + if (step?.type === WorkflowActionType.FILTER) { + stepIdsToRemove.push(step.id); + + return step.nextStepIds; + } + + return id; + }) + .filter(isDefined) + .flat() ?? []; + const updatedSteps = existingSteps - .filter((step) => step.id !== stepIdToDelete) + .filter((step) => !stepIdsToRemove.includes(step.id)) .map((step) => { if (step.nextStepIds?.includes(stepIdToDelete)) { return { @@ -45,7 +69,7 @@ export const removeStep = ({ nextStepIds: computeUpdatedNextStepIds({ existingNextStepIds: step.nextStepIds, stepIdToDelete, - stepToDeleteChildrenIds, + stepToDeleteChildrenIds: stepIdsToRemoveChildrenIds, }), }; } @@ -62,11 +86,66 @@ export const removeStep = ({ nextStepIds: computeUpdatedNextStepIds({ existingNextStepIds: existingTrigger.nextStepIds, stepIdToDelete, - stepToDeleteChildrenIds, + stepToDeleteChildrenIds: stepIdsToRemoveChildrenIds, }), }; } } - return { trigger: updatedTrigger, steps: updatedSteps }; + return { + updatedTrigger, + updatedSteps, + removedStepIds: stepIdsToRemove, + }; +}; + +const removeTrigger = ({ + existingSteps, + triggerChildrenIds, +}: { + existingSteps: WorkflowAction[]; + triggerChildrenIds?: string[]; +}) => { + const stepIdsToRemove = + triggerChildrenIds?.filter((id) => { + const step = existingSteps.find((step) => step.id === id); + + return step?.type === WorkflowActionType.FILTER; + }) ?? []; + + const updatedSteps = existingSteps.filter( + (step) => !stepIdsToRemove.includes(step.id), + ); + + return { + updatedSteps, + updatedTrigger: null, + removedStepIds: ['trigger', ...stepIdsToRemove], + }; +}; + +export const removeStep = ({ + existingTrigger, + existingSteps, + stepIdToDelete, + stepToDeleteChildrenIds, +}: { + existingTrigger: WorkflowTrigger | null; + existingSteps: WorkflowAction[]; + stepIdToDelete: string; + stepToDeleteChildrenIds?: string[]; +}) => { + if (stepIdToDelete === 'trigger') { + return removeTrigger({ + existingSteps, + triggerChildrenIds: stepToDeleteChildrenIds, + }); + } else { + return removeRegularStep({ + existingTrigger, + existingSteps, + stepIdToDelete, + stepToDeleteChildrenIds, + }); + } }; diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step.workspace-service.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step.workspace-service.ts index 2768253f49..7aa8331935 100644 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step.workspace-service.ts +++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/workflow-version-step.workspace-service.ts @@ -10,6 +10,8 @@ import { v4 } from 'uuid'; import { BASE_TYPESCRIPT_PROJECT_INPUT_SCHEMA } from 'src/engine/core-modules/serverless/drivers/constants/base-typescript-project-input-schema'; import { CreateWorkflowVersionStepInput } from 'src/engine/core-modules/workflow/dtos/create-workflow-version-step-input.dto'; +import { WorkflowStepPositionInput } from 'src/engine/core-modules/workflow/dtos/update-workflow-step-position-input.dto'; +import { WorkflowVersionStepChangesDTO } from 'src/engine/core-modules/workflow/dtos/workflow-version-step-changes.dto'; import { AgentService } from 'src/engine/metadata-modules/agent/agent.service'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service'; @@ -21,6 +23,7 @@ import { import { WorkflowVersionWorkspaceEntity } from 'src/modules/workflow/common/standard-objects/workflow-version.workspace-entity'; import { assertWorkflowVersionIsDraft } from 'src/modules/workflow/common/utils/assert-workflow-version-is-draft.util'; import { WorkflowCommonWorkspaceService } from 'src/modules/workflow/common/workspace-services/workflow-common.workspace-service'; +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 { removeStep } from 'src/modules/workflow/workflow-builder/workflow-version-step/utils/remove-step'; @@ -32,9 +35,6 @@ import { } from 'src/modules/workflow/workflow-executor/workflow-actions/types/workflow-action.type'; import { WorkflowRunWorkspaceService } from 'src/modules/workflow/workflow-runner/workflow-run/workflow-run.workspace-service'; import { WorkflowRunnerWorkspaceService } from 'src/modules/workflow/workflow-runner/workspace-services/workflow-runner.workspace-service'; -import { WorkflowStepPositionInput } from 'src/engine/core-modules/workflow/dtos/update-workflow-step-position-input.dto'; -import { WorkflowVersionStepChangesDTO } from 'src/engine/core-modules/workflow/dtos/workflow-version-step-changes.dto'; -import { computeWorkflowVersionStepChanges } from 'src/modules/workflow/workflow-builder/utils/compute-workflow-version-step-updates.util'; const BASE_STEP_DEFINITION: BaseWorkflowActionSettings = { outputSchema: {}, @@ -225,51 +225,54 @@ export class WorkflowVersionStepWorkspaceService { ); } - if (stepIdToDelete === 'trigger') { - await workflowVersionRepository.update(workflowVersion.id, { - trigger: null, - }); - - return computeWorkflowVersionStepChanges({ - trigger: null, - steps: workflowVersion?.steps, - deletedStepId: stepIdToDelete, - }); - } - const existingTrigger = workflowVersion.trigger; + const isDeletingTrigger = + stepIdToDelete === 'trigger' && isDefined(existingTrigger); const stepToDelete = workflowVersion.steps.find( (step) => step.id === stepIdToDelete, ); - if (!isDefined(stepToDelete)) { + if (!isDefined(stepToDelete) && !isDeletingTrigger) { throw new WorkflowVersionStepException( "Can't delete not existing step", WorkflowVersionStepExceptionCode.NOT_FOUND, ); } - const workflowVersionUpdates = removeStep({ + const stepToDeleteChildrenIds = isDeletingTrigger + ? (existingTrigger?.nextStepIds ?? []) + : (stepToDelete?.nextStepIds ?? []); + + const { updatedSteps, updatedTrigger, removedStepIds } = removeStep({ existingTrigger, existingSteps: workflowVersion.steps, stepIdToDelete, - stepToDeleteChildrenIds: stepToDelete.nextStepIds, + stepToDeleteChildrenIds, }); - await workflowVersionRepository.update( - workflowVersion.id, - workflowVersionUpdates, + await workflowVersionRepository.update(workflowVersion.id, { + steps: updatedSteps, + trigger: updatedTrigger, + }); + + const removedSteps = workflowVersion.steps.filter((step) => + removedStepIds.includes(step.id), ); - await this.runWorkflowVersionStepDeletionSideEffects({ - step: stepToDelete, - workspaceId, - }); + await Promise.all( + removedSteps.map((step) => + this.runWorkflowVersionStepDeletionSideEffects({ + step, + workspaceId, + }), + ), + ); return computeWorkflowVersionStepChanges({ - ...workflowVersionUpdates, - deletedStepId: stepIdToDelete, + steps: updatedSteps, + trigger: updatedTrigger, + deletedStepIds: removedStepIds, }); }