TableRow,
TableCell,
Paper,
- Link,
Grid,
Chip,
} from '@material-ui/core';
import { AuthState } from 'store/auth/auth-reducer';
import mime from 'mime';
import { DefaultView } from 'components/default-view/default-view';
+import { getNavUrl } from 'routes/routes';
+import { Link as RouterLink } from 'react-router-dom';
+import { Link as MuiLink } from '@material-ui/core';
+import { InputCollectionMount } from 'store/processes/processes-actions';
+import { connect } from 'react-redux';
+import { RootState } from 'store/store';
type CssRules = 'card' | 'content' | 'title' | 'header' | 'avatar' | 'iconHeader' | 'tableWrapper' | 'tableRoot' | 'paramValue' | 'keepLink' | 'imagePreview' | 'valArray' | 'emptyValue';
alignItems: 'center',
},
keepLink: {
+ color: theme.palette.primary.main,
+ textDecoration: 'none',
+ overflowWrap: 'break-word',
cursor: 'pointer',
},
imagePreview: {
display: 'flex',
gap: '10px',
flexWrap: 'wrap',
+ '& span': {
+ display: 'inline',
+ }
},
emptyValue: {
color: theme.customs.colors.grey500,
},
});
+export enum ProcessIOCardType {
+ INPUT = 'Inputs',
+ OUTPUT = 'Outputs',
+}
export interface ProcessIOCardDataProps {
- label: string;
+ label: ProcessIOCardType;
params: ProcessIOParameter[];
raw?: any;
+ mounts?: InputCollectionMount[];
}
type ProcessIOCardProps = ProcessIOCardDataProps & WithStyles<CssRules> & MPVPanelProps;
export const ProcessIOCard = withStyles(styles)(
- ({ classes, label, params, raw, doHidePanel, panelName }: ProcessIOCardProps) => {
+ ({ classes, label, params, raw, mounts, doHidePanel, panelName }: ProcessIOCardProps) => {
const [tabState, setTabState] = useState(0);
const handleChange = (event: React.MouseEvent<HTMLElement>, value: number) => {
setTabState(value);
</div>
} />
<CardContent className={classes.content}>
- {params.length ?
<div>
<Tabs value={tabState} onChange={handleChange} variant="fullWidth">
<Tab label="Preview" />
<Tab label="Raw" />
+ {label === ProcessIOCardType.INPUT && <Tab label="Input Mounts" />}
</Tabs>
{tabState === 0 && <div className={classes.tableWrapper}>
- <ProcessIOPreview data={params} />
+ {params.length ?
+ <ProcessIOPreview data={params} /> :
+ <Grid container item alignItems='center' justify='center'>
+ <DefaultView messages={["No parameters found"]} icon={InfoIcon} />
+ </Grid>}
</div>}
{tabState === 1 && <div className={classes.tableWrapper}>
- <ProcessIORaw data={raw || params} />
+ {params.length ?
+ <ProcessIORaw data={raw || params} /> :
+ <Grid container item alignItems='center' justify='center'>
+ <DefaultView messages={["No parameters found"]} icon={InfoIcon} />
+ </Grid>}
+ </div>}
+ {tabState === 2 && <div className={classes.tableWrapper}>
+ {label === ProcessIOCardType.INPUT && <ProcessInputMounts mounts={mounts || []} />}
</div>}
- </div> : <Grid container item alignItems='center' justify='center'>
- <DefaultView messages={["No parameters found"]} icon={InfoIcon} />
- </Grid>}
+ </div>
</CardContent>
</Card>;
}
export type ProcessIOValue = {
display: ReactElement<any, any>;
- nav?: string;
imageUrl?: string;
}
const ProcessIOPreview = withStyles(styles)(
({ classes, data }: ProcessIOPreviewProps) =>
- <Table className={classes.tableRoot} aria-label="simple table">
+ <Table className={classes.tableRoot} aria-label="Process IO Preview">
<TableHead>
<TableRow>
<TableCell>Label</TableCell>
<TableCell>{param.value.map(val => (
<Typography className={classes.paramValue}>
{val.imageUrl ? <img className={classes.imagePreview} src={val.imageUrl} alt="Inline Preview" /> : ""}
- {val.nav ?
- <Link className={classes.keepLink} onClick={() => handleClick(val.nav)}>{val.display}</Link>
- : <span className={classes.valArray}>
- {val.display}
- </span>
- }
+ <span className={classes.valArray}>
+ {val.display}
+ </span>
</Typography>
))}</TableCell>
</TableRow>;
</Paper>
);
+interface ProcessInputMountsDataProps {
+ mounts: InputCollectionMount[];
+}
+
+type ProcessInputMountsProps = ProcessInputMountsDataProps & WithStyles<CssRules>;
+
+const ProcessInputMounts = withStyles(styles)(connect((state: RootState) => ({
+ auth: state.auth,
+}))(({ mounts, classes, auth }: ProcessInputMountsProps & { auth: AuthState }) => (
+ <Table className={classes.tableRoot} aria-label="Process Input Mounts">
+ <TableHead>
+ <TableRow>
+ <TableCell>Path</TableCell>
+ <TableCell>Portable Data Hash</TableCell>
+ </TableRow>
+ </TableHead>
+ <TableBody>
+ {mounts.map(mount => (
+ <TableRow key={mount.path}>
+ <TableCell><pre>{mount.path}</pre></TableCell>
+ <TableCell>
+ <RouterLink to={getNavUrl(mount.pdh, auth)} className={classes.keepLink}>{mount.pdh}</RouterLink>
+ </TableCell>
+ </TableRow>
+ ))}
+ </TableBody>
+ </Table>
+)));
+
type FileWithSecondaryFiles = {
secondaryFiles: File[];
}
return keepUrl || '';
};
-const getNavUrl = (auth: AuthState, file: File | Directory, pdh?: string): string => {
+interface KeepUrlProps {
+ auth: AuthState;
+ res: File | Directory;
+ pdh?: string;
+}
+
+const KeepUrlBase = withStyles(styles)(({auth, res, pdh, classes}: KeepUrlProps & WithStyles<CssRules>) => {
+ const keepUrl = getKeepUrl(res, pdh);
+ const pdhUrl = keepUrl ? keepUrl.split('/').slice(0, 1)[0] : '';
+ // Passing a pdh always returns a relative wb2 collection url
+ const pdhWbPath = getNavUrl(pdhUrl.replace('keep:', ''), auth);
+ return pdhUrl && pdhWbPath ?
+ <RouterLink to={pdhWbPath} className={classes.keepLink}>{pdhUrl}</RouterLink> :
+ <></>;
+});
+
+const KeepUrlPath = withStyles(styles)(({auth, res, pdh, classes}: KeepUrlProps & WithStyles<CssRules>) => {
+ const keepUrl = getKeepUrl(res, pdh);
+ const keepUrlParts = keepUrl ? keepUrl.split('/') : [];
+ const keepUrlPath = keepUrlParts.length > 1 ? keepUrlParts.slice(1).join('/') : '';
+
+ const keepUrlPathNav = getKeepNavUrl(auth, res, pdh);
+ return keepUrlPath && keepUrlPathNav ?
+ <MuiLink className={classes.keepLink} onClick={() => handleClick(keepUrlPathNav)}>{keepUrlPath}</MuiLink> :
+ <></>;
+});
+
+const getKeepNavUrl = (auth: AuthState, file: File | Directory, pdh?: string): string => {
let keepUrl = getKeepUrl(file, pdh).replace('keep:', '');
return (getInlineFileUrl(`${auth.config.keepWebServiceUrl}/c=${keepUrl}?api_token=${auth.apiToken}`, auth.config.keepWebServiceUrl, auth.config.keepWebInlineServiceUrl));
};
const directoryToProcessIOValue = (directory: Directory, auth: AuthState, pdh?: string): ProcessIOValue => {
const normalizedDirectory = normalizeDirectoryLocation(directory);
return {
- display: <>{getKeepUrl(normalizedDirectory, pdh)}</>,
- nav: getNavUrl(auth, normalizedDirectory, pdh),
+ display: <span>
+ <KeepUrlBase auth={auth} res={normalizedDirectory} pdh={pdh}/>/<KeepUrlPath auth={auth} res={normalizedDirectory} pdh={pdh}/>
+ </span>,
};
};
-const fileToProcessIOValue = (file: File, auth: AuthState, pdh?: string): ProcessIOValue => ({
- display: <>{getKeepUrl(file, pdh)}</>,
- nav: getNavUrl(auth, file, pdh),
- imageUrl: isFileImage(file.basename) ? getImageUrl(auth, file, pdh) : undefined,
-});
+const fileToProcessIOValue = (file: File, auth: AuthState, pdh?: string): ProcessIOValue => {
+ return {
+ display: <span>
+ <KeepUrlBase auth={auth} res={file} pdh={pdh}/>/<KeepUrlPath auth={auth} res={file} pdh={pdh}/>
+ </span>,
+ imageUrl: isFileImage(file.basename) ? getImageUrl(auth, file, pdh) : undefined,
+ }
+};
const EmptyValue = withStyles(styles)(
({classes}: WithStyles<CssRules>) => <span className={classes.emptyValue}>No value</span>
import { MPVContainer, MPVPanelContent, MPVPanelState } from 'components/multi-panel-view/multi-panel-view';
import { ArvadosTheme } from 'common/custom-theme';
import { ProcessDetailsCard } from './process-details-card';
-import { getIOParamDisplayValue, ProcessIOCard, ProcessIOParameter } from './process-io-card';
+import { getIOParamDisplayValue, ProcessIOCard, ProcessIOCardType, ProcessIOParameter } from './process-io-card';
import { getProcessPanelLogs, ProcessLogsPanel } from 'store/process-logs-panel/process-logs-panel';
import { ProcessLogsCard } from './process-log-card';
import { FilterOption } from 'views/process-panel/process-log-form';
-import { getInputs, getOutputParameters } from 'store/processes/processes-actions';
+import { getInputs, getInputCollectionMounts, getOutputParameters } from 'store/processes/processes-actions';
import { CommandInputParameter, getIOParamId } from 'models/workflow';
import { CommandOutputParameter } from 'cwlts/mappings/v1.0/CommandOutputParameter';
import { AuthState } from 'store/auth/auth-reducer';
const outputUuid = process?.containerRequest.outputUuid;
const requestUuid = process?.containerRequest.uuid;
+ const inputMounts = getInputCollectionMounts(process?.containerRequest);
+
React.useEffect(() => {
if (outputUuid) {
fetchOutputs(outputUuid, setOutputs);
</MPVPanelContent>
<MPVPanelContent forwardProps xs="auto" data-cy="process-inputs">
<ProcessIOCard
- label="Inputs"
+ label={ProcessIOCardType.INPUT}
params={processedInputs}
raw={rawInputs}
+ mounts={inputMounts}
/>
</MPVPanelContent>
<MPVPanelContent forwardProps xs="auto" data-cy="process-outputs">
<ProcessIOCard
- label="Outputs"
+ label={ProcessIOCardType.OUTPUT}
params={processedOutputs}
raw={outputDetails.rawOutputs}
/>