Stirling-PDF/src/main/resources/templates/pipeline.html

524 lines
21 KiB
HTML
Raw Normal View History

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-13 20:18:40 +02:00
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<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
Pipeline Configuration</button>
2023-06-21 00:49:53 +02:00
<button id="validateButton" class="btn btn-success">Validate</button>
2023-06-13 20:18:40 +02:00
<div class="btn-group">
<button id="uploadPipelineBtn" class="btn btn-primary">Upload
Pipeline 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>
<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>
2023-06-14 00:50:46 +02:00
<input type="file" id="fileInput" multiple>
<button class="btn btn-primary" id="submitConfigBtn">Submit</button>
2023-06-13 20:18:40 +02:00
</div>
<!-- pipelineSettings modal -->
<div id="pipelineSettingsModal" class="modal">
<div class="modal-content">
<div class="modal-body">
<span class="close">&times;</span>
<h2>Operation Settings</h2>
<div id="pipelineSettingsContent">
<!-- pipelineSettings will be dynamically populated here -->
</div>
</div>
</div>
</div>
<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 01:32:15 +02:00
}
/* Modal Content */
.modal-content {
2023-06-13 20:18:40 +02:00
background-color: #fefefe;
margin: auto;
padding: 20px;
border: 1px solid #888;
width: 50%;
2023-06-13 01:32:15 +02:00
}
2023-06-13 20:18:40 +02:00
2023-06-13 01:32:15 +02:00
.btn-margin {
2023-06-13 20:18:40 +02:00
margin-right: 2px;
}
2023-06-13 01:32:15 +02:00
.modal-body {
2023-06-13 20:18:40 +02:00
display: flex;
flex-direction: column;
2023-06-13 01:32:15 +02:00
}
2023-06-13 20:18:40 +02:00
</style>
<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>&uarr;</span></button>
<button class="btn btn-secondary move-down btn-margin"><span>&darr;</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>