From 88735f6788c63eac5764b27853e6e31a9c3b014e Mon Sep 17 00:00:00 2001 From: Thomas Trompette Date: Wed, 13 Aug 2025 18:02:13 +0200 Subject: [PATCH] Stop propagating full workflow in components (#13900) Workflow, workflow version and workflow runs should not be passed through props or context. These are set in recoil component states and that's where all hooks should look for these. This PR stop propagating workflow with version through all components. --- .../CommandMenuWorkflowEditStep.tsx | 2 +- .../CommandMenuWorkflowEditStepContent.tsx | 13 +-- ...ndMenuWorkflowSelectTriggerTypeContent.tsx | 2 +- .../record-show/components/CardComponents.tsx | 2 +- .../ServerlessFunctionExecutionResult.tsx | 2 +- .../hooks/useTestServerlessFunction.ts | 4 +- .../SettingsServerlessFunctionTestTab.tsx | 2 +- .../useServerlessFunctionUpdateFormState.ts | 2 +- .../useGetUpdatableWorkflowVersion.test.ts | 104 ++++++++++++------ .../hooks/useGetUpdatableWorkflowVersion.ts | 20 ---- .../useGetUpdatableWorkflowVersionOrThrow.ts | 37 +++++++ ...orkflowWithCurrentVersionIsDefined.test.ts | 16 --- ...sertWorkflowWithCurrentVersionIsDefined.ts | 15 --- .../WorkflowDiagramCanvasEditable.tsx | 44 ++++---- .../WorkflowDiagramDefaultEdgeEditable.tsx | 2 +- .../components/WorkflowDiagramEffect.tsx | 22 ++-- .../WorkflowDiagramFilterEdgeEditable.tsx | 11 +- .../WorkflowDiagramRightClickCommandMenu.tsx | 14 +-- .../WorkflowDiagramStepNodeEditable.tsx | 11 +- .../components/WorkflowVisualizer.tsx | 19 +--- .../hooks/__tests__/useCreateStep.test.tsx | 26 ++++- .../hooks/__tests__/useDeleteStep.test.ts | 76 +++++++------ .../hooks/__tests__/useUpdateStep.test.ts | 53 ++++----- .../workflow-steps/hooks/useCreateEdge.ts | 21 +--- .../workflow-steps/hooks/useCreateStep.ts | 11 +- .../workflow-steps/hooks/useDeleteEdge.ts | 22 +--- .../workflow-steps/hooks/useDeleteStep.ts | 22 +--- .../workflow-steps/hooks/useUpdateStep.ts | 27 +---- .../WorkflowEditActionServerlessFunction.tsx | 9 +- .../serverlessFunctionTestDataFamilyState.ts | 0 .../useUpdateWorkflowVersionTrigger.test.ts | 26 ++--- .../hooks/useUpdateWorkflowVersionTrigger.ts | 23 +--- .../hooks/useTidyUpWorkflowVersion.ts | 28 ++--- 33 files changed, 299 insertions(+), 389 deletions(-) delete mode 100644 packages/twenty-front/src/modules/workflow/hooks/useGetUpdatableWorkflowVersion.ts create mode 100644 packages/twenty-front/src/modules/workflow/hooks/useGetUpdatableWorkflowVersionOrThrow.ts delete mode 100644 packages/twenty-front/src/modules/workflow/utils/__tests__/assertWorkflowWithCurrentVersionIsDefined.test.ts delete mode 100644 packages/twenty-front/src/modules/workflow/utils/assertWorkflowWithCurrentVersionIsDefined.ts rename packages/twenty-front/src/modules/workflow/{ => workflow-steps/workflow-actions/code-action}/states/serverlessFunctionTestDataFamilyState.ts (100%) diff --git a/packages/twenty-front/src/modules/command-menu/pages/workflow/step/edit/components/CommandMenuWorkflowEditStep.tsx b/packages/twenty-front/src/modules/command-menu/pages/workflow/step/edit/components/CommandMenuWorkflowEditStep.tsx index 27e8892734..3c6b671df1 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/workflow/step/edit/components/CommandMenuWorkflowEditStep.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/workflow/step/edit/components/CommandMenuWorkflowEditStep.tsx @@ -25,7 +25,7 @@ export const CommandMenuWorkflowEditStep = () => { - + ); diff --git a/packages/twenty-front/src/modules/command-menu/pages/workflow/step/edit/components/CommandMenuWorkflowEditStepContent.tsx b/packages/twenty-front/src/modules/command-menu/pages/workflow/step/edit/components/CommandMenuWorkflowEditStepContent.tsx index ef7fbbbc1a..7590731958 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/workflow/step/edit/components/CommandMenuWorkflowEditStepContent.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/workflow/step/edit/components/CommandMenuWorkflowEditStepContent.tsx @@ -1,6 +1,5 @@ import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { useFlowOrThrow } from '@/workflow/hooks/useFlowOrThrow'; -import { type WorkflowWithCurrentVersion } from '@/workflow/types/Workflow'; import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState'; import { WorkflowStepDetail } from '@/workflow/workflow-steps/components/WorkflowStepDetail'; import { useUpdateStep } from '@/workflow/workflow-steps/hooks/useUpdateStep'; @@ -14,20 +13,14 @@ const StyledContainer = styled.div` height: 100%; `; -export const CommandMenuWorkflowEditStepContent = ({ - workflow, -}: { - workflow: WorkflowWithCurrentVersion; -}) => { +export const CommandMenuWorkflowEditStepContent = () => { const flow = useFlowOrThrow(); const workflowSelectedNode = useRecoilComponentValue( workflowSelectedNodeComponentState, ); - const { updateTrigger } = useUpdateWorkflowVersionTrigger({ workflow }); - const { updateStep } = useUpdateStep({ - workflow, - }); + const { updateTrigger } = useUpdateWorkflowVersionTrigger(); + const { updateStep } = useUpdateStep(); if (!isDefined(workflowSelectedNode)) { return null; diff --git a/packages/twenty-front/src/modules/command-menu/pages/workflow/trigger-type/components/CommandMenuWorkflowSelectTriggerTypeContent.tsx b/packages/twenty-front/src/modules/command-menu/pages/workflow/trigger-type/components/CommandMenuWorkflowSelectTriggerTypeContent.tsx index b55084b4ac..7c405bfb59 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/workflow/trigger-type/components/CommandMenuWorkflowSelectTriggerTypeContent.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/workflow/trigger-type/components/CommandMenuWorkflowSelectTriggerTypeContent.tsx @@ -24,7 +24,7 @@ export const CommandMenuWorkflowSelectTriggerTypeContent = ({ workflow: WorkflowWithCurrentVersion; }) => { const { getIcon } = useIcons(); - const { updateTrigger } = useUpdateWorkflowVersionTrigger({ workflow }); + const { updateTrigger } = useUpdateWorkflowVersionTrigger(); const { activeNonSystemObjectMetadataItems } = useFilteredObjectMetadataItems(); diff --git a/packages/twenty-front/src/modules/object-record/record-show/components/CardComponents.tsx b/packages/twenty-front/src/modules/object-record/record-show/components/CardComponents.tsx index 6a407fca56..956f658a07 100644 --- a/packages/twenty-front/src/modules/object-record/record-show/components/CardComponents.tsx +++ b/packages/twenty-front/src/modules/object-record/record-show/components/CardComponents.tsx @@ -158,7 +158,7 @@ export const CardComponents: Record = { > }> - + ); diff --git a/packages/twenty-front/src/modules/serverless-functions/components/ServerlessFunctionExecutionResult.tsx b/packages/twenty-front/src/modules/serverless-functions/components/ServerlessFunctionExecutionResult.tsx index 40c3ddb3ab..65182f635d 100644 --- a/packages/twenty-front/src/modules/serverless-functions/components/ServerlessFunctionExecutionResult.tsx +++ b/packages/twenty-front/src/modules/serverless-functions/components/ServerlessFunctionExecutionResult.tsx @@ -2,7 +2,7 @@ import { type ExecutionStatus, WorkflowStepExecutionResult, } from '@/workflow/components/WorkflowStepExecutionResult'; -import { type ServerlessFunctionTestData } from '@/workflow/states/serverlessFunctionTestDataFamilyState'; +import { type ServerlessFunctionTestData } from '@/workflow/workflow-steps/workflow-actions/code-action/states/serverlessFunctionTestDataFamilyState'; import { ServerlessFunctionExecutionStatus } from '~/generated-metadata/graphql'; export const ServerlessFunctionExecutionResult = ({ diff --git a/packages/twenty-front/src/modules/serverless-functions/hooks/useTestServerlessFunction.ts b/packages/twenty-front/src/modules/serverless-functions/hooks/useTestServerlessFunction.ts index 585f619666..04c208aa1b 100644 --- a/packages/twenty-front/src/modules/serverless-functions/hooks/useTestServerlessFunction.ts +++ b/packages/twenty-front/src/modules/serverless-functions/hooks/useTestServerlessFunction.ts @@ -1,9 +1,9 @@ import { useExecuteOneServerlessFunction } from '@/settings/serverless-functions/hooks/useExecuteOneServerlessFunction'; -import { serverlessFunctionTestDataFamilyState } from '@/workflow/states/serverlessFunctionTestDataFamilyState'; +import { serverlessFunctionTestDataFamilyState } from '@/workflow/workflow-steps/workflow-actions/code-action/states/serverlessFunctionTestDataFamilyState'; import { useState } from 'react'; import { useRecoilState } from 'recoil'; -import { sleep } from '~/utils/sleep'; import { isDefined } from 'twenty-shared/utils'; +import { sleep } from '~/utils/sleep'; export const useTestServerlessFunction = ({ serverlessFunctionId, diff --git a/packages/twenty-front/src/modules/settings/serverless-functions/components/tabs/SettingsServerlessFunctionTestTab.tsx b/packages/twenty-front/src/modules/settings/serverless-functions/components/tabs/SettingsServerlessFunctionTestTab.tsx index e32f219051..b40aa951da 100644 --- a/packages/twenty-front/src/modules/settings/serverless-functions/components/tabs/SettingsServerlessFunctionTestTab.tsx +++ b/packages/twenty-front/src/modules/settings/serverless-functions/components/tabs/SettingsServerlessFunctionTestTab.tsx @@ -1,5 +1,5 @@ import { ServerlessFunctionExecutionResult } from '@/serverless-functions/components/ServerlessFunctionExecutionResult'; -import { serverlessFunctionTestDataFamilyState } from '@/workflow/states/serverlessFunctionTestDataFamilyState'; +import { serverlessFunctionTestDataFamilyState } from '@/workflow/workflow-steps/workflow-actions/code-action/states/serverlessFunctionTestDataFamilyState'; import styled from '@emotion/styled'; import { useRecoilState } from 'recoil'; import { H2Title, IconPlayerPlay } from 'twenty-ui/display'; diff --git a/packages/twenty-front/src/modules/settings/serverless-functions/hooks/useServerlessFunctionUpdateFormState.ts b/packages/twenty-front/src/modules/settings/serverless-functions/hooks/useServerlessFunctionUpdateFormState.ts index 09bd194356..f77bcc2b7f 100644 --- a/packages/twenty-front/src/modules/settings/serverless-functions/hooks/useServerlessFunctionUpdateFormState.ts +++ b/packages/twenty-front/src/modules/settings/serverless-functions/hooks/useServerlessFunctionUpdateFormState.ts @@ -2,7 +2,7 @@ import { INDEX_FILE_PATH } from '@/serverless-functions/constants/IndexFilePath' import { getFunctionInputFromSourceCode } from '@/serverless-functions/utils/getFunctionInputFromSourceCode'; import { useGetOneServerlessFunction } from '@/settings/serverless-functions/hooks/useGetOneServerlessFunction'; import { useGetOneServerlessFunctionSourceCode } from '@/settings/serverless-functions/hooks/useGetOneServerlessFunctionSourceCode'; -import { serverlessFunctionTestDataFamilyState } from '@/workflow/states/serverlessFunctionTestDataFamilyState'; +import { serverlessFunctionTestDataFamilyState } from '@/workflow/workflow-steps/workflow-actions/code-action/states/serverlessFunctionTestDataFamilyState'; import { type Dispatch, type SetStateAction, useState } from 'react'; import { useRecoilState } from 'recoil'; import { type FindOneServerlessFunctionSourceCodeQuery } from '~/generated-metadata/graphql'; diff --git a/packages/twenty-front/src/modules/workflow/hooks/__tests__/useGetUpdatableWorkflowVersion.test.ts b/packages/twenty-front/src/modules/workflow/hooks/__tests__/useGetUpdatableWorkflowVersion.test.ts index 036444ae22..92c93f2ae9 100644 --- a/packages/twenty-front/src/modules/workflow/hooks/__tests__/useGetUpdatableWorkflowVersion.test.ts +++ b/packages/twenty-front/src/modules/workflow/hooks/__tests__/useGetUpdatableWorkflowVersion.test.ts @@ -1,8 +1,16 @@ -import { renderHook } from '@testing-library/react'; -import { useGetUpdatableWorkflowVersion } from '@/workflow/hooks/useGetUpdatableWorkflowVersion'; +import { useGetUpdatableWorkflowVersionOrThrow } from '@/workflow/hooks/useGetUpdatableWorkflowVersionOrThrow'; import { type WorkflowWithCurrentVersion } from '@/workflow/types/Workflow'; +import { renderHook } from '@testing-library/react'; const mockCreateDraftFromWorkflowVersion = jest.fn().mockResolvedValue('457'); +const mockWorkflowId = '123'; +const mockWorkflow = { + id: mockWorkflowId, + currentVersion: { + id: '456', + status: 'DRAFT', + }, +} as WorkflowWithCurrentVersion; jest.mock('@/workflow/hooks/useCreateDraftFromWorkflowVersion', () => ({ useCreateDraftFromWorkflowVersion: () => ({ @@ -10,43 +18,75 @@ jest.mock('@/workflow/hooks/useCreateDraftFromWorkflowVersion', () => ({ }), })); -describe('useGetUpdatableWorkflowVersion', () => { - const mockWorkflow = (status: 'ACTIVE' | 'DRAFT') => - ({ - id: '123', - __typename: 'Workflow', - statuses: [], - lastPublishedVersionId: '1', - name: 'toto', - versions: [], - currentVersion: { - id: '456', - name: 'toto', - createdAt: '2024-07-03T20:03:35.064Z', - updatedAt: '2024-07-03T20:03:35.064Z', - workflowId: '123', - __typename: 'WorkflowVersion', - status, - steps: [], - trigger: null, - }, - }) as WorkflowWithCurrentVersion; +jest.mock( + '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue', + () => ({ + useRecoilComponentValue: jest.fn(() => mockWorkflowId), + }), +); - it('should not create workflow version if draft version exists', async () => { - const { result } = renderHook(() => useGetUpdatableWorkflowVersion()); - const workflowVersionId = await result.current.getUpdatableWorkflowVersion( - mockWorkflow('DRAFT'), +jest.mock('@/workflow/hooks/useWorkflowWithCurrentVersion', () => ({ + useWorkflowWithCurrentVersion: jest.fn((workflowId) => + workflowId === mockWorkflowId ? mockWorkflow : undefined, + ), +})); + +describe('useGetUpdatableWorkflowVersionOrThrow', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should return draft version id when current version is draft', async () => { + const { result } = renderHook(() => + useGetUpdatableWorkflowVersionOrThrow(), ); + const workflowVersionId = + await result.current.getUpdatableWorkflowVersion(); + expect(mockCreateDraftFromWorkflowVersion).not.toHaveBeenCalled(); expect(workflowVersionId).toEqual('456'); }); - it('should create workflow version if no draft version exists', async () => { - const { result } = renderHook(() => useGetUpdatableWorkflowVersion()); - const workflowVersionId = await result.current.getUpdatableWorkflowVersion( - mockWorkflow('ACTIVE'), + it('should create draft from active version when current version is active', async () => { + // Mock the workflow to have an active version + const mockActiveWorkflow = { + ...mockWorkflow, + currentVersion: { + ...mockWorkflow.currentVersion, + status: 'ACTIVE', + }, + } as WorkflowWithCurrentVersion; + + const { + useWorkflowWithCurrentVersion, + } = require('@/workflow/hooks/useWorkflowWithCurrentVersion'); + useWorkflowWithCurrentVersion.mockReturnValue(mockActiveWorkflow); + + const { result } = renderHook(() => + useGetUpdatableWorkflowVersionOrThrow(), ); - expect(mockCreateDraftFromWorkflowVersion).toHaveBeenCalled(); + const workflowVersionId = + await result.current.getUpdatableWorkflowVersion(); + + expect(mockCreateDraftFromWorkflowVersion).toHaveBeenCalledWith({ + workflowId: mockWorkflowId, + workflowVersionIdToCopy: '456', + }); expect(workflowVersionId).toEqual('457'); }); + + it('should throw an error when workflow is not found', async () => { + const { + useWorkflowWithCurrentVersion, + } = require('@/workflow/hooks/useWorkflowWithCurrentVersion'); + useWorkflowWithCurrentVersion.mockReturnValue(undefined); + + const { result } = renderHook(() => + useGetUpdatableWorkflowVersionOrThrow(), + ); + + await expect(result.current.getUpdatableWorkflowVersion()).rejects.toThrow( + 'Failed to get updatable workflow version', + ); + }); }); diff --git a/packages/twenty-front/src/modules/workflow/hooks/useGetUpdatableWorkflowVersion.ts b/packages/twenty-front/src/modules/workflow/hooks/useGetUpdatableWorkflowVersion.ts deleted file mode 100644 index 327cdd043f..0000000000 --- a/packages/twenty-front/src/modules/workflow/hooks/useGetUpdatableWorkflowVersion.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { type WorkflowWithCurrentVersion } from '@/workflow/types/Workflow'; -import { useCreateDraftFromWorkflowVersion } from '@/workflow/hooks/useCreateDraftFromWorkflowVersion'; - -export const useGetUpdatableWorkflowVersion = () => { - const { createDraftFromWorkflowVersion } = - useCreateDraftFromWorkflowVersion(); - const getUpdatableWorkflowVersion = async ( - workflow: WorkflowWithCurrentVersion, - ) => { - if (workflow.currentVersion.status === 'DRAFT') { - return workflow.currentVersion.id; - } - return await createDraftFromWorkflowVersion({ - workflowId: workflow.id, - workflowVersionIdToCopy: workflow.currentVersion.id, - }); - }; - - return { getUpdatableWorkflowVersion }; -}; diff --git a/packages/twenty-front/src/modules/workflow/hooks/useGetUpdatableWorkflowVersionOrThrow.ts b/packages/twenty-front/src/modules/workflow/hooks/useGetUpdatableWorkflowVersionOrThrow.ts new file mode 100644 index 0000000000..7e1eac57bf --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/hooks/useGetUpdatableWorkflowVersionOrThrow.ts @@ -0,0 +1,37 @@ +import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useCreateDraftFromWorkflowVersion } from '@/workflow/hooks/useCreateDraftFromWorkflowVersion'; +import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion'; +import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState'; +import { isDefined } from 'twenty-shared/utils'; + +export const useGetUpdatableWorkflowVersionOrThrow = () => { + const { createDraftFromWorkflowVersion } = + useCreateDraftFromWorkflowVersion(); + const workflowVisualizerWorkflowId = useRecoilComponentValue( + workflowVisualizerWorkflowIdComponentState, + ); + const workflow = useWorkflowWithCurrentVersion(workflowVisualizerWorkflowId); + + const getUpdatableWorkflowVersion = async (): Promise => { + if (!isDefined(workflowVisualizerWorkflowId) || !isDefined(workflow)) { + throw new Error('Failed to get updatable workflow version'); + } + + if (workflow.currentVersion.status === 'DRAFT') { + return workflow.currentVersion.id; + } + + const draftVersionId = await createDraftFromWorkflowVersion({ + workflowId: workflowVisualizerWorkflowId, + workflowVersionIdToCopy: workflow.currentVersion.id, + }); + + if (!isDefined(draftVersionId)) { + throw new Error('Failed to create draft version'); + } + + return draftVersionId; + }; + + return { getUpdatableWorkflowVersion }; +}; diff --git a/packages/twenty-front/src/modules/workflow/utils/__tests__/assertWorkflowWithCurrentVersionIsDefined.test.ts b/packages/twenty-front/src/modules/workflow/utils/__tests__/assertWorkflowWithCurrentVersionIsDefined.test.ts deleted file mode 100644 index af417e2801..0000000000 --- a/packages/twenty-front/src/modules/workflow/utils/__tests__/assertWorkflowWithCurrentVersionIsDefined.test.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { type WorkflowWithCurrentVersion } from '@/workflow/types/Workflow'; -import { assertWorkflowWithCurrentVersionIsDefined } from '../assertWorkflowWithCurrentVersionIsDefined'; - -it('throws when provided workflow is undefined', () => { - expect(() => { - assertWorkflowWithCurrentVersionIsDefined(undefined); - }).toThrow(); -}); - -it("throws when provided workflow's current version is undefined", () => { - expect(() => { - assertWorkflowWithCurrentVersionIsDefined( - {} as unknown as WorkflowWithCurrentVersion, - ); - }).toThrow(); -}); diff --git a/packages/twenty-front/src/modules/workflow/utils/assertWorkflowWithCurrentVersionIsDefined.ts b/packages/twenty-front/src/modules/workflow/utils/assertWorkflowWithCurrentVersionIsDefined.ts deleted file mode 100644 index 4e3350815c..0000000000 --- a/packages/twenty-front/src/modules/workflow/utils/assertWorkflowWithCurrentVersionIsDefined.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { - type Workflow, - type WorkflowVersion, - type WorkflowWithCurrentVersion, -} from '@/workflow/types/Workflow'; -import { isDefined } from 'twenty-shared/utils'; - -// eslint-disable-next-line prefer-arrow/prefer-arrow-functions -export function assertWorkflowWithCurrentVersionIsDefined( - workflow: WorkflowWithCurrentVersion | undefined, -): asserts workflow is Workflow & { currentVersion: WorkflowVersion } { - if (!isDefined(workflow) || !isDefined(workflow.currentVersion)) { - throw new Error('Expected workflow and its current version to be defined'); - } -} diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasEditable.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasEditable.tsx index d006ba26b7..a7651b61c2 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasEditable.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasEditable.tsx @@ -1,5 +1,7 @@ +import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; -import { type WorkflowWithCurrentVersion } from '@/workflow/types/Workflow'; +import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion'; +import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState'; import { WorkflowDiagramBlankEdge } from '@/workflow/workflow-diagram/components/WorkflowDiagramBlankEdge'; import { WorkflowDiagramCanvasBase } from '@/workflow/workflow-diagram/components/WorkflowDiagramCanvasBase'; import { WorkflowDiagramCanvasEditableEffect } from '@/workflow/workflow-diagram/components/WorkflowDiagramCanvasEditableEffect'; @@ -23,14 +25,14 @@ import { addEdge, type Connection, ReactFlowProvider } from '@xyflow/react'; import React from 'react'; import { isDefined } from 'twenty-shared/utils'; -export const WorkflowDiagramCanvasEditable = ({ - workflowWithCurrentVersion, -}: { - workflowWithCurrentVersion: WorkflowWithCurrentVersion; -}) => { - const tagProps = getWorkflowVersionStatusTagProps({ - workflowVersionStatus: workflowWithCurrentVersion.currentVersion.status, - }); +export const WorkflowDiagramCanvasEditable = () => { + const workflowVisualizerWorkflowId = useRecoilComponentValue( + workflowVisualizerWorkflowIdComponentState, + ); + + const workflowWithCurrentVersion = useWorkflowWithCurrentVersion( + workflowVisualizerWorkflowId, + ); const setWorkflowDiagram = useSetRecoilComponentState( workflowDiagramComponentState, @@ -40,21 +42,13 @@ export const WorkflowDiagramCanvasEditable = ({ workflowDiagramRightClickMenuPositionState, ); - const { createEdge } = useCreateEdge({ - workflow: workflowWithCurrentVersion, - }); + const { createEdge } = useCreateEdge(); - const { deleteEdge } = useDeleteEdge({ - workflow: workflowWithCurrentVersion, - }); + const { deleteEdge } = useDeleteEdge(); - const { updateStep } = useUpdateStep({ - workflow: workflowWithCurrentVersion, - }); + const { updateStep } = useUpdateStep(); - const { updateTrigger } = useUpdateWorkflowVersionTrigger({ - workflow: workflowWithCurrentVersion, - }); + const { updateTrigger } = useUpdateWorkflowVersionTrigger(); const onConnect = (edgeConnect: Connection) => { setWorkflowDiagram((diagram) => { @@ -107,6 +101,14 @@ export const WorkflowDiagramCanvasEditable = ({ } }; + if (!isDefined(workflowWithCurrentVersion)) { + return null; + } + + const tagProps = getWorkflowVersionStatusTagProps({ + workflowVersionStatus: workflowWithCurrentVersion.currentVersion.status, + }); + const handlePaneContextMenu = ({ x, y }: { x: number; y: number }) => { setWorkflowDiagramRightClickMenuPosition({ x, diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramDefaultEdgeEditable.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramDefaultEdgeEditable.tsx index efbf80695b..cd1fa29ae0 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramDefaultEdgeEditable.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramDefaultEdgeEditable.tsx @@ -61,7 +61,7 @@ export const WorkflowDiagramDefaultEdgeEditable = ({ const { createStep } = useCreateStep({ workflow }); - const { deleteEdge } = useDeleteEdge({ workflow }); + const { deleteEdge } = useDeleteEdge(); const { startNodeCreation, isNodeCreationStarted } = useStartNodeCreation(); diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramEffect.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramEffect.tsx index e3e50e2f4b..b1dfbddea0 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramEffect.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramEffect.tsx @@ -1,13 +1,13 @@ import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; +import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; import { useStepsOutputSchema } from '@/workflow/hooks/useStepsOutputSchema'; +import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion'; import { flowComponentState } from '@/workflow/states/flowComponentState'; import { workflowLastCreatedStepIdComponentState } from '@/workflow/states/workflowLastCreatedStepIdComponentState'; -import { - type WorkflowVersion, - type WorkflowWithCurrentVersion, -} from '@/workflow/types/Workflow'; +import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState'; +import { type WorkflowVersion } from '@/workflow/types/Workflow'; import { workflowDiagramComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramComponentState'; import { getWorkflowVersionDiagram } from '@/workflow/workflow-diagram/utils/getWorkflowVersionDiagram'; @@ -18,11 +18,15 @@ import { useRecoilCallback } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; import { FeatureFlagKey } from '~/generated/graphql'; -export const WorkflowDiagramEffect = ({ - workflowWithCurrentVersion, -}: { - workflowWithCurrentVersion: WorkflowWithCurrentVersion | undefined; -}) => { +export const WorkflowDiagramEffect = () => { + const workflowVisualizerWorkflowId = useRecoilComponentValue( + workflowVisualizerWorkflowIdComponentState, + ); + + const workflowWithCurrentVersion = useWorkflowWithCurrentVersion( + workflowVisualizerWorkflowId, + ); + const workflowDiagramState = useRecoilComponentCallbackState( workflowDiagramComponentState, ); diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramFilterEdgeEditable.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramFilterEdgeEditable.tsx index 3ee717c3ca..558731e9ef 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramFilterEdgeEditable.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramFilterEdgeEditable.tsx @@ -9,8 +9,6 @@ import { useOpenDropdown } from '@/ui/layout/dropdown/hooks/useOpenDropdown'; import { isDropdownOpenComponentState } from '@/ui/layout/dropdown/states/isDropdownOpenComponentState'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; -import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion'; -import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState'; import { WorkflowDiagramBaseEdge } from '@/workflow/workflow-diagram/components/WorkflowDiagramBaseEdge'; import { WorkflowDiagramEdgeButtonGroup } from '@/workflow/workflow-diagram/components/WorkflowDiagramEdgeButtonGroup'; import { WorkflowDiagramEdgeV2Container } from '@/workflow/workflow-diagram/components/WorkflowDiagramEdgeV2Container'; @@ -107,15 +105,10 @@ export const WorkflowDiagramFilterEdgeEditable = ({ targetY, }); - const workflowVisualizerWorkflowId = useRecoilComponentValue( - workflowVisualizerWorkflowIdComponentState, - ); - const workflow = useWorkflowWithCurrentVersion(workflowVisualizerWorkflowId); - const { isInRightDrawer } = useContext(ActionMenuContext); - const { deleteStep } = useDeleteStep({ workflow }); - const { deleteEdge } = useDeleteEdge({ workflow }); + const { deleteStep } = useDeleteStep(); + const { deleteEdge } = useDeleteEdge(); const { startNodeCreation, isNodeCreationStarted } = useStartNodeCreation(); const setCommandMenuNavigationStack = useSetRecoilState( diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramRightClickCommandMenu.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramRightClickCommandMenu.tsx index 0f9880920c..0c1e729a9d 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramRightClickCommandMenu.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramRightClickCommandMenu.tsx @@ -1,6 +1,4 @@ import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; -import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion'; -import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState'; import { useCloseRightClickMenu } from '@/workflow/workflow-diagram/hooks/useCloseRightClickMenu'; import { useStartNodeCreation } from '@/workflow/workflow-diagram/hooks/useStartNodeCreation'; import { useWorkflowDiagramScreenToFlowPosition } from '@/workflow/workflow-diagram/hooks/useWorkflowDiagramScreenToFlowPosition'; @@ -43,17 +41,7 @@ export const WorkflowDiagramRightClickCommandMenu = () => { workflowDiagramRightClickMenuPositionState, ); - const workflowVisualizerWorkflowId = useRecoilComponentValue( - workflowVisualizerWorkflowIdComponentState, - ); - - const workflowWithCurrentVersion = useWorkflowWithCurrentVersion( - workflowVisualizerWorkflowId, - ); - - const { tidyUpWorkflowVersion } = useTidyUpWorkflowVersion({ - workflow: workflowWithCurrentVersion, - }); + const { tidyUpWorkflowVersion } = useTidyUpWorkflowVersion(); const handleReorderWorkflowDiagram = async () => { await tidyUpWorkflowVersion(); diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramStepNodeEditable.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramStepNodeEditable.tsx index 7455dea9e9..1559eca3a5 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramStepNodeEditable.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramStepNodeEditable.tsx @@ -3,9 +3,7 @@ import { useWorkflowCommandMenu } from '@/command-menu/hooks/useWorkflowCommandM import { commandMenuNavigationStackState } from '@/command-menu/states/commandMenuNavigationStackState'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; -import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion'; import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState'; -import { assertWorkflowWithCurrentVersionIsDefined } from '@/workflow/utils/assertWorkflowWithCurrentVersionIsDefined'; import { WorkflowDiagramStepNodeEditableContent } from '@/workflow/workflow-diagram/components/WorkflowDiagramStepNodeEditableContent'; import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState'; import { type WorkflowDiagramStepNodeData } from '@/workflow/workflow-diagram/types/WorkflowDiagram'; @@ -31,14 +29,7 @@ export const WorkflowDiagramStepNodeEditable = ({ workflowVisualizerWorkflowIdComponentState, ); - const workflowWithCurrentVersion = useWorkflowWithCurrentVersion( - workflowVisualizerWorkflowId, - ); - assertWorkflowWithCurrentVersionIsDefined(workflowWithCurrentVersion); - - const { deleteStep } = useDeleteStep({ - workflow: workflowWithCurrentVersion, - }); + const { deleteStep } = useDeleteStep(); const setWorkflowSelectedNode = useSetRecoilComponentState( workflowSelectedNodeComponentState, diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowVisualizer.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowVisualizer.tsx index 16b1418b62..531ec6f69b 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowVisualizer.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowVisualizer.tsx @@ -1,24 +1,11 @@ -import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion'; import { WorkflowDiagramCanvasEditable } from '@/workflow/workflow-diagram/components/WorkflowDiagramCanvasEditable'; import { WorkflowDiagramEffect } from '@/workflow/workflow-diagram/components/WorkflowDiagramEffect'; -import '@xyflow/react/dist/style.css'; -import { isDefined } from 'twenty-shared/utils'; - -export const WorkflowVisualizer = ({ workflowId }: { workflowId: string }) => { - const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(workflowId); - - if (!isDefined(workflowWithCurrentVersion)) { - return null; - } +export const WorkflowVisualizer = () => { return ( <> - - + + ); }; diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/__tests__/useCreateStep.test.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/__tests__/useCreateStep.test.tsx index 622849b79e..234064845e 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/__tests__/useCreateStep.test.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/__tests__/useCreateStep.test.tsx @@ -4,7 +4,7 @@ import { RecoilRoot } from 'recoil'; import { WorkflowVisualizerComponentInstanceContext } from '../../../workflow-diagram/states/contexts/WorkflowVisualizerComponentInstanceContext'; import { useCreateStep } from '../useCreateStep'; -const mockCreateDraftFromWorkflowVersion = jest.fn().mockResolvedValue('457'); +const mockGetUpdatableWorkflowVersion = jest.fn(); const mockCreateWorkflowVersionStep = jest.fn().mockResolvedValue({ data: { createWorkflowVersionStep: { createdStep: { id: '1', type: 'CODE' } }, @@ -20,9 +20,9 @@ jest.mock( }), ); -jest.mock('@/workflow/hooks/useCreateDraftFromWorkflowVersion', () => ({ - useCreateDraftFromWorkflowVersion: () => ({ - createDraftFromWorkflowVersion: mockCreateDraftFromWorkflowVersion, +jest.mock('@/workflow/hooks/useGetUpdatableWorkflowVersionOrThrow', () => ({ + useGetUpdatableWorkflowVersionOrThrow: () => ({ + getUpdatableWorkflowVersion: mockGetUpdatableWorkflowVersion, }), })); @@ -55,7 +55,14 @@ describe('useCreateStep', () => { versions: [], }; - it('should create step in draft version', async () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should create step in workflow version', async () => { + const mockWorkflowVersionId = 'version-123'; + mockGetUpdatableWorkflowVersion.mockResolvedValue(mockWorkflowVersionId); + const { result } = renderHook( () => useCreateStep({ @@ -74,6 +81,13 @@ describe('useCreateStep', () => { }); }); - expect(mockCreateWorkflowVersionStep).toHaveBeenCalled(); + expect(mockGetUpdatableWorkflowVersion).toHaveBeenCalled(); + expect(mockCreateWorkflowVersionStep).toHaveBeenCalledWith({ + workflowVersionId: mockWorkflowVersionId, + stepType: 'CODE', + parentStepId: 'parent-step-id', + nextStepId: undefined, + position: undefined, + }); }); }); diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/__tests__/useDeleteStep.test.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/__tests__/useDeleteStep.test.ts index 994b70b023..3fb8f43f1f 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/__tests__/useDeleteStep.test.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/__tests__/useDeleteStep.test.ts @@ -1,17 +1,10 @@ -import { type WorkflowWithCurrentVersion } from '@/workflow/types/Workflow'; import { useDeleteStep } from '@/workflow/workflow-steps/hooks/useDeleteStep'; import { renderHook } from '@testing-library/react'; -import { RecoilRoot } from 'recoil'; const mockDeleteWorkflowVersionStep = jest.fn(); -const updateOneRecordMock = jest.fn(); -const mockCreateDraftFromWorkflowVersion = jest.fn().mockResolvedValue('457'); - -jest.mock('@/object-record/hooks/useUpdateOneRecord', () => ({ - useUpdateOneRecord: () => ({ - updateOneRecord: updateOneRecordMock, - }), -})); +const mockGetUpdatableWorkflowVersion = jest.fn(); +const mockDeleteStepsOutputSchema = jest.fn(); +const mockCloseCommandMenu = jest.fn(); jest.mock('@/workflow/hooks/useDeleteWorkflowVersionStep', () => ({ useDeleteWorkflowVersionStep: () => ({ @@ -19,34 +12,51 @@ jest.mock('@/workflow/hooks/useDeleteWorkflowVersionStep', () => ({ }), })); -jest.mock('@/workflow/hooks/useCreateDraftFromWorkflowVersion', () => ({ - useCreateDraftFromWorkflowVersion: () => ({ - createDraftFromWorkflowVersion: mockCreateDraftFromWorkflowVersion, +jest.mock('@/workflow/hooks/useGetUpdatableWorkflowVersionOrThrow', () => ({ + useGetUpdatableWorkflowVersionOrThrow: () => ({ + getUpdatableWorkflowVersion: mockGetUpdatableWorkflowVersion, + }), +})); + +jest.mock('@/workflow/hooks/useStepsOutputSchema', () => ({ + useStepsOutputSchema: () => ({ + deleteStepsOutputSchema: mockDeleteStepsOutputSchema, + }), +})); + +jest.mock('@/command-menu/hooks/useCommandMenu', () => ({ + useCommandMenu: () => ({ + closeCommandMenu: mockCloseCommandMenu, }), })); describe('useDeleteStep', () => { - const mockWorkflow = { - id: '123', - currentVersion: { - id: '456', - status: 'DRAFT', - steps: [], - trigger: { type: 'manual' }, - }, - versions: [], - }; + beforeEach(() => { + jest.clearAllMocks(); + }); - it('should delete step in draft version', async () => { - const { result } = renderHook( - () => - useDeleteStep({ - workflow: mockWorkflow as unknown as WorkflowWithCurrentVersion, - }), - { wrapper: RecoilRoot }, - ); - await result.current.deleteStep('1'); + it('should delete step and clean up dependencies', async () => { + const mockWorkflowVersionId = 'version-123'; + const mockStepId = 'step-1'; + const mockDeletedStepIds = ['step-1', 'step-2']; - expect(mockDeleteWorkflowVersionStep).toHaveBeenCalled(); + mockGetUpdatableWorkflowVersion.mockResolvedValue(mockWorkflowVersionId); + mockDeleteWorkflowVersionStep.mockResolvedValue({ + deletedStepIds: mockDeletedStepIds, + }); + + const { result } = renderHook(() => useDeleteStep()); + await result.current.deleteStep(mockStepId); + + expect(mockGetUpdatableWorkflowVersion).toHaveBeenCalled(); + expect(mockDeleteWorkflowVersionStep).toHaveBeenCalledWith({ + workflowVersionId: mockWorkflowVersionId, + stepId: mockStepId, + }); + expect(mockCloseCommandMenu).toHaveBeenCalled(); + expect(mockDeleteStepsOutputSchema).toHaveBeenCalledWith({ + stepIds: mockDeletedStepIds, + workflowVersionId: mockWorkflowVersionId, + }); }); }); diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/__tests__/useUpdateStep.test.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/__tests__/useUpdateStep.test.ts index 98fcfb4120..ac68bf0f12 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/__tests__/useUpdateStep.test.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/__tests__/useUpdateStep.test.ts @@ -1,15 +1,8 @@ -import { type WorkflowWithCurrentVersion } from '@/workflow/types/Workflow'; import { useUpdateStep } from '@/workflow/workflow-steps/hooks/useUpdateStep'; import { renderHook } from '@testing-library/react'; const mockUpdateWorkflowVersionStep = jest.fn(); -const mockCreateDraftFromWorkflowVersion = jest.fn().mockResolvedValue('457'); - -jest.mock('recoil', () => ({ - useRecoilValue: () => 'parent-step-id', - useSetRecoilState: () => jest.fn(), - atom: (params: any) => params, -})); +const mockGetUpdatableWorkflowVersion = jest.fn(); jest.mock( '@/workflow/workflow-steps/hooks/useUpdateWorkflowVersionStep', @@ -20,35 +13,24 @@ jest.mock( }), ); -jest.mock('@/workflow/hooks/useCreateDraftFromWorkflowVersion', () => ({ - useCreateDraftFromWorkflowVersion: () => ({ - createDraftFromWorkflowVersion: mockCreateDraftFromWorkflowVersion, +jest.mock('@/workflow/hooks/useGetUpdatableWorkflowVersionOrThrow', () => ({ + useGetUpdatableWorkflowVersionOrThrow: () => ({ + getUpdatableWorkflowVersion: mockGetUpdatableWorkflowVersion, }), })); describe('useUpdateStep', () => { - const mockWorkflow = { - id: '123', - currentVersion: { - id: '456', - status: 'DRAFT', - steps: [], - trigger: { type: 'manual' }, - }, - versions: [], - }; + beforeEach(() => { + jest.clearAllMocks(); + }); - it('should update step in draft version', async () => { - const { result } = renderHook(() => - useUpdateStep({ - workflow: mockWorkflow as unknown as WorkflowWithCurrentVersion, - }), - ); - await result.current.updateStep({ + it('should update step in workflow version', async () => { + const mockWorkflowVersionId = 'version-123'; + const mockStep = { id: '1', name: 'name', valid: true, - type: 'CODE', + type: 'CODE' as const, settings: { input: { serverlessFunctionId: 'id', @@ -65,8 +47,17 @@ describe('useUpdateStep', () => { }, }, }, - }); + }; - expect(mockUpdateWorkflowVersionStep).toHaveBeenCalled(); + mockGetUpdatableWorkflowVersion.mockResolvedValue(mockWorkflowVersionId); + + const { result } = renderHook(() => useUpdateStep()); + await result.current.updateStep(mockStep); + + expect(mockGetUpdatableWorkflowVersion).toHaveBeenCalled(); + expect(mockUpdateWorkflowVersionStep).toHaveBeenCalledWith({ + workflowVersionId: mockWorkflowVersionId, + step: mockStep, + }); }); }); diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useCreateEdge.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useCreateEdge.ts index 2e4ff77e67..d7f44ab018 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useCreateEdge.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useCreateEdge.ts @@ -1,20 +1,15 @@ -import { useGetUpdatableWorkflowVersion } from '@/workflow/hooks/useGetUpdatableWorkflowVersion'; -import { type WorkflowWithCurrentVersion } from '@/workflow/types/Workflow'; +import { useGetUpdatableWorkflowVersionOrThrow } from '@/workflow/hooks/useGetUpdatableWorkflowVersionOrThrow'; +import { type WorkflowDiagramEdge } from '@/workflow/workflow-diagram/types/WorkflowDiagramEdge'; import { useCreateWorkflowVersionEdge } from '@/workflow/workflow-steps/hooks/useCreateWorkflowVersionEdge'; import { useState } from 'react'; -import { isDefined } from 'twenty-shared/utils'; -import { type WorkflowDiagramEdge } from '@/workflow/workflow-diagram/types/WorkflowDiagramEdge'; -export const useCreateEdge = ({ - workflow, -}: { - workflow: WorkflowWithCurrentVersion; -}) => { +export const useCreateEdge = () => { const [isLoading, setIsLoading] = useState(false); const { createWorkflowVersionEdge } = useCreateWorkflowVersionEdge(); - const { getUpdatableWorkflowVersion } = useGetUpdatableWorkflowVersion(); + const { getUpdatableWorkflowVersion } = + useGetUpdatableWorkflowVersionOrThrow(); const createEdge = async ({ source, target }: WorkflowDiagramEdge) => { if (isLoading) { @@ -24,11 +19,7 @@ export const useCreateEdge = ({ setIsLoading(true); try { - const workflowVersionId = await getUpdatableWorkflowVersion(workflow); - - if (!isDefined(workflowVersionId)) { - throw new Error('Cannot find a workflow version to update'); - } + const workflowVersionId = await getUpdatableWorkflowVersion(); const createdEdge = ( await createWorkflowVersionEdge({ diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useCreateStep.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useCreateStep.ts index c49bd68426..0cccd36021 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useCreateStep.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useCreateStep.ts @@ -1,5 +1,5 @@ import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; -import { useGetUpdatableWorkflowVersion } from '@/workflow/hooks/useGetUpdatableWorkflowVersion'; +import { useGetUpdatableWorkflowVersionOrThrow } from '@/workflow/hooks/useGetUpdatableWorkflowVersionOrThrow'; import { workflowLastCreatedStepIdComponentState } from '@/workflow/states/workflowLastCreatedStepIdComponentState'; import { type WorkflowStepType, @@ -24,7 +24,8 @@ export const useCreateStep = ({ workflowLastCreatedStepIdComponentState, ); - const { getUpdatableWorkflowVersion } = useGetUpdatableWorkflowVersion(); + const { getUpdatableWorkflowVersion } = + useGetUpdatableWorkflowVersionOrThrow(); if (!isDefined(workflow)) { return { @@ -50,11 +51,7 @@ export const useCreateStep = ({ setIsLoading(true); try { - const workflowVersionId = await getUpdatableWorkflowVersion(workflow); - - if (!isDefined(workflowVersionId)) { - throw new Error("Couldn't get updatable workflow version"); - } + const workflowVersionId = await getUpdatableWorkflowVersion(); const workflowVersionStepChanges = ( await createWorkflowVersionStep({ diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useDeleteEdge.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useDeleteEdge.ts index aa4bfd6045..58cdf986a6 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useDeleteEdge.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useDeleteEdge.ts @@ -1,25 +1,17 @@ -import { useGetUpdatableWorkflowVersion } from '@/workflow/hooks/useGetUpdatableWorkflowVersion'; -import { type WorkflowWithCurrentVersion } from '@/workflow/types/Workflow'; -import { assertWorkflowWithCurrentVersionIsDefined } from '@/workflow/utils/assertWorkflowWithCurrentVersionIsDefined'; +import { useGetUpdatableWorkflowVersionOrThrow } from '@/workflow/hooks/useGetUpdatableWorkflowVersionOrThrow'; import { type WorkflowDiagramEdge } from '@/workflow/workflow-diagram/types/WorkflowDiagramEdge'; import { useDeleteWorkflowVersionEdge } from '@/workflow/workflow-steps/hooks/useDeleteWorkflowVersionEdge'; import { useState } from 'react'; -import { isDefined } from 'twenty-shared/utils'; -export const useDeleteEdge = ({ - workflow, -}: { - workflow: WorkflowWithCurrentVersion | undefined; -}) => { +export const useDeleteEdge = () => { const [isLoading, setIsLoading] = useState(false); const { deleteWorkflowVersionEdge } = useDeleteWorkflowVersionEdge(); - const { getUpdatableWorkflowVersion } = useGetUpdatableWorkflowVersion(); + const { getUpdatableWorkflowVersion } = + useGetUpdatableWorkflowVersionOrThrow(); const deleteEdge = async ({ source, target }: WorkflowDiagramEdge) => { - assertWorkflowWithCurrentVersionIsDefined(workflow); - if (isLoading) { return; } @@ -27,11 +19,7 @@ export const useDeleteEdge = ({ setIsLoading(true); try { - const workflowVersionId = await getUpdatableWorkflowVersion(workflow); - - if (!isDefined(workflowVersionId)) { - throw new Error('Cannot find a workflow version to update'); - } + const workflowVersionId = await getUpdatableWorkflowVersion(); const deletedEdge = ( await deleteWorkflowVersionEdge({ 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 dcb72cc87b..5c9e3d0f61 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,30 +1,18 @@ import { useCommandMenu } from '@/command-menu/hooks/useCommandMenu'; import { useDeleteWorkflowVersionStep } from '@/workflow/hooks/useDeleteWorkflowVersionStep'; -import { useGetUpdatableWorkflowVersion } from '@/workflow/hooks/useGetUpdatableWorkflowVersion'; +import { useGetUpdatableWorkflowVersionOrThrow } from '@/workflow/hooks/useGetUpdatableWorkflowVersionOrThrow'; import { useStepsOutputSchema } from '@/workflow/hooks/useStepsOutputSchema'; -import { type WorkflowWithCurrentVersion } from '@/workflow/types/Workflow'; -import { assertWorkflowWithCurrentVersionIsDefined } from '@/workflow/utils/assertWorkflowWithCurrentVersionIsDefined'; -import { isDefined } from 'twenty-shared/utils'; -export const useDeleteStep = ({ - workflow, -}: { - workflow: WorkflowWithCurrentVersion | undefined; -}) => { +export const useDeleteStep = () => { const { deleteWorkflowVersionStep } = useDeleteWorkflowVersionStep(); const { deleteStepsOutputSchema } = useStepsOutputSchema(); - const { getUpdatableWorkflowVersion } = useGetUpdatableWorkflowVersion(); + const { getUpdatableWorkflowVersion } = + useGetUpdatableWorkflowVersionOrThrow(); const { closeCommandMenu } = useCommandMenu(); const deleteStep = async (stepId: string) => { - assertWorkflowWithCurrentVersionIsDefined(workflow); - - const workflowVersionId = await getUpdatableWorkflowVersion(workflow); - - if (!isDefined(workflowVersionId)) { - throw new Error('Could not find workflow version'); - } + const workflowVersionId = await getUpdatableWorkflowVersion(); const workflowVersionStepChanges = await deleteWorkflowVersionStep({ workflowVersionId, diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useUpdateStep.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useUpdateStep.ts index 94d65d2f99..486947af70 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useUpdateStep.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useUpdateStep.ts @@ -1,29 +1,14 @@ -import { useGetUpdatableWorkflowVersion } from '@/workflow/hooks/useGetUpdatableWorkflowVersion'; -import { - type WorkflowStep, - type WorkflowWithCurrentVersion, -} from '@/workflow/types/Workflow'; +import { useGetUpdatableWorkflowVersionOrThrow } from '@/workflow/hooks/useGetUpdatableWorkflowVersionOrThrow'; +import { type WorkflowStep } from '@/workflow/types/Workflow'; import { useUpdateWorkflowVersionStep } from '@/workflow/workflow-steps/hooks/useUpdateWorkflowVersionStep'; -import { isDefined } from 'twenty-shared/utils'; -export const useUpdateStep = ({ - workflow, -}: { - workflow: WorkflowWithCurrentVersion; -}) => { - const { getUpdatableWorkflowVersion } = useGetUpdatableWorkflowVersion(); +export const useUpdateStep = () => { + const { getUpdatableWorkflowVersion } = + useGetUpdatableWorkflowVersionOrThrow(); const { updateWorkflowVersionStep } = useUpdateWorkflowVersionStep(); const updateStep = async (updatedStep: T) => { - if (!isDefined(workflow.currentVersion)) { - throw new Error('Could not find current workflow version'); - } - - const workflowVersionId = await getUpdatableWorkflowVersion(workflow); - - if (!isDefined(workflowVersionId)) { - throw new Error('Workflow version not found'); - } + const workflowVersionId = await getUpdatableWorkflowVersion(); await updateWorkflowVersionStep({ workflowVersionId, diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionServerlessFunction.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionServerlessFunction.tsx index 4be8bc14ff..2a5c56c656 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionServerlessFunction.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionServerlessFunction.tsx @@ -8,7 +8,7 @@ import { Breadcrumb, type BreadcrumbProps, } from '@/ui/navigation/bread-crumb/components/Breadcrumb'; -import { useGetUpdatableWorkflowVersion } from '@/workflow/hooks/useGetUpdatableWorkflowVersion'; +import { useGetUpdatableWorkflowVersionOrThrow } from '@/workflow/hooks/useGetUpdatableWorkflowVersionOrThrow'; import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion'; import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState'; import { type WorkflowCodeAction } from '@/workflow/types/Workflow'; @@ -30,11 +30,11 @@ import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTab import { useHotkeysOnFocusedElement } from '@/ui/utilities/hotkey/hooks/useHotkeysOnFocusedElement'; import { useListenClickOutside } from '@/ui/utilities/pointer-event/hooks/useListenClickOutside'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; -import { serverlessFunctionTestDataFamilyState } from '@/workflow/states/serverlessFunctionTestDataFamilyState'; import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody'; import { WorkflowEditActionServerlessFunctionFields } from '@/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionServerlessFunctionFields'; import { WorkflowServerlessFunctionCodeEditor } from '@/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowServerlessFunctionCodeEditor'; import { WORKFLOW_SERVERLESS_FUNCTION_TAB_LIST_COMPONENT_ID } from '@/workflow/workflow-steps/workflow-actions/code-action/constants/WorkflowServerlessFunctionTabListComponentId'; +import { serverlessFunctionTestDataFamilyState } from '@/workflow/workflow-steps/workflow-actions/code-action/states/serverlessFunctionTestDataFamilyState'; import { WorkflowServerlessFunctionTabId } from '@/workflow/workflow-steps/workflow-actions/code-action/types/WorkflowServerlessFunctionTabId'; import { getWrongExportedFunctionMarkers } from '@/workflow/workflow-steps/workflow-actions/code-action/utils/getWrongExportedFunctionMarkers'; import { useActionHeaderTypeOrThrow } from '@/workflow/workflow-steps/workflow-actions/hooks/useActionHeaderTypeOrThrow'; @@ -140,7 +140,8 @@ export const WorkflowEditActionServerlessFunction = ({ ); const { updateOneServerlessFunction } = useUpdateOneServerlessFunction(serverlessFunctionId); - const { getUpdatableWorkflowVersion } = useGetUpdatableWorkflowVersion(); + const { getUpdatableWorkflowVersion } = + useGetUpdatableWorkflowVersionOrThrow(); const workflowVisualizerWorkflowId = useRecoilComponentValue( workflowVisualizerWorkflowIdComponentState, @@ -323,7 +324,7 @@ export const WorkflowEditActionServerlessFunction = ({ if (actionOptions.readonly === true || !isDefined(workflow)) { return; } - await getUpdatableWorkflowVersion(workflow); + await getUpdatableWorkflowVersion(); await onCodeChange(value); }; diff --git a/packages/twenty-front/src/modules/workflow/states/serverlessFunctionTestDataFamilyState.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/states/serverlessFunctionTestDataFamilyState.ts similarity index 100% rename from packages/twenty-front/src/modules/workflow/states/serverlessFunctionTestDataFamilyState.ts rename to packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/states/serverlessFunctionTestDataFamilyState.ts diff --git a/packages/twenty-front/src/modules/workflow/workflow-trigger/hooks/__tests__/useUpdateWorkflowVersionTrigger.test.ts b/packages/twenty-front/src/modules/workflow/workflow-trigger/hooks/__tests__/useUpdateWorkflowVersionTrigger.test.ts index 1b68d46a7a..17474e8c28 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-trigger/hooks/__tests__/useUpdateWorkflowVersionTrigger.test.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-trigger/hooks/__tests__/useUpdateWorkflowVersionTrigger.test.ts @@ -1,9 +1,6 @@ -import { act, renderHook } from '@testing-library/react'; +import { type WorkflowTrigger } from '@/workflow/types/Workflow'; import { useUpdateWorkflowVersionTrigger } from '@/workflow/workflow-trigger/hooks/useUpdateWorkflowVersionTrigger'; -import { - type WorkflowTrigger, - type WorkflowWithCurrentVersion, -} from '@/workflow/types/Workflow'; +import { act, renderHook } from '@testing-library/react'; const mockUpdateOneRecord = jest.fn(); const mockGetUpdatableWorkflowVersion = jest.fn(); @@ -15,8 +12,8 @@ jest.mock('@/object-record/hooks/useUpdateOneRecord', () => ({ })), })); -jest.mock('@/workflow/hooks/useGetUpdatableWorkflowVersion', () => ({ - useGetUpdatableWorkflowVersion: jest.fn(() => ({ +jest.mock('@/workflow/hooks/useGetUpdatableWorkflowVersionOrThrow', () => ({ + useGetUpdatableWorkflowVersionOrThrow: jest.fn(() => ({ getUpdatableWorkflowVersion: mockGetUpdatableWorkflowVersion, })), })); @@ -28,11 +25,6 @@ jest.mock('@/workflow/hooks/useComputeStepOutputSchema', () => ({ })); describe('useUpdateWorkflowVersionTrigger', () => { - const mockWorkflow = { - id: 'workflow-id', - currentVersion: { id: 'version-id' }, - } as WorkflowWithCurrentVersion; - const trigger: WorkflowTrigger = { name: 'Company created', type: 'DATABASE_EVENT', @@ -53,15 +45,13 @@ describe('useUpdateWorkflowVersionTrigger', () => { data: { computeStepOutputSchema: { field1: 'string' } }, }); - const { result } = renderHook(() => - useUpdateWorkflowVersionTrigger({ workflow: mockWorkflow }), - ); + const { result } = renderHook(() => useUpdateWorkflowVersionTrigger()); await act(async () => { await result.current.updateTrigger(trigger); }); - expect(mockGetUpdatableWorkflowVersion).toHaveBeenCalledWith(mockWorkflow); + expect(mockGetUpdatableWorkflowVersion).toHaveBeenCalled(); expect(mockComputeStepOutputSchema).toHaveBeenCalledWith({ step: trigger }); expect(mockUpdateOneRecord).toHaveBeenCalledWith({ idToUpdate: 'version-id', @@ -77,9 +67,7 @@ describe('useUpdateWorkflowVersionTrigger', () => { it('skips output schema computation when disabled', async () => { mockGetUpdatableWorkflowVersion.mockResolvedValue('version-id'); - const { result } = renderHook(() => - useUpdateWorkflowVersionTrigger({ workflow: mockWorkflow }), - ); + const { result } = renderHook(() => useUpdateWorkflowVersionTrigger()); await act(async () => { await result.current.updateTrigger(trigger, { diff --git a/packages/twenty-front/src/modules/workflow/workflow-trigger/hooks/useUpdateWorkflowVersionTrigger.ts b/packages/twenty-front/src/modules/workflow/workflow-trigger/hooks/useUpdateWorkflowVersionTrigger.ts index 9fdf691222..684b063f1c 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-trigger/hooks/useUpdateWorkflowVersionTrigger.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-trigger/hooks/useUpdateWorkflowVersionTrigger.ts @@ -1,25 +1,20 @@ import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord'; import { useComputeStepOutputSchema } from '@/workflow/hooks/useComputeStepOutputSchema'; -import { useGetUpdatableWorkflowVersion } from '@/workflow/hooks/useGetUpdatableWorkflowVersion'; +import { useGetUpdatableWorkflowVersionOrThrow } from '@/workflow/hooks/useGetUpdatableWorkflowVersionOrThrow'; import { type WorkflowTrigger, type WorkflowVersion, - type WorkflowWithCurrentVersion, } from '@/workflow/types/Workflow'; -import { isDefined } from 'twenty-shared/utils'; -export const useUpdateWorkflowVersionTrigger = ({ - workflow, -}: { - workflow: WorkflowWithCurrentVersion; -}) => { +export const useUpdateWorkflowVersionTrigger = () => { const { updateOneRecord: updateOneWorkflowVersion } = useUpdateOneRecord({ objectNameSingular: CoreObjectNameSingular.WorkflowVersion, }); - const { getUpdatableWorkflowVersion } = useGetUpdatableWorkflowVersion(); + const { getUpdatableWorkflowVersion } = + useGetUpdatableWorkflowVersionOrThrow(); const { computeStepOutputSchema } = useComputeStepOutputSchema(); @@ -27,11 +22,7 @@ export const useUpdateWorkflowVersionTrigger = ({ updatedTrigger: WorkflowTrigger, options: { computeOutputSchema: boolean } = { computeOutputSchema: true }, ) => { - if (!isDefined(workflow.currentVersion)) { - throw new Error('Cannot find current workflow version'); - } - - const workflowVersionId = await getUpdatableWorkflowVersion(workflow); + const workflowVersionId = await getUpdatableWorkflowVersion(); if (options.computeOutputSchema) { const outputSchema = ( @@ -46,10 +37,6 @@ export const useUpdateWorkflowVersionTrigger = ({ }; } - if (!isDefined(workflowVersionId)) { - throw new Error('Workflow version not found'); - } - await updateOneWorkflowVersion({ idToUpdate: workflowVersionId, updateOneRecordInput: { diff --git a/packages/twenty-front/src/modules/workflow/workflow-version/hooks/useTidyUpWorkflowVersion.ts b/packages/twenty-front/src/modules/workflow/workflow-version/hooks/useTidyUpWorkflowVersion.ts index f4afd83687..8bd2d8570c 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-version/hooks/useTidyUpWorkflowVersion.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-version/hooks/useTidyUpWorkflowVersion.ts @@ -1,8 +1,5 @@ -import { useGetUpdatableWorkflowVersion } from '@/workflow/hooks/useGetUpdatableWorkflowVersion'; -import { - type WorkflowVersion, - type WorkflowWithCurrentVersion, -} from '@/workflow/types/Workflow'; +import { useGetUpdatableWorkflowVersionOrThrow } from '@/workflow/hooks/useGetUpdatableWorkflowVersionOrThrow'; +import { type WorkflowVersion } from '@/workflow/types/Workflow'; import { useMutation } from '@apollo/client'; import { isDefined } from 'twenty-shared/utils'; import { @@ -23,11 +20,7 @@ import { workflowDiagramComponentState } from '@/workflow/workflow-diagram/state import { getOrganizedDiagram } from '@/workflow/workflow-diagram/utils/getOrganizedDiagram'; import { UPDATE_WORKFLOW_VERSION_POSITIONS } from '@/workflow/workflow-version/graphql/mutations/updateWorkflowVersionPositions'; -export const useTidyUpWorkflowVersion = ({ - workflow, -}: { - workflow?: WorkflowWithCurrentVersion; -}) => { +export const useTidyUpWorkflowVersion = () => { const [workflowDiagram, setWorkflowDiagram] = useRecoilComponentState( workflowDiagramComponentState, ); @@ -49,20 +42,13 @@ export const useTidyUpWorkflowVersion = ({ UpdateWorkflowVersionPositionsMutationVariables >(UPDATE_WORKFLOW_VERSION_POSITIONS, { client: apolloCoreClient }); - const { getUpdatableWorkflowVersion } = useGetUpdatableWorkflowVersion(); + const { getUpdatableWorkflowVersion } = + useGetUpdatableWorkflowVersionOrThrow(); const updateWorkflowVersionPosition = async ( positions: { id: string; position: { x: number; y: number } }[], ) => { - if (!isDefined(workflow)) { - throw new Error('Cannot find a workflow to update'); - } - - const workflowVersionId = await getUpdatableWorkflowVersion(workflow); - - if (!isDefined(workflowVersionId)) { - throw new Error('Cannot find a workflow version to update'); - } + const workflowVersionId = await getUpdatableWorkflowVersion(); await mutate({ variables: { input: { workflowVersionId, positions } } }); @@ -115,7 +101,7 @@ export const useTidyUpWorkflowVersion = ({ }; const tidyUpWorkflowVersion = async () => { - if (!isDefined(workflowDiagram) || !isDefined(workflow?.currentVersion)) { + if (!isDefined(workflowDiagram)) { return; }