From 10a5c393a2bcd2569886ff6effb3167b60b0c2e4 Mon Sep 17 00:00:00 2001 From: chibongho Date: Tue, 26 Nov 2024 16:53:38 -0500 Subject: [PATCH] =?UTF-8?q?FM2-648=20fix=20NPE=20when=20querying=20decease?= =?UTF-8?q?d=20patient=20with=20openmrsPatients=20n=E2=80=A6=20(#553)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../openmrs/module/fhir2/FhirConstants.java | 8 ++ .../api/dao/impl/FhirPatientDaoImpl.java | 5 +- .../fhir2/api/dao/impl/FhirPersonDaoImpl.java | 3 +- .../param/OpenmrsPatientSearchParams.java | 2 +- .../api/search/param/PatientSearchParams.java | 8 +- .../api/search/param/PersonSearchParams.java | 4 +- .../api/search/PatientSearchQueryTest.java | 88 +++++++++++++++++++ .../module/fhir2/matchers/FhirMatchers.java | 20 +++++ .../fhir2/matchers/IsDeceasedMatcher.java | 39 ++++++++ 9 files changed, 167 insertions(+), 10 deletions(-) create mode 100644 api/src/test/java/org/openmrs/module/fhir2/matchers/FhirMatchers.java create mode 100644 api/src/test/java/org/openmrs/module/fhir2/matchers/IsDeceasedMatcher.java diff --git a/api/src/main/java/org/openmrs/module/fhir2/FhirConstants.java b/api/src/main/java/org/openmrs/module/fhir2/FhirConstants.java index f989d4df73..5da7dbe78d 100644 --- a/api/src/main/java/org/openmrs/module/fhir2/FhirConstants.java +++ b/api/src/main/java/org/openmrs/module/fhir2/FhirConstants.java @@ -225,6 +225,14 @@ private FhirConstants() { public static final String FAMILY_PROPERTY = "family.property"; + public static final String GENDER_PROPERTY = "gender"; + + public static final String BIRTHDATE_PROPERTY = "birthdate"; + + public static final String DEATHDATE_PROPERTY = "deathDate"; + + public static final String DECEASED_PROPERTY = "dead"; + public static final String CITY_PROPERTY = "city.property"; public static final String COUNTRY_PROPERTY = "country.property"; diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirPatientDaoImpl.java b/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirPatientDaoImpl.java index e329861088..f7d1d9f166 100644 --- a/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirPatientDaoImpl.java +++ b/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirPatientDaoImpl.java @@ -90,8 +90,9 @@ protected void setupSearchParams(Criteria criteria, SearchParameterMap theParams handleNames(criteria, entry.getValue()); break; case FhirConstants.GENDER_SEARCH_HANDLER: - entry.getValue().forEach( - p -> handleGender(p.getPropertyName(), (TokenAndListParam) p.getParam()).ifPresent(criteria::add)); + entry.getValue() + .forEach(p -> handleGender(FhirConstants.GENDER_PROPERTY, (TokenAndListParam) p.getParam()) + .ifPresent(criteria::add)); break; case FhirConstants.IDENTIFIER_SEARCH_HANDLER: entry.getValue() diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirPersonDaoImpl.java b/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirPersonDaoImpl.java index df99e2f700..19d1432a0a 100644 --- a/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirPersonDaoImpl.java +++ b/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirPersonDaoImpl.java @@ -57,7 +57,8 @@ protected void setupSearchParams(Criteria criteria, SearchParameterMap theParams break; case FhirConstants.GENDER_SEARCH_HANDLER: entry.getValue().forEach( - param -> handleGender("gender", (TokenAndListParam) param.getParam()).ifPresent(criteria::add)); + param -> handleGender(FhirConstants.GENDER_PROPERTY, (TokenAndListParam) param.getParam()) + .ifPresent(criteria::add)); break; case FhirConstants.DATE_RANGE_SEARCH_HANDLER: entry.getValue().forEach( diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/search/param/OpenmrsPatientSearchParams.java b/api/src/main/java/org/openmrs/module/fhir2/api/search/param/OpenmrsPatientSearchParams.java index 161e887b0a..e43e8cedff 100644 --- a/api/src/main/java/org/openmrs/module/fhir2/api/search/param/OpenmrsPatientSearchParams.java +++ b/api/src/main/java/org/openmrs/module/fhir2/api/search/param/OpenmrsPatientSearchParams.java @@ -70,7 +70,7 @@ public SearchParameterMap toSearchParameterMap() { .addParameter(FhirConstants.GENDER_SEARCH_HANDLER, "gender", getGender()) .addParameter(FhirConstants.DATE_RANGE_SEARCH_HANDLER, "birthdate", getBirthDate()) .addParameter(FhirConstants.DATE_RANGE_SEARCH_HANDLER, "deathDate", getDeathDate()) - .addParameter(FhirConstants.BOOLEAN_SEARCH_HANDLER, getDeceased()) + .addParameter(FhirConstants.BOOLEAN_SEARCH_HANDLER, "dead", getDeceased()) .addParameter(FhirConstants.ADDRESS_SEARCH_HANDLER, FhirConstants.CITY_PROPERTY, getCity()) .addParameter(FhirConstants.ADDRESS_SEARCH_HANDLER, FhirConstants.STATE_PROPERTY, getState()) .addParameter(FhirConstants.ADDRESS_SEARCH_HANDLER, FhirConstants.POSTAL_CODE_PROPERTY, getPostalCode()) diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/search/param/PatientSearchParams.java b/api/src/main/java/org/openmrs/module/fhir2/api/search/param/PatientSearchParams.java index fe45f7f663..9225fa1378 100644 --- a/api/src/main/java/org/openmrs/module/fhir2/api/search/param/PatientSearchParams.java +++ b/api/src/main/java/org/openmrs/module/fhir2/api/search/param/PatientSearchParams.java @@ -81,10 +81,10 @@ public SearchParameterMap toSearchParameterMap() { .addParameter(FhirConstants.NAME_SEARCH_HANDLER, FhirConstants.GIVEN_PROPERTY, getGiven()) .addParameter(FhirConstants.NAME_SEARCH_HANDLER, FhirConstants.FAMILY_PROPERTY, getFamily()) .addParameter(FhirConstants.IDENTIFIER_SEARCH_HANDLER, getIdentifier()) - .addParameter(FhirConstants.GENDER_SEARCH_HANDLER, "gender", getGender()) - .addParameter(FhirConstants.DATE_RANGE_SEARCH_HANDLER, "birthdate", getBirthDate()) - .addParameter(FhirConstants.DATE_RANGE_SEARCH_HANDLER, "deathDate", getDeathDate()) - .addParameter(FhirConstants.BOOLEAN_SEARCH_HANDLER, getDeceased()) + .addParameter(FhirConstants.GENDER_SEARCH_HANDLER, FhirConstants.GENDER_PROPERTY, getGender()) + .addParameter(FhirConstants.DATE_RANGE_SEARCH_HANDLER, FhirConstants.BIRTHDATE_PROPERTY, getBirthDate()) + .addParameter(FhirConstants.DATE_RANGE_SEARCH_HANDLER, FhirConstants.DEATHDATE_PROPERTY, getDeathDate()) + .addParameter(FhirConstants.BOOLEAN_SEARCH_HANDLER, FhirConstants.DECEASED_PROPERTY, getDeceased()) .addParameter(FhirConstants.ADDRESS_SEARCH_HANDLER, FhirConstants.CITY_PROPERTY, getCity()) .addParameter(FhirConstants.ADDRESS_SEARCH_HANDLER, FhirConstants.STATE_PROPERTY, getState()) .addParameter(FhirConstants.ADDRESS_SEARCH_HANDLER, FhirConstants.POSTAL_CODE_PROPERTY, getPostalCode()) diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/search/param/PersonSearchParams.java b/api/src/main/java/org/openmrs/module/fhir2/api/search/param/PersonSearchParams.java index 8a73d5781f..c4c2124252 100644 --- a/api/src/main/java/org/openmrs/module/fhir2/api/search/param/PersonSearchParams.java +++ b/api/src/main/java/org/openmrs/module/fhir2/api/search/param/PersonSearchParams.java @@ -61,8 +61,8 @@ public PersonSearchParams(StringAndListParam name, TokenAndListParam gender, Dat public SearchParameterMap toSearchParameterMap() { return baseSearchParameterMap() .addParameter(FhirConstants.NAME_SEARCH_HANDLER, FhirConstants.NAME_PROPERTY, getName()) - .addParameter(FhirConstants.GENDER_SEARCH_HANDLER, getGender()) - .addParameter(FhirConstants.DATE_RANGE_SEARCH_HANDLER, getBirthDate()) + .addParameter(FhirConstants.GENDER_SEARCH_HANDLER, FhirConstants.GENDER_PROPERTY, getGender()) + .addParameter(FhirConstants.DATE_RANGE_SEARCH_HANDLER, FhirConstants.BIRTHDATE_PROPERTY, getBirthDate()) .addParameter(FhirConstants.ADDRESS_SEARCH_HANDLER, FhirConstants.CITY_PROPERTY, getCity()) .addParameter(FhirConstants.ADDRESS_SEARCH_HANDLER, FhirConstants.STATE_PROPERTY, getState()) .addParameter(FhirConstants.ADDRESS_SEARCH_HANDLER, FhirConstants.POSTAL_CODE_PROPERTY, getPostalCode()) diff --git a/api/src/test/java/org/openmrs/module/fhir2/api/search/PatientSearchQueryTest.java b/api/src/test/java/org/openmrs/module/fhir2/api/search/PatientSearchQueryTest.java index ba027e04d5..39477c348e 100644 --- a/api/src/test/java/org/openmrs/module/fhir2/api/search/PatientSearchQueryTest.java +++ b/api/src/test/java/org/openmrs/module/fhir2/api/search/PatientSearchQueryTest.java @@ -29,6 +29,7 @@ import static org.hl7.fhir.r4.model.Patient.SP_FAMILY; import static org.hl7.fhir.r4.model.Patient.SP_GIVEN; import static org.mockito.Mockito.when; +import static org.openmrs.module.fhir2.matchers.FhirMatchers.isDeceased; import java.util.HashMap; import java.util.HashSet; @@ -114,6 +115,10 @@ public class PatientSearchQueryTest extends BaseModuleContextSensitiveTest { private static final String PATIENT_BIRTHDATE_PATIENT_UUID = "ca17fcc5-ec96-487f-b9ea-42973c8973e3"; + private static final String PATIENT_DEATH_DATE = "2003-01-01"; + + private static final String PATIENT_DEATH_DATE_LOWER_BOUND = "2001-08-01"; + private static final String PATIENT_ADDRESS_CITY = "Indianapolis"; private static final String PATIENT_ADDRESS_STATE = "IN"; @@ -605,6 +610,89 @@ public void searchForPatients_shouldSearchForPatientsByUuid() { assertThat(resultList.get(0).getIdElement().getIdPart(), equalTo(PATIENT_UUID)); } + @Test + public void searchForPatients_shouldSearchForPatientsByDeceasedStatus() { + SearchParameterMap theParams = new SearchParameterMap().addParameter(FhirConstants.BOOLEAN_SEARCH_HANDLER, + FhirConstants.DECEASED_PROPERTY, new TokenAndListParam().addAnd(new TokenParam("true"))); + + IBundleProvider results = search(theParams); + + assertThat(results, notNullValue()); + assertThat(results.size(), equalTo(2)); + + List resultList = get(results); + + assertThat(resultList, hasSize(equalTo(2))); + assertThat(resultList, everyItem(isDeceased())); + } + + @Test + public void searchForPatients_shouldSearchForPatientsByDeceasedDate() { + SearchParameterMap theParams = new SearchParameterMap().addParameter(FhirConstants.DATE_RANGE_SEARCH_HANDLER, + FhirConstants.DEATHDATE_PROPERTY, new DateRangeParam().setLowerBound(PATIENT_DEATH_DATE)); + + IBundleProvider results = search(theParams); + + assertThat(results, notNullValue()); + assertThat(results.size(), equalTo(1)); + + List resultList = get(results); + + assertThat(resultList, hasSize(equalTo(1))); + assertThat(resultList, everyItem(isDeceased())); + } + + @Test + public void searchForPatients_shouldSearchForPatientsByDeceasedDateWithLowerBound() { + SearchParameterMap theParams = new SearchParameterMap().addParameter(FhirConstants.DATE_RANGE_SEARCH_HANDLER, + FhirConstants.DEATHDATE_PROPERTY, new DateRangeParam().setLowerBound(PATIENT_DEATH_DATE)); + + IBundleProvider results = search(theParams); + + assertThat(results, notNullValue()); + assertThat(results.size(), greaterThanOrEqualTo(1)); + + List resultList = get(results); + + assertThat(resultList, not(empty())); + assertThat(resultList, everyItem(isDeceased())); + } + + @Test + public void searchForPatients_shouldSearchForPatientsByDeathDateWithUpperBound() { + SearchParameterMap theParams = new SearchParameterMap().addParameter(FhirConstants.DATE_RANGE_SEARCH_HANDLER, + FhirConstants.DEATHDATE_PROPERTY, new DateRangeParam().setUpperBound(PATIENT_DEATH_DATE)); + + IBundleProvider results = search(theParams); + + assertThat(results, notNullValue()); + assertThat(results.size(), greaterThan(1)); + + List resultList = get(results); + + assertThat(resultList, not(empty())); + assertThat(resultList, hasSize(greaterThan(1))); + assertThat(resultList, everyItem(isDeceased())); + } + + @Test + public void searchForPatients_shouldSearchForPatientsByDeathDateWithinBoundaries() { + SearchParameterMap theParams = new SearchParameterMap().addParameter(FhirConstants.DATE_RANGE_SEARCH_HANDLER, + FhirConstants.DEATHDATE_PROPERTY, + new DateRangeParam().setLowerBound(PATIENT_DEATH_DATE_LOWER_BOUND).setUpperBound(PATIENT_DEATH_DATE)); + + IBundleProvider results = search(theParams); + + assertThat(results, notNullValue()); + assertThat(results.size(), greaterThan(1)); + + List resultList = get(results); + + assertThat(resultList, not(empty())); + assertThat(resultList, hasSize(greaterThan(1))); + assertThat(resultList, everyItem(isDeceased())); + } + @Test public void searchForPatients_shouldSearchForPatientsByLastUpdatedDateCreated() { DateRangeParam lastUpdated = new DateRangeParam().setUpperBound(DATE_CREATED).setLowerBound(DATE_CREATED); diff --git a/api/src/test/java/org/openmrs/module/fhir2/matchers/FhirMatchers.java b/api/src/test/java/org/openmrs/module/fhir2/matchers/FhirMatchers.java new file mode 100644 index 0000000000..834ef18116 --- /dev/null +++ b/api/src/test/java/org/openmrs/module/fhir2/matchers/FhirMatchers.java @@ -0,0 +1,20 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under + * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. + * + * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS + * graphic logo is a trademark of OpenMRS Inc. + */ +package org.openmrs.module.fhir2.matchers; + +import org.hamcrest.Matcher; +import org.hl7.fhir.r4.model.Patient; + +public class FhirMatchers { + + public static Matcher isDeceased() { + return new IsDeceasedMatcher(); + } +} diff --git a/api/src/test/java/org/openmrs/module/fhir2/matchers/IsDeceasedMatcher.java b/api/src/test/java/org/openmrs/module/fhir2/matchers/IsDeceasedMatcher.java new file mode 100644 index 0000000000..8c77734c7d --- /dev/null +++ b/api/src/test/java/org/openmrs/module/fhir2/matchers/IsDeceasedMatcher.java @@ -0,0 +1,39 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under + * the terms of the Healthcare Disclaimer located at http://openmrs.org/license. + * + * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS + * graphic logo is a trademark of OpenMRS Inc. + */ +package org.openmrs.module.fhir2.matchers; + +import org.hamcrest.Description; +import org.hamcrest.TypeSafeDiagnosingMatcher; +import org.hl7.fhir.r4.model.Patient; + +public class IsDeceasedMatcher extends TypeSafeDiagnosingMatcher { + + @Override + protected boolean matchesSafely(Patient p, Description description) { + if (!p.hasDeceased()) { + description.appendText("patient does not have a deceased attribute"); + return false; + } else if (p.hasDeceasedBooleanType() && !p.getDeceasedBooleanType().booleanValue()) { + description.appendText("patient is not marked as deceased"); + return false; + } else if (p.hasDeceasedDateTimeType() && p.getDeceasedDateTimeType().isEmpty()) { + description.appendText("patient does not have a deceased date"); + return false; + } else { + description.appendText("patient is marked as deceased"); + return true; + } + } + + @Override + public void describeTo(Description description) { + description.appendText("a patient who is deceased"); + } +}