improved execution framework
This commit is contained in:
parent
e8151b580f
commit
bb6427afaa
|
@ -4,22 +4,21 @@ class CCPExecutionForm extends HTMLElement{
|
||||||
#rootdoc;
|
#rootdoc;
|
||||||
#data;
|
#data;
|
||||||
#method;
|
#method;
|
||||||
#infrastructurecontroller;
|
#executionmonitor;
|
||||||
|
|
||||||
#serviceurl = "https://nubis1.int.d4science.net:8080"
|
#serviceurl = "https://nubis1.int.d4science.net:8080"
|
||||||
//#cdnurl = "https://nubis1.int.d4science.net:8080/ccp/executionformfragment.html"
|
#cdnurl = "https://nubis1.int.d4science.net:8080/ccp/executionformfragment.html"
|
||||||
#cdnurl = "http://d4science-cdn-public:8984/resources/ccp/executionformfragment.html"
|
//#cdnurl = "http://d4science-cdn-public:8984/resources/ccp/executionformfragment.html"
|
||||||
|
|
||||||
constructor(){
|
constructor(){
|
||||||
super()
|
super()
|
||||||
this.#boot = document.querySelector("d4s-boot-2")
|
this.#boot = document.querySelector("d4s-boot-2")
|
||||||
this.#rootdoc = this.attachShadow({ "mode" : "open"})
|
this.#rootdoc = this.attachShadow({ "mode" : "open"})
|
||||||
this.#infrastructurecontroller = document.querySelector("d4s-ccp-infrastructurelist")
|
|
||||||
this.fetchMarkup()
|
this.fetchMarkup()
|
||||||
}
|
}
|
||||||
|
|
||||||
static get observedAttributes() {
|
static get observedAttributes() {
|
||||||
return ["method"];
|
return ["method"];
|
||||||
}
|
}
|
||||||
|
|
||||||
attributeChangedCallback(name, oldValue, newValue) {
|
attributeChangedCallback(name, oldValue, newValue) {
|
||||||
|
@ -52,6 +51,16 @@ class CCPExecutionForm extends HTMLElement{
|
||||||
}
|
}
|
||||||
).then(data=>{
|
).then(data=>{
|
||||||
this.#data = 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()
|
this.showMethod()
|
||||||
}).catch(err=>alert(err))
|
}).catch(err=>alert(err))
|
||||||
}
|
}
|
||||||
|
@ -70,14 +79,49 @@ class CCPExecutionForm extends HTMLElement{
|
||||||
|
|
||||||
sendExecutionRequest(){
|
sendExecutionRequest(){
|
||||||
const url = this.#serviceurl + "/processes/" + this.#method + "/execution"
|
const url = this.#serviceurl + "/processes/" + this.#method + "/execution"
|
||||||
|
const req = this.buildRequest()
|
||||||
this.#boot.secureFetch(
|
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=>{
|
).then(reply=>{
|
||||||
if(reply.status !== 200 && reply.status !== 201){
|
if(reply.status !== 200 && reply.status !== 201){
|
||||||
throw "Error while requesting resource"
|
throw "Error while requesting resource"
|
||||||
}
|
}
|
||||||
console.log("Execution reuqest sent")
|
return reply.json()
|
||||||
}).catch(err => alert("Unable to call execute"))
|
}).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 = {
|
#empty_executionform_bss = {
|
||||||
|
@ -153,29 +197,13 @@ class CCPExecutionForm extends HTMLElement{
|
||||||
BSS.apply(this.#executionform_bss)
|
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",
|
target: "#execute_method_button",
|
||||||
on_click : ev=>{
|
on_click : ev=>{
|
||||||
ev.preventDefault()
|
ev.preventDefault()
|
||||||
ev.stopPropagation()
|
ev.stopPropagation()
|
||||||
this.sendExecutionRequest()
|
if(this.#data.executable) this.sendExecutionRequest();
|
||||||
|
else alert("This method has no compatible runtimes available")
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -185,3 +213,35 @@ class CCPExecutionForm extends HTMLElement{
|
||||||
}
|
}
|
||||||
|
|
||||||
window.customElements.define('d4s-ccp-executionform', CCPExecutionForm);
|
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{
|
class CCPInfrastructureList extends HTMLElement{
|
||||||
|
|
||||||
#boot;
|
#boot;
|
||||||
#infrastructures;
|
|
||||||
#socket;
|
#socket;
|
||||||
#data = {};
|
#data = {};
|
||||||
|
|
||||||
|
@ -23,29 +22,29 @@ class CCPInfrastructureList extends HTMLElement{
|
||||||
|
|
||||||
fetchInfrastructures(){
|
fetchInfrastructures(){
|
||||||
console.log("Calling fetch infrastructures")
|
console.log("Calling fetch infrastructures")
|
||||||
this.#boot.secureFetch(this.#serviceurl + "/infrastructures").
|
this.#boot.secureFetch(this.#serviceurl + "/infrastructures/cache").
|
||||||
then(resp=>{
|
then(resp=>{
|
||||||
console.log("Received resp for infrastructures ", resp.status)
|
|
||||||
return resp.json()
|
return resp.json()
|
||||||
}).then(data=>{
|
}).then(data=>{
|
||||||
this.#infrastructures = data
|
this.#data = data
|
||||||
this.updateList()
|
/*this.updateList()*/
|
||||||
}).catch(err=>{
|
}).catch(err=>{
|
||||||
alert("Error while downloading methods: ", err)
|
alert("Error while downloading methods: ", err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
updateList(resp){
|
/* updateList(resp){
|
||||||
this.connectBroadcast()
|
this.connectBroadcast()
|
||||||
this.#infrastructures.forEach(i=>{
|
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(
|
this.#boot.secureFetch(this.#serviceurl + `/infrastructures/${i.id}/status`).then(
|
||||||
()=>console.log("Requested async status update for infrastructure ", i.id)
|
()=>console.log("Requested async status update for infrastructure ", i.id)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
connectBroadcast(){
|
/* connectBroadcast(){
|
||||||
this.#socket = new WebSocket(this.#broadcasturl + "/infrastructures");
|
this.#socket = new WebSocket(this.#broadcasturl + "/infrastructures");
|
||||||
this.#socket.onmessage = event=>{
|
this.#socket.onmessage = event=>{
|
||||||
const data = JSON.parse(event.data)
|
const data = JSON.parse(event.data)
|
||||||
|
@ -59,11 +58,10 @@ class CCPInfrastructureList extends HTMLElement{
|
||||||
}
|
}
|
||||||
window.setInterval( ()=>this.#socket.send("ping"), 30000)
|
window.setInterval( ()=>this.#socket.send("ping"), 30000)
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
getCompatibleRuntimes(rts){
|
getCompatibleRuntimes(rts){
|
||||||
console.log("Request for ", rts)
|
|
||||||
const available = Object.keys(this.#data).reduce((acc, i) => {
|
const available = Object.keys(this.#data).reduce((acc, i) => {
|
||||||
console.log(this.#data[i].runtimes)
|
|
||||||
const compatiblerts = this.#data[i].runtimes.
|
const compatiblerts = this.#data[i].runtimes.
|
||||||
filter(r=>rts.indexOf(r["descriptor-id"]) >= 0).
|
filter(r=>rts.indexOf(r["descriptor-id"]) >= 0).
|
||||||
map(cr=>{ return { runtime : cr, infrastructure : this.#data[i] } })
|
map(cr=>{ return { runtime : cr, infrastructure : this.#data[i] } })
|
||||||
|
|
|
@ -2,7 +2,6 @@ class CCPInputWidgetController extends HTMLElement {
|
||||||
|
|
||||||
#input = null;
|
#input = null;
|
||||||
#renderer = null;
|
#renderer = null;
|
||||||
#rootdoc = null;
|
|
||||||
|
|
||||||
constructor(){
|
constructor(){
|
||||||
super()
|
super()
|
||||||
|
@ -19,6 +18,14 @@ class CCPInputWidgetController extends HTMLElement {
|
||||||
render(){
|
render(){
|
||||||
return this.#renderer.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);
|
window.customElements.define('d4s-ccp-input', CCPInputWidgetController);
|
||||||
|
|
||||||
|
@ -88,7 +95,11 @@ class SimpleInputRenderer extends Renderer{
|
||||||
constructor(input){
|
constructor(input){
|
||||||
super(input)
|
super(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getValue(parent){
|
||||||
|
return parent.querySelector("input").value
|
||||||
|
}
|
||||||
|
|
||||||
render(){
|
render(){
|
||||||
let required = this.required ? 'required="required"' : ""
|
let required = this.required ? 'required="required"' : ""
|
||||||
let readonly = this.readOnly ? 'readonly="readOnly"' : ""
|
let readonly = this.readOnly ? 'readonly="readOnly"' : ""
|
||||||
|
@ -113,6 +124,10 @@ class DateTimeInputRenderer extends Renderer{
|
||||||
super(input)
|
super(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getValue(parent){
|
||||||
|
return parent.querySelector("input").value
|
||||||
|
}
|
||||||
|
|
||||||
render(){
|
render(){
|
||||||
let required = this.required ? 'required="required"' : ""
|
let required = this.required ? 'required="required"' : ""
|
||||||
let readonly = this.schema.readOnly ? 'readonly="readOnly"' : ""
|
let readonly = this.schema.readOnly ? 'readonly="readOnly"' : ""
|
||||||
|
@ -138,6 +153,10 @@ class EnumInputRenderer extends Renderer{
|
||||||
super(input)
|
super(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getValue(parent){
|
||||||
|
return parent.querySelector("select").value
|
||||||
|
}
|
||||||
|
|
||||||
render(){
|
render(){
|
||||||
let options = this.schema.enum.map(e => {
|
let options = this.schema.enum.map(e => {
|
||||||
return e === this.schema.default ?
|
return e === this.schema.default ?
|
||||||
|
@ -170,6 +189,10 @@ class CodeInputRenderer extends Renderer{
|
||||||
super(input)
|
super(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getValue(parent){
|
||||||
|
return parent.querySelector("textarea").textContent
|
||||||
|
}
|
||||||
|
|
||||||
connectedCallback(controller){
|
connectedCallback(controller){
|
||||||
/*const ta = controller.querySelector("textarea")
|
/*const ta = controller.querySelector("textarea")
|
||||||
const opts = {
|
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