1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
5 import { unionize, ofType, UnionOf } from "common/unionize";
6 import { getInputs, getOutputParameters, getRawInputs, getRawOutputs, loadProcess } from "store/processes/processes-actions";
7 import { Dispatch } from "redux";
8 import { ProcessStatus } from "store/processes/process";
9 import { RootState } from "store/store";
10 import { ServiceRepository } from "services/services";
11 import { navigateTo } from "store/navigation/navigation-action";
12 import { snackbarActions } from "store/snackbar/snackbar-actions";
13 import { SnackbarKind } from "../snackbar/snackbar-actions";
14 import { loadSubprocessPanel, subprocessPanelActions } from "../subprocess-panel/subprocess-panel-actions";
15 import { initProcessLogsPanel, processLogsPanelActions } from "store/process-logs-panel/process-logs-panel-actions";
16 import { CollectionFile } from "models/collection-file";
17 import { ContainerRequestResource } from "models/container-request";
18 import { CommandOutputParameter } from "cwlts/mappings/v1.0/CommandOutputParameter";
19 import { CommandInputParameter, getIOParamId, WorkflowInputsData } from "models/workflow";
20 import { getIOParamDisplayValue, ProcessIOParameter } from "views/process-panel/process-io-card";
21 import { OutputDetails, NodeInstanceType, NodeInfo } from "./process-panel";
22 import { AuthState } from "store/auth/auth-reducer";
23 import { ContextMenuResource } from "store/context-menu/context-menu-actions";
24 import { OutputDataUpdate } from "./process-panel-reducer";
26 export const processPanelActions = unionize({
27 RESET_PROCESS_PANEL: ofType<{}>(),
28 SET_PROCESS_PANEL_CONTAINER_REQUEST_UUID: ofType<string>(),
29 SET_PROCESS_PANEL_FILTERS: ofType<string[]>(),
30 TOGGLE_PROCESS_PANEL_FILTER: ofType<string>(),
31 SET_INPUT_RAW: ofType<WorkflowInputsData | null>(),
32 SET_INPUT_PARAMS: ofType<ProcessIOParameter[] | null>(),
33 SET_OUTPUT_DATA: ofType<OutputDataUpdate | null>(),
34 SET_OUTPUT_DEFINITIONS: ofType<CommandOutputParameter[]>(),
35 SET_OUTPUT_PARAMS: ofType<ProcessIOParameter[] | null>(),
36 SET_NODE_INFO: ofType<NodeInfo>(),
39 export type ProcessPanelAction = UnionOf<typeof processPanelActions>;
41 export const toggleProcessPanelFilter = processPanelActions.TOGGLE_PROCESS_PANEL_FILTER;
43 export const loadProcessPanel = (uuid: string) => async (dispatch: Dispatch, getState: () => RootState) => {
44 // Reset subprocess data explorer if navigating to new process
45 // Avoids resetting pagination when refreshing same process
46 if (getState().processPanel.containerRequestUuid !== uuid) {
47 dispatch(subprocessPanelActions.CLEAR());
49 dispatch(processPanelActions.RESET_PROCESS_PANEL());
50 dispatch(processLogsPanelActions.RESET_PROCESS_LOGS_PANEL());
51 dispatch<ProcessPanelAction>(processPanelActions.SET_PROCESS_PANEL_CONTAINER_REQUEST_UUID(uuid));
52 await dispatch<any>(loadProcess(uuid));
53 dispatch(initProcessPanelFilters);
54 dispatch<any>(initProcessLogsPanel(uuid));
55 dispatch<any>(loadSubprocessPanel());
58 export const navigateToOutput = (resource: ContextMenuResource | ContainerRequestResource) => async (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
60 await services.collectionService.get(resource.outputUuid || '');
61 dispatch<any>(navigateTo(resource.outputUuid || ''));
63 dispatch(snackbarActions.OPEN_SNACKBAR({ message: "Output collection was trashed or deleted.", hideDuration: 4000, kind: SnackbarKind.WARNING }));
67 export const loadInputs =
68 (containerRequest: ContainerRequestResource) => async (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
69 dispatch<ProcessPanelAction>(processPanelActions.SET_INPUT_RAW(getRawInputs(containerRequest)));
70 dispatch<ProcessPanelAction>(processPanelActions.SET_INPUT_PARAMS(formatInputData(getInputs(containerRequest), getState().auth)));
73 export const loadOutputs =
74 (containerRequest: ContainerRequestResource) => async (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
75 const noOutputs: OutputDetails = { raw: {} };
77 if (!containerRequest.outputUuid) {
78 dispatch<ProcessPanelAction>(processPanelActions.SET_OUTPUT_DATA({
79 uuid: containerRequest.uuid,
85 const propsOutputs = getRawOutputs(containerRequest);
86 const filesPromise = services.collectionService.files(containerRequest.outputUuid);
87 const collectionPromise = services.collectionService.get(containerRequest.outputUuid);
88 const [files, collection] = await Promise.all([filesPromise, collectionPromise]);
90 // If has propsOutput, skip fetching cwl.output.json
91 if (propsOutputs !== undefined) {
92 dispatch<ProcessPanelAction>(
93 processPanelActions.SET_OUTPUT_DATA({
94 uuid: containerRequest.uuid,
97 pdh: collection.portableDataHash,
102 // Fetch outputs from keep
103 const outputFile = files.find(file => file.name === "cwl.output.json") as CollectionFile | undefined;
104 let outputData = outputFile ? await services.collectionService.getFileContents(outputFile) : undefined;
105 if (outputData && (outputData = JSON.parse(outputData)) && collection.portableDataHash) {
106 dispatch<ProcessPanelAction>(
107 processPanelActions.SET_OUTPUT_DATA({
108 uuid: containerRequest.uuid,
111 pdh: collection.portableDataHash,
116 dispatch<ProcessPanelAction>(processPanelActions.SET_OUTPUT_DATA({ uuid: containerRequest.uuid, payload: noOutputs }));
120 dispatch<ProcessPanelAction>(processPanelActions.SET_OUTPUT_DATA({ uuid: containerRequest.uuid, payload: noOutputs }));
124 export const loadNodeJson =
125 (containerRequest: ContainerRequestResource) => async (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
126 const noLog = { nodeInfo: null };
127 if (!containerRequest.logUuid) {
128 dispatch<ProcessPanelAction>(processPanelActions.SET_NODE_INFO(noLog));
132 const filesPromise = services.collectionService.files(containerRequest.logUuid);
133 const collectionPromise = services.collectionService.get(containerRequest.logUuid);
134 const [files] = await Promise.all([filesPromise, collectionPromise]);
136 // Fetch node.json from keep
137 const nodeFile = files.find(file => file.name === "node.json") as CollectionFile | undefined;
138 let nodeData = nodeFile ? await services.collectionService.getFileContents(nodeFile) : undefined;
139 if (nodeData && (nodeData = JSON.parse(nodeData))) {
140 dispatch<ProcessPanelAction>(
141 processPanelActions.SET_NODE_INFO({
142 nodeInfo: nodeData as NodeInstanceType,
146 dispatch<ProcessPanelAction>(processPanelActions.SET_NODE_INFO(noLog));
149 dispatch<ProcessPanelAction>(processPanelActions.SET_NODE_INFO(noLog));
153 export const loadOutputDefinitions =
154 (containerRequest: ContainerRequestResource) => async (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
155 if (containerRequest && containerRequest.mounts) {
156 dispatch<ProcessPanelAction>(processPanelActions.SET_OUTPUT_DEFINITIONS(getOutputParameters(containerRequest)));
160 export const updateOutputParams = () => async (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
161 const outputDefinitions = getState().processPanel.outputDefinitions;
162 const outputData = getState().processPanel.outputData;
164 if (outputData && outputData.raw) {
165 dispatch<ProcessPanelAction>(
166 processPanelActions.SET_OUTPUT_PARAMS(formatOutputData(outputDefinitions, outputData.raw, outputData.pdh, getState().auth))
171 export const openWorkflow = (uuid: string) => (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
172 dispatch<any>(navigateTo(uuid));
175 export const initProcessPanelFilters = processPanelActions.SET_PROCESS_PANEL_FILTERS([
176 ProcessStatus.QUEUED,
177 ProcessStatus.COMPLETED,
178 ProcessStatus.FAILED,
179 ProcessStatus.RUNNING,
180 ProcessStatus.ONHOLD,
181 ProcessStatus.FAILING,
182 ProcessStatus.WARNING,
183 ProcessStatus.CANCELLED,
186 export const formatInputData = (inputs: CommandInputParameter[], auth: AuthState): ProcessIOParameter[] => {
187 return inputs.map(input => {
189 id: getIOParamId(input),
190 label: input.label || "",
191 value: getIOParamDisplayValue(auth, input),
196 export const formatOutputData = (
197 definitions: CommandOutputParameter[],
199 pdh: string | undefined,
201 ): ProcessIOParameter[] => {
202 return definitions.map(output => {
204 id: getIOParamId(output),
205 label: output.label || "",
206 value: getIOParamDisplayValue(auth, Object.assign(output, { value: values[getIOParamId(output)] || [] }), pdh),