diff --git a/frontend/src/js/arvados/process/wb-submit-container-request.js b/frontend/src/js/arvados/process/wb-submit-container-request.js new file mode 100644 index 0000000..060b2c1 --- /dev/null +++ b/frontend/src/js/arvados/process/wb-submit-container-request.js @@ -0,0 +1,87 @@ +import makeArvadosRequest from 'make-arvados-request'; +import wbUuidsToCwl from 'wb-uuids-to-cwl'; + +function wbParseWorkflowInputs(workflowDefinition, userInputs, errors) { + // first see if all inputs are parseable + const inputs = {}; + + const main = workflowDefinition['$graph'].find(a => (a.id === '#main')); + + for (let k in userInputs) { + try { + let val = jsyaml.safeLoad(userInputs[k]); + val = wbUuidsToCwl(val); + k = k.split('/').slice(1).join('/'); + inputs[k] = (val === undefined ? null : val); + } catch (exc) { + errors.push('Error parsing ' + k + ': ' + exc.message); + } + } + + return inputs; +} + +// params: +// arvHost, arvToken, inputs, +// projectUuid, workflowDefinition, workflowUuid +// processName, processDescription +function wbSubmitContainerRequest(params) { + + const { workflowDefinition, workflowUuid, + processName, processDescription, inputs, + arvHost, arvToken, projectUuid } = params; + + // prepare a request + const req = { + name: processName, + description: processDescription, + owner_uuid: projectUuid, + 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, + '--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 + } + } + }; + + const prom = makeArvadosRequest(arvHost, arvToken, + '/arvados/v1/container_requests', + { method: 'POST', data: JSON.stringify(req) }); + + return prom; +} + +export { wbParseWorkflowInputs, wbSubmitContainerRequest }; diff --git a/frontend/src/js/arvados/process/wb-uuids-to-cwl.js b/frontend/src/js/arvados/process/wb-uuids-to-cwl.js index 6c0a3ff..cb1df7c 100644 --- a/frontend/src/js/arvados/process/wb-uuids-to-cwl.js +++ b/frontend/src/js/arvados/process/wb-uuids-to-cwl.js @@ -2,7 +2,7 @@ function wbUuidsToCwl(obj) { if (obj instanceof Array) { const res = []; for (let k in obj) { - res[k] = uuidsToCwl(obj[k]); + res[k] = wbUuidsToCwl(obj[k]); } return res; } diff --git a/frontend/src/js/component/wb-workflow-input.js b/frontend/src/js/component/wb-workflow-input.js new file mode 100644 index 0000000..473a611 --- /dev/null +++ b/frontend/src/js/component/wb-workflow-input.js @@ -0,0 +1,71 @@ +import { h, Component } from 'preact'; +import wbInputSpecInfo from 'wb-input-spec-info'; +import WBPathDisplay from 'wb-path-display'; +import { parseKeepRef } from 'wb-process-misc'; + +class WBWorkflowInput extends Component { + render({ app, inputSpec, inputsDict, browseDialogRef }) { + const { isFile, isDirectory, isArray } = wbInputSpecInfo(inputSpec); + + if (!isFile && !isDirectory) + return ( +
+ (inputsDict[inputSpec.id] = e.target.value) }> +
{ inputSpec.doc }
+
+ ); + + const button = ( + + ); + + let value = inputsDict[inputSpec.id]; + if (value) { + try { + value = jsyaml.load(value); + } catch (_) {} + } + + return ( +
+
+ (inputsDict[inputSpec.id] = e.target.value) }> +
+ { button } +
+
+
{ inputSpec.doc }
+ { value ? + isArray ? ( + + ) : ( + + ) : null } +
+ ); + } +} + +export default WBWorkflowInput; diff --git a/frontend/src/js/page/wb-launch-workflow-page.js b/frontend/src/js/page/wb-launch-workflow-page.js index cda3671..603ad5b 100644 --- a/frontend/src/js/page/wb-launch-workflow-page.js +++ b/frontend/src/js/page/wb-launch-workflow-page.js @@ -4,15 +4,12 @@ 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'; import wbParseWorkflowDef from 'wb-parse-workflow-def'; -import wbInputSpecInfo from 'wb-input-spec-info'; -import wbUuidsToCwl from 'wb-uuids-to-cwl'; -import { encodeURIComponentIncludingDots, parseKeepRef } from 'wb-process-misc'; -import WBPathDisplay from 'wb-path-display'; +import { wbParseWorkflowInputs, wbSubmitContainerRequest } from 'wb-submit-container-request'; +import WBWorkflowInput from 'wb-workflow-input'; class WBLaunchWorkflowPage extends Component { constructor(...args) { @@ -43,156 +40,40 @@ class WBLaunchWorkflowPage extends Component { }); } - renderInput(inputSpec) { - const { app } = this.props; - - const { isFile, isDirectory, isArray } = wbInputSpecInfo(inputSpec); - - if (!isFile && !isDirectory) - return ( -
- (this.state.inputs[inputSpec.id] = e.target.value) }> -
{ inputSpec.doc }
-
- ); - - const button = ( - - ); - - let value = this.state.inputs[inputSpec.id]; - if (value) { - try { - value = jsyaml.load(value); - } catch (_) {} - } - - return ( -
-
- (this.state.inputs[inputSpec.id] = e.target.value) }> -
- { button } -
-
-
{ inputSpec.doc }
- { value ? - isArray ? ( - - ) : ( - - ) : null } -
- ); - } - 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]); - val = wbUuidsToCwl(val); - k = k.split('/').slice(1).join('/'); - inputs[k] = (val === undefined ? null : val); - } catch (exc) { - errors.push('Error parsing ' + k + ': ' + exc.message); - } - } + const { app, workflowUuid } = this.props; + const { arvHost, arvToken, currentUser } = app.state; + const { workflowDefinition, projectUuid, + processName, processDescription, + defaultProcessName, defaultProcessDescription } = this.state; + const errors = []; + const inputs = wbParseWorkflowInputs(workflowDefinition, + this.state.inputs, errors); 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 - } - } - }; + const params = { + arvHost, arvToken, inputs, + processName: processName || defaultProcessName, + processDescription: processDescription || defaultProcessDescription, + projectUuid: projectUuid || currentUser.uuid, + workflowUuid, workflowDefinition + } wbDisableControls(); - let prom = makeArvadosRequest(arvHost, arvToken, - '/arvados/v1/container_requests', - { method: 'POST', data: JSON.stringify(req) }); + let prom = wbSubmitContainerRequest(params); prom = prom.then(xhr => { wbEnableControls(); route('/process/' + xhr.response.uuid); }); - - // throw Error('Not implemented'); + prom = prom.catch(exc => { + wbEnableControls(); + this.setState({ errors: [ exc.message ] }); + }); } render({ app, workflowUuid }, @@ -254,16 +135,12 @@ class WBLaunchWorkflowPage extends Component { (a.id === '#main')).inputs.map(it => [ it.label || it.id, - this.renderInput(it) + ( ) ]) } /> -
- -
- { errors.length > 0 ? (
{ errors.map(err => ( @@ -273,6 +150,12 @@ class WBLaunchWorkflowPage extends Component { ))}
) : null } + +
+ +
) :
Loading...
} );