|
|
@@ -1,10 +1,12 @@ |
|
|
|
import { h, Component, createRef } from 'preact';
|
|
|
|
import { route } from 'preact-router';
|
|
|
|
import WBNavbarCommon from 'wb-navbar-common';
|
|
|
|
import WBArvadosCrumbs from 'wb-arvados-crumbs';
|
|
|
|
import WBBrowseDialog from 'wb-browse-dialog';
|
|
|
|
import WBTable from 'wb-table';
|
|
|
|
import WBNameAndUuid from 'wb-name-and-uuid';
|
|
|
|
import makeArvadosRequest from 'make-arvados-request';
|
|
|
|
import { wbDisableControls, wbEnableControls } from 'wb-disable-controls';
|
|
|
|
import linkState from 'linkstate';
|
|
|
|
|
|
|
|
function parseDefinition(text) {
|
|
|
@@ -21,6 +23,41 @@ function encodeURIComponentIncludingDots(s) { |
|
|
|
return encodeURIComponent(s).replace('.', '%2E');
|
|
|
|
}
|
|
|
|
|
|
|
|
function inputSpecInfo(inputSpec) {
|
|
|
|
const isFile = (inputSpec.type === 'File' || inputSpec.type === 'File[]' ||
|
|
|
|
(inputSpec.type.type === 'array' && inputSpec.type.items === 'File'));
|
|
|
|
|
|
|
|
const isDirectory = (inputSpec.type === 'Directory' || inputSpec.type === 'Directory[]' ||
|
|
|
|
(inputSpec.type.type === 'array' && inputSpec.type.items === 'Directory'));
|
|
|
|
|
|
|
|
const isArray = (inputSpec.type === 'File[]' || inputSpec.type === 'Directory[]' ||
|
|
|
|
inputSpec.type.type === 'array');
|
|
|
|
|
|
|
|
return { isFile, isDirectory, isArray };
|
|
|
|
}
|
|
|
|
|
|
|
|
function uuidsToCwl(obj, isFile) {
|
|
|
|
if (obj instanceof Array) {
|
|
|
|
const res = {};
|
|
|
|
for (let k in obj) {
|
|
|
|
res[k] = uuidsToCwl(obj[k], isFile);
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (typeof(obj) === 'string' &&
|
|
|
|
(/^[0-9a-z]{5}-[0-9a-z]{5}-[0-9a-z]{15}/.exec(obj) ||
|
|
|
|
/^[0-9a-f]{32}\+[0-9]+/.exec(obj))) {
|
|
|
|
|
|
|
|
return {
|
|
|
|
'class': (isFile ? 'File' : 'Directory'),
|
|
|
|
'location': 'keep:' + obj
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
throw Error('Expected Arvados path or array of paths');
|
|
|
|
}
|
|
|
|
|
|
|
|
class WBPathDisplay extends Component {
|
|
|
|
fetchData() {
|
|
|
|
const { app, path } = this.props;
|
|
|
@@ -67,6 +104,7 @@ class WBLaunchWorkflowPage extends Component { |
|
|
|
super(...args);
|
|
|
|
this.browseDialogRef = createRef();
|
|
|
|
this.state.inputs = {};
|
|
|
|
this.state.errors = [];
|
|
|
|
}
|
|
|
|
|
|
|
|
componentDidMount() {
|
|
|
@@ -86,14 +124,7 @@ class WBLaunchWorkflowPage extends Component { |
|
|
|
renderInput(inputSpec) {
|
|
|
|
const { app } = this.props;
|
|
|
|
|
|
|
|
const isFile = (inputSpec.type === 'File' || inputSpec.type === 'File[]' ||
|
|
|
|
(inputSpec.type.type === 'array' && inputSpec.type.items === 'File'));
|
|
|
|
|
|
|
|
const isDirectory = (inputSpec.type === 'Directory' || inputSpec.type === 'Directory[]' ||
|
|
|
|
(inputSpec.type.type === 'array' && inputSpec.type.items === 'Directory'));
|
|
|
|
|
|
|
|
const isArray = true; // (inputSpec.type === 'File[]' || inputSpec.type === 'Directory[]' ||
|
|
|
|
// inputSpec.type.type === 'array');
|
|
|
|
const { isFile, isDirectory, isArray } = inputSpecInfo(inputSpec);
|
|
|
|
|
|
|
|
if (!isFile && !isDirectory)
|
|
|
|
return (
|
|
|
@@ -153,9 +184,97 @@ class WBLaunchWorkflowPage extends Component { |
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
submit() {
|
|
|
|
// first see if all inputs are parseable
|
|
|
|
const inputs = {};
|
|
|
|
const errors = [];
|
|
|
|
|
|
|
|
const { workflowDefinition } = this.state;
|
|
|
|
const main = workflowDefinition['$graph'].find(a => (a.id === '#main'));
|
|
|
|
|
|
|
|
for (let k in this.state.inputs) {
|
|
|
|
try {
|
|
|
|
let val = jsyaml.safeLoad(this.state.inputs[k]);
|
|
|
|
const { isFile } = inputSpecInfo(main.inputs.find(a => (a.id === k)));
|
|
|
|
val = uuidsToCwl(val, isFile);
|
|
|
|
k = k.split('/').slice(1).join('/');
|
|
|
|
inputs[k] = val;
|
|
|
|
} catch (exc) {
|
|
|
|
errors.push('Error parsing ' + k + ': ' + exc.message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (errors.length > 0) {
|
|
|
|
this.setState({ errors });
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// prepare a request
|
|
|
|
const { app, workflowUuid } = this.props;
|
|
|
|
const { processName, processDescription,
|
|
|
|
defaultProcessName, defaultProcessDescription,
|
|
|
|
projectUuid } = this.state;
|
|
|
|
const { arvHost, arvToken, currentUser } = app.state;
|
|
|
|
const req = {
|
|
|
|
name: processName || defaultProcessName,
|
|
|
|
description: processDescription || defaultProcessDescription,
|
|
|
|
owner_uuid: projectUuid || currentUser.uuid,
|
|
|
|
container_image: 'arvados/jobs',
|
|
|
|
properties: {
|
|
|
|
template_uuid: workflowUuid
|
|
|
|
},
|
|
|
|
runtime_constraints: {
|
|
|
|
API: true,
|
|
|
|
vcpus: 1,
|
|
|
|
ram: 1073741824
|
|
|
|
},
|
|
|
|
cwd: '/var/spool/cwl',
|
|
|
|
command: [
|
|
|
|
'arvados-cwl-runner',
|
|
|
|
'--local',
|
|
|
|
'--api=containers',
|
|
|
|
'--project-uuid=' + (projectUuid || currentUser.uuid),
|
|
|
|
'--collection-cache-size=256',
|
|
|
|
'/var/lib/cwl/workflow.json#main',
|
|
|
|
'/var/lib/cwl/cwl.input.json'],
|
|
|
|
output_path: '/var/spool/cwl',
|
|
|
|
priority: 1,
|
|
|
|
state: 'Committed',
|
|
|
|
mounts: {
|
|
|
|
'stdout': {
|
|
|
|
kind: 'file',
|
|
|
|
path: '/var/spool/cwl/cwl.output.json'
|
|
|
|
},
|
|
|
|
'/var/spool/cwl': {
|
|
|
|
kind: 'collection',
|
|
|
|
writable: true
|
|
|
|
},
|
|
|
|
'/var/lib/cwl/workflow.json': {
|
|
|
|
kind: 'json',
|
|
|
|
content: workflowDefinition
|
|
|
|
},
|
|
|
|
'/var/lib/cwl/cwl.input.json': {
|
|
|
|
kind: 'json',
|
|
|
|
content: inputs
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
wbDisableControls();
|
|
|
|
let prom = makeArvadosRequest(arvHost, arvToken,
|
|
|
|
'/arvados/v1/container_requests',
|
|
|
|
{ method: 'POST', data: JSON.stringify(req) });
|
|
|
|
prom = prom.then(xhr => {
|
|
|
|
wbEnableControls();
|
|
|
|
route('/process/' + xhr.response.uuid);
|
|
|
|
});
|
|
|
|
|
|
|
|
// throw Error('Not implemented');
|
|
|
|
}
|
|
|
|
|
|
|
|
render({ app, workflowUuid },
|
|
|
|
{ workflow, workflowDefinition, projectUuid, processName, processDescription,
|
|
|
|
defaultProcessName, defaultProcessDescription }) {
|
|
|
|
defaultProcessName, defaultProcessDescription, errors }) {
|
|
|
|
|
|
|
|
return (
|
|
|
|
<div>
|
|
|
@@ -221,6 +340,16 @@ class WBLaunchWorkflowPage extends Component { |
|
|
|
Submit
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
{ errors.length > 0 ? (
|
|
|
|
<div class="form-group">
|
|
|
|
{ errors.map(err => (
|
|
|
|
<div class="alert alert-danger" role="alert">
|
|
|
|
{ err }
|
|
|
|
</div>
|
|
|
|
))}
|
|
|
|
</div>
|
|
|
|
) : null }
|
|
|
|
</form>) : <div>Loading...</div> }
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|