Merge branch '16041-valid-name' refs #16041
[arvados.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 import { MOUNT_PATH_CWL_WORKFLOW, MOUNT_PATH_CWL_INPUT } from '~/models/process';
22 import { getWorkflow, getWorkflowInputs } from "~/models/workflow";
23
24 export const loadProcess = (containerRequestUuid: string) =>
25     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository): Promise<Process> => {
26         const response = await services.workflowService.list();
27         dispatch(runProcessPanelActions.SET_WORKFLOWS(response.items));
28         const containerRequest = await services.containerRequestService.get(containerRequestUuid);
29         dispatch<any>(updateResources([containerRequest]));
30         if (containerRequest.containerUuid) {
31             const container = await services.containerService.get(containerRequest.containerUuid);
32             dispatch<any>(updateResources([container]));
33             await dispatch<any>(loadSubprocesses(containerRequest.containerUuid));
34             return { containerRequest, container };
35         }
36         return { containerRequest };
37     };
38
39 export const loadSubprocesses = (containerUuid: string) =>
40     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
41         const containerRequests = await dispatch<any>(loadContainerRequests(
42             new FilterBuilder().addEqual('requesting_container_uuid', containerUuid).getFilters()
43         )) as ContainerRequestResource[];
44
45         const containerUuids: string[] = containerRequests.reduce((uuids, { containerUuid }) =>
46             containerUuid
47                 ? [...uuids, containerUuid]
48                 : uuids, []);
49
50         if (containerUuids.length > 0) {
51             await dispatch<any>(loadContainers(
52                 new FilterBuilder().addIn('uuid', containerUuids).getFilters()
53             ));
54         }
55     };
56
57 const MAX_AMOUNT_OF_SUBPROCESSES = 10000;
58
59 export const loadContainerRequests = (filters: string) =>
60     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
61         const { items } = await services.containerRequestService.list({ filters, limit: MAX_AMOUNT_OF_SUBPROCESSES });
62         dispatch<any>(updateResources(items));
63         return items;
64     };
65
66 export const loadContainers = (filters: string) =>
67     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
68         const { items } = await services.containerService.list({ filters });
69         dispatch<any>(updateResources(items));
70         return items;
71     };
72
73 export const cancelRunningWorkflow = (uuid: string) =>
74     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
75         try {
76             const process = await services.containerRequestService.update(uuid, { priority: 0 });
77             return process;
78         } catch (e) {
79             throw new Error('Could not cancel the process.');
80         }
81     };
82
83 export const reRunProcess = (processUuid: string, workflowUuid: string) =>
84     (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
85         const process = getResource<any>(processUuid)(getState().resources);
86         const workflows = getState().runProcessPanel.searchWorkflows;
87         const workflow = workflows.find(workflow => workflow.uuid === workflowUuid);
88         if (workflow && process) {
89             const mainWf = getWorkflow(process.mounts[MOUNT_PATH_CWL_WORKFLOW]);
90             if (mainWf) { mainWf.inputs = getInputs(process); }
91             const stringifiedDefinition = JSON.stringify(process.mounts[MOUNT_PATH_CWL_WORKFLOW].content);
92             const newWorkflow = { ...workflow, definition: stringifiedDefinition };
93
94             const basicInitialData: RunProcessBasicFormData = { name: `Copy of: ${process.name}`, description: process.description };
95             dispatch<any>(initialize(RUN_PROCESS_BASIC_FORM, basicInitialData));
96
97             const advancedInitialData: RunProcessAdvancedFormData = {
98                 output: process.outputName,
99                 runtime: process.schedulingParameters.max_run_time,
100                 ram: process.runtimeConstraints.ram,
101                 vcpus: process.runtimeConstraints.vcpus,
102                 keep_cache_ram: process.runtimeConstraints.keep_cache_ram,
103                 api: process.runtimeConstraints.API
104             };
105             dispatch<any>(initialize(RUN_PROCESS_ADVANCED_FORM, advancedInitialData));
106
107             dispatch<any>(navigateToRunProcess);
108             dispatch<any>(goToStep(1));
109             dispatch(runProcessPanelActions.SET_STEP_CHANGED(true));
110             dispatch(runProcessPanelActions.SET_SELECTED_WORKFLOW(newWorkflow));
111         } else {
112             dispatch<any>(snackbarActions.OPEN_SNACKBAR({ message: `You can't re-run this process`, kind: SnackbarKind.ERROR }));
113         }
114     };
115
116 const getInputs = (data: any) => {
117     if (!data || !data.mounts || !data.mounts[MOUNT_PATH_CWL_WORKFLOW]) { return []; }
118     const inputs = getWorkflowInputs(data.mounts[MOUNT_PATH_CWL_WORKFLOW].content);
119     return inputs ? inputs.map(
120         (it: any) => (
121             {
122                 type: it.type,
123                 id: it.id,
124                 label: it.label,
125                 default: data.mounts[MOUNT_PATH_CWL_INPUT].content[it.id],
126                 doc: it.doc
127             }
128     )
129     ) : [];
130 };
131
132 export const openRemoveProcessDialog = (uuid: string) =>
133     (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
134         dispatch(dialogActions.OPEN_DIALOG({
135             id: REMOVE_PROCESS_DIALOG,
136             data: {
137                 title: 'Remove process permanently',
138                 text: 'Are you sure you want to remove this process?',
139                 confirmButtonLabel: 'Remove',
140                 uuid
141             }
142         }));
143     };
144
145 export const REMOVE_PROCESS_DIALOG = 'removeProcessDialog';
146
147 export const removeProcessPermanently = (uuid: string) =>
148     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
149         dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Removing ...', kind: SnackbarKind.INFO }));
150         await services.containerRequestService.delete(uuid);
151         dispatch(projectPanelActions.REQUEST_ITEMS());
152         dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Removed.', hideDuration: 2000, kind: SnackbarKind.SUCCESS }));
153     };
154
155