improved execution framework
This commit is contained in:
parent
e8151b580f
commit
bb6427afaa
|
@ -4,22 +4,21 @@ class CCPExecutionForm extends HTMLElement{
|
|||
#rootdoc;
|
||||
#data;
|
||||
#method;
|
||||
#infrastructurecontroller;
|
||||
#executionmonitor;
|
||||
|
||||
#serviceurl = "https://nubis1.int.d4science.net:8080"
|
||||
//#cdnurl = "https://nubis1.int.d4science.net:8080/ccp/executionformfragment.html"
|
||||
#cdnurl = "http://d4science-cdn-public:8984/resources/ccp/executionformfragment.html"
|
||||
#cdnurl = "https://nubis1.int.d4science.net:8080/ccp/executionformfragment.html"
|
||||
//#cdnurl = "http://d4science-cdn-public:8984/resources/ccp/executionformfragment.html"
|
||||
|
||||
constructor(){
|
||||
super()
|
||||
this.#boot = document.querySelector("d4s-boot-2")
|
||||
this.#rootdoc = this.attachShadow({ "mode" : "open"})
|
||||
this.#infrastructurecontroller = document.querySelector("d4s-ccp-infrastructurelist")
|
||||
this.fetchMarkup()
|
||||
}
|
||||
|
||||
static get observedAttributes() {
|
||||
return ["method"];
|
||||
return ["method"];
|
||||
}
|
||||
|
||||
attributeChangedCallback(name, oldValue, newValue) {
|
||||
|
@ -52,6 +51,16 @@ class CCPExecutionForm extends HTMLElement{
|
|||
}
|
||||
).then(data=>{
|
||||
this.#data = data
|
||||
const rts =
|
||||
this.#data.links
|
||||
.filter(l => l.rel === "compatibleWith")
|
||||
.map(l=>l.href.replace("runtimes/",""))
|
||||
.join(" ")
|
||||
return this.#boot.secureFetch(this.#serviceurl + "/infrastructures/runtimes?runtimes=" + rts)
|
||||
|
||||
}).then(resp=>{
|
||||
this.#data.executable = resp.status === 200
|
||||
}).then(()=>{
|
||||
this.showMethod()
|
||||
}).catch(err=>alert(err))
|
||||
}
|
||||
|
@ -70,14 +79,49 @@ class CCPExecutionForm extends HTMLElement{
|
|||
|
||||
sendExecutionRequest(){
|
||||
const url = this.#serviceurl + "/processes/" + this.#method + "/execution"
|
||||
const req = this.buildRequest()
|
||||
this.#boot.secureFetch(
|
||||
url, { method : "POST", body : JSON.stringify({}), headers : { "Content-Type" : "application/json"}}
|
||||
url, { method : "POST", body : JSON.stringify(req), headers : { "Content-Type" : "application/json"}}
|
||||
).then(reply=>{
|
||||
if(reply.status !== 200 && reply.status !== 201){
|
||||
throw "Error while requesting resource"
|
||||
}
|
||||
console.log("Execution reuqest sent")
|
||||
}).catch(err => alert("Unable to call execute"))
|
||||
return reply.json()
|
||||
}).then(data=>{
|
||||
if(data.status !== "accepted"){
|
||||
throw "Execution has not been accepted by server"
|
||||
}
|
||||
this.#executionmonitor = document.querySelector("d4s-ccp-executionmonitor")
|
||||
if(this.#executionmonitor){
|
||||
this.#executionmonitor.addExecution( { self : data.links[0].href, events : [data], jobID : data.jobID, method : this.#data.title})
|
||||
}
|
||||
}).catch(err => alert("Unable to call execute: " + err))
|
||||
}
|
||||
|
||||
buildRequest(){
|
||||
let request = { inputs : {}, outputs : {}, response : "raw"}
|
||||
|
||||
//fill inputs
|
||||
const inputs = this.getInputs()
|
||||
inputs.forEach(i=>{
|
||||
request.inputs[i.name] = i.value
|
||||
})
|
||||
|
||||
//fill outputs
|
||||
const outputs = this.getOutputs()
|
||||
outputs.forEach(o=>{
|
||||
if(o.enabled) request.outputs[o.name] = { transmissionMode : "value" };
|
||||
})
|
||||
|
||||
return request
|
||||
}
|
||||
|
||||
getInputs(){
|
||||
return Array.prototype.slice.call(this.#rootdoc.querySelectorAll("d4s-ccp-input"))
|
||||
}
|
||||
|
||||
getOutputs(){
|
||||
return Array.prototype.slice.call(this.#rootdoc.querySelectorAll("d4s-ccp-output"))
|
||||
}
|
||||
|
||||
#empty_executionform_bss = {
|
||||
|
@ -153,29 +197,13 @@ class CCPExecutionForm extends HTMLElement{
|
|||
BSS.apply(this.#executionform_bss)
|
||||
}
|
||||
},
|
||||
{
|
||||
target: "div.ccp-runtimes select",
|
||||
in : (e,d)=>d,
|
||||
recurse : [
|
||||
{
|
||||
"in" : (e,d)=>{
|
||||
const rts = d.links.filter(l=> l.rel === "compatibleWith").map(l=>l.href.replace("runtimes/",""))
|
||||
return this.#infrastructurecontroller.getCompatibleRuntimes(rts)
|
||||
},
|
||||
target : "option",
|
||||
apply : (e,d)=>{
|
||||
e.value = d.runtime["descriptor-id"]
|
||||
e.textContent = `${d.runtime.name} [${d.infrastructure.name}]`
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
target: "#execute_method_button",
|
||||
on_click : ev=>{
|
||||
ev.preventDefault()
|
||||
ev.stopPropagation()
|
||||
this.sendExecutionRequest()
|
||||
if(this.#data.executable) this.sendExecutionRequest();
|
||||
else alert("This method has no compatible runtimes available")
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
@ -185,3 +213,35 @@ class CCPExecutionForm extends HTMLElement{
|
|||
}
|
||||
|
||||
window.customElements.define('d4s-ccp-executionform', CCPExecutionForm);
|
||||
|
||||
class CCPExecutionEnvironmentOption extends HTMLOptionElement{
|
||||
|
||||
#infrastructure;
|
||||
#runtime;
|
||||
|
||||
constructor(runtime, infrastructure){
|
||||
super(`${runtime.name} [${infrastructure.name}]`, runtime["descriptor-id"])
|
||||
this.#runtime = runtime
|
||||
this.#infrastructure = infrastructure
|
||||
this.textContent = `${runtime.name} [${infrastructure.name}]`
|
||||
this.value = this.runtimeId
|
||||
}
|
||||
|
||||
get infrastructureName(){
|
||||
return this.#infrastructure.name
|
||||
}
|
||||
|
||||
get infrastructureId(){
|
||||
return this.#infrastructure.id
|
||||
}
|
||||
|
||||
get runtimeName(){
|
||||
return this.#runtime.name
|
||||
}
|
||||
|
||||
get runtimeId(){
|
||||
return this.#runtime["descriptor-id"]
|
||||
}
|
||||
}
|
||||
|
||||
window.customElements.define('d4s-ccp-environment-option', CCPExecutionEnvironmentOption, {extends:'option'});
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
class CCPExecutionMonitor extends HTMLElement {
|
||||
|
||||
#boot = null;
|
||||
#rootdoc = null;
|
||||
#broadcasturl = "ws://nubis1.int.d4science.net:8989/ws/notification";
|
||||
#executions = [];
|
||||
#socket = null;
|
||||
|
||||
constructor(){
|
||||
super()
|
||||
this.#boot = document.querySelector("d4s-boot-2")
|
||||
this.#rootdoc = this.attachShadow({ "mode" : "open"})
|
||||
this.connectBroadcast()
|
||||
}
|
||||
|
||||
connectedCallback(){
|
||||
this.#rootdoc.innerHTML = this.render()
|
||||
}
|
||||
|
||||
render(){
|
||||
return `
|
||||
<template id="EXECUTIOM_LIST_TEMPLATE">
|
||||
<ol name="ccp_execution_list" class="ccp-execution-list" class="ccp-execution-list">
|
||||
<li class="ccp-execution-item">
|
||||
<details open>
|
||||
<summary>
|
||||
<h5 style="display:inline"></h5>
|
||||
<span name="status" class="badge"></span>
|
||||
</summary>
|
||||
<p name="message" class="status-message font-weight-light font-italic" style="margin-top:revert"></span>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item"></li>
|
||||
</ul>
|
||||
</details>
|
||||
</li>
|
||||
</ol>
|
||||
</template>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
|
||||
<h3>Execution monitor</h3>
|
||||
<ol name="ccp_execution_list" style="display:none"></ol>
|
||||
`
|
||||
}
|
||||
|
||||
addExecution(execution){
|
||||
this.#executions.push(execution)
|
||||
this.refreshExecution(execution)
|
||||
}
|
||||
|
||||
refreshExecution(execution){
|
||||
this.#boot.secureFetch(execution.self).then(reply =>{
|
||||
if(reply.status === 200) return reply.json();
|
||||
else throw "Unable to load job links" + reply.text
|
||||
}).then(data=>{
|
||||
execution.links = data
|
||||
BSS.apply(this.execution_list_bss, this.#rootdoc)
|
||||
}).catch(err=>{ console.error(err)})
|
||||
}
|
||||
|
||||
connectBroadcast(){
|
||||
this.#socket = new WebSocket(this.#broadcasturl + "/executions");
|
||||
this.#socket.onmessage = event=>{
|
||||
const data = JSON.parse(event.data)
|
||||
let exec = this.#executions.filter(e=>e.jobID === data.jobID)[0]
|
||||
if(exec){
|
||||
exec.events.push(data)
|
||||
if(!exec.self) exec.self = data.links[0].href;
|
||||
this.refreshExecution(exec)
|
||||
}
|
||||
}
|
||||
window.setInterval( ()=>this.#socket.send("ping"), 30000)
|
||||
}
|
||||
|
||||
execution_list_bss = {
|
||||
template : "#EXECUTIOM_LIST_TEMPLATE",
|
||||
target : "ol[name=ccp_execution_list]",
|
||||
in : ()=>{ return {executions : this.#executions} },
|
||||
recurse:[
|
||||
{
|
||||
target : "li",
|
||||
in : (e,d)=>d.executions,
|
||||
apply: (e,d)=>{
|
||||
if(d.events && d.events.length > 0){
|
||||
e.alt = e.title = (new Date(d.events[d.events.length - 1].updated)).toLocaleString()
|
||||
}
|
||||
},
|
||||
recurse : [
|
||||
{
|
||||
target : "h5",
|
||||
apply : (e,d)=>{
|
||||
e.textContent = d.method
|
||||
}
|
||||
},
|
||||
{
|
||||
target : "span[name=status]",
|
||||
apply : (e,d)=>{
|
||||
if(d.events && d.events.length > 0){
|
||||
const status = d.events[d.events.length - 1].status
|
||||
e.textContent = status
|
||||
if (status === "running") e.classList.add("badge-primary");
|
||||
else if (status === "successful") e.classList.add("badge-success");
|
||||
else if (status === "failure") e.classList.add("badge-danger");
|
||||
else e.classList.add("badge-secondary");
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
target : ".status-message",
|
||||
apply : (e,d)=>{
|
||||
if(d.events && d.events.length > 0){
|
||||
e.textContent = d.events[d.events.length - 1].message
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
target : "ul",
|
||||
recurse : [
|
||||
{
|
||||
target : "li",
|
||||
"in" : (e,d)=>{return d.links.map(l=>{ return { href : d.self + "/" + l.path, path : l.path} })},
|
||||
apply : (e,d)=>{
|
||||
console.log("applying", e, d)
|
||||
e.innerHTML = `<a href="${d.href}">${d.path}</a>`
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
window.customElements.define('d4s-ccp-executionmonitor', CCPExecutionMonitor);
|
|
@ -1,7 +1,6 @@
|
|||
class CCPInfrastructureList extends HTMLElement{
|
||||
|
||||
#boot;
|
||||
#infrastructures;
|
||||
#socket;
|
||||
#data = {};
|
||||
|
||||
|
@ -23,29 +22,29 @@ class CCPInfrastructureList extends HTMLElement{
|
|||
|
||||
fetchInfrastructures(){
|
||||
console.log("Calling fetch infrastructures")
|
||||
this.#boot.secureFetch(this.#serviceurl + "/infrastructures").
|
||||
this.#boot.secureFetch(this.#serviceurl + "/infrastructures/cache").
|
||||
then(resp=>{
|
||||
console.log("Received resp for infrastructures ", resp.status)
|
||||
return resp.json()
|
||||
}).then(data=>{
|
||||
this.#infrastructures = data
|
||||
this.updateList()
|
||||
this.#data = data
|
||||
/*this.updateList()*/
|
||||
}).catch(err=>{
|
||||
alert("Error while downloading methods: ", err)
|
||||
})
|
||||
}
|
||||
|
||||
updateList(resp){
|
||||
/* updateList(resp){
|
||||
this.connectBroadcast()
|
||||
this.#infrastructures.forEach(i=>{
|
||||
this.#data[i.id] = { status : "unknown", type : i.type, name : i.name, runtimes : [] }
|
||||
this.#data[i.id] = { id : i.id, status : "unknown", type : i.type, name : i.name, runtimes : [] }
|
||||
this.#boot.secureFetch(this.#serviceurl + `/infrastructures/${i.id}/status`).then(
|
||||
()=>console.log("Requested async status update for infrastructure ", i.id)
|
||||
)
|
||||
})
|
||||
}
|
||||
*/
|
||||
|
||||
connectBroadcast(){
|
||||
/* connectBroadcast(){
|
||||
this.#socket = new WebSocket(this.#broadcasturl + "/infrastructures");
|
||||
this.#socket.onmessage = event=>{
|
||||
const data = JSON.parse(event.data)
|
||||
|
@ -59,11 +58,10 @@ class CCPInfrastructureList extends HTMLElement{
|
|||
}
|
||||
window.setInterval( ()=>this.#socket.send("ping"), 30000)
|
||||
}
|
||||
*/
|
||||
|
||||
getCompatibleRuntimes(rts){
|
||||
console.log("Request for ", rts)
|
||||
const available = Object.keys(this.#data).reduce((acc, i) => {
|
||||
console.log(this.#data[i].runtimes)
|
||||
const compatiblerts = this.#data[i].runtimes.
|
||||
filter(r=>rts.indexOf(r["descriptor-id"]) >= 0).
|
||||
map(cr=>{ return { runtime : cr, infrastructure : this.#data[i] } })
|
||||
|
|
|
@ -2,7 +2,6 @@ class CCPInputWidgetController extends HTMLElement {
|
|||
|
||||
#input = null;
|
||||
#renderer = null;
|
||||
#rootdoc = null;
|
||||
|
||||
constructor(){
|
||||
super()
|
||||
|
@ -19,6 +18,14 @@ class CCPInputWidgetController extends HTMLElement {
|
|||
render(){
|
||||
return this.#renderer.render()
|
||||
}
|
||||
|
||||
get name(){
|
||||
return this.#renderer.name
|
||||
}
|
||||
|
||||
get value(){
|
||||
return this.#renderer.getValue(this)
|
||||
}
|
||||
}
|
||||
window.customElements.define('d4s-ccp-input', CCPInputWidgetController);
|
||||
|
||||
|
@ -88,7 +95,11 @@ class SimpleInputRenderer extends Renderer{
|
|||
constructor(input){
|
||||
super(input)
|
||||
}
|
||||
|
||||
|
||||
getValue(parent){
|
||||
return parent.querySelector("input").value
|
||||
}
|
||||
|
||||
render(){
|
||||
let required = this.required ? 'required="required"' : ""
|
||||
let readonly = this.readOnly ? 'readonly="readOnly"' : ""
|
||||
|
@ -113,6 +124,10 @@ class DateTimeInputRenderer extends Renderer{
|
|||
super(input)
|
||||
}
|
||||
|
||||
getValue(parent){
|
||||
return parent.querySelector("input").value
|
||||
}
|
||||
|
||||
render(){
|
||||
let required = this.required ? 'required="required"' : ""
|
||||
let readonly = this.schema.readOnly ? 'readonly="readOnly"' : ""
|
||||
|
@ -138,6 +153,10 @@ class EnumInputRenderer extends Renderer{
|
|||
super(input)
|
||||
}
|
||||
|
||||
getValue(parent){
|
||||
return parent.querySelector("select").value
|
||||
}
|
||||
|
||||
render(){
|
||||
let options = this.schema.enum.map(e => {
|
||||
return e === this.schema.default ?
|
||||
|
@ -170,6 +189,10 @@ class CodeInputRenderer extends Renderer{
|
|||
super(input)
|
||||
}
|
||||
|
||||
getValue(parent){
|
||||
return parent.querySelector("textarea").textContent
|
||||
}
|
||||
|
||||
connectedCallback(controller){
|
||||
/*const ta = controller.querySelector("textarea")
|
||||
const opts = {
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
class CCPOutputWidgetController extends HTMLElement {
|
||||
|
||||
#output = null;
|
||||
|
||||
constructor(){
|
||||
super()
|
||||
this.#output = JSON.parse(this.getAttribute("output"))
|
||||
}
|
||||
|
||||
connectedCallback(){
|
||||
this.innerHTML = this.render()
|
||||
}
|
||||
|
||||
get name(){
|
||||
return this.#output.id
|
||||
}
|
||||
|
||||
get enabled(){
|
||||
return this.querySelector("input").checked
|
||||
}
|
||||
|
||||
render(){
|
||||
return `
|
||||
<div class="col form-check">
|
||||
<input class="form-check-input" type="checkbox" name="this.#output.id" alt="${this.#output.description}" title="${this.#output.description}" checked="checked"/>
|
||||
<label class="form-check-label">${this.#output.title}</label>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
}
|
||||
window.customElements.define('d4s-ccp-output', CCPOutputWidgetController);
|
Loading…
Reference in New Issue