Compare commits

...

74 Commits
0.0.1 ... prod

Author SHA1 Message Date
Marco Lettere b2d0ae9e82 Merge pull request 'master' (#13) from master into prod
Reviewed-on: #13
2024-04-23 18:26:23 +02:00
dcore94 b254262f58 improved display 2024-04-23 16:46:47 +02:00
dcore94 f7cd0932b5 fire input event when file is read, fix deprecated func and improved display 2024-04-23 16:41:53 +02:00
dcore94 e3efc70bad fixed case of json for ccpimage 2024-04-23 12:50:58 +02:00
dcore94 96030670c2 cleaned up code and moved everything to bs5.0.2 2024-04-17 17:47:28 +02:00
dcore94 e8c71ecb55 overflow improvements 2024-04-17 17:12:30 +02:00
dcore94 4173ee650c position inside 2024-04-17 16:46:54 +02:00
dcore94 cc752abe40 Added select button for storage tree, added icons for well-known file types, corrected bug with direct input to ws link input field 2024-04-17 14:52:36 +02:00
dcore94 78a3009573 removed unnecessary style 2024-04-16 13:40:26 +02:00
dcore94 cc792a3e62 refactored method import to use preview widgets for input and scripts, added possibility to choose for public or protected link from workspace, fixed several bugs 2024-04-16 13:07:23 +02:00
dcore94 2f8f9483b8 added index test page for CCP webcomponents 2024-04-16 13:06:00 +02:00
dcore94 f4faebc9f1 changed gateway to next.dev 2024-04-16 13:05:06 +02:00
dcore94 687be71314 added some prevent defaults to events 2024-04-16 13:03:51 +02:00
dcore94 1078e7abbb removed redundant logs 2024-04-16 13:03:11 +02:00
Marco Lettere 87b65cab81 Merge pull request 'fix enum default selection' (#12) from master into prod
Reviewed-on: #12
2024-04-11 17:38:53 +02:00
dcore94 297b2f6ffe fix enum default selection 2024-04-11 17:25:41 +02:00
Marco Lettere a51e2cd358 Merge pull request 'master' (#11) from master into prod
Reviewed-on: #11
2024-04-10 18:06:49 +02:00
dcore94 05ef785cf0 added support for bash scripts generation 2024-04-10 12:28:47 +02:00
dcore94 79ec13cdc5 added support for generating bash scripts 2024-04-10 12:21:14 +02:00
dcore94 ff613b70c2 show place holder instead of full link 2024-04-09 13:22:14 +02:00
dcore94 f8902077ec show place holder instead of full link 2024-04-09 13:16:51 +02:00
Marco Lettere 8649ef8d3d Merge pull request 'master' (#10) from master into prod
Reviewed-on: #10
2024-02-15 18:31:39 +01:00
dcore94 d275b087b8 fix to access correct uri 2024-02-09 11:59:40 +01:00
dcore94 4533b43773 fix to correct datasource 2024-02-09 11:57:23 +01:00
dcore94 6cb7fb7cae allow user to activate automatic archiving of outputs to workspace 2024-02-09 11:42:49 +01:00
dcore94 bcc02f680c fixed missing brackets for value expansions 2024-02-09 11:01:00 +01:00
Marco Lettere c4c6c2bb15 Merge pull request 'master' (#9) from master into prod
Reviewed-on: #9
2024-02-02 19:10:46 +01:00
dcore94 d4cae32fe2 fix show link for direct execution 2024-02-02 19:07:30 +01:00
dcore94 3f02f0a1a1 fix show link for direct execution 2024-02-02 19:06:01 +01:00
dcore94 c8b31cecc7 fix show link for direct execution 2024-02-02 18:44:01 +01:00
dcore94 d4f3ed98f5 show link for direct execution 2024-02-02 18:33:42 +01:00
dcore94 a6517301d5 fix 2024-02-02 18:12:35 +01:00
dcore94 0aee111148 fix 2024-02-02 18:10:43 +01:00
dcore94 556168a239 fix 2024-02-02 18:08:59 +01:00
dcore94 f69ee9b3e6 show direct execution url for method 2024-02-02 18:07:28 +01:00
dcore94 4f3a892eeb added support for initializing from execution 2024-02-02 17:45:04 +01:00
dcore94 a087e4904f fix 2024-02-02 17:35:59 +01:00
dcore94 cfea15251a fix 2024-02-02 17:32:51 +01:00
dcore94 0ce705bb3f added method inferring from url parametere 2024-02-02 17:28:05 +01:00
Marco Lettere cafb395cfc Merge pull request 'non authors can only derive a method' (#8) from master into prod
Reviewed-on: #8
2024-02-01 09:28:46 +01:00
dcore94 19d7be6ea0 non authors can only derive a method 2024-01-31 16:37:17 +01:00
Marco Lettere 4392e93d68 Merge pull request 'master' (#7) from master into prod
Reviewed-on: #7
2023-12-14 10:49:57 +01:00
dcore94 ab07310252 removed commented code 2023-12-14 10:46:54 +01:00
dcore94 a4bbbc0ccf pass encoded description as attribute 2023-12-13 19:24:58 +01:00
dcore94 1381140c49 fixed typo 2023-12-13 19:19:36 +01:00
dcore94 a9ec721892 removed spaces 2023-12-13 19:17:04 +01:00
dcore94 125f5492c5 fixed typo 2023-12-13 19:10:11 +01:00
dcore94 ccd75c7e6e changed description to textarea and show it without trigger 2023-12-13 19:07:56 +01:00
dcore94 45ab5dccca encode to base64 before setting as attirbute to fix descriptions containing quotes 2023-12-13 18:29:44 +01:00
Marco Lettere 6bff2ddf10 Merge pull request 'master' (#6) from master into prod
Reviewed-on: #6
2023-12-12 15:21:58 +01:00
dcore94 f57057e307 added option for generating Julia code 2023-12-12 11:13:16 +01:00
dcore94 8c73d0c8c5 added prortotype for external app manager 2023-12-01 14:05:42 +01:00
dcore94 901abe5869 derivation as query-param 2023-11-16 13:57:26 +01:00
dcore94 7f76178458 added derivation button 2023-11-16 13:45:24 +01:00
dcore94 5476851d1f fix possible npe 2023-11-16 11:31:34 +01:00
dcore94 d374f62ae0 use title instead of name to avoid uuid appearing as labels 2023-10-27 16:21:10 +02:00
dcore94 bfea923d4b visual fix 2023-10-27 15:59:52 +02:00
dcore94 801e349e80 fixed query to avoid hierarchy clashes 2023-10-27 15:53:32 +02:00
dcore94 4818464409 fixed query to avoid hierarchy clashes 2023-10-27 15:52:35 +02:00
dcore94 07382c94a7 fixed typo 2023-10-27 15:48:44 +02:00
dcore94 28d609a011 draw SharedFolder as folder 2023-10-27 15:46:10 +02:00
dcore94 00d53aed50 scroll only content of tree widget 2023-10-27 15:44:24 +02:00
dcore94 1d6fe0beb8 moved tool addition for remote file to render time 2023-10-27 15:37:53 +02:00
dcore94 b891562705 moved tool addition for remote file to render time 2023-10-27 15:30:11 +02:00
dcore94 1d52dc9fb8 moved tool addition for remote file to render time 2023-10-27 15:28:23 +02:00
dcore94 b7bd25fa7a improved visual of storage tree 2023-10-25 19:05:47 +02:00
dcore94 528db74d03 improved visual of storage tree 2023-10-25 19:04:20 +02:00
dcore94 63af988a9d improved visual of storage tree 2023-10-25 19:02:57 +02:00
dcore94 71c6d94674 improved visual of storage tree 2023-10-25 19:01:42 +02:00
dcore94 2897f65a60 improved visual of storage tree 2023-10-25 18:59:36 +02:00
Marco Lettere 1c9cccb06f Merge pull request 'Aggiornare 'README.md'' (#5) from master into prod
Reviewed-on: #5
2023-10-17 17:40:03 +02:00
Marco Lettere 6dff3cd55c Aggiornare 'README.md' 2023-10-17 17:38:53 +02:00
Marco Lettere c0f90f567f Merge pull request 'Aggiornare 'README.md'' (#4) from master into prod
Reviewed-on: #4
2023-10-17 16:05:47 +02:00
Marco Lettere 51ca438382 Aggiornare 'README.md' 2023-10-17 16:03:35 +02:00
15 changed files with 1258 additions and 797 deletions

View File

@ -124,14 +124,14 @@ window.customElements.define('d4s-boot-2', class extends HTMLElement {
}).then(token => {
console.log("Authorized. Token exp: " + this.expirationDate(this.parseJwt(token).exp))
//transform all queued requests to fetches
console.log("All pending requests to promises")
//console.log("All pending requests to promises")
let promises = this.#queue.map(r => {
r.request.headers["Authorization"] = "Bearer " + token
return r.resolve( fetch(r.url, r.request) )
})
//clear queue
this.#queue = []
console.log("Resolving all fetches")
//console.log("Resolving all fetches")
return Promise.all(promises)
}).catch(err => console.error("Unable to make calls: " + err)) // Sometimes throws: Unable to make calls: TypeError: Cannot read properties of undefined (reading 'split')
@ -177,7 +177,7 @@ window.customElements.define('d4s-boot-2', class extends HTMLElement {
} else {
req.headers = { "Authorization" : null}
}
console.log("Queued request to url ", url)
//console.log("Queued request to url ", url)
this.#queue.push({ url : url, request : req, resolve : resolve, reject : reject})
})
return p
@ -202,7 +202,7 @@ window.customElements.define('d4s-boot-2', class extends HTMLElement {
.then(response => response.json())
.then(json => {
this.#config = json
console.log("Keycloak uma2 configuration loaded")
//console.log("Keycloak uma2 configuration loaded")
resolve(true)
})
.catch(err => reject("Failed to fetch uma2-configuration from server: " + err))

34
ccp/index.html Normal file
View File

@ -0,0 +1,34 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<script src="js/methodlistcontroller.js"></script>
<script src="js/methodeditorcontroller.js"></script>
<script src="js/inputwidgeteditorcontroller.js"></script>
<script src="js/outputwidgeteditorcontroller.js"></script>
<script src="js/executionformcontroller.js"></script>
<script src="js/inputwidgetcontroller.js"></script>
<script src="js/outputwidgetcontroller.js"></script>
<link href="css/common.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">
<script src="https://cdn.cloud-dev.d4science.org/common/js/bss-min-1.2.6.js"></script>
<script src="../storage/d4s-storage.js"></script>
</head>
<body class="m-4">
<d4s-boot-2 clientid="https://next.dev.d4science.org" context="%2Fgcube%2Fdevsec%2FCCP" gateway="next.dev.d4science.org" redirect-url="http://localhost:8080/ccp/index.html" url="https://accounts.dev.d4science.org/auth"> <script src="https://cdn.dev.d4science.org/boot/d4s-boot.js"></script> </d4s-boot-2>
<div class="row">
<div class="col">
<d4s-ccp-methodlist serviceurl="https://ccp.cloud-dev.d4science.org" allow-edit="true"></d4s-ccp-methodlist>
</div>
<div class="col">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<d4s-ccp-methodeditor serviceurl="https://ccp.cloud-dev.d4science.org"></d4s-ccp-methodeditor>
</div>
<div class="col">
<d4s-ccp-executionform serviceurl="https://ccp.cloud-dev.d4science.org"></d4s-ccp-executionform>
</div>
</div>
</body>
</html>

View File

@ -11,14 +11,26 @@ class CCPExecutionForm extends HTMLElement{
constructor(){
super()
this.#boot = document.querySelector("d4s-boot-2")
this.#serviceurl = this.getAttribute("serviceurl")
this.#rootdoc = this.attachShadow({ "mode" : "open"})
this.connectNewExecutionRequest()
this.render()
this.showMethod()
}
connectedCallback(){
this.#serviceurl = this.getAttribute("serviceurl")
this.connectNewExecutionRequest()
this.render()
const params = new URLSearchParams(window.location.search)
if(params.get('execution')){
const execution = { id : params.get('execution') }
this.prepareFromExecution(execution)
} else if(params.get('method')){
this.#method = params.get('method')
this.loadMethod()
} else {
this.showMethod()
}
}
static get observedAttributes() {
return ["method"];
}
@ -43,8 +55,9 @@ class CCPExecutionForm extends HTMLElement{
render(){
this.#rootdoc.innerHTML = `
<div>
<link rel="canonical" href="https://getbootstrap.com/docs/5.0/components/modal/">
<link rel="stylesheet" href="https://cdn.dev.d4science.org/ccp/css/common.css"></link>
<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">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" crossorigin="anonymous">
<style>
.ccp-execution-form{
position: relative;
@ -66,34 +79,53 @@ class CCPExecutionForm extends HTMLElement{
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<h5>Options</h5>
</div>
<div class="card-body">
<div class="col form-check">
<input class="form-check-input" type="checkbox" name="auto-archive-outputs" alt="Automatically archive outputs to workspace" title="Automatically archive outputs to workspace" checked="checked">
<label class="form-check-label">Automatically archive outputs to workspace</label>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<h5>Outputs</h5>
</div>
<div class="card-body">
<div class="form-row ccp-outputs">
<div class="row ccp-outputs">
<div class="col form-group"></div>
</div>
</div>
</div>
<div class="form-row">
<div class="row">
<div class="col-6">
<button id="execute_method_button" class="btn btn-info">Execute</button>
</div>
<div class="col-6">
<div class="">
<div class="mb-3">
<label>Generate code for:</label>
<div class="d-flex">
<select name="language-selector" class="form-control" style="padding:2px">
<option value="text/python" data-ext="py" title="Generate plain Python3">Python 3</option>
<option value="text/plain+r" data-ext="r" title="Generate plain R">R</option>
<option value="application/vnd.jupyter+python" data-ext="ipynb" title="Generate Jupyter notebook with Python 3 cells">Jupyter Python3</option>
</select>
<button name="codegen" title="Generate code" class="btn btn-primary ccp-toolbar-button ccp-toolbar-button-small">
<svg viewBox="0 96 960 960">
<path d="M320 814 80 574l242-242 43 43-199 199 197 197-43 43Zm318 2-43-43 199-199-197-197 43-43 240 240-242 242Z"></path>
</svg>
</button>
<select name="language-selector" class="form-control" style="padding:2px">
<option value="text/python" data-ext="py" title="Generate plain Python3">Python 3</option>
<option value="text/plain+r" data-ext="r" title="Generate plain R">R</option>
<option value="application/vnd.jupyter+python" data-ext="ipynb" title="Generate Jupyter notebook with Python 3 cells">Jupyter Python3</option>
<option value="text/julia" data-ext="jl" title="Generate Julia 1.9.0 code (HTTP 1.10.0, JSON3 1.13.2)">Julia 1.9.0</option>
<option value="application/x-sh+curl" data-ext="sh" title="Generate Bash script (curl)">Bash (curl)</option>
<option value="application/x-sh+wget" data-ext="sh" title="Generate Bash script (wget)">Bash (wget)</option>
</select>
<button name="codegen" title="Generate code" class="btn btn-primary ccp-toolbar-button ccp-toolbar-button-small">
<svg viewBox="0 96 960 960">
<path d="M320 814 80 574l242-242 43 43-199 199 197 197-43 43Zm318 2-43-43 199-199-197-197 43-43 240 240-242 242Z"></path>
</svg>
</button>
</div>
</div>
<div>
<div class="d-flex">
<a name="direct_link_method" class="text-truncate" href="${window.location.href}">Direct link</a>
</div>
</div>
</div>
@ -218,6 +250,13 @@ class CCPExecutionForm extends HTMLElement{
outputs.forEach(o=>{
if(o.enabled) request.outputs[o.name] = { transmissionMode : "value" };
})
const autoarchiveoption = this.#rootdoc.querySelector("input[name='auto-archive-outputs']")
if (autoarchiveoption.checked){
request.subscribers = [
{ successUri : "http://registry:8080/executions/archive-to-folder" }
]
}
return request
}
@ -230,7 +269,7 @@ class CCPExecutionForm extends HTMLElement{
return Array.prototype.slice.call(this.#rootdoc.querySelectorAll("d4s-ccp-output"))
}
initValues(inputs){
initInputValues(inputs){
Object.keys(inputs).forEach(k=>{
const w = this.#rootdoc.querySelector(`d4s-ccp-input[name=${k}]`)
if(w){
@ -238,6 +277,11 @@ class CCPExecutionForm extends HTMLElement{
}
})
}
initOptionValues(request){
const autoarchiveoption = this.#rootdoc.querySelector("input[name='auto-archive-outputs']")
autoarchiveoption.checked = request.subscribers && request.subscribers.filter(s=>s.successUri === "http://registry:8080/executions/archive-to-folder").length === 1
}
prepareFromExecution(exec){
let f1 =
@ -270,7 +314,8 @@ class CCPExecutionForm extends HTMLElement{
this.#data.executable = resp.status === 200
}).then(()=>{
this.showMethod()
this.initValues(requestdata.inputs)
this.initInputValues(requestdata.inputs)
this.initOptionValues(requestdata)
}).catch(err=>alert(err))
}
@ -340,7 +385,7 @@ class CCPExecutionForm extends HTMLElement{
"in" : (e,d)=>{ return Object.values(d.inputs) },
target : "div",
apply : (e,d)=>{
e.innerHTML = `<d4s-ccp-input name="${d.id}" input='${JSON.stringify(d)}'></d4s-ccp-input>`
e.innerHTML = `<d4s-ccp-input name="${d.id}" input='${btoa(JSON.stringify(d))}'></d4s-ccp-input>`
}
}
]
@ -353,7 +398,7 @@ class CCPExecutionForm extends HTMLElement{
"in" : (e,d)=>{ return Object.values(d.outputs) },
target : "div",
apply : (e,d)=>{
e.innerHTML = `<d4s-ccp-output name="${d.id}" output='${JSON.stringify(d)}'></d4s-ccp-output>`
e.innerHTML = `<d4s-ccp-output name="${d.id}" output='${btoa(JSON.stringify(d))}'></d4s-ccp-output>`
}
}
]
@ -386,6 +431,10 @@ class CCPExecutionForm extends HTMLElement{
return false
}
},
{
target: "a[name=direct_link_method]",
apply : (e,d)=>e.href = window.location.origin + window.location.pathname + "?method=" + d.id
}
]
}

View File

@ -119,18 +119,28 @@ class CCPExecutionHistory extends HTMLElement {
<ul>
<li></li>
</ul>
<div class="d-flex justify-content-end" style="gap: 3px;">
<label>Generate code for:</label>
<select name="language-selector" class="form-control" style="max-width:10rem;height:inherit;padding:2px">
<option value="text/python" data-ext="py" title="Generate plain Python3">Python 3</option>
<option value="text/plain+r" data-ext="r" title="Generate plain R">R</option>
<option value="application/vnd.jupyter+python" data-ext="ipynb" title="Generate Jupyter notebook with Python 3 cells">Jupyter Python3</option>
</select>
<button data-index="0" name="codegen" title="Generate code" class="btn btn-primary ccp-toolbar-button ccp-toolbar-button-small">
<svg viewBox="0 96 960 960">
<path d="m384 721 43-43-101-102 101-101-43-43-144 144.5L384 721Zm192 0 145-145-144-144-43 43 101 101-102 102 43 43ZM180 936q-24.75 0-42.375-17.625T120 876V276q0-24.75 17.625-42.375T180 216h205q5-35 32-57.5t63-22.5q36 0 63 22.5t32 57.5h205q24.75 0 42.375 17.625T840 276v600q0 24.75-17.625 42.375T780 936H180Zm0-60h600V276H180v600Zm300-617q14 0 24.5-10.5T515 224q0-14-10.5-24.5T480 189q-14 0-24.5 10.5T445 224q0 14 10.5 24.5T480 259ZM180 876V276v600Z"/>
</svg>
</button>
<div class="d-flex flex-column align-items-end" style="gap: 3px;">
<div class="d-flex align-items-center" style="gap:5px">
<label style="text-wrap:nowrap">Generate code for:</label>
<select name="language-selector" class="form-control">
<option value="text/python" data-ext="py" title="Generate plain Python3">Python 3</option>
<option value="text/plain+r" data-ext="r" title="Generate plain R">R</option>
<option value="application/vnd.jupyter+python" data-ext="ipynb" title="Generate Jupyter notebook with Python 3 cells">Jupyter Python3</option>
<option value="text/julia" data-ext="jl" title="Generate Julia 1.9.0 code (HTTP 1.10.0, JSON3 1.13.2)">Julia 1.9.0</option>
<option value="application/x-sh+curl" data-ext="sh" title="Generate Bash script (curl)">Bash (curl)</option>
<option value="application/x-sh+wget" data-ext="sh" title="Generate Bash script (wget)">Bash (wget)</option>
</select>
<button data-index="0" name="codegen" title="Generate code" class="btn btn-primary ccp-toolbar-button ccp-toolbar-button-small">
<svg viewBox="0 96 960 960">
<path d="m384 721 43-43-101-102 101-101-43-43-144 144.5L384 721Zm192 0 145-145-144-144-43 43 101 101-102 102 43 43ZM180 936q-24.75 0-42.375-17.625T120 876V276q0-24.75 17.625-42.375T180 216h205q5-35 32-57.5t63-22.5q36 0 63 22.5t32 57.5h205q24.75 0 42.375 17.625T840 276v600q0 24.75-17.625 42.375T780 936H180Zm0-60h600V276H180v600Zm300-617q14 0 24.5-10.5T515 224q0-14-10.5-24.5T480 189q-14 0-24.5 10.5T445 224q0 14 10.5 24.5T480 259ZM180 876V276v600Z"/>
</svg>
</button>
</div>
<div class="d-flex align-items-middle" style="gap:5px">
<div class="d-flex">
<a class="text-truncate" name="direct_link_execution" href="${window.location.href}">Direct link</a>
</div>
</div>
</div>
</details>
</li>
@ -157,8 +167,9 @@ class CCPExecutionHistory extends HTMLElement {
<div class="d-flex" style="gap:2px">
<input type="text" class="form-control" placeholder="Paste link here"/>
<button name="archive" class="btn btn-primary ccp-toolbar-button m-0" title="Upload from link">
<svg viewBox="0 96 960 960"><path d="M450 776H280q-83 0-141.5-58.5T80 576q0-83 58.5-141.5T280 376h170v60H280q-58.333 0-99.167 40.765-40.833 40.764-40.833 99Q140 634 180.833 675q40.834 41 99.167 41h170v60ZM324 606v-60h310v60H324Zm556-30h-60q0-58-40.833-99-40.834-41-99.167-41H510v-60h170q83 0 141.5 58.5T880 576ZM699 896V776H579v-60h120V596h60v120h120v60H759v120h-60Z"/></svg>
<svg viewBox="0 96 960 960">
<path d="M450 776H280q-83 0-141.5-58.5T80 576q0-83 58.5-141.5T280 376h170v60H280q-58.333 0-99.167 40.765-40.833 40.764-40.833 99Q140 634 180.833 675q40.834 41 99.167 41h170v60ZM324 606v-60h310v60H324Zm556-30h-60q0-58-40.833-99-40.834-41-99.167-41H510v-60h170q83 0 141.5 58.5T880 576ZM699 896V776H579v-60h120V596h60v120h120v60H759v120h-60Z"/>
</svg>
</button>
</div>
</div>
@ -656,6 +667,10 @@ class CCPExecutionHistory extends HTMLElement {
this.generateCode(id, lang, `${id}.${ext}`)
}
},
{
target : "a[name=direct_link_execution]",
apply : (e,d)=>e.href = window.location.origin + window.location.pathname + "?execution=" + d.id
},
{
target : "div[name=logterminalcontainer]",
apply : (e,d)=>{

File diff suppressed because it is too large Load Diff

View File

@ -23,7 +23,7 @@ class CCPInputWidgetEditorController extends HTMLElement{
return this.#index
}
computeDefaultInputType(){
/*computeDefaultInputType(){
if(this.#input.schema.format === "secret"){
return "password"
}else if(this.#input.schema.format === "date"){
@ -34,14 +34,15 @@ class CCPInputWidgetEditorController extends HTMLElement{
return "datetime-local"
}
return "text"
}
}*/
isSelectedFormat(fmt){
return this.#input.schema.format === fmt
return this.#input.schema.format === fmt
}
renderDefaultByType(){
if(this.#input.schema.format === "code"){
return `<d4s-ccp-input name="default" input="${btoa(JSON.stringify(this.#input))}"></d4s-ccp-input>`
/*if(this.#input.schema.format === "code"){
return `
<textarea rows="5" name="default" class="form-control" placeholder="default">${this.#input.schema.default}</textarea>
`
@ -49,7 +50,7 @@ class CCPInputWidgetEditorController extends HTMLElement{
return `
<input type="${this.computeDefaultInputType()}" value="${this.#input.schema.default}" name="default" class="form-control" placeholder="default"/>
`
}
}*/
}
renderDeleteButton(){
@ -62,6 +63,8 @@ class CCPInputWidgetEditorController extends HTMLElement{
this.#index = i
this.#input = input
this.#type = input.schema.enum ? "enum" : "string"
const minOccurs = input.minOccurs = Number(input.minOccurs) ? Number(input.minOccurs) : 0
const maxOccurs = input.maxOccurs = Number(input.maxOccurs) ? Number(input.maxOccurs) : 0
this.innerHTML = `
<details ${ reopen ? 'open' : ''}>
<summary class="mb-3">
@ -78,29 +81,29 @@ class CCPInputWidgetEditorController extends HTMLElement{
</div>
<div class="mb-3">
<div class="form-field" title="description">
<input name="description" class="form-control" placeholder="description" value="${input.description}" required="required" ${ input.id === 'ccpimage' ? 'readonly' : ''}/>
<textarea rows="1" name="description" class="form-control" placeholder="description" required="required" ${ input.id === 'ccpimage' ? 'readonly' : ''}>${input.description}</textarea>
</div>
</div>
<div class="row mb-3">
<div class="col-3">
<div class="form-field" title="Type">
<label>Type</label>
<select name="type" class="form-control" placeholder="type" value="${this.#type}" ${ input.id === 'ccpimage' ? 'readonly' : ''}>
<select name="type" class="form-control" placeholder="type" value="${this.#type}">
<option value="string" ${this.#type === "string" ? "selected" : ""}>String</option>
<option value="enum" ${this.#type === "enum" ? "selected" : ""}>Enum</option>
${ input.id === 'ccpimage' ? '' : `<option value="enum" ${this.#type === "enum" ? "selected" : ""}>Enum</option>` }
</select>
</div>
</div>
<div class="col">
<div class="form-field" title="Minimum occurrences">
<label>Min occurs</label>
<input value="${input.minOccurs}" type="number" min="0" step="1" name="minOccurs" value="${input.minOccurs}" required="required" class="form-control" placeholder="minOccurs" ${ input.id === 'ccpimage' ? 'readonly' : ''}/>
<input value="${minOccurs}" type="number" min="0" step="1" name="minOccurs" value="${minOccurs}" required="required" class="form-control" placeholder="minOccurs" ${ input.id === 'ccpimage' ? 'readonly' : ''}/>
</div>
</div>
<div class="col">
<div class="form-field" title="Maximum occurrences">
<label>Max occurs</label>
<input value="${input.maxOccurs}" type="number" min="0" step="1" name="maxOccurs" value="${input.maxOccurs}" required="required" class="form-control" placeholder="maxOccurs" ${ input.id === 'ccpimage' ? 'readonly' : ''}/>
<input value="${maxOccurs}" type="number" min="0" step="1" name="maxOccurs" value="${maxOccurs}" required="required" class="form-control" placeholder="maxOccurs" ${ input.id === 'ccpimage' ? 'readonly' : ''}/>
</div>
</div>
<div class="col" title="Read only">
@ -123,7 +126,7 @@ class CCPInputWidgetEditorController extends HTMLElement{
<option value="boolean" ${this.isSelectedFormat('boolean') ? "selected" : ""}>True/False</option>
<option value="code" ${this.isSelectedFormat('code') ? "selected" : ""}>Code</option>
<option value="file" ${this.isSelectedFormat('file') ? "selected" : ""}>File</option>
<option value="remotefile" ${this.isSelectedFormat('remotefile') ? "selected" : ""}>Remote file</option>
<option value="remotefile" ${this.isSelectedFormat('remotefile') ? "selected" : ""}>Workspace file</option>
<option value="secret" ${this.isSelectedFormat('secret') ? "selected" : ""}>Secret</option>
<option value="url" ${this.isSelectedFormat('url') ? "selected" : ""}>Url</option>
</select>
@ -150,8 +153,9 @@ class CCPInputWidgetEditorController extends HTMLElement{
</div>
</div>
<div name="input-default" class="mb-3">
<div class="form-field" title="Default value">
${this.renderDefaultByType()}
<label>Default value</label>
<div class="form-field border border-info px-2 py-1" style="background-color:#0dcaf022" title="Default value">
<div name="default-container">${this.renderDefaultByType()}</div>
<span style="user-select:none;position:relative;top:-1.6rem;float:right;cursor:pointer" name="password_toggle" class="${this.isSelectedFormat('secret') ? 'inline' : 'd-none'}">&#128065;</span>
</div>
</div>
@ -166,54 +170,64 @@ class CCPInputWidgetEditorController extends HTMLElement{
ev.preventDefault()
}
})
const defaultinp = this.querySelector("div[name='input-default']")
defaultinp.addEventListener("input", ev=>{
const inp = this.querySelector("d4s-ccp-input")
this.#input.schema.default = inp.value
})
defaultinp.addEventListener("change", ev=>{
const inp = this.querySelector("d4s-ccp-input")
this.#input.schema.default = inp.value
})
defaultinp.addEventListener("click", ev=>{
const src = ev.target.getAttribute("name")
if (src === "plus" || src === "minus") {
const inp = this.querySelector("d4s-ccp-input")
this.#input.schema.default = inp.value
}
})
this.addEventListener("input", ev=>{
const val = ev.target.value
const ename = ev.target.getAttribute("name")
const display = this.querySelector("div[name='default-container']")
if(ename === "id"){
this.#input.id = val
}
else if(ename === "title"){
this.#input.title = val
display.innerHTML = this.renderDefaultByType()
}
else if(ename === "description"){
this.#input.description = val
display.innerHTML = this.renderDefaultByType()
}
else if(ename === "minOccurs"){
this.#input.minOccurs = val
this.#input.minOccurs = Number(val) ? Number(val) : 0
display.innerHTML = this.renderDefaultByType()
}
else if(ename === "maxOccurs"){
this.#input.maxOccurs = val
this.#input.maxOccurs = Number(val) ? Number(val) : 0
display.innerHTML = this.renderDefaultByType()
}
else if(ename === "format"){
this.#input.schema.format = val
//this.render(this.#input, this.#index, true)
/*this.querySelector("div[name=input-default] span[name=password_toggle]").classList.add("d-none")
this.querySelector("div[name=input-default] input[name=default]").type = ""
if(this.#input.schema.format === "secret"){
this.querySelector("div[name=input-default] input[name=default]").type = "password"
this.querySelector("div[name=input-default] span[name=password_toggle]").classList.remove("d-none")
}else if(this.#input.schema.format === "date"){
this.querySelector("div[name=input-default] input[name=default]").type = "date"
}else if(this.#input.schema.format === "time"){
this.querySelector("div[name=input-default] input[name=default]").type = "time"
}else if(this.#input.schema.format === "dateTime"){
this.querySelector("div[name=input-default] input[name=default]").type = "dateTime"
}else if(this.#input.schema.format === "file"){
this.querySelector("div[name=input-default] input[name=default]").type = "file"
}*/
display.innerHTML = this.renderDefaultByType()
}
else if(ename === "contentMediaType"){
this.#input.schema.contentMediaType = val
display.innerHTML = this.renderDefaultByType()
}
else if(ename === "options"){
this.#input.schema.enum = val.split(",")
}
else if(ename === "default"){
this.#input.schema.default = val
display.innerHTML = this.renderDefaultByType()
}
else if(ename === "readonly"){
this.#input.schema.readOnly = ev.target.checked
display.innerHTML = this.renderDefaultByType()
}
else if(ename === "type"){
this.#type = ev.target.value
@ -225,10 +239,10 @@ class CCPInputWidgetEditorController extends HTMLElement{
this.querySelector("div[name=enum-input]").classList.add("d-none")
this.querySelector("div[name=string-input]").classList.remove("d-none")
delete this.#input.schema['enum']
}
}
}
display.innerHTML = this.renderDefaultByType()
}
})
}
}

File diff suppressed because it is too large Load Diff

View File

@ -17,11 +17,8 @@ class CCPMethodList extends HTMLElement{
constructor(){
super()
this.#boot = document.querySelector("d4s-boot-2")
this.#serviceurl = this.getAttribute("serviceurl")
this.#rootdoc = this.attachShadow({ "mode" : "open"})
this.#archive = this.getAttribute("archive")
this.render()
this.fetchProcesses()
}
render(){
@ -167,6 +164,9 @@ class CCPMethodList extends HTMLElement{
connectedCallback(){
this.#allowedit = this.getAttribute("allow-edit") === "true"
this.#allowexecute = this.getAttribute("allow-execute") === "true"
this.#serviceurl = this.getAttribute("serviceurl")
this.render()
this.fetchProcesses()
}
fetchProcesses(){
@ -220,9 +220,9 @@ class CCPMethodList extends HTMLElement{
const f = filter.toLowerCase()
this.#filtered = this.#data.filter(d=>{
return false ||
(d.title.toLowerCase().indexOf(f) !== -1)||
(d.description.indexOf(f) !== -1) ||
(d.keywords.map(k=>k.toLowerCase()).filter(i=>i.indexOf(f) !== -1)).length
(d.title && d.title.toLowerCase().indexOf(f) !== -1)||
(d.description && d.description.indexOf(f) !== -1) ||
(Array.isArray(d.keywords) && d.keywords.map(k=>k.toLowerCase()).filter(i=>i.indexOf(f) !== -1)).length
})
}
this.groupBy()

View File

@ -4,7 +4,7 @@ class CCPOutputWidgetController extends HTMLElement {
constructor(){
super()
this.#output = JSON.parse(this.getAttribute("output"))
this.#output = JSON.parse(atob(this.getAttribute("output")))
}
connectedCallback(){
@ -22,7 +22,7 @@ class CCPOutputWidgetController extends HTMLElement {
render(){
return `
<div class="col form-check">
<input class="form-check-input" type="checkbox" name="this.#output.id" alt="${this.#output.description}" title="${this.#output.description}" checked="checked"/>
<input class="form-check-input" type="checkbox" name="${this.#output.id}" alt="${this.#output.description}" title="${this.#output.description}" checked="checked"/>
<label class="form-check-label">${this.#output.title}</label>
</div>
`

View File

@ -44,7 +44,7 @@ class CCPOutputWidgetEditorController extends HTMLElement {
</div>
<div class="mb-3">
<div class="form-field" title="Description">
<input name="description" class="form-control" placeholder="description" value="${this.#output.description}" required="required"/>
<textarea rows="1" name="description" class="form-control" placeholder="description" required="required">${this.#output.description}</textarea>
</div>
</div>
<div class="row mb-3">

16
extapp/index.html Normal file
View File

@ -0,0 +1,16 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<script src="js/rolescontroller.js"></script>
<script src="../common/js/keycloak.js" type="text/javascript"></script>
<script src="../boot/d4s-boot.js"></script>
</head>
<body>
<d4s-boot-2 context="_myextapp"
gateway="next.d4science.org"
redirect-url="http://localhost:8080"
url="https://accounts.dev.d4science.org/auth">
</d4s-boot-2>
<d4s-extapp-role-manager appid="076bfe7d-12a2-41d1-a720-05911f0ae527"></d4s-extapp-role-manager>
</body>
</html>

View File

@ -0,0 +1,90 @@
class ExtAppRoleController extends HTMLElement{
#boot;
#roles;
#users;
#mappings;
#serviceurl;
#appid;
#loading = false;
constructor(){
super()
this.#boot = document.querySelector("d4s-boot-2")
this.#serviceurl = this.#boot.url
}
connectedCallback(){
this.#appid = this.getAttribute("appid")
if(!this.#loading){
this.#loading = true
this.loadData()
}
}
loadData(){
Promise.all([
this.loadEligibleUsers(), this.loadRoles()
]).then(()=>{
console.log("Done. Ui should be complete now")
}).catch(err=>alert(err))
}
loadEligibleUsers(){
const url = this.#serviceurl + `/admin/realms/d4science/clients/${this.#appid}/roles/eligible/users?briefRepresentation=true&max=-1&first=0`
return this.#boot.secureFetch(url).then(resp=>{
if(resp.ok){
return resp.json()
}else if(resp.status === 403){
throw "Fetching users: You are not allowed to manage roles for this application."
}else{
throw "Fetching users: Unspecified error"
}
}).then(json=>{
console.log("Fetched eligible users can show table")
this.#users = json
return loadMappings()
}).then(()=>{
console.log(this.#mappings)
})
}
loadRoles(){
const url = this.#serviceurl + `/admin/realms/d4science/clients/${this.#appid}/roles`
return this.#boot.secureFetch(url).then(resp=>{
if(resp.ok){
return resp.json()
}else if(resp.status === 403){
throw "Fetching roles: You are not allowed to manage roles for this application."
}else{
throw "Fetching roles: Unspecified error"
}
}).then(json=>{
this.#roles = json
console.log("Roles loaded, could draw header")
})
}
loadMappings(){
return Promise.all(
this.#users.forEach(u => {
const url = this.#serviceurl + `/admin/realms/d4science/users/${u.id}/clients/${this.#appid}/roles`
this.#boot.secureFetch(url).then(resp=>{
if(resp.ok){
return resp.json()
}else if(resp.status === 403){
throw "Fetching role mappings: You are not allowed to manage roles for this application."
}else{
throw "Fetching role mappings: Unspecified error"
}
}).then(json=>{
u.mappings = json
console.log(`Fetched mappings for user ${u.id} can update the proper table`)
})
})
)
}
}
window.customElements.define('d4s-extapp-role-manager', ExtAppRoleController);

View File

@ -12,10 +12,13 @@
<h3>CDN sandbox</h3>
<p>Select the section you want to enter</p>
</header>
<div class="d-flex">
<div class="d-flex flex-column">
<div class="d-inline-block">
<a href="storage/index.html">D4S Workspace</a> (OIDC protected)
</div>
<div class="d-inline-block">
<a href="ccp/index.html">CCP</a> (OIDC protected)
</div>
</div>
</body>

View File

@ -16,10 +16,7 @@ class D4SStorageHtmlElement extends HTMLElement {
}
connectedCallback() {
let linkElem = document.createElement('link');
linkElem.setAttribute('rel', 'stylesheet');
linkElem.setAttribute('href', 'https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css');
this.shadowRoot.appendChild(linkElem);
}
get srcBaseURL() {
@ -98,7 +95,7 @@ class D4SStorageToolbar extends D4SStorageHtmlElement {
class D4SStorageTree extends D4SStorageHtmlElement {
static tree_event_name = "d4s-toolbar";
static tree_event_name = "d4s-tree";
static folder_data_event_name = "d4s-folder-data";
static dataid_attr = 'data-id';
@ -226,8 +223,10 @@ class D4SStorageTree extends D4SStorageHtmlElement {
const div = document.createElement('div')
div.innerHTML = /*css*/`
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<style>
.selected > span {
.selected > .li-title {
background-color: ${this.#selectedbgcolor};
}
ul.root {
@ -238,6 +237,12 @@ class D4SStorageTree extends D4SStorageHtmlElement {
margin-bottom: 0;
padding-left: 0.6em;
}
li{
color: #444444;
}
li:hover{
color: #555555;
}
li {
cursor: pointer;
}
@ -378,7 +383,7 @@ class D4SStorageTree extends D4SStorageHtmlElement {
this.dispatchEvent(
new CustomEvent(
D4SStorageTree.tree_event_name,
{detail: {id: id}}
{detail: {id: id, bubbles: true}}
)
);
}
@ -417,15 +422,43 @@ class D4SStorageTree extends D4SStorageHtmlElement {
data
.filter(item => this.showFiles || item['@class'].includes('FolderItem'))
.forEach(item => {
ul.appendChild(this.createListItem(item.name, item.id, item['@class'], parentId));
ul.appendChild(this.createListItem(item, parentId));
})
} else {
ul.appendChild(this.createListItem(data.displayName ? data.displayName : data.name, data.id, data['@class'], parentId));
ul.appendChild(this.createListItem(data, parentId));
}
parentElement.appendChild(ul);
}
createListItem(label, id, type, parentId) {
getIconByMIME(mime){
if(mime.startsWith("image")) return "image";
if(mime.startsWith("application/pdf")) return "picture_as_pdf";
if(mime.startsWith("application/zip")) return "archive";
if(mime.startsWith("application/xml")) return "code";
if(mime.match(/tar|compressed|gzip|gz|tgz/)) return 'archive';
return null;
}
getIconByExtension(name){
const tks = name.split(".")
if(tks.length === 0) return null;
const ext = tks[tks.length - 1]
if(ext.match(/java$|py$|^r$|^c$|ccp|lua|jl|^sh$|json|xml|xsl|xslt|md$|xq$|xqm$/i)) return "code";
if(ext.match(/js$/i)) return "javascript";
if(ext.match(/html$|css$|php$/i)) return ext;
if(ext.match(/kml$/i)) return "place";
if(ext.match(/ics$/i)) return "event";
if(ext.match(/csv$|xls$|ods$/i)) return "assessment";
if(ext.match(/txt$|odt$|rtf$|doc$|docx$/i)) return "description";
if(ext.match(/ppt$|odp$/i)) return "slideshow";
return null;
}
createListItem(item, parentId) {
const label = item.displayName ? item.displayName : item.title
const id = item.id
const type = item["@class"]
const mime = item.content && item.content.mimeType ? item.content.mimeType : null
const li = document.createElement('li');
li.setAttribute(D4SStorageTree.dataname_attr, label);
li.setAttribute(D4SStorageTree.dataid_attr, id);
@ -434,14 +467,30 @@ class D4SStorageTree extends D4SStorageHtmlElement {
if (parentId) {
li.setAttribute(D4SStorageTree.parentid_attr, parentId);
}
const icon = type.includes('FolderItem') ? "folder.svg" : "file-earmark.svg"
//const icon = type.includes('FolderItem') || type.includes("SharedFolder")? "folder.svg" : "file-earmark.svg"
var icon = "insert_drive_file";
if(type.includes('FolderItem')){
icon = "folder"
}else if(type.includes("SharedFolder")){
icon = "folder_shared"
}else{
const im = mime ? this.getIconByMIME(mime) : null
if(im) icon = im;
else{
const ie = this.getIconByExtension(label)
if(ie) icon = ie;
}
}
li.innerHTML = `
<img class="px-1" src="${this.srcBaseURL}/img/${icon}"</img>
<span>${label}</span>
<div class="li-title d-flex gap-1">
<span class="material-icons">${icon}</span>
<span>${label}</span>
</div>
`
li.addEventListener('click', (ev) => {
ev.stopPropagation();
ev.preventDefault()
this.select(ev.currentTarget.getAttribute(D4SStorageTree.dataid_attr));
});
@ -668,6 +717,7 @@ class D4SStorageFolder extends D4SStorageHtmlElement {
const isFolder = item['@class'].includes('FolderItem')
filename.addEventListener('click', (ev) => {
ev.stopPropagation()
ev.preventDefault()
if (isFolder) {
const span = ev.currentTarget.querySelector(':nth-child(2)')
if (span) {

View File

@ -10,7 +10,7 @@
<body class="m-4">
<d4s-boot-2 context="%2Fgcube%2Fdevsec%2FdevVRE"
gateway="next.d4science.org"
gateway="next.dev.d4science.org"
redirect-url="https://cdn.dev.d4science.org/storage/"
url="https://accounts.dev.d4science.org/auth">
<!-- redirect-url="http://localhost:8080/storage/" -->