Skip to content

Commit

Permalink
feat: add recover option to workflows (#192)
Browse files Browse the repository at this point in the history
* feat: add recover button to console

Signed-off-by: csirius <[email protected]>

* fix: unit testing wrapper to have react query provider

Signed-off-by: csirius <[email protected]>
  • Loading branch information
govalt authored Aug 27, 2021
1 parent c49f6bf commit 818c686
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { makeStyles, Theme } from '@material-ui/core/styles';
import ArrowBack from '@material-ui/icons/ArrowBack';
import * as classnames from 'classnames';
import { navbarGridHeight } from 'common/layout';
import { ButtonCircularProgress } from 'components/common/ButtonCircularProgress';
import { MoreOptionsMenu } from 'components/common/MoreOptionsMenu';
import { useCommonStyles } from 'components/common/styles';
import { useLocationState } from 'components/hooks/useLocationState';
Expand All @@ -20,6 +21,7 @@ import { executionIsRunning, executionIsTerminal } from '../utils';
import { backLinkTitle, executionActionStrings } from './constants';
import { RelaunchExecutionForm } from './RelaunchExecutionForm';
import { getExecutionBackLink, getExecutionSourceId } from './utils';
import { useRecoverExecutionState } from './useRecoverExecutionState';

const useStyles = makeStyles((theme: Theme) => {
return {
Expand Down Expand Up @@ -95,6 +97,16 @@ export const ExecutionDetailsAppBarContent: React.FC<{
const backLink = fromExecutionNav
? Routes.ProjectDetails.sections.executions.makeUrl(project, domain)
: originalBackLink;
const {
recoverExecution,
recoverState: { isLoading: recovering, error, data: recoveredId }
} = useRecoverExecutionState();

React.useEffect(() => {
if (!recovering && recoveredId) {
history.push(Routes.ExecutionDetails.makeUrl(recoveredId));
}
}, [recovering, recoveredId]);

let modalContent: JSX.Element | null = null;
if (showInputsOutputs) {
Expand All @@ -107,21 +119,41 @@ export const ExecutionDetailsAppBarContent: React.FC<{
);
}

const onClickRecover = React.useCallback(async () => {
await recoverExecution();
}, [recoverExecution]);

const actionContent = isRunning ? (
<TerminateExecutionButton className={styles.actionButton} />
) : isTerminal ? (
<Button
variant="outlined"
color="primary"
className={classnames(
styles.actionButton,
commonStyles.buttonWhiteOutlined
)}
onClick={onClickRelaunch}
size="small"
>
Relaunch
</Button>
<>
<Button
variant="outlined"
color="primary"
disabled={recovering}
className={classnames(
styles.actionButton,
commonStyles.buttonWhiteOutlined
)}
onClick={onClickRecover}
size="small"
>
Recover
{recovering && <ButtonCircularProgress />}
</Button>
<Button
variant="outlined"
color="primary"
className={classnames(
styles.actionButton,
commonStyles.buttonWhiteOutlined
)}
onClick={onClickRelaunch}
size="small"
>
Relaunch
</Button>
</>
) : null;

// For non-terminal executions, add an overflow menu with the ability to clone
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import { createMockExecution } from 'models/__mocks__/executionsData';
import * as React from 'react';
import { MemoryRouter } from 'react-router';
import { Routes } from 'routes/routes';
import { QueryClient, QueryClientProvider } from 'react-query';
import { createTestQueryClient } from 'test/utils';
import { backLinkTitle, executionActionStrings } from '../constants';
import { ExecutionDetailsAppBarContent } from '../ExecutionDetailsAppBarContent';

Expand All @@ -27,6 +29,7 @@ describe('ExecutionDetailsAppBarContent', () => {
let execution: Execution;
let executionContext: ExecutionContextData;
let sourceId: Identifier;
let queryClient: QueryClient;

beforeEach(() => {
execution = createMockExecution();
Expand All @@ -35,15 +38,19 @@ describe('ExecutionDetailsAppBarContent', () => {
executionContext = {
execution
};

queryClient = createTestQueryClient();
});

const renderContent = () =>
render(
<MemoryRouter>
<ExecutionContext.Provider value={executionContext}>
<ExecutionDetailsAppBarContent execution={execution} />
</ExecutionContext.Provider>
</MemoryRouter>
<QueryClientProvider client={queryClient}>
<MemoryRouter>
<ExecutionContext.Provider value={executionContext}>
<ExecutionDetailsAppBarContent execution={execution} />
</ExecutionContext.Provider>
</MemoryRouter>
</QueryClientProvider>
);

describe('for running executions', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { useAPIContext } from 'components/data/apiContext';
import { useContext } from 'react';
import { useMutation } from 'react-query';
import { ExecutionContext } from '../contexts';
import { WorkflowExecutionIdentifier } from 'models/Execution/types';

export function useRecoverExecutionState() {
const { recoverWorkflowExecution } = useAPIContext();
const {
execution: { id }
} = useContext(ExecutionContext);

const { mutate, ...recoverState } = useMutation<
WorkflowExecutionIdentifier,
Error
>(async () => {
const { id: recoveredId } = await recoverWorkflowExecution({ id });
if (!recoveredId) {
throw new Error('API Response did not include new execution id');
}
return recoveredId as WorkflowExecutionIdentifier;
});

const recoverExecution = () => mutate();

return {
recoverState,
recoverExecution
};
}
1 change: 1 addition & 0 deletions src/models/Common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export const endpointPrefixes = {
nodeExecution: '/node_executions',
project: '/projects',
relaunchExecution: '/executions/relaunch',
recoverExecution: '/executions/recover',
task: '/tasks',
taskExecution: '/task_executions',
taskExecutionChildren: '/children/task_executions',
Expand Down
28 changes: 28 additions & 0 deletions src/models/Execution/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { defaultExecutionPrincipal } from './constants';
import {
Execution,
ExecutionData,
ExecutionMetadata,
NodeExecution,
NodeExecutionIdentifier,
TaskExecution,
Expand Down Expand Up @@ -181,6 +182,33 @@ export const relaunchWorkflowExecution = (
config
);

interface RecoverParams {
id: WorkflowExecutionIdentifier;
name?: string;
metadata?: ExecutionMetadata;
}

/**
* Submits a request to recover a WorkflowExecution
*/

export const recoverWorkflowExecution = (
{ id, name, metadata }: RecoverParams,
config?: RequestConfig
) =>
postAdminEntity<
Admin.IExecutionRecoverRequest,
Admin.ExecutionCreateResponse
>(
{
data: { id, name, metadata },
path: endpointPrefixes.recoverExecution,
requestMessageType: Admin.ExecutionRecoverRequest,
responseMessageType: Admin.ExecutionCreateResponse
},
config
);

/** Retrieves a single `NodeExecution` record */
export const getNodeExecution = (
id: NodeExecutionIdentifier,
Expand Down

0 comments on commit 818c686

Please sign in to comment.