Skip to content

fix: merge 3.6.2 to release 3.8.1#419

Merged
vanitha1822 merged 40 commits into
mainfrom
vb/merge
May 21, 2026
Merged

fix: merge 3.6.2 to release 3.8.1#419
vanitha1822 merged 40 commits into
mainfrom
vb/merge

Conversation

@vishwab1
Copy link
Copy Markdown
Member

@vishwab1 vishwab1 commented May 21, 2026

📋 Description

JIRA ID:

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


✅ Type of Change

  • 🐞 Bug fix (non-breaking change which resolves an issue)
  • New feature (non-breaking change which adds functionality)
  • 🔥 Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • 🛠 Refactor (change that is neither a fix nor a new feature)
  • ⚙️ Config change (configuration file or build script updates)
  • 📚 Documentation (updates to docs or readme)
  • 🧪 Tests (adding new or updating existing tests)
  • 🎨 UI/UX (changes that affect the user interface)
  • 🚀 Performance (improves performance)
  • 🧹 Chore (miscellaneous changes that don't modify src or test files)

ℹ️ Additional Information

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

Summary by CodeRabbit

Release Notes

  • New Features

    • Added video consultation meeting link resolution and agent token generation for Jitsi video calls.
    • Implemented user account lock/unlock functionality with automatic unlock after configured duration.
    • Added CTI disposition count reporting endpoint.
    • Enhanced facility login data enrichment with supervisor and ASHA worker mappings.
    • Improved form field options with multi-language label support.
  • Improvements

    • Refined concurrent session logout with token denylisting.
    • Enhanced video call status updates with atomic database operations.
    • Better null/empty handling in SMS and beneficiary workflows.
    • Upgraded configuration management to use Spring property injection.
  • Configuration

    • Migrated to Jitsi for video consultations.
    • Added account lock duration configuration.

Review Change Stack

vanitha1822 and others added 30 commits December 18, 2025 12:44
* fix: implement functionality to search beneficiaries with Elasticsearch

* fix: remove unwanted import

* fix: update pom.xml

* fix: change the response code
* 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
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>
* 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>
snehar-nd and others added 10 commits May 13, 2026 21:40
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.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 21, 2026

📝 Walkthrough

<review_stack_artifact>

Walkthrough

This 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 @Value across multiple services, enhances dynamic forms with database-backed localized options, adds CTI disposition count reporting, and updates numerous entity/DTO/test artifacts.

Changes

Jitsi Video Call Meeting Resolution and Recording

Layer / File(s) Summary
Jitsi JWT Token Utility with Tests and Configuration
src/main/java/com/iemr/common/utils/JitsiJwtUtil.java, src/main/environment/common_*.properties, src/test/java/com/iemr/common/utils/JitsiJwtUtilTest.java
JitsiJwtUtil Spring component generates HS256 JWTs for Jitsi room access with configurable app credentials, domain, subject, TTL, and room prefix from properties or environment variables; comprehensive test suite validates token claims and error handling.
VideoCall Service Interface, Implementation, and Repository
src/main/java/com/iemr/common/service/videocall/VideoCallService*.java, src/main/java/com/iemr/common/data/videocall/VideoCallParameters.java, src/main/java/com/iemr/common/model/videocall/UpdateCall*.java, src/main/java/com/iemr/common/repository/videocall/VideoCallParameterRepository.java
VideoCallService interface declares resolveMeetingLink and generateAgentToken; VideoCallServiceImpl implements meeting link generation, resolution, agent token creation, and atomic call-status updates with recording metadata; models extended with recordingFileName and isLinkUsed fields; repository adds atomic updateCallStatusAndRecording method.
VideoCall Controller Endpoints with Tests
src/main/java/com/iemr/common/controller/videocall/VideoCallController.java, src/test/java/com/iemr/common/controller/videocall/VideoCallControllerTest.java, src/test/java/com/iemr/common/service/videocall/VideoCallServiceImplTest.java
Controller updated with request validation, structured error handling; new /video-consultation/resolve public redirect endpoint and /video-consultation/agent-token endpoint; comprehensive test coverage for resolution flow and JWT generation.

User Account Locking and Authentication

Layer / File(s) Summary
Account Lock Management and Admin Endpoints
src/main/java/com/iemr/common/service/users/IEMRAdminUserService*.java, src/main/java/com/iemr/common/controller/users/IEMRAdminController.java, src/main/java/com/iemr/common/data/users/User.java
IEMRAdminUserServiceImpl adds configurable auto-unlock duration, failed-attempt tracking with threshold enforcement, and shared password-validation/lockout helper; IEMRAdminController adds lock/unlock/status endpoints with StringRedisTemplate for jti mapping and AshaSupervisorLoginService for facility enrichment; User entity adds lockTimestamp and employeeID fields.
Secure JWT Token Generation and Public Endpoint Exclusions
src/main/java/com/iemr/common/utils/JwtUtil.java, src/main/java/com/iemr/common/utils/JwtUserIdValidationFilter.java, src/main/java/com/iemr/common/config/InterceptorConfig.java, src/main/java/com/iemr/common/utils/http/HTTPRequestInterceptor.java
JwtUtil adds generateSecureToken and generateSecureRefreshToken methods for mobile login with random jti claims; JwtUserIdValidationFilter and interceptors mark /video-consultation/resolve as public, skipping authentication and request validation.

ASHA Supervisor and Facility Login Enrichment

Layer / File(s) Summary
ASHA Supervisor Mapping, Repositories, and Login Service
src/main/java/com/iemr/common/data/users/AshaSupervisorMapping.java, src/main/java/com/iemr/common/repository/users/AshaSupervisorLoginRepo.java, src/main/java/com/iemr/common/repository/users/FacilityLoginRepo.java, src/main/java/com/iemr/common/service/users/AshaSupervisorLoginService.java
New AshaSupervisorMapping JPA entity with supervisor-to-ASHA-to-facility mappings; repositories provide complex native SQL queries for facility details, supervisor lookups, peer lists, and ASHA lists by role; AshaSupervisorLoginService builds role-based JSON responses with facility details, ASHA mappings, peer lists, and supervisor information for authentication enrichment.

Configuration Modernization: Spring @Value Injection

Layer / File(s) Summary
Service Configuration Injection and OpenKM Refactoring
src/main/java/com/iemr/common/utils/config/ConfigProperties.java, src/main/java/com/iemr/common/service/feedback/FeedbackServiceImpl.java, src/main/java/com/iemr/common/service/notification/NotificationServiceImpl.java, src/main/java/com/iemr/common/service/scheme/SchemeServiceImpl.java, src/main/java/com/iemr/common/service/services/CommonServiceImpl.java, src/main/java/com/iemr/common/service/cti/CTIServiceImpl.java, src/main/java/com/iemr/common/service/nhm_dashboard/NHM_DashboardServiceImpl.java, src/main/java/com/iemr/common/utils/km/openkm/OpenKMServiceImpl.java, src/main/java/com/iemr/common/CommonApplication.java, src/main/java/com/iemr/common/utils/IEMRApplBeans.java
ConfigProperties enhanced with Environment-first lookup; multiple services migrate to @Value-injected configuration instead of ConfigProperties lookups; OpenKMServiceImpl refactored to Spring-managed @Service with @PostConstruct initialization; CommonApplication adds @EnableAsync; IEMRApplBeans comments out getOpenKMService bean.

Dynamic Form Field Localization

Layer / File(s) Summary
FormFieldOption Entity, Repository, and Localized Service
src/main/java/com/iemr/common/data/dynamic_from/FormFieldOption.java, src/main/java/com/iemr/common/repository/dynamic_form/FormFieldOptionRepository.java, src/main/java/com/iemr/common/data/dynamic_from/FormField.java, src/main/java/com/iemr/common/repository/dynamic_form/FieldRepository.java, src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java, src/main/java/com/iemr/common/dto/dynamicForm/FieldResponseDTO.java
New FormFieldOption JPA entity stores localized option labels (en/hi/as) with sort order; FormField adds optionKey field; repositories extended with query methods; FormMasterServiceImpl refactored to fetch options from database by optionKey, localize field labels and placeholders via translationRepo, and emit localized option maps; FieldResponseDTO.options changed from List to List<Map<String, Object>>.

CTI Disposition Count Reporting

Layer / File(s) Summary
CTI Disposition Count Models, Service, and Controller
src/main/java/com/iemr/common/data/cti/DispositionCountRequest.java, src/main/java/com/iemr/common/data/cti/DispositionCountResponse.java, src/main/java/com/iemr/common/service/cti/CTIService.java, src/main/java/com/iemr/common/service/cti/CTIServiceImpl.java, src/main/java/com/iemr/common/controller/cti/ComputerTelephonyIntegrationController.java, src/main/resources/application.properties
New request/response DTOs; CTIService interface declares getDispositionCount; CTIServiceImpl implements POSTing to CTI server with response parsing; ComputerTelephonyIntegrationController adds /cti/getDispositionCount endpoint; application.properties adds get-disposition-count-URL configuration.

Beneficiary Registration and SMS Enhancements

Layer / File(s) Summary
Beneficiary Services, Models, and SMS Enhancements
src/main/java/com/iemr/common/service/beneficiary/RegisterBenificiaryServiceImpl.java, src/main/java/com/iemr/common/service/sms/SMSServiceImpl.java, src/main/java/com/iemr/common/service/welcomeSms/WelcomeBenificarySmsServiceImpl.java, src/main/java/com/iemr/common/model/beneficiary/BeneficiaryModel.java, src/main/java/com/iemr/common/model/user/LoginResponseModel.java, src/main/java/com/iemr/common/service/beneficiaryOTPHandler/BeneficiaryOTPHandlerImpl.java
RegisterBenificiaryServiceImpl adds null/empty checks and try/catch for welcome SMS, updates demographic handling; SMSServiceImpl refactored to handle null beneficiaries and special-case VideoCallParameters; WelcomeBenificarySmsServiceImpl marked @Async; BeneficiaryModel changes doYouHavechildren to boolean and adds noOfchildren; LoginResponseModel adds employeeID; BeneficiaryOTPHandlerImpl hardcodes smsTemplateName.

Miscellaneous Configuration and Cleanup

Layer / File(s) Summary
Version Bump, License Headers, and Import Cleanup
pom.xml, src/main/java/com/iemr/common/mapper/videocall/VideoCallMapper.java, src/main/java/com/iemr/common/model/videocall/VideoCallRequest.java, plus various service and controller classes
Maven version incremented from 3.8.0 to 3.8.1; GPL license headers added to VideoCallMapper and VideoCallRequest; import reorganization in service classes; whitespace adjustments in controllers, repositories, and data classes; minor updates to beneficiary controller and JWT authentication utility.
Application Properties Configuration Updates
src/main/resources/application.properties, src/main/environment/common_example.properties
Adds account.lock.duration.hours configuration, Quartz scheduler header adjustments, comments out OpenKM configuration properties, updates tempFilePath to /tmp.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 Jitsi tokens hop through Redis gates,
Accounts lock up on failed debates,
Forms bloom with fields from databases bright,
@Value beans spring into the night,
ASHA maps dance—facility login's delight!

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch vb/merge

@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
1 Security Hotspot

See analysis details on SonarQube Cloud

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 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 win

Critical: Null check order causes potential NPE.

The user.getDeleted() and user.getStatusID() calls on lines 342 and 346 occur before the null check on line 352. If getUserById returns null, this will throw an NPE. Additionally, getDeleted() returns Boolean (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 win

Avoid reinitializing OpenKM connector per method call

With @PostConstruct at Line 84, connector setup should happen once. Calling this.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 win

Ensure @Async uses a bounded executor (not Spring’s default fallback).

@EnableAsync is enabled in both src/main/java/com/iemr/common/CommonApplication.java and src/main/java/com/iemr/common/utils/CommonMain.java without specifying an executor bean. The codebase does not define any TaskExecutor/AsyncTaskExecutor bean or a bean named taskExecutor; it only provides @Bean public Executor asyncExecutor() (backed by ThreadPoolTaskExecutor) in CommonMain. Spring’s default @EnableAsync lookup only targets a TaskExecutor bean (or a bean named taskExecutor), so async methods may fall back to SimpleAsyncTaskExecutor (unbounded thread creation) under load.

Update the existing async executor bean to be discoverable by @EnableAsync (e.g., expose it as TaskExecutor and/or rename it to taskExecutor, 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 win

Remove redundant duplicate imports in BeneficiaryRegistrationController

  • The RequestHeader / CookieUtil / JwtUtil import 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 win

Remove duplicate import declarations in IEMRSearchUserServiceImpl.java (lines 28-29 and 81-82).

The file re-imports java.util.Map and com.iemr.common.utils.exception.IEMRException twice, 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 win

Use 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 win

Consider primitive boolean to match repository contract.

The Boolean wrapper type allows null values, but the repository method updateCallStatusAndRecording expects a primitive boolean parameter (line 61 in VideoCallParameterRepository.java). If isLinkUsed is null when passed to the repository, auto-unboxing will throw a NullPointerException.

🔧 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 win

Keep 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 win

Guard missing property before calling trim()

At Line 157, properties.getProperty(propertyName) can be null; calling trim() 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 value

Extract 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 win

Add 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 win

Decouple 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 win

Use a typed data field instead of Object.

On Line 20, Object data removes compile-time checks and makes downstream parsing brittle, despite having CampaignData/DispositionDetail models 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 win

Extract 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

📥 Commits

Reviewing files that changed from the base of the PR and between 1c69049 and 46adfdd.

📒 Files selected for processing (67)
  • pom.xml
  • src/main/environment/common_ci.properties
  • src/main/environment/common_docker.properties
  • src/main/environment/common_example.properties
  • src/main/java/com/iemr/common/CommonApplication.java
  • src/main/java/com/iemr/common/config/InterceptorConfig.java
  • src/main/java/com/iemr/common/config/encryption/SecurePassword.java
  • src/main/java/com/iemr/common/controller/beneficiary/BeneficiaryRegistrationController.java
  • src/main/java/com/iemr/common/controller/cti/ComputerTelephonyIntegrationController.java
  • src/main/java/com/iemr/common/controller/dynamicForm/DynamicFormController.java
  • src/main/java/com/iemr/common/controller/users/IEMRAdminController.java
  • src/main/java/com/iemr/common/controller/version/VersionController.java
  • src/main/java/com/iemr/common/controller/videocall/VideoCallController.java
  • src/main/java/com/iemr/common/data/cti/DispositionCountRequest.java
  • src/main/java/com/iemr/common/data/cti/DispositionCountResponse.java
  • src/main/java/com/iemr/common/data/dynamic_from/FormField.java
  • src/main/java/com/iemr/common/data/dynamic_from/FormFieldOption.java
  • src/main/java/com/iemr/common/data/translation/Translation.java
  • src/main/java/com/iemr/common/data/users/AshaSupervisorMapping.java
  • src/main/java/com/iemr/common/data/users/User.java
  • src/main/java/com/iemr/common/data/videocall/VideoCallParameters.java
  • src/main/java/com/iemr/common/dto/dynamicForm/FieldResponseDTO.java
  • src/main/java/com/iemr/common/mapper/videocall/VideoCallMapper.java
  • src/main/java/com/iemr/common/model/beneficiary/BeneficiaryModel.java
  • src/main/java/com/iemr/common/model/user/LoginResponseModel.java
  • src/main/java/com/iemr/common/model/videocall/UpdateCallRequest.java
  • src/main/java/com/iemr/common/model/videocall/UpdateCallResponse.java
  • src/main/java/com/iemr/common/model/videocall/VideoCallRequest.java
  • src/main/java/com/iemr/common/repository/dynamic_form/FieldRepository.java
  • src/main/java/com/iemr/common/repository/dynamic_form/FormFieldOptionRepository.java
  • src/main/java/com/iemr/common/repository/translation/TranslationRepo.java
  • src/main/java/com/iemr/common/repository/users/AshaSupervisorLoginRepo.java
  • src/main/java/com/iemr/common/repository/users/FacilityLoginRepo.java
  • src/main/java/com/iemr/common/repository/users/IEMRUserRepositoryCustom.java
  • src/main/java/com/iemr/common/repository/videocall/VideoCallParameterRepository.java
  • src/main/java/com/iemr/common/service/beneficiary/IEMRSearchUserServiceImpl.java
  • src/main/java/com/iemr/common/service/beneficiary/IdentityBeneficiaryServiceImpl.java
  • src/main/java/com/iemr/common/service/beneficiary/RegisterBenificiaryServiceImpl.java
  • src/main/java/com/iemr/common/service/beneficiaryOTPHandler/BeneficiaryOTPHandlerImpl.java
  • src/main/java/com/iemr/common/service/cti/CTIService.java
  • src/main/java/com/iemr/common/service/cti/CTIServiceImpl.java
  • src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java
  • src/main/java/com/iemr/common/service/feedback/FeedbackServiceImpl.java
  • src/main/java/com/iemr/common/service/kmfilemanager/KMFileManagerServiceImpl.java
  • src/main/java/com/iemr/common/service/nhm_dashboard/NHM_DashboardServiceImpl.java
  • src/main/java/com/iemr/common/service/notification/NotificationServiceImpl.java
  • src/main/java/com/iemr/common/service/scheme/SchemeServiceImpl.java
  • src/main/java/com/iemr/common/service/services/CommonServiceImpl.java
  • src/main/java/com/iemr/common/service/sms/SMSServiceImpl.java
  • src/main/java/com/iemr/common/service/users/AshaSupervisorLoginService.java
  • src/main/java/com/iemr/common/service/users/IEMRAdminUserService.java
  • src/main/java/com/iemr/common/service/users/IEMRAdminUserServiceImpl.java
  • src/main/java/com/iemr/common/service/videocall/VideoCallService.java
  • src/main/java/com/iemr/common/service/videocall/VideoCallServiceImpl.java
  • src/main/java/com/iemr/common/service/welcomeSms/WelcomeBenificarySmsServiceImpl.java
  • src/main/java/com/iemr/common/utils/IEMRApplBeans.java
  • src/main/java/com/iemr/common/utils/JitsiJwtUtil.java
  • src/main/java/com/iemr/common/utils/JwtAuthenticationUtil.java
  • src/main/java/com/iemr/common/utils/JwtUserIdValidationFilter.java
  • src/main/java/com/iemr/common/utils/JwtUtil.java
  • src/main/java/com/iemr/common/utils/config/ConfigProperties.java
  • src/main/java/com/iemr/common/utils/http/HTTPRequestInterceptor.java
  • src/main/java/com/iemr/common/utils/km/openkm/OpenKMServiceImpl.java
  • src/main/resources/application.properties
  • src/test/java/com/iemr/common/controller/videocall/VideoCallControllerTest.java
  • src/test/java/com/iemr/common/service/videocall/VideoCallServiceImplTest.java
  • src/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

Comment on lines +237 to +243
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
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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.

Suggested change
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.

Comment on lines +530 to +533
String remoteAddress = serverRequest.getHeader("X-FORWARDED-FOR");
if (remoteAddress == null || remoteAddress.trim().length() == 0) {
remoteAddress = serverRequest.getRemoteAddr();
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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.

Suggested change
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.

Comment on lines +152 to +159
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);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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.

Comment on lines +194 to +205
} 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();
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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.

Comment on lines +121 to +124
private boolean doYouHavechildren;

@Expose
private Integer noOfchildren;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 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" || true

Repository: 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 80

Repository: 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
    done

Repository: 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 || true

Repository: 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:


🌐 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:


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.

Comment on lines +73 to +97
// 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();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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.

Suggested change
// 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.

See more on https://sonarcloud.io/project/issues?id=PSMRI_Common-API&issues=AZ5JtZMZDgMflhM0iaMr&open=AZ5JtZMZDgMflhM0iaMr&pullRequest=419


[failure] 76-76: Define a constant instead of duplicating this literal "facilityId" 4 times.

See more on https://sonarcloud.io/project/issues?id=PSMRI_Common-API&issues=AZ5JtZMZDgMflhM0iaMt&open=AZ5JtZMZDgMflhM0iaMt&pullRequest=419


[failure] 78-78: Define a constant instead of duplicating this literal "facilityType" 4 times.

See more on https://sonarcloud.io/project/issues?id=PSMRI_Common-API&issues=AZ5JtZMZDgMflhM0iaMp&open=AZ5JtZMZDgMflhM0iaMp&pullRequest=419


[failure] 87-87: Define a constant instead of duplicating this literal "fullName" 3 times.

See more on https://sonarcloud.io/project/issues?id=PSMRI_Common-API&issues=AZ5JtZMZDgMflhM0iaMv&open=AZ5JtZMZDgMflhM0iaMv&pullRequest=419

🤖 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.

Comment on lines +50 to +65
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);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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.

Comment on lines +52 to 53
@Async
public String sendWelcomeSMStoBenificiary(String contactNo, String beneficiaryName, String beneficiaryId) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 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:


🏁 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
fi

Repository: 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.

Comment on lines +65 to +66
@Value("${jitsi.token.ttl.seconds:${JITSI_TOKEN_TTL_SECONDS:3600}}")
private long ttlSeconds;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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.

Comment on lines +201 to +234
// 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");
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ 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.

@vanitha1822 vanitha1822 merged commit 0da4823 into main May 21, 2026
3 of 5 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request May 22, 2026
10 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants