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