Skip to content

Feature/s3integration#4

Open
gurkvatten wants to merge 12 commits intomainfrom
feature/s3integration
Open

Feature/s3integration#4
gurkvatten wants to merge 12 commits intomainfrom
feature/s3integration

Conversation

@gurkvatten
Copy link
Copy Markdown
Contributor

@gurkvatten gurkvatten commented Apr 1, 2026

Summary by CodeRabbit

  • New Features
    • Optional file upload when creating reports (form now supports multipart uploads and a file input)
    • Reports now store generated PDFs and attached files in cloud-backed storage for later retrieval
    • Download reports as generated PDFs
    • Download uploaded files attached to reports
    • View a list of all reports via a new reports listing endpoint
    • Save failures during upload surface an error message on the submission form

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 1, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds S3/MinIO-backed storage: Report gains persisted S3 keys; S3 client and bucket-init config added; service generates/uploads PDFs and optional files, persists S3 keys, exposes downloads and listing; controller and UI updated for multipart uploads and new endpoints. (≈34 words)

Changes

Cohort / File(s) Summary
Report Entity & Mapper
src/main/java/org/example/crimearchive/bevis/Report.java, src/main/java/org/example/crimearchive/mapper/ReportMapper.java
Added persisted fields s3KeyPdf and s3KeyFile to Report (constructor, getters/setters). ReportMapper.toEntity signature changed to accept s3KeyPdf and s3KeyFile.
S3/MinIO Config
src/main/java/org/example/crimearchive/config/S3Config.java, src/main/resources/application.properties
New Spring S3Config registers an S3Client and an ApplicationRunner that checks/creates the configured bucket. Added minio.* properties.
Service Layer
src/main/java/org/example/crimearchive/service/ReportService.java
Injects S3Client and reads minio.bucket. saveReport(...) now generates a PDF, uploads PDF and optional file to S3 with randomized keys, persists via mapper with S3 keys, attempts S3 cleanup on failure, and throws IOException. Added downloadPdf(UUID), downloadFile(UUID), and getAllReportResponses().
Controller & API
src/main/java/org/example/crimearchive/controllers/HomeController.java
Made reportService private final. saveReport(...) accepts an optional MultipartFile, handles IOException and model binding. Added endpoints: GET /reports, GET /reports/{uuid}/download/pdf, GET /reports/{uuid}/download/file.
UI Template
src/main/resources/templates/private.html
Form updated to enctype="multipart/form-data" and includes a file input named file.
DTO
src/main/java/org/example/crimearchive/DTO/ReportResponse.java
Added ReportResponse record (UUID uuid, String name, String event) for API responses.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant HomeController
    participant ReportService
    participant iText as iText_PDF
    participant S3Client
    participant S3 as S3_Storage
    participant Database

    Client->>HomeController: POST /reports/add (form + optional file)
    HomeController->>ReportService: saveReport(CreateReport, MultipartFile)
    ReportService->>iText_PDF: Generate PDF bytes
    iText_PDF-->>ReportService: PDF bytes
    ReportService->>S3Client: PUT reports/pdf/{uuid}.pdf (PDF bytes)
    S3Client->>S3_Storage: Store object
    S3_Storage-->>S3Client: OK (object key)
    alt file provided
        ReportService->>S3Client: PUT reports/files/{uuid}.{ext} (file bytes)
        S3Client->>S3_Storage: Store object
        S3_Storage-->>S3Client: OK (object key)
    end
    ReportService->>Database: Save Report(entity with s3KeyPdf,s3KeyFile)
    Database-->>ReportService: Persisted
    ReportService-->>HomeController: Complete
    HomeController-->>Client: Redirect/Response
Loading
sequenceDiagram
    participant Client
    participant HomeController
    participant ReportService
    participant Database
    participant S3Client
    participant S3 as S3_Storage

    Client->>HomeController: GET /reports/{uuid}/download/pdf
    HomeController->>ReportService: downloadPdf(UUID)
    ReportService->>Database: Find report by UUID
    Database-->>ReportService: Report (s3KeyPdf)
    ReportService->>S3Client: GET object (s3KeyPdf)
    S3Client->>S3_Storage: Retrieve object
    S3_Storage-->>S3Client: File bytes
    S3Client-->>ReportService: bytes
    ReportService-->>HomeController: ResponseEntity<byte[]> (application/pdf)
    HomeController-->>Client: PDF file download
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐇 I hopped through code with nimble paws,
PDFs and files tucked into S3 jaws,
Buckets hum where bytes now sleep,
Endpoints fetch what memories keep,
A rabbit cheers this storage cause!

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Feature/s3integration' is vague and uses generic naming convention rather than clearly summarizing the main change from a user/reviewer perspective. Use a more descriptive title that clearly indicates the main feature, such as 'Add S3/MinIO integration for PDF and file storage' or 'Integrate AWS S3 storage for report attachments'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/s3integration

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/org/example/crimearchive/config/S3Config.java`:
- Around line 45-60: The createBucket ApplicationRunner currently swallows
non-404 S3Exception and only logs to stderr, allowing startup to succeed; change
the catch in S3Config.createBucket so that for non-404 S3Exception you re-throw
the exception (or wrap it in a RuntimeException) instead of just logging,
ensuring the ApplicationRunner fails and Spring stops startup on bucket
access/permission/credential errors.

In `@src/main/java/org/example/crimearchive/controllers/HomeController.java`:
- Around line 44-46: The getAllReports endpoint in HomeController currently
returns Report entities directly (HomeController.getAllReports ->
ResponseEntity<List<Report>>), which exposes internal S3 object names via
Report.getS3KeyPdf() and Report.getS3KeyFile(); fix by either creating and
returning a ReportDTO with only public fields from HomeController.getAllReports
(map Report -> ReportDTO before ResponseEntity.ok) or annotate the Report
accessors getS3KeyPdf and getS3KeyFile with `@JsonIgnore` so those fields are not
serialized in the JSON response. Ensure mapping is applied consistently wherever
Reports are serialized.

In `@src/main/java/org/example/crimearchive/service/ReportService.java`:
- Around line 89-103: In ReportService, update downloadPdf (and similarly
downloadFile) to throw ResponseStatusException(HttpStatus.NOT_FOUND, "...")
instead of RuntimeException when simpleRepository.findById(uuid) returns empty
or when report.getS3KeyPdf() is null, and wrap or catch S3 client errors (e.g.,
S3Exception/NoSuchKey thrown by s3Client.getObjectAsBytes) to rethrow as
ResponseStatusException(HttpStatus.NOT_FOUND, "...") so missing S3 objects
return 404; ensure you add the import for
org.springframework.web.server.ResponseStatusException and HttpStatus and apply
the same pattern in the downloadFile method.
- Around line 42-86: Mark saveReport as transactional (add `@Transactional` on the
method or class) and ensure any S3 uploads are cleaned up if the DB save fails:
after uploading PDF (s3KeyPdf) and optional file (s3KeyFile) via
s3Client.putObject, wrap the simpleRepository.save(…) call in a try/catch (or
use transaction rollback listener) and on failure call s3Client.deleteObject for
s3KeyPdf and s3KeyFile if they were uploaded; also propagate the original
exception as IOException so callers see the failure. Ensure you reference the
existing variables s3KeyPdf, s3KeyFile, s3Client.putObject and
simpleRepository.save when implementing the compensating deletes.
- Line 57: The S3 key construction in ReportService (variables s3KeyPdf and the
use of file.getOriginalFilename()) embeds user input; change to generate opaque
UUID-only keys (e.g., "reports/pdf/{UUID}.pdf" and "reports/uploads/{UUID}") and
stop persisting original filenames into the key. Instead persist a separate
sanitized display name field in the report entity (store the original filename
after sanitizing/truncating) and use that display field when showing download
names. Also ensure the code path that sets the Content-Disposition header (the
code around where the filename is read for the header) performs proper
sanitization/quoting and length truncation before inserting into the header to
prevent header injection and overly long values.

In `@src/main/resources/application.properties`:
- Around line 6-9: Remove hardcoded MinIO endpoint and credentials from
application.properties: delete or blank the values for minio.endpoint,
minio.access-key, and minio.secret-key in the shared default file and instead
read them from environment variables (e.g., MINIO_ENDPOINT, MINIO_ACCESS_KEY,
MINIO_SECRET_KEY) or place them into a local-only Spring profile (e.g.,
application-local.properties) that is not checked into source control; keep
minio.bucket if it is a safe default, otherwise also move it to env/profile and
ensure the code that loads these values (the Spring property binding for
minio.endpoint/minio.access-key/minio.secret-key) still resolves from env or
active profile.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b3aaa37e-ea16-4418-8a21-77a6775f84a8

📥 Commits

Reviewing files that changed from the base of the PR and between 6d43db4 and ca46689.

📒 Files selected for processing (7)
  • src/main/java/org/example/crimearchive/bevis/Report.java
  • src/main/java/org/example/crimearchive/config/S3Config.java
  • src/main/java/org/example/crimearchive/controllers/HomeController.java
  • src/main/java/org/example/crimearchive/mapper/ReportMapper.java
  • src/main/java/org/example/crimearchive/service/ReportService.java
  • src/main/resources/application.properties
  • src/main/resources/templates/private.html

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
src/main/java/org/example/crimearchive/service/ReportService.java (2)

112-117: ⚠️ Potential issue | 🟠 Major

S3 getObjectAsBytes exceptions will result in HTTP 500 instead of 404.

If the S3 object doesn't exist or is inaccessible, s3Client.getObjectAsBytes() throws S3Exception (or NoSuchKeyException), which isn't caught and will propagate as a 500 error. Wrap in try-catch and convert to ResponseStatusException with 404.

Proposed fix
+import software.amazon.awssdk.services.s3.model.S3Exception;
...
-        ResponseBytes<GetObjectResponse> objectBytes = s3Client.getObjectAsBytes(
-                GetObjectRequest.builder()
-                        .bucket(bucket)
-                        .key(report.getS3KeyPdf())
-                        .build()
-        );
+        ResponseBytes<GetObjectResponse> objectBytes;
+        try {
+            objectBytes = s3Client.getObjectAsBytes(
+                    GetObjectRequest.builder()
+                            .bucket(bucket)
+                            .key(report.getS3KeyPdf())
+                            .build()
+            );
+        } catch (S3Exception e) {
+            throw new ResponseStatusException(HttpStatus.NOT_FOUND, 
+                    "PDF-filen kunde inte hämtas från lagring");
+        }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/crimearchive/service/ReportService.java` around
lines 112 - 117, Wrap the S3 download call in ReportService that invokes
s3Client.getObjectAsBytes(...) in a try-catch and convert S3 errors to a 404
ResponseStatusException: catch S3Exception (and NoSuchKeyException if used)
around the getObjectAsBytes call and throw new
ResponseStatusException(HttpStatus.NOT_FOUND, "Report PDF not found for key: " +
report.getS3KeyPdf(), e) so missing/inaccessible objects return 404 instead of
propagating a 500.

134-143: ⚠️ Potential issue | 🟠 Major

Same S3 exception issue; also missing Content-Type header.

  1. The s3Client.getObjectAsBytes() call has the same unhandled exception issue as in downloadPdf.
  2. The response doesn't set a Content-Type, which may cause browsers to mishandle the file. Consider using MediaType.APPLICATION_OCTET_STREAM as a fallback or retrieving the content type from the S3 object metadata.
Proposed fix
-        ResponseBytes<GetObjectResponse> objectBytes = s3Client.getObjectAsBytes(
-                GetObjectRequest.builder()
-                        .bucket(bucket)
-                        .key(report.getS3KeyFile())
-                        .build()
-        );
-
-        return ResponseEntity.ok()
-                .header("Content-Disposition", "attachment; filename=fil")
-                .body(objectBytes.asByteArray());
+        ResponseBytes<GetObjectResponse> objectBytes;
+        try {
+            objectBytes = s3Client.getObjectAsBytes(
+                    GetObjectRequest.builder()
+                            .bucket(bucket)
+                            .key(report.getS3KeyFile())
+                            .build()
+            );
+        } catch (S3Exception e) {
+            throw new ResponseStatusException(HttpStatus.NOT_FOUND, 
+                    "Filen kunde inte hämtas från lagring");
+        }
+
+        String contentType = objectBytes.response().contentType();
+        return ResponseEntity.ok()
+                .contentType(MediaType.parseMediaType(
+                        contentType != null ? contentType : MediaType.APPLICATION_OCTET_STREAM_VALUE))
+                .header("Content-Disposition", "attachment; filename=fil")
+                .body(objectBytes.asByteArray());
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/crimearchive/service/ReportService.java` around
lines 134 - 143, Wrap the s3Client.getObjectAsBytes(...) call in the same
exception handling used in downloadPdf so S3 errors are caught and translated to
a meaningful HTTP response; specifically guard the call that produces
ResponseBytes<GetObjectResponse> objectBytes and handle S3Exceptions and
IOExceptions around objectBytes.asByteArray(). Also set an explicit Content-Type
on the ResponseEntity (use MediaType.APPLICATION_OCTET_STREAM as a fallback or
retrieve the content type from the S3 object's metadata/GetObjectResponse) and
fix the Content-Disposition header (currently "attachment; filename=fil") to use
the real filename (e.g., from report.getS3KeyFile() or a report.getFilename()
field) when building the ResponseEntity.
🧹 Nitpick comments (1)
src/main/java/org/example/crimearchive/service/ReportService.java (1)

52-61: iText Document resource not properly closed on failure.

If an exception occurs between document.open() (line 56) and document.close() (line 61), the document may not be properly released. Use try-finally or move close to a finally block.

Proposed fix with proper resource handling
         try {
             ByteArrayOutputStream pdfStream = new ByteArrayOutputStream();
             Document document = new Document();
-            PdfWriter.getInstance(document, pdfStream);
-            document.open();
-            document.add(new Paragraph("Brottsanmälan"));
-            document.add(new Paragraph("Namn: " + report.name()));
-            document.add(new Paragraph("Brottstyp: " + report.event()));
-            document.add(new Paragraph("Datum: " + LocalDateTime.now()));
-            document.close();
+            try {
+                PdfWriter.getInstance(document, pdfStream);
+                document.open();
+                document.add(new Paragraph("Brottsanmälan"));
+                document.add(new Paragraph("Namn: " + report.name()));
+                document.add(new Paragraph("Brottstyp: " + report.event()));
+                document.add(new Paragraph("Datum: " + LocalDateTime.now()));
+            } finally {
+                document.close();
+            }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/crimearchive/service/ReportService.java` around
lines 52 - 61, The try block in ReportService that creates and writes the iText
Document (variables pdfStream and document, and call PdfWriter.getInstance(...))
doesn't guarantee document.close() if an exception is thrown; wrap document
creation/open/write in a try-finally (or use try-with-resources if using a
compatible iText AutoCloseable Document) so that document.close() is always
called in the finally block, and ensure pdfStream is also closed/handled after
use; update the block around PdfWriter.getInstance, document.open(),
document.add(...), and document.close() to guarantee proper closing on failure.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/org/example/crimearchive/controllers/HomeController.java`:
- Around line 37-43: The saveReport method in HomeController currently lets
IOException from reportService.saveReport propagate, causing HTTP 500; modify
HomeController.saveReport to catch IOException around the
reportService.saveReport(newReport, file) call, add a user-friendly error
message to the model (or flash attributes) and return an appropriate view (e.g.,
an error page or the report form view) instead of throwing; ensure you reference
the same parameters (CreateReport newReport and MultipartFile file) and keep the
successful path returning "redirect:/".

In `@src/main/java/org/example/crimearchive/service/ReportService.java`:
- Around line 90-98: The catch block in ReportService that performs S3 cleanup
(references: variables s3KeyPdf, s3KeyFile and calls
s3Client.deleteObject(DeleteObjectRequest.builder()...)) can throw and mask the
original exception e; update this block to perform each s3Client.deleteObject
call inside its own try-catch (or a single try that catches and logs/suppresses
cleanup exceptions) so that any exception thrown during deletion does not
overwrite or replace the original exception e — ensure the original exception is
rethrown (or logged/propagated) after cleanup attempts and any cleanup errors
are handled separately.

---

Duplicate comments:
In `@src/main/java/org/example/crimearchive/service/ReportService.java`:
- Around line 112-117: Wrap the S3 download call in ReportService that invokes
s3Client.getObjectAsBytes(...) in a try-catch and convert S3 errors to a 404
ResponseStatusException: catch S3Exception (and NoSuchKeyException if used)
around the getObjectAsBytes call and throw new
ResponseStatusException(HttpStatus.NOT_FOUND, "Report PDF not found for key: " +
report.getS3KeyPdf(), e) so missing/inaccessible objects return 404 instead of
propagating a 500.
- Around line 134-143: Wrap the s3Client.getObjectAsBytes(...) call in the same
exception handling used in downloadPdf so S3 errors are caught and translated to
a meaningful HTTP response; specifically guard the call that produces
ResponseBytes<GetObjectResponse> objectBytes and handle S3Exceptions and
IOExceptions around objectBytes.asByteArray(). Also set an explicit Content-Type
on the ResponseEntity (use MediaType.APPLICATION_OCTET_STREAM as a fallback or
retrieve the content type from the S3 object's metadata/GetObjectResponse) and
fix the Content-Disposition header (currently "attachment; filename=fil") to use
the real filename (e.g., from report.getS3KeyFile() or a report.getFilename()
field) when building the ResponseEntity.

---

Nitpick comments:
In `@src/main/java/org/example/crimearchive/service/ReportService.java`:
- Around line 52-61: The try block in ReportService that creates and writes the
iText Document (variables pdfStream and document, and call
PdfWriter.getInstance(...)) doesn't guarantee document.close() if an exception
is thrown; wrap document creation/open/write in a try-finally (or use
try-with-resources if using a compatible iText AutoCloseable Document) so that
document.close() is always called in the finally block, and ensure pdfStream is
also closed/handled after use; update the block around PdfWriter.getInstance,
document.open(), document.add(...), and document.close() to guarantee proper
closing on failure.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1d294d4c-50b4-4fbe-ba9a-88b83efd0e79

📥 Commits

Reviewing files that changed from the base of the PR and between ca46689 and 92dc9aa.

📒 Files selected for processing (4)
  • src/main/java/org/example/crimearchive/DTO/ReportResponse.java
  • src/main/java/org/example/crimearchive/config/S3Config.java
  • src/main/java/org/example/crimearchive/controllers/HomeController.java
  • src/main/java/org/example/crimearchive/service/ReportService.java
✅ Files skipped from review due to trivial changes (1)
  • src/main/java/org/example/crimearchive/DTO/ReportResponse.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/java/org/example/crimearchive/config/S3Config.java

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/main/java/org/example/crimearchive/controllers/HomeController.java (1)

57-65: Add exception handling for S3 client operations in the service layer.

The downloadPdf and downloadFile methods (lines 112-122 and 134-139 in ReportService) call s3Client.getObjectAsBytes() without catching S3Exception or SdkClientException. While NOT_FOUND cases are properly handled via ResponseStatusException, infrastructure errors (network failures, bucket access issues) will propagate as unhandled 500 errors, potentially exposing stack traces.

Wrap S3 calls in try-catch blocks to return appropriate error responses (e.g., 503 Service Unavailable for infrastructure failures), or add a @ControllerAdvice to handle S3 exceptions globally.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/org/example/crimearchive/controllers/HomeController.java`
around lines 57 - 65, ReportService currently calls s3Client.getObjectAsBytes()
from methods downloadPdf and downloadFile without catching S3Exception or
SdkClientException, so infrastructure errors bubble up; modify the ReportService
implementations of downloadPdf(UUID) and downloadFile(UUID) to wrap the
s3Client.getObjectAsBytes(...) calls in try-catch blocks catching
software.amazon.awssdk.services.s3.model.S3Exception and
software.amazon.awssdk.core.exception.SdkClientException, map S3Exception (e.g.,
404 already handled) and other SDK/infrastructure errors to an appropriate
ResponseStatusException (for example HttpStatus.SERVICE_UNAVAILABLE with a clear
message) or rethrow a custom exception that a new `@ControllerAdvice` handles and
converts to 503, and ensure the controller methods downloadPdf and downloadFile
still return the same ResponseEntity<byte[]> from reportService.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/main/java/org/example/crimearchive/controllers/HomeController.java`:
- Around line 57-65: ReportService currently calls s3Client.getObjectAsBytes()
from methods downloadPdf and downloadFile without catching S3Exception or
SdkClientException, so infrastructure errors bubble up; modify the ReportService
implementations of downloadPdf(UUID) and downloadFile(UUID) to wrap the
s3Client.getObjectAsBytes(...) calls in try-catch blocks catching
software.amazon.awssdk.services.s3.model.S3Exception and
software.amazon.awssdk.core.exception.SdkClientException, map S3Exception (e.g.,
404 already handled) and other SDK/infrastructure errors to an appropriate
ResponseStatusException (for example HttpStatus.SERVICE_UNAVAILABLE with a clear
message) or rethrow a custom exception that a new `@ControllerAdvice` handles and
converts to 503, and ensure the controller methods downloadPdf and downloadFile
still return the same ResponseEntity<byte[]> from reportService.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 16828447-75d3-42da-a474-8707c131841e

📥 Commits

Reviewing files that changed from the base of the PR and between 92dc9aa and 388b85f.

📒 Files selected for processing (1)
  • src/main/java/org/example/crimearchive/controllers/HomeController.java

@gurkvatten gurkvatten marked this pull request as draft April 2, 2026 08:54
@gurkvatten gurkvatten marked this pull request as ready for review April 2, 2026 15:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant