From f78e32125a940926a50bf9533a78371d8a4f7af7 Mon Sep 17 00:00:00 2001 From: PragyaTripathi990 <16pragyatripathi@gmail.com> Date: Thu, 14 May 2026 22:23:14 +0530 Subject: [PATCH 1/2] fix: resolve 5 bugs across controller, feed worker, and patient data handler - CreateHealthIdRecord: return ResponseEntity with HTTP 500 on FHIRException instead of always returning HTTP 200; replace full request/response body logging at INFO with safe summary messages to prevent patient data leaks - ClinicalFeedWorker: replace bare Exception throws with FHIRException for domain consistency; add error logging to outer catch blocks that were silently swallowing feed processing failures; guard both feed URL pointer parsings with bounds check and NumberFormatException handling to prevent crashes on malformed URLs - PatientDataGatewayServiceImpl: add @Transactional to both overloads of generatePatientProfileAMRIT_SaveTo_Mongo so MongoDB write and processed-flag update are atomic; fix copy-paste bug where all three address line OR conditions checked getAddressLine1() instead of lines 1, 2, and 3 Co-Authored-By: Claude Sonnet 4.6 --- .../healthID/CreateHealthIdRecord.java | 25 +++++++++------- .../atoms/feed/bahmni/ClinicalFeedWorker.java | 29 ++++++++++++++----- .../PatientDataGatewayServiceImpl.java | 11 ++++--- 3 files changed, 43 insertions(+), 22 deletions(-) diff --git a/src/main/java/com/wipro/fhir/controller/healthID/CreateHealthIdRecord.java b/src/main/java/com/wipro/fhir/controller/healthID/CreateHealthIdRecord.java index e2cf7b7..7a5de82 100644 --- a/src/main/java/com/wipro/fhir/controller/healthID/CreateHealthIdRecord.java +++ b/src/main/java/com/wipro/fhir/controller/healthID/CreateHealthIdRecord.java @@ -3,6 +3,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -33,9 +35,9 @@ public class CreateHealthIdRecord { */ @Operation(summary = "Map ABHA to beneficiary") @PostMapping(value = { "/mapHealthIDToBeneficiary" }) - public String mapHealthIDToBeneficiary( + public ResponseEntity mapHealthIDToBeneficiary( @RequestBody String request, @RequestHeader(value = "Authorization") String Authorization) { - logger.info("NDHM_FHIR Map ABHA to beneficiary API request " + request); + logger.info("NDHM_FHIR Map ABHA to beneficiary API request received"); OutputResponse response = new OutputResponse(); try { if (request != null) { @@ -45,16 +47,18 @@ public String mapHealthIDToBeneficiary( throw new FHIRException("NDHM_FHIR Empty request object"); } catch (FHIRException e) { response.setError(5000, e.getMessage()); - logger.error(e.toString()); + logger.error("NDHM_FHIR error mapping ABHA to beneficiary: {}", e.getMessage()); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response.toString()); } - logger.info("NDHM_FHIR Map ABHA to beneficiary API response " + response.toString()); - return response.toString(); + logger.info("NDHM_FHIR Map ABHA to beneficiary API response sent successfully"); + return ResponseEntity.ok(response.toString()); } + @Operation(summary = "Add New health ID record to healthId table") @PostMapping(value = { "/addHealthIdRecord" }) - public String addRecordToHealthIdTable( + public ResponseEntity addRecordToHealthIdTable( @RequestBody String request, @RequestHeader(value = "Authorization") String Authorization) { - logger.info("NDHM_FHIR API to add the new health record coming from FLW request " + request); + logger.info("NDHM_FHIR API to add new health record request received"); OutputResponse response = new OutputResponse(); try { if (request != null) { @@ -64,10 +68,11 @@ public String addRecordToHealthIdTable( throw new FHIRException("NDHM_FHIR Empty request object"); } catch (FHIRException e) { response.setError(5000, e.getMessage()); - logger.error(e.toString()); + logger.error("NDHM_FHIR error adding health record: {}", e.getMessage()); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response.toString()); } - logger.info("NDHM_FHIR API to add the new health record coming from FLW response " + response.toString()); - return response.toString(); + logger.info("NDHM_FHIR API to add new health record response sent successfully"); + return ResponseEntity.ok(response.toString()); } diff --git a/src/main/java/com/wipro/fhir/service/atoms/feed/bahmni/ClinicalFeedWorker.java b/src/main/java/com/wipro/fhir/service/atoms/feed/bahmni/ClinicalFeedWorker.java index e86d219..e885dbf 100644 --- a/src/main/java/com/wipro/fhir/service/atoms/feed/bahmni/ClinicalFeedWorker.java +++ b/src/main/java/com/wipro/fhir/service/atoms/feed/bahmni/ClinicalFeedWorker.java @@ -92,7 +92,14 @@ public String encounterFeedManager() throws IllegalArgumentException, FeedExcept if (feed != null && feed.getId() != null && feed.getLinkSelf() != null) { String[] arr = feed.getLinkSelf().split("/"); - pointer = Integer.parseInt(arr[arr.length - 1]); + if (arr.length > 0 && !arr[arr.length - 1].isEmpty()) { + try { + pointer = Integer.parseInt(arr[arr.length - 1]); + } catch (NumberFormatException e) { + logger.error("Invalid feed page pointer in linkSelf URL: {}", feed.getLinkSelf()); + pointer = atomsFeedStartPage; + } + } } else if (feed == null || (feed.getLinkSelf() == null && feed.getLinkVia() == null && feed.getLinkPrevArchive() == null)) { @@ -173,10 +180,10 @@ public List readPatientEncounterFeeds(String parent feedPageCompleted); returnList.add(encounterFullRepresentation); } else - throw new Exception("Error in saving clinical data for patient : " + throw new FHIRException("Error in saving clinical data for patient : " + encounterFullRepresentation.getPatientId()); } else - throw new Exception("Patient ID not available"); + throw new FHIRException("Patient ID not available"); } } @@ -209,23 +216,29 @@ public List readPatientEncounterFeeds(String parent if (link.getRel() != null && link.getHref() != null && link.getRel().equalsIgnoreCase("next-archive")) { String[] arr = link.getHref().split("/"); - tempPointer = Integer.parseInt(arr[arr.length - 1]); + if (arr.length > 0 && !arr[arr.length - 1].isEmpty()) { + try { + tempPointer = Integer.parseInt(arr[arr.length - 1]); + } catch (NumberFormatException e) { + logger.error("Invalid next-archive pointer in feed URL: {}", link.getHref()); + } + } break; - } } pointer = tempPointer; } catch (IllegalArgumentException e) { + logger.error("Invalid argument while processing feed page {}: {}", pointer, e.getMessage()); pointer = 0; - } catch (FeedException e) { + logger.error("Feed parsing error on page {}: {}", pointer, e.getMessage()); pointer = 0; - } catch (IOException e) { + logger.error("IO error reading feed page {}: {}", pointer, e.getMessage()); pointer = 0; - } catch (Exception e) { + logger.error("Unexpected error processing feed page {}: {}", pointer, e.getMessage()); pointer = 0; } nextFeed = true; diff --git a/src/main/java/com/wipro/fhir/service/patient_data_handler/PatientDataGatewayServiceImpl.java b/src/main/java/com/wipro/fhir/service/patient_data_handler/PatientDataGatewayServiceImpl.java index 7586ec3..23771ae 100644 --- a/src/main/java/com/wipro/fhir/service/patient_data_handler/PatientDataGatewayServiceImpl.java +++ b/src/main/java/com/wipro/fhir/service/patient_data_handler/PatientDataGatewayServiceImpl.java @@ -35,6 +35,7 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import com.google.gson.Gson; import com.wipro.fhir.data.healthID.BenHealthIDMapping; @@ -89,8 +90,9 @@ public List feedPatientProfileToMo } @Override + @Transactional(rollbackFor = FHIRException.class) public String generatePatientProfileAMRIT_SaveTo_Mongo(String Authorization) throws FHIRException { - + List ppList = new ArrayList<>(); List resultSetList = tRG_PatientResourceData_Repo @@ -112,9 +114,10 @@ public String generatePatientProfileAMRIT_SaveTo_Mongo(String Authorization) thr } @Override + @Transactional(rollbackFor = FHIRException.class) public String generatePatientProfileAMRIT_SaveTo_Mongo(String Authorization, ResourceRequestHandler resourceRequestHandler) throws FHIRException { - + List ppList = new ArrayList<>(); List resultSetList = tRG_PatientResourceData_Repo @@ -291,8 +294,8 @@ private PatientDemographicModel_NDHM_Patient_Profile generatePatientProfileFromP address.setState(pd.getI_bendemographics().getStateName()); if (pd.getI_bendemographics().getAddressLine1() != null - || pd.getI_bendemographics().getAddressLine1() != null - || pd.getI_bendemographics().getAddressLine1() != null) { + || pd.getI_bendemographics().getAddressLine2() != null + || pd.getI_bendemographics().getAddressLine3() != null) { String address1 = (pd.getI_bendemographics().getAddressLine1() != null) ? pd.getI_bendemographics().getAddressLine1() : ""; From 788398e2aa839da90b6014c5acb740f465238023 Mon Sep 17 00:00:00 2001 From: PragyaTripathi990 <16pragyatripathi@gmail.com> Date: Fri, 15 May 2026 12:00:39 +0530 Subject: [PATCH 2/2] fix: address CodeRabbit review comments on PR #145 - CreateHealthIdRecord: add catch(Exception e) to both endpoints so all unhandled runtime exceptions return a consistent 500 OutputResponse instead of leaking unformatted errors to clients - ClinicalFeedWorker: guard parsed pointer against non-positive values and empty path segments at both parsing sites; fallback to atomsFeedStartPage so the feed loop never silently exits on bad URLs - PatientDataGatewayServiceImpl: wrap MySQL updateProcessedFlag call in try/catch on both overloads; log affected IDs and re-throw on failure to surface cross-store inconsistency (Mongo+MySQL) explicitly Co-Authored-By: Claude Sonnet 4.6 --- .../healthID/CreateHealthIdRecord.java | 8 +++++ .../atoms/feed/bahmni/ClinicalFeedWorker.java | 16 ++++++++-- .../PatientDataGatewayServiceImpl.java | 32 ++++++++++++++----- 3 files changed, 45 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/wipro/fhir/controller/healthID/CreateHealthIdRecord.java b/src/main/java/com/wipro/fhir/controller/healthID/CreateHealthIdRecord.java index 7a5de82..f9adb2a 100644 --- a/src/main/java/com/wipro/fhir/controller/healthID/CreateHealthIdRecord.java +++ b/src/main/java/com/wipro/fhir/controller/healthID/CreateHealthIdRecord.java @@ -49,6 +49,10 @@ public ResponseEntity mapHealthIDToBeneficiary( response.setError(5000, e.getMessage()); logger.error("NDHM_FHIR error mapping ABHA to beneficiary: {}", e.getMessage()); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response.toString()); + } catch (Exception e) { + response.setError(5000, "An unexpected error occurred"); + logger.error("NDHM_FHIR unexpected error mapping ABHA to beneficiary", e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response.toString()); } logger.info("NDHM_FHIR Map ABHA to beneficiary API response sent successfully"); return ResponseEntity.ok(response.toString()); @@ -70,6 +74,10 @@ public ResponseEntity addRecordToHealthIdTable( response.setError(5000, e.getMessage()); logger.error("NDHM_FHIR error adding health record: {}", e.getMessage()); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response.toString()); + } catch (Exception e) { + response.setError(5000, "An unexpected error occurred"); + logger.error("NDHM_FHIR unexpected error adding health record", e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response.toString()); } logger.info("NDHM_FHIR API to add new health record response sent successfully"); return ResponseEntity.ok(response.toString()); diff --git a/src/main/java/com/wipro/fhir/service/atoms/feed/bahmni/ClinicalFeedWorker.java b/src/main/java/com/wipro/fhir/service/atoms/feed/bahmni/ClinicalFeedWorker.java index e885dbf..bf94133 100644 --- a/src/main/java/com/wipro/fhir/service/atoms/feed/bahmni/ClinicalFeedWorker.java +++ b/src/main/java/com/wipro/fhir/service/atoms/feed/bahmni/ClinicalFeedWorker.java @@ -94,11 +94,14 @@ public String encounterFeedManager() throws IllegalArgumentException, FeedExcept String[] arr = feed.getLinkSelf().split("/"); if (arr.length > 0 && !arr[arr.length - 1].isEmpty()) { try { - pointer = Integer.parseInt(arr[arr.length - 1]); + int parsedPointer = Integer.parseInt(arr[arr.length - 1]); + pointer = parsedPointer > 0 ? parsedPointer : atomsFeedStartPage; } catch (NumberFormatException e) { logger.error("Invalid feed page pointer in linkSelf URL: {}", feed.getLinkSelf()); pointer = atomsFeedStartPage; } + } else { + pointer = atomsFeedStartPage; } } else if (feed == null @@ -210,7 +213,7 @@ public List readPatientEncounterFeeds(String parent } } - int tempPointer = 0; + int tempPointer = pointer; for (SyndLink link : feedLink) { if (link.getRel() != null && link.getHref() != null @@ -218,10 +221,17 @@ public List readPatientEncounterFeeds(String parent String[] arr = link.getHref().split("/"); if (arr.length > 0 && !arr[arr.length - 1].isEmpty()) { try { - tempPointer = Integer.parseInt(arr[arr.length - 1]); + int parsedPointer = Integer.parseInt(arr[arr.length - 1]); + if (parsedPointer > 0) { + tempPointer = parsedPointer; + } else { + logger.error("Non-positive next-archive pointer in feed URL: {}", link.getHref()); + } } catch (NumberFormatException e) { logger.error("Invalid next-archive pointer in feed URL: {}", link.getHref()); } + } else { + logger.error("Missing next-archive pointer in feed URL: {}", link.getHref()); } break; } diff --git a/src/main/java/com/wipro/fhir/service/patient_data_handler/PatientDataGatewayServiceImpl.java b/src/main/java/com/wipro/fhir/service/patient_data_handler/PatientDataGatewayServiceImpl.java index 23771ae..831f13d 100644 --- a/src/main/java/com/wipro/fhir/service/patient_data_handler/PatientDataGatewayServiceImpl.java +++ b/src/main/java/com/wipro/fhir/service/patient_data_handler/PatientDataGatewayServiceImpl.java @@ -99,15 +99,23 @@ public String generatePatientProfileAMRIT_SaveTo_Mongo(String Authorization) thr .getByProcessedOrderByCreatedDateLimit20(); ppList = generatePatientProfileFromAMRIT(Authorization, resultSetList); - ppList = feedPatientProfileToMongoDB(ppList); + List savedList = feedPatientProfileToMongoDB(ppList); - if (ppList != null && ppList.size() > 0) { + if (savedList != null && savedList.size() > 0) { List ids = new ArrayList<>(); ids.add((long) 0); - for (PatientDemographicModel_NDHM_Patient_Profile pp : ppList) { + for (PatientDemographicModel_NDHM_Patient_Profile pp : savedList) { ids.add(pp.getTriggerTableAIId()); } - tRG_PatientResourceData_Repo.updateProcessedFlagForProfileCreated(ids); + try { + tRG_PatientResourceData_Repo.updateProcessedFlagForProfileCreated(ids); + } catch (Exception e) { + // Mongo save succeeded but MySQL update failed — stores are now inconsistent. + // Log affected IDs so ops can manually reconcile; re-throw to signal failure. + logger.error("MySQL update failed after Mongo save — inconsistent state. Affected IDs: {}. Error: {}", ids, e.getMessage()); + throw new FHIRException("Failed to update processed flag after Mongo save. " + e.getMessage()); + } + ppList = savedList; } return new Gson().toJson(ppList); @@ -125,15 +133,23 @@ public String generatePatientProfileAMRIT_SaveTo_Mongo(String Authorization, ppList = generatePatientProfileFromAMRIT(Authorization, resultSetList); - ppList = feedPatientProfileToMongoDB(ppList); + List savedList = feedPatientProfileToMongoDB(ppList); - if (ppList != null && ppList.size() > 0) { + if (savedList != null && savedList.size() > 0) { List ids = new ArrayList<>(); ids.add((long) 0); - for (PatientDemographicModel_NDHM_Patient_Profile pp : ppList) { + for (PatientDemographicModel_NDHM_Patient_Profile pp : savedList) { ids.add(pp.getTriggerTableAIId()); } - tRG_PatientResourceData_Repo.updateProcessedFlagForProfileCreated(ids); + try { + tRG_PatientResourceData_Repo.updateProcessedFlagForProfileCreated(ids); + } catch (Exception e) { + // Mongo save succeeded but MySQL update failed — stores are now inconsistent. + // Log affected IDs so ops can manually reconcile; re-throw to signal failure. + logger.error("MySQL update failed after Mongo save — inconsistent state. Affected IDs: {}. Error: {}", ids, e.getMessage()); + throw new FHIRException("Failed to update processed flag after Mongo save. " + e.getMessage()); + } + ppList = savedList; } return new Gson().toJson(ppList);