Merge commit '3b735dd9330e0989f51a76771c3303031154154e' into 21158-wf-page-list
[arvados.git] / services / workbench2 / src / models / workflow.ts
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import { Resource, ResourceKind } from "./resource";
6 import { safeLoad } from 'js-yaml';
7 import { CommandOutputParameter } from "cwlts/mappings/v1.0/CommandOutputParameter";
8
9 export interface WorkflowResource extends Resource {
10     kind: ResourceKind.WORKFLOW;
11     name: string;
12     description: string;
13     definition: string;
14 }
15 export interface WorkflowResourceDefinition {
16     cwlVersion: string;
17     $graph?: Array<Workflow | CommandLineTool>;
18 }
19 export interface Workflow {
20     class: 'Workflow';
21     doc?: string;
22     id?: string;
23     inputs: CommandInputParameter[];
24     outputs: any[];
25     steps: any[];
26     hints?: ProcessRequirement[];
27 }
28
29 export interface CommandLineTool {
30     class: 'CommandLineTool';
31     id: string;
32     inputs: CommandInputParameter[];
33     outputs: any[];
34     hints?: ProcessRequirement[];
35 }
36
37 export type ProcessRequirement = GenericProcessRequirement | WorkflowRunnerResources;
38
39 export interface GenericProcessRequirement {
40     class: string;
41 }
42
43 export interface WorkflowRunnerResources {
44     class: 'http://arvados.org/cwl#WorkflowRunnerResources';
45     ramMin?: number;
46     coresMin?: number;
47     keep_cache?: number;
48     acrContainerImage?: string;
49 }
50
51 export type CommandInputParameter =
52     BooleanCommandInputParameter |
53     IntCommandInputParameter |
54     LongCommandInputParameter |
55     FloatCommandInputParameter |
56     DoubleCommandInputParameter |
57     StringCommandInputParameter |
58     FileCommandInputParameter |
59     DirectoryCommandInputParameter |
60     StringArrayCommandInputParameter |
61     IntArrayCommandInputParameter |
62     FloatArrayCommandInputParameter |
63     FileArrayCommandInputParameter |
64     DirectoryArrayCommandInputParameter |
65     EnumCommandInputParameter;
66
67 export enum CWLType {
68     NULL = 'null',
69     BOOLEAN = 'boolean',
70     INT = 'int',
71     LONG = 'long',
72     FLOAT = 'float',
73     DOUBLE = 'double',
74     STRING = 'string',
75     FILE = 'File',
76     DIRECTORY = 'Directory',
77 }
78
79 export interface CommandInputEnumSchema {
80     symbols: string[];
81     type: 'enum';
82     label?: string;
83     name?: string;
84 }
85
86 export interface CommandInputArraySchema<ItemType> {
87     items: ItemType;
88     type: 'array';
89     label?: string;
90 }
91
92 export interface File {
93     class: CWLType.FILE;
94     location?: string;
95     path?: string;
96     basename?: string;
97 }
98
99 export interface Directory {
100     class: CWLType.DIRECTORY;
101     location?: string;
102     path?: string;
103     basename?: string;
104 }
105
106 export interface GenericCommandInputParameter<Type, Value> {
107     id: string;
108     label?: string;
109     doc?: string | string[];
110     default?: Value;
111     type?: Type | Array<Type | CWLType.NULL>;
112     value?: Value;
113     disabled?: boolean;
114 }
115 export type GenericArrayCommandInputParameter<Type, Value> = GenericCommandInputParameter<CommandInputArraySchema<Type>, Value[]>;
116
117 export type BooleanCommandInputParameter = GenericCommandInputParameter<CWLType.BOOLEAN, boolean>;
118 export type IntCommandInputParameter = GenericCommandInputParameter<CWLType.INT, number>;
119 export type LongCommandInputParameter = GenericCommandInputParameter<CWLType.LONG, number>;
120 export type FloatCommandInputParameter = GenericCommandInputParameter<CWLType.FLOAT, number>;
121 export type DoubleCommandInputParameter = GenericCommandInputParameter<CWLType.DOUBLE, number>;
122 export type StringCommandInputParameter = GenericCommandInputParameter<CWLType.STRING, string>;
123 export type FileCommandInputParameter = GenericCommandInputParameter<CWLType.FILE, File>;
124 export type DirectoryCommandInputParameter = GenericCommandInputParameter<CWLType.DIRECTORY, Directory>;
125 export type EnumCommandInputParameter = GenericCommandInputParameter<CommandInputEnumSchema, string>;
126
127 export type StringArrayCommandInputParameter = GenericArrayCommandInputParameter<CWLType.STRING, string>;
128 export type IntArrayCommandInputParameter = GenericArrayCommandInputParameter<CWLType.INT, string>;
129 export type FloatArrayCommandInputParameter = GenericArrayCommandInputParameter<CWLType.FLOAT, string>;
130 export type FileArrayCommandInputParameter = GenericArrayCommandInputParameter<CWLType.FILE, File>;
131 export type DirectoryArrayCommandInputParameter = GenericArrayCommandInputParameter<CWLType.DIRECTORY, Directory>;
132
133 export type WorkflowInputsData = {
134     [key: string]: boolean | number | string | File | Directory;
135 };
136 export const parseWorkflowDefinition = (workflow: WorkflowResource): WorkflowResourceDefinition => {
137     const definition = safeLoad(workflow.definition);
138     return definition;
139 };
140
141 export const getWorkflow = (workflowDefinition: WorkflowResourceDefinition) => {
142     if (!workflowDefinition.$graph) { return undefined; }
143     const mainWorkflow = workflowDefinition.$graph.find(item => item.id === '#main');
144     return mainWorkflow
145         ? mainWorkflow
146         : undefined;
147 };
148
149 export const getWorkflowInputs = (workflowDefinition: WorkflowResourceDefinition) => {
150     if (!workflowDefinition) { return undefined; }
151     return getWorkflow(workflowDefinition)
152         ? getWorkflow(workflowDefinition)!.inputs
153         : undefined;
154 };
155
156 export const getWorkflowOutputs = (workflowDefinition: WorkflowResourceDefinition) => {
157     if (!workflowDefinition) { return undefined; }
158     return getWorkflow(workflowDefinition)
159         ? getWorkflow(workflowDefinition)!.outputs
160         : undefined;
161 };
162
163 export const getInputLabel = (input: CommandInputParameter) => {
164     return `${input.label || input.id.split('/').pop()}`;
165 };
166
167 export const getIOParamId = (input: CommandInputParameter | CommandOutputParameter) => {
168     return `${input.id.split('/').pop()}`;
169 };
170
171 export const isRequiredInput = ({ type }: CommandInputParameter) => {
172     if (type instanceof Array) {
173         for (const t of type) {
174             if (t === CWLType.NULL) {
175                 return false;
176             }
177         }
178     }
179     return true;
180 };
181
182 export const isPrimitiveOfType = (input: GenericCommandInputParameter<any, any>, type: CWLType) =>
183     input.type instanceof Array
184         ? input.type.indexOf(type) > -1
185         : input.type === type;
186
187 export const isArrayOfType = (input: GenericCommandInputParameter<any, any>, type: CWLType) =>
188     input.type instanceof Array
189         ? (input.type.filter(t => typeof t === 'object' &&
190             t.type === 'array' &&
191             t.items === type).length > 0)
192         : (typeof input.type === 'object' &&
193             input.type.type === 'array' &&
194             input.type.items === type);
195
196 export const getEnumType = (input: GenericCommandInputParameter<any, any>) => {
197     if (input.type instanceof Array) {
198         const f = input.type.filter(t => typeof t === 'object' &&
199             !(t instanceof Array) &&
200             t.type === 'enum');
201         if (f.length > 0) {
202             return f[0];
203         }
204     } else {
205         if ((typeof input.type === 'object' &&
206             !(input.type instanceof Array) &&
207             input.type.type === 'enum')) {
208             return input.type;
209         }
210     }
211     return null;
212 };
213
214 export const stringifyInputType = ({ type }: CommandInputParameter) => {
215     if (typeof type === 'string') {
216         return type;
217     } else if (type instanceof Array) {
218         return type.join(' | ');
219     } else if (typeof type === 'object') {
220         if (type.type === 'enum') {
221             return 'enum';
222         } else if (type.type === 'array') {
223             return `${type.items}[]`;
224         } else {
225             return 'unknown';
226         }
227     } else {
228         return 'unknown';
229     }
230 };