From 5a28f4b1082b3cfd552d2d34bdfec1dba011827b Mon Sep 17 00:00:00 2001 From: Saurav Mishra <80103738+SauravBizbRolly@users.noreply.github.com> Date: Wed, 26 Nov 2025 18:00:42 +0530 Subject: [PATCH 01/58] Update application.properties --- src/main/resources/application.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 18723465..a1135a2a 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -373,4 +373,4 @@ allowed.file.extensions=msg,pdf,png,jpeg,doc,docx,xlsx,xls,csv,txt ##sms details for beneficiary otp cosent sms-template-name = otp_consent - +cors.allowed-origin = From ea12e3e16f68484da66a07f621e2fdccd0ca96d9 Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Wed, 26 Nov 2025 19:54:09 +0530 Subject: [PATCH 02/58] add column in create BeneficiaryModel --- .../com/iemr/common/model/beneficiary/BeneficiaryModel.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/iemr/common/model/beneficiary/BeneficiaryModel.java b/src/main/java/com/iemr/common/model/beneficiary/BeneficiaryModel.java index e7a7a3de..5d42f275 100644 --- a/src/main/java/com/iemr/common/model/beneficiary/BeneficiaryModel.java +++ b/src/main/java/com/iemr/common/model/beneficiary/BeneficiaryModel.java @@ -118,7 +118,10 @@ public class BeneficiaryModel implements Comparable { private Boolean isMarried; @Expose - private Integer doYouHavechildren; + private boolean doYouHavechildren; + + @Expose + private Integer noOfchildren; @Expose private Integer noofAlivechildren; From 50c595288043614744079694c17d02749d4f1d5b Mon Sep 17 00:00:00 2001 From: Vanitha S <116701245+vanitha1822@users.noreply.github.com> Date: Thu, 18 Dec 2025 12:44:57 +0530 Subject: [PATCH 03/58] 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 --- pom.xml | 2 +- .../BeneficiaryRegistrationController.java | 58 +++++++++++++ .../beneficiary/IEMRSearchUserService.java | 2 + .../IEMRSearchUserServiceImpl.java | 31 ++++++- .../IdentityBeneficiaryService.java | 5 ++ .../IdentityBeneficiaryServiceImpl.java | 82 ++++++++++++++++++- src/main/resources/application.properties | 1 + 7 files changed, 178 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 11ad9f37..171ab162 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.iemr.common-API common-api - 3.6.0 + 3.6.1 war Common-API diff --git a/src/main/java/com/iemr/common/controller/beneficiary/BeneficiaryRegistrationController.java b/src/main/java/com/iemr/common/controller/beneficiary/BeneficiaryRegistrationController.java index 8f573d6d..cdce13b0 100644 --- a/src/main/java/com/iemr/common/controller/beneficiary/BeneficiaryRegistrationController.java +++ b/src/main/java/com/iemr/common/controller/beneficiary/BeneficiaryRegistrationController.java @@ -36,6 +36,7 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestBody; +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; @@ -70,6 +71,8 @@ import com.iemr.common.service.userbeneficiarydata.MaritalStatusService; 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.mapper.InputMapper; import com.iemr.common.utils.mapper.OutputMapper; import com.iemr.common.utils.response.OutputResponse; @@ -104,6 +107,10 @@ public class BeneficiaryRegistrationController { private GovtIdentityTypeService govtIdentityTypeService; + + @Autowired + private JwtUtil jwtUtil; + @Autowired public void setBenRelationshipTypeService(BenRelationshipTypeService benRelationshipTypeService) { this.benRelationshipTypeService = benRelationshipTypeService; @@ -342,6 +349,57 @@ public String searchUserByPhone( return response.toString(); } + @Operation(summary = "Provide the list of beneficiaries using Elasticsearch") + @RequestMapping(value = "/searchUser", method = RequestMethod.POST, headers = "Authorization") + public String searchUser( + @RequestBody String request, + @RequestHeader(value = "Authorization", required = false) String auth, HttpServletRequest httpRequest) { + + OutputResponse response = new OutputResponse(); + + try { + logger.info("Universal search request received"); + + JsonParser parser = new JsonParser(); + JsonObject requestObj = parser.parse(request).getAsJsonObject(); + + String searchQuery = null; + if (requestObj.has("search") && !requestObj.get("search").isJsonNull()) { + searchQuery = requestObj.get("search").getAsString(); + } + + if (searchQuery == null || searchQuery.trim().isEmpty()) { + response.setError(400, "Search query is required"); + return response.toString(); + } + + String jwtToken = CookieUtil.getJwtTokenFromCookie(httpRequest); + String userId = jwtUtil.getUserIdFromToken(jwtToken); + int userID=Integer.parseInt(userId); + + Boolean is1097 = false; + if (requestObj.has("is1097") && !requestObj.get("is1097").isJsonNull()) { + is1097 = requestObj.get("is1097").getAsBoolean(); + } + + logger.info("Searching with query: {}, userId: {}, is1097: {}", searchQuery, userID, is1097); + + String result = iemrSearchUserService.searchUser(searchQuery, userID, auth, is1097); + + if (result == null || result.trim().isEmpty()) { + response.setError(200, "No beneficiaries found"); + return response.toString(); + } + + return result; + + } catch (Exception e) { + logger.error("Error in universal search: {}", e.getMessage(), e); + response.setError(400, "Error searching beneficiaries: " + e.getMessage()); + return response.toString(); + } + } + @Operation(summary = "Provide the list of beneficiaries based on search criteria") @RequestMapping(value = "/searchBeneficiary", method = RequestMethod.POST, headers = "Authorization") public String searchBeneficiary( diff --git a/src/main/java/com/iemr/common/service/beneficiary/IEMRSearchUserService.java b/src/main/java/com/iemr/common/service/beneficiary/IEMRSearchUserService.java index 6e7848cd..ab4c40fd 100644 --- a/src/main/java/com/iemr/common/service/beneficiary/IEMRSearchUserService.java +++ b/src/main/java/com/iemr/common/service/beneficiary/IEMRSearchUserService.java @@ -38,6 +38,8 @@ String findByBeneficiaryPhoneNo(BenPhoneMap benPhoneMap, Integer pageNo, Integer String findBeneficiary(BeneficiaryModel request, String auth) throws Exception; + String searchUser(String searchQuery, Integer userId, String auth, Boolean is1097) throws Exception; + List userExitsCheckWithId(String beneficiaryID, String auth, Boolean is1097) throws Exception; public List userExitsCheckWithHealthId_ABHAId(String healthID, String auth, Boolean is1097) diff --git a/src/main/java/com/iemr/common/service/beneficiary/IEMRSearchUserServiceImpl.java b/src/main/java/com/iemr/common/service/beneficiary/IEMRSearchUserServiceImpl.java index f67d7815..7c9bfc2d 100644 --- a/src/main/java/com/iemr/common/service/beneficiary/IEMRSearchUserServiceImpl.java +++ b/src/main/java/com/iemr/common/service/beneficiary/IEMRSearchUserServiceImpl.java @@ -25,6 +25,7 @@ import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Objects; import org.slf4j.Logger; @@ -76,7 +77,7 @@ import com.iemr.common.repository.userbeneficiarydata.MaritalStatusRepository; import com.iemr.common.repository.userbeneficiarydata.SexualOrientationRepository; import com.iemr.common.repository.userbeneficiarydata.TitleRepository; -import com.iemr.common.utils.mapper.OutputMapper; +import com.iemr.common.utils.exception.IEMRException; /** * @@ -322,6 +323,34 @@ private void setBeneficiaryGender(List iBeneficiary) { } + /** + Universal search using Elasticsearch + */ + @Override + public String searchUser(String searchQuery, Integer userId, String auth, Boolean is1097) throws Exception { + + try { + if (searchQuery == null || searchQuery.trim().isEmpty()) { + throw new IEMRException("Search query is required"); + } + + logger.info("Universal search with query: {}, userId: {}", searchQuery, userId); + + Map response = + identityBeneficiaryService.searchBeneficiariesUsingES( + searchQuery, userId, auth, is1097 + ); + + ObjectMapper mapper = new ObjectMapper(); + return mapper.writeValueAsString(response); + + } catch (Exception e) { + logger.error("Error in universal search", e); + throw new Exception("Error searching beneficiaries: " + e.getMessage(), e); + } +} + + // Advance search @Override public String findBeneficiary(BeneficiaryModel i_beneficiary, String auth) throws Exception { diff --git a/src/main/java/com/iemr/common/service/beneficiary/IdentityBeneficiaryService.java b/src/main/java/com/iemr/common/service/beneficiary/IdentityBeneficiaryService.java index 8b84bc8a..5ebe3b24 100644 --- a/src/main/java/com/iemr/common/service/beneficiary/IdentityBeneficiaryService.java +++ b/src/main/java/com/iemr/common/service/beneficiary/IdentityBeneficiaryService.java @@ -23,6 +23,7 @@ import java.util.HashSet; import java.util.List; +import java.util.Map; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonMappingException; @@ -69,4 +70,8 @@ public List getBeneficiaryListByFamilyId(String familyId, Stri public List getBeneficiaryListByGovId(String identity, String auth, Boolean is1097) throws IEMRException; + + public Map searchBeneficiariesUsingES(String query, Integer userId, String auth, Boolean is1097) throws IEMRException; + + } diff --git a/src/main/java/com/iemr/common/service/beneficiary/IdentityBeneficiaryServiceImpl.java b/src/main/java/com/iemr/common/service/beneficiary/IdentityBeneficiaryServiceImpl.java index f9ca6c96..0136c0f8 100644 --- a/src/main/java/com/iemr/common/service/beneficiary/IdentityBeneficiaryServiceImpl.java +++ b/src/main/java/com/iemr/common/service/beneficiary/IdentityBeneficiaryServiceImpl.java @@ -21,15 +21,20 @@ */ package com.iemr.common.service.beneficiary; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import com.google.gson.*; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.iemr.common.dto.identity.BeneficiariesDTO; @@ -43,6 +48,12 @@ import com.iemr.common.utils.mapper.OutputMapper; import com.iemr.common.utils.response.OutputResponse; +import org.springframework.beans.factory.annotation.Value; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + @Service public class IdentityBeneficiaryServiceImpl implements IdentityBeneficiaryService { @@ -107,6 +118,75 @@ public List getBeneficiaryListByIDs(HashSet benIdList, String return listBenDetailForOutboundDTO; } + /** + Call Identity API's Elasticsearch universal search + */ + @Override + public Map searchBeneficiariesUsingES(String query, Integer userId, String auth, Boolean is1097 ) throws IEMRException { + + Map response = new HashMap<>(); + + try { + HashMap headers = new HashMap<>(); + if (auth != null && !auth.isEmpty()) { + headers.put("Authorization", auth); + } + + String baseUrl = ConfigProperties + .getPropertyByName("identity-api-url-searchByES") + .replace( + IDENTITY_BASE_URL, + (Boolean.TRUE.equals(is1097)) ? identity1097BaseURL : identityBaseURL + ); + + StringBuilder url = new StringBuilder(baseUrl) + .append("?query=").append(URLEncoder.encode(query, StandardCharsets.UTF_8)); + + if (userId != null) { + url.append("&userId=").append(userId); + } + + logger.info("Calling Identity ES search URL: {}", url); + + String result = httpUtils.get(url.toString(), headers); + + if (result == null || result.isEmpty()) { + response.put("data", Collections.emptyList()); + response.put("statusCode", 200); + response.put("status", "Success"); + response.put("errorMessage", "Success"); + return response; + } + + ObjectMapper mapper = new ObjectMapper(); + + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + mapper.configure(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS, false); + + JsonNode rootNode = mapper.readTree(result); + + if (rootNode.has("statusCode") && rootNode.get("statusCode").asInt() != 200) { + String errMsg = rootNode.has("errorMessage") + ? rootNode.get("errorMessage").asText() + : "Identity ES search failed"; + throw new IEMRException(errMsg); + } + + response.put("data", rootNode.path("data")); + response.put("statusCode", 200); + response.put("status", "Success"); + response.put("errorMessage", "Success"); + + return response; + + } catch (IEMRException e) { + throw e; + } catch (Exception e) { + logger.error("Error calling Identity ES search API", e); + throw new IEMRException("Error calling Identity ES search API"); + } +} + @Override public List getPartialBeneficiaryListByIDs(HashSet benIdList, String auth, Boolean is1097) throws IEMRException { diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 18723465..8ab4669b 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -118,6 +118,7 @@ identity-api-url-getByBenRegId =IDENTITY_BASE_URL/id/getByBenRegId?benRegId= identity-api-url-benCreate =IDENTITY_BASE_URL/id/create identity-api-url-benEdit =IDENTITY_BASE_URL/id/edit identity-api-url-benEditEducationCommunity=IDENTITY_BASE_URL/id/editEducationOrCommunity +identity-api-url-searchByES=IDENTITY_BASE_URL/beneficiary/search identity-api-url-getByFamilyId=IDENTITY_BASE_URL/id/searchByFamilyId?familyId= identity-api-url-getByGovIdentity=IDENTITY_BASE_URL/id/searchByGovIdentity?identity= From 1bc9298bf8783f400a022507782d4c5d91ddef34 Mon Sep 17 00:00:00 2001 From: Sachin Kadam <152252767+sac2kadam@users.noreply.github.com> Date: Wed, 31 Dec 2025 16:08:54 +0530 Subject: [PATCH 04/58] variable added --- src/main/environment/common_ci.properties | 1 + src/main/environment/common_docker.properties | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/environment/common_ci.properties b/src/main/environment/common_ci.properties index 0184b32f..9f54a35d 100644 --- a/src/main/environment/common_ci.properties +++ b/src/main/environment/common_ci.properties @@ -19,6 +19,7 @@ km-base-path=@env.KM_API_BASE_PATH@ km-root-path=/okm:personal/users/ km-guest-user=@env.KM_GUEST_USER@ km-guest-password=@env.KM_GUEST_PASSWORD@ +tempFilePath=@env.TEMP_FILE_PATH@ # CTI Config cti-server-ip=@env.CTI_SERVER_IP@ diff --git a/src/main/environment/common_docker.properties b/src/main/environment/common_docker.properties index a81ea62e..59cb580d 100644 --- a/src/main/environment/common_docker.properties +++ b/src/main/environment/common_docker.properties @@ -126,7 +126,7 @@ everwellRegisterBenficiary = ${COMMON_API_BASE_URL}/beneficiary/create ## LungAssessment credentials lungAssessmentEmail = ${SWAASA_EMAIL} lungAssessmentPassword =${SWAASA_PASSWORD} - +tempFilePath=${TEMP_FILE_PATH} ## SWASSA APIs lungAssessmentAdminLogin = ${SWAASA_BASE_URL}/api/adminLogin From c501e8037f967fa497d132a9780b8913452927e6 Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Wed, 7 Jan 2026 18:41:15 +0530 Subject: [PATCH 05/58] update language --- .../java/com/iemr/common/data/translation/Translation.java | 2 ++ .../common/service/dynamicForm/FormMasterServiceImpl.java | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/iemr/common/data/translation/Translation.java b/src/main/java/com/iemr/common/data/translation/Translation.java index 81a906fa..0dad116d 100644 --- a/src/main/java/com/iemr/common/data/translation/Translation.java +++ b/src/main/java/com/iemr/common/data/translation/Translation.java @@ -18,6 +18,8 @@ public class Translation { private String english; @Column(name = "hindi_translation") private String hindiTranslation; + @Column(name = "assamese_translation") + private String assameseTranslation; @Column(name = "is_active") private Boolean isActive; } diff --git a/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java b/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java index df019de7..cfe643b3 100644 --- a/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java +++ b/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java @@ -122,8 +122,11 @@ public FormResponseDTO getStructuredFormByFormId(String formId,String lang) { if (t != null) { if ("hi".equalsIgnoreCase(lang)) { translatedLabel = t.getHindiTranslation(); - } else { + } else if("am".equalsIgnoreCase(lang)){ + translatedLabel = t.getAssameseTranslation(); + }else if("en".equalsIgnoreCase(lang)){ translatedLabel = t.getEnglish(); + } } From ec3aac3557f4fec6c5bedcbada3195f17654b42a Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Wed, 7 Jan 2026 18:50:37 +0530 Subject: [PATCH 06/58] update language --- .../iemr/common/service/dynamicForm/FormMasterServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java b/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java index cfe643b3..98d93dbe 100644 --- a/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java +++ b/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java @@ -122,7 +122,7 @@ public FormResponseDTO getStructuredFormByFormId(String formId,String lang) { if (t != null) { if ("hi".equalsIgnoreCase(lang)) { translatedLabel = t.getHindiTranslation(); - } else if("am".equalsIgnoreCase(lang)){ + } else if("as".equalsIgnoreCase(lang)){ translatedLabel = t.getAssameseTranslation(); }else if("en".equalsIgnoreCase(lang)){ translatedLabel = t.getEnglish(); From bafc8794a9afd5750b26ff74d61c3d1a7862d959 Mon Sep 17 00:00:00 2001 From: Saurav Mishra <80103738+SauravBizbRolly@users.noreply.github.com> Date: Wed, 7 Jan 2026 23:15:36 +0530 Subject: [PATCH 07/58] Downgrade version from 3.6.1 to 3.6.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 171ab162..11ad9f37 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.iemr.common-API common-api - 3.6.1 + 3.6.0 war Common-API From 65efdc7362f558e6cadc8f735c7606cdec42fb28 Mon Sep 17 00:00:00 2001 From: Vanitha S <116701245+vanitha1822@users.noreply.github.com> Date: Thu, 8 Jan 2026 09:26:07 +0530 Subject: [PATCH 08/58] 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 --- .../BeneficiaryRegistrationController.java | 74 ++++-- .../beneficiary/IEMRSearchUserService.java | 2 + .../IEMRSearchUserServiceImpl.java | 104 +++++++-- .../IdentityBeneficiaryService.java | 2 + .../IdentityBeneficiaryServiceImpl.java | 214 +++++++++++------- .../java/com/iemr/common/utils/JwtUtil.java | 55 +++++ .../iemr/common/utils/RestTemplateUtil.java | 7 +- .../utils/http/HTTPRequestInterceptor.java | 177 ++++++++------- src/main/resources/application.properties | 1 + 9 files changed, 423 insertions(+), 213 deletions(-) diff --git a/src/main/java/com/iemr/common/controller/beneficiary/BeneficiaryRegistrationController.java b/src/main/java/com/iemr/common/controller/beneficiary/BeneficiaryRegistrationController.java index cdce13b0..3784f3a9 100644 --- a/src/main/java/com/iemr/common/controller/beneficiary/BeneficiaryRegistrationController.java +++ b/src/main/java/com/iemr/common/controller/beneficiary/BeneficiaryRegistrationController.java @@ -106,9 +106,7 @@ public class BeneficiaryRegistrationController { private BeneficiaryOccupationService beneficiaryOccupationService; private GovtIdentityTypeService govtIdentityTypeService; - - - @Autowired + @Autowired private JwtUtil jwtUtil; @Autowired @@ -351,54 +349,51 @@ public String searchUserByPhone( @Operation(summary = "Provide the list of beneficiaries using Elasticsearch") @RequestMapping(value = "/searchUser", method = RequestMethod.POST, headers = "Authorization") - public String searchUser( - @RequestBody String request, - @RequestHeader(value = "Authorization", required = false) String auth, HttpServletRequest httpRequest) { - + public String searchUser(@RequestBody String request, HttpServletRequest httpRequest) { OutputResponse response = new OutputResponse(); - try { logger.info("Universal search request received"); - + JsonParser parser = new JsonParser(); JsonObject requestObj = parser.parse(request).getAsJsonObject(); - + String searchQuery = null; if (requestObj.has("search") && !requestObj.get("search").isJsonNull()) { searchQuery = requestObj.get("search").getAsString(); } - + if (searchQuery == null || searchQuery.trim().isEmpty()) { response.setError(400, "Search query is required"); return response.toString(); } - - String jwtToken = CookieUtil.getJwtTokenFromCookie(httpRequest); - String userId = jwtUtil.getUserIdFromToken(jwtToken); - int userID=Integer.parseInt(userId); - + + String auth = httpRequest.getHeader("Authorization"); + + Integer userID = jwtUtil.getUserIdFromRequest(httpRequest); + + logger.info("ES search for userId: {}", userID); + Boolean is1097 = false; if (requestObj.has("is1097") && !requestObj.get("is1097").isJsonNull()) { is1097 = requestObj.get("is1097").getAsBoolean(); } - + logger.info("Searching with query: {}, userId: {}, is1097: {}", searchQuery, userID, is1097); - String result = iemrSearchUserService.searchUser(searchQuery, userID, auth, is1097); - + if (result == null || result.trim().isEmpty()) { response.setError(200, "No beneficiaries found"); return response.toString(); } - + return result; - + } catch (Exception e) { logger.error("Error in universal search: {}", e.getMessage(), e); response.setError(400, "Error searching beneficiaries: " + e.getMessage()); return response.toString(); } - } + } @Operation(summary = "Provide the list of beneficiaries based on search criteria") @RequestMapping(value = "/searchBeneficiary", method = RequestMethod.POST, headers = "Authorization") @@ -422,6 +417,41 @@ public String searchBeneficiary( return output.toString(); } + /** + * Elasticsearch-based advanced search endpoint + */ + @Operation(summary = "Advanced search beneficiaries using Elasticsearch") + @RequestMapping(value = "/searchBeneficiaryES", method = RequestMethod.POST, headers = "Authorization") + public String searchBeneficiaryES( + @RequestBody BeneficiaryModel request, + HttpServletRequest httpRequest) { + + logger.info("searchBeneficiaryES request: {}", request); + OutputResponse output = new OutputResponse(); + + try { + + String auth = httpRequest.getHeader("Authorization"); + + Integer userID = jwtUtil.getUserIdFromRequest(httpRequest); + + logger.info("ES Advanced search for userId: {}", userID); + + String result = iemrSearchUserService.findBeneficiaryES(request, userID, auth); + + return result; + + } catch (NumberFormatException ne) { + logger.error("searchBeneficiaryES failed with number format error: {}", ne.getMessage(), ne); + output.setError(400, "Invalid number format in search criteria"); + return output.toString(); + } catch (Exception e) { + logger.error("searchBeneficiaryES failed with error: {}", e.getMessage(), e); + output.setError(500, "Error searching beneficiaries: " + e.getMessage()); + return output.toString(); + } + } + @Operation(summary = "Provide all common data list needed for beneficiary registration") @RequestMapping(value = "/getRegistrationData", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON, headers = "Authorization") public String getRegistrationData() { diff --git a/src/main/java/com/iemr/common/service/beneficiary/IEMRSearchUserService.java b/src/main/java/com/iemr/common/service/beneficiary/IEMRSearchUserService.java index ab4c40fd..e39cfcab 100644 --- a/src/main/java/com/iemr/common/service/beneficiary/IEMRSearchUserService.java +++ b/src/main/java/com/iemr/common/service/beneficiary/IEMRSearchUserService.java @@ -40,6 +40,8 @@ String findByBeneficiaryPhoneNo(BenPhoneMap benPhoneMap, Integer pageNo, Integer String searchUser(String searchQuery, Integer userId, String auth, Boolean is1097) throws Exception; + String findBeneficiaryES(BeneficiaryModel i_beneficiary, Integer userId, String auth) throws Exception; + List userExitsCheckWithId(String beneficiaryID, String auth, Boolean is1097) throws Exception; public List userExitsCheckWithHealthId_ABHAId(String healthID, String auth, Boolean is1097) diff --git a/src/main/java/com/iemr/common/service/beneficiary/IEMRSearchUserServiceImpl.java b/src/main/java/com/iemr/common/service/beneficiary/IEMRSearchUserServiceImpl.java index 7c9bfc2d..28d664c8 100644 --- a/src/main/java/com/iemr/common/service/beneficiary/IEMRSearchUserServiceImpl.java +++ b/src/main/java/com/iemr/common/service/beneficiary/IEMRSearchUserServiceImpl.java @@ -199,7 +199,7 @@ private void addCreatedDateToOtherFields(BeneficiaryModel beneficiaryModel) { JsonNode otherFieldsNode = objectMapper.readTree(beneficiaryModel.getOtherFields()); // Convert createdDate to a string - String createdDateString = beneficiaryModel.getCreatedDate().toString(); + String createdDateString = beneficiaryModel.getCreatedDate().toString(); // Add createdDate to the JSON node ((ObjectNode) otherFieldsNode).put("createdDate", createdDateString); @@ -220,10 +220,10 @@ public List userExitsCheckWithHealthId_ABHAId(String healthID, List beneficiaryList = new ArrayList(); // search patient by ben id, call Identity API List listBen = null; - if(healthID.contains("@")) { + if (healthID.contains("@")) { listBen = identityBeneficiaryService.getBeneficiaryListByHealthID_ABHAAddress(healthID, auth, is1097); - }else { + } else { String healthIdNumber = getHealthId(healthID); listBen = identityBeneficiaryService.getBeneficiaryListByHealthIDNo_ABHAIDNo(healthIdNumber, auth, is1097); } @@ -233,6 +233,7 @@ public List userExitsCheckWithHealthId_ABHAId(String healthID, } return beneficiaryList; } + private String getHealthId(String healthID) { String healthIdNumber = null; if (null != healthID) { @@ -250,6 +251,7 @@ private String getHealthId(String healthID) { } return healthIdNumber; } + // search patient by healthidNo / ABHA Id No @Override public List userExitsCheckWithHealthIdNo_ABHAIdNo(String healthIDNo, String auth, Boolean is1097) @@ -324,32 +326,88 @@ private void setBeneficiaryGender(List iBeneficiary) { } /** - Universal search using Elasticsearch - */ + * Universal search using Elasticsearch + */ @Override public String searchUser(String searchQuery, Integer userId, String auth, Boolean is1097) throws Exception { - try { - if (searchQuery == null || searchQuery.trim().isEmpty()) { - throw new IEMRException("Search query is required"); - } + try { + if (searchQuery == null || searchQuery.trim().isEmpty()) { + throw new IEMRException("Search query is required"); + } + + logger.info("Universal search with query: {}, userId: {}", searchQuery, userId); - logger.info("Universal search with query: {}, userId: {}", searchQuery, userId); + Map response = identityBeneficiaryService.searchBeneficiariesUsingES( + searchQuery, userId, auth, is1097); - Map response = - identityBeneficiaryService.searchBeneficiariesUsingES( - searchQuery, userId, auth, is1097 - ); + ObjectMapper mapper = new ObjectMapper(); + return mapper.writeValueAsString(response); - ObjectMapper mapper = new ObjectMapper(); - return mapper.writeValueAsString(response); + } catch (Exception e) { + logger.error("Error in universal search", e); + throw new Exception("Error searching beneficiaries: " + e.getMessage(), e); + } + } - } catch (Exception e) { - logger.error("Error in universal search", e); - throw new Exception("Error searching beneficiaries: " + e.getMessage(), e); - } -} + /** + * Advanced search using Elasticsearch with multiple criteria + */ + + @Override + public String findBeneficiaryES( + BeneficiaryModel i_beneficiary, + Integer userId, + String auth) throws Exception { + + try { + IdentitySearchDTO identitySearchDTO = identityBenEditMapper.getidentitysearchModel(i_beneficiary); + + if (i_beneficiary.getDOB() != null) { + identitySearchDTO.setDob(i_beneficiary.getDOB()); + } + + if (i_beneficiary.getHouseHoldID() != null) { + identitySearchDTO.setHouseHoldID(i_beneficiary.getHouseHoldID()); + } + + if (i_beneficiary.getIsD2D() != null) { + identitySearchDTO.setIsD2D(i_beneficiary.getIsD2D()); + } + + if (i_beneficiary.getBenPhoneMaps() != null + && !i_beneficiary.getBenPhoneMaps().isEmpty()) { + identitySearchDTO.setContactNumber( + i_beneficiary.getBenPhoneMaps().get(0).getPhoneNo()); + } + + if (i_beneficiary.getBeneficiaryID() != null + && !i_beneficiary.getBeneficiaryID().isEmpty()) { + identitySearchDTO.setBeneficiaryId( + new BigInteger(i_beneficiary.getBeneficiaryID())); + } + + i_beneficiary.setIs1097(Boolean.TRUE.equals(i_beneficiary.getIs1097())); + Gson gson = new GsonBuilder() + .setDateFormat("yyyy-MM-dd") + .create(); + + String requestJson = gson.toJson(identitySearchDTO); + + Map response = identityBeneficiaryService.searchBeneficiaryListES( + requestJson, + auth, + i_beneficiary.getIs1097()); + + ObjectMapper mapper = new ObjectMapper(); + return mapper.writeValueAsString(response); + + } catch (Exception e) { + logger.error("Error in ES advance search", e); + throw new Exception("Error searching beneficiaries using ES", e); + } + } // Advance search @Override @@ -393,7 +451,7 @@ public String findBeneficiary(BeneficiaryModel i_beneficiary, String auth) throw + (beneficiaryList != null ? beneficiaryList.size() : "No Beneficiary Found")); ObjectMapper mapper = new ObjectMapper(); return mapper.writeValueAsString(beneficiaryList); - + } // get response mapper @@ -403,7 +461,7 @@ public List getBeneficiaryListFromMapper(List { BeneficiaryModel beneficiary = benCompleteMapper.benDetailForOutboundDTOToIBeneficiary(beneficiaryModel); - if(null != beneficiaryModel && null != beneficiaryModel.getBeneficiaryDetails()) { + if (null != beneficiaryModel && null != beneficiaryModel.getBeneficiaryDetails()) { beneficiary.setCommunityName(beneficiaryModel.getBeneficiaryDetails().getCommunity()); beneficiary.setReligion(beneficiaryModel.getBeneficiaryDetails().getReligion()); beneficiary.setReligionName(beneficiaryModel.getBeneficiaryDetails().getReligion()); diff --git a/src/main/java/com/iemr/common/service/beneficiary/IdentityBeneficiaryService.java b/src/main/java/com/iemr/common/service/beneficiary/IdentityBeneficiaryService.java index 5ebe3b24..41a132b0 100644 --- a/src/main/java/com/iemr/common/service/beneficiary/IdentityBeneficiaryService.java +++ b/src/main/java/com/iemr/common/service/beneficiary/IdentityBeneficiaryService.java @@ -54,6 +54,8 @@ List getBeneficiaryListByBenRegID(Long benRegId, String auth, List searchBeneficiaryList(String identitySearchDTO, String auth, Boolean is1097) throws IEMRException; + public Map searchBeneficiaryListES(String identitySearchDTO, String auth, Boolean is1097) throws IEMRException ; + Integer editIdentityEditDTOCommunityorEducation(IdentityEditDTO identityEditDTO, String auth, Boolean is1097) throws IEMRException; diff --git a/src/main/java/com/iemr/common/service/beneficiary/IdentityBeneficiaryServiceImpl.java b/src/main/java/com/iemr/common/service/beneficiary/IdentityBeneficiaryServiceImpl.java index 0136c0f8..350f2527 100644 --- a/src/main/java/com/iemr/common/service/beneficiary/IdentityBeneficiaryServiceImpl.java +++ b/src/main/java/com/iemr/common/service/beneficiary/IdentityBeneficiaryServiceImpl.java @@ -34,7 +34,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.iemr.common.dto.identity.BeneficiariesDTO; @@ -70,12 +69,10 @@ public class IdentityBeneficiaryServiceImpl implements IdentityBeneficiaryServic private static final String IDENTITY_BASE_URL = "IDENTITY_BASE_URL"; @Value("${genben-api}") - private String BEN_GEN ; - + private String BEN_GEN; @Value("${generateBeneficiaryIDs-api-url}") - private String BEN_GEN_API_URL ; - + private String BEN_GEN_API_URL; @Override // public List getBeneficiaryListByIDs() {// search by regID @@ -99,13 +96,10 @@ public List getBeneficiaryListByIDs(HashSet benIdList, String } if (null != result) { JsonObject responseObj = (JsonObject) parser.parse(result); - // JsonArray data = (JsonArray) parser.parse( JsonObject data1 = (JsonObject) responseObj.get("response"); String s = data1.get("data").getAsString(); JsonArray responseArray = parser.parse(s).getAsJsonArray(); - // String data="s"; - // JsonArray responseArray = (JsonArray) parser.parse(data); for (JsonElement jsonElement : responseArray) { @@ -118,20 +112,21 @@ public List getBeneficiaryListByIDs(HashSet benIdList, String return listBenDetailForOutboundDTO; } - /** - Call Identity API's Elasticsearch universal search - */ + /** + * Call Identity API's Elasticsearch universal search + */ @Override - public Map searchBeneficiariesUsingES(String query, Integer userId, String auth, Boolean is1097 ) throws IEMRException { + public Map searchBeneficiariesUsingES(String query, Integer userId, String auth, Boolean is1097) + throws IEMRException { - Map response = new HashMap<>(); + Map response = new HashMap<>(); try { HashMap headers = new HashMap<>(); if (auth != null && !auth.isEmpty()) { headers.put("Authorization", auth); } - + String baseUrl = ConfigProperties .getPropertyByName("identity-api-url-searchByES") .replace( @@ -139,58 +134,57 @@ public Map searchBeneficiariesUsingES(String query, Integer user (Boolean.TRUE.equals(is1097)) ? identity1097BaseURL : identityBaseURL ); - StringBuilder url = new StringBuilder(baseUrl) - .append("?query=").append(URLEncoder.encode(query, StandardCharsets.UTF_8)); + StringBuilder url = new StringBuilder(baseUrl) + .append("?query=").append(URLEncoder.encode(query, StandardCharsets.UTF_8)); - if (userId != null) { - url.append("&userId=").append(userId); - } + if (userId != null) { + url.append("&userId=").append(userId); + } - logger.info("Calling Identity ES search URL: {}", url); + logger.info("Calling Identity ES search URL: {}", url); - String result = httpUtils.get(url.toString(), headers); + String result = httpUtils.get(url.toString()); - if (result == null || result.isEmpty()) { - response.put("data", Collections.emptyList()); - response.put("statusCode", 200); - response.put("status", "Success"); - response.put("errorMessage", "Success"); - return response; - } + if (result == null || result.isEmpty()) { + response.put("data", Collections.emptyList()); + response.put("statusCode", 200); + response.put("status", "Success"); + response.put("errorMessage", "Success"); + return response; + } - ObjectMapper mapper = new ObjectMapper(); + ObjectMapper mapper = new ObjectMapper(); - mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - mapper.configure(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS, false); + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + mapper.configure(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS, false); - JsonNode rootNode = mapper.readTree(result); + JsonNode rootNode = mapper.readTree(result); - if (rootNode.has("statusCode") && rootNode.get("statusCode").asInt() != 200) { - String errMsg = rootNode.has("errorMessage") - ? rootNode.get("errorMessage").asText() - : "Identity ES search failed"; - throw new IEMRException(errMsg); - } + if (rootNode.has("statusCode") && rootNode.get("statusCode").asInt() != 200) { + String errMsg = rootNode.has("errorMessage") + ? rootNode.get("errorMessage").asText() + : "Identity ES search failed"; + throw new IEMRException(errMsg); + } - response.put("data", rootNode.path("data")); - response.put("statusCode", 200); - response.put("status", "Success"); - response.put("errorMessage", "Success"); + response.put("data", rootNode.path("data")); + response.put("statusCode", 200); + response.put("status", "Success"); + response.put("errorMessage", "Success"); - return response; + return response; - } catch (IEMRException e) { - throw e; - } catch (Exception e) { - logger.error("Error calling Identity ES search API", e); - throw new IEMRException("Error calling Identity ES search API"); - } -} + } catch (IEMRException e) { + throw e; + } catch (Exception e) { + logger.error("Error calling Identity ES search API", e); + throw new IEMRException("Error calling Identity ES search API"); + } + } @Override public List getPartialBeneficiaryListByIDs(HashSet benIdList, String auth, Boolean is1097) throws IEMRException { - // TODO Auto-generated method stub List listBenDetailForOutboundDTO = new ArrayList<>(); JsonParser parser = new JsonParser(); @@ -210,13 +204,10 @@ public List getPartialBeneficiaryListByIDs(HashSet benI throw new IEMRException(identityResponse.getErrorMessage()); } JsonObject responseObj = (JsonObject) parser.parse(result); - // JsonArray data = (JsonArray) parser.parse( JsonObject data1 = (JsonObject) responseObj.get("response"); String s = data1.get("data").getAsString(); JsonArray responseArray = parser.parse(s).getAsJsonArray(); - // String data="s"; - // JsonArray responseArray = (JsonArray) parser.parse(data); for (JsonElement jsonElement : responseArray) { @@ -231,9 +222,9 @@ public List getPartialBeneficiaryListByIDs(HashSet benI // search beneficiaries by phone number public List getBeneficiaryListByPhone(String phoneNo, String auth, Boolean is1097) throws IEMRException { - logger.info("Phone no from getBeneficiaryListByPhone: " + phoneNo); - String cleanedPhoneNo = cleanPhoneNumber(phoneNo); - logger.info("Cleaned phone no: " + cleanedPhoneNo); + logger.info("Phone no from getBeneficiaryListByPhone: " + phoneNo); + String cleanedPhoneNo = cleanPhoneNumber(phoneNo); + logger.info("Cleaned phone no: " + cleanedPhoneNo); List listBenDetailForOutboundDTO = new ArrayList<>(); @@ -245,12 +236,13 @@ public List getBeneficiaryListByPhone(String phoneNo, String a if (auth != null) { header.put("Authorization", auth); } - - logger.info("Result="+(ConfigProperties.getPropertyByName("identity-api-url-getByPhoneNum") + + logger.info("Result=" + (ConfigProperties.getPropertyByName("identity-api-url-getByPhoneNum") .replace(IDENTITY_BASE_URL, (is1097 ? identity1097BaseURL : identityBaseURL))) + cleanedPhoneNo); result = httpUtils.post((ConfigProperties.getPropertyByName("identity-api-url-getByPhoneNum") - .replace(IDENTITY_BASE_URL, (is1097 ? identity1097BaseURL : identityBaseURL))) + cleanedPhoneNo, "", header); + .replace(IDENTITY_BASE_URL, (is1097 ? identity1097BaseURL : identityBaseURL))) + cleanedPhoneNo, "", + header); OutputResponse identityResponse = InputMapper.gson().fromJson(result, OutputResponse.class); if (identityResponse.getStatusCode() == OutputResponse.USERID_FAILURE) { @@ -271,22 +263,22 @@ public List getBeneficiaryListByPhone(String phoneNo, String a } private String cleanPhoneNumber(String phoneNumber) { - if (phoneNumber == null || phoneNumber.trim().isEmpty()) { - return phoneNumber; - } - - String cleaned = phoneNumber.trim(); - - // Remove +91 prefix - if (cleaned.startsWith("+91")) { - cleaned = cleaned.substring(3); - } - // Remove 91 prefix if it's a 12-digit number (91 + 10 digit mobile) - else if (cleaned.startsWith("91") && cleaned.length() == 12) { - cleaned = cleaned.substring(2); - } - - return cleaned.trim(); + if (phoneNumber == null || phoneNumber.trim().isEmpty()) { + return phoneNumber; + } + + String cleaned = phoneNumber.trim(); + + // Remove +91 prefix + if (cleaned.startsWith("+91")) { + cleaned = cleaned.substring(3); + } + // Remove 91 prefix if it's a 12-digit number (91 + 10 digit mobile) + else if (cleaned.startsWith("91") && cleaned.length() == 12) { + cleaned = cleaned.substring(2); + } + + return cleaned.trim(); } @Override @@ -533,7 +525,6 @@ public String getIdentityResponse(String request, String auth, Boolean is1097) t return result; } - public Integer editIdentityEditDTO(IdentityEditDTO identityEditDTO, String auth, Boolean is1097) throws IEMRException { JsonParser parser = new JsonParser(); @@ -578,13 +569,10 @@ public List searchBeneficiaryList(String identitySearchDTO, St IDENTITY_BASE_URL, (is1097 ? identity1097BaseURL : identityBaseURL)), identitySearchDTO, header); JsonObject responseObj = (JsonObject) parser.parse(result); - // JsonArray data = (JsonArray) parser.parse( JsonObject data1 = (JsonObject) responseObj.get("response"); String s = data1.get("data").getAsString(); JsonArray responseArray = parser.parse(s).getAsJsonArray(); - // String data="s"; - // JsonArray responseArray = (JsonArray) parser.parse(data); for (JsonElement jsonElement : responseArray) { @@ -596,6 +584,68 @@ public List searchBeneficiaryList(String identitySearchDTO, St return listBenDetailForOutboundDTO; } + @Override + public Map searchBeneficiaryListES(String identitySearchDTO, String auth, Boolean is1097) + throws IEMRException { + + Map response = new HashMap<>(); + + try { + HashMap headers = new HashMap<>(); + if (auth != null && !auth.isEmpty()) { + headers.put("Authorization", auth); + } + + String url = ConfigProperties + .getPropertyByName("identity-api-url-advancesearch-es") + .replace( + IDENTITY_BASE_URL, + Boolean.TRUE.equals(is1097) + ? identity1097BaseURL + : identityBaseURL); + + logger.info("Calling Identity ES Advance Search API"); + + String result = httpUtils.post(url, identitySearchDTO, headers); + + if (result == null || result.isEmpty()) { + response.put("data", Collections.emptyList()); + response.put("statusCode", 200); + response.put("status", "Success"); + response.put("errorMessage", "Success"); + return response; + } + + ObjectMapper mapper = new ObjectMapper(); + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + + JsonNode rootNode = mapper.readTree(result); + + if (rootNode.has("statusCode") + && rootNode.get("statusCode").asInt() != 200) { + + String errMsg = rootNode.has("errorMessage") + ? rootNode.get("errorMessage").asText() + : "Identity ES advance search failed"; + + throw new IEMRException(errMsg); + } + + response.put("data", rootNode.path("data")); + response.put("statusCode", 200); + response.put("status", "Success"); + response.put("errorMessage", "Success"); + + return response; + + } catch (IEMRException e) { + throw e; + } catch (Exception e) { + logger.error("Error calling Identity ES advance search API", e); + throw new IEMRException("Error calling Identity ES advance search API", e); + } + } + @Override public Integer editIdentityEditDTOCommunityorEducation(IdentityEditDTO identityEditDTO, String auth, Boolean is1097) throws IEMRException { @@ -635,11 +685,11 @@ public List generateBeneficiaryIDs(String request, String a if (auth != null) { header.put("Authorization", auth); } - + logger.info("Request to generate ben IDs: " + request); logger.info("Generating ben IDs API URL: " + BEN_GEN + BEN_GEN_API_URL); result = httpUtils.post(BEN_GEN + BEN_GEN_API_URL, request, header); -logger.info("Response from generate ben IDs: " + result); + logger.info("Response from generate ben IDs: " + result); OutputResponse identityResponse = inputMapper.gson().fromJson(result, OutputResponse.class); if (identityResponse.getStatusCode() == OutputResponse.USERID_FAILURE) { diff --git a/src/main/java/com/iemr/common/utils/JwtUtil.java b/src/main/java/com/iemr/common/utils/JwtUtil.java index d8414968..5d37a990 100644 --- a/src/main/java/com/iemr/common/utils/JwtUtil.java +++ b/src/main/java/com/iemr/common/utils/JwtUtil.java @@ -2,6 +2,7 @@ import io.jsonwebtoken.*; import io.jsonwebtoken.security.Keys; +import jakarta.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -161,4 +162,58 @@ public String getUserIdFromToken(String token) { public long getRefreshTokenExpiration() { return REFRESH_EXPIRATION_TIME; } + + /** + * Extract user ID from JWT token in the request (checks header and cookie) + * @param request the HTTP request + * @return the user ID, or null if not found + */ +public Integer getUserIdFromRequest(HttpServletRequest request) { + try { + String jwtToken = request.getHeader("Jwttoken"); + String cookieToken = CookieUtil.getJwtTokenFromCookie(request); + + // Prefer header token, fallback to cookie + String token = (jwtToken != null && !jwtToken.isEmpty()) ? jwtToken : cookieToken; + + if (token == null || token.isEmpty()) { + return null; + } + + Claims claims = validateToken(token); + if (claims == null) { + return null; + } + + String userId = claims.get("userId", String.class); + return userId != null ? Integer.parseInt(userId) : null; + + } catch (Exception e) { + return null; + } +} + +/** + * Extract username from JWT token in the request (checks header and cookie) + * @param request the HTTP request + * @return the username, or null if not found + */ +public String getUsernameFromRequest(HttpServletRequest request) { + try { + String jwtToken = request.getHeader("Jwttoken"); + String cookieToken = CookieUtil.getJwtTokenFromCookie(request); + + String token = (jwtToken != null && !jwtToken.isEmpty()) ? jwtToken : cookieToken; + + if (token == null || token.isEmpty()) { + return null; + } + + Claims claims = validateToken(token); + return claims != null ? claims.getSubject() : null; + + } catch (Exception e) { + return null; + } +} } diff --git a/src/main/java/com/iemr/common/utils/RestTemplateUtil.java b/src/main/java/com/iemr/common/utils/RestTemplateUtil.java index c8299fe7..4e4fa483 100644 --- a/src/main/java/com/iemr/common/utils/RestTemplateUtil.java +++ b/src/main/java/com/iemr/common/utils/RestTemplateUtil.java @@ -39,6 +39,8 @@ public static HttpEntity createRequestEntity(Object body, String authori headers.add(HttpHeaders.AUTHORIZATION, authorization); if (null != requestHeader.getHeader(Constants.JWT_TOKEN)) { headers.add(Constants.JWT_TOKEN, requestHeader.getHeader(Constants.JWT_TOKEN)); + headers.add(HttpHeaders.COOKIE, "Jwttoken=" + requestHeader.getHeader(Constants.JWT_TOKEN)); + } if (null != jwtTokenFromCookie) { headers.add(HttpHeaders.COOKIE, "Jwttoken=" + jwtTokenFromCookie); @@ -77,9 +79,10 @@ public static void getJwttokenFromHeaders(HttpHeaders headers) { if (null != jwtTokenFromCookie) { headers.add(HttpHeaders.COOKIE, Constants.JWT_TOKEN + "=" + jwtTokenFromCookie); } else if (null != requestHeader.getHeader(Constants.JWT_TOKEN)) { - headers.add(Constants.JWT_TOKEN, requestHeader.getHeader(Constants.JWT_TOKEN)); - } + headers.add(Constants.JWT_TOKEN, requestHeader.getHeader(Constants.JWT_TOKEN)); + headers.add(HttpHeaders.COOKIE, Constants.JWT_TOKEN + "=" + requestHeader.getHeader(Constants.JWT_TOKEN)); + } } } diff --git a/src/main/java/com/iemr/common/utils/http/HTTPRequestInterceptor.java b/src/main/java/com/iemr/common/utils/http/HTTPRequestInterceptor.java index 0c609839..b4aaad60 100644 --- a/src/main/java/com/iemr/common/utils/http/HTTPRequestInterceptor.java +++ b/src/main/java/com/iemr/common/utils/http/HTTPRequestInterceptor.java @@ -36,10 +36,14 @@ import com.iemr.common.utils.sessionobject.SessionObject; import com.iemr.common.utils.validator.Validator; +import com.iemr.common.utils.JwtUtil; +import io.jsonwebtoken.Claims; +import com.iemr.common.utils.CookieUtil; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; + @Configuration @Component public class HTTPRequestInterceptor implements HandlerInterceptor { @@ -50,6 +54,9 @@ public class HTTPRequestInterceptor implements HandlerInterceptor { @Value("${cors.allowed-origins}") private String allowedOrigins; + @Autowired + private JwtUtil jwtUtil; + @Autowired public void setValidator(Validator validator) { this.validator = validator; @@ -67,100 +74,101 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons boolean status = true; logger.info("In info preHandle we are Intercepting the Request"); logger.debug("In preHandle we are Intercepting the Request"); - // String authorization = request.getHeader("Authorization"); + // String authorization = request.getHeader("Authorization"); String authorization = null; String preAuth = request.getHeader("Authorization"); - if(null != preAuth && preAuth.contains("Bearer ")) - authorization=preAuth.replace("Bearer ", ""); + if (null != preAuth && preAuth.contains("Bearer ")) + authorization = preAuth.replace("Bearer ", ""); else authorization = preAuth; - + if (authorization == null || authorization.isEmpty()) { - logger.info("Authorization header is null or empty. Skipping HTTPRequestInterceptor."); - return true; // Allow the request to proceed without validation - } - logger.debug("RequestURI::" + request.getRequestURI() + " || Authorization ::" + authorization + logger.info("Authorization header is null or empty. Skipping HTTPRequestInterceptor."); + return true; // Allow the request to proceed without validation + } + + logger.debug("RequestURI::" + request.getRequestURI() + " || Authorization ::" + authorization + " || method :: " + request.getMethod()); if (!request.getMethod().equalsIgnoreCase("OPTIONS")) { try { String[] requestURIParts = request.getRequestURI().split("/"); String requestAPI = requestURIParts[requestURIParts.length - 1]; switch (requestAPI) { - case "userAuthenticate": - case "superUserAuthenticate": - case "userAuthenticateNew": - case "userAuthenticateV1": - case "forgetPassword": - case "setForgetPassword": - case "changePassword": - case "saveUserSecurityQuesAns": - case "doAgentLogout": - case "userLogout": - case "swagger-ui.html": - case "index.html": - case "index.css": - case "swagger-initializer.js": - case "swagger-config": - case "swagger-ui-bundle.js": - case "swagger-ui.css": - case "ui": - case "swagger-ui-standalone-preset.js": - case "favicon-32x32.png": - case "favicon-16x16.png": - case "swagger-resources": - case "api-docs": - case "updateBenCallIdsInPhoneBlock": - case "userAuthenticateByEncryption": - case "sendOTP": - case "validateOTP": - case "resendOTP": - case "validateSecurityQuestionAndAnswer": - case "logOutUserFromConcurrentSession": - case "refreshToken": - break; - case "error": - status = false; - break; - default: - String remoteAddress = request.getHeader("X-FORWARDED-FOR"); - if (remoteAddress == null || remoteAddress.trim().length() == 0) { - remoteAddress = request.getRemoteAddr(); - } - validator.checkKeyExists(authorization, remoteAddress); - break; + case "userAuthenticate": + case "superUserAuthenticate": + case "userAuthenticateNew": + case "userAuthenticateV1": + case "forgetPassword": + case "setForgetPassword": + case "changePassword": + case "saveUserSecurityQuesAns": + case "doAgentLogout": + case "userLogout": + case "swagger-ui.html": + case "index.html": + case "index.css": + case "swagger-initializer.js": + case "swagger-config": + case "swagger-ui-bundle.js": + case "swagger-ui.css": + case "ui": + case "swagger-ui-standalone-preset.js": + case "favicon-32x32.png": + case "favicon-16x16.png": + case "swagger-resources": + case "api-docs": + case "updateBenCallIdsInPhoneBlock": + case "userAuthenticateByEncryption": + case "sendOTP": + case "validateOTP": + case "resendOTP": + case "validateSecurityQuestionAndAnswer": + case "logOutUserFromConcurrentSession": + case "refreshToken": + break; + case "error": + status = false; + break; + default: + String remoteAddress = request.getHeader("X-FORWARDED-FOR"); + if (remoteAddress == null || remoteAddress.trim().length() == 0) { + remoteAddress = request.getRemoteAddr(); + } + validator.checkKeyExists(authorization, remoteAddress); + break; } } catch (Exception e) { logger.error("Authorization failed: {}", e.getMessage(), e); - String errorMessage = e.getMessage(); - if (errorMessage == null || errorMessage.trim().isEmpty()) { - errorMessage = "Unauthorized access or session expired."; - } - - String jsonErrorResponse = "{" - + "\"status\": \"Unauthorized\"," - + "\"statusCode\": 401," - + "\"errorMessage\": \"" + errorMessage.replace("\"", "\\\"") + "\"" - + "}"; - - response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); // 401 - response.setContentType(MediaType.APPLICATION_JSON); - - String origin = request.getHeader("Origin"); - if (origin != null && isOriginAllowed(origin)) { - response.setHeader("Access-Control-Allow-Origin", origin); - response.setHeader("Access-Control-Allow-Credentials", "true"); - } else if (origin != null) { - logger.warn("CORS headers NOT added for error response | Unauthorized origin: {}", origin); - } - - // Better to use getBytes().length for accurate byte size - byte[] responseBytes = jsonErrorResponse.getBytes(StandardCharsets.UTF_8); - response.setContentLength(responseBytes.length); - - ServletOutputStream out = response.getOutputStream(); - out.write(responseBytes); - out.flush(); + String errorMessage = e.getMessage(); + if (errorMessage == null || errorMessage.trim().isEmpty()) { + errorMessage = "Unauthorized access or session expired."; + } + + String jsonErrorResponse = "{" + + "\"status\": \"Unauthorized\"," + + "\"statusCode\": 401," + + "\"errorMessage\": \"" + errorMessage.replace("\"", "\\\"") + "\"" + + "}"; + + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); // 401 + response.setContentType(MediaType.APPLICATION_JSON); + + String origin = request.getHeader("Origin"); + if (origin != null && isOriginAllowed(origin)) { + response.setHeader("Access-Control-Allow-Origin", origin); + response.setHeader("Access-Control-Allow-Credentials", "true"); + } else if (origin != null) { + logger.warn("CORS headers NOT added for error response | Unauthorized origin: {}", origin); + } + + // Better to use getBytes().length for accurate byte size + byte[] responseBytes = jsonErrorResponse.getBytes(StandardCharsets.UTF_8); + response.setContentLength(responseBytes.length); + + ServletOutputStream out = response.getOutputStream(); + out.write(responseBytes); + out.flush(); status = false; } } @@ -172,15 +180,14 @@ public void postHandle(HttpServletRequest request, HttpServletResponse response, throws Exception { try { logger.debug("In postHandle we are Intercepting the Request"); - // String authorization = request.getHeader("Authorization"); String authorization = null; String postAuth = request.getHeader("Authorization"); - if(null != postAuth && postAuth.contains("Bearer ")) - authorization=postAuth.replace("Bearer ", ""); + if (null != postAuth && postAuth.contains("Bearer ")) + authorization = postAuth.replace("Bearer ", ""); else authorization = postAuth; logger.debug("RequestURI::" + request.getRequestURI() + " || Authorization ::" + authorization); - + if (authorization != null && !authorization.equals("")) { sessionObject.updateSessionObject(authorization, sessionObject.getSessionObject(authorization)); } @@ -212,8 +219,10 @@ private boolean isOriginAllowed(String origin) { .anyMatch(pattern -> { String regex = pattern .replace(".", "\\.") - .replace("*", ".*"); + .replace("*", ".*"); return origin.matches(regex); }); } + + } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 8ab4669b..5263ca41 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -110,6 +110,7 @@ iemr.extend.expiry.time.changePassword=true iemr.session.expiry.time.changePassword=600 identity-api-url-advancesearch =IDENTITY_BASE_URL/id/advanceSearch +identity-api-url-advancesearch-es =IDENTITY_BASE_URL/beneficiary/advancedSearchES identity-api-url-getByBenRegIdList =IDENTITY_BASE_URL/id/getByBenRegIdList identity-api-url-getByPartialBenRegIdList =IDENTITY_BASE_URL/id/getByPartialBenRegIdList identity-api-url-getByPhoneNum =IDENTITY_BASE_URL/id/getByPhoneNum?phoneNum= From 38a4147f1a07c607888cbf9953b1703cbd0c90a4 Mon Sep 17 00:00:00 2001 From: Saurav Mishra <80103738+SauravBizbRolly@users.noreply.github.com> Date: Thu, 8 Jan 2026 11:02:34 +0530 Subject: [PATCH 09/58] Remove empty line in application.properties --- src/main/resources/application.properties | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index a1135a2a..57652e66 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -372,5 +372,3 @@ allowed.file.extensions=msg,pdf,png,jpeg,doc,docx,xlsx,xls,csv,txt ##sms details for beneficiary otp cosent sms-template-name = otp_consent - -cors.allowed-origin = From 17620d329ba6d2085e2c49aeeecb6e42bb02d428 Mon Sep 17 00:00:00 2001 From: vishwab1 Date: Fri, 9 Jan 2026 10:08:42 +0530 Subject: [PATCH 10/58] fix:signature check for mmu --- .../common/service/users/EmployeeSignatureServiceImpl.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/iemr/common/service/users/EmployeeSignatureServiceImpl.java b/src/main/java/com/iemr/common/service/users/EmployeeSignatureServiceImpl.java index 115970ed..eff5c8e4 100644 --- a/src/main/java/com/iemr/common/service/users/EmployeeSignatureServiceImpl.java +++ b/src/main/java/com/iemr/common/service/users/EmployeeSignatureServiceImpl.java @@ -33,8 +33,6 @@ public class EmployeeSignatureServiceImpl implements EmployeeSignatureService { @Autowired EmployeeSignatureRepo employeeSignatureRepo; - - @Override public EmployeeSignature fetchSignature(Long userSignID) { // TODO Auto-generated method stub @@ -44,12 +42,12 @@ public EmployeeSignature fetchSignature(Long userSignID) { @Override public EmployeeSignature fetchActiveSignature(Long userSignID) { // New method - fetches only non-deleted records - return employeeSignatureRepo.findOneByUserIDAndDeleted(userSignID, false); + return employeeSignatureRepo.findOneByUserID(userSignID); } public Boolean existSignature(Long userID) { // TODO Auto-generated method stub - return employeeSignatureRepo.countByUserIDAndSignatureNotNull(userID)>0; + return employeeSignatureRepo.countByUserIDAndSignatureNotNull(userID) > 0; } } From 9c1493a3b45de27c946ff29b1a31ac9480f3ca0d Mon Sep 17 00:00:00 2001 From: Saurav Mishra <80103738+SauravBizbRolly@users.noreply.github.com> Date: Fri, 9 Jan 2026 15:24:25 +0530 Subject: [PATCH 11/58] Update application.properties --- src/main/resources/application.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 57652e66..f34f770c 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -372,3 +372,4 @@ allowed.file.extensions=msg,pdf,png,jpeg,doc,docx,xlsx,xls,csv,txt ##sms details for beneficiary otp cosent sms-template-name = otp_consent +cors.allowed-origin = From 984d533671f5e84e58b85c7d5efe0248bebc12e4 Mon Sep 17 00:00:00 2001 From: Saurav Mishra <80103738+SauravBizbRolly@users.noreply.github.com> Date: Fri, 9 Jan 2026 16:43:02 +0530 Subject: [PATCH 12/58] Update application.properties --- src/main/resources/application.properties | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index f34f770c..4409afd4 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -372,4 +372,5 @@ allowed.file.extensions=msg,pdf,png,jpeg,doc,docx,xlsx,xls,csv,txt ##sms details for beneficiary otp cosent sms-template-name = otp_consent -cors.allowed-origin = +cors.allowed-origin=https://amritdemo.piramalswasthya.org + From c3bc3cbda47b0465df3008ad3407bc73b6dd7590 Mon Sep 17 00:00:00 2001 From: vishwab1 Date: Tue, 13 Jan 2026 17:16:25 +0530 Subject: [PATCH 13/58] fix: retrive any user without deleted --- .../controller/users/IEMRAdminController.java | 20 ++++++++++++++++++- .../users/IEMRUserRepositoryCustom.java | 3 +++ .../service/users/IEMRAdminUserService.java | 2 ++ .../users/IEMRAdminUserServiceImpl.java | 6 ++++++ 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/iemr/common/controller/users/IEMRAdminController.java b/src/main/java/com/iemr/common/controller/users/IEMRAdminController.java index 8bc0e74d..554500f3 100644 --- a/src/main/java/com/iemr/common/controller/users/IEMRAdminController.java +++ b/src/main/java/com/iemr/common/controller/users/IEMRAdminController.java @@ -1224,7 +1224,25 @@ public ResponseEntity getUserDetails(@PathVariable("userName") String userNam return new ResponseEntity<>(Map.of("error", "UserName Not Found"), HttpStatus.NOT_FOUND); } User user = users.get(0); - return new ResponseEntity<>(Map.of("userName", user.getUserName(), "userId", user.getUserID()), HttpStatus.OK); + return new ResponseEntity<>(Map.of("userName", user.getUserName(), "userId", user.getUserID()), + HttpStatus.OK); + } catch (Exception e) { + return new ResponseEntity<>(Map.of("error", "Internal server error"), HttpStatus.INTERNAL_SERVER_ERROR); + } + + } + + @Operation(summary = "Get UserId based on userName") + @GetMapping(value = "/checkUserName/{userName}", produces = MediaType.APPLICATION_JSON, headers = "Authorization") + public ResponseEntity checkUserDetails(@PathVariable("userName") String userName) { + try { + List users = iemrAdminUserServiceImpl.findUserIdByUserName(userName); + if (users.isEmpty()) { + return new ResponseEntity<>(Map.of("error", "UserName Not Found"), HttpStatus.NOT_FOUND); + } + User user = users.get(0); + return new ResponseEntity<>(Map.of("userName", user.getUserName(), "userId", user.getUserID()), + HttpStatus.OK); } catch (Exception e) { return new ResponseEntity<>(Map.of("error", "Internal server error"), HttpStatus.INTERNAL_SERVER_ERROR); } diff --git a/src/main/java/com/iemr/common/repository/users/IEMRUserRepositoryCustom.java b/src/main/java/com/iemr/common/repository/users/IEMRUserRepositoryCustom.java index 3ee48ab3..cc1abccc 100644 --- a/src/main/java/com/iemr/common/repository/users/IEMRUserRepositoryCustom.java +++ b/src/main/java/com/iemr/common/repository/users/IEMRUserRepositoryCustom.java @@ -78,4 +78,7 @@ UserSecurityQMapping verifySecurityQuestionAnswers(@Param("UserID") Long UserID, User findByUserID(Long userID); + @Query("SELECT u FROM User u WHERE LOWER(u.userName) = LOWER(:userName)") + List findUserName(@Param("userName") String username); + } diff --git a/src/main/java/com/iemr/common/service/users/IEMRAdminUserService.java b/src/main/java/com/iemr/common/service/users/IEMRAdminUserService.java index d7dc6e2e..26b7bb15 100644 --- a/src/main/java/com/iemr/common/service/users/IEMRAdminUserService.java +++ b/src/main/java/com/iemr/common/service/users/IEMRAdminUserService.java @@ -123,6 +123,8 @@ public List getUserServiceRoleMappingForProvider(Integ List getUserIdbyUserName(String userName) throws IEMRException; + List findUserIdByUserName(String userName) throws IEMRException; + } diff --git a/src/main/java/com/iemr/common/service/users/IEMRAdminUserServiceImpl.java b/src/main/java/com/iemr/common/service/users/IEMRAdminUserServiceImpl.java index 44bd2247..71d72c97 100644 --- a/src/main/java/com/iemr/common/service/users/IEMRAdminUserServiceImpl.java +++ b/src/main/java/com/iemr/common/service/users/IEMRAdminUserServiceImpl.java @@ -1224,4 +1224,10 @@ public List getUserIdbyUserName(String userName) { return iEMRUserRepositoryCustom.findByUserName(userName); } + + @Override + public List findUserIdByUserName(String userName) { + + return iEMRUserRepositoryCustom.findUserName(userName); + } } From 519073753e456648ccf9be428b98fb0037e8c37c Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Wed, 14 Jan 2026 16:35:19 +0530 Subject: [PATCH 14/58] implement state wise hide un hide form fields --- .../dynamicForm/DynamicFormController.java | 4 +- .../common/data/dynamic_from/FormField.java | 6 + .../common/data/users/UserServiceRole.java | 368 ++++++++++++++++++ .../dto/dynamicForm/FieldResponseDTO.java | 2 + .../repository/users/UserServiceRoleRepo.java | 16 + .../dynamicForm/FormMasterService.java | 2 +- .../dynamicForm/FormMasterServiceImpl.java | 188 +++++---- 7 files changed, 503 insertions(+), 83 deletions(-) create mode 100644 src/main/java/com/iemr/common/data/users/UserServiceRole.java create mode 100644 src/main/java/com/iemr/common/repository/users/UserServiceRoleRepo.java diff --git a/src/main/java/com/iemr/common/controller/dynamicForm/DynamicFormController.java b/src/main/java/com/iemr/common/controller/dynamicForm/DynamicFormController.java index 30a1bc3f..cd83246f 100644 --- a/src/main/java/com/iemr/common/controller/dynamicForm/DynamicFormController.java +++ b/src/main/java/com/iemr/common/controller/dynamicForm/DynamicFormController.java @@ -84,9 +84,9 @@ public ResponseEntity> deleteField(@PathVariable Long fieldId) { } @GetMapping(value = "form/{formId}/fields") - public ResponseEntity> getStructuredForm(@PathVariable String formId, @RequestParam(name = "lang", defaultValue = "en") String lang) { + public ResponseEntity> getStructuredForm(@PathVariable String formId, @RequestParam(name = "lang", defaultValue = "en") String lang,@RequestHeader(value = "JwtToken") String token) { try { - Object result = formMasterService.getStructuredFormByFormId(formId,lang); + Object result = formMasterService.getStructuredFormByFormId(formId,lang,token); return ResponseEntity.status(HttpStatus.OK) .body(ApiResponse.success("Form structure fetched successfully", HttpStatus.OK.value(), result)); } catch (Exception e) { diff --git a/src/main/java/com/iemr/common/data/dynamic_from/FormField.java b/src/main/java/com/iemr/common/data/dynamic_from/FormField.java index 39785ae9..e902acf1 100644 --- a/src/main/java/com/iemr/common/data/dynamic_from/FormField.java +++ b/src/main/java/com/iemr/common/data/dynamic_from/FormField.java @@ -53,6 +53,12 @@ public class FormField { @Column(name = "sequence") private Integer sequence; + @Column(name = "is_editable") + private Boolean isEditable; + + @Column(name = "state_code") + private Integer stateCode; + @Column(name = "created_at") private LocalDateTime createdAt = LocalDateTime.now(); diff --git a/src/main/java/com/iemr/common/data/users/UserServiceRole.java b/src/main/java/com/iemr/common/data/users/UserServiceRole.java new file mode 100644 index 00000000..63af40dd --- /dev/null +++ b/src/main/java/com/iemr/common/data/users/UserServiceRole.java @@ -0,0 +1,368 @@ +package com.iemr.common.data.users; + +import jakarta.persistence.*; +import java.util.Objects; + +@Entity +@Table(name = "v_userservicerolemapping", schema = "db_iemr") +public class UserServiceRole { + private Integer userId; + private int usrMappingId; + private String name; + private String userName; + private Short serviceId; + private String serviceName; + private Boolean isNational; + private Integer stateId; + private String stateName; + private Integer workingDistrictId; + private String workingDistrictName; + private Integer workingLocationId; + private Short serviceProviderId; + private String locationName; + private String workingLocationAddress; + private Integer roleId; + private String roleName; + private Integer providerServiceMapId; + private String agentId; + private Short psmStatusId; + private String psmStatus; + private Boolean userServciceRoleDeleted; + private Boolean userDeleted; + private Boolean serviceProviderDeleted; + private Boolean roleDeleted; + private Boolean providerServiceMappingDeleted; + private Boolean isInbound; + private Boolean isOutbound; + private Integer blockid; + private String blockname; + private String villageid; + private String villagename; + + @Basic + @Column(name = "UserID") + public Integer getUserId() { + return userId; + } + + public void setUserId(Integer userId) { + this.userId = userId; + } + + @Basic + @Column(name = "USRMappingID") + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + public int getUsrMappingId() { + return usrMappingId; + } + + public void setUsrMappingId(int usrMappingId) { + this.usrMappingId = usrMappingId; + } + + @Basic + @Column(name = "Name") + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Basic + @Column(name = "UserName") + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + @Basic + @Column(name = "ServiceID") + public Short getServiceId() { + return serviceId; + } + + public void setServiceId(Short serviceId) { + this.serviceId = serviceId; + } + + @Basic + @Column(name = "ServiceName") + public String getServiceName() { + return serviceName; + } + + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + @Basic + @Column(name = "IsNational") + public Boolean getNational() { + return isNational; + } + + public void setNational(Boolean national) { + isNational = national; + } + + @Basic + @Column(name = "StateID") + public Integer getStateId() { + return stateId; + } + + public void setStateId(Integer stateId) { + this.stateId = stateId; + } + + @Basic + @Column(name = "StateName") + public String getStateName() { + return stateName; + } + + public void setStateName(String stateName) { + this.stateName = stateName; + } + + @Basic + @Column(name = "WorkingDistrictID") + public Integer getWorkingDistrictId() { + return workingDistrictId; + } + + public void setWorkingDistrictId(Integer workingDistrictId) { + this.workingDistrictId = workingDistrictId; + } + + @Basic + @Column(name = "WorkingDistrictName") + public String getWorkingDistrictName() { + return workingDistrictName; + } + + public void setWorkingDistrictName(String workingDistrictName) { + this.workingDistrictName = workingDistrictName; + } + + @Basic + @Column(name = "WorkingLocationID") + public Integer getWorkingLocationId() { + return workingLocationId; + } + + public void setWorkingLocationId(Integer workingLocationId) { + this.workingLocationId = workingLocationId; + } + + @Basic + @Column(name = "ServiceProviderID") + public Short getServiceProviderId() { + return serviceProviderId; + } + + public void setServiceProviderId(Short serviceProviderId) { + this.serviceProviderId = serviceProviderId; + } + + @Basic + @Column(name = "LocationName") + public String getLocationName() { + return locationName; + } + + public void setLocationName(String locationName) { + this.locationName = locationName; + } + + @Basic + @Column(name = "WorkingLocationAddress") + public String getWorkingLocationAddress() { + return workingLocationAddress; + } + + public void setWorkingLocationAddress(String workingLocationAddress) { + this.workingLocationAddress = workingLocationAddress; + } + + @Basic + @Column(name = "RoleID") + public Integer getRoleId() { + return roleId; + } + + public void setRoleId(Integer roleId) { + this.roleId = roleId; + } + + @Basic + @Column(name = "RoleName") + public String getRoleName() { + return roleName; + } + + public void setRoleName(String roleName) { + this.roleName = roleName; + } + + @Basic + @Column(name = "ProviderServiceMapID") + public Integer getProviderServiceMapId() { + return providerServiceMapId; + } + + public void setProviderServiceMapId(Integer providerServiceMapId) { + this.providerServiceMapId = providerServiceMapId; + } + + @Basic + @Column(name = "AgentID") + public String getAgentId() { + return agentId; + } + + public void setAgentId(String agentId) { + this.agentId = agentId; + } + + @Basic + @Column(name = "PSMStatusID") + public Short getPsmStatusId() { + return psmStatusId; + } + + public void setPsmStatusId(Short psmStatusId) { + this.psmStatusId = psmStatusId; + } + + @Basic + @Column(name = "PSMStatus") + public String getPsmStatus() { + return psmStatus; + } + + public void setPsmStatus(String psmStatus) { + this.psmStatus = psmStatus; + } + + @Basic + @Column(name = "UserServciceRoleDeleted") + public Boolean getUserServciceRoleDeleted() { + return userServciceRoleDeleted; + } + + public void setUserServciceRoleDeleted(Boolean userServciceRoleDeleted) { + this.userServciceRoleDeleted = userServciceRoleDeleted; + } + + @Basic + @Column(name = "UserDeleted") + public Boolean getUserDeleted() { + return userDeleted; + } + + public void setUserDeleted(Boolean userDeleted) { + this.userDeleted = userDeleted; + } + + @Basic + @Column(name = "ServiceProviderDeleted") + public Boolean getServiceProviderDeleted() { + return serviceProviderDeleted; + } + + public void setServiceProviderDeleted(Boolean serviceProviderDeleted) { + this.serviceProviderDeleted = serviceProviderDeleted; + } + + @Basic + @Column(name = "RoleDeleted") + public Boolean getRoleDeleted() { + return roleDeleted; + } + + public void setRoleDeleted(Boolean roleDeleted) { + this.roleDeleted = roleDeleted; + } + + @Basic + @Column(name = "ProviderServiceMappingDeleted") + public Boolean getProviderServiceMappingDeleted() { + return providerServiceMappingDeleted; + } + + public void setProviderServiceMappingDeleted(Boolean providerServiceMappingDeleted) { + this.providerServiceMappingDeleted = providerServiceMappingDeleted; + } + + @Basic + @Column(name = "isInbound") + public Boolean getInbound() { + return isInbound; + } + + public void setInbound(Boolean inbound) { + isInbound = inbound; + } + + @Basic + @Column(name = "isOutbound") + public Boolean getOutbound() { + return isOutbound; + } + + public void setOutbound(Boolean outbound) { + isOutbound = outbound; + } + + @Basic + @Column(name = "blockid") + public Integer getBlockid() { + return blockid; + } + + public void setBlockid(Integer blockid) { + this.blockid = blockid; + } + + @Basic + @Column(name = "blockname") + public String getBlockname() { + return blockname; + } + + public void setBlockname(String blockname) { + this.blockname = blockname; + } + + @Basic + @Column(name = "villageid") + public String getVillageid() { + return villageid; + } + + public void setVillageid(String villageid) { + this.villageid = villageid; + } + + @Basic + @Column(name = "villagename") + public String getVillagename() { + return villagename; + } + + public void setVillagename(String villagename) { + this.villagename = villagename; + } + + @Override + public int hashCode() { + return Objects.hash(userId, usrMappingId, name, userName, serviceId, serviceName, isNational, stateId, stateName, workingDistrictId, workingDistrictName, workingLocationId, serviceProviderId, locationName, workingLocationAddress, roleId, roleName, providerServiceMapId, agentId, psmStatusId, psmStatus, userServciceRoleDeleted, userDeleted, serviceProviderDeleted, roleDeleted, providerServiceMappingDeleted, isInbound, isOutbound, blockid, blockname, villageid, villagename); + } +} diff --git a/src/main/java/com/iemr/common/dto/dynamicForm/FieldResponseDTO.java b/src/main/java/com/iemr/common/dto/dynamicForm/FieldResponseDTO.java index 3415d91a..e41f8e80 100644 --- a/src/main/java/com/iemr/common/dto/dynamicForm/FieldResponseDTO.java +++ b/src/main/java/com/iemr/common/dto/dynamicForm/FieldResponseDTO.java @@ -18,6 +18,8 @@ public class FieldResponseDTO { private String defaultValue; private String placeholder; private Integer sequence; + private Boolean isEditable; + private Integer stateCode; private List options; private Map validation; private Map conditional; diff --git a/src/main/java/com/iemr/common/repository/users/UserServiceRoleRepo.java b/src/main/java/com/iemr/common/repository/users/UserServiceRoleRepo.java new file mode 100644 index 00000000..ee7db934 --- /dev/null +++ b/src/main/java/com/iemr/common/repository/users/UserServiceRoleRepo.java @@ -0,0 +1,16 @@ +package com.iemr.common.repository.users; + +import com.iemr.common.data.users.UserServiceRole; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface UserServiceRoleRepo extends JpaRepository { + UserServiceRole findByUserName(String userName); + UserServiceRole findByUserId(Integer userId); + +} diff --git a/src/main/java/com/iemr/common/service/dynamicForm/FormMasterService.java b/src/main/java/com/iemr/common/service/dynamicForm/FormMasterService.java index 6d22e59a..2f7360b8 100644 --- a/src/main/java/com/iemr/common/service/dynamicForm/FormMasterService.java +++ b/src/main/java/com/iemr/common/service/dynamicForm/FormMasterService.java @@ -16,7 +16,7 @@ public interface FormMasterService { List createField(List dto); FormField updateField(FieldDTO dto); - FormResponseDTO getStructuredFormByFormId(String formId,String lang); + FormResponseDTO getStructuredFormByFormId(String formId,String lang,String token); void deleteField(Long fieldId); } diff --git a/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java b/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java index 98d93dbe..27dd5d08 100644 --- a/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java +++ b/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java @@ -7,23 +7,29 @@ import com.iemr.common.data.dynamic_from.FormField; import com.iemr.common.data.dynamic_from.FormModule; import com.iemr.common.data.translation.Translation; +import com.iemr.common.data.users.UserServiceRole; +import com.iemr.common.data.users.UserServiceRoleMapping; import com.iemr.common.dto.dynamicForm.*; import com.iemr.common.repository.dynamic_form.FieldRepository; import com.iemr.common.repository.dynamic_form.FormRepository; import com.iemr.common.repository.dynamic_form.ModuleRepository; import com.iemr.common.repository.translation.TranslationRepo; +import com.iemr.common.repository.users.UserRoleMappingRepository; +import com.iemr.common.repository.users.UserServiceRoleRepo; +import com.iemr.common.utils.JwtUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.fasterxml.jackson.core.type.TypeReference; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.Map; +import javax.persistence.criteria.CriteriaBuilder; +import java.util.*; import java.util.stream.Collectors; @Service public class FormMasterServiceImpl implements FormMasterService { + final Logger logger = LoggerFactory.getLogger(this.getClass().getName()); @Autowired private ModuleRepository moduleRepo; @@ -33,6 +39,12 @@ public class FormMasterServiceImpl implements FormMasterService { @Autowired private TranslationRepo translationRepo; + @Autowired + private UserServiceRoleRepo userServiceRoleRepo; + + @Autowired + private JwtUtil jwtUtil; + @Override public FormModule createModule(ModuleDTO dto) { FormModule module = new FormModule(); @@ -103,99 +115,115 @@ public FormField updateField(FieldDTO dto) { } @Override - public FormResponseDTO getStructuredFormByFormId(String formId,String lang) { - FormDefinition form = formRepo.findByFormId(formId) - .orElseThrow(() -> new IllegalArgumentException("Invalid form ID")); + public FormResponseDTO getStructuredFormByFormId(String formId,String lang,String token) { + int stateId =0 ; + + try { + UserServiceRole userServiceRole= userServiceRoleRepo.findByUserName(jwtUtil.getUsernameFromToken(token)); + if(userServiceRole!=null){ + stateId = userServiceRole.getStateId(); + } + FormDefinition form = formRepo.findByFormId(formId) + .orElseThrow(() -> new IllegalArgumentException("Invalid form ID")); - List fields = fieldRepo.findByForm_FormIdOrderBySequenceAsc(formId); - ObjectMapper objectMapper = new ObjectMapper(); + List fields = fieldRepo.findByForm_FormIdOrderBySequenceAsc(formId); + ObjectMapper objectMapper = new ObjectMapper(); - List fieldDtos = fields.stream() - .map(field -> { - String labelKey = field.getFieldId(); // field label already contains label_key + Integer finalStateId = stateId; + List fieldDtos = fields.stream().filter(formField -> Objects.equals(formField.getStateCode(), finalStateId) || formField.getStateCode()==0) + .map(field -> { + String labelKey = field.getFieldId(); // field label already contains label_key - Translation t = translationRepo.findByLabelKeyAndIsActive(labelKey, true) - .orElse(null); + Translation t = translationRepo.findByLabelKeyAndIsActive(labelKey, true) + .orElse(null); - String translatedLabel = field.getLabel(); // fallback + String translatedLabel = field.getLabel(); // fallback - if (t != null) { - if ("hi".equalsIgnoreCase(lang)) { - translatedLabel = t.getHindiTranslation(); - } else if("as".equalsIgnoreCase(lang)){ - translatedLabel = t.getAssameseTranslation(); - }else if("en".equalsIgnoreCase(lang)){ - translatedLabel = t.getEnglish(); + if (t != null) { + if ("hi".equalsIgnoreCase(lang)) { + translatedLabel = t.getHindiTranslation(); + } else if("as".equalsIgnoreCase(lang)){ + translatedLabel = t.getAssameseTranslation(); + }else if("en".equalsIgnoreCase(lang)){ + translatedLabel = t.getEnglish(); - } - } - - FieldResponseDTO dto = new FieldResponseDTO(); - dto.setId(field.getId()); - dto.setVisible(field.getIsVisible()); - dto.setFormId(field.getForm().getFormId()); - dto.setSectionTitle(field.getSectionTitle()); - dto.setFieldId(field.getFieldId()); - dto.setLabel(translatedLabel); - dto.setType(field.getType()); - dto.setIsRequired(field.getIsRequired()); - dto.setDefaultValue(field.getDefaultValue()); - dto.setPlaceholder(field.getPlaceholder()); - dto.setSequence(field.getSequence()); - - try { - // Handle options - if (field.getOptions() != null && !field.getOptions().isBlank()) { - JsonNode node = objectMapper.readTree(field.getOptions()); - List options = null; - if (node.isArray()) { - options = objectMapper.convertValue(node, new TypeReference<>() {}); - } else if (node.has("options")) { - options = objectMapper.convertValue(node.get("options"), new TypeReference<>() {}); } - dto.setOptions(options == null || options.isEmpty() ? null : options); - } else { - dto.setOptions(null); } - // Handle validation - if (field.getValidation() != null && !field.getValidation().isBlank()) { - Map validation = objectMapper.readValue(field.getValidation(), new TypeReference<>() {}); - dto.setValidation(validation.isEmpty() ? null : validation); - } else { - dto.setValidation(null); - } + FieldResponseDTO dto = new FieldResponseDTO(); + dto.setId(field.getId()); + dto.setIsEditable(field.getIsEditable()); + dto.setStateCode(field.getStateCode()); + dto.setVisible(field.getIsVisible()); + dto.setFormId(field.getForm().getFormId()); + dto.setSectionTitle(field.getSectionTitle()); + dto.setFieldId(field.getFieldId()); + dto.setLabel(translatedLabel); + dto.setType(field.getType()); + dto.setIsRequired(field.getIsRequired()); + dto.setDefaultValue(field.getDefaultValue()); + dto.setPlaceholder(field.getPlaceholder()); + dto.setSequence(field.getSequence()); + + try { + // Handle options + if (field.getOptions() != null && !field.getOptions().isBlank()) { + JsonNode node = objectMapper.readTree(field.getOptions()); + List options = null; + if (node.isArray()) { + options = objectMapper.convertValue(node, new TypeReference<>() {}); + } else if (node.has("options")) { + options = objectMapper.convertValue(node.get("options"), new TypeReference<>() {}); + } + dto.setOptions(options == null || options.isEmpty() ? null : options); + } else { + dto.setOptions(null); + } + + // Handle validation + if (field.getValidation() != null && !field.getValidation().isBlank()) { + Map validation = objectMapper.readValue(field.getValidation(), new TypeReference<>() {}); + dto.setValidation(validation.isEmpty() ? null : validation); + } else { + dto.setValidation(null); + } - // Handle conditional - if (field.getConditional() != null && !field.getConditional().isBlank()) { - Map conditional = objectMapper.readValue(field.getConditional(), new TypeReference<>() {}); - dto.setConditional(conditional.isEmpty() ? null : conditional); - } else { - dto.setConditional(null); + // Handle conditional + if (field.getConditional() != null && !field.getConditional().isBlank()) { + Map conditional = objectMapper.readValue(field.getConditional(), new TypeReference<>() {}); + dto.setConditional(conditional.isEmpty() ? null : conditional); + } else { + dto.setConditional(null); + } + } catch (Exception e) { + + System.err.println("JSON Parsing Error in field: " + field.getFieldId()); + throw new RuntimeException("Failed to parse JSON for field: " + field.getFieldId(), e); } - } catch (Exception e) { - System.err.println("JSON Parsing Error in field: " + field.getFieldId()); - throw new RuntimeException("Failed to parse JSON for field: " + field.getFieldId(), e); - } + return dto; + }) + .sorted(Comparator.comparing(FieldResponseDTO::getId)) + .collect(Collectors.toList()); + - return dto; - }) - .sorted(Comparator.comparing(FieldResponseDTO::getId)) - .collect(Collectors.toList()); + GroupedFieldResponseDTO singleSection = new GroupedFieldResponseDTO(); + singleSection.setSectionTitle(singleSection.getSectionTitle()); // your custom section title + singleSection.setFields(fieldDtos); + FormResponseDTO response = new FormResponseDTO(); + response.setVersion(form.getVersion()); + response.setFormId(form.getFormId()); + response.setFormName(form.getFormName()); + response.setSections(List.of(singleSection)); + return response; - GroupedFieldResponseDTO singleSection = new GroupedFieldResponseDTO(); - singleSection.setSectionTitle(singleSection.getSectionTitle()); // your custom section title - singleSection.setFields(fieldDtos); + }catch (Exception e){ + logger.error("Exception:"+e.getMessage()); + } - FormResponseDTO response = new FormResponseDTO(); - response.setVersion(form.getVersion()); - response.setFormId(form.getFormId()); - response.setFormName(form.getFormName()); - response.setSections(List.of(singleSection)); + return null; - return response; } From 4d20b8cf8be32079cd84ec21c4c461f24f32a752 Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Wed, 14 Jan 2026 16:46:22 +0530 Subject: [PATCH 15/58] implement state wise hide un hide form fields --- .../common/controller/dynamicForm/DynamicFormController.java | 1 + .../java/com/iemr/common/data/users/UserServiceRole.java | 1 + .../iemr/common/repository/dynamic_form/FieldRepository.java | 5 +++++ .../iemr/common/repository/users/UserServiceRoleRepo.java | 1 + .../iemr/common/service/dynamicForm/FormMasterService.java | 1 + .../common/service/dynamicForm/FormMasterServiceImpl.java | 5 ++--- 6 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/iemr/common/controller/dynamicForm/DynamicFormController.java b/src/main/java/com/iemr/common/controller/dynamicForm/DynamicFormController.java index cd83246f..3d49e808 100644 --- a/src/main/java/com/iemr/common/controller/dynamicForm/DynamicFormController.java +++ b/src/main/java/com/iemr/common/controller/dynamicForm/DynamicFormController.java @@ -97,4 +97,5 @@ public ResponseEntity> getStructuredForm(@PathVariable String for } + } diff --git a/src/main/java/com/iemr/common/data/users/UserServiceRole.java b/src/main/java/com/iemr/common/data/users/UserServiceRole.java index 63af40dd..27c044cf 100644 --- a/src/main/java/com/iemr/common/data/users/UserServiceRole.java +++ b/src/main/java/com/iemr/common/data/users/UserServiceRole.java @@ -365,4 +365,5 @@ public void setVillagename(String villagename) { public int hashCode() { return Objects.hash(userId, usrMappingId, name, userName, serviceId, serviceName, isNational, stateId, stateName, workingDistrictId, workingDistrictName, workingLocationId, serviceProviderId, locationName, workingLocationAddress, roleId, roleName, providerServiceMapId, agentId, psmStatusId, psmStatus, userServciceRoleDeleted, userDeleted, serviceProviderDeleted, roleDeleted, providerServiceMappingDeleted, isInbound, isOutbound, blockid, blockname, villageid, villagename); } + } diff --git a/src/main/java/com/iemr/common/repository/dynamic_form/FieldRepository.java b/src/main/java/com/iemr/common/repository/dynamic_form/FieldRepository.java index 4aea5698..20a86e3e 100644 --- a/src/main/java/com/iemr/common/repository/dynamic_form/FieldRepository.java +++ b/src/main/java/com/iemr/common/repository/dynamic_form/FieldRepository.java @@ -9,4 +9,9 @@ @Repository public interface FieldRepository extends JpaRepository { List findByForm_FormIdOrderBySequenceAsc(String formId); + List findByForm_FormIdAndStateCodeOrderBySequenceAsc( + String formId, + Integer stateCode + ); + } diff --git a/src/main/java/com/iemr/common/repository/users/UserServiceRoleRepo.java b/src/main/java/com/iemr/common/repository/users/UserServiceRoleRepo.java index ee7db934..798f47c1 100644 --- a/src/main/java/com/iemr/common/repository/users/UserServiceRoleRepo.java +++ b/src/main/java/com/iemr/common/repository/users/UserServiceRoleRepo.java @@ -13,4 +13,5 @@ public interface UserServiceRoleRepo extends JpaRepository new IllegalArgumentException("Invalid form ID")); - List fields = fieldRepo.findByForm_FormIdOrderBySequenceAsc(formId); + List fields = fieldRepo.findByForm_FormIdAndStateCodeOrderBySequenceAsc(formId,stateId); ObjectMapper objectMapper = new ObjectMapper(); - Integer finalStateId = stateId; - List fieldDtos = fields.stream().filter(formField -> Objects.equals(formField.getStateCode(), finalStateId) || formField.getStateCode()==0) + List fieldDtos = fields.stream() .map(field -> { String labelKey = field.getFieldId(); // field label already contains label_key From bed849b895b9aca8e121696717e3668bd55ac91c Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Fri, 16 Jan 2026 20:18:50 +0530 Subject: [PATCH 16/58] implement state wise hide un hide form fields --- .../common/controller/dynamicForm/DynamicFormController.java | 1 + src/main/java/com/iemr/common/data/translation/Translation.java | 1 + .../com/iemr/common/repository/dynamic_form/FieldRepository.java | 1 + .../com/iemr/common/repository/translation/TranslationRepo.java | 1 + .../com/iemr/common/repository/users/UserServiceRoleRepo.java | 1 + .../com/iemr/common/service/dynamicForm/FormMasterService.java | 1 + .../iemr/common/service/dynamicForm/FormMasterServiceImpl.java | 1 - 7 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/iemr/common/controller/dynamicForm/DynamicFormController.java b/src/main/java/com/iemr/common/controller/dynamicForm/DynamicFormController.java index 3d49e808..6f64a135 100644 --- a/src/main/java/com/iemr/common/controller/dynamicForm/DynamicFormController.java +++ b/src/main/java/com/iemr/common/controller/dynamicForm/DynamicFormController.java @@ -98,4 +98,5 @@ public ResponseEntity> getStructuredForm(@PathVariable String for + } diff --git a/src/main/java/com/iemr/common/data/translation/Translation.java b/src/main/java/com/iemr/common/data/translation/Translation.java index 0dad116d..52cb8027 100644 --- a/src/main/java/com/iemr/common/data/translation/Translation.java +++ b/src/main/java/com/iemr/common/data/translation/Translation.java @@ -22,4 +22,5 @@ public class Translation { private String assameseTranslation; @Column(name = "is_active") private Boolean isActive; + } diff --git a/src/main/java/com/iemr/common/repository/dynamic_form/FieldRepository.java b/src/main/java/com/iemr/common/repository/dynamic_form/FieldRepository.java index 20a86e3e..50e84248 100644 --- a/src/main/java/com/iemr/common/repository/dynamic_form/FieldRepository.java +++ b/src/main/java/com/iemr/common/repository/dynamic_form/FieldRepository.java @@ -14,4 +14,5 @@ List findByForm_FormIdAndStateCodeOrderBySequenceAsc( Integer stateCode ); + } diff --git a/src/main/java/com/iemr/common/repository/translation/TranslationRepo.java b/src/main/java/com/iemr/common/repository/translation/TranslationRepo.java index f6a5dcb0..139b5ee9 100644 --- a/src/main/java/com/iemr/common/repository/translation/TranslationRepo.java +++ b/src/main/java/com/iemr/common/repository/translation/TranslationRepo.java @@ -10,4 +10,5 @@ public interface TranslationRepo extends JpaRepository { Optional findByLabelKeyAndIsActive(String labelKey, boolean isActive); + } diff --git a/src/main/java/com/iemr/common/repository/users/UserServiceRoleRepo.java b/src/main/java/com/iemr/common/repository/users/UserServiceRoleRepo.java index 798f47c1..17edb5e0 100644 --- a/src/main/java/com/iemr/common/repository/users/UserServiceRoleRepo.java +++ b/src/main/java/com/iemr/common/repository/users/UserServiceRoleRepo.java @@ -14,4 +14,5 @@ public interface UserServiceRoleRepo extends JpaRepository Date: Sun, 18 Jan 2026 11:30:45 +0530 Subject: [PATCH 17/58] enhance welcome sms code --- .../com/iemr/common/CommonApplication.java | 2 + .../RegisterBenificiaryServiceImpl.java | 25 ++++++- .../WelcomeBenificarySmsServiceImpl.java | 72 +++++++++---------- 3 files changed, 60 insertions(+), 39 deletions(-) diff --git a/src/main/java/com/iemr/common/CommonApplication.java b/src/main/java/com/iemr/common/CommonApplication.java index e4a59994..dff5d809 100644 --- a/src/main/java/com/iemr/common/CommonApplication.java +++ b/src/main/java/com/iemr/common/CommonApplication.java @@ -29,6 +29,7 @@ import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; +import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.web.client.RestTemplate; @@ -40,6 +41,7 @@ @SpringBootApplication @EnableScheduling +@EnableAsync public class CommonApplication extends SpringBootServletInitializer { @Bean diff --git a/src/main/java/com/iemr/common/service/beneficiary/RegisterBenificiaryServiceImpl.java b/src/main/java/com/iemr/common/service/beneficiary/RegisterBenificiaryServiceImpl.java index 54637a4c..388d6298 100644 --- a/src/main/java/com/iemr/common/service/beneficiary/RegisterBenificiaryServiceImpl.java +++ b/src/main/java/com/iemr/common/service/beneficiary/RegisterBenificiaryServiceImpl.java @@ -29,6 +29,7 @@ import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.CompletableFuture; import com.iemr.common.model.beneficiary.RMNCHBeneficiaryDetailsRmnch; import com.iemr.common.service.welcomeSms.WelcomeBenificarySmsService; @@ -234,9 +235,27 @@ public String save(BeneficiaryModel beneficiaryModel, HttpServletRequest servlet } else { return response.toString(); } - if(beneficiary!=null){ - if(beneficiary.getBenPhoneMaps().get(0).getPhoneNo()!=null){ - welcomeBenificarySmsService.sendWelcomeSMStoBenificiary(beneficiary.getBenPhoneMaps().get(0).getPhoneNo(),beneficiary.getFirstName()+" "+beneficiary.getLastName(),beneficiary.getBeneficiaryID()); + // ========== SEND SMS BUT DON'T FAIL IF IT ERRORS ========== + if (beneficiary != null && beneficiary.getBenPhoneMaps() != null && !beneficiary.getBenPhoneMaps().isEmpty()) { + String phoneNo = beneficiary.getBenPhoneMaps().get(0).getPhoneNo(); + + if (phoneNo != null && !phoneNo.trim().isEmpty()) { + String beneficiaryName = (beneficiary.getFirstName() != null ? beneficiary.getFirstName() : "") + " " + + (beneficiary.getLastName() != null ? beneficiary.getLastName() : ""); + + try { + logger.info("[SMS] Attempting to send welcome SMS to: " + phoneNo); + String smsResult = welcomeBenificarySmsService.sendWelcomeSMStoBenificiary( + phoneNo, + beneficiaryName.trim(), + beneficiary.getBeneficiaryID() + ); + logger.info("[SMS] Result: " + smsResult); + } catch (Exception smsError) { + // SMS failed but beneficiary is already created - don't fail the request + logger.warn("[SMS] Failed to send SMS: " + smsError.getMessage() + + " - But beneficiary already created successfully"); + } } } diff --git a/src/main/java/com/iemr/common/service/welcomeSms/WelcomeBenificarySmsServiceImpl.java b/src/main/java/com/iemr/common/service/welcomeSms/WelcomeBenificarySmsServiceImpl.java index 67b642ab..a80a98e8 100644 --- a/src/main/java/com/iemr/common/service/welcomeSms/WelcomeBenificarySmsServiceImpl.java +++ b/src/main/java/com/iemr/common/service/welcomeSms/WelcomeBenificarySmsServiceImpl.java @@ -12,6 +12,7 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; @@ -46,58 +47,57 @@ public class WelcomeBenificarySmsServiceImpl implements WelcomeBenificarySmsServ private String smsTemplateName = "welcome_sms"; - private String smsTemplate; + private String smsTemplate =null; @Override + @Async public String sendWelcomeSMStoBenificiary(String contactNo, String beneficiaryName, String beneficiaryId) { - final RestTemplate restTemplate = new RestTemplate(); - - Optional 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; + + final RestTemplate restTemplate = new RestTemplate(); - String message = smsTemplate.replace("$$BENE_NAME$$", beneficiaryName).replace("$$BEN_ID$$", beneficiaryId); - // Build payload - Map 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())); + Optional 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); + // Build payload + Map 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> request = new HttpEntity<>(payload, headers); - // Call API - ResponseEntity 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"; + // Call API + ResponseEntity 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"; + } } + } catch (Exception e) { return "Error sending SMS: " + e.getMessage().toString(); } - + return null; } } From 4104ad034e2983d7e11f5696057a607181e579ec Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Sun, 18 Jan 2026 23:15:23 +0530 Subject: [PATCH 18/58] fix hide unhide form issue --- .../common/service/dynamicForm/FormMasterServiceImpl.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java b/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java index 2edd805c..4e952a68 100644 --- a/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java +++ b/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java @@ -125,10 +125,11 @@ public FormResponseDTO getStructuredFormByFormId(String formId,String lang,Strin FormDefinition form = formRepo.findByFormId(formId) .orElseThrow(() -> new IllegalArgumentException("Invalid form ID")); - List fields = fieldRepo.findByForm_FormIdAndStateCodeOrderBySequenceAsc(formId,stateId); + List fields = fieldRepo.findByForm_FormIdOrderBySequenceAsc(formId); ObjectMapper objectMapper = new ObjectMapper(); - List fieldDtos = fields.stream() + int finalStateId = stateId; + List fieldDtos = fields.stream().filter(formField -> formField.getStateCode()== 0 || formField.getStateCode()== finalStateId) .map(field -> { String labelKey = field.getFieldId(); // field label already contains label_key From fdbc9b762da293fe4763e3ef45206c332400de79 Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Wed, 21 Jan 2026 17:46:01 +0530 Subject: [PATCH 19/58] fix hide unhide form issue --- .../common/service/dynamicForm/FormMasterServiceImpl.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java b/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java index 27dd5d08..a4db6b46 100644 --- a/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java +++ b/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java @@ -126,11 +126,11 @@ public FormResponseDTO getStructuredFormByFormId(String formId,String lang,Strin FormDefinition form = formRepo.findByFormId(formId) .orElseThrow(() -> new IllegalArgumentException("Invalid form ID")); - List fields = fieldRepo.findByForm_FormIdOrderBySequenceAsc(formId); + int finalStateId = stateId; + List fields = fieldRepo.findByForm_FormIdOrderBySequenceAsc(formId).stream().filter(formField -> (formField.getStateCode() == 0 || formField.getStateCode() == finalStateId)).toList(); ObjectMapper objectMapper = new ObjectMapper(); - Integer finalStateId = stateId; - List fieldDtos = fields.stream().filter(formField -> Objects.equals(formField.getStateCode(), finalStateId) || formField.getStateCode()==0) + List fieldDtos = fields.stream() .map(field -> { String labelKey = field.getFieldId(); // field label already contains label_key From 0c64cef2ec7456507645d96726038f51f41f5c27 Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Wed, 21 Jan 2026 17:58:48 +0530 Subject: [PATCH 20/58] fix hide unhide form issue --- .../iemr/common/service/dynamicForm/FormMasterServiceImpl.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java b/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java index a4db6b46..645bc272 100644 --- a/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java +++ b/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java @@ -126,8 +126,7 @@ public FormResponseDTO getStructuredFormByFormId(String formId,String lang,Strin FormDefinition form = formRepo.findByFormId(formId) .orElseThrow(() -> new IllegalArgumentException("Invalid form ID")); - int finalStateId = stateId; - List fields = fieldRepo.findByForm_FormIdOrderBySequenceAsc(formId).stream().filter(formField -> (formField.getStateCode() == 0 || formField.getStateCode() == finalStateId)).toList(); + List fields = fieldRepo.findByForm_FormIdOrderBySequenceAsc(formId); ObjectMapper objectMapper = new ObjectMapper(); List fieldDtos = fields.stream() From b3a1922544e11e181c4ac043cac897d0c1c54e4c Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Wed, 21 Jan 2026 18:11:51 +0530 Subject: [PATCH 21/58] fix hide unhide form issue --- .../service/dynamicForm/FormMasterServiceImpl.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java b/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java index 645bc272..9d3b14e3 100644 --- a/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java +++ b/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java @@ -122,7 +122,16 @@ public FormResponseDTO getStructuredFormByFormId(String formId,String lang,Strin UserServiceRole userServiceRole= userServiceRoleRepo.findByUserName(jwtUtil.getUsernameFromToken(token)); if(userServiceRole!=null){ stateId = userServiceRole.getStateId(); + logger.info("State:Id"+stateId); } + if(!token.isEmpty()){ + logger.info("Token: "+token); + + } + + logger.info("State outSide: "+stateId); + + FormDefinition form = formRepo.findByFormId(formId) .orElseThrow(() -> new IllegalArgumentException("Invalid form ID")); From c4c1d48f79184173906838814177e81530c76d24 Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Wed, 21 Jan 2026 18:17:59 +0530 Subject: [PATCH 22/58] fix hide unhide form issue --- .../common/controller/dynamicForm/DynamicFormController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/iemr/common/controller/dynamicForm/DynamicFormController.java b/src/main/java/com/iemr/common/controller/dynamicForm/DynamicFormController.java index cd83246f..62bf7e7c 100644 --- a/src/main/java/com/iemr/common/controller/dynamicForm/DynamicFormController.java +++ b/src/main/java/com/iemr/common/controller/dynamicForm/DynamicFormController.java @@ -84,7 +84,7 @@ public ResponseEntity> deleteField(@PathVariable Long fieldId) { } @GetMapping(value = "form/{formId}/fields") - public ResponseEntity> getStructuredForm(@PathVariable String formId, @RequestParam(name = "lang", defaultValue = "en") String lang,@RequestHeader(value = "JwtToken") String token) { + public ResponseEntity> getStructuredForm(@PathVariable String formId, @RequestParam(name = "lang", defaultValue = "en") String lang,@RequestHeader(value = "jwttoken") String token) { try { Object result = formMasterService.getStructuredFormByFormId(formId,lang,token); return ResponseEntity.status(HttpStatus.OK) From f10f37cb86948e657f84c4663627403b70d05930 Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Wed, 21 Jan 2026 18:23:40 +0530 Subject: [PATCH 23/58] fix hide unhide form issue --- .../service/dynamicForm/FormMasterServiceImpl.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java b/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java index 9d3b14e3..7ce3e01d 100644 --- a/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java +++ b/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java @@ -119,11 +119,14 @@ public FormResponseDTO getStructuredFormByFormId(String formId,String lang,Strin int stateId =0 ; try { - UserServiceRole userServiceRole= userServiceRoleRepo.findByUserName(jwtUtil.getUsernameFromToken(token)); - if(userServiceRole!=null){ - stateId = userServiceRole.getStateId(); - logger.info("State:Id"+stateId); + if(!token.isEmpty()){ + UserServiceRole userServiceRole= userServiceRoleRepo.findByUserName(jwtUtil.getUsernameFromToken(token)); + if(userServiceRole!=null){ + stateId = userServiceRole.getStateId(); + logger.info("State:Id"+stateId); + } } + if(!token.isEmpty()){ logger.info("Token: "+token); From b3da893ff0fb540203939c57f0527565e56ebcc7 Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Wed, 21 Jan 2026 18:32:07 +0530 Subject: [PATCH 24/58] fix hide unhide form issue --- .../dynamicForm/FormMasterServiceImpl.java | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java b/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java index 7ce3e01d..e61f7b17 100644 --- a/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java +++ b/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java @@ -116,23 +116,7 @@ public FormField updateField(FieldDTO dto) { @Override public FormResponseDTO getStructuredFormByFormId(String formId,String lang,String token) { - int stateId =0 ; - try { - if(!token.isEmpty()){ - UserServiceRole userServiceRole= userServiceRoleRepo.findByUserName(jwtUtil.getUsernameFromToken(token)); - if(userServiceRole!=null){ - stateId = userServiceRole.getStateId(); - logger.info("State:Id"+stateId); - } - } - - if(!token.isEmpty()){ - logger.info("Token: "+token); - - } - - logger.info("State outSide: "+stateId); FormDefinition form = formRepo.findByFormId(formId) From f94d0b07cc4ebfc6c4cdb54b13a0df6dd7e14d75 Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Wed, 21 Jan 2026 18:40:18 +0530 Subject: [PATCH 25/58] fix hide unhide form issue --- .../common/repository/users/UserServiceRoleRepo.java | 2 +- .../service/dynamicForm/FormMasterServiceImpl.java | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/iemr/common/repository/users/UserServiceRoleRepo.java b/src/main/java/com/iemr/common/repository/users/UserServiceRoleRepo.java index ee7db934..65691053 100644 --- a/src/main/java/com/iemr/common/repository/users/UserServiceRoleRepo.java +++ b/src/main/java/com/iemr/common/repository/users/UserServiceRoleRepo.java @@ -10,7 +10,7 @@ @Repository public interface UserServiceRoleRepo extends JpaRepository { - UserServiceRole findByUserName(String userName); + List findByUserName(String userName); UserServiceRole findByUserId(Integer userId); } diff --git a/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java b/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java index e61f7b17..93a8d209 100644 --- a/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java +++ b/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java @@ -116,7 +116,16 @@ public FormField updateField(FieldDTO dto) { @Override public FormResponseDTO getStructuredFormByFormId(String formId,String lang,String token) { + int stateId =0 ; + try { + if(!token.isEmpty()){ + List userServiceRole= userServiceRoleRepo.findByUserName(jwtUtil.getUsernameFromToken(token)); + if(userServiceRole!=null){ + stateId = userServiceRole.get(0).getStateId(); + logger.info("State:Id"+stateId); + } + } FormDefinition form = formRepo.findByFormId(formId) @@ -125,7 +134,8 @@ public FormResponseDTO getStructuredFormByFormId(String formId,String lang,Strin List fields = fieldRepo.findByForm_FormIdOrderBySequenceAsc(formId); ObjectMapper objectMapper = new ObjectMapper(); - List fieldDtos = fields.stream() + int finalStateId = stateId; + List fieldDtos = fields.stream().filter(formField -> (formField.getStateCode()==0 || formField.getStateCode().equals(finalStateId))) .map(field -> { String labelKey = field.getFieldId(); // field label already contains label_key From be7889e1407544df7fe91c48ffa35b83d28003b1 Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Wed, 21 Jan 2026 18:48:20 +0530 Subject: [PATCH 26/58] fix hide unhide form issue --- .../java/com/iemr/common/config/encryption/SecurePassword.java | 3 +++ .../iemr/common/service/dynamicForm/FormMasterServiceImpl.java | 1 + 2 files changed, 4 insertions(+) diff --git a/src/main/java/com/iemr/common/config/encryption/SecurePassword.java b/src/main/java/com/iemr/common/config/encryption/SecurePassword.java index 15463b7a..7aa9c6fd 100644 --- a/src/main/java/com/iemr/common/config/encryption/SecurePassword.java +++ b/src/main/java/com/iemr/common/config/encryption/SecurePassword.java @@ -30,6 +30,8 @@ import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Service; @Service @@ -149,4 +151,5 @@ private byte[] fromHex(String hex) { } return bytes; } + } diff --git a/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java b/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java index 93a8d209..512ad015 100644 --- a/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java +++ b/src/main/java/com/iemr/common/service/dynamicForm/FormMasterServiceImpl.java @@ -128,6 +128,7 @@ public FormResponseDTO getStructuredFormByFormId(String formId,String lang,Strin } + FormDefinition form = formRepo.findByFormId(formId) .orElseThrow(() -> new IllegalArgumentException("Invalid form ID")); From 00223293fed97811c81cec301268b20cc56813e8 Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Wed, 21 Jan 2026 18:48:40 +0530 Subject: [PATCH 27/58] fix hide unhide form issue --- .../com/iemr/common/repository/users/UserServiceRoleRepo.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/iemr/common/repository/users/UserServiceRoleRepo.java b/src/main/java/com/iemr/common/repository/users/UserServiceRoleRepo.java index 65691053..111fcf43 100644 --- a/src/main/java/com/iemr/common/repository/users/UserServiceRoleRepo.java +++ b/src/main/java/com/iemr/common/repository/users/UserServiceRoleRepo.java @@ -11,6 +11,5 @@ @Repository public interface UserServiceRoleRepo extends JpaRepository { List findByUserName(String userName); - UserServiceRole findByUserId(Integer userId); } From ba824bee7ce2cc2717b8c0298dc08513057748cf Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Wed, 21 Jan 2026 18:50:02 +0530 Subject: [PATCH 28/58] fix hide unhide form issue --- .../com/iemr/common/config/encryption/SecurePassword.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/com/iemr/common/config/encryption/SecurePassword.java b/src/main/java/com/iemr/common/config/encryption/SecurePassword.java index 7aa9c6fd..9b717375 100644 --- a/src/main/java/com/iemr/common/config/encryption/SecurePassword.java +++ b/src/main/java/com/iemr/common/config/encryption/SecurePassword.java @@ -29,9 +29,6 @@ import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; - -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Service; @Service @@ -151,5 +148,4 @@ private byte[] fromHex(String hex) { } return bytes; } - } From c0c53333b8ba76f52e1e3bcbe1eb658ef30bcb4a Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Wed, 21 Jan 2026 18:51:26 +0530 Subject: [PATCH 29/58] fix hide unhide form issue --- src/main/resources/application.properties | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 4409afd4..9fb04c9d 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -372,5 +372,4 @@ allowed.file.extensions=msg,pdf,png,jpeg,doc,docx,xlsx,xls,csv,txt ##sms details for beneficiary otp cosent sms-template-name = otp_consent -cors.allowed-origin=https://amritdemo.piramalswasthya.org From 2e18fddcdd254a4ce7ff7ab0ec02df4a9445b9a1 Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Wed, 21 Jan 2026 18:52:29 +0530 Subject: [PATCH 30/58] fix hide unhide form issue --- .../java/com/iemr/common/config/encryption/SecurePassword.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/iemr/common/config/encryption/SecurePassword.java b/src/main/java/com/iemr/common/config/encryption/SecurePassword.java index 9b717375..95cdd7f3 100644 --- a/src/main/java/com/iemr/common/config/encryption/SecurePassword.java +++ b/src/main/java/com/iemr/common/config/encryption/SecurePassword.java @@ -26,7 +26,6 @@ import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.spec.InvalidKeySpecException; - import javax.crypto.SecretKeyFactory; import javax.crypto.spec.PBEKeySpec; import org.springframework.stereotype.Service; From acd9d8e9d7348ddfb43e34330af4537ea262e399 Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Wed, 21 Jan 2026 18:53:58 +0530 Subject: [PATCH 31/58] fix hide unhide form issue --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 11ad9f37..171ab162 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.iemr.common-API common-api - 3.6.0 + 3.6.1 war Common-API From 475a4acae64e598d980de47a89a700d5ae4ac929 Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Wed, 21 Jan 2026 18:55:10 +0530 Subject: [PATCH 32/58] fix hide unhide form issue --- src/main/resources/application.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 9fb04c9d..6b52b9b5 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -372,4 +372,4 @@ allowed.file.extensions=msg,pdf,png,jpeg,doc,docx,xlsx,xls,csv,txt ##sms details for beneficiary otp cosent sms-template-name = otp_consent - +cors.allowed-origin = From f95270843c29a9037ee5fe105c91c3c2556070dc Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Wed, 21 Jan 2026 18:55:47 +0530 Subject: [PATCH 33/58] fix hide unhide form issue --- src/main/resources/application.properties | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 6b52b9b5..eee27bbc 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -76,14 +76,14 @@ get-details-call-report-URL=http://CTI_SERVER/apps/customize_apps/piramil_report #============================================================================ # Configure Main Scheduler Properties #============================================================================ - + org.quartz.scheduler.instanceId = AUTO org.quartz.scheduler.makeSchedulerThreadDaemon = true - + #============================================================================ # Configure ThreadPool #============================================================================ - + org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool org.quartz.threadPool.makeThreadsDaemons = true org.quartz.threadPool.threadCount: 20 @@ -110,6 +110,7 @@ iemr.extend.expiry.time.changePassword=true iemr.session.expiry.time.changePassword=600 identity-api-url-advancesearch =IDENTITY_BASE_URL/id/advanceSearch +identity-api-url-advancesearch-es =IDENTITY_BASE_URL/beneficiary/advancedSearchES identity-api-url-getByBenRegIdList =IDENTITY_BASE_URL/id/getByBenRegIdList identity-api-url-getByPartialBenRegIdList =IDENTITY_BASE_URL/id/getByPartialBenRegIdList identity-api-url-getByPhoneNum =IDENTITY_BASE_URL/id/getByPhoneNum?phoneNum= @@ -118,6 +119,7 @@ identity-api-url-getByBenRegId =IDENTITY_BASE_URL/id/getByBenRegId?benRegId= identity-api-url-benCreate =IDENTITY_BASE_URL/id/create identity-api-url-benEdit =IDENTITY_BASE_URL/id/edit identity-api-url-benEditEducationCommunity=IDENTITY_BASE_URL/id/editEducationOrCommunity +identity-api-url-searchByES=IDENTITY_BASE_URL/beneficiary/search identity-api-url-getByFamilyId=IDENTITY_BASE_URL/id/searchByFamilyId?familyId= identity-api-url-getByGovIdentity=IDENTITY_BASE_URL/id/searchByGovIdentity?identity= @@ -372,4 +374,5 @@ allowed.file.extensions=msg,pdf,png,jpeg,doc,docx,xlsx,xls,csv,txt ##sms details for beneficiary otp cosent sms-template-name = otp_consent -cors.allowed-origin = + +cors.allowed-origin = \ No newline at end of file From 64b2a7e8eb7a4378e1e4ebfddb048b3975ffb44c Mon Sep 17 00:00:00 2001 From: Saurav Mishra Date: Wed, 21 Jan 2026 18:56:43 +0530 Subject: [PATCH 34/58] fix hide unhide form issue --- src/main/resources/application.properties | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index eee27bbc..beefff31 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -110,7 +110,6 @@ iemr.extend.expiry.time.changePassword=true iemr.session.expiry.time.changePassword=600 identity-api-url-advancesearch =IDENTITY_BASE_URL/id/advanceSearch -identity-api-url-advancesearch-es =IDENTITY_BASE_URL/beneficiary/advancedSearchES identity-api-url-getByBenRegIdList =IDENTITY_BASE_URL/id/getByBenRegIdList identity-api-url-getByPartialBenRegIdList =IDENTITY_BASE_URL/id/getByPartialBenRegIdList identity-api-url-getByPhoneNum =IDENTITY_BASE_URL/id/getByPhoneNum?phoneNum= @@ -119,7 +118,6 @@ identity-api-url-getByBenRegId =IDENTITY_BASE_URL/id/getByBenRegId?benRegId= identity-api-url-benCreate =IDENTITY_BASE_URL/id/create identity-api-url-benEdit =IDENTITY_BASE_URL/id/edit identity-api-url-benEditEducationCommunity=IDENTITY_BASE_URL/id/editEducationOrCommunity -identity-api-url-searchByES=IDENTITY_BASE_URL/beneficiary/search identity-api-url-getByFamilyId=IDENTITY_BASE_URL/id/searchByFamilyId?familyId= identity-api-url-getByGovIdentity=IDENTITY_BASE_URL/id/searchByGovIdentity?identity= @@ -375,4 +373,4 @@ allowed.file.extensions=msg,pdf,png,jpeg,doc,docx,xlsx,xls,csv,txt ##sms details for beneficiary otp cosent sms-template-name = otp_consent -cors.allowed-origin = \ No newline at end of file +cors.allowed-origin = From 833f1574eb79fef4611b9c64c78023a2b3469b6b Mon Sep 17 00:00:00 2001 From: Vanitha S <116701245+vanitha1822@users.noreply.github.com> Date: Wed, 23 Jul 2025 11:05:19 +0530 Subject: [PATCH 35/58] fix: video consultation functionality --- src/main/environment/common_ci.properties | 4 +- .../environment/common_example.properties | 4 +- .../videocall/VideoCallController.java | 24 +++++++- .../data/videocall/VideoCallParameters.java | 22 +++++++ .../mapper/videocall/VideoCallMapper.java | 23 +++++++- .../model/videocall/UpdateCallRequest.java | 22 +++++++ .../model/videocall/UpdateCallResponse.java | 22 +++++++ .../model/videocall/VideoCallRequest.java | 22 +++++++ .../VideoCallParameterRepository.java | 22 +++++++ .../service/videocall/VideoCallService.java | 24 +++++++- .../videocall/VideoCallServiceImpl.java | 57 +++++++++---------- 11 files changed, 208 insertions(+), 38 deletions(-) diff --git a/src/main/environment/common_ci.properties b/src/main/environment/common_ci.properties index 0184b32f..6997035e 100644 --- a/src/main/environment/common_ci.properties +++ b/src/main/environment/common_ci.properties @@ -189,8 +189,8 @@ captcha.enable-captcha=@env.ENABLE_CAPTCHA@ cors.allowed-origins=@env.CORS_ALLOWED_ORIGINS@ -video-call-url=@env.VIDEO_CALL_URL@ -jibri.output.path=@env.JIBRI_OUTPUT_PATH@ +# Jitsi configuration +videocall.url=@env.VIDEO_CALL_URL@ video.recording.path=@env.VIDEO_RECORDING_PATH@ platform.feedback.ratelimit.enabled=@env.PLATFORM_FEEDBACK_RATELIMIT_ENABLED@ diff --git a/src/main/environment/common_example.properties b/src/main/environment/common_example.properties index aca73ddb..f55f142b 100644 --- a/src/main/environment/common_example.properties +++ b/src/main/environment/common_example.properties @@ -198,8 +198,8 @@ grievanceAllocationRetryConfiguration=3 logging.path=logs/ logging.file.name=logs/common-api.log -video-call-url=https://vc.piramalswasthya.org/? -jibri.output.path=/srv/jibri/recordings +# Jitsi configuration +videocall.url=https://vc.piramalswasthya.org/? video.recording.path=/srv/recordings captcha.secret-key= diff --git a/src/main/java/com/iemr/common/controller/videocall/VideoCallController.java b/src/main/java/com/iemr/common/controller/videocall/VideoCallController.java index 8eb2a3ad..fdbd9575 100644 --- a/src/main/java/com/iemr/common/controller/videocall/VideoCallController.java +++ b/src/main/java/com/iemr/common/controller/videocall/VideoCallController.java @@ -1,3 +1,25 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ + package com.iemr.common.controller.videocall; import java.util.HashMap; @@ -10,7 +32,6 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -19,7 +40,6 @@ import com.iemr.common.model.videocall.VideoCallRequest; import com.iemr.common.service.videocall.VideoCallService; import com.iemr.common.utils.response.OutputResponse; -import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.servlet.http.HttpServletRequest; diff --git a/src/main/java/com/iemr/common/data/videocall/VideoCallParameters.java b/src/main/java/com/iemr/common/data/videocall/VideoCallParameters.java index c9df2d87..48a7b8ae 100644 --- a/src/main/java/com/iemr/common/data/videocall/VideoCallParameters.java +++ b/src/main/java/com/iemr/common/data/videocall/VideoCallParameters.java @@ -1,3 +1,25 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ + package com.iemr.common.data.videocall; import java.sql.Timestamp; diff --git a/src/main/java/com/iemr/common/mapper/videocall/VideoCallMapper.java b/src/main/java/com/iemr/common/mapper/videocall/VideoCallMapper.java index 521d5921..7e9a8f12 100644 --- a/src/main/java/com/iemr/common/mapper/videocall/VideoCallMapper.java +++ b/src/main/java/com/iemr/common/mapper/videocall/VideoCallMapper.java @@ -1,8 +1,29 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ + package com.iemr.common.mapper.videocall; import java.util.List; import org.mapstruct.Mapper; -import org.mapstruct.factory.Mappers; import org.mapstruct.IterableMapping; import org.mapstruct.factory.Mappers; diff --git a/src/main/java/com/iemr/common/model/videocall/UpdateCallRequest.java b/src/main/java/com/iemr/common/model/videocall/UpdateCallRequest.java index 343198b3..681d4b63 100644 --- a/src/main/java/com/iemr/common/model/videocall/UpdateCallRequest.java +++ b/src/main/java/com/iemr/common/model/videocall/UpdateCallRequest.java @@ -1,3 +1,25 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ + package com.iemr.common.model.videocall; import lombok.Data; diff --git a/src/main/java/com/iemr/common/model/videocall/UpdateCallResponse.java b/src/main/java/com/iemr/common/model/videocall/UpdateCallResponse.java index f01f46f5..44c01215 100644 --- a/src/main/java/com/iemr/common/model/videocall/UpdateCallResponse.java +++ b/src/main/java/com/iemr/common/model/videocall/UpdateCallResponse.java @@ -1,3 +1,25 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ + package com.iemr.common.model.videocall; import java.sql.Timestamp; diff --git a/src/main/java/com/iemr/common/model/videocall/VideoCallRequest.java b/src/main/java/com/iemr/common/model/videocall/VideoCallRequest.java index d8a61eee..64abc044 100644 --- a/src/main/java/com/iemr/common/model/videocall/VideoCallRequest.java +++ b/src/main/java/com/iemr/common/model/videocall/VideoCallRequest.java @@ -1,3 +1,25 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ + package com.iemr.common.model.videocall; import com.fasterxml.jackson.annotation.JsonFormat; diff --git a/src/main/java/com/iemr/common/repository/videocall/VideoCallParameterRepository.java b/src/main/java/com/iemr/common/repository/videocall/VideoCallParameterRepository.java index 251b877a..99798da2 100644 --- a/src/main/java/com/iemr/common/repository/videocall/VideoCallParameterRepository.java +++ b/src/main/java/com/iemr/common/repository/videocall/VideoCallParameterRepository.java @@ -1,3 +1,25 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ + package com.iemr.common.repository.videocall; import java.util.List; diff --git a/src/main/java/com/iemr/common/service/videocall/VideoCallService.java b/src/main/java/com/iemr/common/service/videocall/VideoCallService.java index 9322050b..dd457688 100644 --- a/src/main/java/com/iemr/common/service/videocall/VideoCallService.java +++ b/src/main/java/com/iemr/common/service/videocall/VideoCallService.java @@ -1,5 +1,27 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ + package com.iemr.common.service.videocall; -import com.iemr.common.utils.response.OutputResponse; + import com.iemr.common.model.videocall.UpdateCallRequest; import com.iemr.common.model.videocall.VideoCallRequest; diff --git a/src/main/java/com/iemr/common/service/videocall/VideoCallServiceImpl.java b/src/main/java/com/iemr/common/service/videocall/VideoCallServiceImpl.java index 9db1a771..8b61d525 100644 --- a/src/main/java/com/iemr/common/service/videocall/VideoCallServiceImpl.java +++ b/src/main/java/com/iemr/common/service/videocall/VideoCallServiceImpl.java @@ -1,3 +1,25 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ + package com.iemr.common.service.videocall; import org.apache.commons.lang.RandomStringUtils; @@ -36,14 +58,14 @@ public class VideoCallServiceImpl implements VideoCallService { private String meetingLink; private boolean isLinkSent = false; - private String consultationStatus = "Not Initiated"; - @Value("${video-call-url}") + @Value("${videocall.url}") private String jitsiLink; public VideoCallServiceImpl() { - // this.jitsiLink = ConfigProperties.getPropertyByName("video-call-url"); - // logger.info("Jitsi Link fetched: " + this.jitsiLink); + // Default constructor + this.meetingLink = null; + this.isLinkSent = false; } @Override @@ -99,10 +121,7 @@ public String updateCallStatus(UpdateCallRequest callRequest) throws Exception { if (updateCount > 0) { videoCall.setLinkUsed(true); videoCallRepository.save(videoCall); - - // if ("Completed".equalsIgnoreCase(requestEntity.getCallStatus())) { - // saveRecordingFile(videoCall.getMeetingLink()); - // } + } else { throw new Exception("Failed to update the call status"); } @@ -110,28 +129,6 @@ public String updateCallStatus(UpdateCallRequest callRequest) throws Exception { return OutputMapper.gsonWithoutExposeRestriction() .toJson(videoCallMapper.videoCallToResponse(videoCall)); } -private void saveRecordingFile(String meetingLink) { - try { - // Configurable Jibri recording location - String jibriOutputDir = ConfigProperties.getPropertyByName("jibri.output.path"); // e.g., /srv/jibri/recordings - String saveDir = ConfigProperties.getPropertyByName("video.recording.path"); // e.g., /srv/recordings - - File jibriDir = new File(jibriOutputDir); - File[] matchingFiles = jibriDir.listFiles((dir, name) -> name.contains(meetingLink) && name.endsWith(".mp4")); - - if (matchingFiles != null && matchingFiles.length > 0) { - File recording = matchingFiles[0]; - Path targetPath = Paths.get(saveDir, meetingLink + ".mp4"); - - Files.copy(recording.toPath(), targetPath, StandardCopyOption.REPLACE_EXISTING); - logger.info("Recording file saved: " + targetPath); - } else { - logger.warn("No matching recording file found for meeting: " + meetingLink); - } - } catch (IOException e) { - logger.error("Error saving recording file: ", e); - } -} } From 114dee96205521604638b7589615bb3f22d3fd80 Mon Sep 17 00:00:00 2001 From: vanitha1822 Date: Tue, 24 Mar 2026 14:43:58 +0530 Subject: [PATCH 36/58] fix: pom version update --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 11ad9f37..7e7b29d8 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.iemr.common-API common-api - 3.6.0 + 3.7.0 war Common-API From 8e9a650515cba30bdd1ea0276bd102a22d65acc9 Mon Sep 17 00:00:00 2001 From: vanitha1822 Date: Mon, 30 Mar 2026 12:15:31 +0530 Subject: [PATCH 37/58] fix: add cti-server-ip --- src/main/java/com/iemr/common/service/cti/CTIServiceImpl.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/com/iemr/common/service/cti/CTIServiceImpl.java b/src/main/java/com/iemr/common/service/cti/CTIServiceImpl.java index 81d8953a..fccf54f5 100644 --- a/src/main/java/com/iemr/common/service/cti/CTIServiceImpl.java +++ b/src/main/java/com/iemr/common/service/cti/CTIServiceImpl.java @@ -26,6 +26,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; @@ -96,6 +97,9 @@ public class CTIServiceImpl implements CTIService { @Autowired private IEMRCalltypeRepositoryImplCustom iemrCalltypeRepositoryImplCustom; + @Value("${cti-server-ip}") + private String ctiServerIp; + public CTIServiceImpl() { if (httpUtils == null) { httpUtils = new HttpUtils(); From dfa00ac2906ceaddf0ad1394f6530d960a3c7169 Mon Sep 17 00:00:00 2001 From: vanitha1822 Date: Mon, 30 Mar 2026 13:24:24 +0530 Subject: [PATCH 38/58] fix: comment unwanted code --- .../common/service/cti/CTIServiceImpl.java | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/iemr/common/service/cti/CTIServiceImpl.java b/src/main/java/com/iemr/common/service/cti/CTIServiceImpl.java index fccf54f5..7e5de8e9 100644 --- a/src/main/java/com/iemr/common/service/cti/CTIServiceImpl.java +++ b/src/main/java/com/iemr/common/service/cti/CTIServiceImpl.java @@ -98,7 +98,7 @@ public class CTIServiceImpl implements CTIService { private IEMRCalltypeRepositoryImplCustom iemrCalltypeRepositoryImplCustom; @Value("${cti-server-ip}") - private String ctiServerIp; + private String serverURL; public CTIServiceImpl() { if (httpUtils == null) { @@ -118,7 +118,7 @@ public OutputResponse addUpdateAgentSkills(String request, String ipAddress) thr ObjectMapper objectMapper = new ObjectMapper(); logger.debug("addUpdateAgentSkills input is " + request); String ctiURI = ConfigProperties.getPropertyByName("add-update-agent-skills-URL"); - String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); + // String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); AgentSkills agentSkills = objectMapper.readValue(request, AgentSkills.class); String agentID = (agentSkills.getAgentID() != null) ? agentSkills.getAgentID() : ""; @@ -150,7 +150,7 @@ public OutputResponse getCampaignSkills(String request, String ipAddress) throws OutputResponse output = new OutputResponse(); ObjectMapper objectMapper = new ObjectMapper(); String ctiURI = ConfigProperties.getPropertyByName("get-campaign-skills-URL"); - String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); + // String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); CampaignSkills agentState = objectMapper.readValue(request, CampaignSkills.class); ctiURI = ctiURI.replace("CTI_SERVER", serverURL); ctiURI = ctiURI.replace("CAMPAIGN_NAME", @@ -177,7 +177,7 @@ public OutputResponse getAgentState(String request, String ipAddress) throws IEM OutputResponse output = new OutputResponse(); ObjectMapper objectMapper = new ObjectMapper(); String ctiURI = ConfigProperties.getPropertyByName("get-agent-status-URL"); - String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); + // String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); AgentState agentState = objectMapper.readValue(request, AgentState.class); String agentID = (agentState.getAgent_id() != null) ? agentState.getAgent_id() : ""; @@ -207,7 +207,7 @@ public OutputResponse getAgentCallStats(String request, String ipAddress) throws OutputResponse output = new OutputResponse(); ObjectMapper objectMapper = new ObjectMapper(); String ctiURI = ConfigProperties.getPropertyByName("get-agent-call-stats-URL"); - String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); + // String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); AgentCallStats agentState = objectMapper.readValue(request, AgentCallStats.class); String agentID = (agentState.getAgentID() != null) ? agentState.getAgentID() : ""; @@ -239,7 +239,7 @@ public OutputResponse getCampaignNames(String request, String ipAddress) throws OutputResponse output = new OutputResponse(); ObjectMapper objectMapper = new ObjectMapper(); String ctiURI = ConfigProperties.getPropertyByName("get-campaign-name-URL"); - String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); + // String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); CampaignNames agentState = objectMapper.readValue(request, CampaignNames.class); ctiURI = ctiURI.replace("CTI_SERVER", serverURL); ctiURI = ctiURI.replace("SEARCH_KEY", (agentState.getServiceName() != null) ? agentState.getServiceName() : ""); @@ -266,7 +266,7 @@ public OutputResponse doAgentLogin(String request, String ipAddress) throws IEMR OutputResponse output = new OutputResponse(); ObjectMapper objectMapper = new ObjectMapper(); String ctiURI = ConfigProperties.getPropertyByName("do-agent-login-URL"); - String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); + // String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); AgentState agentState = objectMapper.readValue(request, AgentState.class); String agentID = (agentState.getAgent_id() != null) ? agentState.getAgent_id() : ""; @@ -296,7 +296,7 @@ public OutputResponse getLoginKey(String request, String ipAddress) throws IEMRE OutputResponse output = new OutputResponse(); ObjectMapper objectMapper = new ObjectMapper(); String ctiURI = ConfigProperties.getPropertyByName("get-login-key-URL"); - String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); + // String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); AgentLoginKey agentState = objectMapper.readValue(request, AgentLoginKey.class); String decryptPassword = null; @@ -329,7 +329,7 @@ public OutputResponse agentLogout(String request, String ipAddress) throws IEMRE OutputResponse output = new OutputResponse(); ObjectMapper objectMapper = new ObjectMapper(); String ctiURI = ConfigProperties.getPropertyByName("do-agent-logout-URL"); - String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); + // String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); AgentState agentState = objectMapper.readValue(request, AgentState.class); String agentID = (agentState.getAgent_id() != null) ? agentState.getAgent_id() : ""; @@ -361,7 +361,7 @@ public OutputResponse getOnlineAgents(String request, String ipAddress) throws I OutputResponse output = new OutputResponse(); ObjectMapper objectMapper = new ObjectMapper(); String ctiURI = ConfigProperties.getPropertyByName("do-online-agent-URL"); - String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); + // String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); AgentState agentState = objectMapper.readValue(request, AgentState.class); String agentID = (agentState.getAgent_id() != null) ? agentState.getAgent_id() : ""; @@ -392,7 +392,7 @@ public OutputResponse callBeneficiary(String request, String ipAddress) throws I OutputResponse output = new OutputResponse(); ObjectMapper objectMapper = new ObjectMapper(); String ctiURI = ConfigProperties.getPropertyByName("call-beneficiary-URL"); - String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); + // String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); CallBeneficiary agentState = objectMapper.readValue(request, CallBeneficiary.class); String agentID = (agentState.getAgent_id() != null) ? agentState.getAgent_id() : ""; @@ -433,7 +433,7 @@ public OutputResponse addUpdateUserData(String request, String ipAddress) throws * SESSION_TIMEOUT&designation=DESIGNATION&resFormat=3 */ - String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); + // String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); CTIUser ctiUser = objectMapper.readValue(request, CTIUser.class); ctiURI = ctiURI.replace("CTI_SERVER", serverURL); ctiURI = ctiURI.replace("USERNAME", (ctiUser.getUsername() != null) ? ctiUser.getUsername() : ""); @@ -465,7 +465,7 @@ public OutputResponse getTransferCampaigns(String request, String ipAddress) thr OutputResponse output = new OutputResponse(); ObjectMapper objectMapper = new ObjectMapper(); String ctiURI = ConfigProperties.getPropertyByName("fetch-transferrable-campaigns-URL"); - String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); + // String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); CTICampaigns agentState = objectMapper.readValue(request, CTICampaigns.class); String agentID = (agentState.getAgent_id() != null) ? agentState.getAgent_id() : ""; @@ -497,7 +497,7 @@ public OutputResponse getCampaignRoles(String request, String remoteAddr) throws OutputResponse output = new OutputResponse(); ObjectMapper objectMapper = new ObjectMapper(); String ctiURI = ConfigProperties.getPropertyByName("get-campaign-roles-URL"); - String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); + // String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); CampaignRole campaign = objectMapper.readValue(request, CampaignRole.class); ctiURI = ctiURI.replace("CTI_SERVER", serverURL); ctiURI = ctiURI.replace("CAMPAIGN_NAME", (campaign.getCampaign() != null) ? campaign.getCampaign() : ""); @@ -529,7 +529,7 @@ public OutputResponse setCallDisposition(String request, String remoteAddr) thro String agentIP = !agentIPResp.equals(DEFAULT_IP) ? agentIPResp : remoteAddr; String ctiURI = ConfigProperties.getPropertyByName("update-call-disposition-URL"); - String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); + // String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); ctiURI = ctiURI.replace("CTI_SERVER", serverURL); ctiURI = ctiURI.replace("AGENT_ID", agentID); ctiURI = ctiURI.replace("AGENT_IP", agentIP); @@ -557,7 +557,7 @@ public OutputResponse createVoiceFile(String request, String remoteAddr) throws ObjectMapper objectMapper = new ObjectMapper(); CTIVoiceFile disposition = objectMapper.readValue(request, CTIVoiceFile.class); String ctiURI = ConfigProperties.getPropertyByName("mix-voice-file-URL"); - String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); + // String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); ctiURI = ctiURI.replace("CTI_SERVER", serverURL); ctiURI = ctiURI.replace("AGENT_ID", (disposition.getAgent_id() != null) ? disposition.getAgent_id() : ""); // ctiURI = ctiURI.replace("AGENT_IP", remoteAddr); @@ -582,7 +582,7 @@ public OutputResponse getVoiceFile(String request, String remoteAddr) throws IEM ObjectMapper objectMapper = new ObjectMapper(); CTIVoiceFile disposition = objectMapper.readValue(request, CTIVoiceFile.class); String ctiURI = ConfigProperties.getPropertyByName("get-voice-file-URL"); - String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); + // String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); ctiURI = ctiURI.replace("CTI_SERVER", serverURL); ctiURI = ctiURI.replace("AGENT_ID", (disposition.getAgent_id() != null) ? disposition.getAgent_id() : ""); // ctiURI = ctiURI.replace("AGENT_IP", remoteAddr); @@ -608,7 +608,7 @@ public OutputResponse getVoiceFileNew(String request, String remoteAddr) throws OutputResponse output = new OutputResponse(); CTIVoiceFile disposition = InputMapper.gson().fromJson(request, CTIVoiceFile.class); String ctiURI = ConfigProperties.getPropertyByName("get-voice-file-URL-New"); - String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); + // String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); ctiURI = ctiURI.replace("CTI_SERVER", serverURL); ctiURI = ctiURI.replace("AGENT_ID", (disposition.getAgent_id() != null) ? disposition.getAgent_id() : ""); // ctiURI = ctiURI.replace("AGENT_IP", remoteAddr); @@ -975,7 +975,7 @@ public OutputResponse addAutoDialNumbers(String request, String ipAddress) throw OutputResponse result = new OutputResponse(); logger.debug("addUpdateAgentSkills input is " + request); String ctiURI = ConfigProperties.getPropertyByName("add-auto-dail-numbers-URL"); - String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); + // String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); ObjectMapper objectMapper = new ObjectMapper(); AutoPreviewDial[] autoPreviewDialArray = objectMapper.readValue(request, AutoPreviewDial[].class); @@ -1016,7 +1016,7 @@ public OutputResponse setAutoDialNumbers(String request, String ipAddress) throw OutputResponse result = new OutputResponse(); logger.debug("setAutoDialNumbers input is " + request); String ctiURI = ConfigProperties.getPropertyByName("set-auto-dail-numbers-URL"); - String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); + // String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); ObjectMapper objectMapper = new ObjectMapper(); AutoPreviewDial autoPreviewDial = objectMapper.readValue(request, AutoPreviewDial.class); @@ -1059,7 +1059,7 @@ public OutputResponse getIVRSPathDetails(String request, String remoteAddress) t OutputResponse result = new OutputResponse(); logger.debug("getZoneDetails input is " + request); String ctiURI = ConfigProperties.getPropertyByName("agent-ivrs-path-URL"); - String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); + // String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); ObjectMapper objectMapper = new ObjectMapper(); AgentState zoneData = objectMapper.readValue(request, AgentState.class); From 9f379fb14a8c83231d19fc429e35a777f977cf11 Mon Sep 17 00:00:00 2001 From: vanitha1822 Date: Mon, 30 Mar 2026 14:31:34 +0530 Subject: [PATCH 39/58] fix: update videocall url property --- src/main/environment/common_docker.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/environment/common_docker.properties b/src/main/environment/common_docker.properties index a81ea62e..12408a9a 100644 --- a/src/main/environment/common_docker.properties +++ b/src/main/environment/common_docker.properties @@ -192,7 +192,7 @@ firebase.enabled=${FIREBASE_ENABLE} firebase.credential-file=${FIREBASE_CREDENTIAL} -video-call-url=${VIDEO_CALL_URL} +videocall.url=${VIDEO_CALL_URL} jibri.output.path={JIBRI_OUTPUT_PATH} video.recording.path={VIDEO_RECORDING_PATH} From f0a1177a3c630a129a6d1f7075aeec50e5db3d13 Mon Sep 17 00:00:00 2001 From: vanitha1822 Date: Mon, 30 Mar 2026 15:53:01 +0530 Subject: [PATCH 40/58] fix: update cti-server-ip --- .../service/nhm_dashboard/NHM_DashboardServiceImpl.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/iemr/common/service/nhm_dashboard/NHM_DashboardServiceImpl.java b/src/main/java/com/iemr/common/service/nhm_dashboard/NHM_DashboardServiceImpl.java index 4448cc07..d7afd579 100644 --- a/src/main/java/com/iemr/common/service/nhm_dashboard/NHM_DashboardServiceImpl.java +++ b/src/main/java/com/iemr/common/service/nhm_dashboard/NHM_DashboardServiceImpl.java @@ -32,6 +32,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import com.google.gson.Gson; @@ -61,6 +62,9 @@ public class NHM_DashboardServiceImpl implements NHM_DashboardService { @Autowired private DetailedCallReportRepo detailedCallReportRepo; + @Value("${cti-server-ip}") + private String serverURL; + public String pushAbandonCalls(AbandonCallSummary abandonCallSummary) throws Exception { logger.info("NHM_abandon call push API request : " + abandonCallSummary.toString()); @@ -227,7 +231,7 @@ public List callAgentSummaryReportCTI_API() throws IEMRExcep // throw new IEMRException("Please pass correct period for schedular - in hours"); String ctiURI = ConfigProperties.getPropertyByName("get-agent-summary-report-URL"); - String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); + // String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); ctiURI = ctiURI.replace("CTI_SERVER", serverURL); ctiURI = ctiURI.replace("END_DATE", endDate); ctiURI = ctiURI.replace("START_DATE", fromDate); @@ -272,7 +276,7 @@ public List callDetailedCallReportCTI_API() throws IEMRExcep // throw new IEMRException("Please pass correct period for schedular - in hours"); String ctiURI = ConfigProperties.getPropertyByName("get-details-call-report-URL"); - String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); + // String serverURL = ConfigProperties.getPropertyByName("cti-server-ip"); ctiURI = ctiURI.replace("CTI_SERVER", serverURL); ctiURI = ctiURI.replace("END_DATE", endDate); ctiURI = ctiURI.replace("START_DATE", fromDate); From eec7cfefbadbbb7c6c46b23964300695b3088199 Mon Sep 17 00:00:00 2001 From: SnehaRH Date: Mon, 30 Mar 2026 19:05:18 +0530 Subject: [PATCH 41/58] docs: add CLAUDE.md for Claude Code guidance Co-Authored-By: Claude Opus 4.6 (1M context) --- CLAUDE.md | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..913e1435 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,98 @@ +# CLAUDE.md - Common-API + +## Project Overview + +Common-API is the gateway microservice for the AMRIT healthcare platform. It provides shared APIs consumed by all frontend UIs including authentication, beneficiary registration, call handling, location masters, notifications, feedback, reporting, and integrations with external systems (c-Zentrix CTI, Everwell, eAusadha, eSanjeevani, ABDM, Firebase, Honeywell POCT devices). + +## Tech Stack + +- Java 17 +- Spring Boot 3.2.2 +- Spring Data JPA / Hibernate +- MySQL 8.0 +- Redis (session management, caching) +- MongoDB (optional, for specific integrations) +- Maven (build tool) +- Swagger/OpenAPI (API documentation) +- Lombok, MapStruct +- CryptoJS-compatible AES encryption +- Firebase Admin SDK +- WAR packaging (deploys to Wildfly) + +## Build and Run + +```bash +# Build +mvn clean install -DENV_VAR=local + +# Run locally (start Redis first) +mvn spring-boot:run -DENV_VAR=local + +# Package WAR +mvn -B package --file pom.xml -P # profiles: dev, local, test, ci, uat + +# Run tests +mvn test +``` + +### Configuration + +- Copy `src/main/environment/common_example.properties` to `common_local.properties` and edit. +- Environment selected via `-DENV_VAR=`. +- Swagger UI: `http://localhost:8083/swagger-ui.html` + +## Package Structure + +Base package: `com.iemr.common` + +| Layer | Package | Description | +|-------|---------|-------------| +| Controllers | `controller.*` | REST endpoints (40+ sub-packages) | +| Services | `service.*` | Business logic | +| Repositories | `repository.*`, `repo.*` | JPA repositories | +| Entities | `data.*` | JPA entity classes | +| DTOs | `model.*` | Transfer objects | +| Mappers | `mapper.*` | Object mapping | +| Config | `config.*` | Swagger, encryption, Firebase, Quartz, prototypes | +| Constants | `constant` | Application constants | +| Utils | `utils.*` | Redis, HTTP, session, validation, exception | + +## Key Functional Domains + +- **Authentication/Authorization**: `controller.users` - login, session, user management +- **Beneficiary Registration**: `controller.beneficiary` - create, search, update beneficiaries +- **Call Handling**: `controller.callhandling` - CTI integration, call lifecycle +- **Feedback/Grievance**: `controller.feedback`, `controller.grievance` - feedback and complaint management +- **Location**: `controller.location` - state, district, block, village masters +- **Notifications**: `controller.notification` - alerts, SMS, email, Firebase push +- **Reporting**: `controller.report`, `controller.secondaryReport` - CRM reports +- **Helpline 104**: `controller.helpline104history` - medical advice history +- **COVID**: `controller.covid` - vaccination status +- **CTI Integration**: `controller.cti` - c-Zentrix computer telephony +- **External Integrations**: `controller.eausadha`, `controller.esanjeevani`, `controller.everwell`, `controller.honeywell`, `controller.brd`, `controller.carestream` +- **ABDM**: `controller.abdmfacility` - Ayushman Bharat Digital Mission +- **KM File Management**: `controller.kmfilemanager` - OpenKM document management +- **OTP/SMS**: `controller.otp`, `controller.sms` (via SMS gateway) +- **Scheduling**: `controller.questionconfig`, `controller.scheme` +- **Door-to-Door App**: `controller.door_to_door_app` - field worker support +- **NHM Dashboard**: `controller.nhmdashboard` - National Health Mission integration + +## Architecture Notes + +- Entry point: `CommonMain.java` (main class in `utils` package) +- Acts as the API gateway; all frontend UIs authenticate through Common-API +- Session management via Redis with 27-minute timeout +- HTTP interceptors attach `Authorization` and `ServerAuthorization` headers +- Status code `5002` signals session expiration to frontends +- AES + PBKDF2 encryption for password handling (`config.encryption`) +- Firebase integration for push notifications (`config.firebase`) +- Quartz scheduler for background jobs (`config.quartz`) +- Extensive test coverage with unit tests under `src/test/` + +## CI/CD + +- GitHub Actions: `package.yml`, `build-on-pull-request.yml`, `sast.yml`, `commit-lint.yml`, `codeql.yml` +- Conventional Commits enforced via Husky + commitlint +- Checkstyle configuration in `checkstyle.xml` +- JaCoCo for code coverage, SonarQube integration configured +- Dockerfile for containerized deployment From 4b5d2500e1756350cf1712e2ee134c5243fdc03c Mon Sep 17 00:00:00 2001 From: vanitha1822 Date: Tue, 31 Mar 2026 16:26:26 +0530 Subject: [PATCH 42/58] fix: KM issue --- .../utils/km/openkm/OpenKMServiceImpl.java | 53 ++++++++++--------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/src/main/java/com/iemr/common/utils/km/openkm/OpenKMServiceImpl.java b/src/main/java/com/iemr/common/utils/km/openkm/OpenKMServiceImpl.java index 2be04cfc..acb47e6f 100644 --- a/src/main/java/com/iemr/common/utils/km/openkm/OpenKMServiceImpl.java +++ b/src/main/java/com/iemr/common/utils/km/openkm/OpenKMServiceImpl.java @@ -51,38 +51,43 @@ import org.glassfish.jersey.client.JerseyClientBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +@Service public class OpenKMServiceImpl implements KMService { - // private ConfigProperties configProperties; - // - // @Autowired - // public void setConfigProperties(ConfigProperties configProperties) - // { - // this.configProperties = configProperties; - // } + private final Logger logger = LoggerFactory.getLogger(this.getClass().getName()); - private static String url; - private static String username; - private static String password; - private static String kmRootPath; - private static String guestUser; - private static String guestPassword; + + @Value("${km-base-url}") + private String url; + + @Value("${km-username}") + private String username; + + @Value("${km-password}") + private String password; + + @Value("${km-root-path}") + private String kmRootPath; + + @Value("${km-guest-user}") + private String guestUser; + + @Value("${km-guest-password}") + private String guestPassword; public OpenKMServiceImpl() { } - private static OKMWebservices connector = null; + private OKMWebservices connector; + @PostConstruct public void init() { - if (connector == null) { - url = ConfigProperties.getPropertyByName("km-base-url"); - username = ConfigProperties.getPropertyByName("km-username"); - password = ConfigProperties.getPropertyByName("km-password"); - kmRootPath = ConfigProperties.getPropertyByName("km-root-path"); - guestUser = ConfigProperties.getPropertyByName("km-guest-user"); - guestPassword = ConfigProperties.getPropertyByName("km-guest-password"); - connector = OpenKMConnector.initialize(url, username, password); - - } + logger.info("KM URL: " + url); + logger.info("KM Username: " + username); + + connector = OpenKMConnector.initialize(url, username, password); + } @Override From e1c84e8d24d8ef918afc2e419e4e00c71c0574fd Mon Sep 17 00:00:00 2001 From: vanitha1822 Date: Tue, 31 Mar 2026 16:29:47 +0530 Subject: [PATCH 43/58] fix: KM issue --- .../java/com/iemr/common/utils/km/openkm/OpenKMServiceImpl.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/iemr/common/utils/km/openkm/OpenKMServiceImpl.java b/src/main/java/com/iemr/common/utils/km/openkm/OpenKMServiceImpl.java index acb47e6f..29c2717a 100644 --- a/src/main/java/com/iemr/common/utils/km/openkm/OpenKMServiceImpl.java +++ b/src/main/java/com/iemr/common/utils/km/openkm/OpenKMServiceImpl.java @@ -46,6 +46,8 @@ import com.openkm.sdk4j.exception.VirusDetectedException; import com.openkm.sdk4j.exception.WebserviceException; +import jakarta.annotation.PostConstruct; + import org.glassfish.jersey.client.ClientConfig; import org.glassfish.jersey.client.ClientProperties; import org.glassfish.jersey.client.JerseyClientBuilder; From cd61d46b0137696945f1c2bbea566eb8c313bdcd Mon Sep 17 00:00:00 2001 From: vanitha1822 Date: Tue, 31 Mar 2026 16:45:34 +0530 Subject: [PATCH 44/58] fix: remove unwanted imports --- .../iemr/common/utils/km/openkm/OpenKMServiceImpl.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/iemr/common/utils/km/openkm/OpenKMServiceImpl.java b/src/main/java/com/iemr/common/utils/km/openkm/OpenKMServiceImpl.java index 29c2717a..d004243d 100644 --- a/src/main/java/com/iemr/common/utils/km/openkm/OpenKMServiceImpl.java +++ b/src/main/java/com/iemr/common/utils/km/openkm/OpenKMServiceImpl.java @@ -48,14 +48,13 @@ import jakarta.annotation.PostConstruct; -import org.glassfish.jersey.client.ClientConfig; -import org.glassfish.jersey.client.ClientProperties; -import org.glassfish.jersey.client.JerseyClientBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; -@Service +// import org.springframework.context.annotation.Primary; +// import org.springframework.stereotype.Service; +// @Service +// @Primary public class OpenKMServiceImpl implements KMService { private final Logger logger = LoggerFactory.getLogger(this.getClass().getName()); From 92d49ad2515d76f0465fd706f58300360e8572bf Mon Sep 17 00:00:00 2001 From: vanitha1822 Date: Tue, 31 Mar 2026 16:50:42 +0530 Subject: [PATCH 45/58] fix: conflicts --- .../java/com/iemr/common/utils/IEMRApplBeans.java | 12 ++++++------ .../common/utils/km/openkm/OpenKMServiceImpl.java | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/iemr/common/utils/IEMRApplBeans.java b/src/main/java/com/iemr/common/utils/IEMRApplBeans.java index 92d3c339..7747f6ee 100644 --- a/src/main/java/com/iemr/common/utils/IEMRApplBeans.java +++ b/src/main/java/com/iemr/common/utils/IEMRApplBeans.java @@ -40,12 +40,12 @@ @Configuration public class IEMRApplBeans { - @Bean - public KMService getOpenKMService() - { - KMService kmService = new OpenKMServiceImpl(); - return kmService; - } + // @Bean + // public KMService getOpenKMService() + // { + // KMService kmService = new OpenKMServiceImpl(); + // return kmService; + // } @Bean public Validator getVaidator() diff --git a/src/main/java/com/iemr/common/utils/km/openkm/OpenKMServiceImpl.java b/src/main/java/com/iemr/common/utils/km/openkm/OpenKMServiceImpl.java index d004243d..dd304a64 100644 --- a/src/main/java/com/iemr/common/utils/km/openkm/OpenKMServiceImpl.java +++ b/src/main/java/com/iemr/common/utils/km/openkm/OpenKMServiceImpl.java @@ -51,9 +51,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; -// import org.springframework.context.annotation.Primary; -// import org.springframework.stereotype.Service; -// @Service +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Service; +@Service // @Primary public class OpenKMServiceImpl implements KMService { From f1496fa481dedd3ebbca68fa3dbe03ec1345b91d Mon Sep 17 00:00:00 2001 From: vanitha1822 Date: Wed, 1 Apr 2026 10:19:43 +0530 Subject: [PATCH 46/58] fix: update the temp path --- .../service/kmfilemanager/KMFileManagerServiceImpl.java | 5 ++++- src/main/resources/application.properties | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/iemr/common/service/kmfilemanager/KMFileManagerServiceImpl.java b/src/main/java/com/iemr/common/service/kmfilemanager/KMFileManagerServiceImpl.java index 4297022a..b214a9ee 100644 --- a/src/main/java/com/iemr/common/service/kmfilemanager/KMFileManagerServiceImpl.java +++ b/src/main/java/com/iemr/common/service/kmfilemanager/KMFileManagerServiceImpl.java @@ -89,6 +89,9 @@ public void setSubCategoryRepository(SubCategoryRepository subCategoryRepository @Value("${allowed.file.extensions}") private String allowedFileExtensions; + @Value("${tempFilePath}") + private String tempFilePath; + @Override public String getKMFileLists(String request) throws Exception { ObjectMapper objectMapper = new ObjectMapper(); @@ -183,7 +186,7 @@ private ArrayList addKMFile(Iterable kmFileManager .replace("}", "").replace("[", "").replace("]", "").replace("|", "").replace("\\", "") .replace(":", "").replace(";", "").replace("-", "").replace("_", "").replace("+", "") .replace("=", "").replace("\"", "").replace("'", "")); - String tempFilePath = ConfigProperties.getPropertyByName("tempFilePath"); + // String tempFilePath = ConfigProperties.getPropertyByName("tempFilePath"); newFile = new FileOutputStream(tempFilePath + "/" + kmFileManager.getFileName()); newFile.write(Base64.getDecoder().decode(kmFileManager.getFileContent())); newFile.flush(); diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 18723465..8f8bbb4a 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -28,7 +28,7 @@ BeneficiarySmsTemplate=Beneficiary UPTSU SMS future-days=7 ## Path where the files would be stored before uploading to KM server -tempFilePath=c:/temp/ +tempFilePath=/tmp ## swaasa file path lungAssessmentPath=c:/swaasa_audio/ From 72f0c28a2b6c10f134e7470c5a14fd4ed047229e Mon Sep 17 00:00:00 2001 From: Vanitha S <116701245+vanitha1822@users.noreply.github.com> Date: Tue, 7 Apr 2026 15:27:06 +0530 Subject: [PATCH 47/58] 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. --- .../environment/common_example.properties | 2 + .../com/iemr/common/CommonApplication.java | 2 +- .../common/service/cti/CTIServiceImpl.java | 2 + .../service/feedback/FeedbackServiceImpl.java | 19 +++++-- .../notification/NotificationServiceImpl.java | 19 +++++-- .../service/scheme/SchemeServiceImpl.java | 24 +++++--- .../service/services/CommonServiceImpl.java | 25 +++++--- .../com/iemr/common/utils/IEMRApplBeans.java | 12 ++-- .../common/utils/JwtAuthenticationUtil.java | 4 -- .../utils/km/openkm/OpenKMServiceImpl.java | 57 ++++++++++--------- src/main/resources/application.properties | 16 +++--- 11 files changed, 113 insertions(+), 69 deletions(-) diff --git a/src/main/environment/common_example.properties b/src/main/environment/common_example.properties index aca73ddb..e2b2555c 100644 --- a/src/main/environment/common_example.properties +++ b/src/main/environment/common_example.properties @@ -25,6 +25,8 @@ km-root-path=/okm:personal/users/ km-guest-user=guest km-guest-password=guest +tempFilePath=/tmp + # CTI Config cti-server-ip=10.208.122.99 cti-logger_base_url=http://10.208.122.99/logger diff --git a/src/main/java/com/iemr/common/CommonApplication.java b/src/main/java/com/iemr/common/CommonApplication.java index dff5d809..45d61800 100644 --- a/src/main/java/com/iemr/common/CommonApplication.java +++ b/src/main/java/com/iemr/common/CommonApplication.java @@ -41,7 +41,7 @@ @SpringBootApplication @EnableScheduling -@EnableAsync +@EnableAsync(proxyTargetClass = true) public class CommonApplication extends SpringBootServletInitializer { @Bean diff --git a/src/main/java/com/iemr/common/service/cti/CTIServiceImpl.java b/src/main/java/com/iemr/common/service/cti/CTIServiceImpl.java index 81d8953a..3348befe 100644 --- a/src/main/java/com/iemr/common/service/cti/CTIServiceImpl.java +++ b/src/main/java/com/iemr/common/service/cti/CTIServiceImpl.java @@ -26,6 +26,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; @@ -90,6 +91,7 @@ public class CTIServiceImpl implements CTIService { @Autowired private BeneficiaryCallRepository beneficiaryCallRepository; + @Lazy @Autowired private CTIService ctiService; diff --git a/src/main/java/com/iemr/common/service/feedback/FeedbackServiceImpl.java b/src/main/java/com/iemr/common/service/feedback/FeedbackServiceImpl.java index b4c7782e..f94b6e9a 100644 --- a/src/main/java/com/iemr/common/service/feedback/FeedbackServiceImpl.java +++ b/src/main/java/com/iemr/common/service/feedback/FeedbackServiceImpl.java @@ -36,6 +36,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; @@ -104,6 +105,19 @@ public class FeedbackServiceImpl implements FeedbackService { private Logger logger = LoggerFactory.getLogger(FeedbackServiceImpl.class); // private ExecutorService executor = Executors.newCachedThreadPool(); + + @Value("${km-base-path}") + private String dmsPath; + + @Value("${km-guest-user}") + private String userName; + + @Value("${km-guest-password}") + private String userPassword; + + @Value("${km-base-protocol}") + private String dmsProtocol; + @Autowired private T_EpidemicOutbreakRepo t_EpidemicOutbreakRepo; @@ -736,10 +750,7 @@ private String getFilePath(KMFileManager kmFileManager) { String fileUIDAsURI = null; if (kmFileManager != null && kmFileManager.getFileUID() != null) { String fileUID = kmFileManager.getFileUID(); - String dmsPath = ConfigProperties.getPropertyByName("km-base-path"); - String dmsProtocol = ConfigProperties.getPropertyByName("km-base-protocol"); - String userName = ConfigProperties.getPropertyByName("km-guest-user"); - String userPassword = ConfigProperties.getPassword("km-guest-user"); + fileUIDAsURI = dmsProtocol + "://" + userName + ":" + userPassword + "@" + dmsPath + "/Download?uuid=" + fileUID; } diff --git a/src/main/java/com/iemr/common/service/notification/NotificationServiceImpl.java b/src/main/java/com/iemr/common/service/notification/NotificationServiceImpl.java index 2d8aaeb3..a4fda6f5 100644 --- a/src/main/java/com/iemr/common/service/notification/NotificationServiceImpl.java +++ b/src/main/java/com/iemr/common/service/notification/NotificationServiceImpl.java @@ -38,6 +38,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import com.fasterxml.jackson.core.JsonProcessingException; @@ -70,6 +71,19 @@ public class NotificationServiceImpl implements NotificationService private EmailService emailService; + + @Value("${km-base-path}") + private String dmsPath; + + @Value("${km-guest-user}") + private String userName; + + @Value("${km-guest-password}") + private String userPassword; + + @Value("${km-base-protocol}") + private String dmsProtocol; + @Autowired public void setEmailService(EmailService emailService) { @@ -415,10 +429,7 @@ private String getFilePath(KMFileManager kmFileManager) if (kmFileManager != null && kmFileManager.getFileUID() != null) { String fileUID = kmFileManager.getFileUID(); - String dmsPath = ConfigProperties.getPropertyByName("km-base-path"); - String dmsProtocol = ConfigProperties.getPropertyByName("km-base-protocol"); - String userName = ConfigProperties.getPropertyByName("km-guest-user"); - String userPassword = ConfigProperties.getPassword("km-guest-user"); + fileUIDAsURI = dmsProtocol + "://" + userName + ":" + userPassword + "@" + dmsPath + "/Download?uuid=" + fileUID; } diff --git a/src/main/java/com/iemr/common/service/scheme/SchemeServiceImpl.java b/src/main/java/com/iemr/common/service/scheme/SchemeServiceImpl.java index 44e1efaa..947c2c17 100644 --- a/src/main/java/com/iemr/common/service/scheme/SchemeServiceImpl.java +++ b/src/main/java/com/iemr/common/service/scheme/SchemeServiceImpl.java @@ -30,6 +30,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import com.fasterxml.jackson.databind.DeserializationFeature; @@ -59,6 +60,18 @@ public class SchemeServiceImpl implements SchemeService { private KMFileManagerService kmFileManagerService; + @Value("${km-api-base-protocol}") + private String dmsProtocol; + + @Value("${km-api-base-url}") + private String dmsPath; + + @Value("${km-guest-user}") + private String userName; + + @Value("${km-guest-password}") + private String userPassword; + @Autowired public void setKmFileManagerService(KMFileManagerService kmFileManagerService) { this.kmFileManagerService = kmFileManagerService; @@ -104,16 +117,13 @@ public String getFilePath(KMFileManager kmFileManager) { String fileUIDAsURI = null; if (kmFileManager != null && kmFileManager.getFileUID() != null) { String fileUID = kmFileManager.getFileUID(); - String dmsPath = ConfigProperties.getPropertyByName("km-base-path"); - String dmsProtocol = ConfigProperties.getPropertyByName("km-base-protocol"); - String userName = ConfigProperties.getPropertyByName("km-guest-user"); - String userPassword = ConfigProperties.getPassword("km-guest-user"); + fileUIDAsURI = dmsProtocol + "://" + userName + ":" + userPassword + "@" + dmsPath + "/Download?uuid=" + fileUID; } - // return fileUIDAsURI; - String message = kmFileManager.getFileUID() ; - return message; + return fileUIDAsURI; + // String message = kmFileManager.getFileUID() ; + // return message; } @Override diff --git a/src/main/java/com/iemr/common/service/services/CommonServiceImpl.java b/src/main/java/com/iemr/common/service/services/CommonServiceImpl.java index ff6f83e9..d8587a86 100644 --- a/src/main/java/com/iemr/common/service/services/CommonServiceImpl.java +++ b/src/main/java/com/iemr/common/service/services/CommonServiceImpl.java @@ -64,7 +64,19 @@ public class CommonServiceImpl implements CommonService { private Logger logger = LoggerFactory.getLogger(this.getClass().getSimpleName()); + + @Value("${km-base-path}") + private String dmsPath; + + @Value("${km-guest-user}") + private String userName; + @Value("${km-guest-password}") + private String userPassword; + + @Value("${km-base-protocol}") + private String dmsProtocol; + private static final String FILE_PATH = "filePath"; /** @@ -177,10 +189,6 @@ private String getFilePath(String fileUID) { String fileUIDAsURI = null; - String dmsPath = ConfigProperties.getPropertyByName("km-base-path"); - String dmsProtocol = ConfigProperties.getPropertyByName("km-base-protocol"); - String userName = ConfigProperties.getPropertyByName("km-guest-user"); - String userPassword = ConfigProperties.getPassword("km-guest-user"); fileUIDAsURI = dmsProtocol + "://" + userName + ":" + userPassword + "@" + dmsPath + "/Download?uuid=" + fileUID; @@ -233,12 +241,13 @@ public List getSubCategoryFilesWithURL(String request) throw SubCategoryDetails subCategory = subCategoriesList.get(index); if (subCategory.getSubCatFilePath() != null && subCategory.getSubCatFilePath().length() > 0) { String subCatFilePath = subCategory.getSubCatFilePath(); - String dmsPath = ConfigProperties.getPropertyByName("km-base-path"); - String dmsProtocol = ConfigProperties.getPropertyByName("km-base-protocol"); - String userName = ConfigProperties.getPropertyByName("km-guest-user"); - String userPassword = ConfigProperties.getPassword("km-guest-user"); String fileUIDAsURI = dmsProtocol + "://" + userName + ":" + userPassword + "@" + dmsPath + "/Download?uuid=" + subCategory.getSubCatFilePath(); + logger.info("file url="+fileUIDAsURI); + logger.info("file path="+subCategory.getSubCatFilePath()); + logger.info("dms Path="+dmsPath); + logger.info("subcatfilePath="+subCatFilePath); + subCategory.setSubCatFilePath(fileUIDAsURI); subCategoriesList.get(index).setFileManger(kmFileManagerRepository .getKMFileLists(subCategoryDetails.getProviderServiceMapID(), subCatFilePath)); diff --git a/src/main/java/com/iemr/common/utils/IEMRApplBeans.java b/src/main/java/com/iemr/common/utils/IEMRApplBeans.java index 92d3c339..7747f6ee 100644 --- a/src/main/java/com/iemr/common/utils/IEMRApplBeans.java +++ b/src/main/java/com/iemr/common/utils/IEMRApplBeans.java @@ -40,12 +40,12 @@ @Configuration public class IEMRApplBeans { - @Bean - public KMService getOpenKMService() - { - KMService kmService = new OpenKMServiceImpl(); - return kmService; - } + // @Bean + // public KMService getOpenKMService() + // { + // KMService kmService = new OpenKMServiceImpl(); + // return kmService; + // } @Bean public Validator getVaidator() diff --git a/src/main/java/com/iemr/common/utils/JwtAuthenticationUtil.java b/src/main/java/com/iemr/common/utils/JwtAuthenticationUtil.java index 1e9f589d..d9eb1ce2 100644 --- a/src/main/java/com/iemr/common/utils/JwtAuthenticationUtil.java +++ b/src/main/java/com/iemr/common/utils/JwtAuthenticationUtil.java @@ -13,7 +13,6 @@ import com.iemr.common.data.users.User; import com.iemr.common.repository.users.IEMRUserRepositoryCustom; -import com.iemr.common.service.users.IEMRAdminUserServiceImpl; import com.iemr.common.utils.exception.IEMRException; import io.jsonwebtoken.Claims; @@ -32,9 +31,6 @@ public class JwtAuthenticationUtil { private IEMRUserRepositoryCustom iEMRUserRepositoryCustom; private final Logger logger = LoggerFactory.getLogger(this.getClass().getName()); - @Autowired - private IEMRAdminUserServiceImpl iEMRAdminUserServiceImpl; - public JwtAuthenticationUtil(CookieUtil cookieUtil, JwtUtil jwtUtil) { this.cookieUtil = cookieUtil; this.jwtUtil = jwtUtil; diff --git a/src/main/java/com/iemr/common/utils/km/openkm/OpenKMServiceImpl.java b/src/main/java/com/iemr/common/utils/km/openkm/OpenKMServiceImpl.java index 2be04cfc..4a6e68ec 100644 --- a/src/main/java/com/iemr/common/utils/km/openkm/OpenKMServiceImpl.java +++ b/src/main/java/com/iemr/common/utils/km/openkm/OpenKMServiceImpl.java @@ -46,44 +46,47 @@ import com.openkm.sdk4j.exception.VirusDetectedException; import com.openkm.sdk4j.exception.WebserviceException; -import org.glassfish.jersey.client.ClientConfig; -import org.glassfish.jersey.client.ClientProperties; -import org.glassfish.jersey.client.JerseyClientBuilder; +import jakarta.annotation.PostConstruct; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Service; + +@Service public class OpenKMServiceImpl implements KMService { - // private ConfigProperties configProperties; - // - // @Autowired - // public void setConfigProperties(ConfigProperties configProperties) - // { - // this.configProperties = configProperties; - // } + private final Logger logger = LoggerFactory.getLogger(this.getClass().getName()); - private static String url; - private static String username; - private static String password; - private static String kmRootPath; - private static String guestUser; - private static String guestPassword; + @Value("${km-base-url}") + private String url; + + @Value("${km-username}") + private String username; + + @Value("${km-password}") + private String password; + + @Value("${km-root-path}") + private String kmRootPath; + + @Value("${km-guest-user}") + private String guestUser; + + @Value("${km-guest-password}") + private String guestPassword; public OpenKMServiceImpl() { } - private static OKMWebservices connector = null; + private OKMWebservices connector; + @PostConstruct public void init() { - if (connector == null) { - url = ConfigProperties.getPropertyByName("km-base-url"); - username = ConfigProperties.getPropertyByName("km-username"); - password = ConfigProperties.getPropertyByName("km-password"); - kmRootPath = ConfigProperties.getPropertyByName("km-root-path"); - guestUser = ConfigProperties.getPropertyByName("km-guest-user"); - guestPassword = ConfigProperties.getPropertyByName("km-guest-password"); - connector = OpenKMConnector.initialize(url, username, password); + logger.info("KM URL=",url); + connector = OpenKMConnector.initialize(url, username, password); - } - } + } @Override public String getDocumentRoot() { diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index a3de6772..c44efdf0 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -181,14 +181,14 @@ jwt.refresh.expiration=604800000 ## KM Configuration -km-base-protocol=http -km-username=okmAdmin -km-password=admin -km-base-url=http://localhost:8084/OpenKM -km-base-path=localhost:8084/OpenKM -km-root-path=/okm:personal/users/ -km-guest-user=guest -km-guest-password=guest +# km-base-protocol=http +# km-username=okmAdmin +# km-password=admin +# km-base-url=http://localhost:8084/OpenKM +# km-base-path=localhost:8084/OpenKM +# km-root-path=/okm:personal/users/ +# km-guest-user=guest +# km-guest-password=guest # CTI Config cti-server-ip=10.208.122.99 From 79b050da315571512b9cfa3b7e8b59e3e37315b3 Mon Sep 17 00:00:00 2001 From: Vishwanath Balkur <118195001+vishwab1@users.noreply.github.com> Date: Tue, 7 Apr 2026 21:14:18 +0530 Subject: [PATCH 48/58] Fix ConfigProperties to resolve env variable placeholders via Spring Environment (#390) Co-authored-by: Claude Opus 4.6 (1M context) --- .../common/utils/config/ConfigProperties.java | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/iemr/common/utils/config/ConfigProperties.java b/src/main/java/com/iemr/common/utils/config/ConfigProperties.java index 59b69b82..43a49364 100644 --- a/src/main/java/com/iemr/common/utils/config/ConfigProperties.java +++ b/src/main/java/com/iemr/common/utils/config/ConfigProperties.java @@ -144,11 +144,21 @@ public static String getPropertyByName(String propertyName) String result = null; try { - if (properties == null) + if (environment != null) { - initalizeProperties(); + result = environment.getProperty(propertyName); + } + if (result == null) + { + if (properties == null) + { + initalizeProperties(); + } + result = properties.getProperty(propertyName).trim(); + } else + { + result = result.trim(); } - result = properties.getProperty(propertyName).trim(); } catch (Exception e) { logger.error(propertyName + " retrival failed.", e); From 31ff8211940c2c5a3604b4b60c20dcae8df5134c Mon Sep 17 00:00:00 2001 From: vanitha1822 Date: Wed, 8 Apr 2026 22:22:48 +0530 Subject: [PATCH 49/58] fix: update sms issue --- src/main/java/com/iemr/common/service/sms/SMSServiceImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/iemr/common/service/sms/SMSServiceImpl.java b/src/main/java/com/iemr/common/service/sms/SMSServiceImpl.java index efe0d16a..af443ffa 100644 --- a/src/main/java/com/iemr/common/service/sms/SMSServiceImpl.java +++ b/src/main/java/com/iemr/common/service/sms/SMSServiceImpl.java @@ -391,8 +391,8 @@ public SMSNotification prepareVideoCallSMS(SMSRequest request, VideoCallParamete String variable = smsParametersMap.getSmsParameterName(); String methodName = smsParametersMap.getSmsParameter().getDataName(); String variableValue = ""; - variableValue = getVideoCallData(methodName, vcParams); - smsToSend = smsToSend.replace("$$" + variable + "$$", variableValue); + // variableValue = getVideoCallData(methodName, vcParams); + // smsToSend = smsToSend.replace("$$" + variable + "$$", variableValue); if ("VideoCall".equalsIgnoreCase(smsParametersMap.getSmsParameter().getSmsParameterType())) { variableValue = getVideoCallData(methodName, vcParams); From ac388d221fd3b806453e9a04d5fc7aca3960ab3d Mon Sep 17 00:00:00 2001 From: vanitha1822 Date: Wed, 8 Apr 2026 22:44:58 +0530 Subject: [PATCH 50/58] fix: build issue --- src/main/java/com/iemr/common/service/cti/CTIServiceImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/iemr/common/service/cti/CTIServiceImpl.java b/src/main/java/com/iemr/common/service/cti/CTIServiceImpl.java index 7a6a2e4b..5536a721 100644 --- a/src/main/java/com/iemr/common/service/cti/CTIServiceImpl.java +++ b/src/main/java/com/iemr/common/service/cti/CTIServiceImpl.java @@ -26,6 +26,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; From 00aa6daa5254bf30c3908df14505d2d2195dbe45 Mon Sep 17 00:00:00 2001 From: vanitha1822 Date: Wed, 8 Apr 2026 23:24:14 +0530 Subject: [PATCH 51/58] fix: update condition --- .../common/service/sms/SMSServiceImpl.java | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/iemr/common/service/sms/SMSServiceImpl.java b/src/main/java/com/iemr/common/service/sms/SMSServiceImpl.java index af443ffa..758bf59b 100644 --- a/src/main/java/com/iemr/common/service/sms/SMSServiceImpl.java +++ b/src/main/java/com/iemr/common/service/sms/SMSServiceImpl.java @@ -436,10 +436,15 @@ public String getVideoCallData(String methodName, VideoCallParameters videoCall) variableValue = videoCall.getCallerPhoneNumber() !=null ? videoCall.getCallerPhoneNumber().toString() : ""; break; default: - Method method = videoCall.getClass().getDeclaredMethod("get" + capitalize(methodName)); - method.setAccessible(true); - Object result = method.invoke(videoCall); - variableValue = result != null ? result.toString() : ""; + try { + Method method = videoCall.getClass().getDeclaredMethod("get" + capitalize(methodName)); + method.setAccessible(true); + Object result = method.invoke(videoCall); + variableValue = result != null ? result.toString() : ""; + } catch (NoSuchMethodException e) { + logger.warn("No getter found for methodName: " + methodName + " on VideoCallParameters"); + variableValue = ""; + } break; } return variableValue.trim(); @@ -875,9 +880,16 @@ private String getBeneficiaryData(String className, String methodName, SMSReques variableValue = imrName; break; default: - Class clazz = Class.forName(className); - Method method = clazz.getDeclaredMethod("get" + methodName, null); - variableValue = method.invoke(beneficiary, null).toString(); + if ("com.iemr.common.data.videocall.VideoCallParameters".equals(className)) { + VideoCallParameters vcParams = getVideoCallParameters(request.getSmsAdvice()); + if (vcParams != null) { + variableValue = getVideoCallData(methodName, vcParams); + } + } else { + Class clazz = Class.forName(className); + Method method = clazz.getDeclaredMethod("get" + methodName, null); + variableValue = method.invoke(beneficiary, null).toString(); + } break; } From c49d792a37ade00cee80a06528ebdc2bf6ff60e6 Mon Sep 17 00:00:00 2001 From: vanitha1822 Date: Thu, 9 Apr 2026 09:24:24 +0530 Subject: [PATCH 52/58] fix: edit ben issue --- .../service/beneficiary/RegisterBenificiaryServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/iemr/common/service/beneficiary/RegisterBenificiaryServiceImpl.java b/src/main/java/com/iemr/common/service/beneficiary/RegisterBenificiaryServiceImpl.java index 1dd0b533..ac084e15 100644 --- a/src/main/java/com/iemr/common/service/beneficiary/RegisterBenificiaryServiceImpl.java +++ b/src/main/java/com/iemr/common/service/beneficiary/RegisterBenificiaryServiceImpl.java @@ -120,7 +120,7 @@ private void updateBeneficiaryID(String beneficiaryID, Long beneficiaryRegID) { } } - @Async + // @Async @Override public Integer updateBenificiary(BeneficiaryModel benificiaryDetails, String auth) throws IEMRException { Integer updatedRows = 0; From 9bbfdfc120e654405a7e986d280ff2d705bb0ee7 Mon Sep 17 00:00:00 2001 From: vanitha1822 Date: Thu, 9 Apr 2026 10:40:27 +0530 Subject: [PATCH 53/58] fix: phone number issue for sms --- .../java/com/iemr/common/service/sms/SMSServiceImpl.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/iemr/common/service/sms/SMSServiceImpl.java b/src/main/java/com/iemr/common/service/sms/SMSServiceImpl.java index 758bf59b..c49eca10 100644 --- a/src/main/java/com/iemr/common/service/sms/SMSServiceImpl.java +++ b/src/main/java/com/iemr/common/service/sms/SMSServiceImpl.java @@ -683,7 +683,7 @@ private SMSNotification prepareSMS( sms.setReceivingUserID(request.getUserID()); String smsToSend = ""; BeneficiaryModel beneficiary = null; - if (request.getBeneficiaryRegID() != null) { + if (request.getBeneficiaryRegID() != null && !request.getBeneficiaryRegID().toString().isEmpty()) { List beneficiaries = searchBeneficiary.userExitsCheckWithId(request.getBeneficiaryRegID(), authToken, request.getIs1097()); if (beneficiaries.size() == 1) @@ -849,6 +849,12 @@ private String getUserData(String className, String methodName, SMSRequest reque private String getBeneficiaryData(String className, String methodName, SMSRequest request, BeneficiaryModel beneficiary) throws Exception { String variableValue = ""; + if (beneficiary == null) { + if ("phoneno".equalsIgnoreCase(methodName)) { + return request.getBenPhoneNo() != null ? request.getBenPhoneNo() : ""; + } + return ""; + } switch (methodName.toLowerCase()) { case "name": String fname = beneficiary.getFirstName() != null ? beneficiary.getFirstName() + " " : ""; From 61cc94e466e9727f000302c04baee2d540618f68 Mon Sep 17 00:00:00 2001 From: vanitha1822 Date: Thu, 9 Apr 2026 15:05:45 +0530 Subject: [PATCH 54/58] fix: update the url with jwt token --- src/main/environment/common_ci.properties | 9 ++ src/main/environment/common_docker.properties | 9 ++ .../environment/common_example.properties | 8 ++ .../videocall/VideoCallController.java | 36 ++++- .../service/videocall/VideoCallService.java | 14 +- .../videocall/VideoCallServiceImpl.java | 49 ++++++- .../com/iemr/common/utils/JitsiJwtUtil.java | 108 +++++++++++++++ .../utils/JwtUserIdValidationFilter.java | 6 +- .../videocall/VideoCallControllerTest.java | 37 +++++ .../videocall/VideoCallServiceImplTest.java | 69 ++++++++++ .../iemr/common/utils/JitsiJwtUtilTest.java | 126 ++++++++++++++++++ 11 files changed, 466 insertions(+), 5 deletions(-) create mode 100644 src/main/java/com/iemr/common/utils/JitsiJwtUtil.java create mode 100644 src/test/java/com/iemr/common/utils/JitsiJwtUtilTest.java diff --git a/src/main/environment/common_ci.properties b/src/main/environment/common_ci.properties index 92c0be50..4a21c0bf 100644 --- a/src/main/environment/common_ci.properties +++ b/src/main/environment/common_ci.properties @@ -194,6 +194,15 @@ cors.allowed-origins=@env.CORS_ALLOWED_ORIGINS@ videocall.url=@env.VIDEO_CALL_URL@ video.recording.path=@env.VIDEO_RECORDING_PATH@ +# Jitsi JWT (prosody token-auth) +jitsi.app.id=@env.JITSI_APP_ID@ +jitsi.app.secret=@env.JITSI_APP_SECRET@ +jitsi.domain=@env.JITSI_DOMAIN@ +jitsi.sub=@env.JITSI_SUB@ +jitsi.token.ttl.seconds=@env.JITSI_TOKEN_TTL_SECONDS@ +jitsi.room.prefix=@env.JITSI_ROOM_PREFIX@ +jitsi.default.user.email=@env.JITSI_DEFAULT_USER_EMAIL@ + platform.feedback.ratelimit.enabled=@env.PLATFORM_FEEDBACK_RATELIMIT_ENABLED@ platform.feedback.ratelimit.pepper=@env.PLATFORM_FEEDBACK_RATELIMIT_PEPPER@ platform.feedback.ratelimit.trust-forwarded-for=@env.PLATFORM_FEEDBACK_RATELIMIT_TRUST_FORWARDED_FOR@ diff --git a/src/main/environment/common_docker.properties b/src/main/environment/common_docker.properties index 5d674bfc..64266613 100644 --- a/src/main/environment/common_docker.properties +++ b/src/main/environment/common_docker.properties @@ -196,6 +196,15 @@ videocall.url=${VIDEO_CALL_URL} jibri.output.path={JIBRI_OUTPUT_PATH} video.recording.path={VIDEO_RECORDING_PATH} +# Jitsi JWT (prosody token-auth) +jitsi.app.id=${JITSI_APP_ID} +jitsi.app.secret=${JITSI_APP_SECRET} +jitsi.domain=${JITSI_DOMAIN} +jitsi.sub=${JITSI_SUB} +jitsi.token.ttl.seconds=${JITSI_TOKEN_TTL_SECONDS} +jitsi.room.prefix=${JITSI_ROOM_PREFIX} +jitsi.default.user.email=${JITSI_DEFAULT_USER_EMAIL} + # Platform Feedback module platform.feedback.ratelimit.enabled=${PLATFORM_FEEDBACK_RATELIMIT_ENABLED} platform.feedback.ratelimit.pepper=${PLATFORM_FEEDBACK_RATELIMIT_PEPPER} diff --git a/src/main/environment/common_example.properties b/src/main/environment/common_example.properties index 2f13e4f1..288c3ec5 100644 --- a/src/main/environment/common_example.properties +++ b/src/main/environment/common_example.properties @@ -228,3 +228,11 @@ platform.feedback.ratelimit.backoff-minutes=15 ### generate Beneficiary IDs URL generateBeneficiaryIDs-api-url=/generateBeneficiaryController/generateBeneficiaryIDs + +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 diff --git a/src/main/java/com/iemr/common/controller/videocall/VideoCallController.java b/src/main/java/com/iemr/common/controller/videocall/VideoCallController.java index fdbd9575..671133c5 100644 --- a/src/main/java/com/iemr/common/controller/videocall/VideoCallController.java +++ b/src/main/java/com/iemr/common/controller/videocall/VideoCallController.java @@ -22,6 +22,7 @@ package com.iemr.common.controller.videocall; +import java.net.URI; import java.util.HashMap; import java.util.Map; @@ -32,8 +33,10 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import com.iemr.common.model.videocall.UpdateCallRequest; @@ -113,6 +116,35 @@ public ResponseEntity updateCallStatus(@RequestBody UpdateCallRequest re return ResponseEntity.ok(response.toString()); } - - +/** + * Public redirect endpoint hit when a beneficiary clicks the short SMS link. + * + * Flow: + * 1. Jitsi host nginx receives "https://vc.piramalswasthya.org/?m=<slug>" + * and proxies/redirects it to this endpoint. + * 2. This endpoint looks up the slug, mints a fresh Jitsi JWT bound to the + * room and the agent, and 302-redirects the browser to the full Jitsi URL + * "https://vc.piramalswasthya.org/<room>?jwt=<token>". + * 3. The Jitsi server enforces the JWT (prosody token-auth) and admits the user. + * + * Intentionally NOT guarded by Authorization header - the SMS recipient is on + * a phone browser and has no app session. Access control is the JWT itself + * plus the slug being unguessable and the meeting row existing. + */ +@GetMapping(value = "/resolve") +public ResponseEntity resolveMeetingLink(@RequestParam("m") String slug) { + 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 (Exception e) { + logger.error("resolveMeetingLink failed for slug={}: {}", slug, e.getMessage(), e); + return ResponseEntity.status(HttpStatus.NOT_FOUND).build(); + } +} + } diff --git a/src/main/java/com/iemr/common/service/videocall/VideoCallService.java b/src/main/java/com/iemr/common/service/videocall/VideoCallService.java index dd457688..096c000b 100644 --- a/src/main/java/com/iemr/common/service/videocall/VideoCallService.java +++ b/src/main/java/com/iemr/common/service/videocall/VideoCallService.java @@ -26,10 +26,22 @@ import com.iemr.common.model.videocall.VideoCallRequest; public interface VideoCallService { - + public String generateMeetingLink() throws Exception; public String sendMeetingLink(VideoCallRequest request) throws Exception; public String updateCallStatus(UpdateCallRequest request) throws Exception; + + /** + * Resolve the short slug carried in the SMS link (the value after "?m=") + * into the full Jitsi URL with a freshly minted JWT appended. + * Called by the public redirect endpoint that the Jitsi host's nginx + * proxies "/?m=<slug>" requests to. + * + * @param slug the random slug originally generated by {@link #generateMeetingLink()} + * @return absolute URL of the form + * https://<jitsi.domain>/<jitsi.room.prefix><slug>?jwt=<token> + */ + public String resolveMeetingLink(String slug) throws Exception; } diff --git a/src/main/java/com/iemr/common/service/videocall/VideoCallServiceImpl.java b/src/main/java/com/iemr/common/service/videocall/VideoCallServiceImpl.java index 8b61d525..2669ed92 100644 --- a/src/main/java/com/iemr/common/service/videocall/VideoCallServiceImpl.java +++ b/src/main/java/com/iemr/common/service/videocall/VideoCallServiceImpl.java @@ -40,6 +40,7 @@ import com.iemr.common.model.videocall.UpdateCallRequest; import com.iemr.common.model.videocall.VideoCallRequest; import com.iemr.common.repository.videocall.VideoCallParameterRepository; +import com.iemr.common.utils.JitsiJwtUtil; import com.iemr.common.utils.config.ConfigProperties; import com.iemr.common.utils.mapper.OutputMapper; import com.iemr.common.utils.response.OutputResponse; @@ -51,10 +52,13 @@ public class VideoCallServiceImpl implements VideoCallService { @Autowired private VideoCallParameterRepository videoCallRepository; - + @Autowired private VideoCallMapper videoCallMapper; + @Autowired + private JitsiJwtUtil jitsiJwtUtil; + private String meetingLink; private boolean isLinkSent = false; @@ -62,6 +66,17 @@ public class VideoCallServiceImpl implements VideoCallService { @Value("${videocall.url}") private String jitsiLink; + // Fallback chains let either dot-form or JITSI_*-form work in any property + // source (.properties files do NOT get Spring relaxed binding for @Value). + @Value("${jitsi.domain:${JITSI_DOMAIN:vc.piramalswasthya.org}}") + private String jitsiDomain; + + @Value("${jitsi.room.prefix:${JITSI_ROOM_PREFIX:piramal-meeting-}}") + private String roomPrefix; + + @Value("${jitsi.default.user.email:${JITSI_DEFAULT_USER_EMAIL:admin@piramalswasthya.org}}") + private String defaultUserEmail; + public VideoCallServiceImpl() { // Default constructor this.meetingLink = null; @@ -130,6 +145,38 @@ public String updateCallStatus(UpdateCallRequest callRequest) throws Exception { .toJson(videoCallMapper.videoCallToResponse(videoCall)); } +@Override +public String resolveMeetingLink(String slug) throws Exception { + if (slug == null || slug.isEmpty()) { + throw new IllegalArgumentException("Meeting slug is required"); + } + + // The persisted meetingLink is the short URL produced by generateMeetingLink(), + // i.e. "m=". Reconstruct it to look up the row. + String shortLink = jitsiLink + "m=" + slug; + VideoCallParameters params = videoCallRepository.findByMeetingLink(shortLink); + + if (params == null) { + throw new Exception("No meeting found for slug: " + slug); + } + + // Note: we deliberately do NOT block on linkUsed=true here, because real + // calls drop and the agent/beneficiary often have to rejoin. The linkUsed + // flag is for reporting in updateCallStatus, not access control. Access + // control comes from the JWT exp + the room claim. + + String roomName = roomPrefix + slug; + String userName = params.getAgentName() != null && !params.getAgentName().isEmpty() + ? params.getAgentName() + : "Guest"; + + String token = jitsiJwtUtil.generateRoomToken(roomName, userName, defaultUserEmail); + + String redirectUrl = "https://" + jitsiDomain + "/" + roomName + "?jwt=" + token; + logger.info("Resolved slug {} -> room {}", slug, roomName); + return redirectUrl; +} + } diff --git a/src/main/java/com/iemr/common/utils/JitsiJwtUtil.java b/src/main/java/com/iemr/common/utils/JitsiJwtUtil.java new file mode 100644 index 00000000..4c6be54e --- /dev/null +++ b/src/main/java/com/iemr/common/utils/JitsiJwtUtil.java @@ -0,0 +1,108 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.common.utils; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import javax.crypto.SecretKey; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; + +/** + * Mints HS256 JWTs that are accepted by the Jitsi/prosody token-auth module + * running on the video-conferencing host. This is intentionally separate from + * {@link JwtUtil} (which mints application session tokens) because the secret, + * claim set, and expiration policy are completely different. + * + * Claims produced (matches what devops configured on prosody): + * aud -> jitsi.app.id (e.g. "piramal_vc") + * iss -> jitsi.app.id (e.g. "piramal_vc") + * sub -> jitsi.sub (must always be "meet.jitsi") + * room -> the room name to admit the bearer into + * exp -> now + jitsi.token.ttl.seconds + * context.user.{name,email} -> displayed in the Jitsi UI + */ +@Component +public class JitsiJwtUtil { + + // Fallback chains let either dot-form (jitsi.app.id=...) or upper-form + // (JITSI_APP_ID=...) work in any property source, including .properties + // files which Spring does NOT relaxed-bind for @Value. + @Value("${jitsi.app.id:${JITSI_APP_ID:}}") + private String appId; + + @Value("${jitsi.app.secret:${JITSI_APP_SECRET:}}") + private String appSecret; + + @Value("${jitsi.sub:${JITSI_SUB:meet.jitsi}}") + private String sub; + + @Value("${jitsi.token.ttl.seconds:${JITSI_TOKEN_TTL_SECONDS:3600}}") + private long ttlSeconds; + + private SecretKey getSigningKey() { + if (appSecret == null || appSecret.isEmpty()) { + throw new IllegalStateException("jitsi.app.secret is not configured"); + } + return Keys.hmacShaKeyFor(appSecret.getBytes()); + } + + /** + * Build a Jitsi room JWT. + * + * @param room the exact room name the bearer will join (must match the URL path) + * @param userName display name shown in the Jitsi UI + * @param userEmail email shown in the Jitsi UI (used for gravatar etc.) + * @return signed compact JWT string + */ + public String generateRoomToken(String room, String userName, String userEmail) { + if (room == null || room.isEmpty()) { + throw new IllegalArgumentException("room is required to mint a Jitsi token"); + } + + long nowMs = System.currentTimeMillis(); + Date expiry = new Date(nowMs + (ttlSeconds * 1000L)); + + Map user = new HashMap<>(); + user.put("name", userName != null ? userName : "Guest"); + user.put("email", userEmail != null ? userEmail : ""); + + Map context = new HashMap<>(); + context.put("user", user); + + return Jwts.builder() + .audience().add(appId).and() + .issuer(appId) + .subject(sub) + .claim("room", room) + .claim("context", context) + .expiration(expiry) + .signWith(getSigningKey()) + .compact(); + } +} diff --git a/src/main/java/com/iemr/common/utils/JwtUserIdValidationFilter.java b/src/main/java/com/iemr/common/utils/JwtUserIdValidationFilter.java index 81d79221..a0358da0 100644 --- a/src/main/java/com/iemr/common/utils/JwtUserIdValidationFilter.java +++ b/src/main/java/com/iemr/common/utils/JwtUserIdValidationFilter.java @@ -251,7 +251,11 @@ private boolean shouldSkipAuthentication(String path, String contextPath) { || path.startsWith(contextPath + "/user/userLogout") || path.startsWith(contextPath + "/user/validateSecurityQuestionAndAnswer") || path.startsWith(contextPath + "/user/logOutUserFromConcurrentSession") - || path.startsWith(contextPath + "/user/refreshToken"); + || path.startsWith(contextPath + "/user/refreshToken") + // Public Jitsi short-link redirect: hit by SMS recipients on phone + // browsers that have no app session. Access control is the JWT minted + // inside the redirect handler + the unguessable slug. + || path.equals(contextPath + "/video-consultation/resolve"); } private String getJwtTokenFromCookies(HttpServletRequest request) { diff --git a/src/test/java/com/iemr/common/controller/videocall/VideoCallControllerTest.java b/src/test/java/com/iemr/common/controller/videocall/VideoCallControllerTest.java index b3b82380..705beffa 100644 --- a/src/test/java/com/iemr/common/controller/videocall/VideoCallControllerTest.java +++ b/src/test/java/com/iemr/common/controller/videocall/VideoCallControllerTest.java @@ -36,7 +36,9 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; @@ -195,4 +197,39 @@ void shouldReturnOkWithErrorInBody_whenUpdateCallStatusServiceFails() throws Exc verify(videoCallService, times(1)).updateCallStatus(any(UpdateCallRequest.class)); } + + // 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"); + } } \ No newline at end of file diff --git a/src/test/java/com/iemr/common/service/videocall/VideoCallServiceImplTest.java b/src/test/java/com/iemr/common/service/videocall/VideoCallServiceImplTest.java index baed9029..2723fda0 100644 --- a/src/test/java/com/iemr/common/service/videocall/VideoCallServiceImplTest.java +++ b/src/test/java/com/iemr/common/service/videocall/VideoCallServiceImplTest.java @@ -26,6 +26,7 @@ import com.iemr.common.model.videocall.UpdateCallRequest; import com.iemr.common.model.videocall.VideoCallRequest; import com.iemr.common.repository.videocall.VideoCallParameterRepository; +import com.iemr.common.utils.JitsiJwtUtil; import com.iemr.common.utils.config.ConfigProperties; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -59,10 +60,15 @@ public class VideoCallServiceImplTest { UpdateCallRequest updateCallRequest; @Mock VideoCallParameters videoCallParameters; + @Mock + JitsiJwtUtil jitsiJwtUtil; @BeforeEach public void setup() throws Exception { ReflectionTestUtils.setField(service, "jitsiLink", "https://meet.jit.si/"); + ReflectionTestUtils.setField(service, "jitsiDomain", "meet.jit.si"); + ReflectionTestUtils.setField(service, "roomPrefix", "piramal-meeting-"); + ReflectionTestUtils.setField(service, "defaultUserEmail", "admin@piramalswasthya.org"); } @Test @@ -175,6 +181,69 @@ public void testSaveRecordingFile_noMatchingFile() throws Exception { } } + @Test + public void testResolveMeetingLink_success() throws Exception { + when(videoCallRepository.findByMeetingLink("https://meet.jit.si/m=Ab3xQ9pK")) + .thenReturn(videoCallParameters); + when(videoCallParameters.getAgentName()).thenReturn("Dr. Asha"); + when(jitsiJwtUtil.generateRoomToken( + eq("piramal-meeting-Ab3xQ9pK"), + eq("Dr. Asha"), + eq("admin@piramalswasthya.org"))).thenReturn("FAKE.JWT.TOKEN"); + + String result = service.resolveMeetingLink("Ab3xQ9pK"); + + assertEquals( + "https://meet.jit.si/piramal-meeting-Ab3xQ9pK?jwt=FAKE.JWT.TOKEN", + result); + verify(jitsiJwtUtil).generateRoomToken( + "piramal-meeting-Ab3xQ9pK", "Dr. Asha", "admin@piramalswasthya.org"); + } + + @Test + public void testResolveMeetingLink_emptySlug() { + IllegalArgumentException ex = assertThrows( + IllegalArgumentException.class, + () -> service.resolveMeetingLink("")); + assertEquals("Meeting slug is required", ex.getMessage()); + } + + @Test + public void testResolveMeetingLink_nullSlug() { + IllegalArgumentException ex = assertThrows( + IllegalArgumentException.class, + () -> service.resolveMeetingLink(null)); + assertEquals("Meeting slug is required", ex.getMessage()); + } + + @Test + public void testResolveMeetingLink_notFound() { + when(videoCallRepository.findByMeetingLink("https://meet.jit.si/m=missing")) + .thenReturn(null); + + Exception ex = assertThrows( + Exception.class, + () -> service.resolveMeetingLink("missing")); + assertTrue(ex.getMessage().contains("No meeting found")); + } + + @Test + public void testResolveMeetingLink_fallbackUserNameWhenAgentMissing() throws Exception { + when(videoCallRepository.findByMeetingLink("https://meet.jit.si/m=Ab3xQ9pK")) + .thenReturn(videoCallParameters); + when(videoCallParameters.getAgentName()).thenReturn(null); + when(jitsiJwtUtil.generateRoomToken( + eq("piramal-meeting-Ab3xQ9pK"), + eq("Guest"), + eq("admin@piramalswasthya.org"))).thenReturn("FAKE.JWT.TOKEN"); + + String result = service.resolveMeetingLink("Ab3xQ9pK"); + + assertTrue(result.endsWith("?jwt=FAKE.JWT.TOKEN")); + verify(jitsiJwtUtil).generateRoomToken( + "piramal-meeting-Ab3xQ9pK", "Guest", "admin@piramalswasthya.org"); + } + @Test public void testSaveRecordingFile_ioException() throws Exception { try (MockedStatic configMock = mockStatic(ConfigProperties.class); diff --git a/src/test/java/com/iemr/common/utils/JitsiJwtUtilTest.java b/src/test/java/com/iemr/common/utils/JitsiJwtUtilTest.java new file mode 100644 index 00000000..c300e27b --- /dev/null +++ b/src/test/java/com/iemr/common/utils/JitsiJwtUtilTest.java @@ -0,0 +1,126 @@ +/* +* AMRIT – Accessible Medical Records via Integrated Technology +* Integrated EHR (Electronic Health Records) Solution +* +* Copyright (C) "Piramal Swasthya Management and Research Institute" +* +* This file is part of AMRIT. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see https://www.gnu.org/licenses/. +*/ +package com.iemr.common.utils; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.Date; +import java.util.Map; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.test.util.ReflectionTestUtils; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.security.Keys; + +class JitsiJwtUtilTest { + + // Same secret format as the one devops gave us (HS256, length must be >=32 bytes for Keys.hmacShaKeyFor) + private static final String APP_ID = "piramal_vc"; + private static final String APP_SECRET = "5b9883418be6f228ffe3ceaa74dd3d3b91737733a4a85c5e82fc584ad449850b"; + private static final String SUB = "meet.jitsi"; + + private JitsiJwtUtil util; + + @BeforeEach + void setUp() { + util = new JitsiJwtUtil(); + ReflectionTestUtils.setField(util, "appId", APP_ID); + ReflectionTestUtils.setField(util, "appSecret", APP_SECRET); + ReflectionTestUtils.setField(util, "sub", SUB); + ReflectionTestUtils.setField(util, "ttlSeconds", 3600L); + } + + @Test + void generateRoomToken_producesAllRequiredClaims() { + String token = util.generateRoomToken("piramal-meeting-Ab3xQ9pK", "Dr. Asha", "asha@piramalswasthya.org"); + + assertNotNull(token); + assertTrue(token.split("\\.").length == 3, "JWT should have 3 dot-separated parts"); + + Claims claims = Jwts.parser() + .verifyWith(Keys.hmacShaKeyFor(APP_SECRET.getBytes())) + .build() + .parseSignedClaims(token) + .getPayload(); + + assertEquals(APP_ID, claims.getIssuer()); + assertTrue(claims.getAudience().contains(APP_ID)); + assertEquals(SUB, claims.getSubject()); + assertEquals("piramal-meeting-Ab3xQ9pK", claims.get("room", String.class)); + + @SuppressWarnings("unchecked") + Map context = claims.get("context", Map.class); + assertNotNull(context); + @SuppressWarnings("unchecked") + Map user = (Map) context.get("user"); + assertNotNull(user); + assertEquals("Dr. Asha", user.get("name")); + assertEquals("asha@piramalswasthya.org", user.get("email")); + + Date exp = claims.getExpiration(); + assertNotNull(exp); + assertTrue(exp.after(new Date()), "exp should be in the future"); + } + + @Test + void generateRoomToken_fallsBackToGuestWhenUserNameNull() { + String token = util.generateRoomToken("piramal-meeting-xyz", null, null); + + Claims claims = Jwts.parser() + .verifyWith(Keys.hmacShaKeyFor(APP_SECRET.getBytes())) + .build() + .parseSignedClaims(token) + .getPayload(); + + @SuppressWarnings("unchecked") + Map context = claims.get("context", Map.class); + @SuppressWarnings("unchecked") + Map user = (Map) context.get("user"); + assertEquals("Guest", user.get("name")); + assertEquals("", user.get("email")); + } + + @Test + void generateRoomToken_rejectsEmptyRoom() { + assertThrows(IllegalArgumentException.class, + () -> util.generateRoomToken("", "Dr. Asha", "asha@piramalswasthya.org")); + } + + @Test + void generateRoomToken_rejectsNullRoom() { + assertThrows(IllegalArgumentException.class, + () -> util.generateRoomToken(null, "Dr. Asha", "asha@piramalswasthya.org")); + } + + @Test + void generateRoomToken_failsWhenAppSecretMissing() { + ReflectionTestUtils.setField(util, "appSecret", ""); + assertThrows(IllegalStateException.class, + () -> util.generateRoomToken("piramal-meeting-xyz", "Dr. Asha", "asha@piramalswasthya.org")); + } +} From a48b4afe6a9f35a7db3e33a31263e9c7e8888e9b Mon Sep 17 00:00:00 2001 From: vanitha1822 Date: Fri, 10 Apr 2026 14:46:20 +0530 Subject: [PATCH 55/58] fix: jitsi authorization issue --- src/main/java/com/iemr/common/config/InterceptorConfig.java | 3 ++- .../java/com/iemr/common/utils/JwtUserIdValidationFilter.java | 2 +- .../com/iemr/common/utils/http/HTTPRequestInterceptor.java | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/iemr/common/config/InterceptorConfig.java b/src/main/java/com/iemr/common/config/InterceptorConfig.java index a321eb08..b39a7358 100644 --- a/src/main/java/com/iemr/common/config/InterceptorConfig.java +++ b/src/main/java/com/iemr/common/config/InterceptorConfig.java @@ -36,7 +36,8 @@ public class InterceptorConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { - registry.addInterceptor(requestInterceptor); + registry.addInterceptor(requestInterceptor) + .excludePathPatterns("/video-consultation/resolve"); } } \ No newline at end of file diff --git a/src/main/java/com/iemr/common/utils/JwtUserIdValidationFilter.java b/src/main/java/com/iemr/common/utils/JwtUserIdValidationFilter.java index a0358da0..94c4b8ca 100644 --- a/src/main/java/com/iemr/common/utils/JwtUserIdValidationFilter.java +++ b/src/main/java/com/iemr/common/utils/JwtUserIdValidationFilter.java @@ -255,7 +255,7 @@ private boolean shouldSkipAuthentication(String path, String contextPath) { // Public Jitsi short-link redirect: hit by SMS recipients on phone // browsers that have no app session. Access control is the JWT minted // inside the redirect handler + the unguessable slug. - || path.equals(contextPath + "/video-consultation/resolve"); + || path.endsWith("/video-consultation/resolve"); } private String getJwtTokenFromCookies(HttpServletRequest request) { diff --git a/src/main/java/com/iemr/common/utils/http/HTTPRequestInterceptor.java b/src/main/java/com/iemr/common/utils/http/HTTPRequestInterceptor.java index b4aaad60..757e59d9 100644 --- a/src/main/java/com/iemr/common/utils/http/HTTPRequestInterceptor.java +++ b/src/main/java/com/iemr/common/utils/http/HTTPRequestInterceptor.java @@ -125,6 +125,7 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons case "validateSecurityQuestionAndAnswer": case "logOutUserFromConcurrentSession": case "refreshToken": + case "resolve": break; case "error": status = false; From 389c20de7f9de04e26f14bd66e877d9cb25a5e29 Mon Sep 17 00:00:00 2001 From: vanitha1822 Date: Fri, 10 Apr 2026 15:55:49 +0530 Subject: [PATCH 56/58] fix: skip auth --- .../iemr/common/config/InterceptorConfig.java | 2 +- .../utils/JwtUserIdValidationFilter.java | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/iemr/common/config/InterceptorConfig.java b/src/main/java/com/iemr/common/config/InterceptorConfig.java index b39a7358..8a45482a 100644 --- a/src/main/java/com/iemr/common/config/InterceptorConfig.java +++ b/src/main/java/com/iemr/common/config/InterceptorConfig.java @@ -37,7 +37,7 @@ public class InterceptorConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(requestInterceptor) - .excludePathPatterns("/video-consultation/resolve"); + .excludePathPatterns("/video-consultation/resolve", "**/video-consultation/resolve"); } } \ No newline at end of file diff --git a/src/main/java/com/iemr/common/utils/JwtUserIdValidationFilter.java b/src/main/java/com/iemr/common/utils/JwtUserIdValidationFilter.java index 94c4b8ca..ec841a4d 100644 --- a/src/main/java/com/iemr/common/utils/JwtUserIdValidationFilter.java +++ b/src/main/java/com/iemr/common/utils/JwtUserIdValidationFilter.java @@ -120,6 +120,15 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo logger.info("JwtUserIdValidationFilter invoked for path: " + path); + // Public video-consultation resolve endpoint: hit by SMS recipients on + // phone browsers that have no app session. Skip ALL auth — the JWT minted + // inside the handler + the unguessable slug provide access control. + if (isVideoConsultationResolvePath(path, contextPath)) { + logger.info("Video-consultation resolve path detected - skipping authentication: {}", path); + filterChain.doFilter(servletRequest, servletResponse); + return; + } + // NEW: if this is a platform-feedback endpoint, treat it as public (skip auth) // and also ensure we don't clear any user cookies for these requests. if (isPlatformFeedbackPath(path, contextPath)) { @@ -206,6 +215,17 @@ private boolean isPlatformFeedbackPath(String path, String contextPath) { return normalized.startsWith(base + "/platform-feedback"); } + /** + * Identifies the public video-consultation resolve endpoint. + * Uses multiple matching strategies to be resilient against + * context-path mismatches between reverse-proxy and Wildfly. + */ + private boolean isVideoConsultationResolvePath(String path, String contextPath) { + if (path == null) return false; + String normalized = path.toLowerCase(); + return normalized.endsWith("/video-consultation/resolve") + || normalized.contains("/video-consultation/resolve"); + } private boolean isOriginAllowed(String origin) { if (origin == null || allowedOrigins == null || allowedOrigins.trim().isEmpty()) { From 1fc65093e63cb6c6f42446f0e28d13995e0e7b32 Mon Sep 17 00:00:00 2001 From: vanitha1822 Date: Fri, 10 Apr 2026 17:38:15 +0530 Subject: [PATCH 57/58] fix: hash key updation --- src/main/java/com/iemr/common/utils/JitsiJwtUtil.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/iemr/common/utils/JitsiJwtUtil.java b/src/main/java/com/iemr/common/utils/JitsiJwtUtil.java index 4c6be54e..a565c599 100644 --- a/src/main/java/com/iemr/common/utils/JitsiJwtUtil.java +++ b/src/main/java/com/iemr/common/utils/JitsiJwtUtil.java @@ -96,13 +96,13 @@ public String generateRoomToken(String room, String userName, String userEmail) context.put("user", user); return Jwts.builder() - .audience().add(appId).and() + .claim("aud", appId) .issuer(appId) .subject(sub) .claim("room", room) .claim("context", context) .expiration(expiry) - .signWith(getSigningKey()) + .signWith(getSigningKey(), Jwts.SIG.HS256) .compact(); } } From 640841b52e9d38d3f235596fd635b422cc38ac44 Mon Sep 17 00:00:00 2001 From: vanitha1822 Date: Fri, 10 Apr 2026 18:05:15 +0530 Subject: [PATCH 58/58] fix: jwt type in header for authorization --- src/main/java/com/iemr/common/utils/JitsiJwtUtil.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/iemr/common/utils/JitsiJwtUtil.java b/src/main/java/com/iemr/common/utils/JitsiJwtUtil.java index a565c599..75591633 100644 --- a/src/main/java/com/iemr/common/utils/JitsiJwtUtil.java +++ b/src/main/java/com/iemr/common/utils/JitsiJwtUtil.java @@ -96,6 +96,7 @@ public String generateRoomToken(String room, String userName, String userEmail) context.put("user", user); return Jwts.builder() + .header().add("typ", "JWT").and() .claim("aud", appId) .issuer(appId) .subject(sub)