some more changes also broke pipeline a bit

This commit is contained in:
Anthony Stirling 2023-07-15 16:06:33 +01:00
parent 6e32c7fe85
commit 9af1b0cfdc
7 changed files with 430 additions and 313 deletions

View file

@ -1,162 +1,171 @@
package stirling.software.SPDF.controller.api.filters; package stirling.software.SPDF.controller.api.filters;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage; import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.common.PDRectangle; import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import stirling.software.SPDF.utils.PdfUtils; import stirling.software.SPDF.utils.PdfUtils;
import stirling.software.SPDF.utils.ProcessExecutor; import stirling.software.SPDF.utils.ProcessExecutor;
import stirling.software.SPDF.utils.WebResponseUtils; import stirling.software.SPDF.utils.WebResponseUtils;
import io.swagger.v3.oas.annotations.media.Schema;
@RestController @RestController
@Tag(name = "Filter", description = "Filter APIs") @Tag(name = "Filter", description = "Filter APIs")
public class FilterController { public class FilterController {
@PostMapping(consumes = "multipart/form-data", value = "/contains-text") @PostMapping(consumes = "multipart/form-data", value = "/contains-text")
@Operation(summary = "Checks if a PDF contains set text, returns true if does", description = "Input:PDF Output:Boolean Type:SISO") @Operation(summary = "Checks if a PDF contains set text, returns true if does", description = "Input:PDF Output:Boolean Type:SISO")
public Boolean containsText( public Boolean containsText(
@RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file to be converted to a PDF/A file", required = true) MultipartFile inputFile, @RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file to be converted to a PDF/A file", required = true) MultipartFile inputFile,
@Parameter(description = "The text to check for", required = true) String text, @Parameter(description = "The text to check for", required = true) String text,
@Parameter(description = "The page number to check for text on accepts 'All', ranges like '1-4'", required = false) String pageNumber) @Parameter(description = "The page number to check for text on accepts 'All', ranges like '1-4'", required = false) String pageNumber)
throws IOException, InterruptedException { throws IOException, InterruptedException {
PDDocument pdfDocument = PDDocument.load(inputFile.getInputStream()); PDDocument pdfDocument = PDDocument.load(inputFile.getInputStream());
return PdfUtils.hasText(pdfDocument, pageNumber); return PdfUtils.hasText(pdfDocument, pageNumber);
} }
@PostMapping(consumes = "multipart/form-data", value = "/contains-image") //TODO
@Operation(summary = "Checks if a PDF contains an image", description = "Input:PDF Output:Boolean Type:SISO") @PostMapping(consumes = "multipart/form-data", value = "/contains-image")
public Boolean containsImage( @Operation(summary = "Checks if a PDF contains an image", description = "Input:PDF Output:Boolean Type:SISO")
@RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file to be converted to a PDF/A file", required = true) MultipartFile inputFile, public Boolean containsImage(
@Parameter(description = "The page number to check for image on accepts 'All', ranges like '1-4'", required = false) String pageNumber) @RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file to be converted to a PDF/A file", required = true) MultipartFile inputFile,
throws IOException, InterruptedException { @Parameter(description = "The page number to check for image on accepts 'All', ranges like '1-4'", required = false) String pageNumber)
PDDocument pdfDocument = PDDocument.load(inputFile.getInputStream()); throws IOException, InterruptedException {
return PdfUtils.hasImagesOnPage(null); PDDocument pdfDocument = PDDocument.load(inputFile.getInputStream());
} return PdfUtils.hasImagesOnPage(null);
}
@PostMapping(consumes = "multipart/form-data", value = "/page-count")
@Operation(summary = "Checks if a PDF is greater, less or equal to a setPageCount", description = "Input:PDF Output:Boolean Type:SISO") @PostMapping(consumes = "multipart/form-data", value = "/page-count")
public Boolean pageCount( @Operation(summary = "Checks if a PDF is greater, less or equal to a setPageCount", description = "Input:PDF Output:Boolean Type:SISO")
@RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file", required = true) MultipartFile inputFile, public Boolean pageCount(
@Parameter(description = "Page Count", required = true) String pageCount, @RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file", required = true) MultipartFile inputFile,
@Parameter(description = "Comparison type, accepts Greater, Equal, Less than", required = false) String comparator) @Parameter(description = "Page Count", required = true) String pageCount,
throws IOException, InterruptedException { @Parameter(description = "Comparison type",
// Load the PDF schema = @Schema(description = "The comparison type, accepts Greater, Equal, Less than",
PDDocument document = PDDocument.load(inputFile.getInputStream()); allowableValues = {"Greater", "Equal", "Less"})) String comparator)
int actualPageCount = document.getNumberOfPages(); throws IOException, InterruptedException {
// Load the PDF
// Perform the comparison PDDocument document = PDDocument.load(inputFile.getInputStream());
switch (comparator) { int actualPageCount = document.getNumberOfPages();
case "Greater":
return actualPageCount > Integer.parseInt(pageCount); // Perform the comparison
case "Equal": switch (comparator) {
return actualPageCount == Integer.parseInt(pageCount); case "Greater":
case "Less": return actualPageCount > Integer.parseInt(pageCount);
return actualPageCount < Integer.parseInt(pageCount); case "Equal":
default: return actualPageCount == Integer.parseInt(pageCount);
throw new IllegalArgumentException("Invalid comparator: " + comparator); case "Less":
} return actualPageCount < Integer.parseInt(pageCount);
} default:
throw new IllegalArgumentException("Invalid comparator: " + comparator);
@PostMapping(consumes = "multipart/form-data", value = "/page-size") }
@Operation(summary = "Checks if a PDF is of a certain size", description = "Input:PDF Output:Boolean Type:SISO") }
public Boolean pageSize(
@RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file", required = true) MultipartFile inputFile, @PostMapping(consumes = "multipart/form-data", value = "/page-size")
@Parameter(description = "Standard Page Size", required = true) String standardPageSize, @Operation(summary = "Checks if a PDF is of a certain size", description = "Input:PDF Output:Boolean Type:SISO")
@Parameter(description = "Comparison type, accepts Greater, Equal, Less than", required = false) String comparator) public Boolean pageSize(
throws IOException, InterruptedException { @RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file", required = true) MultipartFile inputFile,
@Parameter(description = "Standard Page Size", required = true) String standardPageSize,
// Load the PDF @Parameter(description = "Comparison type",
PDDocument document = PDDocument.load(inputFile.getInputStream()); schema = @Schema(description = "The comparison type, accepts Greater, Equal, Less than",
allowableValues = {"Greater", "Equal", "Less"})) String comparator)
PDPage firstPage = document.getPage(0); throws IOException, InterruptedException {
PDRectangle actualPageSize = firstPage.getMediaBox();
// Load the PDF
// Calculate the area of the actual page size PDDocument document = PDDocument.load(inputFile.getInputStream());
float actualArea = actualPageSize.getWidth() * actualPageSize.getHeight();
PDPage firstPage = document.getPage(0);
// Get the standard size and calculate its area PDRectangle actualPageSize = firstPage.getMediaBox();
PDRectangle standardSize = PdfUtils.textToPageSize(standardPageSize);
float standardArea = standardSize.getWidth() * standardSize.getHeight(); // Calculate the area of the actual page size
float actualArea = actualPageSize.getWidth() * actualPageSize.getHeight();
// Perform the comparison
switch (comparator) { // Get the standard size and calculate its area
case "Greater": PDRectangle standardSize = PdfUtils.textToPageSize(standardPageSize);
return actualArea > standardArea; float standardArea = standardSize.getWidth() * standardSize.getHeight();
case "Equal":
return actualArea == standardArea; // Perform the comparison
case "Less": switch (comparator) {
return actualArea < standardArea; case "Greater":
default: return actualArea > standardArea;
throw new IllegalArgumentException("Invalid comparator: " + comparator); case "Equal":
} return actualArea == standardArea;
} case "Less":
return actualArea < standardArea;
default:
@PostMapping(consumes = "multipart/form-data", value = "/file-size") throw new IllegalArgumentException("Invalid comparator: " + comparator);
@Operation(summary = "Checks if a PDF is a set file size", description = "Input:PDF Output:Boolean Type:SISO") }
public Boolean fileSize( }
@RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file", required = true) MultipartFile inputFile,
@Parameter(description = "File Size", required = true) String fileSize,
@Parameter(description = "Comparison type, accepts Greater, Equal, Less than", required = false) String comparator) @PostMapping(consumes = "multipart/form-data", value = "/file-size")
throws IOException, InterruptedException { @Operation(summary = "Checks if a PDF is a set file size", description = "Input:PDF Output:Boolean Type:SISO")
public Boolean fileSize(
// Get the file size @RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file", required = true) MultipartFile inputFile,
long actualFileSize = inputFile.getSize(); @Parameter(description = "File Size", required = true) String fileSize,
@Parameter(description = "Comparison type",
// Perform the comparison schema = @Schema(description = "The comparison type, accepts Greater, Equal, Less than",
switch (comparator) { allowableValues = {"Greater", "Equal", "Less"})) String comparator)
case "Greater": throws IOException, InterruptedException {
return actualFileSize > Long.parseLong(fileSize);
case "Equal": // Get the file size
return actualFileSize == Long.parseLong(fileSize); long actualFileSize = inputFile.getSize();
case "Less":
return actualFileSize < Long.parseLong(fileSize); // Perform the comparison
default: switch (comparator) {
throw new IllegalArgumentException("Invalid comparator: " + comparator); case "Greater":
} return actualFileSize > Long.parseLong(fileSize);
} case "Equal":
return actualFileSize == Long.parseLong(fileSize);
case "Less":
@PostMapping(consumes = "multipart/form-data", value = "/page-rotation") return actualFileSize < Long.parseLong(fileSize);
@Operation(summary = "Checks if a PDF is of a certain rotation", description = "Input:PDF Output:Boolean Type:SISO") default:
public Boolean pageRotation( throw new IllegalArgumentException("Invalid comparator: " + comparator);
@RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file", required = true) MultipartFile inputFile, }
@Parameter(description = "Rotation in degrees", required = true) int rotation, }
@Parameter(description = "Comparison type, accepts Greater, Equal, Less than", required = false) String comparator)
throws IOException, InterruptedException {
@PostMapping(consumes = "multipart/form-data", value = "/page-rotation")
// Load the PDF @Operation(summary = "Checks if a PDF is of a certain rotation", description = "Input:PDF Output:Boolean Type:SISO")
PDDocument document = PDDocument.load(inputFile.getInputStream()); public Boolean pageRotation(
@RequestPart(required = true, value = "fileInput") @Parameter(description = "The input PDF file", required = true) MultipartFile inputFile,
// Get the rotation of the first page @Parameter(description = "Rotation in degrees", required = true) int rotation,
PDPage firstPage = document.getPage(0); @Parameter(description = "Comparison type",
int actualRotation = firstPage.getRotation(); schema = @Schema(description = "The comparison type, accepts Greater, Equal, Less than",
allowableValues = {"Greater", "Equal", "Less"})) String comparator)
// Perform the comparison throws IOException, InterruptedException {
switch (comparator) {
case "Greater": // Load the PDF
return actualRotation > rotation; PDDocument document = PDDocument.load(inputFile.getInputStream());
case "Equal":
return actualRotation == rotation; // Get the rotation of the first page
case "Less": PDPage firstPage = document.getPage(0);
return actualRotation < rotation; int actualRotation = firstPage.getRotation();
default:
throw new IllegalArgumentException("Invalid comparator: " + comparator); // Perform the comparison
} switch (comparator) {
} case "Greater":
return actualRotation > rotation;
} case "Equal":
return actualRotation == rotation;
case "Less":
return actualRotation < rotation;
default:
throw new IllegalArgumentException("Invalid comparator: " + comparator);
}
}
}

View file

@ -30,6 +30,8 @@ import com.google.zxing.Result;
import com.google.zxing.common.HybridBinarizer; import com.google.zxing.common.HybridBinarizer;
import stirling.software.SPDF.utils.WebResponseUtils; import stirling.software.SPDF.utils.WebResponseUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
@RestController @RestController
public class AutoSplitPdfController { public class AutoSplitPdfController {
@ -37,7 +39,10 @@ public class AutoSplitPdfController {
private static final String QR_CONTENT = "https://github.com/Frooodle/Stirling-PDF"; private static final String QR_CONTENT = "https://github.com/Frooodle/Stirling-PDF";
@PostMapping(value = "/auto-split-pdf", consumes = "multipart/form-data") @PostMapping(value = "/auto-split-pdf", consumes = "multipart/form-data")
public ResponseEntity<byte[]> autoSplitPdf(@RequestParam("fileInput") MultipartFile file) throws IOException { @Operation(summary = "Auto split PDF pages into separate documents", description = "This endpoint accepts a PDF file, scans each page for a specific QR code, and splits the document at the QR code boundaries. The output is a zip file containing each separate PDF document. Input:PDF Output:ZIP Type:SISO")
public ResponseEntity<byte[]> autoSplitPdf(
@RequestParam("fileInput") @Parameter(description = "The input PDF file which needs to be split into separate documents based on QR code boundaries.", required = true) MultipartFile file)
throws IOException {
InputStream inputStream = file.getInputStream(); InputStream inputStream = file.getInputStream();
PDDocument document = PDDocument.load(inputStream); PDDocument document = PDDocument.load(inputStream);
PDFRenderer pdfRenderer = new PDFRenderer(document); PDFRenderer pdfRenderer = new PDFRenderer(document);

View file

@ -53,9 +53,9 @@ import stirling.software.SPDF.utils.WebResponseUtils;
@RestController @RestController
@Tag(name = "Pipeline", description = "Pipeline APIs") @Tag(name = "Pipeline", description = "Pipeline APIs")
public class Controller { public class PipelineController {
private static final Logger logger = LoggerFactory.getLogger(Controller.class); private static final Logger logger = LoggerFactory.getLogger(PipelineController.class);
@Autowired @Autowired
private ObjectMapper objectMapper; private ObjectMapper objectMapper;
@ -246,11 +246,11 @@ public class Controller {
List<Resource> processFiles(List<Resource> outputFiles, String jsonString) throws Exception { List<Resource> processFiles(List<Resource> outputFiles, String jsonString) throws Exception {
logger.info("Processing files... " + outputFiles);
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
JsonNode jsonNode = mapper.readTree(jsonString); JsonNode jsonNode = mapper.readTree(jsonString);
JsonNode pipelineNode = jsonNode.get("pipeline"); JsonNode pipelineNode = jsonNode.get("pipeline");
logger.info("Running pipelineNode: {}", pipelineNode);
ByteArrayOutputStream logStream = new ByteArrayOutputStream(); ByteArrayOutputStream logStream = new ByteArrayOutputStream();
PrintStream logPrintStream = new PrintStream(logStream); PrintStream logPrintStream = new PrintStream(logStream);
@ -298,19 +298,32 @@ public class Controller {
continue; continue;
} }
// Check if the response body is a zip file
if (isZip(response.getBody())) { // Define filename
// Unzip the file and add all the files to the new output files String filename;
newOutputFiles.addAll(unzip(response.getBody())); if ("auto-rename".equals(operation)) {
} else { // If the operation is "auto-rename", generate a new filename.
Resource outputResource = new ByteArrayResource(response.getBody()) { // This is a simple example of generating a filename using current timestamp.
@Override // Modify as per your needs.
public String getFilename() { filename = "file_" + System.currentTimeMillis();
return file.getFilename(); // Preserving original filename } else {
} // Otherwise, keep the original filename.
}; filename = file.getFilename();
newOutputFiles.add(outputResource); }
}
// Check if the response body is a zip file
if (isZip(response.getBody())) {
// Unzip the file and add all the files to the new output files
newOutputFiles.addAll(unzip(response.getBody()));
} else {
Resource outputResource = new ByteArrayResource(response.getBody()) {
@Override
public String getFilename() {
return filename;
}
};
newOutputFiles.add(outputResource);
}
} }
if (!hasInputFileType) { if (!hasInputFileType) {

View file

@ -47,6 +47,11 @@ public class WatermarkController {
@RequestPart(required = true) @Parameter(description = "The watermark type (text or image)") String watermarkType, @RequestPart(required = true) @Parameter(description = "The watermark type (text or image)") String watermarkType,
@RequestPart(required = false) @Parameter(description = "The watermark text") String watermarkText, @RequestPart(required = false) @Parameter(description = "The watermark text") String watermarkText,
@RequestPart(required = false) @Parameter(description = "The watermark image") MultipartFile watermarkImage, @RequestPart(required = false) @Parameter(description = "The watermark image") MultipartFile watermarkImage,
@RequestParam(defaultValue = "roman", name = "alphabet") @Parameter(description = "The selected alphabet",
schema = @Schema(type = "string",
allowableValues = {"roman","arabic","japanese","korean","chinese"},
defaultValue = "roman")) String alphabet,
@RequestParam(defaultValue = "30", name = "fontSize") @Parameter(description = "The font size of the watermark text", example = "30") float fontSize, @RequestParam(defaultValue = "30", name = "fontSize") @Parameter(description = "The font size of the watermark text", example = "30") float fontSize,
@RequestParam(defaultValue = "0", name = "rotation") @Parameter(description = "The rotation of the watermark in degrees", example = "0") float rotation, @RequestParam(defaultValue = "0", name = "rotation") @Parameter(description = "The rotation of the watermark in degrees", example = "0") float rotation,
@RequestParam(defaultValue = "0.5", name = "opacity") @Parameter(description = "The opacity of the watermark (0.0 - 1.0)", example = "0.5") float opacity, @RequestParam(defaultValue = "0.5", name = "opacity") @Parameter(description = "The opacity of the watermark (0.0 - 1.0)", example = "0.5") float opacity,
@ -71,7 +76,7 @@ public class WatermarkController {
if (watermarkType.equalsIgnoreCase("text")) { if (watermarkType.equalsIgnoreCase("text")) {
addTextWatermark(contentStream, watermarkText, document, page, rotation, widthSpacer, heightSpacer, addTextWatermark(contentStream, watermarkText, document, page, rotation, widthSpacer, heightSpacer,
fontSize); fontSize, alphabet);
} else if (watermarkType.equalsIgnoreCase("image")) { } else if (watermarkType.equalsIgnoreCase("image")) {
addImageWatermark(contentStream, watermarkImage, document, page, rotation, widthSpacer, heightSpacer, addImageWatermark(contentStream, watermarkImage, document, page, rotation, widthSpacer, heightSpacer,
fontSize); fontSize);
@ -86,9 +91,41 @@ public class WatermarkController {
} }
private void addTextWatermark(PDPageContentStream contentStream, String watermarkText, PDDocument document, private void addTextWatermark(PDPageContentStream contentStream, String watermarkText, PDDocument document,
PDPage page, float rotation, int widthSpacer, int heightSpacer, float fontSize) throws IOException { PDPage page, float rotation, int widthSpacer, int heightSpacer, float fontSize, String alphabet) throws IOException {
// Set font and other properties for text watermark String resourceDir = "";
PDFont font = PDType1Font.HELVETICA_BOLD; PDFont font = PDType1Font.HELVETICA_BOLD;
switch (alphabet) {
case "arabic":
resourceDir = "static/fonts/NotoSansArabic-Regular.ttf";
break;
case "japanese":
resourceDir = "static/fonts/Meiryo.ttf";
break;
case "korean":
resourceDir = "static/fonts/malgun.ttf";
break;
case "chinese":
resourceDir = "static/fonts/SimSun.ttf";
break;
case "roman":
default:
resourceDir = "static/fonts/NotoSans-Regular.ttf";
break;
}
if(!resourceDir.equals("")) {
ClassPathResource classPathResource = new ClassPathResource(resourceDir);
String fileExtension = resourceDir.substring(resourceDir.lastIndexOf("."));
File tempFile = File.createTempFile("NotoSansFont", fileExtension);
try (InputStream is = classPathResource.getInputStream(); FileOutputStream os = new FileOutputStream(tempFile)) {
IOUtils.copy(is, os);
}
font = PDType0Font.load(document, tempFile);
tempFile.deleteOnExit();
}
contentStream.setFont(font, fontSize); contentStream.setFont(font, fontSize);
contentStream.setNonStrokingColor(Color.LIGHT_GRAY); contentStream.setNonStrokingColor(Color.LIGHT_GRAY);

View file

@ -44,7 +44,7 @@ public class PdfUtils {
public static PDRectangle textToPageSize(String size) { public static PDRectangle textToPageSize(String size) {
switch (size) { switch (size.toUpperCase()) {
case "A0": case "A0":
return PDRectangle.A0; return PDRectangle.A0;
case "A1": case "A1":

View file

@ -87,7 +87,7 @@ document.getElementById('submitConfigBtn').addEventListener('click', function()
let formData = new FormData(); let formData = new FormData();
let fileInput = document.getElementById('fileInput'); let fileInput = document.getElementById('fileInput-input');
let files = fileInput.files; let files = fileInput.files;
for (let i = 0; i < files.length; i++) { for (let i = 0; i < files.length; i++) {
@ -177,7 +177,11 @@ document.getElementById('addOperationBtn').addEventListener('click', function()
let listItem = document.createElement('li'); let listItem = document.createElement('li');
listItem.className = "list-group-item"; listItem.className = "list-group-item";
let hasSettings = (apiDocs[selectedOperation] && apiDocs[selectedOperation].post && let hasSettings = (apiDocs[selectedOperation] && apiDocs[selectedOperation].post &&
apiDocs[selectedOperation].post.parameters && apiDocs[selectedOperation].post.parameters.length > 0); ((apiDocs[selectedOperation].post.parameters && apiDocs[selectedOperation].post.parameters.length > 0) ||
(apiDocs[selectedOperation].post.requestBody &&
apiDocs[selectedOperation].post.requestBody.content['multipart/form-data'].schema.properties)));
listItem.innerHTML = ` listItem.innerHTML = `
@ -222,52 +226,77 @@ document.getElementById('addOperationBtn').addEventListener('click', function()
let pipelineSettingsModal = document.getElementById('pipelineSettingsModal'); let pipelineSettingsModal = document.getElementById('pipelineSettingsModal');
let pipelineSettingsContent = document.getElementById('pipelineSettingsContent'); let pipelineSettingsContent = document.getElementById('pipelineSettingsContent');
let operationData = apiDocs[operation].post.parameters || []; let operationData = apiDocs[operation].post.parameters || [];
let requestBodyData = apiDocs[operation].post.requestBody.content['multipart/form-data'].schema.properties || {};
// Combine operationData and requestBodyData into a single array
operationData = operationData.concat(Object.keys(requestBodyData).map(key => ({
name: key,
schema: requestBodyData[key]
})));
pipelineSettingsContent.innerHTML = ''; pipelineSettingsContent.innerHTML = '';
operationData.forEach(parameter => { operationData.forEach(parameter => {
let parameterDiv = document.createElement('div'); let parameterDiv = document.createElement('div');
parameterDiv.className = "form-group"; parameterDiv.className = "form-group";
let parameterLabel = document.createElement('label'); let parameterLabel = document.createElement('label');
parameterLabel.textContent = `${parameter.name} (${parameter.schema.type}): `; parameterLabel.textContent = `${parameter.name} (${parameter.schema.type}): `;
parameterLabel.title = parameter.description; parameterLabel.title = parameter.description;
parameterDiv.appendChild(parameterLabel); parameterDiv.appendChild(parameterLabel);
let parameterInput; let parameterInput;
switch (parameter.schema.type) {
case 'string': // check if enum exists in schema
case 'number': if (parameter.schema.enum) {
case 'integer': // if enum exists, create a select element
parameterInput = document.createElement('input'); parameterInput = document.createElement('select');
parameterInput.type = parameter.schema.type === 'string' ? 'text' : 'number'; parameterInput.className = "form-control";
parameterInput.className = "form-control";
break; // iterate over each enum value and create an option for it
case 'boolean': parameter.schema.enum.forEach(value => {
parameterInput = document.createElement('input'); let option = document.createElement('option');
parameterInput.type = 'checkbox'; option.value = value;
break; option.text = value;
case 'array': parameterInput.appendChild(option);
case 'object': });
parameterInput = document.createElement('textarea'); } else {
parameterInput.placeholder = `Enter a JSON formatted ${parameter.schema.type}`; // switch-case statement for handling non-enum types
parameterInput.className = "form-control"; switch (parameter.schema.type) {
break; case 'string':
case 'enum': if (parameter.schema.format === 'binary') {
parameterInput = document.createElement('select'); // This is a file input
parameterInput.className = "form-control"; parameterInput = document.createElement('input');
parameter.schema.enum.forEach(option => { parameterInput.type = 'file';
let optionElement = document.createElement('option'); parameterInput.className = "form-control";
optionElement.value = option; } else {
optionElement.text = option; parameterInput = document.createElement('input');
parameterInput.appendChild(optionElement); parameterInput.type = 'text';
}); parameterInput.className = "form-control";
break; }
default: break;
parameterInput = document.createElement('input'); case 'number':
parameterInput.type = 'text'; case 'integer':
parameterInput.className = "form-control"; parameterInput = document.createElement('input');
} parameterInput.type = '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;
default:
parameterInput = document.createElement('input');
parameterInput.type = 'text';
parameterInput.className = "form-control";
}
}
parameterInput.id = parameter.name; parameterInput.id = parameter.name;
if (operationSettings[operation] && operationSettings[operation][parameter.name] !== undefined) { if (operationSettings[operation] && operationSettings[operation][parameter.name] !== undefined) {

View file

@ -3,98 +3,122 @@
th:lang-direction="#{language.direction}" th:lang-direction="#{language.direction}"
xmlns:th="http://www.thymeleaf.org"> xmlns:th="http://www.thymeleaf.org">
<th:block th:insert="~{fragments/common :: head(title=#{pipeline.title})}"></th:block> <th:block
<body> th:insert="~{fragments/common :: head(title=#{pipeline.title})}"></th:block>
<div id="page-container"> <style>
<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">
<!-- Trigger/Open The Modal -->
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#pipelineSettingsModal">
Open Pipeline Settings
</button>
<button id="uploadPipelineBtn" class="btn btn-primary">Upload Custom Pipeline</button>
<select id="pipelineSelect">
<option value="">Select a pipeline</option>
<th:block th:each="config : ${pipelineConfigsWithNames}">
<option th:value="${config.json}" th:text="${config.name}"></option>
</th:block>
</select>
<input type="file" id="fileInput" multiple>
<button class="btn btn-primary" id="submitConfigBtn">Submit</button>
<!-- The Modal -->
<div class="modal" id="pipelineSettingsModal">
<div class="modal-dialog">
<div class="modal-content">
<!-- Modal Header -->
<div class="modal-header">
<h2 class="modal-title">Pipeline Configuration</h2>
<button type="button" class="close" data-dismiss="modal">&times;</button>
</div>
<!-- Modal body -->
<div class="modal-body">
<div class="mb-3">
<label for="pipelineName" class="form-label">Pipeline Name</label>
<input type="text" id="pipelineName" class="form-control" placeholder="Enter pipeline name here">
</div>
<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</button>
</div>
<h3>Pipeline:</h3>
<ol id="pipelineList" class="list-group">
<!-- Pipeline operations will be dynamically populated here -->
</ol>
<div id="pipelineSettingsContent">
<!-- pipelineSettings will be dynamically populated here -->
</div>
</div>
<!-- Modal footer -->
<div class="modal-footer">
<button id="savePipelineBtn" class="btn btn-success">Download</button>
<button id="validateButton" class="btn btn-success">Validate</button>
<div class="btn-group">
<input type="file" id="uploadPipelineInput" accept=".json" style="display: none;">
</div>
</div>
</div>
</div>
</div>
<script src="js/pipeline.js"></script>
</div>
</div>
</div>
<style>
.btn-margin { .btn-margin {
margin-right: 2px; margin-right: 2px;
} }
.bordered-box {
border: 1px solid #ddd;
padding: 20px;
margin: 20px;
width: 70%;
}
.center-element {
width: 80%;
text-align: center;
margin: auto;
}
.element-margin {
margin: 10px 0; /* Adjust this value to increase/decrease the margin as needed */
}
</style> </style>
<body>
<div id="page-container">
<div id="content-wrap">
<div th:insert="~{fragments/navbar.html :: navbar}"></div>
<br> <br>
<div class="container">
<div class="row justify-content-center">
<div class="bordered-box">
<div class="text-right text-top">
<button id="uploadPipelineBtn" class="btn btn-primary">Upload
Custom</button>
<button type="button" class="btn btn-primary" data-toggle="modal"
data-target="#pipelineSettingsModal">Configure</button>
</div>
<div class="center-element">
<div class="element-margin">
<select id="pipelineSelect" class="custom-select">
<option value="">Select a pipeline</option>
<th:block th:each="config : ${pipelineConfigsWithNames}">
<option th:value="${config.json}" th:text="${config.name}"></option>
</th:block>
</select>
</div>
<div class="element-margin">
<div th:replace="~{fragments/common :: fileSelector(name='fileInput', multiple=true)}"></div>
</div>
<div class="element-margin">
<button class="btn btn-primary" id="submitConfigBtn">Submit</button>
</div>
</div>
</div>
<!-- The Modal -->
<div class="modal" id="pipelineSettingsModal">
<div class="modal-dialog">
<div class="modal-content">
<!-- Modal Header -->
<div class="modal-header">
<h2 class="modal-title">Pipeline Configuration</h2>
<button type="button" class="close" data-dismiss="modal">&times;</button>
</div>
<!-- Modal body -->
<div class="modal-body">
<div class="mb-3">
<label for="pipelineName" class="form-label">Pipeline
Name</label> <input type="text" id="pipelineName"
class="form-control" placeholder="Enter pipeline name here">
</div>
<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</button>
</div>
<h3>Pipeline:</h3>
<ol id="pipelineList" class="list-group">
<!-- Pipeline operations will be dynamically populated here -->
</ol>
<div id="pipelineSettingsContent">
<!-- pipelineSettings will be dynamically populated here -->
</div>
</div>
<!-- Modal footer -->
<div class="modal-footer">
<button id="savePipelineBtn" class="btn btn-success">Download</button>
<button id="validateButton" class="btn btn-success">Validate</button>
<div class="btn-group">
<input type="file" id="uploadPipelineInput" accept=".json"
style="display: none;">
</div>
</div>
</div>
</div>
</div>
<script src="js/pipeline.js"></script>
</div>
</div>
</div>
<div th:insert="~{fragments/footer.html :: footer}"></div> <div th:insert="~{fragments/footer.html :: footer}"></div>
</div> </div>