From 2c4352c7c5182fcbb5cf616ee9f9acc40a534a91 Mon Sep 17 00:00:00 2001 From: dcore94 Date: Thu, 26 Jan 2023 15:03:47 +0100 Subject: [PATCH] added execution history controller --- ccp/js/executionhistorycontroller.js | 261 +++++++++++++++++++++++++++ 1 file changed, 261 insertions(+) create mode 100644 ccp/js/executionhistorycontroller.js diff --git a/ccp/js/executionhistorycontroller.js b/ccp/js/executionhistorycontroller.js new file mode 100644 index 0000000..534bc0d --- /dev/null +++ b/ccp/js/executionhistorycontroller.js @@ -0,0 +1,261 @@ +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);