From 952aa1c7170d4b24951f40ed66b5f19a53d11daf Mon Sep 17 00:00:00 2001 From: Michal Klobukowski Date: Fri, 28 Sep 2018 01:29:10 +0200 Subject: [PATCH] Add workflow definition parser Feature #13863 Arvados-DCO-1.1-Signed-off-by: Michal Klobukowski --- src/index.tsx | 1 - src/models/workflow.ts | 99 ++++++++++++++++++- .../workflow-panel/workflow-panel-actions.ts | 17 +++- .../workflow-description-card.tsx | 30 ++++-- .../workflow-panel/workflow-panel-view.tsx | 19 ++-- src/views/workflow-panel/workflow-panel.tsx | 18 ++-- 6 files changed, 159 insertions(+), 25 deletions(-) diff --git a/src/index.tsx b/src/index.tsx index d64798bca5..2f8e73c4a1 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -113,7 +113,6 @@ const initListener = (history: History, store: RootStore, services: ServiceRepos initWebSocket(config, services.authService, store); await store.dispatch(loadWorkbench()); addRouteChangeHandlers(history, store); - // createSampleProcess(services); } }; }; diff --git a/src/models/workflow.ts b/src/models/workflow.ts index d644b79090..923a9cbd31 100644 --- a/src/models/workflow.ts +++ b/src/models/workflow.ts @@ -3,10 +3,107 @@ // SPDX-License-Identifier: AGPL-3.0 import { Resource, ResourceKind } from "./resource"; +import { safeLoad } from 'js-yaml'; export interface WorkflowResource extends Resource { kind: ResourceKind.WORKFLOW; name: string; description: string; definition: string; -} \ No newline at end of file +} +export interface WorkflowResoruceDefinition { + cwlVersion: string; + $graph: Array; +} +export interface Workflow { + class: 'Workflow'; + doc?: string; + id?: string; + inputs: CommandInputParameter[]; + outputs: any[]; + steps: any[]; +} + +export interface CommandLineTool { + class: 'CommandLineTool'; + id: string; + inputs: CommandInputParameter[]; + outputs: any[]; +} + +export interface CommandInputParameter { + id: string; + label?: string; + doc?: string | string[]; + default?: any; + type?: CWLType | CWLType[] | CommandInputEnumSchema | CommandInputArraySchema; +} + +export enum CWLType { + NULL = 'null', + BOOLEAN = 'boolean', + INT = 'int', + LONG = 'long', + FLOAT = 'float', + DOUBLE = 'double', + STRING = 'string', + FILE = 'File', + DIRECTORY = 'Directory', +} + +export interface CommandInputEnumSchema { + symbols: string[]; + type: 'enum'; + label?: string; + name?: string; +} + +export interface CommandInputArraySchema { + items: CWLType; + type: 'array'; + label?: string; +} + +export interface File { + class: CWLType.FILE; + location?: string; + path?: string; + basename?: string; +} + +export interface Directory { + class: CWLType.DIRECTORY; + location?: string; + path?: string; + basename?: string; +} + +export const parseWorkflowDefinition = (workflow: WorkflowResource): WorkflowResoruceDefinition => { + const definition = safeLoad(workflow.definition); + return definition; +}; + +export const getWorkflowInputs = (workflowDefinition: WorkflowResoruceDefinition) => { + const mainWorkflow = workflowDefinition.$graph.find(item => item.class === 'Workflow' && item.id === '#main'); + return mainWorkflow + ? mainWorkflow.inputs + : undefined; +}; + +export const stringifyInputType = ({ type }: CommandInputParameter) => { + if (typeof type === 'string') { + return type; + } else if (type instanceof Array) { + return type.join(' | '); + } else if (typeof type === 'object') { + if (type.type === 'enum') { + return 'enum'; + } else if (type.type === 'array') { + return `${type.items}[]`; + } else { + return 'unknown'; + } + } else { + return 'unknown'; + } +}; diff --git a/src/store/workflow-panel/workflow-panel-actions.ts b/src/store/workflow-panel/workflow-panel-actions.ts index aa79347cc6..ca72e5a51a 100644 --- a/src/store/workflow-panel/workflow-panel-actions.ts +++ b/src/store/workflow-panel/workflow-panel-actions.ts @@ -7,10 +7,13 @@ import { RootState } from '~/store/store'; import { ServiceRepository } from '~/services/services'; import { bindDataExplorerActions } from '~/store/data-explorer/data-explorer-action'; import { propertiesActions } from '~/store/properties/properties-actions'; +import { getResource } from '../resources/resources'; +import { getProperty } from '~/store/properties/properties'; +import { WorkflowResource } from '../../models/workflow'; export const WORKFLOW_PANEL_ID = "workflowPanel"; const UUID_PREFIX_PROPERTY_NAME = 'uuidPrefix'; - +const WORKFLOW_PANEL_DETAILS_UUID = 'workflowPanelDetailsUuid'; export const workflowPanelActions = bindDataExplorerActions(WORKFLOW_PANEL_ID); export const loadWorkflowPanel = () => @@ -21,6 +24,14 @@ export const loadWorkflowPanel = () => export const setUuidPrefix = (uuidPrefix: string) => propertiesActions.SET_PROPERTY({ key: UUID_PREFIX_PROPERTY_NAME, value: uuidPrefix }); -export const getUuidPrefix = (state: RootState) =>{ +export const getUuidPrefix = (state: RootState) => { return state.properties.uuidPrefix; -}; \ No newline at end of file +}; + +export const showWorkflowDetails = (uuid: string) => + propertiesActions.SET_PROPERTY({ key: WORKFLOW_PANEL_DETAILS_UUID, value: uuid }); + +export const getWorkflowDetails = (state: RootState) => { + const uuid = getProperty(WORKFLOW_PANEL_DETAILS_UUID)(state.properties); + return uuid ? getResource(uuid)(state.resources) : undefined; +}; diff --git a/src/views/workflow-panel/workflow-description-card.tsx b/src/views/workflow-panel/workflow-description-card.tsx index 60e17b6017..57cf89f7ea 100644 --- a/src/views/workflow-panel/workflow-description-card.tsx +++ b/src/views/workflow-panel/workflow-description-card.tsx @@ -7,7 +7,8 @@ import { StyleRulesCallback, WithStyles, withStyles, CardContent, Tab, Tabs, Pap import { ArvadosTheme } from '~/common/custom-theme'; import { WorkflowIcon } from '~/components/icon/icon'; import { DataTableDefaultView } from '~/components/data-table-default-view/data-table-default-view'; -import { WorkflowResource } from '~/models/workflow'; +import { WorkflowResource, parseWorkflowDefinition, getWorkflowInputs, stringifyInputType } from '~/models/workflow'; +import { DetailsAttribute } from '~/components/details-attribute/details-attribute'; export type CssRules = 'root' | 'tab'; @@ -37,7 +38,7 @@ export const WorkflowDetailsCard = withStyles(styles)( } render() { - const { classes } = this.props; + const { classes, workflow } = this.props; const { value } = this.state; return @@ -45,14 +46,29 @@ export const WorkflowDetailsCard = withStyles(styles)( {value === 0 && - Description - + {workflow + ? workflow.description + : } } {value === 1 && - Inputs + {workflow && this.inputs + ? this.inputs.map(input => ) + : } } ; } + + get inputs() { + if (this.props.workflow) { + const definition = parseWorkflowDefinition(this.props.workflow); + if (definition) { + return getWorkflowInputs(definition); + } + } + return; + } }); \ No newline at end of file diff --git a/src/views/workflow-panel/workflow-panel-view.tsx b/src/views/workflow-panel/workflow-panel-view.tsx index 8a29cb7f16..c48f46ad8c 100644 --- a/src/views/workflow-panel/workflow-panel-view.tsx +++ b/src/views/workflow-panel/workflow-panel-view.tsx @@ -6,7 +6,7 @@ import * as React from 'react'; import { DataExplorer } from "~/views-components/data-explorer/data-explorer"; import { WorkflowIcon } from '~/components/icon/icon'; import { DataTableDefaultView } from '~/components/data-table-default-view/data-table-default-view'; -import { WORKFLOW_PANEL_ID } from '~/store/workflow-panel/workflow-panel-actions'; +import { WORKFLOW_PANEL_ID, workflowPanelActions } from '~/store/workflow-panel/workflow-panel-actions'; import { ResourceLastModifiedDate, RosurceWorkflowName, @@ -18,6 +18,7 @@ import { DataColumns } from '~/components/data-table/data-table'; import { DataTableFilterItem } from '~/components/data-table-filters/data-table-filters'; import { Grid } from '@material-ui/core'; import { WorkflowDetailsCard } from './workflow-description-card'; +import { WorkflowResource } from '../../models/workflow'; export enum WorkflowPanelColumnNames { NAME = "Name", @@ -30,11 +31,17 @@ export interface WorkflowPanelFilter extends DataTableFilterItem { type: ResourceStatus; } -interface WorkflowPanelDataProps { - handleRowDoubleClick: any; - handleRowClick: any; +export interface WorkflowPanelDataProps { + workflow?: WorkflowResource; } +export interface WorfklowPanelActionProps { + handleRowDoubleClick: (workflowUuid: string) => void; + handleRowClick: (workflowUuid: string) => void; +} + +export type WorkflowPanelProps = WorkflowPanelDataProps & WorfklowPanelActionProps; + export enum ResourceStatus { PUBLIC = "Public", PRIVATE = "Private", @@ -103,7 +110,7 @@ export const workflowPanelColumns: DataColumns = [ } ]; -export const WorkflowPanelView = ({...props}) => { +export const WorkflowPanelView = (props: WorkflowPanelProps) => { return { dataTableDefaultView={} /> - + ; }; \ No newline at end of file diff --git a/src/views/workflow-panel/workflow-panel.tsx b/src/views/workflow-panel/workflow-panel.tsx index 279097d8e2..0dbf918e2f 100644 --- a/src/views/workflow-panel/workflow-panel.tsx +++ b/src/views/workflow-panel/workflow-panel.tsx @@ -2,23 +2,27 @@ // // SPDX-License-Identifier: AGPL-3.0 -import * as React from "react"; import { Dispatch } from "redux"; import { connect } from "react-redux"; import { navigateTo } from '~/store/navigation/navigation-action'; -import { loadDetailsPanel } from '~/store/details-panel/details-panel-action'; import { WorkflowPanelView } from '~/views/workflow-panel/workflow-panel-view'; +import { WorfklowPanelActionProps, WorkflowPanelDataProps } from './workflow-panel-view'; +import { showWorkflowDetails, getWorkflowDetails } from '../../store/workflow-panel/workflow-panel-actions'; +import { RootState } from '~/store/store'; -const mapDispatchToProps = (dispatch: Dispatch) => ({ +const mapStateToProps = (state: RootState): WorkflowPanelDataProps => ({ + workflow: getWorkflowDetails(state) +}); + +const mapDispatchToProps = (dispatch: Dispatch): WorfklowPanelActionProps => ({ handleRowDoubleClick: (uuid: string) => { dispatch(navigateTo(uuid)); }, - + handleRowClick: (uuid: string) => { - dispatch(loadDetailsPanel(uuid)); + dispatch(showWorkflowDetails(uuid)); } }); -export const WorkflowPanel= connect(undefined, mapDispatchToProps)( - (props) => ); \ No newline at end of file +export const WorkflowPanel = connect(mapStateToProps, mapDispatchToProps)(WorkflowPanelView); \ No newline at end of file -- 2.39.5