Skip to content

Conversation

ravishanigarapu
Copy link
Member

@ravishanigarapu ravishanigarapu commented Sep 3, 2025

πŸ“‹ Description

JIRA ID:

Please provide a summary of the change and the motivation behind it. Include relevant context and details.


βœ… Type of Change

  • 🐞 Bug fix (non-breaking change which resolves an issue)
  • ✨ New feature (non-breaking change which adds functionality)
  • πŸ”₯ Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • πŸ›  Refactor (change that is neither a fix nor a new feature)
  • βš™οΈ Config change (configuration file or build script updates)
  • πŸ“š Documentation (updates to docs or readme)
  • πŸ§ͺ Tests (adding new or updating existing tests)
  • 🎨 UI/UX (changes that affect the user interface)
  • πŸš€ Performance (improves performance)
  • 🧹 Chore (miscellaneous changes that don't modify src or test files)

ℹ️ Additional Information

Please describe how the changes were tested, and include any relevant screenshots, logs, or other information that provides additional context.

Summary by CodeRabbit

  • Bug Fixes
    • Downloads now use proper attachment headers with correct file names (UTF-8 and URL-encoded).
    • More reliable MIME type detection; defaults safely when the file type is invalid or missing.
    • Accurate file size reported for downloads to prevent truncated or failing transfers.
    • Improved error handling with clearer, user-facing messages on failures.

Copy link
Contributor

coderabbitai bot commented Sep 3, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

The controller’s file download method was refactored to build an attachment response with URL-encoded filenames, explicit UTF-8 handling, robust MIME type parsing with fallbacks, accurate content length, and revised error propagation. An unused dependency was removed. No public method signatures changed.

Changes

Cohort / File(s) Change summary
Download response overhaul
src/main/java/com/iemr/common/controller/users/EmployeeSignatureController.java
Reworked fetch logic to return an attachment with Content-Disposition, URL-encoded filename, UTF-8 handling; added MIME type parsing with fallback to application/octet-stream; set content length from byte[]; replaced inline response building with headers-based ResponseEntity; removed unused InputMapper; updated error handling to throw on failure.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Client
  participant Controller as EmployeeSignatureController
  participant MIME as MIME Parser
  participant HTTP as HTTP Response

  Client->>Controller: GET /employee/signature/{id}
  activate Controller
  Controller->>Controller: Retrieve signature bytes, filename, fileType
  alt Parse MIME type
    Controller->>MIME: parse(fileType)
    MIME-->>Controller: MediaType
  else Invalid/Null fileType
    Controller->>Controller: Use application/octet-stream
  end
  Controller->>Controller: URL-encode filename (UTF-8)
  Controller->>HTTP: Build headers (Content-Type, Content-Length, Content-Disposition: attachment)
  deactivate Controller
  HTTP-->>Client: 200 OK + bytes

  opt Error
    Client->>Controller: GET /employee/signature/{id}
    activate Controller
    Controller->>Controller: Exception occurs
    Controller-->>Client: Propagated exception (non-200)
    deactivate Controller
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

In burrows of code I twitch my nose,
Wrapped bytes in headers, where filename goes.
UTF-8 crumbs, I stash with care,
MIME trails set, with fallback there.
Clickβ€”download pops! a tidy featβ€”
A rabbit’s signature, robust and neat.

✨ Finishing Touches
πŸ§ͺ Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/signature

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.

@ravishanigarapu
Copy link
Member Author

@coderabbitai review

Copy link
Contributor

coderabbitai bot commented Sep 3, 2025

βœ… Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@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

🧹 Nitpick comments (2)
src/main/java/com/iemr/common/controller/users/EmployeeSignatureController.java (2)

71-77: Enhance MIME fallback using filename; keep octet-stream as last resort

Current fallback is fine; optionally also infer from filename for better accuracy.

-MediaType mediaType;
-try {
-    mediaType = MediaType.parseMediaType(userSignID.getFileType());
-} catch (InvalidMediaTypeException | NullPointerException e) {
-    mediaType = MediaType.APPLICATION_OCTET_STREAM;
-}
+MediaType mediaType = null;
+try {
+    mediaType = MediaType.parseMediaType(userSignID.getFileType());
+} catch (InvalidMediaTypeException | NullPointerException ignored) { }
+if (mediaType == null) {
+    mediaType = org.springframework.http.MediaTypeFactory
+            .getMediaType(originalName)
+            .orElse(MediaType.APPLICATION_OCTET_STREAM);
+}

24-26: Refactor: use Spring’s ContentDisposition builder instead of URLEncoder Project is on Java 17 and Spring Boot 3.2.2 (so Charset-overload compiles and ContentDisposition is available). Remove URLEncoder, switch to

ContentDisposition cd = ContentDisposition.builder("attachment")
    .filename(userSignID.getFileName(), StandardCharsets.UTF_8)
    .build();
headers.setContentDisposition(cd);

and add imports for org.springframework.http.ContentDisposition, org.springframework.http.CacheControl, org.springframework.http.HttpStatus, and org.springframework.web.server.ResponseStatusException.

πŸ“œ Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

πŸ’‘ Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

πŸ“₯ Commits

Reviewing files that changed from the base of the PR and between 7dbc8ee and 8ced457.

πŸ“’ Files selected for processing (1)
  • src/main/java/com/iemr/common/controller/users/EmployeeSignatureController.java (2 hunks)
πŸ”‡ Additional comments (3)
src/main/java/com/iemr/common/controller/users/EmployeeSignatureController.java (3)

32-32: LGTM: import for robust MIME parsing

Importing InvalidMediaTypeException is appropriate for safe parsing fallback.


80-81: LGTM: correct headers, content type, and length

Good use of contentType and contentLength with byte[] body.


83-86: Don’t throw generic Exception; map to HTTP with cause

Use ResponseStatusException to preserve the stack and set status explicitly.

-} catch (Exception e) {
-    logger.error("File download for userID failed with exception " + e.getMessage(), e);
-    throw new Exception("Error while downloading file. Please contact administrator..");
-}
+} catch (Exception e) {
+    logger.error("File download for userID failed with exception " + e.getMessage(), e);
+    throw new ResponseStatusException(
+            HttpStatus.INTERNAL_SERVER_ERROR,
+            "Error while downloading file. Please contact administrator.",
+            e);
+}
β›” Skipped due to learnings
Learnt from: helenKaryamsetty
PR: PSMRI/Common-API#123
File: src/main/java/com/iemr/common/controller/abdmfacility/AbdmFacilityController.java:41-45
Timestamp: 2024-11-20T07:23:22.514Z
Learning: In the `Common-API` codebase, exception handling within controller classes like `AbdmFacilityController.java` is managed using the `OutputResponse` class, and it is not required to catch specific exceptions separately. General exception handling is sufficient in this context.

Comment on lines 66 to 70
HttpHeaders responseHeaders = new HttpHeaders();
String fileName = URLEncoder.encode(userSignID.getFileName(), StandardCharsets.UTF_8);
responseHeaders.set(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + fileName + "\"; filename*=UTF-8''" + fileName);

Copy link
Contributor

Choose a reason for hiding this comment

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

πŸ› οΈ Refactor suggestion

Build RFC 6266/5987-compliant Content-Disposition and prevent sensitive caching

Use Spring’s builder to correctly emit filename and filename* (UTF-8, percent-encoded), avoid '+' issues, and add no-store headers since signatures are PII.

-HttpHeaders responseHeaders = new HttpHeaders();
-String fileName = URLEncoder.encode(userSignID.getFileName(), StandardCharsets.UTF_8);
-responseHeaders.set(HttpHeaders.CONTENT_DISPOSITION,
-        "attachment; filename=\"" + fileName + "\"; filename*=UTF-8''" + fileName);
+String originalName = userSignID.getFileName();
+if (originalName == null || originalName.isBlank()) {
+    originalName = "signature";
+}
+HttpHeaders responseHeaders = new HttpHeaders();
+responseHeaders.setContentDisposition(
+        ContentDisposition.attachment().filename(originalName, StandardCharsets.UTF_8).build());
+responseHeaders.setCacheControl(CacheControl.noStore());
+responseHeaders.add(HttpHeaders.PRAGMA, "no-cache");
+responseHeaders.setExpires(0);
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
HttpHeaders responseHeaders = new HttpHeaders();
String fileName = URLEncoder.encode(userSignID.getFileName(), StandardCharsets.UTF_8);
responseHeaders.set(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + fileName + "\"; filename*=UTF-8''" + fileName);
String originalName = userSignID.getFileName();
if (originalName == null || originalName.isBlank()) {
originalName = "signature";
}
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.setContentDisposition(
ContentDisposition.attachment().filename(originalName, StandardCharsets.UTF_8).build());
responseHeaders.setCacheControl(CacheControl.noStore());
responseHeaders.add(HttpHeaders.PRAGMA, "no-cache");
responseHeaders.setExpires(0);
πŸ€– Prompt for AI Agents
In
src/main/java/com/iemr/common/controller/users/EmployeeSignatureController.java
around lines 66 to 70, the code manually constructs a Content-Disposition header
with a URL-encoded filename (which can produce '+' for spaces and doesn't fully
follow RFC6266/5987) and lacks anti-caching headers for sensitive PII; replace
the manual header with Spring's ContentDisposition builder
(ContentDisposition.builder("attachment").filename(originalFileName,
StandardCharsets.UTF_8).build()) and use HttpHeaders.setContentDisposition(...)
so Spring emits both filename and filename* correctly (percent-encoded UTF-8),
and add response headers to prevent caching (Cache-Control: no-store, Pragma:
no-cache, Expires: 0).

Comment on lines 78 to 79
byte[] fileBytes = userSignID.getSignature(); // MUST be byte[]

Copy link
Contributor

Choose a reason for hiding this comment

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

πŸ› οΈ Refactor suggestion

Guard against missing payload; return 404 for absent signature

Avoid NPE on length and emit a clear 404 when no bytes are present.

-byte[] fileBytes = userSignID.getSignature(); // MUST be byte[]
+byte[] fileBytes = userSignID.getSignature(); // MUST be byte[]
+if (fileBytes == null || fileBytes.length == 0) {
+    throw new ResponseStatusException(HttpStatus.NOT_FOUND,
+            "Signature not found for userID: " + userID);
+}
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
byte[] fileBytes = userSignID.getSignature(); // MUST be byte[]
byte[] fileBytes = userSignID.getSignature(); // MUST be byte[]
if (fileBytes == null || fileBytes.length == 0) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND,
"Signature not found for userID: " + userID);
}
πŸ€– Prompt for AI Agents
In
src/main/java/com/iemr/common/controller/users/EmployeeSignatureController.java
around lines 78-79, the code assumes userSignID.getSignature() is non-null and
accesses its length, which risks an NPE; add a null/empty check before using the
byte[] and return a 404 when the signature is absent. Specifically, first verify
userSignID != null and signatureBytes != null && signatureBytes.length > 0, and
if that fails respond with a 404 (e.g., ResponseEntity.notFound().build() or
throw new ResponseStatusException(HttpStatus.NOT_FOUND)); only then proceed to
write or return the byte[].

Copy link

sonarqubecloud bot commented Sep 8, 2025

@ravishanigarapu ravishanigarapu merged commit c181b2a into release-3.6.0 Sep 8, 2025
2 checks passed
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.

2 participants