Skip to content

Conversation

SauravBizbRolly
Copy link

@SauravBizbRolly SauravBizbRolly commented Sep 19, 2025

📋 Description

JIRA ID: MHWC -120
fix sync issue due to ClassCastException Boolean cannot be cast to class String.

✅ 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

  • New Features
    • Nurse worklist now shows a high‑risk indicator for beneficiaries.
  • Enhancements
    • More robust CHO app APIs with clearer success, not‑found, and error responses; better handling of activity images.
    • Authentication flow streamlined: consistent JWT/User‑Agent handling across headers and cookies, improved mobile client support.
  • Bug Fixes
    • Prevents session updates when authorization is empty.
    • Safer handling of null/typed data in procedure and screening results.
  • Chores
    • Upgraded API version to 3.4.0.

Copy link

coderabbitai bot commented Sep 19, 2025

Walkthrough

Updates Maven version; renames a local variable in a controller; adds a transient is_high_risk field and wires repository/service logic to populate it in nurse worklists; adjusts type casting in two services and a lab mapping; introduces a Constants utility; refactors JWT filter and related utilities to use Constants and wrapped requests; hardens error handling in CHO app service.

Changes

Cohort / File(s) Summary
Build Version
pom.xml
Bumps hwc-api artifact version 3.1.0 → 3.4.0; adds trailing newline.
Controller Variable Rename
src/main/java/com/iemr/hwc/controller/wo/LocationControllerWo.java
Renames local outputResponse → response in getOutreachMasterForState; updates references.
High-Risk Flag Enrichment
src/main/java/com/iemr/hwc/data/benFlowStatus/BeneficiaryFlowStatus.java, src/main/java/com/iemr/hwc/repo/benFlowStatus/BeneficiaryFlowStatusRepo.java, src/main/java/com/iemr/hwc/service/common/transaction/CommonNurseServiceImpl.java
Adds transient Boolean is_high_risk with duplicate getters/setters; adds repo method getIsHighrisk(ben_id) (native SQL); service fetches and sets high-risk per beneficiary in nurse worklist.
Lab Mapping Cast Adjustments
src/main/java/com/iemr/hwc/data/labModule/ProcedureComponentMapping.java
Switches several (String) casts to String.valueOf(...); stores isMandatory/isCalibration raw values; minor formatting.
CHO App Service Error Handling
src/main/java/com/iemr/hwc/service/choApp/CHOAppSyncServiceImpl.java
Wraps template/activity methods in try/catch; returns 200/404/500 appropriately; encodes images to Base64 when present.
NCD Screening Type Cast
src/main/java/com/iemr/hwc/service/ncdscreening/NCDScreeningServiceImpl.java
Casts result id from Long instead of BigInteger.longValue().
Constants and JWT/Headers Utilities
src/main/java/com/iemr/hwc/utils/Constants.java, src/main/java/com/iemr/hwc/utils/JwtUserIdValidationFilter.java, src/main/java/com/iemr/hwc/utils/RestTemplateUtil.java, src/main/java/com/iemr/hwc/utils/http/HTTPRequestInterceptor.java
Adds Constants (JWT_TOKEN, USER_AGENT, OKHTTP). JWT filter uses constants, wraps requests on valid token, adjusts mobile-client handling and UserAgentContext. RestTemplateUtil conditionally forwards JWT header via Constants. Interceptor adds empty-string check for authorization.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant C as Client
  participant F as JwtUserIdValidationFilter
  participant W as AuthorizationHeaderRequestWrapper
  participant S as Downstream Controller

  C->>F: HTTP request (cookies/headers)
  alt Valid JWT from cookie/header
    F->>W: Wrap request with Authorization header
    W->>S: Forward wrapped request
    S-->>C: Response
  else Invalid/No JWT
    F->>F: Check User-Agent (mobile?)
    alt Mobile client with Authorization present
      F->>S: Forward request (UserAgentContext set)
      S-->>C: Response
      F->>F: Clear UserAgentContext
    else Block/handle as per existing flow
      F-->>C: Error/unauthorized (unchanged messaging)
    end
  end
Loading
sequenceDiagram
  autonumber
  participant NS as CommonNurseServiceImpl
  participant R as BeneficiaryFlowStatusRepo
  participant DB as DB

  NS->>R: getNurseWorklistNew(fromDate,...)
  R->>DB: Query worklist
  DB-->>R: List<BeneficiaryFlowStatus>
  loop For each beneficiary
    NS->>R: getIsHighrisk(ben_id)
    R->>DB: SELECT is_high_risk FROM t_anc_visit
    DB-->>R: Boolean
    R-->>NS: Boolean
    NS->>NS: set is_high_risk if non-null
  end
  NS-->>NS: Return enriched list
Loading
sequenceDiagram
  autonumber
  participant A as CHOAppSyncServiceImpl
  participant Repo as Activity Repository
  participant C as Caller

  C->>A: getActivityById(id, auth)
  A->>Repo: findById(id)
  alt Found
    A->>A: Convert images to Base64 if present
    A-->>C: 200 OK with JSON
  else Not found
    A-->>C: 404 Not Found
  end
  note over A: Exceptions -> log and 500
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • ravishanigarapu
  • helenKaryamsetty
  • drtechie

Poem

In burrows of code I twitch my nose,
New constants sprout where headers rose.
I thump for nurses’ high-risk signs,
And guard the gate with JWT lines.
Try/catch carrots in tidy rows—
3.4.0, the garden grows! 🥕🐇

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.64% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "Feature/sync issue" does reference the sync problem described in the PR (the ClassCastException causing sync failures) so it is related to the changeset, but it includes a branch-style prefix ("Feature/") and lacks a concise, specific action or error detail, making it noisier and less descriptive than ideal.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/sync_issue

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ast-grep (0.38.6)
src/main/java/com/iemr/hwc/service/common/transaction/CommonNurseServiceImpl.java

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


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 link

@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: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/main/java/com/iemr/hwc/service/choApp/CHOAppSyncServiceImpl.java (2)

260-266: Root cause not fixed: robustly parse “data” to avoid ClassCastException

Using getString("data") assumes a String but upstream sometimes returns a JSONArray/Boolean/Number, causing “Boolean cannot be cast to class String”. Parse defensively.

Apply this diff:

-                    JSONObject responseJSON = new JSONObject(response.getBody());
-                    JSONArray jsonArray = new JSONArray(responseJSON.getJSONObject("response").getString("data"));
-
-                    outputResponse.setResponse(jsonArray.toString());
+                    JSONObject responseJSON = new JSONObject(response.getBody());
+                    Object dataNode = responseJSON.getJSONObject("response").opt("data");
+                    JSONArray jsonArray;
+                    if (dataNode instanceof JSONArray) {
+                        jsonArray = (JSONArray) dataNode;
+                    } else if (dataNode instanceof String) {
+                        jsonArray = new JSONArray((String) dataNode);
+                    } else {
+                        jsonArray = new JSONArray();
+                    }
+                    outputResponse.setResponse(jsonArray.toString());

325-329: Same casting pitfall for count; don’t assume String

“data” may be numeric/boolean. Use String.valueOf(opt(...)).

Apply this diff:

-                    JSONObject responseJSON = new JSONObject(response.getBody());
-                    String count = responseJSON.getJSONObject("response").getString("data");
-
-                    outputResponse.setResponse(count);
+                    JSONObject responseJSON = new JSONObject(response.getBody());
+                    Object dataNode = responseJSON.getJSONObject("response").opt("data");
+                    outputResponse.setResponse(String.valueOf(dataNode));
🧹 Nitpick comments (7)
src/main/java/com/iemr/hwc/controller/wo/LocationControllerWo.java (1)

201-215: Avoid shadowing the controller field; use a local name and prefer removing the mutable field.

The local variable ‘response’ shadows the class field ‘response’, which is confusing. Also, mutable controller fields are unsafe in Spring singletons. Use a clearly named local and consider removing the class field across this controller.

Apply within this method:

-        OutputResponse response = new OutputResponse();
+        OutputResponse out = new OutputResponse();
@@
-            response.setResponse(resList);
+            out.setResponse(resList);
@@
-            response.setError(500, "Unable to fetch outreach list for stateId" + stateID + "Exception - " + e);
+            out.setError(500, "Unable to fetch outreach list for stateId" + stateID + "Exception - " + e);
@@
-        return new ResponseEntity<>(response.toStringWithSerializeNulls(), headers, statusCode);
+        return new ResponseEntity<>(out.toStringWithSerializeNulls(), headers, statusCode);

Follow-up: remove the class field private OutputResponse response; and make all methods use locals.

src/main/java/com/iemr/hwc/utils/http/HTTPRequestInterceptor.java (1)

127-129: Post-handle guard LGTM.

Only updating the session when Authorization is non-empty is correct.

Consider using a case-insensitive, prefix check for “Bearer ” in pre/post handle to avoid false positives with tokens in other header positions.

src/main/java/com/iemr/hwc/utils/Constants.java (1)

3-9: Make the constants holder final.

Prevent extension of the utility class.

-public class Constants {
+public final class Constants {
src/main/java/com/iemr/hwc/utils/RestTemplateUtil.java (1)

42-44: Conditional header add is correct and safer.

Good move to only forward JWT header when present.

Minor: when building the Cookie header, prefer the constant for the name to avoid drift (use Constants.JWT_TOKEN + "=" + jwtTokenFromCookie).

src/main/java/com/iemr/hwc/data/labModule/ProcedureComponentMapping.java (1)

332-344: Fixes the Boolean→String CCE; small follow-up nit.

Using String.valueOf(...) and keeping booleans uncast resolves the ClassCastException. Consider storing s.toString() instead of the StringBuilder instance when putting compList, for clarity during JSON serialization.

Please confirm downstream JSON consumers expect booleans for isMandatory/isCalibration (not strings).

src/main/java/com/iemr/hwc/service/choApp/CHOAppSyncServiceImpl.java (1)

620-623: Log the exception with stacktrace

Errors are logged without the throwable, losing stack traces.

Apply this diff:

-            logger.error("Error while fetching Prescription Templates userID : " + userID);
+            logger.error("Error while fetching Prescription Templates userID : {}", userID, e);
-            logger.error("Error while deleting Prescription Templates userID : " + userID + " tempID : " + tempID);
+            logger.error("Error while deleting Prescription Templates userID : {} tempID : {}", userID, tempID, e);
-            logger.error("Encountered exception while fetching activity activityId " + activityId);
+            logger.error("Encountered exception while fetching activity activityId {}", activityId, e);

Also applies to: 638-642, 735-738

src/main/java/com/iemr/hwc/data/benFlowStatus/BeneficiaryFlowStatus.java (1)

299-311: Expose is_high_risk for serializers and accept nulls

  • Field isn’t annotated with @expose; any Gson configured with excludeFieldsWithoutExposeAnnotation will omit it.
  • Setter takes primitive boolean, preventing null assignment.

Apply this diff:

-    @Transient
-    Boolean is_high_risk;
+    @Transient
+    @Expose
+    private Boolean is_high_risk;
@@
-    public Boolean isIs_high_risk() {
-        return is_high_risk;
-    }
+    public Boolean isIs_high_risk() {
+        return is_high_risk;
+    }
@@
-    public void setIs_high_risk(boolean is_high_risk) {
-        this.is_high_risk = is_high_risk;
-    }
+    public void setIs_high_risk(Boolean is_high_risk) {
+        this.is_high_risk = is_high_risk;
+    }

Optional: add a canonical getter to reduce “isIs_” awkwardness without breaking callers:

public Boolean getIs_high_risk() { return is_high_risk; }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8940e22 and ff82e4d.

📒 Files selected for processing (12)
  • pom.xml (2 hunks)
  • src/main/java/com/iemr/hwc/controller/wo/LocationControllerWo.java (1 hunks)
  • src/main/java/com/iemr/hwc/data/benFlowStatus/BeneficiaryFlowStatus.java (2 hunks)
  • src/main/java/com/iemr/hwc/data/labModule/ProcedureComponentMapping.java (1 hunks)
  • src/main/java/com/iemr/hwc/repo/benFlowStatus/BeneficiaryFlowStatusRepo.java (1 hunks)
  • src/main/java/com/iemr/hwc/service/choApp/CHOAppSyncServiceImpl.java (2 hunks)
  • src/main/java/com/iemr/hwc/service/common/transaction/CommonNurseServiceImpl.java (1 hunks)
  • src/main/java/com/iemr/hwc/service/ncdscreening/NCDScreeningServiceImpl.java (1 hunks)
  • src/main/java/com/iemr/hwc/utils/Constants.java (1 hunks)
  • src/main/java/com/iemr/hwc/utils/JwtUserIdValidationFilter.java (3 hunks)
  • src/main/java/com/iemr/hwc/utils/RestTemplateUtil.java (1 hunks)
  • src/main/java/com/iemr/hwc/utils/http/HTTPRequestInterceptor.java (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-06-17T14:52:46.902Z
Learnt from: vishwab1
PR: PSMRI/HWC-API#138
File: src/main/java/com/iemr/hwc/utils/JwtUserIdValidationFilter.java:39-57
Timestamp: 2025-06-17T14:52:46.902Z
Learning: In the PSMRI/HWC-API codebase, the team prefers to keep CORS header logic in the JwtUserIdValidationFilter even when CorsConfig is also present, despite potential header duplication. This is an intentional architectural decision.

Applied to files:

  • src/main/java/com/iemr/hwc/utils/JwtUserIdValidationFilter.java
🧬 Code graph analysis (2)
src/main/java/com/iemr/hwc/utils/RestTemplateUtil.java (1)
src/main/java/com/iemr/hwc/utils/Constants.java (1)
  • Constants (3-9)
src/main/java/com/iemr/hwc/utils/JwtUserIdValidationFilter.java (3)
src/main/java/com/iemr/hwc/utils/http/AuthorizationHeaderRequestWrapper.java (1)
  • AuthorizationHeaderRequestWrapper (10-42)
src/main/java/com/iemr/hwc/utils/Constants.java (1)
  • Constants (3-9)
src/main/java/com/iemr/hwc/utils/UserAgentContext.java (1)
  • UserAgentContext (3-18)
🔇 Additional comments (3)
src/main/java/com/iemr/hwc/service/choApp/CHOAppSyncServiceImpl.java (2)

621-622: Tighten error message formatting

Minor: add separators to improve readability.

[ suggest_nitpick_refactor ]
Apply this diff:

-            outputResponse.setError(500, "Unable to fetch Prescription Templates userID" + userID + "Exception - " + e);
+            outputResponse.setError(500, "Unable to fetch Prescription Templates. userID: " + userID + "; Exception: " + e);
-            outputResponse.setError(500, "Unable to delete Prescription Templates userID : " + userID + " tempID "
-                    + tempID + "Exception - " + e);
+            outputResponse.setError(500, "Unable to delete Prescription Template. userID: " + userID + ", tempID: "
+                    + tempID + "; Exception: " + e);

Also applies to: 639-641


239-243: Good: no token leak in response headers

You add AUTHORIZATION to response headers but remove it before returning. This avoids reflecting tokens to clients.

Please run the API manually once to ensure no Authorization headers are present in 2xx/4xx/5xx responses from these endpoints.

Also applies to: 291-293

src/main/java/com/iemr/hwc/repo/benFlowStatus/BeneficiaryFlowStatusRepo.java (1)

461-462: Return latest is_high_risk (order by visit sequence) and coerce DB boolean

  • ORDER BY 1 DESC sorts by the boolean value (TRUE > FALSE) — it returns TRUE if any historic visit was TRUE, not the most recent visit.
  • Native boolean mappings vary by DB; coerce with CASE to guarantee a Java Boolean.
  • Repo search found only this query (src/main/java/com/iemr/hwc/repo/benFlowStatus/BeneficiaryFlowStatusRepo.java:461-462) and no timestamp/PK to choose from — confirm which column to order by. Prefer an incremental visit id (ben_visit_id/BenVisitID) to reflect visit sequence; fallback to created_date DESC if no visit id exists.

Suggested replacement (adjust column name to your schema):

@Query(value = "SELECT CASE WHEN t.is_high_risk = 1 THEN TRUE ELSE FALSE END " +
               "FROM t_anc_visit t " +
               "WHERE t.ben_id = :ben_id " +
               "ORDER BY t.ben_visit_id DESC " +   -- or ORDER BY t.created_date DESC
               "LIMIT 1",
       nativeQuery = true)
Boolean getIsHighrisk(@Param("ben_id") Long ben_id);

<groupId>com.iemr.hwc</groupId>
<artifactId>hwc-api</artifactId>
<version>3.1.0</version>
<version>3.4.0</version>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

🧩 Analysis chain

Version bump OK; verify repo references and release notes.

Looks good. Please ensure any hard-coded references to 3.1.0 are cleaned up and changelog/release notes are updated.

Run to spot stragglers:


🏁 Script executed:

#!/bin/bash
rg -nP '(hwc-api-)?3\.1\.0' -S

Length of output: 1511


Fix remaining 3.1.0 references before merge

  • pom.xml — lines 148 and 371 contain 3.1.0.
  • package-lock.json — multiple 3.1.0 occurrences (e.g. lines 469–470, 1079–1080, 1570–1571, 1630–1631, 2381–2382, 2426–2427); regenerate or update the lockfile if the version bump should apply to dependencies.
  • Update changelog/release notes to reflect 3.4.0.

Comment on lines 3548 to 3552
for (BeneficiaryFlowStatus beneficiaryFlowStatus : obj) {
Boolean isHighrisk = beneficiaryFlowStatusRepo.getIsHighrisk(beneficiaryFlowStatus.getBeneficiaryID());
if(null != isHighrisk)
beneficiaryFlowStatus.setIs_high_risk(isHighrisk);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid N+1 lookups and null argument to repo

  • Calling getIsHighrisk per row is N+1 and may be slow.
  • If beneficiaryID is null, the repo call will throw for null param.

Apply this minimal safety fix now:

-        for (BeneficiaryFlowStatus beneficiaryFlowStatus : obj) {
-            Boolean isHighrisk = beneficiaryFlowStatusRepo.getIsHighrisk(beneficiaryFlowStatus.getBeneficiaryID());
-            if(null != isHighrisk)
-                beneficiaryFlowStatus.setIs_high_risk(isHighrisk);
-        }
+        for (BeneficiaryFlowStatus bfs : obj) {
+            Long benId = bfs.getBeneficiaryID();
+            if (benId == null) {
+                continue;
+            }
+            Boolean isHighrisk = beneficiaryFlowStatusRepo.getIsHighrisk(benId);
+            if (isHighrisk != null) {
+                bfs.setIs_high_risk(isHighrisk);
+            }
+        }

Then, consider batching for performance:

  • Collect unique benIds from obj.
  • Add a repo method returning Map<Long, Boolean> of latest flags for those IDs.
  • Populate bfs.setIs_high_risk(...) from that map.

I can sketch the repo query if you confirm available columns on t_anc_visit.

📝 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
for (BeneficiaryFlowStatus beneficiaryFlowStatus : obj) {
Boolean isHighrisk = beneficiaryFlowStatusRepo.getIsHighrisk(beneficiaryFlowStatus.getBeneficiaryID());
if(null != isHighrisk)
beneficiaryFlowStatus.setIs_high_risk(isHighrisk);
}
for (BeneficiaryFlowStatus bfs : obj) {
Long benId = bfs.getBeneficiaryID();
if (benId == null) {
continue;
}
Boolean isHighrisk = beneficiaryFlowStatusRepo.getIsHighrisk(benId);
if (isHighrisk != null) {
bfs.setIs_high_risk(isHighrisk);
}
}
🤖 Prompt for AI Agents
In
src/main/java/com/iemr/hwc/service/common/transaction/CommonNurseServiceImpl.java
around lines 3548-3552, the loop calls beneficiaryFlowStatusRepo.getIsHighrisk
for each beneficiary (N+1) and will throw if beneficiaryID is null; as a minimal
safety fix, check beneficiaryFlowStatus.getBeneficiaryID() for null before
invoking the repo and skip the repo call (or set is_high_risk to a safe default)
when null, then set the result on the object only when non-null; afterwards
implement a batched approach: collect unique non-null beneficiary IDs, add a
repo method that returns a Map<Long,Boolean> of flags for those IDs, and
populate each BeneficiaryFlowStatus from that map to avoid N+1 queries.

Comment on lines 1700 to 1701
map.put("beneficiaryRegId", obj[0] != null ? ((Long) obj[0]) : null);
if (obj[1] != null && (Boolean) obj[1] == true)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Robust casting to avoid ClassCastException across drivers.

(Long) obj[0] will fail if the JPA provider returns BigInteger/Integer. Use Number.longValue() instead.

-                    map.put("beneficiaryRegId", obj[0] != null ? ((Long) obj[0]) : null);
+                    map.put("beneficiaryRegId", obj[0] != null ? ((Number) obj[0]).longValue() : null);
📝 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
map.put("beneficiaryRegId", obj[0] != null ? ((Long) obj[0]) : null);
if (obj[1] != null && (Boolean) obj[1] == true)
map.put("beneficiaryRegId", obj[0] != null ? ((Number) obj[0]).longValue() : null);
if (obj[1] != null && (Boolean) obj[1] == true)
🤖 Prompt for AI Agents
In src/main/java/com/iemr/hwc/service/ncdscreening/NCDScreeningServiceImpl.java
around lines 1700-1701, the code casts obj[0] directly to Long which can throw
ClassCastException if the JPA provider returns Integer/BigInteger; replace the
direct cast with a safe Number-based conversion: check for null, ensure obj[0]
is an instance of Number, then call ((Number) obj[0]).longValue() and put that
Long (or null) into the map; keep the existing boolean handling for obj[1]
as-is.

Comment on lines 91 to 116
try {
String jwtFromCookie = getJwtTokenFromCookies(request);
String jwtFromHeader = request.getHeader("JwtToken");
String authHeader = request.getHeader("Authorization");

if (jwtFromCookie != null) {
logger.info("Validating JWT token from cookie");
if (jwtAuthenticationUtil.validateUserIdAndJwtToken(jwtFromCookie)) {
filterChain.doFilter(servletRequest, servletResponse);
return;
}
}

if (jwtFromHeader != null) {
logger.info("Validating JWT token from header");
if (jwtAuthenticationUtil.validateUserIdAndJwtToken(jwtFromHeader)) {
filterChain.doFilter(servletRequest, servletResponse);
return;
}
}
String userAgent = request.getHeader("User-Agent");
logger.info("User-Agent: " + userAgent);

if (userAgent != null && isMobileClient(userAgent) && authHeader != null) {
filterChain.doFilter(servletRequest, servletResponse);
return;
}

logger.warn("No valid authentication token found");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized: Invalid or missing token");

String jwtToken = getJwtTokenFromCookies(request);
String jwtFromHeader = request.getHeader(Constants.JWT_TOKEN);
String authHeader = request.getHeader("Authorization");

if ((jwtToken != null && jwtAuthenticationUtil.validateUserIdAndJwtToken(jwtToken)) ||
(jwtFromHeader != null && jwtAuthenticationUtil.validateUserIdAndJwtToken(jwtFromHeader))) {
AuthorizationHeaderRequestWrapper authorizationHeaderRequestWrapper = new AuthorizationHeaderRequestWrapper(request, "");
filterChain.doFilter(authorizationHeaderRequestWrapper, servletResponse);
return;
} else {
String userAgent = request.getHeader(Constants.USER_AGENT);
logger.info("User-Agent: " + userAgent);
if (userAgent != null && isMobileClient(userAgent) && authHeader != null) {
try {
UserAgentContext.setUserAgent(userAgent);
filterChain.doFilter(servletRequest, servletResponse);
} finally {
UserAgentContext.clear();
}
return;
}
}
logger.warn("No valid authentication token found");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized: Invalid or missing token");
} catch (Exception e) {
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

❓ Verification inconclusive

Do not blank out the Authorization header after validation; propagate a proper Bearer value.

Wrapping the request with an empty Authorization breaks downstream components that rely on it. Propagate the validated token as “Bearer ”.

-            String jwtToken = getJwtTokenFromCookies(request);
-            String jwtFromHeader = request.getHeader(Constants.JWT_TOKEN);
-            String authHeader = request.getHeader("Authorization");
-
-            if ((jwtToken != null && jwtAuthenticationUtil.validateUserIdAndJwtToken(jwtToken)) || 
-                (jwtFromHeader != null && jwtAuthenticationUtil.validateUserIdAndJwtToken(jwtFromHeader))) {
-                AuthorizationHeaderRequestWrapper authorizationHeaderRequestWrapper = new AuthorizationHeaderRequestWrapper(request, "");
-                filterChain.doFilter(authorizationHeaderRequestWrapper, servletResponse);
-                return;
-            } else {
+            String jwtFromCookie = getJwtTokenFromCookies(request);
+            String jwtFromHeader = request.getHeader(Constants.JWT_TOKEN);
+            String authHeader = request.getHeader("Authorization");
+            String effectiveToken = jwtFromHeader != null ? jwtFromHeader : jwtFromCookie;
+
+            if (effectiveToken != null && jwtAuthenticationUtil.validateUserIdAndJwtToken(effectiveToken)) {
+                // Propagate Authorization for downstream usage
+                AuthorizationHeaderRequestWrapper wrapped =
+                    new AuthorizationHeaderRequestWrapper(request, "Bearer " + effectiveToken);
+                filterChain.doFilter(wrapped, servletResponse);
+                return;
+            } else {
                 String userAgent = request.getHeader(Constants.USER_AGENT);
                 logger.info("User-Agent: " + userAgent);
-                if (userAgent != null && isMobileClient(userAgent) && authHeader != null) {
+                if (userAgent != null && isMobileClient(userAgent) && authHeader != null && !authHeader.isEmpty()) {
                     try {
                         UserAgentContext.setUserAgent(userAgent);
                         filterChain.doFilter(servletRequest, servletResponse);
                     } finally {
                         UserAgentContext.clear();
                     }
                     return;
                 }
-            }
+            }

Follow-up: confirm the “mobile bypass” path (okhttp + any Authorization) is intentional; otherwise validate authHeader too.


Propagate Authorization header (don’t blank it)

Wrapping the request with an empty Authorization header breaks downstream consumers — set the header to "Bearer " when a JWT is validated instead of "".

  • Change to use an effectiveToken (jwtFromHeader ?: jwtFromCookie) and pass AuthorizationHeaderRequestWrapper(request, "Bearer " + effectiveToken) after validation.
  • Tighten the mobile-bypass: require authHeader != null && !authHeader.isEmpty(), or confirm the okhttp/mobile bypass accepting any Authorization is intentional.

Location: src/main/java/com/iemr/hwc/utils/JwtUserIdValidationFilter.java (around lines 91–116).

@SauravBizbRolly SauravBizbRolly changed the base branch from main to release-3.4.0 September 23, 2025 09:12
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