diff --git a/ccp/executionformfragment.html b/ccp/executionformfragment.html deleted file mode 100644 index 68fb8fe..0000000 --- a/ccp/executionformfragment.html +++ /dev/null @@ -1,59 +0,0 @@ -
- - - - - -
-
diff --git a/ccp/js/infrastructurelistcontroller.js b/ccp/js/infrastructurelistcontroller.js index 0aef439..6784086 100644 --- a/ccp/js/infrastructurelistcontroller.js +++ b/ccp/js/infrastructurelistcontroller.js @@ -50,7 +50,7 @@ class CCPInfrastructureList extends HTMLElement{ fetchInfrastructures(){ const prom1 = this.#boot.secureFetch(this.#serviceurl + "/infrastructures"). then(resp=>{ - if(resp.status !== 200) throw "Unable to fetch infrastructure cache"; + if(resp.status !== 200) throw "Unable to fetch infrastructures"; return resp.json() }).then(data=>{ this.#infrastructures = data @@ -152,7 +152,7 @@ class CCPInfrastructureList extends HTMLElement{ }else{ this.#socket.send("ping") } - }, 30000) + }, 30000) } } diff --git a/ccp/js/methodeditorcontroller.js b/ccp/js/methodeditorcontroller.js index d589e7a..da9edd2 100644 --- a/ccp/js/methodeditorcontroller.js +++ b/ccp/js/methodeditorcontroller.js @@ -4,7 +4,7 @@ class CCPMethodEditorController extends HTMLElement{ #rootdoc; #serviceurl; - #runtimes; + #infrastructures; #locked = false; #isupdate = false; @@ -55,44 +55,20 @@ class CCPMethodEditorController extends HTMLElement{ position: relative; } - .ccp-toolbar-header { - display: inline-flex; - align-items: center; - gap: 5px; - padding: .2rem; - } - - .ccp-toolbar-right { - position:absolute; - right: 1rem; - } - - .ccp-toolbar-button { - font-weight: bold; - padding:.3rem; - line-height:.8rem; - cursor: pointer; - } - - .ccp-toolbar-button svg { - fill: white; - width:24px; - height:24px; - pointer-events: none; - } - .ccp-option { background-color:#eeffff; color:#0099CC; border:solid 1px #0099CC; + display: inline-flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + gap: 1rem; + font-size: 1rem; } - .plexiglass{ - position: absolute; - width: 100%; - height: 100%; - background-color: rgba(0,0,0,.3); - z-index: 10; + .ccp-option > .btn { + padding: 0; } ul.author_list, ul.context_list{ @@ -162,30 +138,31 @@ class CCPMethodEditorController extends HTMLElement{ this.#rootdoc = this.attachShadow({ "mode" : "open"}) this.#serviceurl = this.getAttribute("serviceurl") this.initMethod() - this.fetchRuntimes() + this.fetchInfrastructures() + this.connectNewEditRequest() } connectedCallback(){ } - - isRuntimeRegistered(rt){ - return (this.#runtimes.filter(r=>r.id === rt.id)).length > 0 + + connectNewEditRequest(){ + document.addEventListener("neweditrequest", ev=>{ + this.cloneOrEditMethod(ev.detail) + }) } - fetchRuntimes(){ - this.#boot.secureFetch(this.#serviceurl + "/infrastructures/cache"). + isInfrastructureRegistered(infra){ + return (this.#infrastructures.filter(i=>i.id === infra.id)).length > 0 + } + + fetchInfrastructures(){ + this.#boot.secureFetch(this.#serviceurl + "/infrastructures"). then(resp=>{ - if(resp.status !== 200) throw "Unable to fetch runtimes"; + if(resp.status !== 200) throw "Unable to fetch infrastructures"; return resp.json() }).then(data=>{ - this.#runtimes = [] - //merge all runtimes - data.forEach( i => { - i.available_runtimes.forEach(r => { - if(!this.isRuntimeRegistered(r)){ this.#runtimes.push(r)} - }) - }) + this.#infrastructures = data this.render() }).catch(err=>{ alert(err) @@ -252,7 +229,22 @@ class CCPMethodEditorController extends HTMLElement{ this.#current = JSON.parse(JSON.stringify(this.#method_template)) this.#current.id = Math.abs((Math.random() * 10e11)|0) this.#current.metadata = [] - this.#tmp_inputs = [] + this.#tmp_inputs = [ + { + id : "ccpimage", + title : "Runtime", + description : "The image of the runtime to use for method execution. This depends on the infrastructure specific protocol for interacting with registries.", + minOccurs : 1, + maxOccurs : 1, + schema : { + type : "string", + format : "url", + contentMediaType : "text/plain", + default : "", + readonly : true, + } + } + ] this.#tmp_outputs = [] } @@ -344,6 +336,7 @@ class CCPMethodEditorController extends HTMLElement{ this.#rootdoc.querySelector(".plexiglass").classList.toggle("d-none") this.render() this.#locked = false + this.parentElement.scrollIntoViewIfNeeded() } render(){ @@ -411,13 +404,13 @@ class CCPMethodEditorController extends HTMLElement{ ${this.renderKeywords()} -
- - + ${this.renderInfrastructureOptions()} -
- ${this.renderRuntimes()} +
+ ${this.renderInfrastructures()}
@@ -458,7 +451,6 @@ class CCPMethodEditorController extends HTMLElement{
Scripts + return ` + ` } ).join("\n") diff --git a/ccp/js/methodeditorcontroller2.js b/ccp/js/methodeditorcontroller2.js deleted file mode 100644 index da9edd2..0000000 --- a/ccp/js/methodeditorcontroller2.js +++ /dev/null @@ -1,881 +0,0 @@ -class CCPMethodEditorController extends HTMLElement{ - - #boot; - #rootdoc; - - #serviceurl; - #infrastructures; - #locked = false; - - #isupdate = false; - #cloneornew_dialog = null; - #dragging_method = null; - - #tmp_inputs = [] - #tmp_outputs = [] - #current = null; - #method_template = { - id : "", - title : "New Method", - description : "New empty method", - version : "1.0.0", - jobControlOptions : "async-execute", - metadata : [], - inputs : {}, - outputs : {}, - additionalParameters : { - parameters : [ - { - name : "deploy-script", - value : [] - }, - { - name : "execute-script", - value : [] - }, - { - name : "undeploy-script", - value : [] - } - ] - }, - links : [] - } - - #scripts = ` - - ` - - #style = ` - - - - - ` - #erase_icon = ` - - - ` - - #plus_icon = ` - - - - ` - #disc_icon = ` - - - - ` - #output_icon = ` - - - - ` - #delete_icon = ` - - - - ` - - constructor(){ - super(); - this.#boot = document.querySelector("d4s-boot-2") - this.#rootdoc = this.attachShadow({ "mode" : "open"}) - this.#serviceurl = this.getAttribute("serviceurl") - this.initMethod() - this.fetchInfrastructures() - this.connectNewEditRequest() - } - - connectedCallback(){ - - } - - connectNewEditRequest(){ - document.addEventListener("neweditrequest", ev=>{ - this.cloneOrEditMethod(ev.detail) - }) - } - - isInfrastructureRegistered(infra){ - return (this.#infrastructures.filter(i=>i.id === infra.id)).length > 0 - } - - fetchInfrastructures(){ - this.#boot.secureFetch(this.#serviceurl + "/infrastructures"). - then(resp=>{ - if(resp.status !== 200) throw "Unable to fetch infrastructures"; - return resp.json() - }).then(data=>{ - this.#infrastructures = data - this.render() - }).catch(err=>{ - alert(err) - }) - } - - saveMethod(){ - if(this.#locked) return; - if(this.#current != null){ - this.adoptTemporaries() - const text = `Confirm ${this.#isupdate ? "updating" : "creation"} of ${this.#current.title} version ${this.#current.version}` - if(window.confirm(text)){ - this.lockRender() - const url = this.#serviceurl + "/methods" - const args = { - body : JSON.stringify(this.#current), - method : this.#isupdate ? "PUT" : "POST", - headers : {"Content-type" : "application/json"} - } - this.#boot.secureFetch(url, args).then( - (resp)=>{ - if(resp.status === 201 || resp.status === 204){ - return resp.text() - }else throw "Error saving process: " + resp.status - }).then(data=>{ - if(!this.#isupdate){ - this.resetMethod() - } - this.unlockRender() - }).catch(err=>{ - alert(err) - this.unlockRender() - }) - } - } - } - - deleteMethod(){ - if(this.#locked) return; - if(this.#current != null){ - const text = `Confirm deletion of ${this.#current.title} version ${this.#current.version}` - if(window.confirm(text)){ - this.lockRender() - const url = this.#serviceurl + "/methods/" + this.#current.id - const args = { - method : "DELETE" - } - this.#boot.secureFetch(url, args).then( - (resp)=>{ - if(resp.status === 404 || resp.status === 204){ - return null - }else throw "Error deleting method: " + resp.status - }).then(data=>{ - this.unlockRender() - }).catch(err=>{ - alert(err) - this.unlockRender() - }) - } - } - } - - initMethod(){ - this.#current = JSON.parse(JSON.stringify(this.#method_template)) - this.#current.id = Math.abs((Math.random() * 10e11)|0) - this.#current.metadata = [] - this.#tmp_inputs = [ - { - id : "ccpimage", - title : "Runtime", - description : "The image of the runtime to use for method execution. This depends on the infrastructure specific protocol for interacting with registries.", - minOccurs : 1, - maxOccurs : 1, - schema : { - type : "string", - format : "url", - contentMediaType : "text/plain", - default : "", - readonly : true, - } - } - ] - this.#tmp_outputs = [] - } - - resetMethod(){ - this.initMethod() - this.render() - } - - cloneOrEditMethod(method){ - const subject = this.#boot.subject - const matchingauthors = method.metadata ? method.metadata.filter(md=>md.role === "author" && md.href.endsWith("/" + subject)).length : 0 - if(matchingauthors === 0) this.cloneMethod(method.id); - else{ - this.#dragging_method = method.id - this.#cloneornew_dialog.style.display = "block" - this.#cloneornew_dialog.classList.add("show") - } - } - - cloneMethod(method){ - if(this.#locked) return; - this.lockRender() - this.#boot.secureFetch(this.#serviceurl + "/methods/" + method + "/clone").then( - (resp)=>{ - if(resp.status === 200){ - return resp.json() - }else throw "Error retrieving process: " + resp.status - } - ).then(data=>{ - this.#current = data - this.#isupdate = false - this.#tmp_inputs = Object.keys(this.#current.inputs).map(k=>this.#current.inputs[k]) - this.#tmp_outputs = Object.keys(this.#current.outputs).map(k=>this.#current.outputs[k]) - this.unlockRender() - }).catch(err=>{ - this.unlockRender() - }) - } - - editMethod(method){ - if(this.#locked) return; - this.lockRender() - this.#boot.secureFetch(this.#serviceurl + "/methods/" + method + "/updatable").then( - (resp)=>{ - if(resp.status === 200){ - return resp.json() - }else throw "Error retrieving process: " + resp.status - } - ).then(data=>{ - this.#current = data - this.#isupdate = true - this.#tmp_inputs = Object.keys(this.#current.inputs).map(k=>this.#current.inputs[k]) - this.#tmp_outputs = Object.keys(this.#current.outputs).map(k=>this.#current.outputs[k]) - this.unlockRender() - }).catch(err=>{ - this.unlockRender() - }) - } - - adoptTemporaries(){ - this.#current.inputs = {} - this.#tmp_inputs.forEach(t=>{ - this.#current.inputs[t.id] = t - }) - this.#current.outputs = {} - this.#tmp_outputs.forEach(t=>{ - this.#current.outputs[t.id] = t - }) - } - - getInputAt(i){ - return this.#tmp_inputs[i] - } - - deleteTmpInputAt(i){ - this.#tmp_inputs.splice(i,1) - } - - deleteTmpOutputAt(i){ - this.#tmp_outputs.splice(i,1) - } - - lockRender(){ - this.#locked = true - this.#rootdoc.querySelector(".plexiglass").classList.toggle("d-none") - } - - unlockRender(){ - this.#rootdoc.querySelector(".plexiglass").classList.toggle("d-none") - this.render() - this.#locked = false - this.parentElement.scrollIntoViewIfNeeded() - } - - render(){ - this.#rootdoc.innerHTML = ` - - -
-
- - - -
- ${this.#style} -
-
-
-
-
- ${this.#current.title} -
-
- ${this.renderSaveButton()} - ${this.renderResetButton()} - ${ this.#isupdate ? this.renderDeleteButton() : "" } -
-
-
-
-
- ${this.renderAuthors()} -
-
- ${this.renderContexts()} -
-
-
- - -
-
- - -
-
-
- - -
-
- - -
- ${this.renderKeywords()} -
-
-
- - -
- ${this.renderInfrastructures()} -
-
-
-
-
- -
-
- Inputs -
-
- ${this.renderPlusButton("add-input")} -
-
-
-
-
-
-
- -
-
- Outputs -
-
- ${this.renderStandardOutputButtons()} - ${this.renderPlusButton("add-output")} -
-
-
-
- -
-
-
- -
-
- Scripts - -
-
-
-
- ${this.renderScripts()} -
-
-
- - ` - this.renderInputs() - this.renderOutputs() - - this.#cloneornew_dialog = this.#rootdoc.querySelector("#cloneornew") - - this.#rootdoc.querySelector("#cloneornew button[name=clone]").addEventListener("click", ev=>{ - this.#cloneornew_dialog.classList.remove("show") - this.#cloneornew_dialog.style.display = "none" - this.cloneMethod(this.#dragging_method) - this.#dragging_method = null - }) - - this.#rootdoc.querySelector("#cloneornew button[name=edit]").addEventListener("click", ev=>{ - this.#cloneornew_dialog.classList.remove("show") - this.#cloneornew_dialog.style.display = "none" - this.editMethod(this.#dragging_method) - this.#dragging_method = null - }) - - this.#rootdoc.querySelector("input[name=title]").addEventListener("input", ev=>{ - this.#current.title = ev.currentTarget.value - this.#rootdoc.querySelector("span[name=header]").innerText = this.#current.title - }) - - this.#rootdoc.querySelector("input[name=version]").addEventListener("input", ev=>{ - this.#current.version = ev.currentTarget.value - }) - - this.#rootdoc.querySelector("input[name=description]").addEventListener("input", ev=>{ - this.#current.description = ev.currentTarget.value - }) - - this.#rootdoc.addEventListener("drop", ev=>{ - if(ev.dataTransfer && ev.dataTransfer.getData('text/plain+ccpmethod')){ - const method = JSON.parse(ev.dataTransfer.getData('application/json+ccpmethod')) - ev.stopImmediatePropagation() - ev.preventDefault() - ev.stopPropagation() - this.cloneOrEditMethod(method) - } - }) - - this.#rootdoc.addEventListener("dragover", ev=>{ - ev.preventDefault() - }) - - this.#rootdoc.querySelector("button[name=reset]").addEventListener("click", ev=>{ - if(window.confirm("All unsaved data will be lost. Proceed?")){ - this.resetMethod() - } - ev.preventDefault() - ev.stopPropagation() - }) - - this.#rootdoc.querySelector("button[name=save]").addEventListener("click", ev=>{ - ev.preventDefault() - ev.stopPropagation() - this.saveMethod() - }) - - if(this.#isupdate){ - this.#rootdoc.querySelector("button[name=delete]").addEventListener("click", ev=>{ - ev.preventDefault() - ev.stopPropagation() - this.deleteMethod() - }) - } - - this.#rootdoc.querySelector("input[name=keyword-input]").addEventListener("keypress", ev=>{ - if(ev.key === "Enter" || ev.which === 13){ - ev.preventDefault() - ev.stopPropagation() - const val = ev.target.value - ev.target.value = null - if(!this.#current.keywords){ - this.#current.keywords = [val] - }else{ - this.#current.keywords.push(val) - } - this.reRenderKeywords() - } - }) - - this.#rootdoc.querySelector("div[name=keyword-list]").addEventListener("click", ev=>{ - ev.preventDefault() - ev.stopPropagation() - if(ev.target.getAttribute('name') === "delete-keyword"){ - const index = ev.target.getAttribute("data-index") - this.#current.keywords.splice(index, 1) - this.reRenderKeywords() - this.#rootdoc.querySelector("input[name=keyword-input]").focus() - } - }) - - this.#rootdoc.querySelector("div[name=infrastructures]").addEventListener("change", ev=>{ - if(ev.target.getAttribute("name") === "infrastructure-input" && ev.target.value){ - ev.preventDefault() - ev.stopPropagation() - const id = ev.target.value - const display = ev.target.options[ev.target.selectedIndex].text - let link = { - rel : "compatibleWith", title : display, href : "infrastructures/" + id - } - if(!this.#current.links){ - this.#current.links = [link] - }else{ - this.#current.links.push(link) - } - this.reRenderInfrastructures(ev.currentTarget) - } - }) - - this.#rootdoc.querySelector("div[name=infrastructure-list]").addEventListener("click", ev=>{ - ev.preventDefault() - ev.stopPropagation() - if(ev.target.getAttribute('name') === "delete-infrastructure"){ - const index = ev.target.getAttribute("data-index") - this.#current.links.splice(index, 1) - this.reRenderInfrastructures() - } - }) - - this.#rootdoc.querySelector("button[name=add-input]").addEventListener("click", ev=>{ - ev.preventDefault() - ev.stopPropagation() - this.#tmp_inputs.push( - { - id : "new_input", - title : "New input", - description : "A new input field", - minOccurs : 1, - maxOccurs : 1, - schema : { - type : "string", - format : null, - contentMediaType : "text/plain", - default : "" - } - } - ) - this.renderInputs() - }) - - this.#rootdoc.querySelector("div[name=input-list]").addEventListener("click", ev=>{ - const evname = ev.target.getAttribute('name') - if(evname === "delete-input"){ - ev.preventDefault() - ev.stopPropagation() - const index = Number(ev.target.getAttribute("data-index")) - console.log("deleting input at index", index) - this.deleteTmpInputAt(index) - this.renderInputs() - } - }) - - this.#rootdoc.querySelector("button[name=add-output]").addEventListener("click", ev=>{ - ev.preventDefault() - ev.stopPropagation() - this.#tmp_outputs.push( - { - id : "new_output", - title : "New ouput", - description : "A new output field", - minOccurs : 1, - maxOccurs : 1, - metadata : [ - { - "role" : "file", - "title" : "newoutput.txt", - "href" : "newoutput.txt" - } - ], - schema : { - type : "string", - contentMediaType : "text/plain" - } - } - ) - this.renderOutputs() - }) - - this.#rootdoc.querySelector("button[name=add-stdout]").addEventListener("click", ev=>{ - ev.preventDefault() - ev.stopPropagation() - this.#tmp_outputs.push( - { - id : "stdout", - title : "Standard output", - description : "Standard output channel", - minOccurs : 1, - maxOccurs : 1, - metadata : [ - { - "role" : "file", - "title" : "stdout", - "href" : "stdout" - } - ], - schema : { - type : "string", - contentMediaType : "text/plain" - } - } - ) - this.renderOutputs() - }) - - this.#rootdoc.querySelector("button[name=add-stderr]").addEventListener("click", ev=>{ - ev.preventDefault() - ev.stopPropagation() - this.#tmp_outputs.push( - { - id : "stderr", - title : "Standard error", - description : "Standard error channel", - minOccurs : 1, - maxOccurs : 1, - metadata : [ - { - "role" : "file", - "title" : "stderr", - "href" : "stderr" - } - ], - schema : { - type : "string", - contentMediaType : "text/plain" - } - } - ) - this.renderOutputs() - }) - - this.#rootdoc.querySelector("div[name=output-list]").addEventListener("click", ev=>{ - const evname = ev.target.getAttribute('name') - if(evname === "delete-output"){ - ev.preventDefault() - ev.stopPropagation() - const index = Number(ev.target.getAttribute("data-index")) - console.log("deleting output at index", index) - this.deleteTmpOutputAt(index) - this.renderOutputs() - } - }) - - this.#rootdoc.querySelector("select[name=script-selector]").addEventListener("change", ev=>{ - ev.preventDefault() - ev.stopPropagation() - const scriptname = ev.target.value - const areas = Array.prototype.slice.call(this.#rootdoc.querySelectorAll("textarea.script-area")) - areas.forEach(a=>{ - if(a.getAttribute("name") === scriptname) a.classList.remove("d-none"); - else a.classList.add("d-none") - }) - }) - - this.#rootdoc.querySelector("details[name=script-list]").addEventListener("input", ev=>{ - ev.preventDefault() - ev.stopPropagation() - const scriptname = ev.target.getAttribute("name") - const script = this.#current.additionalParameters.parameters.filter(p=>p.name === scriptname)[0] - if(script) script.value = ev.target.value.split(/\r?\n/); - }) - } - - renderAuthors(){ - return ` - - ` - } - - renderContexts(){ - return ` - - ` - } - - renderSaveButton(){ - return ` - - ` - } - - renderDeleteButton(){ - return ` - - ` - } - - renderResetButton(){ - return ` - - ` - } - - renderPlusButton(name){ - return ` - - ` - } - - renderStandardOutputButtons(){ - return ` - - - ` - } - - reRenderKeywords(){ - this.#rootdoc.querySelector("div[name=keyword-list]").innerHTML = this.renderKeywords() - } - - renderKeywords(){ - if(this.#current.keywords){ - return this.#current.keywords.map((k,i) => { - return ` -
- ${k} - x -
- ` - }).join("\n") - }else{ - return "" - } - } - - reRenderInfrastructures(){ - this.#rootdoc.querySelector("select[name=infrastructure-input]").innerHTML = this.renderInfrastructureOptions() - this.#rootdoc.querySelector("div[name=infrastructure-list]").innerHTML = this.renderInfrastructures() - } - - renderInfrastructureOptions(){ - const selectedinfras = this.#current.links.filter(l=>l.rel === "compatibleWith").map(i=>i.href.split("/")[1]) - const available = this.#infrastructures.filter(i=>{ return selectedinfras.indexOf(i.id) === -1 }) - return ` - - ${ - available.map(infra=>{ - return ` - - ` - }).join("\n") - } - ` - } - - renderInfrastructures(){ - return this.#current.links.filter(l=>l.rel === "compatibleWith").map((l, i)=>{ - return ` -
- ${l.title} - x -
- ` - }).join("\n") - } - - renderInputs(){ - const parent = this.#rootdoc.querySelector("div[name=input-list]") - parent.innerHTML = "" - return this.#tmp_inputs.map((inp, i)=>{ - const c = document.createElement("d4s-ccp-input-editor"); - parent.appendChild(c) - c.render(inp, i) - }) - } - - renderOutputs(){ - const parent = this.#rootdoc.querySelector("div[name=output-list]") - parent.innerHTML = "" - return this.#tmp_outputs.map((outp, i)=>{ - const c = document.createElement("d4s-ccp-output-editor"); - parent.appendChild(c) - c.render(outp, i) - }) - } - - renderScripts(){ - const val = 'deploy-script' - return this.#current.additionalParameters.parameters.map( - (script, i) => { - let code = script.value.length ? script.value.join("\r\n") : "" - return ` - - ` - } - ).join("\n") - } -} - -window.customElements.define('d4s-ccp-methodeditor', CCPMethodEditorController); diff --git a/ccp/methodlistfragment.html b/ccp/methodlistfragment.html deleted file mode 100644 index 8f8a8b4..0000000 --- a/ccp/methodlistfragment.html +++ /dev/null @@ -1,87 +0,0 @@ -
- - -
- - -
-