Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -33,9 +35,9 @@ public class CreateHealthIdRecord {
*/
@Operation(summary = "Map ABHA to beneficiary")
@PostMapping(value = { "/mapHealthIDToBeneficiary" })
public String mapHealthIDToBeneficiary(
public ResponseEntity<String> mapHealthIDToBeneficiary(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Find usages of mapHealthIDToBeneficiary that might be affected by the signature change

# Search for method calls in Java/test files
rg -n --type java -C3 'mapHealthIDToBeneficiary\s*\(' -g '!CreateHealthIdRecord.java'

# Search for REST client calls or URL references
rg -n --type java -C3 '/mapHealthIDToBeneficiary' -g '!CreateHealthIdRecord.java'

Repository: PSMRI/FHIR-API

Length of output: 2734


🏁 Script executed:

#!/bin/bash

# First, find and read the CreateHealthIdRecord.java file to see the actual signature
echo "=== Finding CreateHealthIdRecord.java ==="
fd -name "CreateHealthIdRecord.java"

# Then read the file around line 38
echo -e "\n=== Reading the method signature ==="
sed -n '35,50p' "$(fd -name 'CreateHealthIdRecord.java')"

# Search for test files that might call this method
echo -e "\n=== Searching for test callers ==="
rg -n --type java 'mapHealthIDToBeneficiary' --iglob '*test*' --iglob '*Test*'

# Search for all references to mapHealthIDToBeneficiary across all file types
echo -e "\n=== Searching all references (including properties, configs, REST clients) ==="
rg -n 'mapHealthIDToBeneficiary' --type-list
rg -n 'mapHealthIDToBeneficiary'

Repository: PSMRI/FHIR-API

Length of output: 2005


🏁 Script executed:

#!/bin/bash

# Read the CreateHealthIdRecord.java file
echo "=== CreateHealthIdRecord.java content ==="
cat -n "src/main/java/com/wipro/fhir/controller/healthID/CreateHealthIdRecord.java" | head -80

# Check if there are any test files for this controller
echo -e "\n=== Looking for test files ==="
fd -type f "CreateHealthIdRecord"
fd -type f "Test" -path "*healthID*"

Repository: PSMRI/FHIR-API

Length of output: 4217


Breaking REST API change requires external client validation.

The return type changed from String to ResponseEntity<String> (line 38). This is a breaking change for any external REST clients consuming this endpoint. While the underlying service layer (HealthIDService) remains unchanged, REST clients calling /healthIDRecord/mapHealthIDToBeneficiary will receive a different response structure and must be updated accordingly. Ensure external client integrations and API documentation are updated to reflect this change.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/wipro/fhir/controller/healthID/CreateHealthIdRecord.java`
at line 38, The method mapHealthIDToBeneficiary in CreateHealthIdRecord was
changed from returning String to ResponseEntity<String>, which is a breaking API
change; either revert the controller signature back to String and return the raw
String from HealthIDService (preserve behavior of
HealthIDService.mapHealthIDToBeneficiary) or, if you must return ResponseEntity,
add a clear compatibility layer (e.g., keep the old endpoint/signature or
provide an overloaded endpoint) and update API docs and external client
integration notes; locate the method mapHealthIDToBeneficiary in
CreateHealthIdRecord and adjust the return type/return value accordingly and
ensure API documentation (OpenAPI/Swagger) and any client-facing contract are
updated to reflect the chosen change.

@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) {
Expand All @@ -45,16 +47,22 @@ 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());
} 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 " + 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<String> 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) {
Expand All @@ -64,10 +72,15 @@ 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());
} 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 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());
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,17 @@
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 {
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;
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

} else if (feed == null
|| (feed.getLinkSelf() == null && feed.getLinkVia() == null && feed.getLinkPrevArchive() == null)) {
Expand Down Expand Up @@ -173,10 +183,10 @@
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");
}

}
Expand All @@ -203,29 +213,42 @@
}
}

int tempPointer = 0;
int tempPointer = pointer;
for (SyndLink link : feedLink) {

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 {

Check warning on line 223 in src/main/java/com/wipro/fhir/service/atoms/feed/bahmni/ClinicalFeedWorker.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Extract this nested try block into a separate method.

See more on https://sonarcloud.io/project/issues?id=PSMRI_FHIR-API&issues=AZ4na_EkPVTzyL34R-QI&open=AZ4na_EkPVTzyL34R-QI&pullRequest=145
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;

}
}
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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -89,48 +90,66 @@
}

@Override
@Transactional(rollbackFor = FHIRException.class)
public String generatePatientProfileAMRIT_SaveTo_Mongo(String Authorization) throws FHIRException {
Comment on lines +93 to 94
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Transaction manager and `@EnableTransactionManagement` declarations =="
rg -n -C3 '@EnableTransactionManagement|PlatformTransactionManager|MongoTransactionManager|JpaTransactionManager|ChainedTransactionManager|transactionManagerRef' $(fd -t f '.*\.java$' src || true)

echo
echo "== Mongo config presence (templates/factories/transaction manager) =="
rg -n -C3 'MongoTemplate|MongoDatabaseFactory|MongoTransactionManager|AbstractMongoClientConfiguration' $(fd -t f '.*\.java$' src || true)

echo
echo "== savePatientProfileDataToMongo implementation and annotations =="
rg -n -C5 'savePatientProfileDataToMongo\s*\(' $(fd -t f '.*\.java$' src || true)

echo
echo "== Repository update method definition =="
rg -n -C5 'updateProcessedFlagForProfileCreated\s*\(' $(fd -t f '.*\.java$' src || true)

Repository: PSMRI/FHIR-API

Length of output: 11637


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== FhirApiApplication.java full content ==="
cat -n src/main/java/com/wipro/fhir/FhirApiApplication.java

echo
echo "=== Application configuration files ==="
fd -t f -e properties -e yml -e yaml src || true | head -20
rg -l 'application' . --max-depth 2 2>/dev/null | grep -E '\.(properties|yml|yaml)$' | head -20

echo
echo "=== Check application.properties for spring.data.mongodb and transaction configs ==="
fd -t f 'application.*properties' . | xargs cat 2>/dev/null | grep -i -E 'mongodb|transaction|jpa' || echo "No matches"

echo
echo "=== Check application.yml/yaml for spring.data.mongodb and transaction configs ==="
fd -t f 'application.*\.ya?ml' . | xargs cat 2>/dev/null | grep -A5 -B5 -i -E 'mongodb|transaction|jpa' || echo "No matches"

echo
echo "=== Search for `@Bean` definitions related to transaction manager ==="
rg -n '@Bean.*Transaction|TransactionManager|MongoTemplate' --type java src || echo "No additional matches"

Repository: PSMRI/FHIR-API

Length of output: 2853


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Find all .properties and .yml files ==="
find . -type f \( -name '*.properties' -o -name '*.yml' -o -name '*.yaml' \) 2>/dev/null | head -20

echo
echo "=== Check src/main/resources directory ==="
ls -la src/main/resources/ 2>/dev/null || echo "Directory not found"

echo
echo "=== Content of application.properties if it exists ==="
cat src/main/resources/application.properties 2>/dev/null || echo "File not found"

echo
echo "=== Content of application.yml if it exists ==="
cat src/main/resources/application.yml 2>/dev/null || echo "File not found"

echo
echo "=== Check for Spring Boot configuration in pom.xml (spring-data-mongodb vs spring-boot-starter-data-jpa) ==="
rg -A2 -B2 'spring-data-mongodb|spring-boot-starter-data-jpa|spring-boot-starter-data-rest' pom.xml 2>/dev/null || echo "Not found in pom.xml"

echo
echo "=== Check if mongoTemplate operations are wrapped in any TX manager binding ==="
rg -n 'mongoTemplate\.|transactionManager\|@Transactional.*mongodb' src --type java | head -20

Repository: PSMRI/FHIR-API

Length of output: 5970


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Full pom.xml search for mongodb dependency ==="
rg 'mongodb|mongo' pom.xml -i

echo
echo "=== Check environment property files for MongoDB config ==="
echo "--- common_example.properties ---"
cat src/main/environment/common_example.properties | grep -i -E 'mongo|mongodb' || echo "No MongoDB config"

echo
echo "--- common_ci.properties ---"
cat src/main/environment/common_ci.properties | grep -i -E 'mongo|mongodb' || echo "No MongoDB config"

echo
echo "--- common_docker.properties ---"
cat src/main/environment/common_docker.properties | grep -i -E 'mongo|mongodb' || echo "No MongoDB config"

echo
echo "=== Check if MongoTemplate bean in FhirApiApplication is actually used or if it fails ==="
rg -n 'MongoTemplate|mongoTemplate' src --type java | grep -v '://' | head -30

Repository: PSMRI/FHIR-API

Length of output: 1638


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Search for `@EnableTransactionManagement` or explicit MongoTransactionManager bean ==="
rg -n '@EnableTransactionManagement|@Bean.*MongoTransactionManager|new MongoTransactionManager' src --type java

echo
echo "=== Check savePatientProfileDataToMongo full implementation ==="
sed -n '654,670p' src/main/java/com/wipro/fhir/service/common/CommonServiceImpl.java

echo
echo "=== Check the transactional method calling savePatientProfileDataToMongo (around line 87) ==="
sed -n '70,95p' src/main/java/com/wipro/fhir/service/patient_data_handler/PatientDataGatewayServiceImpl.java

echo
echo "=== Verify if `@Transactional` on service method covers nested Mongo operations ==="
rg -B5 '@Override' src/main/java/com/wipro/fhir/service/patient_data_handler/PatientDataGatewayServiceImpl.java | grep -A5 'generatePatientProfileAMRIT_SaveTo_Mongo'

Repository: PSMRI/FHIR-API

Length of output: 147


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Check savePatientProfileDataToMongo full implementation ==="
sed -n '654,670p' src/main/java/com/wipro/fhir/service/common/CommonServiceImpl.java

echo
echo "=== Check the transactional method calling savePatientProfileDataToMongo (lines 70-95) ==="
sed -n '70,95p' src/main/java/com/wipro/fhir/service/patient_data_handler/PatientDataGatewayServiceImpl.java

echo
echo "=== Check PatientDataGatewayServiceImpl around the `@Transactional` method (lines 93-114) ==="
sed -n '93,114p' src/main/java/com/wipro/fhir/service/patient_data_handler/PatientDataGatewayServiceImpl.java

Repository: PSMRI/FHIR-API

Length of output: 3181


@Transactional here does not provide atomic rollback across MongoDB and MySQL stores.

These methods span Mongo writes (savePatientProfileDataToMongomongoRepository.saveAll()) and MySQL updates (updateProcessedFlagForProfileCreated). The savePatientProfileDataToMongo() method has no transaction management, while only the MySQL repository method carries @Transactional. This leaves the Mongo operation unmanaged, risking inconsistent state: if the Mongo save succeeds but the MySQL update fails (or vice versa), one store commits while the other doesn't.

No @EnableTransactionManagement, MongoTransactionManager bean, or ChainedTransactionManager configuration was found. Without explicit cross-store transaction coordination, the @Transactional annotation on the service method only binds to the JPA transaction manager and cannot guarantee atomicity.

Also applies to: lines 117–119

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/wipro/fhir/service/patient_data_handler/PatientDataGatewayServiceImpl.java`
around lines 93 - 94, The service method
generatePatientProfileAMRIT_SaveTo_Mongo currently uses `@Transactional` but only
the JPA transaction manager is active, so Mongo writes from
savePatientProfileDataToMongo and MySQL updates via
updateProcessedFlagForProfileCreated are not coordinated and can leave stores
inconsistent; fix by either (A) enabling multi-store transactions: add
`@EnableTransactionManagement`, register a MongoTransactionManager bean (MongoDB
must support transactions i.e., replica set), create a ChainedTransactionManager
combining the JpaTransactionManager and MongoTransactionManager and annotate
generatePatientProfileAMRIT_SaveTo_Mongo to use that chained manager so both
mongoRepository.saveAll(...) and updateProcessedFlagForProfileCreated run in one
atomic transaction, or (B) implement explicit compensation/error-handling: wrap
savePatientProfileDataToMongo and updateProcessedFlagForProfileCreated in a
try/catch and roll back the completed store change manually if the other fails;
apply the same fix to the other affected method(s) referenced in the comment.


List<PatientDemographicModel_NDHM_Patient_Profile> ppList = new ArrayList<>();

List<TRG_PatientResourceData> resultSetList = tRG_PatientResourceData_Repo
.getByProcessedOrderByCreatedDateLimit20();
ppList = generatePatientProfileFromAMRIT(Authorization, resultSetList);

ppList = feedPatientProfileToMongoDB(ppList);
List<PatientDemographicModel_NDHM_Patient_Profile> savedList = feedPatientProfileToMongoDB(ppList);

if (ppList != null && ppList.size() > 0) {
if (savedList != null && savedList.size() > 0) {

Check warning on line 104 in src/main/java/com/wipro/fhir/service/patient_data_handler/PatientDataGatewayServiceImpl.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use isEmpty() to check whether the collection is empty or not.

See more on https://sonarcloud.io/project/issues?id=PSMRI_FHIR-API&issues=AZ4qVXXJhHNRY-AfB0Hg&open=AZ4qVXXJhHNRY-AfB0Hg&pullRequest=145
List<Long> 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);
}

@Override
@Transactional(rollbackFor = FHIRException.class)
public String generatePatientProfileAMRIT_SaveTo_Mongo(String Authorization,
ResourceRequestHandler resourceRequestHandler) throws FHIRException {

List<PatientDemographicModel_NDHM_Patient_Profile> ppList = new ArrayList<>();

List<TRG_PatientResourceData> resultSetList = tRG_PatientResourceData_Repo
.getByBenIdLatestRecord(resourceRequestHandler.getBeneficiaryID());

ppList = generatePatientProfileFromAMRIT(Authorization, resultSetList);

ppList = feedPatientProfileToMongoDB(ppList);
List<PatientDemographicModel_NDHM_Patient_Profile> savedList = feedPatientProfileToMongoDB(ppList);

if (ppList != null && ppList.size() > 0) {
if (savedList != null && savedList.size() > 0) {

Check warning on line 138 in src/main/java/com/wipro/fhir/service/patient_data_handler/PatientDataGatewayServiceImpl.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use isEmpty() to check whether the collection is empty or not.

See more on https://sonarcloud.io/project/issues?id=PSMRI_FHIR-API&issues=AZ4qVXXJhHNRY-AfB0Hh&open=AZ4qVXXJhHNRY-AfB0Hh&pullRequest=145
List<Long> 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);
Expand Down Expand Up @@ -291,8 +310,8 @@
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()
: "";
Expand Down