21225: Rename project panel middleware to data middleware and separate data explorer
[arvados.git] / services / workbench2 / src / store / workflow-panel / workflow-panel-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 { bindDataExplorerActions } from 'store/data-explorer/data-explorer-action';
9 import { propertiesActions } from 'store/properties/properties-actions';
10 import { getProperty } from 'store/properties/properties';
11 import { navigateToRunProcess, navigateTo } from 'store/navigation/navigation-action';
12 import {
13     goToStep,
14     runProcessPanelActions,
15     loadPresets,
16     getWorkflowRunnerSettings
17 } from 'store/run-process-panel/run-process-panel-actions';
18 import { snackbarActions, SnackbarKind } from 'store/snackbar/snackbar-actions';
19 import { initialize } from 'redux-form';
20 import { RUN_PROCESS_BASIC_FORM } from 'views/run-process-panel/run-process-basic-form';
21 import { RUN_PROCESS_INPUTS_FORM } from 'views/run-process-panel/run-process-inputs-form';
22 import { RUN_PROCESS_ADVANCED_FORM } from 'views/run-process-panel/run-process-advanced-form';
23 import { getResource } from 'store/resources/resources';
24 import { ProjectResource } from 'models/project';
25 import { UserResource } from 'models/user';
26 import { getWorkflowInputs, parseWorkflowDefinition } from 'models/workflow';
27 import { ContextMenuResource } from 'store/context-menu/context-menu-actions';
28 import { dialogActions } from 'store/dialog/dialog-actions';
29 import { ResourceKind, Resource } from 'models/resource';
30 import { selectedToArray } from "components/multiselect-toolbar/MultiselectToolbar";
31 import { CommonResourceServiceError, getCommonResourceServiceError } from "services/common-service/common-resource-service";
32 import { projectPanelDataActions } from "store/project-panel/project-panel-action-bind";
33
34 export const WORKFLOW_PANEL_ID = "workflowPanel";
35 const UUID_PREFIX_PROPERTY_NAME = 'uuidPrefix';
36 const WORKFLOW_PANEL_DETAILS_UUID = 'workflowPanelDetailsUuid';
37 export const workflowPanelActions = bindDataExplorerActions(WORKFLOW_PANEL_ID);
38
39 export const WORKFLOW_PROCESSES_PANEL_ID = "workflowProcessesPanel";
40 export const workflowProcessesPanelActions = bindDataExplorerActions(WORKFLOW_PROCESSES_PANEL_ID);
41
42 export const loadWorkflowPanel = () =>
43     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
44         dispatch(workflowPanelActions.REQUEST_ITEMS());
45         const response = await services.workflowService.list();
46         dispatch(runProcessPanelActions.SET_WORKFLOWS(response.items));
47     };
48
49 export const setUuidPrefix = (uuidPrefix: string) =>
50     propertiesActions.SET_PROPERTY({ key: UUID_PREFIX_PROPERTY_NAME, value: uuidPrefix });
51
52 export const getUuidPrefix = (state: RootState) => {
53     return state.properties.uuidPrefix;
54 };
55
56 export const openRunProcess = (workflowUuid: string, ownerUuid?: string, name?: string, inputObj?: { [key: string]: any }) =>
57     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
58         const response = await services.workflowService.list();
59         dispatch(runProcessPanelActions.SET_WORKFLOWS(response.items));
60
61         const workflows = getState().runProcessPanel.searchWorkflows;
62         const listedWorkflow = workflows.find(workflow => workflow.uuid === workflowUuid);
63         const workflow = listedWorkflow || await services.workflowService.get(workflowUuid);
64         if (workflow) {
65             dispatch<any>(navigateToRunProcess);
66             dispatch<any>(goToStep(1));
67             dispatch(runProcessPanelActions.SET_STEP_CHANGED(true));
68             dispatch(runProcessPanelActions.SET_SELECTED_WORKFLOW(workflow));
69             dispatch<any>(loadPresets(workflow.uuid));
70
71             dispatch(initialize(RUN_PROCESS_ADVANCED_FORM, getWorkflowRunnerSettings(workflow)));
72             let owner;
73             if (ownerUuid) {
74                 // Must be writable.
75                 owner = getResource<ProjectResource | UserResource>(ownerUuid)(getState().resources);
76                 if (!owner || !owner.canWrite) {
77                     owner = undefined;
78                 }
79             }
80             if (owner) {
81                 dispatch(runProcessPanelActions.SET_PROCESS_OWNER_UUID(owner.uuid));
82             }
83
84             dispatch(initialize(RUN_PROCESS_BASIC_FORM, { name, owner }));
85
86             const definition = parseWorkflowDefinition(workflow);
87             if (definition) {
88                 const inputs = getWorkflowInputs(definition);
89                 if (inputs) {
90                     const values = inputs.reduce((values, input) => ({
91                         ...values,
92                         [input.id]: input.default,
93                     }), {});
94                     dispatch(initialize(RUN_PROCESS_INPUTS_FORM, values));
95                 }
96             }
97
98             if (inputObj) {
99                 dispatch(initialize(RUN_PROCESS_INPUTS_FORM, inputObj));
100             }
101         } else {
102             dispatch<any>(snackbarActions.OPEN_SNACKBAR({ message: `You can't run this process` }));
103         }
104     };
105
106 export const getPublicUserUuid = (state: RootState) => {
107     const prefix = state.auth.localCluster;
108     return `${prefix}-tpzed-anonymouspublic`;
109 };
110 export const getPublicGroupUuid = (state: RootState) => {
111     const prefix = state.auth.localCluster;
112     return `${prefix}-j7d0g-anonymouspublic`;
113 };
114 export const getAllUsersGroupUuid = (state: RootState) => {
115     const prefix = state.auth.localCluster;
116     return `${prefix}-j7d0g-fffffffffffffff`;
117 };
118
119 export const showWorkflowDetails = (uuid: string) =>
120     propertiesActions.SET_PROPERTY({ key: WORKFLOW_PANEL_DETAILS_UUID, value: uuid });
121
122 export const getWorkflowDetails = (state: RootState) => {
123     const uuid = getProperty<string>(WORKFLOW_PANEL_DETAILS_UUID)(state.properties);
124     const workflows = state.runProcessPanel.workflows;
125     const workflow = workflows.find(workflow => workflow.uuid === uuid);
126     return workflow || undefined;
127 };
128
129 export const openRemoveWorkflowDialog =
130 (resource: ContextMenuResource, numOfWorkflows: Number) => (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
131     const confirmationText =
132         numOfWorkflows === 1
133             ? "Are you sure you want to remove this workflow?"
134             : `Are you sure you want to remove these ${numOfWorkflows} workflows?`;
135     const titleText = numOfWorkflows === 1 ? "Remove workflow permanently" : "Remove workflows permanently";
136
137     dispatch(
138         dialogActions.OPEN_DIALOG({
139             id: REMOVE_WORKFLOW_DIALOG,
140             data: {
141                 title: titleText,
142                 text: confirmationText,
143                 confirmButtonLabel: "Remove",
144                 uuid: resource.uuid,
145                 resource,
146             },
147         })
148     );
149 };
150
151 export const REMOVE_WORKFLOW_DIALOG = "removeWorkflowDialog";
152
153 export const removeWorkflowPermanently = (uuid: string, ownerUuid?: string) => async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
154     const resource = getState().dialog.removeWorkflowDialog.data.resource;
155     const checkedList = getState().multiselect.checkedList;
156
157     const uuidsToRemove: string[] = resource.fromContextMenu ? [resource.uuid] : selectedToArray(checkedList);
158
159     //if no items in checkedlist, default to normal context menu behavior
160     if (!uuidsToRemove.length) uuidsToRemove.push(uuid);
161     if(ownerUuid) dispatch<any>(navigateTo(ownerUuid));
162
163     const workflowsToRemove = uuidsToRemove
164         .map(uuid => getResource(uuid)(getState().resources) as Resource)
165         .filter(resource => resource.kind === ResourceKind.WORKFLOW);
166
167     for (const workflow of workflowsToRemove) {
168         try {
169             dispatch(snackbarActions.OPEN_SNACKBAR({ message: "Removing ...", kind: SnackbarKind.INFO }));
170             await services.workflowService.delete(workflow.uuid);
171             dispatch(projectPanelDataActions.REQUEST_ITEMS());
172             dispatch(snackbarActions.OPEN_SNACKBAR({ message: "Removed.", hideDuration: 2000, kind: SnackbarKind.SUCCESS }));
173         } catch (e) {
174             const error = getCommonResourceServiceError(e);
175             if (error === CommonResourceServiceError.PERMISSION_ERROR_FORBIDDEN) {
176                 dispatch(snackbarActions.OPEN_SNACKBAR({ message: `Access denied`, hideDuration: 2000, kind: SnackbarKind.ERROR }));
177             } else {
178                 dispatch(snackbarActions.OPEN_SNACKBAR({ message: `Deletion failed`, hideDuration: 2000, kind: SnackbarKind.ERROR }));
179             }
180         }
181     }
182 };