From 3b683018de306faba330919155aa416fa1762279 Mon Sep 17 00:00:00 2001 From: Jan Date: Sat, 13 Sep 2025 22:03:51 +0200 Subject: [PATCH] FRONTEND: bulk download mostly working --- src/frontend/src/backend.js | 30 +++++++++++++++---- .../layout/config/BulkOperations.vue | 8 ++--- .../bulk/BulkOperationController.java | 8 ++--- 3 files changed, 32 insertions(+), 14 deletions(-) diff --git a/src/frontend/src/backend.js b/src/frontend/src/backend.js index 5f77966..f1d1007 100644 --- a/src/frontend/src/backend.js +++ b/src/frontend/src/backend.js @@ -29,13 +29,24 @@ const performDownload = async (url, expectResponse = true, expectedException = n method: 'GET', }; - const request = {url: url, params: params, expectResponse: expectResponse, expectedException: expectedException}; + const request = {url: url, params: params, expectResponse: expectResponse, expectedException: expectedException, type: 'blob'}; logger.info("Request:", request); - const processId = await executeRequest(null, request); + const blob = await executeRequest(null, request); - logger.info("Response:", processId); - return processId; + + const downloadUrl = window.URL.createObjectURL(blob); + + // Create temporary link element and trigger download + const link = document.createElement('a'); + link.href = downloadUrl; + link.download = `export.xlsx`; // or get filename from response headers + document.body.appendChild(link); + link.click(); + + // Clean up + document.body.removeChild(link); + window.URL.revokeObjectURL(downloadUrl); } const performUpload = async (url, file, expectResponse = true, expectedException = null) => { @@ -95,7 +106,14 @@ const executeRequest = async (requestingStore, request) => { let data = null; if (request.expectResponse) { - data = await response.json().catch(e => { + try { + if (request.type === 'blob' && response.ok) { + data = await response.blob(); + } else { + data = await response.json(); + } + + } catch (e) { const error = { code: 'Malformed response', message: "Malformed server response. Please contact support.", @@ -106,7 +124,7 @@ const executeRequest = async (requestingStore, request) => { const errorStore = useErrorStore(); void errorStore.addError(error, {store: requestingStore, request: request}); throw e; - }); + } if (!response.ok) { handleErrorResponse(data, requestingStore, request); diff --git a/src/frontend/src/components/layout/config/BulkOperations.vue b/src/frontend/src/components/layout/config/BulkOperations.vue index 5fa2fc8..d274548 100644 --- a/src/frontend/src/components/layout/config/BulkOperations.vue +++ b/src/frontend/src/components/layout/config/BulkOperations.vue @@ -8,8 +8,8 @@
Export
type
- empty template - full data export + empty template + full data export
dataset
@@ -83,7 +83,7 @@ export default { components: {RadioOption, BasicButton, Box}, data() { return { - exportType: "empty", + exportType: "templates", exportDataset: "NODE", importDataset: "NODE", importType: "APPEND", @@ -95,8 +95,8 @@ export default { }, methods: { async downloadFile() { - const url = `${config.backendUrl}/bulk/upload/${this.exportDataset}/${this.exportType}/` + const url = `${config.backendUrl}/bulk/${this.exportType}/${this.exportDataset}/` this.processId = await performDownload(url); }, inputFile(event) { diff --git a/src/main/java/de/avatic/lcc/controller/bulk/BulkOperationController.java b/src/main/java/de/avatic/lcc/controller/bulk/BulkOperationController.java index 3073d32..60b7bc5 100644 --- a/src/main/java/de/avatic/lcc/controller/bulk/BulkOperationController.java +++ b/src/main/java/de/avatic/lcc/controller/bulk/BulkOperationController.java @@ -41,7 +41,7 @@ public class BulkOperationController { * @param id The unique identifier of the operation (processing_id) to check its status. * @return A ResponseEntity with the bulk processing status payload. */ - @GetMapping("/status/{processing_id}") + @GetMapping({"/status/{processing_id}","/status/{processing_id}/"}) public ResponseEntity getUploadStatus(@PathVariable("processing_id") Integer id) { return ResponseEntity.ok(bulkProcessingService.getStatus(id)); } @@ -68,7 +68,7 @@ public class BulkOperationController { * specifying it as a downloadable file. * @throws IllegalArgumentException if the file type is invalid. */ - @GetMapping("/templates/{type}") + @GetMapping({"/templates/{type}","/templates/{type}/"}) public ResponseEntity generateTemplate(@PathVariable BulkFileType type) { HttpHeaders headers = new HttpHeaders(); headers.add("Content-Disposition", "attachment; filename=lcc_template_" + type.name().toLowerCase() + ".xlsx"); @@ -88,7 +88,7 @@ public class BulkOperationController { * The file is served as an Excel document, with proper headers for download. * @throws IllegalArgumentException if the provided file type is not supported. */ - @GetMapping("/download/{type}") + @GetMapping({"/download/{type}", "/download/{type}/"}) public ResponseEntity downloadFile(@PathVariable BulkFileType type) throws IOException { HttpHeaders headers = new HttpHeaders(); headers.add("Content-Disposition", "attachment; filename=lcc_export_" + type.name().toLowerCase() + ".xlsx"); @@ -109,7 +109,7 @@ public class BulkOperationController { * The file is served as an Excel document, with appropriate headers for download. * @throws IllegalArgumentException if the file type or validity period ID is invalid. */ - @GetMapping("/download/{type}/{validity_period_id}") + @GetMapping({"/download/{type}/{validity_period_id}","/download/{type}/{validity_period_id}/"}) public ResponseEntity downloadFile(@PathVariable BulkFileType type, @PathVariable("validity_period_id") Integer validityPeriodId) throws IOException { HttpHeaders headers = new HttpHeaders(); headers.add("Content-Disposition", "attachment; filename=lcc_export_" + type.name().toLowerCase() + ".xlsx");