Merge branch '21600-banner-tests'
[arvados.git] / services / workbench2 / src / views / process-panel / process-panel-root.tsx
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import React from "react";
6 import { StyleRulesCallback, WithStyles, withStyles } from "@material-ui/core";
7 import { ProcessIcon } from "components/icon/icon";
8 import { Process } from "store/processes/process";
9 import { SubprocessPanel } from "views/subprocess-panel/subprocess-panel";
10 import { SubprocessFilterDataProps } from "components/subprocess-filter/subprocess-filter";
11 import { MPVContainer, MPVPanelContent, MPVPanelState } from "components/multi-panel-view/multi-panel-view";
12 import { ArvadosTheme } from "common/custom-theme";
13 import { ProcessDetailsCard } from "./process-details-card";
14 import { ProcessIOCard, ProcessIOCardType, ProcessIOParameter } from "./process-io-card";
15 import { ProcessResourceCard } from "./process-resource-card";
16 import { getProcessPanelLogs, ProcessLogsPanel } from "store/process-logs-panel/process-logs-panel";
17 import { ProcessLogsCard } from "./process-log-card";
18 import { FilterOption } from "views/process-panel/process-log-form";
19 import { getInputCollectionMounts } from "store/processes/processes-actions";
20 import { WorkflowInputsData } from "models/workflow";
21 import { CommandOutputParameter } from "cwlts/mappings/v1.0/CommandOutputParameter";
22 import { AuthState } from "store/auth/auth-reducer";
23 import { ProcessCmdCard } from "./process-cmd-card";
24 import { ContainerRequestResource } from "models/container-request";
25 import { OutputDetails, NodeInstanceType } from "store/process-panel/process-panel";
26 import { NotFoundView } from 'views/not-found-panel/not-found-panel';
27 import { CollectionFile } from 'models/collection-file';
28
29 type CssRules = "root";
30
31 const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
32     root: {
33         width: "100%",
34     },
35 });
36
37 export interface ProcessPanelRootDataProps {
38     process?: Process;
39     subprocesses: Array<Process>;
40     filters: Array<SubprocessFilterDataProps>;
41     processLogsPanel: ProcessLogsPanel;
42     auth: AuthState;
43     inputRaw: WorkflowInputsData | null;
44     inputParams: ProcessIOParameter[] | null;
45     outputData: OutputDetails | null;
46     outputDefinitions: CommandOutputParameter[];
47     outputParams: ProcessIOParameter[] | null;
48     nodeInfo: NodeInstanceType | null;
49     usageReport: string | null;
50 }
51
52 export interface ProcessPanelRootActionProps {
53     onContextMenu: (event: React.MouseEvent<HTMLElement>, process: Process) => void;
54     onToggle: (status: string) => void;
55     cancelProcess: (uuid: string) => void;
56     startProcess: (uuid: string) => void;
57     resumeOnHoldWorkflow: (uuid: string) => void;
58     onLogFilterChange: (filter: FilterOption) => void;
59     navigateToLog: (uuid: string) => void;
60     onCopyToClipboard: (uuid: string) => void;
61     loadInputs: (containerRequest: ContainerRequestResource) => void;
62     loadOutputs: (containerRequest: ContainerRequestResource) => void;
63     loadNodeJson: (containerRequest: ContainerRequestResource) => void;
64     loadOutputDefinitions: (containerRequest: ContainerRequestResource) => void;
65     updateOutputParams: () => void;
66     pollProcessLogs: (processUuid: string) => Promise<void>;
67 }
68
69 export type ProcessPanelRootProps = ProcessPanelRootDataProps & ProcessPanelRootActionProps & WithStyles<CssRules>;
70
71 const panelsData: MPVPanelState[] = [
72     { name: "Details" },
73     { name: "Logs", visible: true },
74     { name: "Subprocesses" },
75     { name: "Outputs" },
76     { name: "Inputs" },
77     { name: "Command" },
78     { name: "Resources" },
79 ];
80
81 export const ProcessPanelRoot = withStyles(styles)(
82     ({
83         process,
84         auth,
85         processLogsPanel,
86         inputRaw,
87         inputParams,
88         outputData,
89         outputDefinitions,
90         outputParams,
91         nodeInfo,
92         usageReport,
93         loadInputs,
94         loadOutputs,
95         loadNodeJson,
96         loadOutputDefinitions,
97         updateOutputParams,
98         ...props
99     }: ProcessPanelRootProps) => {
100         const outputUuid = process?.containerRequest.outputUuid;
101         const containerRequest = process?.containerRequest;
102         const inputMounts = getInputCollectionMounts(process?.containerRequest);
103
104         React.useEffect(() => {
105             if (containerRequest) {
106                 // Load inputs from mounts or props
107                 loadInputs(containerRequest);
108                 // Fetch raw output (loads from props or keep)
109                 loadOutputs(containerRequest);
110                 // Loads output definitions from mounts into store
111                 loadOutputDefinitions(containerRequest);
112                 // load the assigned instance type from node.json in
113                 // the log collection
114                 loadNodeJson(containerRequest);
115             }
116         }, [containerRequest, loadInputs, loadOutputs, loadOutputDefinitions, loadNodeJson]);
117
118         const maxHeight = "100%";
119
120         // Trigger processing output params when raw or definitions change
121         React.useEffect(() => {
122             updateOutputParams();
123         }, [outputData, outputDefinitions, updateOutputParams]);
124
125         return process ? (
126             <MPVContainer
127                 className={props.classes.root}
128                 spacing={8}
129                 panelStates={panelsData}
130                 justify-content="flex-start"
131                 direction="column"
132                 wrap="nowrap">
133                 <MPVPanelContent
134                     forwardProps
135                     xs="auto"
136                     data-cy="process-details">
137                     <ProcessDetailsCard
138                         process={process}
139                         onContextMenu={event => props.onContextMenu(event, process)}
140                         cancelProcess={props.cancelProcess}
141                         startProcess={props.startProcess}
142                         resumeOnHoldWorkflow={props.resumeOnHoldWorkflow}
143                     />
144                 </MPVPanelContent>
145                 <MPVPanelContent
146                     forwardProps
147                     xs
148                     minHeight={maxHeight}
149                     maxHeight={maxHeight}
150                     data-cy="process-logs">
151                     <ProcessLogsCard
152                         onCopy={props.onCopyToClipboard}
153                         process={process}
154                         lines={getProcessPanelLogs(processLogsPanel)}
155                         selectedFilter={{
156                             label: processLogsPanel.selectedFilter,
157                             value: processLogsPanel.selectedFilter,
158                         }}
159                         filters={processLogsPanel.filters.map(filter => ({ label: filter, value: filter }))}
160                         onLogFilterChange={props.onLogFilterChange}
161                         navigateToLog={props.navigateToLog}
162                         pollProcessLogs={props.pollProcessLogs}
163                     />
164                 </MPVPanelContent>
165                 <MPVPanelContent
166                     forwardProps
167                     xs
168                     maxHeight={maxHeight}
169                     data-cy="process-children">
170                     <SubprocessPanel process={process} />
171                 </MPVPanelContent>
172                 <MPVPanelContent
173                     forwardProps
174                     xs
175                     maxHeight={maxHeight}
176                     data-cy="process-outputs">
177                     <ProcessIOCard
178                         label={ProcessIOCardType.OUTPUT}
179                         process={process}
180                         params={outputParams}
181                         raw={outputData?.raw}
182                         outputUuid={outputUuid || ""}
183                     />
184                 </MPVPanelContent>
185                 <MPVPanelContent
186                     forwardProps
187                     xs
188                     maxHeight={maxHeight}
189                     data-cy="process-inputs">
190                     <ProcessIOCard
191                         label={ProcessIOCardType.INPUT}
192                         process={process}
193                         params={inputParams}
194                         raw={inputRaw}
195                         mounts={inputMounts}
196                     />
197                 </MPVPanelContent>
198                 <MPVPanelContent
199                     forwardProps
200                     xs="auto"
201                     data-cy="process-cmd">
202                     <ProcessCmdCard
203                         onCopy={props.onCopyToClipboard}
204                         process={process}
205                     />
206                 </MPVPanelContent>
207                 <MPVPanelContent
208                     forwardProps
209                     xs
210                     data-cy="process-resources">
211                     <ProcessResourceCard
212                         process={process}
213                         nodeInfo={nodeInfo}
214                         usageReport={usageReport}
215                     />
216                 </MPVPanelContent>
217             </MPVContainer>
218         ) : (
219             <NotFoundView
220                 icon={ProcessIcon}
221                 messages={["Process not found"]}
222             />
223         );
224     }
225 );