// 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 { 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 loadOutputs = (uuid: string, setOutputs) =>
+export const loadOutputs = (containerRequest: ContainerRequestResource, setOutputs) =>
async (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
+ if (!containerRequest.outputUuid) {setOutputs({}); return;};
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 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 {
- 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({});
}
};
+/*
+ * Fetches raw inputs from containerRequest mounts with fallback to properties
+ */
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 []; }
+ 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) => (
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
}
)
) : [];
};
+/*
+ * 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;
<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>}
- {params && params.length > 0 &&
+ {raw && Object.keys(raw).length > 0 &&
<>
<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>
- {mainProcTabState === 0 && <div className={classes.tableWrapper}>
+ {(mainProcTabState === 0 && params && params.length > 0) && <div className={classes.tableWrapper}>
<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>}
</>}
- {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>}
</>) :
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';
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>;
const outputUuid = process?.containerRequest.outputUuid;
const requestUuid = process?.containerRequest.uuid;
+ const containerRequest = process?.containerRequest;
+
const inputMounts = getInputCollectionMounts(process?.containerRequest);
// Resets state when changing processes
setProcessedInputs(undefined);
}, [requestUuid]);
+ // Fetch raw output (async for fetching from keep)
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(() => {
- 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));
}
- }, [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(() => {
- if (process) {
- const rawInputs = getRawInputs(process.containerRequest);
+ if (containerRequest) {
+ const rawInputs = getRawInputs(containerRequest);
setInputs(rawInputs);
- const inputs = getInputs(process.containerRequest);
+ const inputs = getInputs(containerRequest);
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">
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[]) => {