Fix workflow step output schema not reflecting user-renamed step name (#20922)
## Summary - Fixes https://github.com/twentyhq/twenty/issues/20906 - Moves `markStepForRecomputation` from `useUpdateStep` into the lower-level `useUpdateWorkflowVersionStep` hook, so **every** caller that updates a step also triggers output schema recomputation. - Previously, renaming a step via the side panel title input called `useUpdateWorkflowVersionStep` directly (bypassing `useUpdateStep`), so the variable picker kept showing the initial default name (e.g. "Create Record") instead of the user's custom name. ## Test plan - [x] Rename a workflow step via the side panel title input - [x] Verify the variable picker dropdown shows the updated name - [x] Verify variable tags/chips in subsequent steps reflect the updated name - [x] Verify that updating step settings (e.g. changing object type) still refreshes the output schema correctly
This commit is contained in:
-73
@@ -1,10 +1,8 @@
|
||||
import { useUpdateStep } from '@/workflow/workflow-steps/hooks/useUpdateStep';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { type WorkflowAction } from '~/generated/graphql';
|
||||
|
||||
const mockUpdateWorkflowVersionStep = jest.fn();
|
||||
const mockGetUpdatableWorkflowVersion = jest.fn();
|
||||
const mockMarkStepForRecomputation = jest.fn();
|
||||
|
||||
jest.mock(
|
||||
'@/workflow/workflow-steps/hooks/useUpdateWorkflowVersionStep',
|
||||
@@ -21,12 +19,6 @@ jest.mock('@/workflow/hooks/useGetUpdatableWorkflowVersionOrThrow', () => ({
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('@/workflow/workflow-variables/hooks/useStepsOutputSchema', () => ({
|
||||
useStepsOutputSchema: jest.fn(() => ({
|
||||
markStepForRecomputation: mockMarkStepForRecomputation,
|
||||
})),
|
||||
}));
|
||||
|
||||
describe('useUpdateStep', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
@@ -69,69 +61,4 @@ describe('useUpdateStep', () => {
|
||||
step: mockStep,
|
||||
});
|
||||
});
|
||||
|
||||
it('should mark step for recomputation after update', async () => {
|
||||
const mockWorkflowVersionId = 'version-123';
|
||||
const mockStep = {
|
||||
id: 'step-1',
|
||||
name: 'Create Record',
|
||||
valid: true,
|
||||
type: 'CREATE_RECORD' as const,
|
||||
settings: {
|
||||
input: {
|
||||
objectName: 'company',
|
||||
},
|
||||
errorHandlingOptions: {
|
||||
retryOnFailure: { value: false },
|
||||
continueOnFailure: { value: false },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
mockGetUpdatableWorkflowVersion.mockResolvedValue(mockWorkflowVersionId);
|
||||
|
||||
const { result } = renderHook(() => useUpdateStep());
|
||||
await act(async () => {
|
||||
await result.current.updateStep(mockStep as WorkflowAction);
|
||||
});
|
||||
|
||||
expect(mockMarkStepForRecomputation).toHaveBeenCalledWith({
|
||||
stepId: 'step-1',
|
||||
workflowVersionId: mockWorkflowVersionId,
|
||||
});
|
||||
});
|
||||
|
||||
it('should mark step for recomputation for all step types', async () => {
|
||||
const mockWorkflowVersionId = 'version-123';
|
||||
const stepTypes = ['CODE', 'HTTP_REQUEST', 'CREATE_RECORD', 'SEND_EMAIL'];
|
||||
|
||||
for (const stepType of stepTypes) {
|
||||
mockMarkStepForRecomputation.mockClear();
|
||||
mockGetUpdatableWorkflowVersion.mockResolvedValue(mockWorkflowVersionId);
|
||||
|
||||
const mockStep = {
|
||||
id: `step-${stepType}`,
|
||||
name: `${stepType} Step`,
|
||||
valid: true,
|
||||
type: stepType as any,
|
||||
settings: {
|
||||
input: {},
|
||||
errorHandlingOptions: {
|
||||
retryOnFailure: { value: false },
|
||||
continueOnFailure: { value: false },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const { result } = renderHook(() => useUpdateStep());
|
||||
await act(async () => {
|
||||
await result.current.updateStep(mockStep as WorkflowAction);
|
||||
});
|
||||
|
||||
expect(mockMarkStepForRecomputation).toHaveBeenCalledWith({
|
||||
stepId: `step-${stepType}`,
|
||||
workflowVersionId: mockWorkflowVersionId,
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
import { useUpdateWorkflowVersionStep } from '@/workflow/workflow-steps/hooks/useUpdateWorkflowVersionStep';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
|
||||
const mockMutate = jest.fn();
|
||||
const mockGetRecordFromCache = jest.fn();
|
||||
const mockMarkStepForRecomputation = jest.fn();
|
||||
const mockEnqueueErrorSnackBar = jest.fn();
|
||||
|
||||
jest.mock('@/object-metadata/hooks/useApolloCoreClient', () => ({
|
||||
useApolloCoreClient: () => ({ cache: {} }),
|
||||
}));
|
||||
|
||||
jest.mock('@/object-metadata/hooks/useObjectMetadataItems', () => ({
|
||||
useObjectMetadataItems: () => ({ objectMetadataItems: [] }),
|
||||
}));
|
||||
|
||||
jest.mock('@/object-metadata/hooks/useObjectMetadataItem', () => ({
|
||||
useObjectMetadataItem: () => ({ objectMetadataItem: {} }),
|
||||
}));
|
||||
|
||||
jest.mock('@/object-record/hooks/useObjectPermissions', () => ({
|
||||
useObjectPermissions: () => ({ objectPermissionsByObjectMetadataId: {} }),
|
||||
}));
|
||||
|
||||
jest.mock('@/ui/feedback/snack-bar-manager/hooks/useSnackBar', () => ({
|
||||
useSnackBar: () => ({ enqueueErrorSnackBar: mockEnqueueErrorSnackBar }),
|
||||
}));
|
||||
|
||||
jest.mock('@/object-record/cache/hooks/useGetRecordFromCache', () => ({
|
||||
useGetRecordFromCache: () => mockGetRecordFromCache,
|
||||
}));
|
||||
|
||||
jest.mock('@/object-record/cache/utils/updateRecordFromCache', () => ({
|
||||
updateRecordFromCache: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('@/workflow/workflow-variables/hooks/useStepsOutputSchema', () => ({
|
||||
useStepsOutputSchema: () => ({
|
||||
markStepForRecomputation: mockMarkStepForRecomputation,
|
||||
}),
|
||||
}));
|
||||
|
||||
jest.mock('@apollo/client/react', () => ({
|
||||
useMutation: () => [mockMutate],
|
||||
}));
|
||||
|
||||
describe('useUpdateWorkflowVersionStep', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should mark step for recomputation after successful update', async () => {
|
||||
const updatedStep = {
|
||||
id: 'step-1',
|
||||
name: 'My Custom Name',
|
||||
type: 'CREATE_RECORD',
|
||||
};
|
||||
|
||||
mockMutate.mockResolvedValue({
|
||||
data: { updateWorkflowVersionStep: updatedStep },
|
||||
});
|
||||
|
||||
mockGetRecordFromCache.mockReturnValue({
|
||||
id: 'version-1',
|
||||
steps: [{ id: 'step-1', name: 'Create Record', type: 'CREATE_RECORD' }],
|
||||
});
|
||||
|
||||
const { result } = renderHook(() => useUpdateWorkflowVersionStep());
|
||||
|
||||
await act(async () => {
|
||||
await result.current.updateWorkflowVersionStep({
|
||||
workflowVersionId: 'version-1',
|
||||
step: updatedStep,
|
||||
});
|
||||
});
|
||||
|
||||
expect(mockMarkStepForRecomputation).toHaveBeenCalledWith({
|
||||
stepId: 'step-1',
|
||||
workflowVersionId: 'version-1',
|
||||
});
|
||||
});
|
||||
|
||||
it('should not mark step for recomputation when mutation returns no data', async () => {
|
||||
mockMutate.mockResolvedValue({ data: null });
|
||||
|
||||
const { result } = renderHook(() => useUpdateWorkflowVersionStep());
|
||||
|
||||
await act(async () => {
|
||||
await result.current.updateWorkflowVersionStep({
|
||||
workflowVersionId: 'version-1',
|
||||
step: { id: 'step-1', name: 'Step', type: 'CODE' },
|
||||
});
|
||||
});
|
||||
|
||||
expect(mockMarkStepForRecomputation).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should still mark step for recomputation when cached record is missing', async () => {
|
||||
mockMutate.mockResolvedValue({
|
||||
data: {
|
||||
updateWorkflowVersionStep: {
|
||||
id: 'step-1',
|
||||
name: 'Step',
|
||||
type: 'CODE',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
mockGetRecordFromCache.mockReturnValue(undefined);
|
||||
|
||||
const { result } = renderHook(() => useUpdateWorkflowVersionStep());
|
||||
|
||||
await act(async () => {
|
||||
await result.current.updateWorkflowVersionStep({
|
||||
workflowVersionId: 'version-1',
|
||||
step: { id: 'step-1', name: 'Step', type: 'CODE' },
|
||||
});
|
||||
});
|
||||
|
||||
expect(mockMarkStepForRecomputation).toHaveBeenCalledWith({
|
||||
stepId: 'step-1',
|
||||
workflowVersionId: 'version-1',
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,13 +1,11 @@
|
||||
import { useGetUpdatableWorkflowVersionOrThrow } from '@/workflow/hooks/useGetUpdatableWorkflowVersionOrThrow';
|
||||
import { type WorkflowAction } from '@/workflow/types/Workflow';
|
||||
import { useUpdateWorkflowVersionStep } from '@/workflow/workflow-steps/hooks/useUpdateWorkflowVersionStep';
|
||||
import { useStepsOutputSchema } from '@/workflow/workflow-variables/hooks/useStepsOutputSchema';
|
||||
|
||||
export const useUpdateStep = () => {
|
||||
const { getUpdatableWorkflowVersion } =
|
||||
useGetUpdatableWorkflowVersionOrThrow();
|
||||
const { updateWorkflowVersionStep } = useUpdateWorkflowVersionStep();
|
||||
const { markStepForRecomputation } = useStepsOutputSchema();
|
||||
|
||||
const updateStep = async (updatedStep: WorkflowAction) => {
|
||||
const workflowVersionId = await getUpdatableWorkflowVersion();
|
||||
@@ -17,11 +15,6 @@ export const useUpdateStep = () => {
|
||||
step: updatedStep,
|
||||
});
|
||||
|
||||
markStepForRecomputation({
|
||||
stepId: updatedStep.id,
|
||||
workflowVersionId,
|
||||
});
|
||||
|
||||
return {
|
||||
updatedStep: result?.data?.updateWorkflowVersionStep,
|
||||
};
|
||||
|
||||
+9
-1
@@ -10,6 +10,7 @@ import {
|
||||
type WorkflowVersion,
|
||||
type WorkflowStep,
|
||||
} from '@/workflow/types/Workflow';
|
||||
import { useStepsOutputSchema } from '@/workflow/workflow-variables/hooks/useStepsOutputSchema';
|
||||
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
|
||||
import { useMutation } from '@apollo/client/react';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
@@ -24,6 +25,7 @@ export const useUpdateWorkflowVersionStep = () => {
|
||||
const { objectMetadataItems } = useObjectMetadataItems();
|
||||
const { objectPermissionsByObjectMetadataId } = useObjectPermissions();
|
||||
const { enqueueErrorSnackBar } = useSnackBar();
|
||||
const { markStepForRecomputation } = useStepsOutputSchema();
|
||||
|
||||
const { objectMetadataItem } = useObjectMetadataItem({
|
||||
objectNameSingular: CoreObjectNameSingular.WorkflowVersion,
|
||||
@@ -52,11 +54,16 @@ export const useUpdateWorkflowVersionStep = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
markStepForRecomputation({
|
||||
stepId: updatedStep.id,
|
||||
workflowVersionId: input.workflowVersionId,
|
||||
});
|
||||
|
||||
const cachedRecord = getRecordFromCache<WorkflowVersion>(
|
||||
input.workflowVersionId,
|
||||
);
|
||||
if (!isDefined(cachedRecord)) {
|
||||
return;
|
||||
return result;
|
||||
}
|
||||
|
||||
const newCachedRecord = {
|
||||
@@ -80,6 +87,7 @@ export const useUpdateWorkflowVersionStep = () => {
|
||||
recordGqlFields,
|
||||
objectPermissionsByObjectMetadataId,
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user