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);