15407: Fixes mount handling. Re-run process now shows dialog without erroring.
[arvados-workbench2.git] / src / store / processes / processes-actions.ts
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import { Dispatch } from "redux";
6 import { RootState } from '~/store/store';
7 import { ServiceRepository } from '~/services/services';
8 import { updateResources } from '~/store/resources/resources-actions';
9 import { FilterBuilder } from '~/services/api/filter-builder';
10 import { ContainerRequestResource } from '~/models/container-request';
11 import { Process } from './process';
12 import { dialogActions } from '~/store/dialog/dialog-actions';
13 import { snackbarActions, SnackbarKind } from '~/store/snackbar/snackbar-actions';
14 import { projectPanelActions } from '~/store/project-panel/project-panel-action';
15 import { navigateToRunProcess } from '~/store/navigation/navigation-action';
16 import { goToStep, runProcessPanelActions } from '~/store/run-process-panel/run-process-panel-actions';
17 import { getResource } from '~/store/resources/resources';
18 import { initialize } from "redux-form";
19 import { RUN_PROCESS_BASIC_FORM, RunProcessBasicFormData } from "~/views/run-process-panel/run-process-basic-form";
20 import { RunProcessAdvancedFormData, RUN_PROCESS_ADVANCED_FORM } from "~/views/run-process-panel/run-process-advanced-form";
21
22 export const loadProcess = (containerRequestUuid: string) =>
23     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository): Promise<Process> => {
24         const response = await services.workflowService.list();
25         dispatch(runProcessPanelActions.SET_WORKFLOWS(response.items));
26         const containerRequest = await services.containerRequestService.get(containerRequestUuid);
27         dispatch<any>(updateResources([containerRequest]));
28         if (containerRequest.containerUuid) {
29             const container = await services.containerService.get(containerRequest.containerUuid);
30             dispatch<any>(updateResources([container]));
31             await dispatch<any>(loadSubprocesses(containerRequest.containerUuid));
32             return { containerRequest, container };
33         }
34         return { containerRequest };
35     };
36
37 export const loadSubprocesses = (containerUuid: string) =>
38     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
39         const containerRequests = await dispatch<any>(loadContainerRequests(
40             new FilterBuilder().addEqual('requestingContainerUuid', containerUuid).getFilters()
41         )) as ContainerRequestResource[];
42
43         const containerUuids: string[] = containerRequests.reduce((uuids, { containerUuid }) =>
44             containerUuid
45                 ? [...uuids, containerUuid]
46                 : uuids, []);
47
48         if (containerUuids.length > 0) {
49             await dispatch<any>(loadContainers(
50                 new FilterBuilder().addIn('uuid', containerUuids).getFilters()
51             ));
52         }
53     };
54
55 const MAX_AMOUNT_OF_SUBPROCESSES = 10000;
56
57 export const loadContainerRequests = (filters: string) =>
58     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
59         const { items } = await services.containerRequestService.list({ filters, limit: MAX_AMOUNT_OF_SUBPROCESSES });
60         dispatch<any>(updateResources(items));
61         return items;
62     };
63
64 export const loadContainers = (filters: string) =>
65     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
66         const { items } = await services.containerService.list({ filters });
67         dispatch<any>(updateResources(items));
68         return items;
69     };
70
71 export const cancelRunningWorkflow = (uuid: string) =>
72     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
73         try {
74             const process = await services.containerRequestService.update(uuid, { priority: 0 });
75             return process;
76         } catch (e) {
77             throw new Error('Could not cancel the process.');
78         }
79     };
80
81 export const reRunProcess = (processUuid: string, workflowUuid: string) =>
82     (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
83         const process = getResource<any>(processUuid)(getState().resources);
84         const workflows = getState().runProcessPanel.searchWorkflows;
85         const workflow = workflows.find(workflow => workflow.uuid === workflowUuid);
86         if (workflow && process) {
87             const newValues = getInputs(process);
88             process.mounts["/var/lib/cwl/workflow.json"].content.$graph.find(
89                 (a: any) => a.id === '#main').inputs = newValues;
90             const stringifiedDefinition = JSON.stringify(process.mounts["/var/lib/cwl/workflow.json"].content);
91             const newWorkflow = { ...workflow, definition: stringifiedDefinition };
92
93             const basicInitialData: RunProcessBasicFormData = { name: `Copy of: ${process.name}`, description: process.description };
94             dispatch<any>(initialize(RUN_PROCESS_BASIC_FORM, basicInitialData));
95
96             const advancedInitialData: RunProcessAdvancedFormData = {
97                 output: process.outputName,
98                 runtime: process.schedulingParameters.max_run_time,
99                 ram: process.runtimeConstraints.ram,
100                 vcpus: process.runtimeConstraints.vcpus,
101                 keep_cache_ram: process.runtimeConstraints.keep_cache_ram,
102                 api: process.runtimeConstraints.API
103             };
104             dispatch<any>(initialize(RUN_PROCESS_ADVANCED_FORM, advancedInitialData));
105
106             dispatch<any>(navigateToRunProcess);
107             dispatch<any>(goToStep(1));
108             dispatch(runProcessPanelActions.SET_STEP_CHANGED(true));
109             dispatch(runProcessPanelActions.SET_SELECTED_WORKFLOW(newWorkflow));
110         } else {
111             dispatch<any>(snackbarActions.OPEN_SNACKBAR({ message: `You can't re-run this process`, kind: SnackbarKind.ERROR }));
112         }
113     };
114
115 const getInputs = (data: any) =>
116     data && data.mounts["/var/lib/cwl/workflow.json"] ? data.mounts["/var/lib/cwl/workflow.json"].content.$graph.find(
117         (a: any) => a.id === '#main').inputs.map(
118             (it: any) => (
119                 {
120                     type: it.type,
121                     id: it.id,
122                     label: it.label,
123                     default: data.mounts["/var/lib/cwl/cwl.input.json"].content[it.id],
124                     doc: it.doc
125                 }
126             )
127         ) : [];
128
129 export const openRemoveProcessDialog = (uuid: string) =>
130     (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
131         dispatch(dialogActions.OPEN_DIALOG({
132             id: REMOVE_PROCESS_DIALOG,
133             data: {
134                 title: 'Remove process permanently',
135                 text: 'Are you sure you want to remove this process?',
136                 confirmButtonLabel: 'Remove',
137                 uuid
138             }
139         }));
140     };
141
142 export const REMOVE_PROCESS_DIALOG = 'removeProcessDialog';
143
144 export const removeProcessPermanently = (uuid: string) =>
145     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
146         dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Removing ...', kind: SnackbarKind.INFO }));
147         await services.containerRequestService.delete(uuid);
148         dispatch(projectPanelActions.REQUEST_ITEMS());
149         dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Removed.', hideDuration: 2000, kind: SnackbarKind.SUCCESS }));
150     };
151
152