21720:
[arvados.git] / services / workbench2 / src / views-components / details-panel / workflow-details.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 { WorkflowIcon, StartIcon } from 'components/icon/icon';
7 import {
8     WorkflowResource, parseWorkflowDefinition, getWorkflowInputs,
9     getWorkflowOutputs, getWorkflow
10 } from 'models/workflow';
11 import { DetailsData } from "./details-data";
12 import { DetailsAttribute } from 'components/details-attribute/details-attribute';
13 import { ResourceWithName } from 'views-components/data-explorer/renderers';
14 import { formatDate } from "common/formatters";
15 import { Grid } from '@mui/material';
16 import { CustomStyleRulesCallback } from 'common/custom-theme';
17 import { Button } from '@mui/material';
18 import { WithStyles } from '@mui/styles';
19 import withStyles from '@mui/styles/withStyles';
20 import { openRunProcess } from "store/workflow-panel/workflow-panel-actions";
21 import { Dispatch } from 'redux';
22 import { connect } from 'react-redux';
23 import { ArvadosTheme } from 'common/custom-theme';
24 import { ProcessIOParameter } from 'views/process-panel/process-io-card';
25 import { formatInputData, formatOutputData } from 'store/process-panel/process-panel-actions';
26 import { AuthState } from 'store/auth/auth-reducer';
27 import { RootState } from 'store/store';
28 import { getPropertyChip } from "views-components/resource-properties-form/property-chip";
29
30 export interface WorkflowDetailsCardDataProps {
31     workflow?: WorkflowResource;
32 }
33
34 export interface WorkflowDetailsCardActionProps {
35     onClick: (wf: WorkflowResource) => () => void;
36 }
37
38 const mapDispatchToProps = (dispatch: Dispatch) => ({
39     onClick: (wf: WorkflowResource) =>
40         () => wf && dispatch<any>(openRunProcess(wf.uuid, wf.ownerUuid, wf.name)),
41 });
42
43 type CssRules = 'runButton' | 'propertyTag';
44
45 const styles: CustomStyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
46     runButton: {
47         backgroundColor: theme.customs.colors.green700,
48         '&:hover': {
49             backgroundColor: theme.customs.colors.green800,
50         },
51         marginRight: "5px",
52         boxShadow: 'none',
53         padding: '2px 10px 2px 5px',
54         marginLeft: 'auto'
55     },
56     propertyTag: {
57         marginRight: theme.spacing(0.5),
58         marginBottom: theme.spacing(0.5)
59     },
60 });
61
62 interface AuthStateDataProps {
63     auth: AuthState;
64 }
65
66 export interface RegisteredWorkflowPanelDataProps {
67     item: WorkflowResource;
68     workflowCollection: string;
69     inputParams: ProcessIOParameter[];
70     outputParams: ProcessIOParameter[];
71     gitprops: { [key: string]: string; };
72 }
73
74 export const getRegisteredWorkflowPanelData = (item: WorkflowResource, auth: AuthState): RegisteredWorkflowPanelDataProps => {
75     let inputParams: ProcessIOParameter[] = [];
76     let outputParams: ProcessIOParameter[] = [];
77     let workflowCollection = "";
78     const gitprops: { [key: string]: string; } = {};
79
80     // parse definition
81     const wfdef = parseWorkflowDefinition(item);
82
83     if (wfdef) {
84         const inputs = getWorkflowInputs(wfdef);
85         if (inputs) {
86             inputs.forEach(elm => {
87                 if (elm.default !== undefined && elm.default !== null) {
88                     elm.value = elm.default;
89                 }
90             });
91             inputParams = formatInputData(inputs, auth);
92         }
93
94         const outputs = getWorkflowOutputs(wfdef);
95         if (outputs) {
96             outputParams = formatOutputData(outputs, {}, undefined, auth);
97         }
98
99         const wf = getWorkflow(wfdef);
100         if (wf) {
101             const REGEX = /keep:([0-9a-f]{32}\+\d+)\/.*/;
102             if (wf["steps"]) {
103                 const pdh = wf["steps"][0].run.match(REGEX);
104                 if (pdh) {
105                     workflowCollection = pdh[1];
106                 }
107             }
108         }
109
110         for (const elm in wfdef) {
111             if (elm.startsWith("http://arvados.org/cwl#git")) {
112                 gitprops[elm.substr(23)] = wfdef[elm]
113             }
114         }
115     }
116
117     return { item, workflowCollection, inputParams, outputParams, gitprops };
118 };
119
120 const mapStateToProps = (state: RootState): AuthStateDataProps => {
121     return { auth: state.auth };
122 };
123
124 export const WorkflowDetailsAttributes = connect(mapStateToProps, mapDispatchToProps)(
125     withStyles(styles)(
126         ({ workflow, onClick, auth, classes }: WorkflowDetailsCardDataProps & AuthStateDataProps & WorkflowDetailsCardActionProps & WithStyles<CssRules>) => {
127             if (!workflow) {
128                 return <Grid />
129             }
130
131             const data = getRegisteredWorkflowPanelData(workflow, auth);
132             return <Grid container>
133                 <Button onClick={workflow && onClick(workflow)} className={classes.runButton} variant='contained'
134                     data-cy='workflow-details-panel-run-btn' color='primary' size='small'>
135                     <StartIcon />
136                     Run Workflow
137                 </Button>
138                 <Grid item xs={12} >
139                     <DetailsAttribute
140                         label={"Workflow UUID"}
141                         linkToUuid={workflow?.uuid} />
142                 </Grid>
143                 <Grid item xs={12} >
144                     <DetailsAttribute
145                         label='Owner' linkToUuid={workflow?.ownerUuid}
146                         uuidEnhancer={(uuid: string) => <ResourceWithName uuid={uuid} />} />
147                 </Grid>
148                 <Grid item xs={12}>
149                     <DetailsAttribute label='Created at' value={formatDate(workflow?.createdAt)} />
150                 </Grid>
151                 <Grid item xs={12}>
152                     <DetailsAttribute label='Last modified' value={formatDate(workflow?.modifiedAt)} />
153                 </Grid>
154                 <Grid item xs={12} data-cy="workflow-details-attributes-modifiedby-user">
155                     <DetailsAttribute
156                         label='Last modified by user' linkToUuid={workflow?.modifiedByUserUuid}
157                         uuidEnhancer={(uuid: string) => <ResourceWithName uuid={uuid} />} />
158                 </Grid>
159                 <Grid item xs={12} md={12}>
160                     <DetailsAttribute label='Properties' />
161                     {Object.keys(data.gitprops).map(k =>
162                         getPropertyChip(k, data.gitprops[k], undefined, classes.propertyTag))}
163                 </Grid>
164             </Grid >;
165         }));
166
167 export class WorkflowDetails extends DetailsData<WorkflowResource> {
168     getIcon(className?: string) {
169         return <WorkflowIcon className={className} />;
170     }
171
172     getDetails() {
173         return <WorkflowDetailsAttributes workflow={this.item} />;
174     }
175 }