|  |  | @@ -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 ( | 
		
	
		
			
			|  |  |  | <div> | 
		
	
		
			
			|  |  |  | <input class="form-control w-100" type="text" placeholder={ inputSpec.label } | 
		
	
		
			
			|  |  |  | value={ this.state.inputs[inputSpec.id] } | 
		
	
		
			
			|  |  |  | onchange={ e => (this.state.inputs[inputSpec.id] = e.target.value) }></input> | 
		
	
		
			
			|  |  |  | <div class="mt-2 text-muted">{ inputSpec.doc }</div> | 
		
	
		
			
			|  |  |  | </div> | 
		
	
		
			
			|  |  |  | ); | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | const button = ( | 
		
	
		
			
			|  |  |  | <button class="btn btn-outline-primary" | 
		
	
		
			
			|  |  |  | onclick={ e => { | 
		
	
		
			
			|  |  |  | e.preventDefault(); | 
		
	
		
			
			|  |  |  | this.browseDialogRef.current.show( | 
		
	
		
			
			|  |  |  | [].concat(isFile ? 'file' : []).concat(isDirectory ? 'directory' : []), | 
		
	
		
			
			|  |  |  | isArray, | 
		
	
		
			
			|  |  |  | v => { | 
		
	
		
			
			|  |  |  | this.state.inputs[inputSpec.id] = JSON.stringify(v); | 
		
	
		
			
			|  |  |  | this.setState({}); | 
		
	
		
			
			|  |  |  | }); | 
		
	
		
			
			|  |  |  | } }> | 
		
	
		
			
			|  |  |  | Browse... | 
		
	
		
			
			|  |  |  | </button> | 
		
	
		
			
			|  |  |  | ); | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | let value = this.state.inputs[inputSpec.id]; | 
		
	
		
			
			|  |  |  | if (value) { | 
		
	
		
			
			|  |  |  | try { | 
		
	
		
			
			|  |  |  | value = jsyaml.load(value); | 
		
	
		
			
			|  |  |  | } catch (_) {} | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | return ( | 
		
	
		
			
			|  |  |  | <div> | 
		
	
		
			
			|  |  |  | <div class="input-group"> | 
		
	
		
			
			|  |  |  | <input class="form-control w-100" type="text" placeholder={ inputSpec.label } | 
		
	
		
			
			|  |  |  | value={ this.state.inputs[inputSpec.id] } | 
		
	
		
			
			|  |  |  | onchange={ e => (this.state.inputs[inputSpec.id] = e.target.value) }></input> | 
		
	
		
			
			|  |  |  | <div class="input-group-append"> | 
		
	
		
			
			|  |  |  | { button } | 
		
	
		
			
			|  |  |  | </div> | 
		
	
		
			
			|  |  |  | </div> | 
		
	
		
			
			|  |  |  | <div class="mt-2 text-muted">{ inputSpec.doc }</div> | 
		
	
		
			
			|  |  |  | { value ? | 
		
	
		
			
			|  |  |  | isArray ? ( | 
		
	
		
			
			|  |  |  | <ul class="mb-0"> | 
		
	
		
			
			|  |  |  | { value.map(path => ( | 
		
	
		
			
			|  |  |  | <li> | 
		
	
		
			
			|  |  |  | <WBPathDisplay app={ app } path={ parseKeepRef(path) } /> | 
		
	
		
			
			|  |  |  | </li> | 
		
	
		
			
			|  |  |  | )) } | 
		
	
		
			
			|  |  |  | </ul> | 
		
	
		
			
			|  |  |  | ) : ( | 
		
	
		
			
			|  |  |  | <WBPathDisplay app={ app } path={ parseKeepRef(value) } /> | 
		
	
		
			
			|  |  |  | ) : null } | 
		
	
		
			
			|  |  |  | </div> | 
		
	
		
			
			|  |  |  | ); | 
		
	
		
			
			|  |  |  | } | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | 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 { | 
		
	
		
			
			|  |  |  | <WBTable columns={ [ 'Name', 'Value'] } | 
		
	
		
			
			|  |  |  | rows={ workflowDefinition.$graph.find(a => (a.id === '#main')).inputs.map(it => [ | 
		
	
		
			
			|  |  |  | it.label || it.id, | 
		
	
		
			
			|  |  |  | this.renderInput(it) | 
		
	
		
			
			|  |  |  | ( <WBWorkflowInput app={ app } inputSpec={ it } | 
		
	
		
			
			|  |  |  | inputsDict={ this.state.inputs } | 
		
	
		
			
			|  |  |  | browseDialogRef={ this.browseDialogRef } /> ) | 
		
	
		
			
			|  |  |  | ]) } /> | 
		
	
		
			
			|  |  |  | </div> | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | <div class="form-group"> | 
		
	
		
			
			|  |  |  | <button class="btn btn-success" onclick={ e => { e.preventDefault(); this.submit(); } }> | 
		
	
		
			
			|  |  |  | Submit | 
		
	
		
			
			|  |  |  | </button> | 
		
	
		
			
			|  |  |  | </div> | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | { errors.length > 0 ? ( | 
		
	
		
			
			|  |  |  | <div class="form-group"> | 
		
	
		
			
			|  |  |  | { errors.map(err => ( | 
		
	
	
		
			
				|  |  | @@ -273,6 +150,12 @@ class WBLaunchWorkflowPage extends Component { | 
		
	
		
			
			|  |  |  | ))} | 
		
	
		
			
			|  |  |  | </div> | 
		
	
		
			
			|  |  |  | ) : null } | 
		
	
		
			
			|  |  |  |  | 
		
	
		
			
			|  |  |  | <div class="form-group"> | 
		
	
		
			
			|  |  |  | <button class="btn btn-success" onclick={ e => { e.preventDefault(); this.submit(); } }> | 
		
	
		
			
			|  |  |  | Submit | 
		
	
		
			
			|  |  |  | </button> | 
		
	
		
			
			|  |  |  | </div> | 
		
	
		
			
			|  |  |  | </form>) : <div>Loading...</div> } | 
		
	
		
			
			|  |  |  | </div> | 
		
	
		
			
			|  |  |  | ); | 
		
	
	
		
			
				|  |  | 
 |