From 8a66ea32e7336e085dd6b925927ea8c8b888985a Mon Sep 17 00:00:00 2001 From: Jamey Huffnagle Date: Mon, 28 Oct 2024 10:29:47 -0400 Subject: [PATCH] refactor(app): Support "resume from recovery assuming false positive" action (#16601) Closes EXEC-791 See #16556 and the linked ticket for details concerning the reasoning for implementing these changes. When skipping a step in Error Recovery, the FE essentially calls two commands: handleIgnoringErrorKind, which deals with updating the recovery policy, and skipFailedCommand, which does the skipping. By using isAssumeFalsePositiveResumeKind, we can conditionally determine which policy ifMatch criteria to use, and we can determine which error recovery resume kind to dispatch to the server. --- .../__tests__/useRecoveryCommands.test.ts | 67 +++++++++++++++- .../hooks/useRecoveryCommands.ts | 47 +++++++++-- .../RunTimeControl/__tests__/hooks.test.tsx | 3 + .../__tests__/RunningProtocol.test.tsx | 3 + ...veryAssumingFalsePositiveMutation.test.tsx | 80 +++++++++++++++++++ .../useResumeRunFromRecoveryMutation.test.tsx | 8 +- .../__tests__/useRunActionMutations.test.tsx | 17 ++++ react-api-client/src/runs/index.ts | 2 + ...omRecoveryAssumingFalsePositiveMutation.ts | 60 ++++++++++++++ .../src/runs/useRunActionMutations.ts | 12 +++ 10 files changed, 288 insertions(+), 11 deletions(-) create mode 100644 react-api-client/src/runs/__tests__/useResumeFromRecoveryAssumingFalsePositiveMutation.test.tsx create mode 100644 react-api-client/src/runs/useResumeFromRecoveryAssumingFalsePositiveMutation.ts diff --git a/app/src/organisms/ErrorRecoveryFlows/hooks/__tests__/useRecoveryCommands.test.ts b/app/src/organisms/ErrorRecoveryFlows/hooks/__tests__/useRecoveryCommands.test.ts index a553bdcb4ee..4079e8a8f1e 100644 --- a/app/src/organisms/ErrorRecoveryFlows/hooks/__tests__/useRecoveryCommands.test.ts +++ b/app/src/organisms/ErrorRecoveryFlows/hooks/__tests__/useRecoveryCommands.test.ts @@ -5,6 +5,7 @@ import { useResumeRunFromRecoveryMutation, useStopRunMutation, useUpdateErrorRecoveryPolicy, + useResumeRunFromRecoveryAssumingFalsePositiveMutation, } from '@opentrons/react-api-client' import { useChainRunCommands } from '/app/resources/runs' @@ -14,13 +15,16 @@ import { RELEASE_GRIPPER_JAW, buildPickUpTips, buildIgnorePolicyRules, + isAssumeFalsePositiveResumeKind, UPDATE_ESTIMATORS_EXCEPT_PLUNGERS, HOME_GRIPPER_Z, } from '../useRecoveryCommands' -import { RECOVERY_MAP } from '../../constants' +import { RECOVERY_MAP, ERROR_KINDS } from '../../constants' +import { getErrorKind } from '/app/organisms/ErrorRecoveryFlows/utils' vi.mock('@opentrons/react-api-client') vi.mock('/app/resources/runs') +vi.mock('/app/organisms/ErrorRecoveryFlows/utils') describe('useRecoveryCommands', () => { const mockFailedCommand = { @@ -41,6 +45,9 @@ describe('useRecoveryCommands', () => { const mockResumeRunFromRecovery = vi.fn(() => Promise.resolve(mockMakeSuccessToast()) ) + const mockResumeRunFromRecoveryAssumingFalsePositive = vi.fn(() => + Promise.resolve(mockMakeSuccessToast()) + ) const mockStopRun = vi.fn() const mockChainRunCommands = vi.fn().mockResolvedValue([]) const mockReportActionSelectedResult = vi.fn() @@ -73,6 +80,11 @@ describe('useRecoveryCommands', () => { vi.mocked(useUpdateErrorRecoveryPolicy).mockReturnValue({ mutateAsync: mockUpdateErrorRecoveryPolicy, } as any) + vi.mocked( + useResumeRunFromRecoveryAssumingFalsePositiveMutation + ).mockReturnValue({ + mutateAsync: mockResumeRunFromRecoveryAssumingFalsePositive, + } as any) }) it('should call chainRunRecoveryCommands with continuePastCommandFailure set to false', async () => { @@ -317,7 +329,8 @@ describe('useRecoveryCommands', () => { const expectedPolicyRules = buildIgnorePolicyRules( 'aspirateInPlace', - 'mockErrorType' + 'mockErrorType', + 'ignoreAndContinue' ) expect(mockUpdateErrorRecoveryPolicy).toHaveBeenCalledWith( @@ -354,4 +367,54 @@ describe('useRecoveryCommands', () => { RECOVERY_MAP.ERROR_WHILE_RECOVERING.ROUTE ) }) + + describe('skipFailedCommand with false positive handling', () => { + it('should call resumeRunFromRecoveryAssumingFalsePositive for tip-related errors', async () => { + vi.mocked(getErrorKind).mockReturnValue(ERROR_KINDS.TIP_NOT_DETECTED) + + const { result } = renderHook(() => useRecoveryCommands(props)) + + await act(async () => { + await result.current.skipFailedCommand() + }) + + expect( + mockResumeRunFromRecoveryAssumingFalsePositive + ).toHaveBeenCalledWith(mockRunId) + expect(mockMakeSuccessToast).toHaveBeenCalled() + }) + + it('should call regular resumeRunFromRecovery for non-tip-related errors', async () => { + vi.mocked(getErrorKind).mockReturnValue(ERROR_KINDS.GRIPPER_ERROR) + + const { result } = renderHook(() => useRecoveryCommands(props)) + + await act(async () => { + await result.current.skipFailedCommand() + }) + + expect(mockResumeRunFromRecovery).toHaveBeenCalledWith(mockRunId) + expect(mockMakeSuccessToast).toHaveBeenCalled() + }) + }) +}) + +describe('isAssumeFalsePositiveResumeKind', () => { + it(`should return true for ${ERROR_KINDS.TIP_NOT_DETECTED} error kind`, () => { + vi.mocked(getErrorKind).mockReturnValue(ERROR_KINDS.TIP_NOT_DETECTED) + + expect(isAssumeFalsePositiveResumeKind({} as any)).toBe(true) + }) + + it(`should return true for ${ERROR_KINDS.TIP_DROP_FAILED} error kind`, () => { + vi.mocked(getErrorKind).mockReturnValue(ERROR_KINDS.TIP_DROP_FAILED) + + expect(isAssumeFalsePositiveResumeKind({} as any)).toBe(true) + }) + + it('should return false for other error kinds', () => { + vi.mocked(getErrorKind).mockReturnValue(ERROR_KINDS.GRIPPER_ERROR) + + expect(isAssumeFalsePositiveResumeKind({} as any)).toBe(false) + }) }) diff --git a/app/src/organisms/ErrorRecoveryFlows/hooks/useRecoveryCommands.ts b/app/src/organisms/ErrorRecoveryFlows/hooks/useRecoveryCommands.ts index 69101d92fe9..7614dec4be3 100644 --- a/app/src/organisms/ErrorRecoveryFlows/hooks/useRecoveryCommands.ts +++ b/app/src/organisms/ErrorRecoveryFlows/hooks/useRecoveryCommands.ts @@ -5,10 +5,12 @@ import { useResumeRunFromRecoveryMutation, useStopRunMutation, useUpdateErrorRecoveryPolicy, + useResumeRunFromRecoveryAssumingFalsePositiveMutation, } from '@opentrons/react-api-client' import { useChainRunCommands } from '/app/resources/runs' -import { RECOVERY_MAP } from '../constants' +import { ERROR_KINDS, RECOVERY_MAP } from '../constants' +import { getErrorKind } from '/app/organisms/ErrorRecoveryFlows/utils' import type { CreateCommand, @@ -23,7 +25,9 @@ import type { } from '@opentrons/shared-data' import type { CommandData, + IfMatchType, RecoveryPolicyRulesParams, + RunAction, } from '@opentrons/api-client' import type { WellGroup } from '@opentrons/components' import type { FailedCommand, RecoveryRoute, RouteStep } from '../types' @@ -89,6 +93,9 @@ export function useRecoveryCommands({ const { mutateAsync: resumeRunFromRecovery, } = useResumeRunFromRecoveryMutation() + const { + mutateAsync: resumeRunFromRecoveryAssumingFalsePositive, + } = useResumeRunFromRecoveryAssumingFalsePositiveMutation() const { stopRun } = useStopRunMutation() const { mutateAsync: updateErrorRecoveryPolicy, @@ -198,9 +205,16 @@ export function useRecoveryCommands({ const handleIgnoringErrorKind = useCallback((): Promise => { if (ignoreErrors) { if (failedCommandByRunRecord?.error != null) { + const ifMatch: IfMatchType = isAssumeFalsePositiveResumeKind( + failedCommandByRunRecord + ) + ? 'assumeFalsePositiveAndContinue' + : 'ignoreAndContinue' + const ignorePolicyRules = buildIgnorePolicyRules( failedCommandByRunRecord.commandType, - failedCommandByRunRecord.error.errorType + failedCommandByRunRecord.error.errorType, + ifMatch ) return updateErrorRecoveryPolicy(ignorePolicyRules) @@ -247,9 +261,17 @@ export function useRecoveryCommands({ stopRun(runId) }, [runId]) + const handleResumeAction = (): Promise => { + if (isAssumeFalsePositiveResumeKind(failedCommandByRunRecord)) { + return resumeRunFromRecoveryAssumingFalsePositive(runId) + } else { + return resumeRunFromRecovery(runId) + } + } + const skipFailedCommand = useCallback((): void => { void handleIgnoringErrorKind().then(() => - resumeRunFromRecovery(runId).then(() => { + handleResumeAction().then(() => { analytics.reportActionSelectedResult( selectedRecoveryOption, 'succeeded' @@ -303,6 +325,20 @@ export function useRecoveryCommands({ } } +export function isAssumeFalsePositiveResumeKind( + failedCommandByRunRecord: UseRecoveryCommandsParams['failedCommandByRunRecord'] +): boolean { + const errorKind = getErrorKind(failedCommandByRunRecord) + + switch (errorKind) { + case ERROR_KINDS.TIP_NOT_DETECTED: + case ERROR_KINDS.TIP_DROP_FAILED: + return true + default: + return false + } +} + export const HOME_PIPETTE_Z_AXES: CreateCommand = { commandType: 'home', params: { axes: ['leftZ', 'rightZ'] }, @@ -372,13 +408,14 @@ export const buildPickUpTips = ( export const buildIgnorePolicyRules = ( commandType: FailedCommand['commandType'], - errorType: string + errorType: string, + ifMatch: IfMatchType ): RecoveryPolicyRulesParams => { return [ { commandType, errorType, - ifMatch: 'ignoreAndContinue', + ifMatch, }, ] } diff --git a/app/src/organisms/RunTimeControl/__tests__/hooks.test.tsx b/app/src/organisms/RunTimeControl/__tests__/hooks.test.tsx index 7450fb34e4e..2ba0d50ea3b 100644 --- a/app/src/organisms/RunTimeControl/__tests__/hooks.test.tsx +++ b/app/src/organisms/RunTimeControl/__tests__/hooks.test.tsx @@ -40,16 +40,19 @@ describe('useRunControls hook', () => { const mockStopRun = vi.fn() const mockCloneRun = vi.fn() const mockResumeRunFromRecovery = vi.fn() + const mockResumeRunFromRecoveryAssumingFalsePositive = vi.fn() when(useRunActionMutations).calledWith(mockPausedRun.id).thenReturn({ playRun: mockPlayRun, pauseRun: mockPauseRun, stopRun: mockStopRun, resumeRunFromRecovery: mockResumeRunFromRecovery, + resumeRunFromRecoveryAssumingFalsePositive: mockResumeRunFromRecoveryAssumingFalsePositive, isPlayRunActionLoading: false, isPauseRunActionLoading: false, isStopRunActionLoading: false, isResumeRunFromRecoveryActionLoading: false, + isResumeRunFromRecoveryAssumingFalsePositiveActionLoading: false, }) when(useCloneRun).calledWith(mockPausedRun.id, undefined, true).thenReturn({ cloneRun: mockCloneRun, diff --git a/app/src/pages/ODD/RunningProtocol/__tests__/RunningProtocol.test.tsx b/app/src/pages/ODD/RunningProtocol/__tests__/RunningProtocol.test.tsx index 2605c1bad5b..f98666d3cbd 100644 --- a/app/src/pages/ODD/RunningProtocol/__tests__/RunningProtocol.test.tsx +++ b/app/src/pages/ODD/RunningProtocol/__tests__/RunningProtocol.test.tsx @@ -76,6 +76,7 @@ const mockPlayRun = vi.fn() const mockPauseRun = vi.fn() const mockStopRun = vi.fn() const mockResumeRunFromRecovery = vi.fn() +const mockResumeRunFromRecoveryAssumingFalsePositive = vi.fn() const render = (path = '/') => { return renderWithProviders( @@ -133,10 +134,12 @@ describe('RunningProtocol', () => { pauseRun: mockPauseRun, stopRun: mockStopRun, resumeRunFromRecovery: mockResumeRunFromRecovery, + resumeRunFromRecoveryAssumingFalsePositive: mockResumeRunFromRecoveryAssumingFalsePositive, isPlayRunActionLoading: false, isPauseRunActionLoading: false, isStopRunActionLoading: false, isResumeRunFromRecoveryActionLoading: false, + isResumeRunFromRecoveryAssumingFalsePositiveActionLoading: false, }) when(vi.mocked(useMostRecentCompletedAnalysis)) .calledWith(RUN_ID) diff --git a/react-api-client/src/runs/__tests__/useResumeFromRecoveryAssumingFalsePositiveMutation.test.tsx b/react-api-client/src/runs/__tests__/useResumeFromRecoveryAssumingFalsePositiveMutation.test.tsx new file mode 100644 index 00000000000..ec5900d4a62 --- /dev/null +++ b/react-api-client/src/runs/__tests__/useResumeFromRecoveryAssumingFalsePositiveMutation.test.tsx @@ -0,0 +1,80 @@ +import { describe, it, expect, beforeEach, vi } from 'vitest' +import { QueryClient, QueryClientProvider } from 'react-query' +import { act, renderHook, waitFor } from '@testing-library/react' + +import { createRunAction } from '@opentrons/api-client' + +import { useHost } from '../../api' +import { useResumeRunFromRecoveryAssumingFalsePositiveMutation } from '..' + +import { RUN_ID_1, mockResumeFromRecoveryAction } from '../__fixtures__' + +import type * as React from 'react' +import type { HostConfig, Response, RunAction } from '@opentrons/api-client' +import type { UseResumeRunFromRecoveryAssumingFalsePositiveMutationOptions } from '../useResumeFromRecoveryAssumingFalsePositiveMutation' + +vi.mock('@opentrons/api-client') +vi.mock('../../api/useHost') + +const HOST_CONFIG: HostConfig = { hostname: 'localhost' } + +describe('useResumeRunFromRecoveryAssumingFalsePositiveMutation hook', () => { + let wrapper: React.FunctionComponent< + { + children: React.ReactNode + } & UseResumeRunFromRecoveryAssumingFalsePositiveMutationOptions + > + + beforeEach(() => { + const queryClient = new QueryClient() + const clientProvider: React.FunctionComponent< + { + children: React.ReactNode + } & UseResumeRunFromRecoveryAssumingFalsePositiveMutationOptions + > = ({ children }) => ( + {children} + ) + wrapper = clientProvider + }) + + it('should return no data when calling resumeRunFromRecoveryAssumingFalsePositive if the request fails', async () => { + vi.mocked(useHost).mockReturnValue(HOST_CONFIG) + vi.mocked(createRunAction).mockRejectedValue('oh no') + + const { result } = renderHook( + useResumeRunFromRecoveryAssumingFalsePositiveMutation, + { + wrapper, + } + ) + + expect(result.current.data).toBeUndefined() + act(() => + result.current.resumeRunFromRecoveryAssumingFalsePositive(RUN_ID_1) + ) + await waitFor(() => { + expect(result.current.data).toBeUndefined() + }) + }) + + it('should create a resumeFromRecoveryAssumingFalsePositive run action when calling the resumeRunFromRecoveryAssumingFalsePositive callback', async () => { + vi.mocked(useHost).mockReturnValue(HOST_CONFIG) + vi.mocked(createRunAction).mockResolvedValue({ + data: mockResumeFromRecoveryAction, + } as Response) + + const { result } = renderHook( + useResumeRunFromRecoveryAssumingFalsePositiveMutation, + { + wrapper, + } + ) + act(() => + result.current.resumeRunFromRecoveryAssumingFalsePositive(RUN_ID_1) + ) + + await waitFor(() => { + expect(result.current.data).toEqual(mockResumeFromRecoveryAction) + }) + }) +}) diff --git a/react-api-client/src/runs/__tests__/useResumeRunFromRecoveryMutation.test.tsx b/react-api-client/src/runs/__tests__/useResumeRunFromRecoveryMutation.test.tsx index cf9794c0dd8..f556b105153 100644 --- a/react-api-client/src/runs/__tests__/useResumeRunFromRecoveryMutation.test.tsx +++ b/react-api-client/src/runs/__tests__/useResumeRunFromRecoveryMutation.test.tsx @@ -9,22 +9,22 @@ import { useResumeRunFromRecoveryMutation } from '..' import { RUN_ID_1, mockResumeFromRecoveryAction } from '../__fixtures__' import type { HostConfig, Response, RunAction } from '@opentrons/api-client' -import type { UsePlayRunMutationOptions } from '../usePlayRunMutation' +import type { UseResumeRunFromRecoveryMutationOptions } from '../useResumeRunFromRecoveryMutation' vi.mock('@opentrons/api-client') vi.mock('../../api/useHost') const HOST_CONFIG: HostConfig = { hostname: 'localhost' } -describe('usePlayRunMutation hook', () => { +describe('useResumeRunFromRecoveryMutation hook', () => { let wrapper: React.FunctionComponent< - { children: React.ReactNode } & UsePlayRunMutationOptions + { children: React.ReactNode } & UseResumeRunFromRecoveryMutationOptions > beforeEach(() => { const queryClient = new QueryClient() const clientProvider: React.FunctionComponent< - { children: React.ReactNode } & UsePlayRunMutationOptions + { children: React.ReactNode } & UseResumeRunFromRecoveryMutationOptions > = ({ children }) => ( {children} ) diff --git a/react-api-client/src/runs/__tests__/useRunActionMutations.test.tsx b/react-api-client/src/runs/__tests__/useRunActionMutations.test.tsx index 7b40e4fc88e..e5c5c4cf265 100644 --- a/react-api-client/src/runs/__tests__/useRunActionMutations.test.tsx +++ b/react-api-client/src/runs/__tests__/useRunActionMutations.test.tsx @@ -10,18 +10,21 @@ import { usePauseRunMutation, useStopRunMutation, useResumeRunFromRecoveryMutation, + useResumeRunFromRecoveryAssumingFalsePositiveMutation, } from '..' import type { UsePlayRunMutationResult, UsePauseRunMutationResult, UseStopRunMutationResult, UseResumeRunFromRecoveryMutationResult, + UseResumeRunFromRecoveryAssumingFalsePositiveMutationResult, } from '..' vi.mock('../usePlayRunMutation') vi.mock('../usePauseRunMutation') vi.mock('../useStopRunMutation') vi.mock('../useResumeRunFromRecoveryMutation') +vi.mock('../useResumeFromRecoveryAssumingFalsePositiveMutation') describe('useRunActionMutations hook', () => { let wrapper: React.FunctionComponent<{ children: React.ReactNode }> @@ -44,6 +47,7 @@ describe('useRunActionMutations hook', () => { const mockPauseRun = vi.fn() const mockStopRun = vi.fn() const mockResumeRunFromRecovery = vi.fn() + const mockResumeRunFromRecoveryAssumingFalsePositive = vi.fn() vi.mocked(usePlayRunMutation).mockReturnValue(({ playRun: mockPlayRun, @@ -61,6 +65,12 @@ describe('useRunActionMutations hook', () => { resumeRunFromRecovery: mockResumeRunFromRecovery, } as unknown) as UseResumeRunFromRecoveryMutationResult) + vi.mocked( + useResumeRunFromRecoveryAssumingFalsePositiveMutation + ).mockReturnValue(({ + resumeRunFromRecoveryAssumingFalsePositive: mockResumeRunFromRecoveryAssumingFalsePositive, + } as unknown) as UseResumeRunFromRecoveryAssumingFalsePositiveMutationResult) + const { result } = renderHook(() => useRunActionMutations(RUN_ID_1), { wrapper, }) @@ -77,5 +87,12 @@ describe('useRunActionMutations hook', () => { act(() => result.current.resumeRunFromRecovery()) expect(mockResumeRunFromRecovery).toHaveBeenCalledTimes(1) expect(mockResumeRunFromRecovery).toHaveBeenCalledWith(RUN_ID_1) + act(() => result.current.resumeRunFromRecoveryAssumingFalsePositive()) + expect( + mockResumeRunFromRecoveryAssumingFalsePositive + ).toHaveBeenCalledTimes(1) + expect(mockResumeRunFromRecoveryAssumingFalsePositive).toHaveBeenCalledWith( + RUN_ID_1 + ) }) }) diff --git a/react-api-client/src/runs/index.ts b/react-api-client/src/runs/index.ts index 71e3360a5f9..5e479ed5093 100644 --- a/react-api-client/src/runs/index.ts +++ b/react-api-client/src/runs/index.ts @@ -10,6 +10,7 @@ export { usePlayRunMutation } from './usePlayRunMutation' export { usePauseRunMutation } from './usePauseRunMutation' export { useStopRunMutation } from './useStopRunMutation' export { useResumeRunFromRecoveryMutation } from './useResumeRunFromRecoveryMutation' +export { useResumeRunFromRecoveryAssumingFalsePositiveMutation } from './useResumeFromRecoveryAssumingFalsePositiveMutation' export { useRunActionMutations } from './useRunActionMutations' export { useAllCommandsQuery } from './useAllCommandsQuery' export { useAllCommandsAsPreSerializedList } from './useAllCommandsAsPreSerializedList' @@ -23,3 +24,4 @@ export type { UsePlayRunMutationResult } from './usePlayRunMutation' export type { UsePauseRunMutationResult } from './usePauseRunMutation' export type { UseStopRunMutationResult } from './useStopRunMutation' export type { UseResumeRunFromRecoveryMutationResult } from './useResumeRunFromRecoveryMutation' +export type { UseResumeRunFromRecoveryAssumingFalsePositiveMutationResult } from './useResumeFromRecoveryAssumingFalsePositiveMutation' diff --git a/react-api-client/src/runs/useResumeFromRecoveryAssumingFalsePositiveMutation.ts b/react-api-client/src/runs/useResumeFromRecoveryAssumingFalsePositiveMutation.ts new file mode 100644 index 00000000000..6eb10990053 --- /dev/null +++ b/react-api-client/src/runs/useResumeFromRecoveryAssumingFalsePositiveMutation.ts @@ -0,0 +1,60 @@ +import { useMutation } from 'react-query' + +import { + RUN_ACTION_TYPE_RESUME_FROM_RECOVERY_ASSUMING_FALSE_POSITIVE, + createRunAction, +} from '@opentrons/api-client' + +import { useHost } from '../api' + +import type { AxiosError } from 'axios' +import type { + UseMutateFunction, + UseMutationOptions, + UseMutationResult, +} from 'react-query' +import type { HostConfig, RunAction } from '@opentrons/api-client' + +export type UseResumeRunFromRecoveryAssumingFalsePositiveMutationResult = UseMutationResult< + RunAction, + AxiosError, + string +> & { + resumeRunFromRecoveryAssumingFalsePositive: UseMutateFunction< + RunAction, + AxiosError, + string + > +} + +export type UseResumeRunFromRecoveryAssumingFalsePositiveMutationOptions = UseMutationOptions< + RunAction, + AxiosError, + string +> + +export const useResumeRunFromRecoveryAssumingFalsePositiveMutation = ( + options: UseResumeRunFromRecoveryAssumingFalsePositiveMutationOptions = {} +): UseResumeRunFromRecoveryAssumingFalsePositiveMutationResult => { + const host = useHost() + const mutation = useMutation( + [ + host, + 'runs', + RUN_ACTION_TYPE_RESUME_FROM_RECOVERY_ASSUMING_FALSE_POSITIVE, + ], + (runId: string) => + createRunAction(host as HostConfig, runId, { + actionType: RUN_ACTION_TYPE_RESUME_FROM_RECOVERY_ASSUMING_FALSE_POSITIVE, + }) + .then(response => response.data) + .catch(e => { + throw e + }), + options + ) + return { + ...mutation, + resumeRunFromRecoveryAssumingFalsePositive: mutation.mutate, + } +} diff --git a/react-api-client/src/runs/useRunActionMutations.ts b/react-api-client/src/runs/useRunActionMutations.ts index 8bf3a08f1cb..a64411e7209 100644 --- a/react-api-client/src/runs/useRunActionMutations.ts +++ b/react-api-client/src/runs/useRunActionMutations.ts @@ -5,6 +5,7 @@ import { usePauseRunMutation, useStopRunMutation, useResumeRunFromRecoveryMutation, + useResumeRunFromRecoveryAssumingFalsePositiveMutation, } from '..' interface UseRunActionMutations { @@ -12,10 +13,12 @@ interface UseRunActionMutations { pauseRun: () => void stopRun: () => void resumeRunFromRecovery: () => void + resumeRunFromRecoveryAssumingFalsePositive: () => void isPlayRunActionLoading: boolean isPauseRunActionLoading: boolean isStopRunActionLoading: boolean isResumeRunFromRecoveryActionLoading: boolean + isResumeRunFromRecoveryAssumingFalsePositiveActionLoading: boolean } export function useRunActionMutations(runId: string): UseRunActionMutations { @@ -43,6 +46,11 @@ export function useRunActionMutations(runId: string): UseRunActionMutations { isLoading: isResumeRunFromRecoveryActionLoading, } = useResumeRunFromRecoveryMutation() + const { + resumeRunFromRecoveryAssumingFalsePositive, + isLoading: isResumeRunFromRecoveryAssumingFalsePositiveActionLoading, + } = useResumeRunFromRecoveryAssumingFalsePositiveMutation() + return { playRun: () => { playRun(runId) @@ -56,9 +64,13 @@ export function useRunActionMutations(runId: string): UseRunActionMutations { resumeRunFromRecovery: () => { resumeRunFromRecovery(runId) }, + resumeRunFromRecoveryAssumingFalsePositive: () => { + resumeRunFromRecoveryAssumingFalsePositive(runId) + }, isPlayRunActionLoading, isPauseRunActionLoading, isStopRunActionLoading, isResumeRunFromRecoveryActionLoading, + isResumeRunFromRecoveryAssumingFalsePositiveActionLoading, } }