From 58a737aee054a28b547fcc56cb75ad0b34c66dcd Mon Sep 17 00:00:00 2001 From: vishwab1 Date: Thu, 4 Jun 2026 12:25:16 +0530 Subject: [PATCH 01/10] feat(stoptb): store camp vanID in Redis on MMU user van selection After MMU user selects their service point/van via getUserVanSpDetails, write camp:vanID and camp:parkingPlaceID to Redis with 30-day TTL. FLW-API and TM-API read these keys to stamp correct vanID on all Stop TB records, enabling MMU DataSync to push data to central. Co-Authored-By: Claude Sonnet 4.6 --- .../login/IemrMmuLoginServiceImpl.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/main/java/com/iemr/mmu/service/login/IemrMmuLoginServiceImpl.java b/src/main/java/com/iemr/mmu/service/login/IemrMmuLoginServiceImpl.java index 36c9b7c6..467cfc70 100644 --- a/src/main/java/com/iemr/mmu/service/login/IemrMmuLoginServiceImpl.java +++ b/src/main/java/com/iemr/mmu/service/login/IemrMmuLoginServiceImpl.java @@ -28,7 +28,13 @@ import java.util.Map; import java.util.Set; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.connection.RedisConnection; +import org.springframework.data.redis.connection.RedisStringCommands.SetOption; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.types.Expiration; import org.springframework.stereotype.Service; import com.google.gson.Gson; @@ -44,6 +50,11 @@ @Service public class IemrMmuLoginServiceImpl implements IemrMmuLoginService { + private static final Logger logger = LoggerFactory.getLogger(IemrMmuLoginServiceImpl.class); + + @Autowired + private LettuceConnectionFactory redisConnectionFactory; + private UserParkingplaceMappingRepo userParkingplaceMappingRepo; private MasterVanRepo masterVanRepo; private VanServicepointMappingRepo vanServicepointMappingRepo; @@ -186,6 +197,24 @@ public String getUserVanSpDetails(Integer userID, Integer providerServiceMapID) parkingPlaceLocationMap.put("blockName", obj1[6]); } resMap.put("UserLocDetails", parkingPlaceLocationMap); + + // Store camp vanID in Redis — FLW-API and TM-API read this to stamp vanID on records + try { + if (objList.size() > 0 && parkingPlaceList.size() > 0) { + Integer campVanID = (Integer) objList.get(0)[1]; // vanID is at index 1 + Integer campParkingPlaceID = (Integer) parkingPlaceList.get(0)[0]; // parkingPlaceID at index 0 + RedisConnection conn = redisConnectionFactory.getConnection(); + conn.set("camp:vanID".getBytes(), String.valueOf(campVanID).getBytes(), + Expiration.seconds(30L * 24 * 3600), SetOption.UPSERT); + conn.set("camp:parkingPlaceID".getBytes(), String.valueOf(campParkingPlaceID).getBytes(), + Expiration.seconds(30L * 24 * 3600), SetOption.UPSERT); + conn.close(); + logger.info("Camp config stored in Redis: vanID={}, parkingPlaceID={}", campVanID, campParkingPlaceID); + } + } catch (Exception e) { + logger.warn("Failed to store camp config in Redis: " + e.getMessage()); + } + // 1.1 return new Gson().toJson(resMap); } From 6cdc2a135ffbc9fed7fb7e69e5854686484f1a47 Mon Sep 17 00:00:00 2001 From: vishwab1 Date: Wed, 10 Jun 2026 11:43:43 +0530 Subject: [PATCH 02/10] feat(stoptb): add Stop TB tables to VALID_TABLES whitelist for central data sync Co-Authored-By: Claude Sonnet 4.6 --- .../dataSyncLayerCentral/DataSyncRepositoryCentral.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/iemr/mmu/service/dataSyncLayerCentral/DataSyncRepositoryCentral.java b/src/main/java/com/iemr/mmu/service/dataSyncLayerCentral/DataSyncRepositoryCentral.java index 4eadbb49..de37013a 100644 --- a/src/main/java/com/iemr/mmu/service/dataSyncLayerCentral/DataSyncRepositoryCentral.java +++ b/src/main/java/com/iemr/mmu/service/dataSyncLayerCentral/DataSyncRepositoryCentral.java @@ -73,7 +73,8 @@ private JdbcTemplate getJdbcTemplate() { "t_cancervitals", "t_cancersignandsymptoms", "t_cancerlymphnode", "t_canceroralexamination", "t_cancerbreastexamination", "t_cancerabdominalexamination", "t_cancergynecologicalexamination", "t_cancerdiagnosis", "t_cancerimageannotation", "i_beneficiaryimage", "t_stockadjustment", - "t_stocktransfer", "t_patientreturn", "t_indent", "t_indentissue", "t_indentorder", "t_saitemmapping"); + "t_stocktransfer", "t_patientreturn", "t_indent", "t_indentissue", "t_indentorder", "t_saitemmapping", + "tb_screening", "tb_suspected", "tb_confirmed_cases", "tb_stoptb_diagnostics"); private boolean isValidDatabaseIdentifierCharacter(String identifier) { return identifier != null && identifier.matches("^[a-zA-Z_][a-zA-Z0-9_]*$"); From def92de6c5f2cc3706ea99a6440473a16b85d59c Mon Sep 17 00:00:00 2001 From: vishwab1 Date: Wed, 10 Jun 2026 13:44:16 +0530 Subject: [PATCH 03/10] feat(stoptb): add tb_stoptb_general_examination and tb_stoptb_general_opd to VALID_TABLES Co-Authored-By: Claude Sonnet 4.6 --- .../dataSyncLayerCentral/DataSyncRepositoryCentral.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/iemr/mmu/service/dataSyncLayerCentral/DataSyncRepositoryCentral.java b/src/main/java/com/iemr/mmu/service/dataSyncLayerCentral/DataSyncRepositoryCentral.java index de37013a..b6e8e24e 100644 --- a/src/main/java/com/iemr/mmu/service/dataSyncLayerCentral/DataSyncRepositoryCentral.java +++ b/src/main/java/com/iemr/mmu/service/dataSyncLayerCentral/DataSyncRepositoryCentral.java @@ -74,7 +74,8 @@ private JdbcTemplate getJdbcTemplate() { "t_cancerbreastexamination", "t_cancerabdominalexamination", "t_cancergynecologicalexamination", "t_cancerdiagnosis", "t_cancerimageannotation", "i_beneficiaryimage", "t_stockadjustment", "t_stocktransfer", "t_patientreturn", "t_indent", "t_indentissue", "t_indentorder", "t_saitemmapping", - "tb_screening", "tb_suspected", "tb_confirmed_cases", "tb_stoptb_diagnostics"); + "tb_screening", "tb_suspected", "tb_confirmed_cases", "tb_stoptb_diagnostics", + "tb_stoptb_general_examination", "tb_stoptb_general_opd"); private boolean isValidDatabaseIdentifierCharacter(String identifier) { return identifier != null && identifier.matches("^[a-zA-Z_][a-zA-Z0-9_]*$"); From dc85df19a49fd9dc496bf81489e853e62eb92daa Mon Sep 17 00:00:00 2001 From: vishwab1 Date: Wed, 10 Jun 2026 14:18:56 +0530 Subject: [PATCH 04/10] fix(datasync): send central JWT as Jwttoken header instead of Authorization Central RoleAuthenticationFilter reads Jwttoken header, not Authorization. Sending as Authorization caused 401 on all central data sync calls. Co-Authored-By: Claude Sonnet 4.6 --- src/main/java/com/iemr/mmu/utils/RestTemplateUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/iemr/mmu/utils/RestTemplateUtil.java b/src/main/java/com/iemr/mmu/utils/RestTemplateUtil.java index 33556d22..218de5fb 100644 --- a/src/main/java/com/iemr/mmu/utils/RestTemplateUtil.java +++ b/src/main/java/com/iemr/mmu/utils/RestTemplateUtil.java @@ -46,7 +46,7 @@ public static HttpEntity createRequestEntity(Object body, String authori if(authorization != null && !authorization.isEmpty() && jwtToken.equalsIgnoreCase("datasync")) { - headers.add(HttpHeaders.AUTHORIZATION, authorization); + headers.add("Jwttoken", authorization); } if (jwtToken == null || jwtToken.isEmpty()) { From f770cac95e741339a2be457d21d91f140500220c Mon Sep 17 00:00:00 2001 From: vishwab1 Date: Thu, 11 Jun 2026 09:12:39 +0530 Subject: [PATCH 05/10] fix(datasync): skip JWT validation for dataSync endpoints to resolve 401 - Add /dataSync to skip list in JwtUserIdValidationFilter - Add /dataSync/** to permitAll in SecurityConfig - Revert RestTemplateUtil to send Authorization header for datasync Restores release-3.4.2 behavior for datasync path while keeping WASA security for all other endpoints. Co-Authored-By: Claude Sonnet 4.6 --- .../java/com/iemr/mmu/utils/JwtUserIdValidationFilter.java | 3 ++- src/main/java/com/iemr/mmu/utils/RestTemplateUtil.java | 2 +- src/main/java/com/iemr/mmu/utils/mapper/SecurityConfig.java | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/iemr/mmu/utils/JwtUserIdValidationFilter.java b/src/main/java/com/iemr/mmu/utils/JwtUserIdValidationFilter.java index f5068825..b197d8b4 100644 --- a/src/main/java/com/iemr/mmu/utils/JwtUserIdValidationFilter.java +++ b/src/main/java/com/iemr/mmu/utils/JwtUserIdValidationFilter.java @@ -110,7 +110,8 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo || path.startsWith(contextPath + "/user/refreshToken") || path.startsWith(contextPath + "/public") || path.equals(contextPath + "/version") - || path.equals(contextPath + "/health")) { + || path.equals(contextPath + "/health") + || path.startsWith(contextPath + "/dataSync")) { logger.info("Skipping filter for path: " + path); filterChain.doFilter(servletRequest, servletResponse); return; diff --git a/src/main/java/com/iemr/mmu/utils/RestTemplateUtil.java b/src/main/java/com/iemr/mmu/utils/RestTemplateUtil.java index 218de5fb..33556d22 100644 --- a/src/main/java/com/iemr/mmu/utils/RestTemplateUtil.java +++ b/src/main/java/com/iemr/mmu/utils/RestTemplateUtil.java @@ -46,7 +46,7 @@ public static HttpEntity createRequestEntity(Object body, String authori if(authorization != null && !authorization.isEmpty() && jwtToken.equalsIgnoreCase("datasync")) { - headers.add("Jwttoken", authorization); + headers.add(HttpHeaders.AUTHORIZATION, authorization); } if (jwtToken == null || jwtToken.isEmpty()) { diff --git a/src/main/java/com/iemr/mmu/utils/mapper/SecurityConfig.java b/src/main/java/com/iemr/mmu/utils/mapper/SecurityConfig.java index e08f1cd5..8aa4ef0b 100644 --- a/src/main/java/com/iemr/mmu/utils/mapper/SecurityConfig.java +++ b/src/main/java/com/iemr/mmu/utils/mapper/SecurityConfig.java @@ -43,6 +43,7 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .requestMatchers("/user/**").permitAll() .requestMatchers("/health").permitAll() .requestMatchers("/version").permitAll() + .requestMatchers("/dataSync/**").permitAll() .anyRequest().authenticated() ) .exceptionHandling(ex -> ex From b5aa4dc86c750ccd522336f3f7ba17dafb9059c2 Mon Sep 17 00:00:00 2001 From: vishwab1 Date: Thu, 11 Jun 2026 09:15:31 +0530 Subject: [PATCH 06/10] fix(datasync): remove PreAuthorize from datasync controller Consistent with release-3.4.2 behavior. Security maintained by @RequestMapping(headers="Authorization") requirement and skip/permitAll configuration for /dataSync/** endpoints. Co-Authored-By: Claude Sonnet 4.6 --- .../controller/dataSyncLayerCentral/MMUDataSyncVanToServer.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/iemr/mmu/controller/dataSyncLayerCentral/MMUDataSyncVanToServer.java b/src/main/java/com/iemr/mmu/controller/dataSyncLayerCentral/MMUDataSyncVanToServer.java index 2a9f3450..ea943d0e 100644 --- a/src/main/java/com/iemr/mmu/controller/dataSyncLayerCentral/MMUDataSyncVanToServer.java +++ b/src/main/java/com/iemr/mmu/controller/dataSyncLayerCentral/MMUDataSyncVanToServer.java @@ -48,7 +48,6 @@ */ @RestController @RequestMapping(value = "/dataSync", headers = "Authorization") -@PreAuthorize("hasRole('DATASYNC') || hasRole('DATA_SYNC') ") public class MMUDataSyncVanToServer { private Logger logger = LoggerFactory.getLogger(this.getClass().getSimpleName()); From b5953551459f0413d107cde8e303f11716d80698 Mon Sep 17 00:00:00 2001 From: vishwab1 Date: Thu, 11 Jun 2026 10:33:42 +0530 Subject: [PATCH 07/10] fix(datasync): fix case-insensitive VanID lookup to prevent duplicate records on central Stop TB tables use vanID (camelCase) while standard AMRIT tables use VanID (PascalCase). HashMap lookup was case-sensitive causing cleanRecord.get("VanID") to return null for Stop TB tables, resulting in duplicate check always returning 0 and every sync run inserting a new record instead of updating the existing one. Co-Authored-By: Claude Sonnet 4.6 --- .../dataSyncLayerCentral/GetDataFromVanAndSyncToDBImpl.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/iemr/mmu/service/dataSyncLayerCentral/GetDataFromVanAndSyncToDBImpl.java b/src/main/java/com/iemr/mmu/service/dataSyncLayerCentral/GetDataFromVanAndSyncToDBImpl.java index ad6f844a..cca5634d 100644 --- a/src/main/java/com/iemr/mmu/service/dataSyncLayerCentral/GetDataFromVanAndSyncToDBImpl.java +++ b/src/main/java/com/iemr/mmu/service/dataSyncLayerCentral/GetDataFromVanAndSyncToDBImpl.java @@ -367,7 +367,9 @@ private boolean performGenericTableSync(SyncUploadDataDigester syncUploadDataDig String vanSerialNo = String.valueOf(cleanRecord.get(vanAutoIncColumnName)); - String vanID = String.valueOf(cleanRecord.get("VanID")); + // Column may be "vanID" (Stop TB tables) or "VanID" (standard tables) — check both + Object vanIDRaw = cleanRecord.get("VanID") != null ? cleanRecord.get("VanID") : cleanRecord.get("vanID"); + String vanID = vanIDRaw != null ? String.valueOf(vanIDRaw) : null; int syncFacilityID = 0; cleanRecord.put("SyncedBy", syncUploadDataDigester.getSyncedBy()); From a6c41887ad43da6098ed144cc15b91eb5cb55f04 Mon Sep 17 00:00:00 2001 From: vishwab1 Date: Tue, 30 Jun 2026 10:33:39 +0530 Subject: [PATCH 08/10] fix(datasync): send plain array to Identity-API for BenGenID import Identity-API's saveGeneratedBenIDToLocalServer expects a plain JSON array, not a wrapped object with vanID and benIDList fields. Co-Authored-By: Claude Sonnet 4.6 --- .../dataSyncActivity/DownloadDataFromServerImpl.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/iemr/mmu/service/dataSyncActivity/DownloadDataFromServerImpl.java b/src/main/java/com/iemr/mmu/service/dataSyncActivity/DownloadDataFromServerImpl.java index 4f011703..d09a5b2c 100644 --- a/src/main/java/com/iemr/mmu/service/dataSyncActivity/DownloadDataFromServerImpl.java +++ b/src/main/java/com/iemr/mmu/service/dataSyncActivity/DownloadDataFromServerImpl.java @@ -348,17 +348,13 @@ public int callCentralAPIToGenerateBenIDAndimportToLocal(String requestOBJ, Stri JSONObject obj = new JSONObject(response.getBody()); if (obj != null && obj.has("data") && obj.has("statusCode") && obj.getInt("statusCode") == 200) { // Consume the response from API and call local identity api to save data - JSONObject localImportPayload = new JSONObject(); - localImportPayload.put("vanID", vanID); - localImportPayload.put("benIDList", obj.get("data")); - + String localImportPayload = obj.get("data").toString(); logger.info("Authorization: " + Authorization); logger.info("ServerAuthorization: " + ServerAuthorization); - logger.info("Payload to local import: " + localImportPayload.toString()); - + logger.info("Payload to local import: " + localImportPayload); - HttpEntity request1 = RestTemplateUtil.createRequestEntity( localImportPayload.toString(), Authorization, token); + HttpEntity request1 = RestTemplateUtil.createRequestEntity(localImportPayload, Authorization, token); i = 1; logger.info("Request to benImporturllocal: " + request1); ResponseEntity response1 = restTemplate.exchange(benImportUrlLocal, HttpMethod.POST, request1, From 0e45d64beb25a5582837d34344f6a2cb5815371d Mon Sep 17 00:00:00 2001 From: vishwab1 Date: Tue, 30 Jun 2026 10:44:34 +0530 Subject: [PATCH 09/10] fix(datasync): inject vanID into each BenGenID record before sending to Identity-API Central response does not include vanID in each record. Added loop to inject vanID from the original request into each element of the array. Co-Authored-By: Claude Sonnet 4.6 --- .../dataSyncActivity/DownloadDataFromServerImpl.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/iemr/mmu/service/dataSyncActivity/DownloadDataFromServerImpl.java b/src/main/java/com/iemr/mmu/service/dataSyncActivity/DownloadDataFromServerImpl.java index d09a5b2c..6a6d12bc 100644 --- a/src/main/java/com/iemr/mmu/service/dataSyncActivity/DownloadDataFromServerImpl.java +++ b/src/main/java/com/iemr/mmu/service/dataSyncActivity/DownloadDataFromServerImpl.java @@ -348,7 +348,12 @@ public int callCentralAPIToGenerateBenIDAndimportToLocal(String requestOBJ, Stri JSONObject obj = new JSONObject(response.getBody()); if (obj != null && obj.has("data") && obj.has("statusCode") && obj.getInt("statusCode") == 200) { // Consume the response from API and call local identity api to save data - String localImportPayload = obj.get("data").toString(); + // Inject vanID into each element since central response doesn't include it + org.json.JSONArray dataArray = obj.getJSONArray("data"); + for (int k = 0; k < dataArray.length(); k++) { + dataArray.getJSONObject(k).put("vanID", vanID); + } + String localImportPayload = dataArray.toString(); logger.info("Authorization: " + Authorization); logger.info("ServerAuthorization: " + ServerAuthorization); From f1b4731c191283f42adad619297ced291c000571 Mon Sep 17 00:00:00 2001 From: vishwab1 Date: Thu, 23 Jul 2026 09:32:21 +0530 Subject: [PATCH 10/10] fix(datasync): add tb_stoptb_visit and i_householddetails to VALID_TABLES These tables were registered in m_synctabledetail but rejected by central's sync endpoint because DataSyncRepositoryCentral's VALID_TABLES whitelist didn't include them, causing "Invalid identifiers provided" errors on every sync attempt. Co-Authored-By: Claude Sonnet 5 --- .../service/dataSyncLayerCentral/DataSyncRepositoryCentral.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/iemr/mmu/service/dataSyncLayerCentral/DataSyncRepositoryCentral.java b/src/main/java/com/iemr/mmu/service/dataSyncLayerCentral/DataSyncRepositoryCentral.java index b6e8e24e..71986301 100644 --- a/src/main/java/com/iemr/mmu/service/dataSyncLayerCentral/DataSyncRepositoryCentral.java +++ b/src/main/java/com/iemr/mmu/service/dataSyncLayerCentral/DataSyncRepositoryCentral.java @@ -75,7 +75,7 @@ private JdbcTemplate getJdbcTemplate() { "t_cancerdiagnosis", "t_cancerimageannotation", "i_beneficiaryimage", "t_stockadjustment", "t_stocktransfer", "t_patientreturn", "t_indent", "t_indentissue", "t_indentorder", "t_saitemmapping", "tb_screening", "tb_suspected", "tb_confirmed_cases", "tb_stoptb_diagnostics", - "tb_stoptb_general_examination", "tb_stoptb_general_opd"); + "tb_stoptb_general_examination", "tb_stoptb_general_opd", "tb_stoptb_visit", "i_householddetails"); private boolean isValidDatabaseIdentifierCharacter(String identifier) { return identifier != null && identifier.matches("^[a-zA-Z_][a-zA-Z0-9_]*$");