2023-06-13 01:32:15 +02:00
<!DOCTYPE html>
2023-06-13 20:18:40 +02:00
< html th:lang = "${#locale.toString()}"
th:lang-direction="#{language.direction}"
xmlns:th="http://www.thymeleaf.org">
2023-06-13 01:32:15 +02:00
< th:block th:insert = "~{fragments/common :: head(title=#{merge.title})}" > < / th:block >
< body >
2023-06-21 22:41:58 +02:00
< style >
.modal {
display: none; /* Hidden by default */
position: fixed; /* Stay in place */
z-index: 1; /* Sit on top */
padding-top: 100px; /* Location of the box */
left: 0;
top: 0;
width: 100%; /* Full width */
height: 100%; /* Full height */
overflow: auto; /* Enable scroll if needed */
background-color: rgb(0, 0, 0); /* Fallback color */
background-color: rgba(0, 0, 0, 0.4); /* Black w/ opacity */
}
2023-06-13 20:18:40 +02:00
2023-06-21 22:41:58 +02:00
/* Modal Content */
.modal-content {
background-color: #fefefe;
margin: auto;
padding: 20px;
border: 1px solid #888;
width: 50%;
}
2023-06-13 20:18:40 +02:00
2023-06-21 22:41:58 +02:00
.btn-margin {
margin-right: 2px;
}
2023-06-13 20:18:40 +02:00
2023-06-21 22:41:58 +02:00
.modal-body {
display: flex;
flex-direction: column;
}
< / style >
2023-06-13 20:18:40 +02:00
2023-06-21 22:41:58 +02:00
< div id = "page-container" >
< div id = "content-wrap" >
< div th:insert = "~{fragments/navbar.html :: navbar}" > < / div >
< div class = "container" id = "dropContainer" >
< div class = "row justify-content-center" >
< div class = "col-md-6" >
< div class = "mb-3" >
< button id = "savePipelineBtn" class = "btn btn-success" > Save Configuration< / button >
< button id = "validateButton" class = "btn btn-success" > Validate< / button >
< div class = "btn-group" >
< button id = "uploadPipelineBtn" class = "btn btn-primary" > Upload Configuration< / button >
< input type = "file" id = "uploadPipelineInput" accept = ".json" style = "display: none;" >
< / div >
< / div >
< div id = "pipelineContainer" class = "card" >
<!-- Pipeline Configuration Card Header -->
< div class = "card-header" >
< h2 class = "card-title" > Pipeline Configuration< / h2 >
< / div >
<!-- Pipeline Configuration Body -->
< div class = "card-body" >
< div class = "mb-3" >
< select id = "operationsDropdown" class = "form-select" >
<!-- Options will be dynamically populated here -->
< / select >
< / div >
2023-06-13 01:32:15 +02:00
2023-06-21 22:41:58 +02:00
< div class = "mb-3" >
< button id = "addOperationBtn" class = "btn btn-primary" > Add operation to pipeline< / button >
< / div >
< h3 > Pipeline:< / h3 >
< ol id = "pipelineList" class = "list-group" >
<!-- Pipeline operations will be dynamically populated here -->
< / ol >
< / div >
< div th:replace = "~{fragments/common :: fileSelector(name='fileInput', multiple=false, accept='application/pdf')}" > < / div >
< button class = "btn btn-primary" id = "submitConfigBtn" > Submit< / button >
< / div >
<!-- pipelineSettings modal -->
< div id = "pipelineSettingsModal" class = "modal" >
< div class = "modal-content" >
< div class = "modal-body" >
< span class = "close" > × < / span >
< h2 > Operation Settings< / h2 >
< div id = "pipelineSettingsContent" >
<!-- pipelineSettings will be dynamically populated here -->
< / div >
< / div >
< / div >
< / div >
2023-06-13 20:18:40 +02:00
2023-06-13 01:32:15 +02:00
2023-06-13 20:18:40 +02:00
< script >
2023-06-13 01:32:15 +02:00
2023-06-21 00:49:53 +02:00
document.getElementById('validateButton').addEventListener('click', function(event) {
event.preventDefault();
validatePipeline();
});
function validatePipeline() {
let pipelineListItems = document.getElementById('pipelineList').children;
let isValid = true;
2023-06-21 22:19:52 +02:00
let containsAddPassword = false;
2023-06-21 00:49:53 +02:00
for (let i = 0; i < pipelineListItems.length - 1 ; i + + ) {
let currentOperation = pipelineListItems[i].querySelector('.operationName').textContent;
let nextOperation = pipelineListItems[i + 1].querySelector('.operationName').textContent;
2023-06-21 22:19:52 +02:00
if(currentOperation === '/add-password'){
containsAddPassword = true;
2023-06-21 00:49:53 +02:00
}
2023-06-21 22:19:52 +02:00
console.log(currentOperation);
console.log(apiDocs[currentOperation]);
let currentOperationDescription = apiDocs[currentOperation]?.post?.description || "";
let nextOperationDescription = apiDocs[nextOperation]?.post?.description || "";
console.log("currentOperationDescription",currentOperationDescription);
console.log("nextOperationDescription", nextOperationDescription);
let currentOperationOutput = currentOperationDescription.match(/Output:([A-Z\/]*)/)?.[1] || "";
let nextOperationInput = nextOperationDescription.match(/Input:([A-Z\/]*)/)?.[1] || "";
console.log("Operation " + currentOperation + " Output: " + currentOperationOutput);
console.log("Operation " + nextOperation + " Input: " + nextOperationInput);
// Splitting in case of multiple possible output/input
let currentOperationOutputArr = currentOperationOutput.split('/');
let nextOperationInputArr = nextOperationInput.split('/');
if (currentOperationOutput !== 'ANY' & & nextOperationInput !== 'ANY') {
let intersection = currentOperationOutputArr.filter(value => nextOperationInputArr.includes(value));
console.log(`Intersection: ${intersection}`);
if (intersection.length === 0) {
isValid = false;
console.log(`Incompatible operations: The output of operation '${currentOperation}' (${currentOperationOutput}) is not compatible with the input of the following operation '${nextOperation}' (${nextOperationInput}).`);
alert(`Incompatible operations: The output of operation '${currentOperation}' (${currentOperationOutput}) is not compatible with the input of the following operation '${nextOperation}' (${nextOperationInput}).`);
break;
}
2023-06-21 00:49:53 +02:00
}
}
2023-06-21 22:19:52 +02:00
if (containsAddPassword & & pipelineListItems[pipelineListItems.length - 1].querySelector('.operationName').textContent !== '/add-password') {
alert('The "add-password" operation should be at the end of the operations sequence. Please adjust the operations order.');
return false;
}
2023-06-21 00:49:53 +02:00
if (isValid) {
console.log('Pipeline is valid');
// Continue with the pipeline operation
} else {
console.error('Pipeline is not valid');
// Stop operation, maybe display an error to the user
}
2023-06-21 22:19:52 +02:00
return isValid;
2023-06-21 00:49:53 +02:00
}
2023-06-21 22:19:52 +02:00
2023-06-21 00:49:53 +02:00
2023-06-14 00:50:46 +02:00
document.getElementById('submitConfigBtn').addEventListener('click', function() {
2023-06-21 22:19:52 +02:00
if(validatePipeline() === false){
return;
}
2023-06-14 00:50:46 +02:00
let selectedOperation = document.getElementById('operationsDropdown').value;
let parameters = operationSettings[selectedOperation] || {};
let pipelineConfig = {
"name": "uniquePipelineName",
"pipeline": [{
"operation": selectedOperation,
"parameters": parameters
}]
};
let pipelineConfigJson = JSON.stringify(pipelineConfig, null, 2);
let formData = new FormData();
let fileInput = document.getElementById('fileInput');
let files = fileInput.files;
for(let i = 0; i < files.length ; i + + ) {
console.log("files[i]",files[i].name);
formData.append('fileInput', files[i], files[i].name);
}
console.log("pipelineConfigJson",pipelineConfigJson);
formData.append('json', pipelineConfigJson);
console.log("formData",formData);
2023-06-21 00:49:53 +02:00
2023-06-14 00:50:46 +02:00
fetch('/handleData', {
method: 'POST',
body: formData
})
.then(response => response.blob())
.then(blob => {
2023-06-21 00:49:53 +02:00
2023-06-14 00:50:46 +02:00
let url = window.URL.createObjectURL(blob);
let a = document.createElement('a');
a.href = url;
2023-06-21 00:49:53 +02:00
a.download = 'outputfile';
document.body.appendChild(a);
2023-06-14 00:50:46 +02:00
a.click();
2023-06-21 00:49:53 +02:00
a.remove();
2023-06-14 00:50:46 +02:00
})
.catch((error) => {
console.error('Error:', error);
});
});
2023-06-13 01:32:15 +02:00
let apiDocs = {};
2023-06-21 00:49:53 +02:00
2023-06-13 20:18:40 +02:00
let operationSettings = {};
2023-06-13 01:32:15 +02:00
fetch('v3/api-docs')
.then(response => response.json())
.then(data => {
apiDocs = data.paths;
let operationsDropdown = document.getElementById('operationsDropdown');
operationsDropdown.innerHTML = '';
Object.keys(apiDocs).forEach(operation => {
2023-06-21 22:19:52 +02:00
if(apiDocs[operation].hasOwnProperty('post')) {
let option = document.createElement('option');
option.textContent = operation;
operationsDropdown.appendChild(option);
}
2023-06-13 01:32:15 +02:00
});
});
document.getElementById('addOperationBtn').addEventListener('click', function() {
let selectedOperation = document.getElementById('operationsDropdown').value;
let pipelineList = document.getElementById('pipelineList');
let listItem = document.createElement('li');
listItem.className = "list-group-item";
listItem.innerHTML = `
< div class = "d-flex justify-content-between align-items-center w-100" >
< div class = "operationName" > ${selectedOperation}< / div >
< div class = "arrows d-flex" >
< button class = "btn btn-secondary move-up btn-margin" > < span > ↑ < / span > < / button >
< button class = "btn btn-secondary move-down btn-margin" > < span > ↓ < / span > < / button >
< button class = "btn btn-warning pipelineSettings btn-margin" > < span > ⚙️< / span > < / button >
< button class = "btn btn-danger remove" > < span > X< / span > < / button >
< / div >
< / div >
`;
pipelineList.appendChild(listItem);
listItem.querySelector('.move-up').addEventListener('click', function(event) {
event.preventDefault();
if (listItem.previousElementSibling) {
pipelineList.insertBefore(listItem, listItem.previousElementSibling);
}
});
listItem.querySelector('.move-down').addEventListener('click', function(event) {
event.preventDefault();
if (listItem.nextElementSibling) {
pipelineList.insertBefore(listItem.nextElementSibling, listItem);
}
});
listItem.querySelector('.remove').addEventListener('click', function(event) {
event.preventDefault();
pipelineList.removeChild(listItem);
});
2023-06-21 00:49:53 +02:00
2023-06-13 01:32:15 +02:00
listItem.querySelector('.pipelineSettings').addEventListener('click', function(event) {
event.preventDefault();
showpipelineSettingsModal(selectedOperation);
});
2023-06-21 00:49:53 +02:00
2023-06-13 01:32:15 +02:00
function showpipelineSettingsModal(operation) {
let pipelineSettingsModal = document.getElementById('pipelineSettingsModal');
let pipelineSettingsContent = document.getElementById('pipelineSettingsContent');
let operationData = apiDocs[operation].post.parameters || [];
pipelineSettingsContent.innerHTML = '';
operationData.forEach(parameter => {
let parameterDiv = document.createElement('div');
parameterDiv.className = "form-group";
let parameterLabel = document.createElement('label');
parameterLabel.textContent = `${parameter.name} (${parameter.schema.type}): `;
2023-06-21 00:49:53 +02:00
parameterLabel.title = parameter.description;
2023-06-13 01:32:15 +02:00
parameterDiv.appendChild(parameterLabel);
let parameterInput;
switch(parameter.schema.type) {
case 'string':
case 'number':
case 'integer':
parameterInput = document.createElement('input');
parameterInput.type = parameter.schema.type === 'string' ? 'text' : 'number';
parameterInput.className = "form-control";
break;
case 'boolean':
parameterInput = document.createElement('input');
parameterInput.type = 'checkbox';
break;
case 'array':
case 'object':
parameterInput = document.createElement('textarea');
parameterInput.placeholder = `Enter a JSON formatted ${parameter.schema.type}`;
parameterInput.className = "form-control";
break;
case 'enum':
parameterInput = document.createElement('select');
parameterInput.className = "form-control";
parameter.schema.enum.forEach(option => {
let optionElement = document.createElement('option');
optionElement.value = option;
optionElement.text = option;
parameterInput.appendChild(optionElement);
});
break;
default:
parameterInput = document.createElement('input');
parameterInput.type = 'text';
parameterInput.className = "form-control";
}
parameterInput.id = parameter.name;
2023-06-21 00:49:53 +02:00
2023-06-13 20:18:40 +02:00
if(operationSettings[operation] & & operationSettings[operation][parameter.name] !== undefined) {
let savedValue = operationSettings[operation][parameter.name];
switch(parameter.schema.type) {
case 'number':
case 'integer':
parameterInput.value = savedValue.toString();
break;
case 'boolean':
parameterInput.checked = savedValue;
break;
case 'array':
case 'object':
parameterInput.value = JSON.stringify(savedValue);
break;
default:
parameterInput.value = savedValue;
}
}
2023-06-21 00:49:53 +02:00
2023-06-13 01:32:15 +02:00
parameterDiv.appendChild(parameterInput);
pipelineSettingsContent.appendChild(parameterDiv);
});
let saveButton = document.createElement('button');
saveButton.textContent = "Save Settings";
saveButton.className = "btn btn-primary";
saveButton.addEventListener('click', function(event) {
event.preventDefault();
let settings = {};
operationData.forEach(parameter => {
let value = document.getElementById(parameter.name).value;
switch(parameter.schema.type) {
case 'number':
case 'integer':
settings[parameter.name] = Number(value);
break;
case 'boolean':
settings[parameter.name] = document.getElementById(parameter.name).checked;
break;
case 'array':
case 'object':
try {
settings[parameter.name] = JSON.parse(value);
} catch(err) {
console.error(`Invalid JSON format for ${parameter.name}`);
}
break;
default:
settings[parameter.name] = value;
}
});
2023-06-13 20:18:40 +02:00
operationSettings[operation] = settings;
2023-06-21 00:49:53 +02:00
console.log(settings);
2023-06-13 01:32:15 +02:00
pipelineSettingsModal.style.display = "none";
});
pipelineSettingsContent.appendChild(saveButton);
pipelineSettingsModal.style.display = "block";
pipelineSettingsModal.getElementsByClassName("close")[0].onclick = function() {
pipelineSettingsModal.style.display = "none";
}
window.onclick = function(event) {
if (event.target == pipelineSettingsModal) {
pipelineSettingsModal.style.display = "none";
}
}
}
2023-06-21 00:49:53 +02:00
2023-06-13 20:18:40 +02:00
document.getElementById('savePipelineBtn').addEventListener('click', function() {
2023-06-21 22:19:52 +02:00
if(validatePipeline() === false){
return;
}
2023-06-13 20:18:40 +02:00
let pipelineList = document.getElementById('pipelineList').children;
let pipelineConfig = {
"name": "uniquePipelineName",
"pipeline": []
};
for(let i=0; i< pipelineList.length ; i + + ) {
let operationName = pipelineList[i].querySelector('.operationName').textContent;
2023-06-21 00:49:53 +02:00
let parameters = operationSettings[operationName] || {};
2023-06-13 20:18:40 +02:00
pipelineConfig.pipeline.push({
"operation": operationName,
"parameters": parameters
});
}
let a = document.createElement('a');
a.href = URL.createObjectURL(new Blob([JSON.stringify(pipelineConfig, null, 2)], {
type: 'application/json'
}));
a.download = 'pipelineConfig.json';
a.style.display = 'none';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
});
document.getElementById('uploadPipelineBtn').addEventListener('click', function() {
document.getElementById('uploadPipelineInput').click();
});
document.getElementById('uploadPipelineInput').addEventListener('change', function(e) {
let reader = new FileReader();
reader.onload = function(event) {
let pipelineConfig = JSON.parse(event.target.result);
let pipelineList = document.getElementById('pipelineList');
2023-06-21 00:49:53 +02:00
2023-06-13 20:18:40 +02:00
while(pipelineList.firstChild) {
pipelineList.removeChild(pipelineList.firstChild);
}
2023-06-21 00:49:53 +02:00
2023-06-13 20:18:40 +02:00
pipelineConfig.pipeline.forEach(operationConfig => {
let operationsDropdown = document.getElementById('operationsDropdown');
operationsDropdown.value = operationConfig.operation;
operationSettings[operationConfig.operation] = operationConfig.parameters;
document.getElementById('addOperationBtn').click();
let lastOperation = pipelineList.lastChild;
lastOperation.querySelector('.pipelineSettings').click();
Object.keys(operationConfig.parameters).forEach(parameterName => {
let input = document.getElementById(parameterName);
if(input) {
switch(input.type) {
case 'checkbox':
input.checked = operationConfig.parameters[parameterName];
break;
case 'number':
input.value = operationConfig.parameters[parameterName].toString();
break;
case 'text':
case 'textarea':
default:
input.value = JSON.stringify(operationConfig.parameters[parameterName]);
}
}
});
document.querySelector('#pipelineSettingsModal .btn-primary').click();
});
};
reader.readAsText(e.target.files[0]);
});
2023-06-13 01:32:15 +02:00
});
< / script >
2023-06-13 20:18:40 +02:00
< / div >
< / div >
< / div >
< / div >
< div th:insert = "~{fragments/footer.html :: footer}" > < / div >
< / div >
2023-06-13 01:32:15 +02:00
< / body >
< / html >