15407: Fixes handling of runtime constraints and scheduling params.
[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 { getInputValue } from "~/views-components/process-input-dialog/process-input-dialog";
19 import { initialize } from "redux-form";
20 import { RUN_PROCESS_BASIC_FORM, RunProcessBasicFormData } from "~/views/run-process-panel/run-process-basic-form";
21 import { RunProcessAdvancedFormData, RUN_PROCESS_ADVANCED_FORM } from "~/views/run-process-panel/run-process-advanced-form";
22
23 export const loadProcess = (containerRequestUuid: string) =>
24     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository): Promise<Process> => {
25         const response = await services.workflowService.list();
26         dispatch(runProcessPanelActions.SET_WORKFLOWS(response.items));
27         const containerRequest = await services.containerRequestService.get(containerRequestUuid);
28         dispatch<any>(updateResources([containerRequest]));
29         if (containerRequest.containerUuid) {
30             const container = await services.containerService.get(containerRequest.containerUuid);
31             dispatch<any>(updateResources([container]));
32             await dispatch<any>(loadSubprocesses(containerRequest.containerUuid));
33             return { containerRequest, container };
34         }
35         return { containerRequest };
36     };
37
38 export const loadSubprocesses = (containerUuid: string) =>
39     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
40         const containerRequests = await dispatch<any>(loadContainerRequests(
41             new FilterBuilder().addEqual('requestingContainerUuid', containerUuid).getFilters()
42         )) as ContainerRequestResource[];
43
44         const containerUuids: string[] = containerRequests.reduce((uuids, { containerUuid }) =>
45             containerUuid
46                 ? [...uuids, containerUuid]
47                 : uuids, []);
48
49         if (containerUuids.length > 0) {
50             await dispatch<any>(loadContainers(
51                 new FilterBuilder().addIn('uuid', containerUuids).getFilters()
52             ));
53         }
54     };
55
56 const MAX_AMOUNT_OF_SUBPROCESSES = 10000;
57
58 export const loadContainerRequests = (filters: string) =>
59     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
60         const { items } = await services.containerRequestService.list({ filters, limit: MAX_AMOUNT_OF_SUBPROCESSES });
61         dispatch<any>(updateResources(items));
62         return items;
63     };
64
65 export const loadContainers = (filters: string) =>
66     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
67         const { items } = await services.containerService.list({ filters });
68         dispatch<any>(updateResources(items));
69         return items;
70     };
71
72 export const cancelRunningWorkflow = (uuid: string) =>
73     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
74         try {
75             const process = await services.containerRequestService.update(uuid, { priority: 0 });
76             return process;
77         } catch (e) {
78             throw new Error('Could not cancel the process.');
79         }
80     };
81
82 export const reRunProcess = (processUuid: string, workflowUuid: string) =>
83     (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
84         const process = getResource<any>(processUuid)(getState().resources);
85         const workflows = getState().runProcessPanel.searchWorkflows;
86         const workflow = workflows.find(workflow => workflow.uuid === workflowUuid);
87         if (workflow && process) {
88             const newValues = getInputs(process);
89             process.mounts.varLibCwlWorkflowJson.content.graph[1].inputs = newValues;
90             const stringifiedDefinition = JSON.stringify(process.mounts.varLibCwlWorkflowJson.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.varLibCwlWorkflowJson ? data.mounts.varLibCwlWorkflowJson.content.graph[1].inputs.map((it: any) => (
117         { type: it.type, id: it.id, label: it.label, default: getInputValue(it.id, data.mounts.varLibCwlCwlInputJson.content), doc: it.doc }
118     )) : [];
119
120 export const openRemoveProcessDialog = (uuid: string) =>
121     (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
122         dispatch(dialogActions.OPEN_DIALOG({
123             id: REMOVE_PROCESS_DIALOG,
124             data: {
125                 title: 'Remove process permanently',
126                 text: 'Are you sure you want to remove this process?',
127                 confirmButtonLabel: 'Remove',
128                 uuid
129             }
130         }));
131     };
132
133 export const REMOVE_PROCESS_DIALOG = 'removeProcessDialog';
134
135 export const removeProcessPermanently = (uuid: string) =>
136     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
137         dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Removing ...', kind: SnackbarKind.INFO }));
138         await services.containerRequestService.delete(uuid);
139         dispatch(projectPanelActions.REQUEST_ITEMS());
140         dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Removed.', hideDuration: 2000, kind: SnackbarKind.SUCCESS }));
141     };
142
143