16073: Display process io params from props, hide preview when lacking workflow mount
authorStephen Smith <stephen@curii.com>
Tue, 11 Oct 2022 22:59:05 +0000 (18:59 -0400)
committerStephen Smith <stephen@curii.com>
Wed, 12 Oct 2022 01:59:05 +0000 (21:59 -0400)
Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen@curii.com>

src/store/process-panel/process-panel-actions.ts
src/store/processes/processes-actions.ts
src/views/process-panel/process-io-card.tsx
src/views/process-panel/process-panel-root.tsx
src/views/process-panel/process-panel.tsx

index c8c0bcc77d34c4d5a4ebcf32f6df7af1bf4e74a7..b62a48863cccced282c9cab0041e78eb7eb9b19a 100644 (file)
@@ -3,7 +3,7 @@
 // SPDX-License-Identifier: AGPL-3.0
 
 import { unionize, ofType, UnionOf } from "common/unionize";
 // SPDX-License-Identifier: AGPL-3.0
 
 import { unionize, ofType, UnionOf } from "common/unionize";
-import { loadProcess } from 'store/processes/processes-actions';
+import { getRawOutputs, loadProcess } from 'store/processes/processes-actions';
 import { Dispatch } from 'redux';
 import { ProcessStatus } from 'store/processes/process';
 import { RootState } from 'store/store';
 import { Dispatch } from 'redux';
 import { ProcessStatus } from 'store/processes/process';
 import { RootState } from 'store/store';
@@ -15,6 +15,7 @@ import { showWorkflowDetails } from 'store/workflow-panel/workflow-panel-actions
 import { loadSubprocessPanel } from "../subprocess-panel/subprocess-panel-actions";
 import { initProcessLogsPanel, processLogsPanelActions } from "store/process-logs-panel/process-logs-panel-actions";
 import { CollectionFile } from "models/collection-file";
 import { loadSubprocessPanel } from "../subprocess-panel/subprocess-panel-actions";
 import { initProcessLogsPanel, processLogsPanelActions } from "store/process-logs-panel/process-logs-panel-actions";
 import { CollectionFile } from "models/collection-file";
+import { ContainerRequestResource } from "models/container-request";
 
 export const processPanelActions = unionize({
     SET_PROCESS_PANEL_CONTAINER_REQUEST_UUID: ofType<string>(),
 
 export const processPanelActions = unionize({
     SET_PROCESS_PANEL_CONTAINER_REQUEST_UUID: ofType<string>(),
@@ -46,22 +47,30 @@ export const navigateToOutput = (uuid: string) =>
         }
     };
 
         }
     };
 
-export const loadOutputs = (uuid: string, setOutputs) =>
+export const loadOutputs = (containerRequest: ContainerRequestResource, setOutputs) =>
     async (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
     async (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
+        if (!containerRequest.outputUuid) {setOutputs({}); return;};
         try {
         try {
-            const filesPromise = services.collectionService.files(uuid);
-            const collectionPromise = services.collectionService.get(uuid);
+            const propsOutputs = getRawOutputs(containerRequest);
+            const filesPromise = services.collectionService.files(containerRequest.outputUuid);
+            const collectionPromise = services.collectionService.get(containerRequest.outputUuid);
             const [files, collection] = await Promise.all([filesPromise, collectionPromise]);
 
             const [files, collection] = await Promise.all([filesPromise, collectionPromise]);
 
-            const outputFile = files.find((file) => file.name === 'cwl.output.json') as CollectionFile | undefined;
-            let outputData = outputFile ? await services.collectionService.getFileContents(outputFile) : undefined;
-            if ((outputData = JSON.parse(outputData)) && collection.portableDataHash) {
-                setOutputs({
-                    rawOutputs: outputData,
-                    pdh: collection.portableDataHash,
-                });
+            // If has propsOutput, skip fetching cwl.output.json
+            if (propsOutputs !== undefined) {
+                setOutputs({rawOutputs: propsOutputs, pdh: collection.portableDataHash});
             } else {
             } else {
-                setOutputs({});
+                // Fetch outputs from keep
+                const outputFile = files.find((file) => file.name === 'cwl.output.json') as CollectionFile | undefined;
+                let outputData = outputFile ? await services.collectionService.getFileContents(outputFile) : undefined;
+                if ((outputData = JSON.parse(outputData)) && collection.portableDataHash) {
+                    setOutputs({
+                        rawOutputs: outputData,
+                        pdh: collection.portableDataHash,
+                    });
+                } else {
+                    setOutputs({});
+                }
             }
         } catch {
             setOutputs({});
             }
         } catch {
             setOutputs({});
index eb04ed676f7644026cb8462760b47c064336dc87..1f672759a085bd339e76b5c659159fec83e46e72 100644 (file)
@@ -133,13 +133,22 @@ export const reRunProcess = (processUuid: string, workflowUuid: string) =>
         }
     };
 
         }
     };
 
+/*
+ * Fetches raw inputs from containerRequest mounts with fallback to properties
+ */
 export const getRawInputs = (data: any): CommandInputParameter[] | undefined => {
 export const getRawInputs = (data: any): CommandInputParameter[] | undefined => {
-    if (!data || !data.mounts || !data.mounts[MOUNT_PATH_CWL_INPUT]) { return undefined; }
-    return (data.mounts[MOUNT_PATH_CWL_INPUT].content);
+    if (!data) { return undefined; }
+    const mountInput = data.mounts?.[MOUNT_PATH_CWL_INPUT]?.content;
+    const propsInput = data.properties?.cwl_input;
+    if (!mountInput && !propsInput) { return undefined; }
+    return (mountInput || propsInput);
 }
 
 export const getInputs = (data: any): CommandInputParameter[] => {
     if (!data || !data.mounts || !data.mounts[MOUNT_PATH_CWL_WORKFLOW]) { return []; }
 }
 
 export const getInputs = (data: any): CommandInputParameter[] => {
     if (!data || !data.mounts || !data.mounts[MOUNT_PATH_CWL_WORKFLOW]) { return []; }
+    const content  = getRawInputs(data) as any;
+    if (!content) { return []; }
+
     const inputs = getWorkflowInputs(data.mounts[MOUNT_PATH_CWL_WORKFLOW].content);
     return inputs ? inputs.map(
         (it: any) => (
     const inputs = getWorkflowInputs(data.mounts[MOUNT_PATH_CWL_WORKFLOW].content);
     return inputs ? inputs.map(
         (it: any) => (
@@ -147,14 +156,22 @@ export const getInputs = (data: any): CommandInputParameter[] => {
                 type: it.type,
                 id: it.id,
                 label: it.label,
                 type: it.type,
                 id: it.id,
                 label: it.label,
-                default: data.mounts[MOUNT_PATH_CWL_INPUT].content[it.id],
-                value: data.mounts[MOUNT_PATH_CWL_INPUT].content[it.id.split('/').pop()] || [],
+                default: content[it.id],
+                value: content[it.id.split('/').pop()] || [],
                 doc: it.doc
             }
         )
     ) : [];
 };
 
                 doc: it.doc
             }
         )
     ) : [];
 };
 
+/*
+ * Fetches raw outputs from containerRequest properties
+ */
+export const getRawOutputs = (data: any): CommandInputParameter[] | undefined => {
+    if (!data || !data.properties || !data.properties.cwl_output) { return undefined; }
+    return (data.properties.cwl_output);
+}
+
 export type InputCollectionMount = {
     path: string;
     pdh: string;
 export type InputCollectionMount = {
     path: string;
     pdh: string;
index 84a9ca6d4c53cd6b345bae17b40e820dce186b5a..937478ef09994d64a8b87f1d1eb73b881e64372d 100644 (file)
@@ -256,23 +256,25 @@ export const ProcessIOCard = withStyles(styles)(connect(null, mapDispatchToProps
             <CardContent className={classes.content}>
                 {mainProcess ?
                     (<>
             <CardContent className={classes.content}>
                 {mainProcess ?
                     (<>
-                        {params === undefined && <Grid container item alignItems='center' justify='center'>
+                        {/* raw is undefined until params are loaded */}
+                        {raw === undefined && <Grid container item alignItems='center' justify='center'>
                             <CircularProgress />
                         </Grid>}
                             <CircularProgress />
                         </Grid>}
-                        {params && params.length > 0 &&
+                        {raw && Object.keys(raw).length > 0 &&
                             <>
                                 <Tabs value={mainProcTabState} onChange={handleMainProcTabChange} variant="fullWidth" className={classes.symmetricTabs}>
                             <>
                                 <Tabs value={mainProcTabState} onChange={handleMainProcTabChange} variant="fullWidth" className={classes.symmetricTabs}>
-                                    <Tab label="Parameters" />
+                                    {/* params will be empty on processes without workflow definitions in mounts, so we only show raw */}
+                                    {(params && params.length) && <Tab label="Parameters" />}
                                     <Tab label="JSON" />
                                 </Tabs>
                                     <Tab label="JSON" />
                                 </Tabs>
-                                {mainProcTabState === 0 && <div className={classes.tableWrapper}>
+                                {(mainProcTabState === 0 && params && params.length > 0) && <div className={classes.tableWrapper}>
                                         <ProcessIOPreview data={params} showImagePreview={showImagePreview} />
                                     </div>}
                                         <ProcessIOPreview data={params} showImagePreview={showImagePreview} />
                                     </div>}
-                                {mainProcTabState === 1 && <div className={classes.tableWrapper}>
-                                        <ProcessIORaw data={raw || params} />
+                                {(mainProcTabState === 1 || !params || !(params.length > 0)) && <div className={classes.tableWrapper}>
+                                        <ProcessIORaw data={raw} />
                                     </div>}
                             </>}
                                     </div>}
                             </>}
-                        {params && params.length === 0 && <Grid container item alignItems='center' justify='center'>
+                        {raw && Object.keys(raw).length === 0 && <Grid container item alignItems='center' justify='center'>
                             <DefaultView messages={["No parameters found"]} />
                         </Grid>}
                     </>) :
                             <DefaultView messages={["No parameters found"]} />
                         </Grid>}
                     </>) :
index a08d8aecec0e925ae860f3ba03fe43adcf1ef46f..248c52158facf9c7076f6321b3a5168fa01abad3 100644 (file)
@@ -22,6 +22,7 @@ import { CommandInputParameter, getIOParamId } from 'models/workflow';
 import { CommandOutputParameter } from 'cwlts/mappings/v1.0/CommandOutputParameter';
 import { AuthState } from 'store/auth/auth-reducer';
 import { ProcessCmdCard } from './process-cmd-card';
 import { CommandOutputParameter } from 'cwlts/mappings/v1.0/CommandOutputParameter';
 import { AuthState } from 'store/auth/auth-reducer';
 import { ProcessCmdCard } from './process-cmd-card';
+import { ContainerRequestResource } from 'models/container-request';
 
 type CssRules = 'root';
 
 
 type CssRules = 'root';
 
@@ -46,7 +47,7 @@ export interface ProcessPanelRootActionProps {
     onLogFilterChange: (filter: FilterOption) => void;
     navigateToLog: (uuid: string) => void;
     onCopyToClipboard: (uuid: string) => void;
     onLogFilterChange: (filter: FilterOption) => void;
     navigateToLog: (uuid: string) => void;
     onCopyToClipboard: (uuid: string) => void;
-    fetchOutputs: (uuid: string, fetchOutputs) => void;
+    fetchOutputs: (containerRequest: ContainerRequestResource, fetchOutputs) => void;
 }
 
 export type ProcessPanelRootProps = ProcessPanelRootDataProps & ProcessPanelRootActionProps & WithStyles<CssRules>;
 }
 
 export type ProcessPanelRootProps = ProcessPanelRootDataProps & ProcessPanelRootActionProps & WithStyles<CssRules>;
@@ -77,6 +78,8 @@ export const ProcessPanelRoot = withStyles(styles)(
     const outputUuid = process?.containerRequest.outputUuid;
     const requestUuid = process?.containerRequest.uuid;
 
     const outputUuid = process?.containerRequest.outputUuid;
     const requestUuid = process?.containerRequest.uuid;
 
+    const containerRequest = process?.containerRequest;
+
     const inputMounts = getInputCollectionMounts(process?.containerRequest);
 
     // Resets state when changing processes
     const inputMounts = getInputCollectionMounts(process?.containerRequest);
 
     // Resets state when changing processes
@@ -87,28 +90,32 @@ export const ProcessPanelRoot = withStyles(styles)(
         setProcessedInputs(undefined);
     }, [requestUuid]);
 
         setProcessedInputs(undefined);
     }, [requestUuid]);
 
+    // Fetch raw output (async for fetching from keep)
     React.useEffect(() => {
     React.useEffect(() => {
-        if (outputUuid) {
-            fetchOutputs(outputUuid, setOutputs);
+        if (containerRequest) {
+            fetchOutputs(containerRequest, setOutputs);
         }
         }
-    }, [outputUuid, fetchOutputs]);
+    }, [containerRequest, fetchOutputs]);
 
 
+    // Format raw output into ProcessIOParameter[] when it changes
     React.useEffect(() => {
     React.useEffect(() => {
-        if (outputDetails !== undefined && outputDetails.rawOutputs && process) {
-            const outputDefinitions = getOutputParameters(process.containerRequest);
+        if (outputDetails !== undefined && outputDetails.rawOutputs && containerRequest) {
+            const outputDefinitions = getOutputParameters(containerRequest);
             setProcessedOutputs(formatOutputData(outputDefinitions, outputDetails.rawOutputs, outputDetails.pdh, auth));
         }
             setProcessedOutputs(formatOutputData(outputDefinitions, outputDetails.rawOutputs, outputDetails.pdh, auth));
         }
-    }, [outputDetails, auth, process]);
+    }, [outputDetails, auth, containerRequest]);
 
 
+    // Fetch raw inputs and format into ProcessIOParameter[]
+    //   Can be sync because inputs are either already in containerRequest mounts or props
     React.useEffect(() => {
     React.useEffect(() => {
-        if (process) {
-            const rawInputs = getRawInputs(process.containerRequest);
+        if (containerRequest) {
+            const rawInputs = getRawInputs(containerRequest);
             setInputs(rawInputs);
 
             setInputs(rawInputs);
 
-            const inputs = getInputs(process.containerRequest);
+            const inputs = getInputs(containerRequest);
             setProcessedInputs(formatInputData(inputs, auth));
         }
             setProcessedInputs(formatInputData(inputs, auth));
         }
-    }, [requestUuid, auth, process]);
+    }, [requestUuid, auth, containerRequest]);
 
     return process
         ? <MPVContainer className={props.classes.root} spacing={8} panelStates={panelsData}  justify-content="flex-start" direction="column" wrap="nowrap">
 
     return process
         ? <MPVContainer className={props.classes.root} spacing={8} panelStates={panelsData}  justify-content="flex-start" direction="column" wrap="nowrap">
index 8adec3bd9f78cdce8558f5759f56ad54b37a46ee..75e934ab62bc7e5036cc00c74538b2638da33b63 100644 (file)
@@ -54,7 +54,7 @@ const mapDispatchToProps = (dispatch: Dispatch): ProcessPanelRootActionProps =>
     cancelProcess: (uuid) => dispatch<any>(cancelRunningWorkflow(uuid)),
     onLogFilterChange: (filter) => dispatch(setProcessLogsPanelFilter(filter.value)),
     navigateToLog: (uuid) => dispatch<any>(navigateToLogCollection(uuid)),
     cancelProcess: (uuid) => dispatch<any>(cancelRunningWorkflow(uuid)),
     onLogFilterChange: (filter) => dispatch(setProcessLogsPanelFilter(filter.value)),
     navigateToLog: (uuid) => dispatch<any>(navigateToLogCollection(uuid)),
-    fetchOutputs: (uuid, setOutputs) => dispatch<any>(loadOutputs(uuid, setOutputs)),
+    fetchOutputs: (containerRequest, setOutputs) => dispatch<any>(loadOutputs(containerRequest, setOutputs)),
 });
 
 const getFilters = (processPanel: ProcessPanelState, processes: Process[]) => {
 });
 
 const getFilters = (processPanel: ProcessPanelState, processes: Process[]) => {