class CCPExecutionHistory extends HTMLElement {
#boot = null;
#rootdoc = null;
#serviceurl = null;
#broadcasturl = null;
#executions = [];
#socket = null;
constructor(){
super()
this.#boot = document.querySelector("d4s-boot-2")
this.#rootdoc = this.attachShadow({ "mode" : "open"})
this.#serviceurl = this.getAttribute("serviceurl")
this.#broadcasturl = this.getAttribute("broadcasturl")
if(!this.#broadcasturl){
this.#broadcasturl = this.#serviceurl.replace(/^http/, "ws")
}
this.#broadcasturl = this.#broadcasturl + "/ws/notification"
this.connectNewExecution()
this.connectBroadcast()
}
connectedCallback(){
this.#rootdoc.innerHTML = this.render()
this.refreshExecutions()
}
render(){
return `
-
:
`
}
refreshExecution(id){
this.#boot.secureFetch(`${this.#serviceurl}/executions?id=${id}`).then(reply =>{
if(reply.status === 200) return reply.json();
else throw `Unable to load execution ${id}`
}).then(data=>{
const exec = data[0]
for(var i=0; i < this.#executions.length; i++){
if(this.#executions[i].id == exec.id){
this.#executions[i] = exec
break
}
if(i === this.#executions.length){
this.#executions = data.concat(this.#executions)
}
}
BSS.apply(this.execution_list_bss, this.#rootdoc)
}).catch(err=>{ console.error(err)})
}
deleteExecution(id){
this.#boot.secureFetch(`${this.#serviceurl}/executions/${id}`, { method: "DELETE"}).then(reply =>{
if(reply.status === 200) this.refreshExecutions();
else throw `Unable to delete execution ${id}`
}).catch(err=>{ console.error(err)})
}
refreshExecutions(){
this.#boot.secureFetch(`${this.#serviceurl}/executions`).then(reply =>{
if(reply.status === 200) return reply.json();
else throw "Unable to load executions"
}).then(data=>{
this.#executions = data
BSS.apply(this.execution_list_bss, this.#rootdoc)
}).catch(err=>{ console.error(err)})
}
connectNewExecution(){
document.addEventListener("newexecution", ev=>{
this.#executions = [ {id: ev.detail } ].concat(this.#executions)
})
}
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.id === data.jobID)[0]
if(exec){
this.refreshExecution(exec.id)
}
}
window.setInterval( ()=>this.#socket.send("ping"), 30000)
}
download(url, name) {
this.#boot.secureFetch(url).then(reply => {
if (reply.status !== 200) {
throw "Unable to download"
}
return reply.blob()
}).then(blob => {
const objectURL = URL.createObjectURL(blob)
var tmplnk = document.createElement("a")
tmplnk.download = name
tmplnk.href = objectURL
//tmplnk.target="_blank"
document.body.appendChild(tmplnk)
tmplnk.click()
document.body.removeChild(tmplnk)
}).catch(err => console.error(err))
}
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,i)=>e.setAttribute("data-index", i),
on_click: ev=>{
if(ev.target.getAttribute("name") === "delete"){
if(window.confirm("Confirm deletion of this execution?")){
const index = Number(ev.target.getAttribute("data-index"))
this.deleteExecution(this.#executions[index].id)
}
}
},
recurse : [
{
target : "details",
apply : (e,d)=>{
e.alt = e.title = d.id
if(sessionStorage.getItem(d.id) === "open") e.open = "open";
else e.removeAttribute("open");
},
on_toggle : ev=>{
if(ev.target.open){
sessionStorage.setItem(ev.currentTarget.alt, 'open')
}else{
sessionStorage.removeItem(ev.currentTarget.alt)
}
}
},
{
target : "span[name=runtime]",
apply : (e,d)=>{
e.classList.add(d.infrastructuretype)
e.title = e.alt = `${d.runtime} (image: ${d.runtimeimage}) on ${d.infrastructure}`
e.textContent = d.runtime
}
},
{
target : "h5",
apply : (e,d)=>{
e.textContent = `${d.method} v. ${d.methodversion}`
}
},
{
target : "span[name=updated]",
apply : (e,d)=>{
const dt = new Date(d.updated)
e.textContent = `Last update ${dt.toLocaleDateString()} @ ${dt.toLocaleTimeString()}`
}
},
{
target : "span[name=status]",
apply : (e,d)=>{
if(d.status){
const status = d.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 : "span[name=message]",
apply : (e,d)=>{
if(d.message){
e.textContent = d.message
}
}
},
{
target : "ul",
recurse : [
{
target : "li",
"in" : (e,d)=>{
return d.resources.map(l=>{
return { href : this.#serviceurl + "/executions/" + d.id + "/" + l.path, path : l.path}
})
},
on_click : ev=>{
const href = ev.currentTarget.bss_input.data.href
const name = ev.currentTarget.bss_input.data.path
this.download(href, name)
},
apply : (e,d)=>{
e.innerHTML = `${d.path}`
}
}
]
}
]
}
]
}
}
window.customElements.define('d4s-ccp-executionhistory', CCPExecutionHistory);