added execution history controller
This commit is contained in:
parent
90b8348d8c
commit
2c4352c7c5
|
@ -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 `
|
||||||
|
<style>
|
||||||
|
.noselect{
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
.lxd{
|
||||||
|
background-color: #dd4814;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
.docker{
|
||||||
|
color: white;
|
||||||
|
background-color: #2496ed;
|
||||||
|
}
|
||||||
|
.ccp-toolbar-button {
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 0.3rem !important;
|
||||||
|
line-height: .8rem !important;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.ccp-toolbar-button svg {
|
||||||
|
fill: white;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<template id="EXECUTIOM_LIST_TEMPLATE">
|
||||||
|
<ol name="ccp_execution_list" class="ccp-execution-list list-group">
|
||||||
|
<li class="ccp-execution-item list-group-item" style="border: none">
|
||||||
|
<details open>
|
||||||
|
<summary class="noselect">
|
||||||
|
<span name="runtime" class="badge"></span>
|
||||||
|
<h5 style="display:inline"></h5>
|
||||||
|
<span name="status" class="badge"></span>
|
||||||
|
<button data-index="0" name="delete" title="Delete" class="btn btn-danger ccp-toolbar-button">
|
||||||
|
<svg viewBox="0 0 24 24">
|
||||||
|
<path d="M19,4H15.5L14.5,3H9.5L8.5,4H5V6H19M6,19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V7H6V19Z"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</summary>
|
||||||
|
<p name="message" class="font-weight-light font-italic" style="margin-top:revert">
|
||||||
|
<span name="updated"></span>:
|
||||||
|
<span name="message" class="ml-1"></span>
|
||||||
|
</p>
|
||||||
|
<ul class="list-group">
|
||||||
|
<li class="list-group-item" style="border: none"></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">
|
||||||
|
<ol name="ccp_execution_list" style="display:none"></ol>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = `<a href="${d.href}" onclick="event.preventDefault()">${d.path}</a>`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.customElements.define('d4s-ccp-executionhistory', CCPExecutionHistory);
|
Loading…
Reference in New Issue