fix: merge 3.6.2 to release 3.8.1#419
Conversation
* fix: implement functionality to search beneficiaries with Elasticsearch * fix: remove unwanted import * fix: update pom.xml * fix: change the response code
variable added
* fix: cherry-pick commits for advanced search * fix: cherry-pick commit for token issue - mobile application * fix: add the missing properties * fix: add function to retrieve userid * fix: move the fetch Userid to jwtUtil
Signature fix for mmu
Fixing the Signature when doctor not available
* FLW-713 Remove All File Upload Options * Fix UserServiceRoleRepo dependency issue and codeRabit comment * fixed coderabit comment * fix userMappingId issue
* fix: cors spell fixes and import of packages updates * fix: deployment issue fix * feat: amm-1959 dhis token for cho report re-direction * fix: beneficiary history on revisit (#320) * fix: call type mapper (#322) * Elasticsearch implementation for Beneficiary Search (#324) * fix: implement functionality to search beneficiaries with Elasticsearch * fix: remove unwanted import * fix: update pom.xml * fix: change the response code * variable added * Elastic Search Implementation for Advanced Search (#327) * fix: cherry-pick commits for advanced search * fix: cherry-pick commit for token issue - mobile application * fix: add the missing properties * fix: add function to retrieve userid * fix: move the fetch Userid to jwtUtil * fix:signature check for mmu * fix: retrive any user without deleted * fix: update KM filepath * FLW-713 Remove All File Upload Options (#350) * FLW-713 Remove All File Upload Options * Fix UserServiceRoleRepo dependency issue and codeRabit comment * fixed coderabit comment * fix userMappingId issue * Add SMS functionality in release-3.6.1 (#358) * Enable SMS Functionality in MMU App to Send Prescriptions (#325) * fix: sms template save and map mmu (#306) * Vb/sms (#307) * fix: sms template save and map mmu * fix: enable mms for mmu prescription * Enable SMS Functionality in MMU App to Send Prescriptions (#325) * fix: sms template save and map mmu (#306) * Vb/sms (#307) * fix: sms template save and map mmu * fix: enable mms for mmu prescription --------- Co-authored-by: Vishwanath Balkur <118195001+vishwab1@users.noreply.github.com> --------- Co-authored-by: 5Amogh <amoghavarsh@navadhiti.com> Co-authored-by: Vanitha S <116701245+vanitha1822@users.noreply.github.com> Co-authored-by: Sachin Kadam <152252767+sac2kadam@users.noreply.github.com> Co-authored-by: vanitha1822 <vanitha@navadhiti.com> Co-authored-by: Saurav Mishra <80103738+SauravBizbRolly@users.noreply.github.com>
…point (#373) - Add OtpRateLimiterService with Redis-backed per-mobile rate limits (3/min, 10/hr, 20/day) - Add OtpRateLimitException for 429 responses - Integrate rate limiter in BeneficiaryOTPHandlerImpl and BeneficiaryConsentController - Add otp.ratelimit.* properties to common_ci and common_docker profiles - Update common_example.properties with new OTP rate limit config Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* Cherry-pick health and version API enhancements to release-3.6.1 (#371) * feat(health,version): update version and health endpoints and add advance check for database * fix(health): normalize severity and fix slow query false positives * fix(health): avoid false CRITICAL on single long-running MySQL transaction * fix(health): enforce 3s DB connection timeout via HikariCP * Release 3.6.1 (#374) * feat(health,version): update version and health endpoints and add advance check for database * fix(health): normalize severity and fix slow query false positives * fix(health): avoid false CRITICAL on single long-running MySQL transaction * fix(health): enforce 3s DB connection timeout via HikariCP * feat(health): add healthcontroller and fix versioncontroller issues * fix: build error (#375) --------- Co-authored-by: KOPPIREDDY DURGA PRASAD <144464542+DurgaPrasad-54@users.noreply.github.com> Co-authored-by: Vanitha S <116701245+vanitha1822@users.noreply.github.com>
* Elasticsearch implementation for Beneficiary Search (#324) * fix: implement functionality to search beneficiaries with Elasticsearch * fix: remove unwanted import * fix: update pom.xml * fix: change the response code * variable added * Elastic Search Implementation for Advanced Search (#327) * fix: cherry-pick commits for advanced search * fix: cherry-pick commit for token issue - mobile application * fix: add the missing properties * fix: add function to retrieve userid * fix: move the fetch Userid to jwtUtil * fix:signature check for mmu * fix: retrive any user without deleted * fix: update KM filepath * FLW-713 Remove All File Upload Options (#350) * FLW-713 Remove All File Upload Options * Fix UserServiceRoleRepo dependency issue and codeRabit comment * fixed coderabit comment * fix userMappingId issue * Add SMS functionality in release-3.6.1 (#358) * Enable SMS Functionality in MMU App to Send Prescriptions (#325) * fix: sms template save and map mmu (#306) * Vb/sms (#307) * fix: sms template save and map mmu * fix: enable mms for mmu prescription * Enable SMS Functionality in MMU App to Send Prescriptions (#325) * fix: sms template save and map mmu (#306) * Vb/sms (#307) * fix: sms template save and map mmu * fix: enable mms for mmu prescription --------- Co-authored-by: Vishwanath Balkur <118195001+vishwab1@users.noreply.github.com> * feat: added the facilty in resonse * fix: login response change * feat: added the facilty in resonse * fix: logoin response * fix: login response --------- Co-authored-by: Vanitha S <116701245+vanitha1822@users.noreply.github.com> Co-authored-by: Sachin Kadam <152252767+sac2kadam@users.noreply.github.com> Co-authored-by: Mithun James <drtechie@users.noreply.github.com> Co-authored-by: SnehaRH <77656297+snehar-nd@users.noreply.github.com> Co-authored-by: vanitha1822 <vanitha@navadhiti.com> Co-authored-by: Saurav Mishra <80103738+SauravBizbRolly@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
feature for hindi translation for CG
* fix: integrate API for get disposition count * fix: respnse structure * fix: coderabbit comments
Move code from release-3.3 to release-3.8.1
…0_to_3.6.2 # Conflicts: # pom.xml # src/main/java/com/iemr/common/data/dynamic_from/FormField.java # src/main/java/com/iemr/common/dto/dynamicForm/FieldResponseDTO.java # src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java
…0_to_3.6.2 # Conflicts: # pom.xml # src/main/java/com/iemr/common/data/dynamic_from/FormField.java # src/main/java/com/iemr/common/dto/dynamicForm/FieldResponseDTO.java # src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java
…_optmiztion Bug/hindhi translation welcome sms optmiztion
Mapped ASHA's list is missing in Login API response
setDemographicDetails() was overwriting occupationName (already set correctly by the mapper from occupationID) with null when no occupation name string was present in the payload. Added null guards so the mapper's resolved name is preserved, and explicitly set occupationId/educationId from i_bendemographics to ensure the IDs always reach Identity-API. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…x server" This reverts commit eb917b2.
Occupation details getting null in DB
logOutUserFromConcurrentSession only cleaned up old-style Redis session keys but never added the displaced user's JWT to the denylist. Because JwtUserIdValidationFilter validates solely via JWT signature and the denylist, System 1's token remained valid and all APIs returned 200 after System 2 forced a concurrent login. Fix: store a username→JTI mapping in Redis at login time; during concurrent-session logout, look up the JTI and add it to the denylist and evict the user_<id> cache so the next request from System 1 is rejected with 401 and the frontend shows the session-expiry message. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
fix: concurrent session logout not invalidating JWT in first system
logOutUserFromConcurrentSession only cleaned up old-style Redis session keys but never added the displaced user's JWT to the denylist. Because JwtUserIdValidationFilter validates solely via JWT signature and the denylist, System 1's token remained valid and all APIs returned 200 after System 2 forced a concurrent login. The root serialization bug: redisTemplate value serializer is Jackson2JsonRedisSerializer<User>, so storing a plain String JTI caused a deserialization failure on retrieval. Fixed by using the existing StringRedisTemplate bean for the jti: key operations. Fix: - Store username->JTI mapping via StringRedisTemplate at login (both userAuthenticate and superUserAuthenticate) - On concurrent-session logout, retrieve the JTI, add it to the denylist, evict user_<id> from User cache, and clean up jti: key - Add getAccessTokenExpiration() to JwtUtil to supply the TTL Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
fix: concurrent session logout not invalidating JWT on first system
Add generateSecureToken/generateSecureRefreshToken methods that use userId as sub instead of username. Mobile logins (okhttp User-Agent) use the secure token — web logins remain unchanged for backward compatibility. Other services will be migrated one by one. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…n response (#417) Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* implement translation in dynamic form * Add SMS functionality in release-3.6.1 (#358) * Enable SMS Functionality in MMU App to Send Prescriptions (#325) * fix: sms template save and map mmu (#306) * Vb/sms (#307) * fix: sms template save and map mmu * fix: enable mms for mmu prescription * Enable SMS Functionality in MMU App to Send Prescriptions (#325) * fix: sms template save and map mmu (#306) * Vb/sms (#307) * fix: sms template save and map mmu * fix: enable mms for mmu prescription --------- Co-authored-by: Vishwanath Balkur <118195001+vishwab1@users.noreply.github.com> * Restrict user when account is locked * Cherry-pick health and version API enhancements to release-3.6.1 (#371) * feat(health,version): update version and health endpoints and add advance check for database * fix(health): normalize severity and fix slow query false positives * fix(health): avoid false CRITICAL on single long-running MySQL transaction * fix(health): enforce 3s DB connection timeout via HikariCP * Release 3.6.1 (#374) * feat(health,version): update version and health endpoints and add advance check for database * fix(health): normalize severity and fix slow query false positives * fix(health): avoid false CRITICAL on single long-running MySQL transaction * fix(health): enforce 3s DB connection timeout via HikariCP * feat(health): add healthcontroller and fix versioncontroller issues * fix: merge 3.7.0 to main * Video Consultation Functionality (#380) * Update application.properties * add column in create BeneficiaryModel * Elasticsearch implementation for Beneficiary Search (#324) * fix: implement functionality to search beneficiaries with Elasticsearch * fix: remove unwanted import * fix: update pom.xml * fix: change the response code * variable added * update language * update language * Downgrade version from 3.6.1 to 3.6.0 * Elastic Search Implementation for Advanced Search (#327) * fix: cherry-pick commits for advanced search * fix: cherry-pick commit for token issue - mobile application * fix: add the missing properties * fix: add function to retrieve userid * fix: move the fetch Userid to jwtUtil * Remove empty line in application.properties * fix:signature check for mmu * Update application.properties * Update application.properties * fix: retrive any user without deleted * implement state wise hide un hide form fields * implement state wise hide un hide form fields * implement state wise hide un hide form fields * enhance welcome sms code * fix hide unhide form issue * docs: add DeepWiki badge and documentation link * Add DeepWiki badge to README Added DeepWiki badge to README for better visibility. * fix hide unhide form issue * fix hide unhide form issue * fix hide unhide form issue * fix hide unhide form issue * fix hide unhide form issue * fix hide unhide form issue * fix hide unhide form issue * fix hide unhide form issue * fix hide unhide form issue * fix hide unhide form issue * fix hide unhide form issue * fix hide unhide form issue * fix hide unhide form issue * fix hide unhide form issue * fix hide unhide form issue * fix hide unhide form issue * chore(swagger): automate swagger sync to amrit-docs (#354) * chore(swagger): automate swagger sync to amrit-docs * chore(swagger): automate swagger sync to amrit-docs * chore(swagger): automate swagger sync to amrit-docs * Update the swagger json github workflow (#359) * chore(swagger): automate swagger sync to amrit-docs * chore(swagger): automate swagger sync to amrit-docs * chore(swagger): automate swagger sync to amrit-docs * fix(swagger): update the workflow and fix the running issue * fix(swagger): fix the swagger json workflow * chore(swagger): add fixed branch name in workflow * chore(ci): prevent multiple swagger sync PRs by using fixed branch * chore(swagger): add Dev/UAT/Demo servers to OpenAPI config * chore(swagger): avoid default server URLs * chore(swagger): remove field injection and inject URLs into OpenAPI bean * Add /health endpoint and standardize /version response (#331) * Add /health endpoint and standardize /version response * Add license headers and Javadocs to health and version controllers * Enhance /health endpoint to check Database and Redis connectivity * Improve /health endpoint HTTP status handling and logging * Enhance database health check with validation query * Refactor health controller to constructor injection and constants * Refactor: Extract business logic to HealthService to keep controller lean * Refactor: Extract business logic to HealthService to keep controller lean * Fix: Use ObjectProvider for optional health dependencies * Add advance health check for database (#361) * chore(swagger): automate swagger sync to amrit-docs * chore(swagger): automate swagger sync to amrit-docs * chore(swagger): automate swagger sync to amrit-docs * fix(swagger): update the workflow and fix the running issue * fix(swagger): fix the swagger json workflow * chore(swagger): add fixed branch name in workflow * chore(ci): prevent multiple swagger sync PRs by using fixed branch * chore(swagger): add Dev/UAT/Demo servers to OpenAPI config * chore(swagger): avoid default server URLs * chore(swagger): remove field injection and inject URLs into OpenAPI bean * feat(health,version): update version and health endpoints and add advance check for database * fix(health): normalize severity and fix slow query false positives * fix(health): avoid false CRITICAL on single long-running MySQL transaction * fix(health): enforce 3s DB connection timeout via HikariCP * Merge Release-3.8.0 (3.6.1) to Main (#379) * Move code to 3.6.1 to 3.8.0 (#372) * fix: cors spell fixes and import of packages updates * fix: deployment issue fix * feat: amm-1959 dhis token for cho report re-direction * fix: beneficiary history on revisit (#320) * fix: call type mapper (#322) * Elasticsearch implementation for Beneficiary Search (#324) * fix: implement functionality to search beneficiaries with Elasticsearch * fix: remove unwanted import * fix: update pom.xml * fix: change the response code * variable added * Elastic Search Implementation for Advanced Search (#327) * fix: cherry-pick commits for advanced search * fix: cherry-pick commit for token issue - mobile application * fix: add the missing properties * fix: add function to retrieve userid * fix: move the fetch Userid to jwtUtil * fix:signature check for mmu * fix: retrive any user without deleted * fix: update KM filepath * FLW-713 Remove All File Upload Options (#350) * FLW-713 Remove All File Upload Options * Fix UserServiceRoleRepo dependency issue and codeRabit comment * fixed coderabit comment * fix userMappingId issue * Add SMS functionality in release-3.6.1 (#358) * Enable SMS Functionality in MMU App to Send Prescriptions (#325) * fix: sms template save and map mmu (#306) * Vb/sms (#307) * fix: sms template save and map mmu * fix: enable mms for mmu prescription * Enable SMS Functionality in MMU App to Send Prescriptions (#325) * fix: sms template save and map mmu (#306) * Vb/sms (#307) * fix: sms template save and map mmu * fix: enable mms for mmu prescription --------- Co-authored-by: Vishwanath Balkur <118195001+vishwab1@users.noreply.github.com> --------- Co-authored-by: 5Amogh <amoghavarsh@navadhiti.com> Co-authored-by: Vanitha S <116701245+vanitha1822@users.noreply.github.com> Co-authored-by: Sachin Kadam <152252767+sac2kadam@users.noreply.github.com> Co-authored-by: vanitha1822 <vanitha@navadhiti.com> Co-authored-by: Saurav Mishra <80103738+SauravBizbRolly@users.noreply.github.com> * fix: add OTP rate limiting to prevent OTP flooding on sendConsent endpoint (#373) - Add OtpRateLimiterService with Redis-backed per-mobile rate limits (3/min, 10/hr, 20/day) - Add OtpRateLimitException for 429 responses - Integrate rate limiter in BeneficiaryOTPHandlerImpl and BeneficiaryConsentController - Add otp.ratelimit.* properties to common_ci and common_docker profiles - Update common_example.properties with new OTP rate limit config Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * Health api (#376) * Cherry-pick health and version API enhancements to release-3.6.1 (#371) * feat(health,version): update version and health endpoints and add advance check for database * fix(health): normalize severity and fix slow query false positives * fix(health): avoid false CRITICAL on single long-running MySQL transaction * fix(health): enforce 3s DB connection timeout via HikariCP * Release 3.6.1 (#374) * feat(health,version): update version and health endpoints and add advance check for database * fix(health): normalize severity and fix slow query false positives * fix(health): avoid false CRITICAL on single long-running MySQL transaction * fix(health): enforce 3s DB connection timeout via HikariCP * feat(health): add healthcontroller and fix versioncontroller issues * fix: build error (#375) --------- Co-authored-by: KOPPIREDDY DURGA PRASAD <144464542+DurgaPrasad-54@users.noreply.github.com> Co-authored-by: Vanitha S <116701245+vanitha1822@users.noreply.github.com> --------- Co-authored-by: Vishwanath Balkur <118195001+vishwab1@users.noreply.github.com> Co-authored-by: 5Amogh <amoghavarsh@navadhiti.com> Co-authored-by: Sachin Kadam <152252767+sac2kadam@users.noreply.github.com> Co-authored-by: Saurav Mishra <80103738+SauravBizbRolly@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: KOPPIREDDY DURGA PRASAD <144464542+DurgaPrasad-54@users.noreply.github.com> * fix: video consultation functionality * fix: pom version update * fix: add cti-server-ip * fix: comment unwanted code * fix: update videocall url property * fix: update cti-server-ip * docs: add CLAUDE.md for Claude Code guidance Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: KM issue * fix: KM issue * fix: remove unwanted imports * fix: conflicts * fix: update the temp path * Fix the OpenKM Issue (#389) * fix: remove km in application.properties * fix: update all the properties to fetch from env * fix: update path * fix: KM issue * fix: get file from km * fix: build issue * fix: build issue * fix: remove unwanted imports * fix: build issue * fix: remove commented line * Enable KM configuration in common_example.properties Uncomment KM configuration properties for OpenKM. * Fix ConfigProperties to resolve env variable placeholders via Spring Environment (#390) Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: update sms issue * fix: build issue * fix: update condition * fix: edit ben issue * fix: phone number issue for sms * fix: update the url with jwt token * fix: jitsi authorization issue * fix: skip auth * fix: hash key updation * fix: jwt type in header for authorization * fix: update file path * fix: vc recording path updation * fix: update video call recording functionality * fix: remove unwanted codes * fix: coderabbit comments --------- Co-authored-by: Saurav Mishra <80103738+SauravBizbRolly@users.noreply.github.com> Co-authored-by: Saurav Mishra <saurav.mishra@bizbrolly.com> Co-authored-by: Sachin Kadam <152252767+sac2kadam@users.noreply.github.com> Co-authored-by: Mithun James <drtechie@users.noreply.github.com> Co-authored-by: Amoghavarsh <93114621+5Amogh@users.noreply.github.com> Co-authored-by: vishwab1 <vishwanath@navadhiti.com> Co-authored-by: SnehaRH <77656297+snehar-nd@users.noreply.github.com> Co-authored-by: DurgaPrasad-54 <prasad8790237@gmail.com> Co-authored-by: KOPPIREDDY DURGA PRASAD <144464542+DurgaPrasad-54@users.noreply.github.com> Co-authored-by: Vaishnav Bhosale <vaishnavbharatbhosale@gmail.com> Co-authored-by: Vishwanath Balkur <118195001+vishwab1@users.noreply.github.com> Co-authored-by: 5Amogh <amoghavarsh@navadhiti.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: SnehaRH <sneha@navadhiti.com> * Fix the Build Issue (#397) * fix: build issue * fix: build issue * fix: merge with main * Elastic Search Implementation for Advanced Search (#327) * fix: cherry-pick commits for advanced search * fix: cherry-pick commit for token issue - mobile application * fix: add the missing properties * fix: add function to retrieve userid * fix: move the fetch Userid to jwtUtil * Fix the End Consultation Call for VC (#407) * fix: end the consultation on clicking the end meeting button * fix: end call * fix: the build issue * fix: the issue in agent-token * fix: remove slug (#414) * fix: build issue --------- Co-authored-by: Saurav Mishra <saurav.mishra@bizbrolly.com> Co-authored-by: Saurav Mishra <80103738+SauravBizbRolly@users.noreply.github.com> Co-authored-by: Vishwanath Balkur <118195001+vishwab1@users.noreply.github.com> Co-authored-by: KOPPIREDDY DURGA PRASAD <144464542+DurgaPrasad-54@users.noreply.github.com> Co-authored-by: SnehaRH <77656297+snehar-nd@users.noreply.github.com> Co-authored-by: Sachin Kadam <152252767+sac2kadam@users.noreply.github.com> Co-authored-by: Mithun James <drtechie@users.noreply.github.com> Co-authored-by: Amoghavarsh <93114621+5Amogh@users.noreply.github.com> Co-authored-by: vishwab1 <vishwanath@navadhiti.com> Co-authored-by: DurgaPrasad-54 <prasad8790237@gmail.com> Co-authored-by: Vaishnav Bhosale <vaishnavbharatbhosale@gmail.com> Co-authored-by: 5Amogh <amoghavarsh@navadhiti.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: SnehaRH <sneha@navadhiti.com>
Co-authored-by: Varun Deep Saini <varun.23bcs10048@ms.sst.scaler.com>
* fix: remove km in application.properties * fix: update all the properties to fetch from env * fix: update path * fix: KM issue * fix: get file from km * fix: build issue * fix: build issue * fix: remove unwanted imports * fix: build issue * fix: remove commented line * Enable KM configuration in common_example.properties Uncomment KM configuration properties for OpenKM.
📝 Walkthrough<review_stack_artifact> WalkthroughThis PR introduces Jitsi video call meeting resolution with JWT tokens, implements account locking and unlock management with Redis-backed concurrent session denylisting, adds facility-based login enrichment for ASHA supervisors, migrates configuration injection to Spring ChangesJitsi Video Call Meeting Resolution and Recording
User Account Locking and Authentication
ASHA Supervisor and Facility Login Enrichment
Configuration Modernization: Spring
Dynamic Form Field Localization
CTI Disposition Count Reporting
Beneficiary Registration and SMS Enhancements
Miscellaneous Configuration and Cleanup
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
|
|
There was a problem hiding this comment.
Actionable comments posted: 18
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
src/main/java/com/iemr/common/controller/users/IEMRAdminController.java (1)
341-355:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winCritical: Null check order causes potential NPE.
The
user.getDeleted()anduser.getStatusID()calls on lines 342 and 346 occur before the null check on line 352. IfgetUserByIdreturns null, this will throw an NPE. Additionally,getDeleted()returnsBoolean(boxed), which can be null and cause NPE during unboxing.🐛 Proposed fix
// Get user details String userId = claims.get("userId", String.class); User user = iemrAdminUserServiceImpl.getUserById(Long.parseLong(userId)); + // Validate that the user still exists and is active + if (user == null) { + logger.warn("Token validation failed: user not found for userId in token."); + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Unauthorized."); + } + // validate if user account is locked or de-activated - if(user.getDeleted()){ + if(Boolean.TRUE.equals(user.getDeleted())){ logger.warn("Your account is locked or de-activated. Please contact administrator"); return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Your account is locked or de-activated. Please contact administrator."); } if(user.getStatusID()>2){ logger.warn("Your account is not active. Please contact administrator"); return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Your account is not active. Please contact administrator."); } - // Validate that the user still exists and is active - if (user == null) { - logger.warn("Token validation failed: user not found for userId in token."); - return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Unauthorized."); - }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/java/com/iemr/common/controller/users/IEMRAdminController.java` around lines 341 - 355, Move the null-check for the retrieved user before any calls to user.getDeleted() or user.getStatusID() in IEMRAdminController so you never invoke methods on a null reference; then use null-safe checks for the boxed Boolean and Integer returns (e.g., replace direct user.getDeleted() with Boolean.TRUE.equals(user.getDeleted()) and guard user.getStatusID() with a null check like user.getStatusID() != null && user.getStatusID() > 2) to avoid unboxing NPEs while preserving the existing log/response behavior.src/main/java/com/iemr/common/utils/km/openkm/OpenKMServiceImpl.java (1)
84-95:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAvoid reinitializing OpenKM connector per method call
With
@PostConstructat Line 84, connector setup should happen once. Callingthis.init()repeatedly in service methods re-creates connector objects and can hurt reliability/performance under load.Proposed fix
`@Override` public String getDocumentRoot() { String response = null; - this.init(); try { ... public String uploadFile() { String response = null; - this.init(); ... `@Override` public String createDocument(String documentPath, String filepath) { String response = null; - this.init(); ... public String createFolder(String path) { String response = null; - this.init(); ... public String deleteDocument(String documentID) { String response = "failure"; - this.init();Also applies to: 109-109, 124-124, 143-143, 179-179
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/java/com/iemr/common/utils/km/openkm/OpenKMServiceImpl.java` around lines 84 - 95, The init() method is annotated with `@PostConstruct` and should run once to initialize the OpenKM connector, so remove the repeated this.init() calls from service methods (e.g., getDocumentRoot(), and other methods that currently call this.init()) to avoid reinitializing the connector per call; instead rely on the `@PostConstruct` initialization and add a safe null-check around the connector (e.g., if (connector == null) { throw new IllegalStateException("OpenKM connector not initialized"); } or implement a synchronized lazy-initialize helper) so methods like getDocumentRoot(), fetchDocument(...), listFolder(...), etc., do not recreate connector objects on each invocation.src/main/java/com/iemr/common/CommonApplication.java (1)
32-44:⚠️ Potential issue | 🟠 Major | ⚡ Quick winEnsure
@Asyncuses a bounded executor (not Spring’s default fallback).
@EnableAsyncis enabled in bothsrc/main/java/com/iemr/common/CommonApplication.javaandsrc/main/java/com/iemr/common/utils/CommonMain.javawithout specifying an executor bean. The codebase does not define anyTaskExecutor/AsyncTaskExecutorbean or a bean namedtaskExecutor; it only provides@Bean public Executor asyncExecutor()(backed byThreadPoolTaskExecutor) inCommonMain. Spring’s default@EnableAsynclookup only targets aTaskExecutorbean (or a bean namedtaskExecutor), so async methods may fall back toSimpleAsyncTaskExecutor(unbounded thread creation) under load.Update the existing async executor bean to be discoverable by
@EnableAsync(e.g., expose it asTaskExecutorand/or rename it totaskExecutor, or explicitly reference it via@EnableAsync("asyncExecutor")/AsyncConfigurer).🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/java/com/iemr/common/CommonApplication.java` around lines 32 - 44, CommonApplication and CommonMain enable async but Spring may fall back to an unbounded SimpleAsyncTaskExecutor because the existing bean is named asyncExecutor() and returns Executor; update the async executor bean so `@EnableAsync` discovers it by either (A) changing the bean signature/name in CommonMain from "public Executor asyncExecutor()" to expose the TaskExecutor type and/or rename it to "taskExecutor" (e.g., public TaskExecutor taskExecutor()) or (B) explicitly bind `@EnableAsync` to the bean name used (e.g., `@EnableAsync`("asyncExecutor")), ensuring the bean is a bounded ThreadPoolTaskExecutor configured with core/max pool sizes and queue capacity so async methods use the bounded pool rather than the unbounded SimpleAsyncTaskExecutor.
🟡 Minor comments (6)
src/main/java/com/iemr/common/controller/beneficiary/BeneficiaryRegistrationController.java-39-40 (1)
39-40:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winRemove redundant duplicate imports in
BeneficiaryRegistrationController
- The
RequestHeader/CookieUtil/JwtUtilimport statements are duplicated (same exact types). Java ignores these duplicates, so it won’t fail compilation by itself, but it’s redundant and can trip lint/checkstyle/IDE rules.Proposed fix
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestHeader; -import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; @@ import com.iemr.common.service.userbeneficiarydata.StatusService; import com.iemr.common.service.userbeneficiarydata.TitleService; import com.iemr.common.utils.CookieUtil; import com.iemr.common.utils.JwtUtil; -import com.iemr.common.utils.CookieUtil; -import com.iemr.common.utils.JwtUtil; import com.iemr.common.utils.mapper.InputMapper;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/java/com/iemr/common/controller/beneficiary/BeneficiaryRegistrationController.java` around lines 39 - 40, The file BeneficiaryRegistrationController contains duplicate import statements (e.g., RequestHeader, CookieUtil, JwtUtil) — remove the redundant repeated imports so each type is imported only once; locate the duplicate import lines in BeneficiaryRegistrationController and delete the repeated occurrences, leaving a single import for each required class to satisfy lint/checkstyle rules.src/main/java/com/iemr/common/service/beneficiary/IEMRSearchUserServiceImpl.java-29-29 (1)
29-29:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winRemove duplicate import declarations in
IEMRSearchUserServiceImpl.java(lines 28-29 and 81-82).The file re-imports
java.util.Mapandcom.iemr.common.utils.exception.IEMRExceptiontwice, creating redundant declarations that can fail style/static-analysis checks.Suggested fix
import java.util.List; import java.util.Map; -import java.util.Map; import java.util.Objects; @@ import com.iemr.common.utils.exception.IEMRException; -import com.iemr.common.utils.exception.IEMRException;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/java/com/iemr/common/service/beneficiary/IEMRSearchUserServiceImpl.java` at line 29, The file IEMRSearchUserServiceImpl contains duplicate import declarations for java.util.Map and com.iemr.common.utils.exception.IEMRException; remove the redundant import lines so each type is imported exactly once (leave a single import for java.util.Map and a single import for com.iemr.common.utils.exception.IEMRException), then run a quick compile/static-check to ensure no unused-import warnings remain and adjust imports if any are unused.src/test/java/com/iemr/common/utils/JitsiJwtUtilTest.java-44-45 (1)
44-45:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winUse a clearly fake test secret to avoid leak-scanner noise.
Line 44 uses a production-like hex secret string. Prefer an obviously synthetic value (still >=32 bytes) so it cannot be mistaken for a deploy credential.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/test/java/com/iemr/common/utils/JitsiJwtUtilTest.java` around lines 44 - 45, Replace the production-like APP_SECRET value in JitsiJwtUtilTest with an obviously synthetic test secret (still at least 32 bytes) to avoid leak-scanner noise; update the static constant APP_SECRET in the JitsiJwtUtilTest class to a clearly fake hex or alphanumeric string (e.g., repeated pattern or "TEST_SECRET_..." long enough) while leaving SUB and other test logic unchanged.src/main/java/com/iemr/common/model/videocall/UpdateCallRequest.java-34-34 (1)
34-34:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winConsider primitive boolean to match repository contract.
The
Booleanwrapper type allows null values, but the repository methodupdateCallStatusAndRecordingexpects a primitivebooleanparameter (line 61 in VideoCallParameterRepository.java). IfisLinkUsedis null when passed to the repository, auto-unboxing will throw aNullPointerException.🔧 Proposed fix to use primitive type
- private Boolean isLinkUsed; + private boolean isLinkUsed;Alternatively, if null values are required for partial updates, add explicit null-handling in the service layer before calling the repository.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/java/com/iemr/common/model/videocall/UpdateCallRequest.java` at line 34, The field isLinkUsed in UpdateCallRequest is a wrapper Boolean which can be null but VideoCallParameterRepository.updateCallStatusAndRecording expects a primitive boolean; change the field type in UpdateCallRequest from Boolean to primitive boolean (isLinkUsed) so it cannot be null, or if null semantics are required, add explicit null-handling in the service layer before calling VideoCallParameterRepository.updateCallStatusAndRecording (e.g., defaulting null to false or branching) to avoid NPE on auto-unboxing.src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java-155-175 (1)
155-175:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winKeep default label/placeholder when localized text is missing.
Current assignments can replace non-null defaults with null if the translation row exists but language-specific value is empty. Keep fallback to
field.getLabel()/field.getPlaceholder()in that case.Suggested fix
- if (label != null) { + if (label != null) { + String localized = null; if ("hi".equalsIgnoreCase(lang)) { - translatedLabel = label.getHindiTranslation(); + localized = label.getHindiTranslation(); } else if ("as".equalsIgnoreCase(lang)) { - translatedLabel = label.getAssameseTranslation(); + localized = label.getAssameseTranslation(); } else if ("en".equalsIgnoreCase(lang)) { - translatedLabel = label.getEnglish(); - + localized = label.getEnglish(); } + if (localized != null && !localized.isBlank()) translatedLabel = localized; } @@ - if (placeHolder != null) { + if (placeHolder != null) { + String localizedPlaceholder = null; if ("hi".equalsIgnoreCase(lang)) { - translatedPlaceHolder= placeHolder.getHindiTranslation(); + localizedPlaceholder = placeHolder.getHindiTranslation(); } else if ("as".equalsIgnoreCase(lang)) { - translatedPlaceHolder = placeHolder.getAssameseTranslation(); + localizedPlaceholder = placeHolder.getAssameseTranslation(); } else if ("en".equalsIgnoreCase(lang)) { - translatedPlaceHolder = placeHolder.getEnglish(); - + localizedPlaceholder = placeHolder.getEnglish(); } + if (localizedPlaceholder != null && !localizedPlaceholder.isBlank()) { + translatedPlaceHolder = localizedPlaceholder; + } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java` around lines 155 - 175, The code in FormMasterServiceImpl currently assigns translatedLabel/translatedPlaceHolder directly from translation rows which can be null/empty and overwrite non-null defaults; change the logic so after selecting the language-specific value from label or placeHolder you only set translatedLabel/translatedPlaceHolder when that language-specific string is non-null and not empty (trimmed); otherwise leave the existing fallback from field.getLabel() / field.getPlaceholder() intact. Locate the blocks referencing label, placeHolder, translatedLabel, translatedPlaceHolder and field.getLabel()/field.getPlaceholder() and add a conditional check (non-null && !isEmpty/trimmed) before assigning the translated values.src/main/java/com/iemr/common/utils/config/ConfigProperties.java-157-157 (1)
157-157:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winGuard missing property before calling
trim()At Line 157,
properties.getProperty(propertyName)can benull; callingtrim()causes an avoidable NPE path.
Use a null-safe fallback before trim.Proposed fix
- result = properties.getProperty(propertyName).trim(); + String raw = properties.getProperty(propertyName); + result = (raw != null) ? raw.trim() : null;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/java/com/iemr/common/utils/config/ConfigProperties.java` at line 157, The code in ConfigProperties (around the use of properties.getProperty(propertyName) assigned to result) calls trim() without null-checking and can NPE; update the logic in the method using properties.getProperty(propertyName) so you first retrieve the raw value into a local (e.g., raw = properties.getProperty(propertyName)), check for null (or use a default like empty string), then call trim() on the non-null value and assign to result; ensure the method still returns the expected fallback when the property is absent.
🧹 Nitpick comments (5)
src/main/java/com/iemr/common/utils/JwtUserIdValidationFilter.java (1)
223-228: 💤 Low valueExtract constant for duplicated path literal.
The literal
"/video-consultation/resolve"is duplicated 3 times across the file. Extract it to a constant for maintainability.♻️ Proposed refactor
public class JwtUserIdValidationFilter implements Filter { private final JwtAuthenticationUtil jwtAuthenticationUtil; private final Logger logger = LoggerFactory.getLogger(this.getClass().getName()); private final String allowedOrigins; + private static final String VIDEO_CONSULTATION_RESOLVE_PATH = "/video-consultation/resolve"; // ... in isVideoConsultationResolvePath: - return normalized.endsWith("/video-consultation/resolve") - || normalized.contains("/video-consultation/resolve"); + return normalized.endsWith(VIDEO_CONSULTATION_RESOLVE_PATH) + || normalized.contains(VIDEO_CONSULTATION_RESOLVE_PATH); // ... in shouldSkipAuthentication: - || path.endsWith("/video-consultation/resolve"); + || path.endsWith(VIDEO_CONSULTATION_RESOLVE_PATH);Also applies to: 276-280
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/java/com/iemr/common/utils/JwtUserIdValidationFilter.java` around lines 223 - 228, Extract the duplicated path literal "/video-consultation/resolve" into a single private static final String constant (e.g., VIDEO_CONSULTATION_RESOLVE) in the JwtUserIdValidationFilter class and replace all usages (including in isVideoConsultationResolvePath and the other occurrences around the ~276-280 region) to reference that constant; update isVideoConsultationResolvePath to use the constant for both endsWith and contains checks to avoid repeating the literal.src/main/java/com/iemr/common/controller/videocall/VideoCallController.java (1)
141-144: ⚡ Quick winAdd slug format validation.
The validation only checks if the slug is null or empty, but doesn't validate its format, length, or character set. A malformed slug could cause issues downstream or enable injection attacks if it's used to construct database queries or file paths.
🛡️ Proposed validation enhancement
if (slug == null || slug.isEmpty()) { response.put("error", "slug is required"); return ResponseEntity.badRequest().body(response); } + if (!slug.matches("^[a-zA-Z0-9_-]{8,32}$")) { + response.put("error", "Invalid slug format"); + return ResponseEntity.badRequest().body(response); + }Adjust the regex pattern to match your slug generation logic.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/java/com/iemr/common/controller/videocall/VideoCallController.java` around lines 141 - 144, The current slug null/empty check in VideoCallController needs stronger validation: after the existing if (slug == null || slug.isEmpty()) block, validate slug against an allowed pattern and length (e.g., /^[a-z0-9\-]{3,64}$/i or your slug rules) to ensure only permitted characters and a reasonable length are accepted; if the slug fails the pattern/length check, put an explanatory error like "invalid slug format" into the response map and return ResponseEntity.badRequest().body(response). Ensure you update the validation where the slug variable is used (in VideoCallController) so downstream uses can assume a sanitized slug.src/main/java/com/iemr/common/data/cti/DispositionCountRequest.java (1)
6-10: ⚡ Quick winDecouple Java naming from wire-format keys.
On Line 6–Line 10, using snake_case Java fields couples style to payload format and keeps Sonar warnings active. Prefer camelCase fields with explicit JSON key mapping so the CTI contract stays unchanged.
Proposed refactor
+import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.gson.annotations.SerializedName; import lombok.Data; @@ public class DispositionCountRequest { - private String transaction_id = "CTI_GET_DISP_COUNT"; - private String campaign_name; + `@JsonProperty`("transaction_id") + `@SerializedName`("transaction_id") + private String transactionId = "CTI_GET_DISP_COUNT"; + + `@JsonProperty`("campaign_name") + `@SerializedName`("campaign_name") + private String campaignName; + private String disposition; private String date; - private String enc_flag; + + `@JsonProperty`("enc_flag") + `@SerializedName`("enc_flag") + private String encFlag; }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/java/com/iemr/common/data/cti/DispositionCountRequest.java` around lines 6 - 10, Rename the snake_case fields in DispositionCountRequest to camelCase (e.g., transactionId, campaignName, encFlag) and annotate each with the appropriate JSON mapping annotation (e.g., `@JsonProperty`("transaction_id"), `@JsonProperty`("campaign_name"), `@JsonProperty`("enc_flag")) so the external CTI wire keys remain unchanged; update any getters/setters to match the new field names and leave the JSON property names as the original snake_case values to avoid changing the contract.src/main/java/com/iemr/common/data/cti/DispositionCountResponse.java (1)
20-30: ⚡ Quick winUse a typed
datafield instead ofObject.On Line 20,
Object dataremoves compile-time checks and makes downstream parsing brittle, despite havingCampaignData/DispositionDetailmodels ready.Proposed refactor
- private Object data; + private List<CampaignData> data;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/java/com/iemr/common/data/cti/DispositionCountResponse.java` around lines 20 - 30, Replace the untyped field "data" in DispositionCountResponse with a concrete type using the existing inner class CampaignData (e.g. change "private Object data" to "private CampaignData data" or "private List<CampaignData> data" depending on the JSON shape), and update any getters/setters and usages to that type; ensure JSON annotations (Jackson/Gson) remain compatible with CampaignData and DispositionDetail so deserialization/type-safety is restored in the DispositionCountResponse class.src/main/java/com/iemr/common/service/users/AshaSupervisorLoginService.java (1)
76-197: ⚡ Quick winExtract repeated JSON keys into constants.
Keys like
"userId","facilityId","facilityType", and"fullName"are repeated many times; centralizing them avoids typo drift and addresses the current Sonar failures.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/main/java/com/iemr/common/service/users/AshaSupervisorLoginService.java` around lines 76 - 197, Several JSON key strings (e.g., "userId", "facilityId", "facilityType", "fullName", "employeeId", "facilityName", "mobile") are duplicated across methods like buildAshaList, enrichAshaSupervisorData, enrichGeneralFacilityData, and the earlier ASHA logic; create a central set of constants (private static final String ...) in AshaSupervisorLoginService for these keys and replace all literal usages in methods populateLocation, buildAshaList, enrichAshaSupervisorData, enrichGeneralFacilityData (and the earlier facility/peer/supervisor blocks) to reference those constants to eliminate duplication and prevent typos.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/main/environment/common_example.properties`:
- Around line 237-243: The example config contains a concrete secret value for
JITSI_APP_SECRET; replace that hardcoded secret with a non-sensitive placeholder
(e.g. a clearly labeled token placeholder like "<YOUR_JITSI_APP_SECRET>") in the
example file so the example remains useful but contains no real credentials;
update the JITSI_APP_SECRET entry in
src/main/environment/common_example.properties and ensure no other example
entries (e.g., JITSI_APP_ID/JITSI_DEFAULT_USER_EMAIL) contain real secrets.
In
`@src/main/java/com/iemr/common/controller/cti/ComputerTelephonyIntegrationController.java`:
- Around line 530-533: The X-FORWARDED-FOR header may contain multiple
comma-separated IPs; update the remoteAddress assignment (where
serverRequest.getHeader("X-FORWARDED-FOR") is read) to normalize it by splitting
on commas, trimming entries, and selecting the first non-empty token as the
client IP (falling back to serverRequest.getRemoteAddr() if none); ensure this
normalized remoteAddress is what you pass downstream (the same variable used
later in this controller) so CTI receives a single valid IP.
In `@src/main/java/com/iemr/common/controller/videocall/VideoCallController.java`:
- Around line 152-159: The current parsing around agentUrl using
lastIndexOf("?jwt=") and substrings is fragile; update the VideoCallController
logic that computes jwtIdx, jwt, pathPart and roomName to use a proper URI/URL
parser (e.g., java.net.URI) to parse the query string and path safely, validate
that the "jwt" query parameter exists before extracting it, defensively extract
the last path segment as roomName only when the path is present and non-empty,
and handle/return an error or skip adding to the response map if validation
fails (log the issue via the controller's logger). Ensure you update the block
that references agentUrl, jwtIdx, jwt, pathPart, roomName and response to
perform parsing, validation and error handling instead of raw substring
operations.
- Around line 194-205: Replace brittle message-based branching in
VideoCallController.resolveMeetingLink by introducing and using explicit
exception types from the service layer: add MeetingLinkExpiredException and
MeetingLinkNotFoundException (or reuse specific subclasses) and have the service
throw them instead of generic Exception; then update the controller catch blocks
to catch MeetingLinkExpiredException and return
ResponseEntity.status(HttpStatus.GONE).build(), catch
MeetingLinkNotFoundException and return
ResponseEntity.status(HttpStatus.NOT_FOUND).build(), keep the
IllegalArgumentException handling as-is, and log exceptions with the full
throwable (logger.error(..., e)) rather than inspecting e.getMessage(); this
removes the fragile string contains check and ties HTTP status to exception
classes.
In `@src/main/java/com/iemr/common/model/beneficiary/BeneficiaryModel.java`:
- Around line 121-124: The field doYouHavechildren in BeneficiaryModel is
declared as a primitive boolean which cannot represent null/unknown during JSON
deserialization; change the field type to the wrapper Boolean (i.e., private
Boolean doYouHavechildren) so missing or null JSON values are preserved, and
update any corresponding getter/setter signatures/methods that reference
doYouHavechildren to use Boolean instead to avoid autoboxing surprises during
Gson/Jackson deserialization.
In `@src/main/java/com/iemr/common/repository/users/FacilityLoginRepo.java`:
- Around line 38-44: The query in getSupervisorForAsha selects with LIMIT 1 but
no ORDER BY or facility filter, causing nondeterministic supervisor selection;
update the native query to scope by facility (e.g., add AND asm.facilityId =
:facilityId or the correct facility column) and add a deterministic ORDER BY
(for example ORDER BY asm.createdAt DESC or ORDER BY asm.id DESC) before LIMIT
1, and update the method signature getSupervisorForAsha(Integer ashaUserID) to
accept the facility parameter (e.g., Integer facilityId) with
`@Param`("facilityId") so the repo returns a predictable supervisor for the ASHA
mapping.
- Around line 23-35: The query used by getFacilityDetails(...) returns an
unordered result but downstream code treats the first row as canonical; make
ordering deterministic by adding an ORDER BY clause (e.g., "ORDER BY
f.FacilityID ASC" or another stable column like f.FacilityName if preferred) to
the native SQL (the query string in FacilityLoginRepo.getFacilityDetails) so
repeated calls return rows in a stable, predictable order.
In
`@src/main/java/com/iemr/common/service/beneficiary/IdentityBeneficiaryServiceImpl.java`:
- Around line 24-27: The file IdentityBeneficiaryServiceImpl contains duplicate
import declarations; remove the redundant duplicate import lines so each type is
imported exactly once—specifically deduplicate java.net.URLEncoder,
java.nio.charset.StandardCharsets, java.util.Collections, java.util.Map,
org.springframework.beans.factory.annotation.Value, and the Jackson imports
(com.fasterxml.jackson.databind.DeserializationFeature,
com.fasterxml.jackson.databind.JsonNode,
com.fasterxml.jackson.databind.ObjectMapper) in the
IdentityBeneficiaryServiceImpl class so only a single import statement remains
for each symbol.
In
`@src/main/java/com/iemr/common/service/beneficiary/RegisterBenificiaryServiceImpl.java`:
- Around line 242-252: The logs in RegisterBenificiaryServiceImpl print the full
phone number when sending SMS; create and use a small masking helper (e.g.,
maskPhone(String phoneNo)) and call it when logging instead of phoneNo so PII is
not emitted. Update the block around
welcomeBenificarySmsService.sendWelcomeSMStoBenificiary(...) and the
logger.info("[SMS] Attempting to send welcome SMS to: ...") / logger.warn(...)
calls to pass maskedPhone = maskPhone(phoneNo) into the log messages (and ensure
maskPhone returns a safe fallback like "****" for null/short inputs).
In `@src/main/java/com/iemr/common/service/cti/CTIServiceImpl.java`:
- Around line 973-975: The current CTIServiceImpl code logs the full POST
payload (variable Json) using logger.info around the httpUtils.post call; remove
or avoid logging the raw Json at info level—either drop the Json from the info
log and only log the URL (logger.info with urlRequest and result), or move the
payload log to debug level and pass a sanitized/redacted payload (use a
redact/mask helper) before logging; update the statements around logger.info,
httpUtils.post, and the variables urlRequest/Json so sensitive data is not
written to info-level logs.
In
`@src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java`:
- Around line 195-215: The current block sets dto.setOptions(null) whenever
field.getOptionKey() is blank, which discards legacy inline options stored on
the FormField; instead, when field.getOptionKey() is null/blank call should fall
back to parsing the field.getOptions() JSON (or appropriate getter on the
FormField) into the same List<Map<String,Object>> shape and set dto.setOptions
to that parsed list (or null if the parsed list is empty); keep the existing
behavior for the optionKey-present branch using
formFieldOptionRepo.findByOptionKeyOrderBySortOrderAsc(...) and the
language-specific label logic, and ensure dto.setOptions is only null when there
are no options from either source.
In `@src/main/java/com/iemr/common/service/feedback/FeedbackServiceImpl.java`:
- Around line 754-755: Update the URL construction for fileUIDAsURI in
FeedbackServiceImpl so encoded KM secrets are preserved: when building
fileUIDAsURI (using dmsProtocol, userName, userPassword, dmsPath, fileUID),
conditionally URL-encode the password for normal credentials but leave it
verbatim if it begins with the KM prefix "0X10:" (i.e., if
userPassword.startsWith("0X10:") use userPassword as-is; otherwise use
URLEncoder.encode(userPassword, StandardCharsets.UTF_8)). This ensures existing
"0X10:<base64>" secrets are not altered while still safely encoding ordinary
passwords.
In `@src/main/java/com/iemr/common/service/services/CommonServiceImpl.java`:
- Around line 246-249: The log prints the credential-bearing URL stored in
fileUIDAsURI (in CommonServiceImpl), leaking secrets; replace that
logger.info("file url="+fileUIDAsURI) with a sanitized value that removes or
masks URI userinfo (e.g., strip/replace URI.getUserInfo() or build a URI without
userinfo) and log only non-sensitive parts such as host, path or a masked URL;
ensure any other logs referencing fileUIDAsURI or similar credential-bearing
strings are updated (keep the other logs for subCategory.getSubCatFilePath(),
dmsPath, subCatFilePath unchanged if they contain no secrets).
In `@src/main/java/com/iemr/common/service/users/AshaSupervisorLoginService.java`:
- Around line 73-97: The peers fetch is using facilityIDs (all mapped
facilities) but the UI shows only the single facility from facilityRows.get(0);
change the peers query to be scoped to that displayed facility by extracting the
chosen facility id (the value used in facility.put("facilityId", row[0])) and
call facilityLoginRepo.getPeersAtFacility with that single facility id (or
provide a new method like getPeersAtFacility(Long facilityId, Long ashaUserID))
instead of facilityIDs so peers returned match the displayed facility; update
any variable names (e.g., row, facility, peerRows) accordingly.
In
`@src/main/java/com/iemr/common/service/welcomeSms/WelcomeBenificarySmsServiceImpl.java`:
- Around line 50-65: The code uses a shared instance field smsTemplate in the
async singleton service which can leak state across concurrent
sendWelcomeSMStoBenificiary calls; change smsTemplate to a local variable inside
sendWelcomeSMStoBenificiary (remove or stop using the instance field
smsTemplate), fetch the SMSTemplate via
smsTemplateRepository.findBySmsTemplateName(smsTemplateName) and assign its
getSmsTemplate() to a method-local String (e.g., localSmsTemplate), then use
that local variable when building message; ensure no other methods reference the
instance field so it can be removed to make the service thread-safe.
- Around line 52-53: The method sendWelcomeSMStoBenificiary in
WelcomeBenificarySmsServiceImpl is annotated with `@Async` but returns String;
change its signature to return CompletableFuture<String> (import
java.util.concurrent.CompletableFuture), update return statements to return
CompletableFuture.completedFuture(yourString) (or
CompletableFuture.completedFuture(null) if previously returned null), and ensure
any callers are updated to handle the CompletableFuture; keep the `@Async` on
sendWelcomeSMStoBenificiary and adjust method internals to produce/return a
CompletableFuture.
In `@src/main/java/com/iemr/common/utils/JitsiJwtUtil.java`:
- Around line 65-66: The ttlSeconds field in JitsiJwtUtil can be zero or
negative which will create already-expired tokens; add a guard where ttlSeconds
is used to compute the token expiry (the token-generation method in class
JitsiJwtUtil that uses ttlSeconds) to validate ttlSeconds > 0 and if not either
set it to a sane default (e.g. 3600L) or throw a clear IllegalArgumentException;
include a short log warning if you coerce the value. Ensure you reference and
update the ttlSeconds field validation before computing expiry so subsequent
math uses the corrected positive value.
In
`@src/test/java/com/iemr/common/controller/videocall/VideoCallControllerTest.java`:
- Around line 201-234: Add a new unit test to cover the controller branch that
returns 410 Gone when the resolveMeetingLink exception message contains "already
been used": in VideoCallControllerTest add a test (e.g.,
shouldReturn410Gone_whenResolveMeetingLinkAlreadyUsed) that stubs
videoCallService.resolveMeetingLink("used-slug") to throw new Exception("This
meeting link has already been used"), performs
mockMvc.get("/video-consultation/resolve").param("m","used-slug"), asserts
status().isGone(), and verifies videoCallService.resolveMeetingLink("used-slug")
was called once.
---
Outside diff comments:
In `@src/main/java/com/iemr/common/CommonApplication.java`:
- Around line 32-44: CommonApplication and CommonMain enable async but Spring
may fall back to an unbounded SimpleAsyncTaskExecutor because the existing bean
is named asyncExecutor() and returns Executor; update the async executor bean so
`@EnableAsync` discovers it by either (A) changing the bean signature/name in
CommonMain from "public Executor asyncExecutor()" to expose the TaskExecutor
type and/or rename it to "taskExecutor" (e.g., public TaskExecutor
taskExecutor()) or (B) explicitly bind `@EnableAsync` to the bean name used (e.g.,
`@EnableAsync`("asyncExecutor")), ensuring the bean is a bounded
ThreadPoolTaskExecutor configured with core/max pool sizes and queue capacity so
async methods use the bounded pool rather than the unbounded
SimpleAsyncTaskExecutor.
In `@src/main/java/com/iemr/common/controller/users/IEMRAdminController.java`:
- Around line 341-355: Move the null-check for the retrieved user before any
calls to user.getDeleted() or user.getStatusID() in IEMRAdminController so you
never invoke methods on a null reference; then use null-safe checks for the
boxed Boolean and Integer returns (e.g., replace direct user.getDeleted() with
Boolean.TRUE.equals(user.getDeleted()) and guard user.getStatusID() with a null
check like user.getStatusID() != null && user.getStatusID() > 2) to avoid
unboxing NPEs while preserving the existing log/response behavior.
In `@src/main/java/com/iemr/common/utils/km/openkm/OpenKMServiceImpl.java`:
- Around line 84-95: The init() method is annotated with `@PostConstruct` and
should run once to initialize the OpenKM connector, so remove the repeated
this.init() calls from service methods (e.g., getDocumentRoot(), and other
methods that currently call this.init()) to avoid reinitializing the connector
per call; instead rely on the `@PostConstruct` initialization and add a safe
null-check around the connector (e.g., if (connector == null) { throw new
IllegalStateException("OpenKM connector not initialized"); } or implement a
synchronized lazy-initialize helper) so methods like getDocumentRoot(),
fetchDocument(...), listFolder(...), etc., do not recreate connector objects on
each invocation.
---
Minor comments:
In
`@src/main/java/com/iemr/common/controller/beneficiary/BeneficiaryRegistrationController.java`:
- Around line 39-40: The file BeneficiaryRegistrationController contains
duplicate import statements (e.g., RequestHeader, CookieUtil, JwtUtil) — remove
the redundant repeated imports so each type is imported only once; locate the
duplicate import lines in BeneficiaryRegistrationController and delete the
repeated occurrences, leaving a single import for each required class to satisfy
lint/checkstyle rules.
In `@src/main/java/com/iemr/common/model/videocall/UpdateCallRequest.java`:
- Line 34: The field isLinkUsed in UpdateCallRequest is a wrapper Boolean which
can be null but VideoCallParameterRepository.updateCallStatusAndRecording
expects a primitive boolean; change the field type in UpdateCallRequest from
Boolean to primitive boolean (isLinkUsed) so it cannot be null, or if null
semantics are required, add explicit null-handling in the service layer before
calling VideoCallParameterRepository.updateCallStatusAndRecording (e.g.,
defaulting null to false or branching) to avoid NPE on auto-unboxing.
In
`@src/main/java/com/iemr/common/service/beneficiary/IEMRSearchUserServiceImpl.java`:
- Line 29: The file IEMRSearchUserServiceImpl contains duplicate import
declarations for java.util.Map and
com.iemr.common.utils.exception.IEMRException; remove the redundant import lines
so each type is imported exactly once (leave a single import for java.util.Map
and a single import for com.iemr.common.utils.exception.IEMRException), then run
a quick compile/static-check to ensure no unused-import warnings remain and
adjust imports if any are unused.
In
`@src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java`:
- Around line 155-175: The code in FormMasterServiceImpl currently assigns
translatedLabel/translatedPlaceHolder directly from translation rows which can
be null/empty and overwrite non-null defaults; change the logic so after
selecting the language-specific value from label or placeHolder you only set
translatedLabel/translatedPlaceHolder when that language-specific string is
non-null and not empty (trimmed); otherwise leave the existing fallback from
field.getLabel() / field.getPlaceholder() intact. Locate the blocks referencing
label, placeHolder, translatedLabel, translatedPlaceHolder and
field.getLabel()/field.getPlaceholder() and add a conditional check (non-null &&
!isEmpty/trimmed) before assigning the translated values.
In `@src/main/java/com/iemr/common/utils/config/ConfigProperties.java`:
- Line 157: The code in ConfigProperties (around the use of
properties.getProperty(propertyName) assigned to result) calls trim() without
null-checking and can NPE; update the logic in the method using
properties.getProperty(propertyName) so you first retrieve the raw value into a
local (e.g., raw = properties.getProperty(propertyName)), check for null (or use
a default like empty string), then call trim() on the non-null value and assign
to result; ensure the method still returns the expected fallback when the
property is absent.
In `@src/test/java/com/iemr/common/utils/JitsiJwtUtilTest.java`:
- Around line 44-45: Replace the production-like APP_SECRET value in
JitsiJwtUtilTest with an obviously synthetic test secret (still at least 32
bytes) to avoid leak-scanner noise; update the static constant APP_SECRET in the
JitsiJwtUtilTest class to a clearly fake hex or alphanumeric string (e.g.,
repeated pattern or "TEST_SECRET_..." long enough) while leaving SUB and other
test logic unchanged.
---
Nitpick comments:
In `@src/main/java/com/iemr/common/controller/videocall/VideoCallController.java`:
- Around line 141-144: The current slug null/empty check in VideoCallController
needs stronger validation: after the existing if (slug == null ||
slug.isEmpty()) block, validate slug against an allowed pattern and length
(e.g., /^[a-z0-9\-]{3,64}$/i or your slug rules) to ensure only permitted
characters and a reasonable length are accepted; if the slug fails the
pattern/length check, put an explanatory error like "invalid slug format" into
the response map and return ResponseEntity.badRequest().body(response). Ensure
you update the validation where the slug variable is used (in
VideoCallController) so downstream uses can assume a sanitized slug.
In `@src/main/java/com/iemr/common/data/cti/DispositionCountRequest.java`:
- Around line 6-10: Rename the snake_case fields in DispositionCountRequest to
camelCase (e.g., transactionId, campaignName, encFlag) and annotate each with
the appropriate JSON mapping annotation (e.g., `@JsonProperty`("transaction_id"),
`@JsonProperty`("campaign_name"), `@JsonProperty`("enc_flag")) so the external CTI
wire keys remain unchanged; update any getters/setters to match the new field
names and leave the JSON property names as the original snake_case values to
avoid changing the contract.
In `@src/main/java/com/iemr/common/data/cti/DispositionCountResponse.java`:
- Around line 20-30: Replace the untyped field "data" in
DispositionCountResponse with a concrete type using the existing inner class
CampaignData (e.g. change "private Object data" to "private CampaignData data"
or "private List<CampaignData> data" depending on the JSON shape), and update
any getters/setters and usages to that type; ensure JSON annotations
(Jackson/Gson) remain compatible with CampaignData and DispositionDetail so
deserialization/type-safety is restored in the DispositionCountResponse class.
In `@src/main/java/com/iemr/common/service/users/AshaSupervisorLoginService.java`:
- Around line 76-197: Several JSON key strings (e.g., "userId", "facilityId",
"facilityType", "fullName", "employeeId", "facilityName", "mobile") are
duplicated across methods like buildAshaList, enrichAshaSupervisorData,
enrichGeneralFacilityData, and the earlier ASHA logic; create a central set of
constants (private static final String ...) in AshaSupervisorLoginService for
these keys and replace all literal usages in methods populateLocation,
buildAshaList, enrichAshaSupervisorData, enrichGeneralFacilityData (and the
earlier facility/peer/supervisor blocks) to reference those constants to
eliminate duplication and prevent typos.
In `@src/main/java/com/iemr/common/utils/JwtUserIdValidationFilter.java`:
- Around line 223-228: Extract the duplicated path literal
"/video-consultation/resolve" into a single private static final String constant
(e.g., VIDEO_CONSULTATION_RESOLVE) in the JwtUserIdValidationFilter class and
replace all usages (including in isVideoConsultationResolvePath and the other
occurrences around the ~276-280 region) to reference that constant; update
isVideoConsultationResolvePath to use the constant for both endsWith and
contains checks to avoid repeating the literal.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: b00cb1ff-79f8-446b-9bb8-8ed539f880fc
📒 Files selected for processing (67)
pom.xmlsrc/main/environment/common_ci.propertiessrc/main/environment/common_docker.propertiessrc/main/environment/common_example.propertiessrc/main/java/com/iemr/common/CommonApplication.javasrc/main/java/com/iemr/common/config/InterceptorConfig.javasrc/main/java/com/iemr/common/config/encryption/SecurePassword.javasrc/main/java/com/iemr/common/controller/beneficiary/BeneficiaryRegistrationController.javasrc/main/java/com/iemr/common/controller/cti/ComputerTelephonyIntegrationController.javasrc/main/java/com/iemr/common/controller/dynamicForm/DynamicFormController.javasrc/main/java/com/iemr/common/controller/users/IEMRAdminController.javasrc/main/java/com/iemr/common/controller/version/VersionController.javasrc/main/java/com/iemr/common/controller/videocall/VideoCallController.javasrc/main/java/com/iemr/common/data/cti/DispositionCountRequest.javasrc/main/java/com/iemr/common/data/cti/DispositionCountResponse.javasrc/main/java/com/iemr/common/data/dynamic_from/FormField.javasrc/main/java/com/iemr/common/data/dynamic_from/FormFieldOption.javasrc/main/java/com/iemr/common/data/translation/Translation.javasrc/main/java/com/iemr/common/data/users/AshaSupervisorMapping.javasrc/main/java/com/iemr/common/data/users/User.javasrc/main/java/com/iemr/common/data/videocall/VideoCallParameters.javasrc/main/java/com/iemr/common/dto/dynamicForm/FieldResponseDTO.javasrc/main/java/com/iemr/common/mapper/videocall/VideoCallMapper.javasrc/main/java/com/iemr/common/model/beneficiary/BeneficiaryModel.javasrc/main/java/com/iemr/common/model/user/LoginResponseModel.javasrc/main/java/com/iemr/common/model/videocall/UpdateCallRequest.javasrc/main/java/com/iemr/common/model/videocall/UpdateCallResponse.javasrc/main/java/com/iemr/common/model/videocall/VideoCallRequest.javasrc/main/java/com/iemr/common/repository/dynamic_form/FieldRepository.javasrc/main/java/com/iemr/common/repository/dynamic_form/FormFieldOptionRepository.javasrc/main/java/com/iemr/common/repository/translation/TranslationRepo.javasrc/main/java/com/iemr/common/repository/users/AshaSupervisorLoginRepo.javasrc/main/java/com/iemr/common/repository/users/FacilityLoginRepo.javasrc/main/java/com/iemr/common/repository/users/IEMRUserRepositoryCustom.javasrc/main/java/com/iemr/common/repository/videocall/VideoCallParameterRepository.javasrc/main/java/com/iemr/common/service/beneficiary/IEMRSearchUserServiceImpl.javasrc/main/java/com/iemr/common/service/beneficiary/IdentityBeneficiaryServiceImpl.javasrc/main/java/com/iemr/common/service/beneficiary/RegisterBenificiaryServiceImpl.javasrc/main/java/com/iemr/common/service/beneficiaryOTPHandler/BeneficiaryOTPHandlerImpl.javasrc/main/java/com/iemr/common/service/cti/CTIService.javasrc/main/java/com/iemr/common/service/cti/CTIServiceImpl.javasrc/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.javasrc/main/java/com/iemr/common/service/feedback/FeedbackServiceImpl.javasrc/main/java/com/iemr/common/service/kmfilemanager/KMFileManagerServiceImpl.javasrc/main/java/com/iemr/common/service/nhm_dashboard/NHM_DashboardServiceImpl.javasrc/main/java/com/iemr/common/service/notification/NotificationServiceImpl.javasrc/main/java/com/iemr/common/service/scheme/SchemeServiceImpl.javasrc/main/java/com/iemr/common/service/services/CommonServiceImpl.javasrc/main/java/com/iemr/common/service/sms/SMSServiceImpl.javasrc/main/java/com/iemr/common/service/users/AshaSupervisorLoginService.javasrc/main/java/com/iemr/common/service/users/IEMRAdminUserService.javasrc/main/java/com/iemr/common/service/users/IEMRAdminUserServiceImpl.javasrc/main/java/com/iemr/common/service/videocall/VideoCallService.javasrc/main/java/com/iemr/common/service/videocall/VideoCallServiceImpl.javasrc/main/java/com/iemr/common/service/welcomeSms/WelcomeBenificarySmsServiceImpl.javasrc/main/java/com/iemr/common/utils/IEMRApplBeans.javasrc/main/java/com/iemr/common/utils/JitsiJwtUtil.javasrc/main/java/com/iemr/common/utils/JwtAuthenticationUtil.javasrc/main/java/com/iemr/common/utils/JwtUserIdValidationFilter.javasrc/main/java/com/iemr/common/utils/JwtUtil.javasrc/main/java/com/iemr/common/utils/config/ConfigProperties.javasrc/main/java/com/iemr/common/utils/http/HTTPRequestInterceptor.javasrc/main/java/com/iemr/common/utils/km/openkm/OpenKMServiceImpl.javasrc/main/resources/application.propertiessrc/test/java/com/iemr/common/controller/videocall/VideoCallControllerTest.javasrc/test/java/com/iemr/common/service/videocall/VideoCallServiceImplTest.javasrc/test/java/com/iemr/common/utils/JitsiJwtUtilTest.java
💤 Files with no reviewable changes (2)
- src/main/java/com/iemr/common/config/encryption/SecurePassword.java
- src/main/java/com/iemr/common/utils/JwtAuthenticationUtil.java
| JITSI_APP_ID=piramal_vc | ||
| JITSI_APP_SECRET=5b9883418be6f228ffe3ceaa74dd3d3b91737733a4a85c5e82fc584ad449850b | ||
| JITSI_DOMAIN=vc.piramalswasthya.org | ||
| JITSI_SUB=meet.jitsi | ||
| JITSI_TOKEN_TTL_SECONDS=3600 | ||
| JITSI_ROOM_PREFIX=piramal-meeting- | ||
| JITSI_DEFAULT_USER_EMAIL=admin@piramalswasthya.org |
There was a problem hiding this comment.
Do not commit a concrete Jitsi app secret in example config.
Line 238 includes a hardcoded secret-like value. Replace with a non-secret placeholder to prevent credential leakage and accidental reuse across environments.
Suggested fix
-JITSI_APP_SECRET=5b9883418be6f228ffe3ceaa74dd3d3b91737733a4a85c5e82fc584ad449850b
+JITSI_APP_SECRET=<set-via-env-or-secret-manager>📝 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.
| JITSI_APP_ID=piramal_vc | |
| JITSI_APP_SECRET=5b9883418be6f228ffe3ceaa74dd3d3b91737733a4a85c5e82fc584ad449850b | |
| JITSI_DOMAIN=vc.piramalswasthya.org | |
| JITSI_SUB=meet.jitsi | |
| JITSI_TOKEN_TTL_SECONDS=3600 | |
| JITSI_ROOM_PREFIX=piramal-meeting- | |
| JITSI_DEFAULT_USER_EMAIL=admin@piramalswasthya.org | |
| JITSI_APP_ID=piramal_vc | |
| JITSI_APP_SECRET=<set-via-env-or-secret-manager> | |
| JITSI_DOMAIN=vc.piramalswasthya.org | |
| JITSI_SUB=meet.jitsi | |
| JITSI_TOKEN_TTL_SECONDS=3600 | |
| JITSI_ROOM_PREFIX=piramal-meeting- | |
| JITSI_DEFAULT_USER_EMAIL=admin@piramalswasthya.org |
🧰 Tools
🪛 Betterleaks (1.2.0)
[high] 238-238: Detected a Generic API Key, potentially exposing access to various services and sensitive operations.
(generic-api-key)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/main/environment/common_example.properties` around lines 237 - 243, The
example config contains a concrete secret value for JITSI_APP_SECRET; replace
that hardcoded secret with a non-sensitive placeholder (e.g. a clearly labeled
token placeholder like "<YOUR_JITSI_APP_SECRET>") in the example file so the
example remains useful but contains no real credentials; update the
JITSI_APP_SECRET entry in src/main/environment/common_example.properties and
ensure no other example entries (e.g., JITSI_APP_ID/JITSI_DEFAULT_USER_EMAIL)
contain real secrets.
| String remoteAddress = serverRequest.getHeader("X-FORWARDED-FOR"); | ||
| if (remoteAddress == null || remoteAddress.trim().length() == 0) { | ||
| remoteAddress = serverRequest.getRemoteAddr(); | ||
| } |
There was a problem hiding this comment.
Normalize X-FORWARDED-FOR before passing IP downstream.
On Line 530–Line 533, the header may contain multiple comma-separated addresses. Passing it unchanged can send an invalid IP to CTI.
Proposed fix
String remoteAddress = serverRequest.getHeader("X-FORWARDED-FOR");
- if (remoteAddress == null || remoteAddress.trim().length() == 0) {
+ if (remoteAddress != null && remoteAddress.contains(",")) {
+ remoteAddress = remoteAddress.split(",")[0].trim();
+ }
+ if (remoteAddress == null || remoteAddress.trim().length() == 0) {
remoteAddress = serverRequest.getRemoteAddr();
}📝 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.
| String remoteAddress = serverRequest.getHeader("X-FORWARDED-FOR"); | |
| if (remoteAddress == null || remoteAddress.trim().length() == 0) { | |
| remoteAddress = serverRequest.getRemoteAddr(); | |
| } | |
| String remoteAddress = serverRequest.getHeader("X-FORWARDED-FOR"); | |
| if (remoteAddress != null && remoteAddress.contains(",")) { | |
| remoteAddress = remoteAddress.split(",")[0].trim(); | |
| } | |
| if (remoteAddress == null || remoteAddress.trim().length() == 0) { | |
| remoteAddress = serverRequest.getRemoteAddr(); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@src/main/java/com/iemr/common/controller/cti/ComputerTelephonyIntegrationController.java`
around lines 530 - 533, The X-FORWARDED-FOR header may contain multiple
comma-separated IPs; update the remoteAddress assignment (where
serverRequest.getHeader("X-FORWARDED-FOR") is read) to normalize it by splitting
on commas, trimming entries, and selecting the first non-empty token as the
client IP (falling back to serverRequest.getRemoteAddr() if none); ensure this
normalized remoteAddress is what you pass downstream (the same variable used
later in this controller) so CTI receives a single valid IP.
| int jwtIdx = agentUrl.lastIndexOf("?jwt="); | ||
| if (jwtIdx > 0) { | ||
| String jwt = agentUrl.substring(jwtIdx + 5); | ||
| String pathPart = agentUrl.substring(0, jwtIdx); | ||
| String roomName = pathPart.substring(pathPart.lastIndexOf('/') + 1); | ||
| response.put("roomName", roomName); | ||
| response.put("jwt", jwt); | ||
| } |
There was a problem hiding this comment.
URL parsing is fragile and lacks validation.
The code uses lastIndexOf("?jwt=") and substring operations to extract jwt and roomName from the URL, but doesn't validate the URL format before parsing. If the URL doesn't match the expected structure (e.g., missing ?jwt=, malformed path), the parsing will fail silently or produce incorrect values.
🔧 Proposed fix with URI parsing and validation
String agentUrl = videoCallService.generateAgentToken(slug, agentName, agentEmail);
response.put("agentMeetingUrl", agentUrl);
- // Parse roomName and jwt out of the URL so the frontend can pass them
- // directly to JitsiMeetExternalAPI without re-parsing the URL itself.
- // URL format: https://<domain>/<roomName>?jwt=<token>
- int jwtIdx = agentUrl.lastIndexOf("?jwt=");
- if (jwtIdx > 0) {
- String jwt = agentUrl.substring(jwtIdx + 5);
- String pathPart = agentUrl.substring(0, jwtIdx);
- String roomName = pathPart.substring(pathPart.lastIndexOf('/') + 1);
- response.put("roomName", roomName);
- response.put("jwt", jwt);
- }
+ // Parse roomName and jwt out of the URL so the frontend can pass them
+ // directly to JitsiMeetExternalAPI without re-parsing the URL itself.
+ try {
+ URI uri = new URI(agentUrl);
+ String query = uri.getQuery();
+ if (query != null && query.startsWith("jwt=")) {
+ String jwt = query.substring(4);
+ String path = uri.getPath();
+ String roomName = path.substring(path.lastIndexOf('/') + 1);
+ response.put("roomName", roomName);
+ response.put("jwt", jwt);
+ } else {
+ logger.warn("Agent URL missing jwt query parameter: {}", agentUrl);
+ }
+ } catch (Exception e) {
+ logger.error("Failed to parse agent URL: {}", agentUrl, e);
+ }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/main/java/com/iemr/common/controller/videocall/VideoCallController.java`
around lines 152 - 159, The current parsing around agentUrl using
lastIndexOf("?jwt=") and substrings is fragile; update the VideoCallController
logic that computes jwtIdx, jwt, pathPart and roomName to use a proper URI/URL
parser (e.g., java.net.URI) to parse the query string and path safely, validate
that the "jwt" query parameter exists before extracting it, defensively extract
the last path segment as roomName only when the path is present and non-empty,
and handle/return an error or skip adding to the response map if validation
fails (log the issue via the controller's logger). Ensure you update the block
that references agentUrl, jwtIdx, jwt, pathPart, roomName and response to
perform parsing, validation and error handling instead of raw substring
operations.
| } catch (IllegalArgumentException e) { | ||
| logger.warn("resolveMeetingLink rejected: {}", e.getMessage()); | ||
| return ResponseEntity.badRequest().build(); | ||
| } catch (Exception e) { | ||
| logger.error("resolveMeetingLink failed for slug={}: {}", slug, e.getMessage(), e); | ||
|
|
||
|
|
||
| // Distinguish "link expired" from "not found" with a 410 Gone | ||
| if (e.getMessage() != null && e.getMessage().contains("already been used")) { | ||
| return ResponseEntity.status(HttpStatus.GONE).build(); // 410 | ||
| } | ||
| return ResponseEntity.status(HttpStatus.NOT_FOUND).build(); | ||
| } |
There was a problem hiding this comment.
Exception message matching for HTTP status is brittle.
The code determines whether to return 410 Gone vs 404 Not Found by checking if the exception message contains the substring "already been used". This coupling to the exact error message text is fragile and will break if the service layer changes its error messages.
♻️ Recommended approach using custom exceptions
Define specific exception types in the service layer:
public class MeetingLinkExpiredException extends Exception {
public MeetingLinkExpiredException(String message) {
super(message);
}
}
public class MeetingLinkNotFoundException extends Exception {
public MeetingLinkNotFoundException(String message) {
super(message);
}
}Then update the controller to catch these specific types:
try {
String redirectUrl = videoCallService.resolveMeetingLink(slug);
return ResponseEntity.status(HttpStatus.FOUND)
.location(URI.create(redirectUrl))
.build();
} catch (IllegalArgumentException e) {
logger.warn("resolveMeetingLink rejected: {}", e.getMessage());
return ResponseEntity.badRequest().build();
+ } catch (MeetingLinkExpiredException e) {
+ logger.warn("resolveMeetingLink expired: {}", e.getMessage());
+ return ResponseEntity.status(HttpStatus.GONE).build();
+ } catch (MeetingLinkNotFoundException e) {
+ logger.warn("resolveMeetingLink not found: {}", e.getMessage());
+ return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
} catch (Exception e) {
- logger.error("resolveMeetingLink failed for slug={}: {}", slug, e.getMessage(), e);
-
- // Distinguish "link expired" from "not found" with a 410 Gone
- if (e.getMessage() != null && e.getMessage().contains("already been used")) {
- return ResponseEntity.status(HttpStatus.GONE).build(); // 410
- }
- return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
+ logger.error("resolveMeetingLink failed for slug={}: {}", slug, e.getMessage(), e);
+ return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/main/java/com/iemr/common/controller/videocall/VideoCallController.java`
around lines 194 - 205, Replace brittle message-based branching in
VideoCallController.resolveMeetingLink by introducing and using explicit
exception types from the service layer: add MeetingLinkExpiredException and
MeetingLinkNotFoundException (or reuse specific subclasses) and have the service
throw them instead of generic Exception; then update the controller catch blocks
to catch MeetingLinkExpiredException and return
ResponseEntity.status(HttpStatus.GONE).build(), catch
MeetingLinkNotFoundException and return
ResponseEntity.status(HttpStatus.NOT_FOUND).build(), keep the
IllegalArgumentException handling as-is, and log exceptions with the full
throwable (logger.error(..., e)) rather than inspecting e.getMessage(); this
removes the fragile string contains check and ties HTTP status to exception
classes.
| private boolean doYouHavechildren; | ||
|
|
||
| @Expose | ||
| private Integer noOfchildren; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify downstream assumptions about doYouHavechildren null/numeric semantics.
rg -nP --type=java -C3 '\b(getDoYouHavechildren|isDoYouHavechildren)\s*\('
rg -nP --type=java -C3 '\bdoYouHavechildren\b\s*(==|!=)\s*(0|1|null|true|false)'Repository: PSMRI/Common-API
Length of output: 42
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="src/main/java/com/iemr/common/model/beneficiary/BeneficiaryModel.java"
echo "== File context around reported lines =="
nl -ba "$FILE" | sed -n '90,160p'
echo
echo "== All occurrences of doYouHavechildren in repo =="
rg -n --hidden --no-ignore-vcs --type java "\bdoYouHavechildren\b" .
echo
echo "== Any comparisons/null checks involving doYouHavechildren =="
rg -n --hidden --no-ignore-vcs --type java "\bdoYouHavechildren\b\s*(==|!=|&&|\|\|)\s*(null|0|1|true|false)" .Repository: PSMRI/Common-API
Length of output: 162
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="src/main/java/com/iemr/common/model/beneficiary/BeneficiaryModel.java"
echo "== File context around reported lines =="
cat -n "$FILE" | sed -n '90,160p'
echo
echo "== All occurrences of doYouHavechildren in repo =="
rg -n --hidden --no-ignore-vcs --type java "\bdoYouHavechildren\b" .
echo
echo "== Any assignments to doYouHavechildren (incl. setters/constructors) =="
rg -n --hidden --no-ignore-vcs --type java "\bdoYouHavechildren\b\s*=" .
echo
echo "== Any serialization/deserialization annotations near the field =="
rg -n --hidden --no-ignore-vcs --type java "Expose|JsonProperty|SerializedName|JsonIgnore|`@NotNull`|`@Null`" "$FILE" || trueRepository: PSMRI/Common-API
Length of output: 1999
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="src/main/java/com/iemr/common/model/beneficiary/BeneficiaryModel.java"
echo "== Imports/annotations in BeneficiaryModel.java =="
sed -n '1,120p' "$FILE" | cat
echo
echo "== Extract class-level Gson-related imports (if any) =="
rg -n --type java "com\.google\.gson|`@Expose`" "$FILE" || true
echo
echo "== Find Gson dependency/build usage in repo =="
rg -n --hidden --no-ignore-vcs --type java --type pom.xml --type build.gradle --type settings.gradle "gson" . | head -n 80
echo
echo "== Find Gson ObjectMapper equivalents (Jackson) if any =="
rg -n --hidden --no-ignore-vcs --type java --type pom.xml --type build.gradle "ObjectMapper|jackson|`@JsonProperty`|`@JsonIgnore`" . | head -n 80Repository: PSMRI/Common-API
Length of output: 5576
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Build files =="
find . -maxdepth 4 -type f \( -iname "pom.xml" -o -iname "build.gradle" -o -iname "build.gradle.kts" -o -iname "settings.gradle" -o -iname "settings.gradle.kts" \) | head -n 200
echo
echo "== Search for Gson usage =="
rg -n --hidden --no-ignore-vcs --type java -S "\bnew Gson\b|\bGsonBuilder\b|excludeFieldsWithoutExposeAnnotation" . | head -n 200
echo
echo "== Search for Jackson usage =="
rg -n --hidden --no-ignore-vcs --type java -S "\bnew ObjectMapper\b|\bObjectMapper\b|SerializationFeature|DeserializationFeature|FAIL_ON_UNKNOWN_PROPERTIES|ACCEPT_EMPTY_STRING_AS_NULL_OBJECT" . | head -n 200
echo
echo "== Search for gson dependency in build files =="
find . -type f -iname "pom.xml" -o -iname "build.gradle" -o -iname "build.gradle.kts" 2>/dev/null \
| while read -r f; do
echo "--- $f"
rg -n "gson" "$f" || true
doneRepository: PSMRI/Common-API
Length of output: 50372
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Where BeneficiaryModel is used =="
rg -n --hidden --no-ignore-vcs --type java "\bBeneficiaryModel\b" src/main/java | head -n 200
echo
echo "== Find Gson fromJson into BeneficiaryModel =="
rg -n --hidden --no-ignore-vcs --type java "\.fromJson\([^,]*,\s*BeneficiaryModel\.class" src/main/java || true
rg -n --hidden --no-ignore-vcs --type java "fromJson\([^)]*BeneficiaryModel" src/main/java | head -n 200 || true
echo
echo "== Find Jackson readValue into BeneficiaryModel =="
rg -n --hidden --no-ignore-vcs --type java "readValue\([^;]*BeneficiaryModel" src/main/java || true
rg -n --hidden --no-ignore-vcs --type java "objectMapper\.[a-zA-Z]+Value\([^;]*BeneficiaryModel" src/main/java || true
echo
echo "== Inspect GsonBuilder config used for input mapping =="
for f in \
src/main/java/com/iemr/common/utils/mapper/InputMapper.java \
src/main/java/com/iemr/common/utils/mapper/OutputMapper.java \
src/main/java/com/iemr/common/utils/response/OutputResponse.java
do
echo
echo "---- $f (first ~260 lines) ----"
sed -n '1,260p' "$f" | head -n 260
done 2>/dev/null || true
echo
echo "== Confirm if serializeNulls() is used anywhere with Expose restriction =="
rg -n --hidden --no-ignore-vcs --type java "excludeFieldsWithoutExposeAnnotation\(\).*serializeNulls|serializeNulls\(\).*excludeFieldsWithoutExposeAnnotation" src/main/java || trueRepository: PSMRI/Common-API
Length of output: 39020
🌐 Web query:
Gson deserialization JSON null into primitive boolean field sets false or throws
💡 Result:
If the JSON field value is literally null and the Java field is a primitive boolean (boolean), Gson does not set it to true/false from null; instead it keeps/assigns the primitive default false for that field. Gson’s official user guide states that during deserialization a missing entry in JSON results in the corresponding field being set to its default value, and explicitly lists false for booleans. It also states that by default null object fields are ignored (so when the JSON has null for an object field, Gson effectively treats it as absent, resulting in the primitive default). [1][2] In contrast, if the JSON token is not actually null (e.g., it’s a string/number when a boolean is expected), Gson can throw. The JsonReader API documents that nextBoolean throws IllegalStateException if the next token is not a boolean (and null is a different token). [3][4] So, for your exact question: - “JSON null” into a primitive boolean field: results in false (default), not an exception, because Gson ignores null object fields and primitive booleans default to false. [1][2] - An exception is mainly expected when the token is not a JSON boolean (e.g., null handling differs only insofar as it is treated as “ignored/missing” for object fields by default). [3][4] Practical implications / fixes: - Use Boolean (wrapper) instead of boolean to be able to distinguish null from false. - Or add a custom JsonDeserializer / TypeAdapter for boolean if you need special null-handling rules (e.g., throw on null).
Citations:
- 1: https://github.com/google/gson/blob/main/UserGuide.md
- 2: http://google.github.io/gson/UserGuide.html
- 3: https://javadoc.io/static/com.google.code.gson/gson/2.8.8/com.google.gson/com/google/gson/stream/JsonReader.html
- 4: https://github.com/google/gson/blob/master/gson/src/main/java/com/google/gson/stream/JsonReader.java
🌐 Web query:
Jackson FAIL_ON_NULL_FOR_PRIMITIVES default behavior null into primitive boolean
💡 Result:
The default behavior of Jackson regarding DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES depends on the version of the library being used: - In Jackson 2.x, FAIL_ON_NULL_FOR_PRIMITIVES is disabled (false) by default [1][2][3]. When a JSON null value is encountered for a Java primitive field (such as boolean), Jackson coerces the value to the primitive's default (e.g., false for boolean, 0 for int) instead of throwing an exception [4][2][3]. - In Jackson 3.x, FAIL_ON_NULL_FOR_PRIMITIVES is enabled (true) by default [5][1]. When enabled, encountering a JSON null for a primitive type results in a JsonProcessingException (or MismatchedInputException) [4][2]. To control this behavior, you can explicitly configure it on your ObjectMapper [6][4]: // Enabling the failure (ensures nulls in JSON cause an exception) objectMapper.enable(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES); // Disabling the failure (allows coercion to primitive defaults) objectMapper.disable(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES); Note that this feature specifically targets explicit JSON null tokens. It may not trigger for missing (absent) fields unless other specific configurations (such as those related to required creator properties) are also in use [5][7].
Citations:
- 1: Change default of `DeserializationFeature. FAIL_ON_NULL_FOR_PRIMITIVES` to `true` for 3.0 FasterXML/jackson-future-ideas#79
- 2: https://fasterxml.github.io/jackson-databind/javadoc/2.11/com/fasterxml/jackson/databind/DeserializationFeature.html
- 3: https://fasterxml.github.io/jackson-databind/javadoc/2.3/com/fasterxml/jackson/databind/DeserializationFeature.html
- 4: https://www.javacodegeeks.com/jackson-field-absent-vs-null-difference.html
- 5:
DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVEStreats absent field same as explicitnullFasterXML/jackson-databind#5734 - 6: https://stackoverflow.com/questions/75659006/jackson-defaulting-empty-or-null-value-of-boolean-field-to-false
- 7: https://stackoverflow.com/questions/46388774/jackson-mapper-how-to-fail-on-null-or-empty-values
Preserve nullable/unknown semantics for doYouHavechildren.
BeneficiaryModel defines @Expose private boolean doYouHavechildren; (primitive). A primitive boolean can’t represent null/unknown, so when requests are deserialized into BeneficiaryModel (both Jackson ObjectMapper.readValue(...) and Gson gson.fromJson(...) are used in BeneficiaryRegistrationController), JSON null/missing won’t stay null—Gson results in false, and Jackson won’t preserve null (it may default or fail depending on configuration).
🔧 Suggested fix
- private boolean doYouHavechildren;
+ private Boolean doYouHavechildren;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/main/java/com/iemr/common/model/beneficiary/BeneficiaryModel.java` around
lines 121 - 124, The field doYouHavechildren in BeneficiaryModel is declared as
a primitive boolean which cannot represent null/unknown during JSON
deserialization; change the field type to the wrapper Boolean (i.e., private
Boolean doYouHavechildren) so missing or null JSON values are preserved, and
update any corresponding getter/setter signatures/methods that reference
doYouHavechildren to use Boolean instead to avoid autoboxing surprises during
Gson/Jackson deserialization.
| // ASHA gets a single facility (first mapped) | ||
| Object[] row = facilityRows.get(0); | ||
| JSONObject facility = new JSONObject(); | ||
| facility.put("facilityId", row[0]); | ||
| facility.put("facilityName", str(row[1])); | ||
| facility.put("facilityType", str(row[9])); | ||
| result.put("facility", facility); | ||
|
|
||
| // Supervisor details | ||
| List<Object[]> supervisorRows = facilityLoginRepo.getSupervisorForAsha(ashaUserID); | ||
| if (supervisorRows != null && !supervisorRows.isEmpty()) { | ||
| Object[] sRow = supervisorRows.get(0); | ||
| JSONObject supervisor = new JSONObject(); | ||
| supervisor.put("userId", sRow[0]); | ||
| supervisor.put("fullName", fullName(sRow[1], sRow[2])); | ||
| supervisor.put("mobile", str(sRow[3])); | ||
| supervisor.put("employeeId", str(sRow[4]).isEmpty() ? JSONObject.NULL : str(sRow[4])); | ||
| result.put("supervisor", supervisor); | ||
| } else { | ||
| result.put("supervisor", JSONObject.NULL); | ||
| } | ||
|
|
||
| // Peers at same facility | ||
| List<Object[]> peerRows = facilityLoginRepo.getPeersAtFacility(facilityIDs, ashaUserID); | ||
| JSONArray peers = new JSONArray(); |
There was a problem hiding this comment.
Keep ASHA peersAtFacility scoped to the same facility shown in facility.
Line 74 selects one facility (facilityRows.get(0)), but Line 96 fetches peers from all mapped facilities. This can return peers unrelated to the displayed facility.
Suggested fix
Object[] row = facilityRows.get(0);
+ Integer selectedFacilityID = (Integer) row[0];
JSONObject facility = new JSONObject();
facility.put("facilityId", row[0]);
facility.put("facilityName", str(row[1]));
facility.put("facilityType", str(row[9]));
result.put("facility", facility);
@@
- List<Object[]> peerRows = facilityLoginRepo.getPeersAtFacility(facilityIDs, ashaUserID);
+ List<Integer> selectedFacilityIDs = new ArrayList<>();
+ selectedFacilityIDs.add(selectedFacilityID);
+ List<Object[]> peerRows = facilityLoginRepo.getPeersAtFacility(selectedFacilityIDs, ashaUserID);📝 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.
| // ASHA gets a single facility (first mapped) | |
| Object[] row = facilityRows.get(0); | |
| JSONObject facility = new JSONObject(); | |
| facility.put("facilityId", row[0]); | |
| facility.put("facilityName", str(row[1])); | |
| facility.put("facilityType", str(row[9])); | |
| result.put("facility", facility); | |
| // Supervisor details | |
| List<Object[]> supervisorRows = facilityLoginRepo.getSupervisorForAsha(ashaUserID); | |
| if (supervisorRows != null && !supervisorRows.isEmpty()) { | |
| Object[] sRow = supervisorRows.get(0); | |
| JSONObject supervisor = new JSONObject(); | |
| supervisor.put("userId", sRow[0]); | |
| supervisor.put("fullName", fullName(sRow[1], sRow[2])); | |
| supervisor.put("mobile", str(sRow[3])); | |
| supervisor.put("employeeId", str(sRow[4]).isEmpty() ? JSONObject.NULL : str(sRow[4])); | |
| result.put("supervisor", supervisor); | |
| } else { | |
| result.put("supervisor", JSONObject.NULL); | |
| } | |
| // Peers at same facility | |
| List<Object[]> peerRows = facilityLoginRepo.getPeersAtFacility(facilityIDs, ashaUserID); | |
| JSONArray peers = new JSONArray(); | |
| // ASHA gets a single facility (first mapped) | |
| Object[] row = facilityRows.get(0); | |
| Integer selectedFacilityID = (Integer) row[0]; | |
| JSONObject facility = new JSONObject(); | |
| facility.put("facilityId", row[0]); | |
| facility.put("facilityName", str(row[1])); | |
| facility.put("facilityType", str(row[9])); | |
| result.put("facility", facility); | |
| // Supervisor details | |
| List<Object[]> supervisorRows = facilityLoginRepo.getSupervisorForAsha(ashaUserID); | |
| if (supervisorRows != null && !supervisorRows.isEmpty()) { | |
| Object[] sRow = supervisorRows.get(0); | |
| JSONObject supervisor = new JSONObject(); | |
| supervisor.put("userId", sRow[0]); | |
| supervisor.put("fullName", fullName(sRow[1], sRow[2])); | |
| supervisor.put("mobile", str(sRow[3])); | |
| supervisor.put("employeeId", str(sRow[4]).isEmpty() ? JSONObject.NULL : str(sRow[4])); | |
| result.put("supervisor", supervisor); | |
| } else { | |
| result.put("supervisor", JSONObject.NULL); | |
| } | |
| // Peers at same facility | |
| List<Integer> selectedFacilityIDs = new ArrayList<>(); | |
| selectedFacilityIDs.add(selectedFacilityID); | |
| List<Object[]> peerRows = facilityLoginRepo.getPeersAtFacility(selectedFacilityIDs, ashaUserID); | |
| JSONArray peers = new JSONArray(); |
🧰 Tools
🪛 GitHub Check: SonarCloud Code Analysis
[failure] 86-86: Define a constant instead of duplicating this literal "userId" 3 times.
[failure] 76-76: Define a constant instead of duplicating this literal "facilityId" 4 times.
[failure] 78-78: Define a constant instead of duplicating this literal "facilityType" 4 times.
[failure] 87-87: Define a constant instead of duplicating this literal "fullName" 3 times.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/main/java/com/iemr/common/service/users/AshaSupervisorLoginService.java`
around lines 73 - 97, The peers fetch is using facilityIDs (all mapped
facilities) but the UI shows only the single facility from facilityRows.get(0);
change the peers query to be scoped to that displayed facility by extracting the
chosen facility id (the value used in facility.put("facilityId", row[0])) and
call facilityLoginRepo.getPeersAtFacility with that single facility id (or
provide a new method like getPeersAtFacility(Long facilityId, Long ashaUserID))
instead of facilityIDs so peers returned match the displayed facility; update
any variable names (e.g., row, facility, peerRows) accordingly.
| private String smsTemplate =null; | ||
|
|
||
| @Override | ||
| @Async | ||
| public String sendWelcomeSMStoBenificiary(String contactNo, String beneficiaryName, String beneficiaryId) { | ||
| final RestTemplate restTemplate = new RestTemplate(); | ||
|
|
||
| Optional<SMSTemplate> smsTemplateData = smsTemplateRepository.findBySmsTemplateName(smsTemplateName); | ||
| if (smsTemplateData.isPresent()) { | ||
| smsTemplate = smsTemplateRepository.findBySmsTemplateID(smsTemplateData.get().getSmsTemplateID()).getSmsTemplate(); | ||
|
|
||
| } | ||
|
|
||
| logger.info("sms template" + smsTemplate); | ||
|
|
||
|
|
||
| String sendSMSAPI = SMS_GATEWAY_URL; | ||
|
|
||
| try { | ||
| String sendSMSAPI = SMS_GATEWAY_URL; | ||
|
|
||
| String message = smsTemplate.replace("$$BENE_NAME$$", beneficiaryName).replace("$$BEN_ID$$", beneficiaryId); | ||
| // Build payload | ||
| Map<String, Object> payload = new HashMap<>(); | ||
| payload.put("customerId", smsUserName); | ||
| payload.put("destinationAddress", contactNo); | ||
| payload.put("message", message); | ||
| payload.put("sourceAddress", smsSourceAddress); | ||
| payload.put("messageType", "SERVICE_IMPLICIT"); | ||
| payload.put("dltTemplateId", smsTemplateData.get().getDltTemplateId()); | ||
| payload.put("entityId", smsEntityId); | ||
| // Set headers | ||
| HttpHeaders headers = new HttpHeaders(); | ||
| String auth = smsUserName + ":" + smsPassword; | ||
| headers.add("Authorization", | ||
| "Basic " + Base64.getEncoder().encodeToString(auth.getBytes())); | ||
|
|
||
| headers.setContentType(MediaType.APPLICATION_JSON); | ||
| logger.info("payload: " + payload); | ||
| HttpEntity<Map<String, Object>> request = new HttpEntity<>(payload, headers); | ||
|
|
||
| // Call API | ||
| ResponseEntity<String> response = restTemplate.postForEntity(sendSMSAPI, request, String.class); | ||
| logger.info("sms-response:" + response.getBody()); | ||
| if (response.getStatusCode().value() == 200) { | ||
| return "OTP sent successfully on register mobile number"; | ||
| } else { | ||
| return "Fail"; | ||
| final RestTemplate restTemplate = new RestTemplate(); | ||
|
|
||
| Optional<SMSTemplate> smsTemplateData = smsTemplateRepository.findBySmsTemplateName(smsTemplateName); | ||
| if (smsTemplateData.isPresent()) { | ||
| smsTemplate = smsTemplateRepository.findBySmsTemplateID(smsTemplateData.get().getSmsTemplateID()).getSmsTemplate(); | ||
| } | ||
| if(smsTemplate!=null){ | ||
| String message = smsTemplate.replace("$$BENE_NAME$$", beneficiaryName).replace("$$BEN_ID$$", beneficiaryId); |
There was a problem hiding this comment.
Avoid shared mutable smsTemplate state in async singleton service.
Lines 50-65 use an instance field for request-scoped template data. Under concurrent async calls this can leak stale values across requests.
🔧 Suggested fix
- private String smsTemplate =null;
+ // remove shared mutable field
...
- if (smsTemplateData.isPresent()) {
- smsTemplate = smsTemplateRepository.findBySmsTemplateID(smsTemplateData.get().getSmsTemplateID()).getSmsTemplate();
- }
- if(smsTemplate!=null){
+ String smsTemplate = null;
+ if (smsTemplateData.isPresent()) {
+ smsTemplate = smsTemplateRepository
+ .findBySmsTemplateID(smsTemplateData.get().getSmsTemplateID())
+ .getSmsTemplate();
+ }
+ if (smsTemplate != null) {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@src/main/java/com/iemr/common/service/welcomeSms/WelcomeBenificarySmsServiceImpl.java`
around lines 50 - 65, The code uses a shared instance field smsTemplate in the
async singleton service which can leak state across concurrent
sendWelcomeSMStoBenificiary calls; change smsTemplate to a local variable inside
sendWelcomeSMStoBenificiary (remove or stop using the instance field
smsTemplate), fetch the SMSTemplate via
smsTemplateRepository.findBySmsTemplateName(smsTemplateName) and assign its
getSmsTemplate() to a method-local String (e.g., localSmsTemplate), then use
that local variable when building message; ensure no other methods reference the
instance field so it can be removed to make the service thread-safe.
| @Async | ||
| public String sendWelcomeSMStoBenificiary(String contactNo, String beneficiaryName, String beneficiaryId) { |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
In Spring Framework 6.1 (used by Spring Boot 3.2.x), what return types are supported for methods annotated with @async?
💡 Result:
In Spring Framework 6.1 (used by Spring Boot 3.2.x), methods annotated with @Async can have return types of either: 1) void 2) java.util.concurrent.Future (with CompletableFuture allowed as a more specific type) The @Async Javadoc states the return type is constrained to either void or Future; in the latter case you may declare a more specific CompletableFuture for richer interaction/composition. [1][2] So, non-Future/non-void return types are not supported for @Async methods in Spring Framework 6.1. [1][2]
Citations:
- 1: https://docs.spring.io/spring-framework/docs/6.1.20/javadoc-api/org/springframework/scheduling/annotation/Async.html
- 2: https://docs.spring.io/spring-framework/docs/6.1.19/javadoc-api/org/springframework/scheduling/annotation/Async.html
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="src/main/java/com/iemr/common/service/welcomeSms/WelcomeBenificarySmsServiceImpl.java"
if [ -f "$FILE" ]; then
echo "=== File: $FILE ==="
nl -ba "$FILE" | sed -n '40,110p'
else
echo "File not found: $FILE" >&2
exit 1
fiRepository: PSMRI/Common-API
Length of output: 221
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="src/main/java/com/iemr/common/service/welcomeSms/WelcomeBenificarySmsServiceImpl.java"
sed -n '1,5p' "$FILE" >/dev/null 2>&1 || true
echo "=== File: $FILE (lines 40-110) ==="
cat -n "$FILE" | sed -n '40,110p'Repository: PSMRI/Common-API
Length of output: 3255
Fix invalid @Async return type (String) in sendWelcomeSMStoBenificiary.
WelcomeBenificarySmsServiceImpl#sendWelcomeSMStoBenificiary is annotated with @Async but declares public String ... and returns String/null. In Spring Framework 6.1, @Async methods must return void or Future (including CompletableFuture); String is not supported for the async contract, leading to incorrect behavior for callers.
🔧 Suggested fix (option: keep async)
+import java.util.concurrent.CompletableFuture;
...
-@Async
-public String sendWelcomeSMStoBenificiary(String contactNo, String beneficiaryName, String beneficiaryId) {
+@Async
+public CompletableFuture<String> sendWelcomeSMStoBenificiary(String contactNo, String beneficiaryName, String beneficiaryId) {
...
- return "OTP sent successfully on register mobile number";
+ return CompletableFuture.completedFuture("OTP sent successfully on register mobile number");
...
- return "Fail";
+ return CompletableFuture.completedFuture("Fail");
...
- return "Error sending SMS: " + e.getMessage().toString();
+ return CompletableFuture.completedFuture("Error sending SMS: " + e.getMessage());
...
- return null;
+ return CompletableFuture.completedFuture(null);
}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@src/main/java/com/iemr/common/service/welcomeSms/WelcomeBenificarySmsServiceImpl.java`
around lines 52 - 53, The method sendWelcomeSMStoBenificiary in
WelcomeBenificarySmsServiceImpl is annotated with `@Async` but returns String;
change its signature to return CompletableFuture<String> (import
java.util.concurrent.CompletableFuture), update return statements to return
CompletableFuture.completedFuture(yourString) (or
CompletableFuture.completedFuture(null) if previously returned null), and ensure
any callers are updated to handle the CompletableFuture; keep the `@Async` on
sendWelcomeSMStoBenificiary and adjust method internals to produce/return a
CompletableFuture.
| @Value("${jitsi.token.ttl.seconds:${JITSI_TOKEN_TTL_SECONDS:3600}}") | ||
| private long ttlSeconds; |
There was a problem hiding this comment.
Guard against non-positive JWT TTL values.
At Line 90, ttlSeconds is used directly; 0 or negative values will mint expired tokens and break join flow. Add validation before computing expiry.
Suggested fix
`@Value`("${jitsi.token.ttl.seconds:${JITSI_TOKEN_TTL_SECONDS:3600}}")
private long ttlSeconds;
public String generateRoomToken(String room, String userName, String userEmail, boolean isModerator) {
+ if (ttlSeconds <= 0) {
+ throw new IllegalStateException("jitsi.token.ttl.seconds must be > 0");
+ }
if (room == null || room.isEmpty()) {
throw new IllegalArgumentException("room is required to mint a Jitsi token");
}Also applies to: 89-90
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/main/java/com/iemr/common/utils/JitsiJwtUtil.java` around lines 65 - 66,
The ttlSeconds field in JitsiJwtUtil can be zero or negative which will create
already-expired tokens; add a guard where ttlSeconds is used to compute the
token expiry (the token-generation method in class JitsiJwtUtil that uses
ttlSeconds) to validate ttlSeconds > 0 and if not either set it to a sane
default (e.g. 3600L) or throw a clear IllegalArgumentException; include a short
log warning if you coerce the value. Ensure you reference and update the
ttlSeconds field validation before computing expiry so subsequent math uses the
corrected positive value.
| // Tests for resolveMeetingLink() - public redirect endpoint hit by SMS recipients | ||
| @Test | ||
| void shouldReturn302WithJitsiUrl_whenResolveMeetingLinkSucceeds() throws Exception { | ||
| String fullJitsiUrl = "https://vc.piramalswasthya.org/piramal-meeting-Ab3xQ9pK?jwt=FAKE.JWT.TOKEN"; | ||
| when(videoCallService.resolveMeetingLink(eq("Ab3xQ9pK"))).thenReturn(fullJitsiUrl); | ||
|
|
||
| mockMvc.perform(get("/video-consultation/resolve").param("m", "Ab3xQ9pK")) | ||
| .andExpect(status().isFound()) | ||
| .andExpect(header().string("Location", fullJitsiUrl)); | ||
|
|
||
| verify(videoCallService, times(1)).resolveMeetingLink("Ab3xQ9pK"); | ||
| } | ||
|
|
||
| @Test | ||
| void shouldReturn400_whenResolveMeetingLinkSlugIsInvalid() throws Exception { | ||
| when(videoCallService.resolveMeetingLink(eq(""))) | ||
| .thenThrow(new IllegalArgumentException("Meeting slug is required")); | ||
|
|
||
| mockMvc.perform(get("/video-consultation/resolve").param("m", "")) | ||
| .andExpect(status().isBadRequest()); | ||
|
|
||
| verify(videoCallService, times(1)).resolveMeetingLink(""); | ||
| } | ||
|
|
||
| @Test | ||
| void shouldReturn404_whenResolveMeetingLinkSlugUnknown() throws Exception { | ||
| when(videoCallService.resolveMeetingLink(eq("missing"))) | ||
| .thenThrow(new Exception("No meeting found for slug: missing")); | ||
|
|
||
| mockMvc.perform(get("/video-consultation/resolve").param("m", "missing")) | ||
| .andExpect(status().isNotFound()); | ||
|
|
||
| verify(videoCallService, times(1)).resolveMeetingLink("missing"); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win
Missing test coverage for 410 Gone response.
The controller code (VideoCallController.java lines 201-203) returns 410 Gone when the exception message contains "already been used", but there's no test case verifying this behavior. This leaves a gap in test coverage for an important error case.
📝 Proposed test case
`@Test`
void shouldReturn410Gone_whenResolveMeetingLinkAlreadyUsed() throws Exception {
when(videoCallService.resolveMeetingLink(eq("used-slug")))
.thenThrow(new Exception("This meeting link has already been used"));
mockMvc.perform(get("/video-consultation/resolve").param("m", "used-slug"))
.andExpect(status().isGone());
verify(videoCallService, times(1)).resolveMeetingLink("used-slug");
}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@src/test/java/com/iemr/common/controller/videocall/VideoCallControllerTest.java`
around lines 201 - 234, Add a new unit test to cover the controller branch that
returns 410 Gone when the resolveMeetingLink exception message contains "already
been used": in VideoCallControllerTest add a test (e.g.,
shouldReturn410Gone_whenResolveMeetingLinkAlreadyUsed) that stubs
videoCallService.resolveMeetingLink("used-slug") to throw new Exception("This
meeting link has already been used"), performs
mockMvc.get("/video-consultation/resolve").param("m","used-slug"), asserts
status().isGone(), and verifies videoCallService.resolveMeetingLink("used-slug")
was called once.


📋 Description
JIRA ID:
Please provide a summary of the change and the motivation behind it. Include relevant context and details.
✅ Type of Change
ℹ️ 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
Release Notes
New Features
Improvements
Configuration