From 4b8ba8612e41f35049dad401f33109888353534f Mon Sep 17 00:00:00 2001 From: Ian <ian_bacher@brown.edu> Date: Mon, 11 Jan 2021 09:40:42 -0500 Subject: [PATCH 01/18] Move FhirImmunizationServiceTest --- .../FhirImmunizationServiceImplTest.java} | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) rename api/src/test/java/org/openmrs/module/fhir2/api/{FhirImmunizationServiceTest.java => impl/FhirImmunizationServiceImplTest.java} (98%) diff --git a/api/src/test/java/org/openmrs/module/fhir2/api/FhirImmunizationServiceTest.java b/api/src/test/java/org/openmrs/module/fhir2/api/impl/FhirImmunizationServiceImplTest.java similarity index 98% rename from api/src/test/java/org/openmrs/module/fhir2/api/FhirImmunizationServiceTest.java rename to api/src/test/java/org/openmrs/module/fhir2/api/impl/FhirImmunizationServiceImplTest.java index 49daf45be..1203f1f52 100644 --- a/api/src/test/java/org/openmrs/module/fhir2/api/FhirImmunizationServiceTest.java +++ b/api/src/test/java/org/openmrs/module/fhir2/api/impl/FhirImmunizationServiceImplTest.java @@ -7,7 +7,7 @@ * 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.api; +package org.openmrs.module.fhir2.api.impl; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; @@ -47,13 +47,14 @@ import org.openmrs.api.ObsService; import org.openmrs.module.fhir2.FhirConstants; import org.openmrs.module.fhir2.TestFhirSpringConfiguration; +import org.openmrs.module.fhir2.api.FhirImmunizationService; import org.openmrs.module.fhir2.api.translators.impl.ImmunizationObsGroupHelper; import org.openmrs.test.BaseModuleContextSensitiveTest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; @ContextConfiguration(classes = TestFhirSpringConfiguration.class, inheritLocations = false) -public class FhirImmunizationServiceTest extends BaseModuleContextSensitiveTest { +public class FhirImmunizationServiceImplTest extends BaseModuleContextSensitiveTest { private static final String IMMUNIZATIONS_METADATA_XML = "org/openmrs/module/fhir2/api/translators/ImmunizationTranslator_metadata.xml"; From 9ec21df83939aa7ed5c761ab86c5c42bd6d1c2fe Mon Sep 17 00:00:00 2001 From: Ian <ian_bacher@brown.edu> Date: Wed, 13 Jan 2021 16:55:39 -0500 Subject: [PATCH 02/18] Log an error, but do not fail if we cannot read the global property for server URI --- .../module/fhir2/api/dao/impl/BaseFhirDao.java | 2 +- .../fhir2/web/util/OpenmrsFhirAddressStrategy.java | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/BaseFhirDao.java b/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/BaseFhirDao.java index 2741af695..6243696dd 100644 --- a/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/BaseFhirDao.java +++ b/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/BaseFhirDao.java @@ -262,7 +262,7 @@ protected T retireObject(T object) { * @param theParams the parameters for this search */ protected void setupSearchParams(Criteria criteria, SearchParameterMap theParams) { - + } @Override diff --git a/omod/src/main/java/org/openmrs/module/fhir2/web/util/OpenmrsFhirAddressStrategy.java b/omod/src/main/java/org/openmrs/module/fhir2/web/util/OpenmrsFhirAddressStrategy.java index a1e58262a..e6b97d6bd 100644 --- a/omod/src/main/java/org/openmrs/module/fhir2/web/util/OpenmrsFhirAddressStrategy.java +++ b/omod/src/main/java/org/openmrs/module/fhir2/web/util/OpenmrsFhirAddressStrategy.java @@ -17,13 +17,17 @@ import ca.uhn.fhir.rest.server.IServerAddressStrategy; import lombok.AccessLevel; import lombok.Setter; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; +import org.hibernate.HibernateException; import org.openmrs.module.fhir2.FhirConstants; import org.openmrs.module.fhir2.api.FhirGlobalPropertyService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; @Component +@Slf4j @Setter(AccessLevel.PACKAGE) public class OpenmrsFhirAddressStrategy implements IServerAddressStrategy { @@ -31,8 +35,16 @@ public class OpenmrsFhirAddressStrategy implements IServerAddressStrategy { private FhirGlobalPropertyService globalPropertyService; @Override + @Transactional(readOnly = true) public String determineServerBase(ServletContext context, HttpServletRequest request) { - String gpPrefix = globalPropertyService.getGlobalProperty(FhirConstants.GLOBAL_PROPERTY_URI_PREFIX); + String gpPrefix = null; + + try { + gpPrefix = globalPropertyService.getGlobalProperty(FhirConstants.GLOBAL_PROPERTY_URI_PREFIX); + } + catch (HibernateException e) { + log.error("An error occurred while trying to read the {} property", FhirConstants.GLOBAL_PROPERTY_URI_PREFIX, e); + } if (StringUtils.isNotBlank(gpPrefix)) { StringBuilder gpUrl = new StringBuilder().append(gpPrefix); From 41f6c12c039fd858e1e5765e476b82f48c0a75d7 Mon Sep 17 00:00:00 2001 From: Ian <ian_bacher@brown.edu> Date: Wed, 20 Jan 2021 11:23:08 -0500 Subject: [PATCH 03/18] Rename test class --- ...latorTest.java => BaseProvenanceHandlingTranslatorTest.java} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/{AbstractProvenanceHandlingTranslatorTest.java => BaseProvenanceHandlingTranslatorTest.java} (99%) diff --git a/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/AbstractProvenanceHandlingTranslatorTest.java b/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/BaseProvenanceHandlingTranslatorTest.java similarity index 99% rename from api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/AbstractProvenanceHandlingTranslatorTest.java rename to api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/BaseProvenanceHandlingTranslatorTest.java index b2c604dd1..a181c897e 100644 --- a/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/AbstractProvenanceHandlingTranslatorTest.java +++ b/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/BaseProvenanceHandlingTranslatorTest.java @@ -29,7 +29,7 @@ import org.openmrs.module.fhir2.api.translators.PractitionerReferenceTranslator; @RunWith(MockitoJUnitRunner.class) -public class AbstractProvenanceHandlingTranslatorTest { +public class BaseProvenanceHandlingTranslatorTest { private static final String USER_UUID = "ddc25312-9798-4e6c-b8f8-269f2dd07cfd"; From 16423202cf860d2da20fa4043beecd6991822df0 Mon Sep 17 00:00:00 2001 From: Ian <ian_bacher@brown.edu> Date: Wed, 20 Jan 2021 11:26:51 -0500 Subject: [PATCH 04/18] FM2-329 Resolve error being thrown when order type unknown Also makes the code more likely to be able to generate an appropriate link --- .../impl/BaseReferenceHandlingTranslator.java | 21 ++--- .../BaseReferenceHandlingTranslatorTest.java | 81 +++++++++++++++++++ .../MedicationRequestTranslatorImplTest.java | 53 +++++++----- ...ionBasedOnReferenceTranslatorImplTest.java | 14 ++-- .../ServiceRequestTranslatorImplTest.java | 6 +- 5 files changed, 140 insertions(+), 35 deletions(-) diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/BaseReferenceHandlingTranslator.java b/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/BaseReferenceHandlingTranslator.java index fe7da0102..fc3d7bb12 100644 --- a/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/BaseReferenceHandlingTranslator.java +++ b/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/BaseReferenceHandlingTranslator.java @@ -15,9 +15,11 @@ import lombok.AccessLevel; import lombok.Setter; +import lombok.extern.slf4j.Slf4j; import org.hl7.fhir.r4.model.Identifier; import org.hl7.fhir.r4.model.Reference; import org.openmrs.Drug; +import org.openmrs.DrugOrder; import org.openmrs.Encounter; import org.openmrs.Location; import org.openmrs.Obs; @@ -28,18 +30,16 @@ import org.openmrs.PatientIdentifierType; import org.openmrs.Person; import org.openmrs.Provider; +import org.openmrs.TestOrder; import org.openmrs.User; import org.openmrs.Visit; import org.openmrs.module.fhir2.FhirConstants; import org.openmrs.module.fhir2.api.util.FhirUtils; @Setter(AccessLevel.PACKAGE) +@Slf4j public abstract class BaseReferenceHandlingTranslator { - public static final String DRUG_ORDER_TYPE_UUID = "131168f4-15f5-102d-96e4-000c29c2a5d7"; - - public static final String TEST_ORDER_TYPE_UUID = "52a447d3-a64a-11e3-9aeb-50e549534c5e"; - protected Reference createEncounterReference(@Nonnull Encounter encounter) { return createEncounterReference((OpenmrsObject) encounter); } @@ -130,18 +130,19 @@ protected Reference createPractitionerReference(@Nonnull Provider provider) { } protected Reference createOrderReference(@Nonnull Order order) { - if (order.getOrderType() == null) { + if (order == null) { return null; } - if (order.getOrderType().getUuid().equals(TEST_ORDER_TYPE_UUID)) { + if (order instanceof TestOrder) { return new Reference().setReference(FhirConstants.SERVICE_REQUEST + "/" + order.getUuid()) .setType(FhirConstants.SERVICE_REQUEST); - } else if (order.getOrderType().getUuid().equals(DRUG_ORDER_TYPE_UUID)) { - return new Reference().setReference(FhirConstants.MEDICATION + "/" + order.getUuid()) - .setType(FhirConstants.MEDICATION); + } else if (order instanceof DrugOrder) { + return new Reference().setReference(FhirConstants.MEDICATION_REQUEST + "/" + order.getUuid()) + .setType(FhirConstants.MEDICATION_REQUEST); } else { - throw new IllegalArgumentException("Cannot create reference of undetermined order type"); + log.warn("Could not determine order type for order {}", order); + return null; } } diff --git a/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/BaseReferenceHandlingTranslatorTest.java b/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/BaseReferenceHandlingTranslatorTest.java index 45e61e97d..93253daed 100644 --- a/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/BaseReferenceHandlingTranslatorTest.java +++ b/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/BaseReferenceHandlingTranslatorTest.java @@ -19,14 +19,17 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; +import org.openmrs.DrugOrder; import org.openmrs.Encounter; import org.openmrs.Location; +import org.openmrs.Order; import org.openmrs.Patient; import org.openmrs.PatientIdentifier; import org.openmrs.PatientIdentifierType; import org.openmrs.Person; import org.openmrs.PersonName; import org.openmrs.Provider; +import org.openmrs.TestOrder; import org.openmrs.User; import org.openmrs.module.fhir2.FhirConstants; @@ -74,6 +77,12 @@ public class BaseReferenceHandlingTranslatorTest { private static final String PRACTITIONER_REFERENCE = FhirConstants.PRACTITIONER + "/" + USER_UUID; + private static final String ORDER_UUID = "7ed459d6-82a2-4cc0-ba44-86c7fcaf3fc5"; + + private static final String TEST_ORDER_REFERENCE = FhirConstants.SERVICE_REQUEST + "/" + ORDER_UUID; + + private static final String DRUG_ORDER_REFERENCE = FhirConstants.MEDICATION_REQUEST + "/" + ORDER_UUID; + private Patient patient; private Provider provider; @@ -226,6 +235,7 @@ public void shouldReturnReferenceWithDisplayForProviderWithPerson() { @Test public void shouldReturnNullDisplayForPractitionerWithNullPerson() { Reference reference = referenceHandlingTranslator.createPractitionerReference(provider); + assertThat(reference, notNullValue()); assertThat(reference.getDisplay(), nullValue()); } @@ -262,6 +272,7 @@ public void shouldAddEncounterReference() { @Test public void shouldAddPractitionerGivenOpenMrsUserReference() { Reference reference = referenceHandlingTranslator.createPractitionerReference(user); + assertThat(reference, notNullValue()); assertThat(reference.getReference(), equalTo(PRACTITIONER_REFERENCE)); assertThat(reference.getType(), equalTo(FhirConstants.PRACTITIONER)); @@ -272,9 +283,79 @@ public void shouldAddPractitionerGivenOpenMrsUserReference() { public void shouldReturnReferenceWithNullDisplayIfUserPersonNameIsNull() { User user = new User(); user.setUuid(USER_UUID); + Reference reference = referenceHandlingTranslator.createPractitionerReference(user); + assertThat(reference, notNullValue()); assertThat(reference.getReference(), equalTo(PRACTITIONER_REFERENCE)); assertThat(reference.getDisplay(), nullValue()); } + + @Test + public void shouldAddOrderReferenceForTestOrder() { + TestOrder order = new TestOrder(); + order.setUuid(ORDER_UUID); + + Reference reference = referenceHandlingTranslator.createOrderReference(order); + + assertThat(reference, notNullValue()); + assertThat(reference.getReference(), equalTo(TEST_ORDER_REFERENCE)); + assertThat(reference.getDisplay(), nullValue()); + } + + @Test + public void shouldAddOrderReferenceForTestOrderSubclass() { + TestOrder order = new TestOrder() {}; + order.setUuid(ORDER_UUID); + + Reference reference = referenceHandlingTranslator.createOrderReference(order); + + assertThat(reference, notNullValue()); + assertThat(reference.getReference(), equalTo(TEST_ORDER_REFERENCE)); + assertThat(reference.getDisplay(), nullValue()); + } + + @Test + public void shouldAddOrderReferenceForDrugOrder() { + DrugOrder order = new DrugOrder(); + order.setUuid(ORDER_UUID); + + Reference reference = referenceHandlingTranslator.createOrderReference(order); + + assertThat(reference, notNullValue()); + assertThat(reference.getReference(), equalTo(DRUG_ORDER_REFERENCE)); + assertThat(reference.getDisplay(), nullValue()); + } + + @Test + public void shouldAddOrderReferenceForDrugOrderSubclass() { + DrugOrder order = new DrugOrder() {}; + order.setUuid(ORDER_UUID); + + Reference reference = referenceHandlingTranslator.createOrderReference(order); + + assertThat(reference, notNullValue()); + assertThat(reference.getReference(), equalTo(DRUG_ORDER_REFERENCE)); + assertThat(reference.getDisplay(), nullValue()); + } + + @Test + public void shouldReturnNullForRawOrder() { + Order order = new Order(); + order.setUuid(ORDER_UUID); + + Reference reference = referenceHandlingTranslator.createOrderReference(order); + + assertThat(reference, nullValue()); + } + + @Test + public void shouldReturnNullForUnknownOrderSubclass() { + Order order = new Order() {}; + order.setUuid(ORDER_UUID); + + Reference reference = referenceHandlingTranslator.createOrderReference(order); + + assertThat(reference, nullValue()); + } } diff --git a/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/MedicationRequestTranslatorImplTest.java b/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/MedicationRequestTranslatorImplTest.java index fec111cc5..9ce47ffec 100644 --- a/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/MedicationRequestTranslatorImplTest.java +++ b/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/MedicationRequestTranslatorImplTest.java @@ -19,6 +19,7 @@ import java.lang.reflect.Field; +import lombok.SneakyThrows; import org.hl7.fhir.r4.model.Annotation; import org.hl7.fhir.r4.model.BooleanType; import org.hl7.fhir.r4.model.CodeableConcept; @@ -53,6 +54,8 @@ @RunWith(MockitoJUnitRunner.class) public class MedicationRequestTranslatorImplTest { + public static final String DRUG_ORDER_TYPE_UUID = "131168f4-15f5-102d-96e4-000c29c2a5d7"; + private static final String DRUG_ORDER_UUID = "44fdc8ad-fe4d-499b-93a8-8a991c1d477e"; private static final String DISCONTINUED_DRUG_ORDER_UUID = "efca4077-493c-496b-8312-856ee5d1cc27"; @@ -61,7 +64,8 @@ public class MedicationRequestTranslatorImplTest { private static final String DISCONTINUED_DRUG_ORDER_NUMBER = "ORD-2"; - private static final String PRIOR_MEDICATION_REQUEST_REFERENCE = FhirConstants.MEDICATION + "/" + DRUG_ORDER_UUID; + private static final String PRIOR_MEDICATION_REQUEST_REFERENCE = FhirConstants.MEDICATION_REQUEST + "/" + + DRUG_ORDER_UUID; private static final String DRUG_UUID = "99fdc8ad-fe4d-499b-93a8-8a991c1d477g"; @@ -125,8 +129,9 @@ public void setup() { drugOrder = new DrugOrder(); drugOrder.setUuid(DRUG_ORDER_UUID); setOrderNumberByReflection(drugOrder, DRUG_ORDER_NUMBER); + OrderType ordertype = new OrderType(); - ordertype.setUuid(BaseReferenceHandlingTranslator.DRUG_ORDER_TYPE_UUID); + ordertype.setUuid(DRUG_ORDER_TYPE_UUID); drugOrder.setOrderType(ordertype); discontinuedDrugOrder = new DrugOrder(); @@ -142,6 +147,7 @@ public void setup() { @Test public void toOpenMrsType_shouldTranslateToOpenType() { DrugOrder result = medicationRequestTranslator.toOpenmrsType(new DrugOrder(), medicationRequest); + assertThat(result, notNullValue()); assertThat(result.getUuid(), notNullValue()); assertThat(result.getUuid(), equalTo(DRUG_ORDER_UUID)); @@ -150,6 +156,7 @@ public void toOpenMrsType_shouldTranslateToOpenType() { @Test public void toFhirResource_shouldTranslateToFhirResource() { MedicationRequest result = medicationRequestTranslator.toFhirResource(drugOrder); + assertThat(result, notNullValue()); assertThat(result.getId(), notNullValue()); assertThat(result.getId(), equalTo(DRUG_ORDER_UUID)); @@ -203,9 +210,12 @@ public void toOpenMrsType_shouldThrowExceptionIfMedicationIsNull() { public void toFhirResource_shouldConvertStatusToFhirType() { drugOrder.setVoided(true); drugOrder.setVoidedBy(new User()); + when(medicationRequestStatusTranslator.toFhirResource(drugOrder)) .thenReturn(MedicationRequest.MedicationRequestStatus.CANCELLED); + MedicationRequest result = medicationRequestTranslator.toFhirResource(drugOrder); + assertThat(result, notNullValue()); assertThat(result.getStatus(), notNullValue()); assertThat(result.getStatus(), equalTo(MedicationRequest.MedicationRequestStatus.CANCELLED)); @@ -215,7 +225,9 @@ public void toFhirResource_shouldConvertStatusToFhirType() { public void toFhirResource_shouldConvertActiveStatusToFhirType() { when(medicationRequestStatusTranslator.toFhirResource(drugOrder)) .thenReturn(MedicationRequest.MedicationRequestStatus.ACTIVE); + MedicationRequest result = medicationRequestTranslator.toFhirResource(drugOrder); + assertThat(result, notNullValue()); assertThat(result.getStatus(), notNullValue()); assertThat(result.getStatus(), equalTo(MedicationRequest.MedicationRequestStatus.ACTIVE)); @@ -326,6 +338,7 @@ public void toFhirResource_shouldTranslatePatientToFhirType() { patientReference.setReference(FhirConstants.PATIENT + "/" + PATIENT_UUID); when(patientReferenceTranslator.toFhirResource(patient)).thenReturn(patientReference); + MedicationRequest result = medicationRequestTranslator.toFhirResource(drugOrder); assertThat(result, notNullValue()); @@ -338,6 +351,7 @@ public void toFhirResource_shouldTranslateUrgencyToFhirType() { drugOrder.setUrgency(DrugOrder.Urgency.ON_SCHEDULED_DATE); when(medicationRequestPriorityTranslator.toFhirResource(DrugOrder.Urgency.ON_SCHEDULED_DATE)) .thenReturn(MedicationRequest.MedicationRequestPriority.URGENT); + MedicationRequest result = medicationRequestTranslator.toFhirResource(drugOrder); assertThat(result, notNullValue()); @@ -349,7 +363,7 @@ public void toFhirResource_shouldTranslateUrgencyToFhirType() { @Test public void toOpenMrsType_shouldTranslateMedicationToOpenMrsDrug() { Reference medicationRef = new Reference(); - medicationRef.setReference(FhirConstants.MEDICATION + "/" + DRUG_UUID); + medicationRef.setReference(FhirConstants.MEDICATION_REQUEST + "/" + DRUG_UUID); Drug drug = new Drug(); drug.setUuid(DRUG_UUID); @@ -368,9 +382,10 @@ public void toFhirResource_shouldTranslateDrugToMedicationReference() { drug.setUuid(DRUG_UUID); drugOrder.setDrug(drug); Reference medicationRef = new Reference(); - medicationRef.setReference(FhirConstants.MEDICATION + "/" + DRUG_UUID); + medicationRef.setReference(FhirConstants.MEDICATION_REQUEST + "/" + DRUG_UUID); when(medicationReferenceTranslator.toFhirResource(drug)).thenReturn(medicationRef); + MedicationRequest result = medicationRequestTranslator.toFhirResource(drugOrder); assertThat(result, notNullValue()); @@ -389,7 +404,9 @@ public void toFhirResource_shouldTranslateOrderReasonToReasonCode() { codeableConcept.addCoding(new Coding().setCode(concept.getConceptId().toString())); when(conceptTranslator.toFhirResource(concept)).thenReturn(codeableConcept); + MedicationRequest result = medicationRequestTranslator.toFhirResource(drugOrder); + assertThat(result, notNullValue()); assertThat(result.getReasonCode(), not(empty())); assertThat(result.getReasonCodeFirstRep(), equalTo(codeableConcept)); @@ -406,7 +423,9 @@ public void toOpenMrsType_shouldTranslateReasonCodeToOrderReason() { medicationRequest.addReasonCode(codeableConcept); when(conceptTranslator.toOpenmrsType(codeableConcept)).thenReturn(concept); + DrugOrder drugOrder = medicationRequestTranslator.toOpenmrsType(new DrugOrder(), medicationRequest); + assertThat(drugOrder, notNullValue()); assertThat(drugOrder.getOrderReason(), notNullValue()); assertThat(drugOrder.getOrderReason().getUuid(), equalTo(CONCEPT_UUID)); @@ -418,6 +437,7 @@ public void toFhirResource_shouldTranslateCommentToFulFillerToNote() { drugOrder.setCommentToFulfiller(COMMENT_TO_THE_FULL_FILLER); MedicationRequest result = medicationRequestTranslator.toFhirResource(drugOrder); + assertThat(result, notNullValue()); assertThat(result.getNote(), not(empty())); assertThat(result.getNoteFirstRep().getText(), equalTo(COMMENT_TO_THE_FULL_FILLER)); @@ -428,6 +448,7 @@ public void toOpenMrsType_shouldTranslateNoteToCommentToFullFiller() { medicationRequest.addNote(new Annotation().setText(COMMENT_TO_THE_FULL_FILLER)); DrugOrder result = medicationRequestTranslator.toOpenmrsType(new DrugOrder(), medicationRequest); + assertThat(result, notNullValue()); assertThat(result.getCommentToFulfiller(), equalTo(COMMENT_TO_THE_FULL_FILLER)); } @@ -453,6 +474,7 @@ public void toFhirResource_shouldAddDosageInstructions() { when(dosageTranslator.toFhirResource(drugOrder)).thenReturn(dosage); MedicationRequest result = medicationRequestTranslator.toFhirResource(drugOrder); + assertThat(result, notNullValue()); assertThat(result.getDosageInstructionFirstRep(), notNullValue()); assertThat(result.getDosageInstructionFirstRep().getText(), equalTo(DOSING_INSTRUCTIONS)); @@ -460,20 +482,15 @@ public void toFhirResource_shouldAddDosageInstructions() { assertThat(result.getDosageInstructionFirstRep().getRoute(), equalTo(codeableConcept)); } - private DrugOrder setOrderNumberByReflection(DrugOrder order, String orderNumber) { - try { - Class clazz = order.getClass(); - Field orderNumberField = clazz.getSuperclass().getDeclaredField("orderNumber"); - Boolean isAccessible = orderNumberField.isAccessible(); - if (!isAccessible) { - orderNumberField.setAccessible(true); - } - orderNumberField.set(((Order) order), orderNumber); - } - catch (Exception e) { - e.printStackTrace(); + @SneakyThrows + private void setOrderNumberByReflection(DrugOrder order, String orderNumber) { + Class<? extends DrugOrder> clazz = order.getClass(); + Field orderNumberField = clazz.getSuperclass().getDeclaredField("orderNumber"); + boolean isAccessible = orderNumberField.isAccessible(); + if (!isAccessible) { + orderNumberField.setAccessible(true); } - return order; + + orderNumberField.set(order, orderNumber); } - } diff --git a/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/ObservationBasedOnReferenceTranslatorImplTest.java b/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/ObservationBasedOnReferenceTranslatorImplTest.java index 6e6ed3c5b..42afcd9bf 100644 --- a/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/ObservationBasedOnReferenceTranslatorImplTest.java +++ b/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/ObservationBasedOnReferenceTranslatorImplTest.java @@ -62,8 +62,9 @@ public void toFhirResource_shouldReturnNullWhenCalledWithNullObject() { @Test public void toFhirResource_shouldConvertTestOrderToReference() { - Order order = new Order(); + Order order = new TestOrder(); order.setUuid(ORDER_UUID); + OrderType orderType = new OrderType(); orderType.setUuid(TEST_ORDER_TYPE_UUID); order.setOrderType(orderType); @@ -76,8 +77,9 @@ public void toFhirResource_shouldConvertTestOrderToReference() { @Test public void toFhirResource_shouldConvertDrugOrderToReference() { - Order order = new Order(); + Order order = new DrugOrder(); order.setUuid(ORDER_UUID); + OrderType orderType = new OrderType(); orderType.setUuid(DRUG_ORDER_TYPE_UUID); order.setOrderType(orderType); @@ -85,7 +87,7 @@ public void toFhirResource_shouldConvertDrugOrderToReference() { Reference result = translator.toFhirResource(order); assertThat(result, notNullValue()); - assertThat(result.getType(), equalTo(FhirConstants.MEDICATION)); + assertThat(result.getType(), equalTo(FhirConstants.MEDICATION_REQUEST)); } @Test @@ -98,9 +100,9 @@ public void toFhirResource_shouldReturnNullIfOrderTypeIsNull() { assertThat(result, nullValue()); } - @Test(expected = IllegalArgumentException.class) - public void toFhirType_shouldThrowIllegalArgumentExceptionException() { - Order order = new DrugOrder(); + @Test + public void toFhirType_shouldReturnNullForUnknownOrderType() { + Order order = new Order() {}; order.setUuid(ORDER_UUID); OrderType orderType = new OrderType(); diff --git a/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/ServiceRequestTranslatorImplTest.java b/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/ServiceRequestTranslatorImplTest.java index 1476492df..90b7acf0c 100644 --- a/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/ServiceRequestTranslatorImplTest.java +++ b/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/ServiceRequestTranslatorImplTest.java @@ -58,6 +58,8 @@ @RunWith(MockitoJUnitRunner.class) public class ServiceRequestTranslatorImplTest { + public static final String TEST_ORDER_TYPE_UUID = "52a447d3-a64a-11e3-9aeb-50e549534c5e"; + private static final String SERVICE_REQUEST_UUID = "4e4851c3-c265-400e-acc9-1f1b0ac7f9c4"; private static final String DISCONTINUED_TEST_ORDER_UUID = "efca4077-493c-496b-8312-856ee5d1cc27"; @@ -116,8 +118,9 @@ public void setup() { order = new TestOrder(); order.setUuid(SERVICE_REQUEST_UUID); setOrderNumberByReflection(order, TEST_ORDER_NUMBER); + OrderType ordertype = new OrderType(); - ordertype.setUuid(BaseReferenceHandlingTranslator.TEST_ORDER_TYPE_UUID); + ordertype.setUuid(TEST_ORDER_TYPE_UUID); order.setOrderType(ordertype); discontinuedTestOrder = new TestOrder(); @@ -126,6 +129,7 @@ public void setup() { discontinuedTestOrder.setPreviousOrder(order); } + @Test public void toFhirResource_shouldTranslateToFhirResourceWithReplacesFieldGivenDiscontinuedOrder() { discontinuedTestOrder.setAction(Order.Action.DISCONTINUE); From 24c4a9bc2d68cacf192ff28b0ab30f40170564dd Mon Sep 17 00:00:00 2001 From: dkayiwa <kayiwadaniel@gmail.com> Date: Fri, 22 Jan 2021 00:05:35 +0300 Subject: [PATCH 05/18] FM2-331: Should run on PostgreSQL --- api/src/main/resources/liquibase.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/api/src/main/resources/liquibase.xml b/api/src/main/resources/liquibase.xml index 2daef5eda..a6d18b98e 100644 --- a/api/src/main/resources/liquibase.xml +++ b/api/src/main/resources/liquibase.xml @@ -67,7 +67,7 @@ <and> <tableExists tableName="fhir_concept_source"/> <tableExists tableName="concept_reference_source"/> - <sqlCheck expectedResult="1">select 1 from concept_reference_source where name='LOINC' and retired = 0 + <sqlCheck expectedResult="1">select 1 from concept_reference_source where name='LOINC' and retired = false </sqlCheck> <sqlCheck expectedResult="0">select count(*) from fhir_concept_source where uuid = '249b13c8-72fa-4b96-8d3d-b200efed985e' @@ -78,7 +78,7 @@ insert into fhir_concept_source (name, concept_source_id, url, creator, date_created, uuid) select name, concept_source_id, 'http://loinc.org', 1, now(), '249b13c8-72fa-4b96-8d3d-b200efed985e' from concept_reference_source - where name = 'LOINC' and retired = 0 + where name = 'LOINC' and retired = false ]]></sql> </changeSet> @@ -87,7 +87,7 @@ <and> <tableExists tableName="fhir_concept_source"/> <tableExists tableName="concept_reference_source"/> - <sqlCheck expectedResult="1">select 1 from concept_reference_source where name='CIEL' and retired = 0 + <sqlCheck expectedResult="1">select 1 from concept_reference_source where name='CIEL' and retired = false </sqlCheck> <sqlCheck expectedResult="0">select count(*) from fhir_concept_source where uuid = '2b3c1ff8-768a-102f-83f4-12313b04a615' @@ -98,7 +98,7 @@ insert into fhir_concept_source (name, concept_source_id, url, creator, date_created, uuid) select name, concept_source_id, 'urn:oid:2.16.840.1.113883.3.7201', 1, now(), '2b3c1ff8-768a-102f-83f4-12313b04a615' from concept_reference_source - where name = 'CIEL' and retired = 0 + where name = 'CIEL' and retired = false ]]></sql> </changeSet> From 5d046de2f0fd8fd2b28e169aceac4dd86f9c2524 Mon Sep 17 00:00:00 2001 From: Ankit kumar <49350053+theanandankit@users.noreply.github.com> Date: Mon, 1 Feb 2021 20:09:41 +0530 Subject: [PATCH 06/18] FM2-324: Mapping ServiceRequest status for TestOrders (#314) --- .../impl/ServiceRequestTranslatorImpl.java | 54 +++---- .../ServiceRequestTranslatorImplTest.java | 140 ++++++++++++------ 2 files changed, 117 insertions(+), 77 deletions(-) diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/ServiceRequestTranslatorImpl.java b/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/ServiceRequestTranslatorImpl.java index 1b053d967..433f9f35f 100644 --- a/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/ServiceRequestTranslatorImpl.java +++ b/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/ServiceRequestTranslatorImpl.java @@ -15,6 +15,7 @@ import java.util.Collection; import java.util.Collections; +import java.util.Date; import java.util.stream.Collectors; import ca.uhn.fhir.rest.api.server.IBundleProvider; @@ -75,7 +76,7 @@ public ServiceRequest toFhirResource(@Nonnull TestOrder order) { serviceRequest.setId(order.getUuid()); - serviceRequest.setStatus(determineServiceRequestStatus(order.getUuid())); + serviceRequest.setStatus(determineServiceRequestStatus(order)); serviceRequest.setCode(conceptTranslator.toFhirResource(order.getConcept())); @@ -111,41 +112,24 @@ public TestOrder toOpenmrsType(@Nonnull ServiceRequest resource) { throw new UnsupportedOperationException(); } - private ServiceRequest.ServiceRequestStatus determineServiceRequestStatus(String orderUuid) { - IBundleProvider results = taskService.searchForTasks( - new ReferenceAndListParam() - .addAnd(new ReferenceOrListParam().add(new ReferenceParam("ServiceRequest", null, orderUuid))), - null, null, null, null, null); - - Collection<Task> serviceRequestTasks = results.getResources(START_INDEX, END_INDEX).stream().map(p -> (Task) p) - .collect(Collectors.toList()); - - ServiceRequest.ServiceRequestStatus serviceRequestStatus = ServiceRequest.ServiceRequestStatus.UNKNOWN; - - if (serviceRequestTasks.size() != 1) { - return serviceRequestStatus; - } - - Task serviceRequestTask = serviceRequestTasks.iterator().next(); - - if (serviceRequestTask.hasStatus()) { - switch (serviceRequestTask.getStatus()) { - case ACCEPTED: - - case REQUESTED: - serviceRequestStatus = ServiceRequest.ServiceRequestStatus.ACTIVE; - break; - - case REJECTED: - serviceRequestStatus = ServiceRequest.ServiceRequestStatus.REVOKED; - break; - - case COMPLETED: - serviceRequestStatus = ServiceRequest.ServiceRequestStatus.COMPLETED; - break; - } + private ServiceRequest.ServiceRequestStatus determineServiceRequestStatus(TestOrder order) { + + Date currentDate = new Date(); + + boolean isCompeted = order.isActivated() + && ((order.getDateStopped() != null && currentDate.after(order.getDateStopped())) + || (order.getAutoExpireDate() != null && currentDate.after(order.getAutoExpireDate()))); + boolean isDiscontinued = order.isActivated() && order.getAction() == Order.Action.DISCONTINUE; + + if ((isCompeted && isDiscontinued)) { + return ServiceRequest.ServiceRequestStatus.UNKNOWN; + } else if (isDiscontinued) { + return ServiceRequest.ServiceRequestStatus.REVOKED; + } else if (isCompeted) { + return ServiceRequest.ServiceRequestStatus.COMPLETED; + } else { + return ServiceRequest.ServiceRequestStatus.ACTIVE; } - return serviceRequestStatus; } private Reference determineServiceRequestPerformer(String orderUuid) { diff --git a/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/ServiceRequestTranslatorImplTest.java b/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/ServiceRequestTranslatorImplTest.java index 90b7acf0c..7acecadcb 100644 --- a/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/ServiceRequestTranslatorImplTest.java +++ b/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/ServiceRequestTranslatorImplTest.java @@ -21,7 +21,7 @@ import static org.mockito.Mockito.when; import java.lang.reflect.Field; -import java.util.Arrays; +import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.Date; @@ -54,6 +54,7 @@ import org.openmrs.module.fhir2.api.translators.PatientReferenceTranslator; import org.openmrs.module.fhir2.api.translators.PractitionerReferenceTranslator; import org.openmrs.module.fhir2.providers.r4.MockIBundleProvider; +import org.openmrs.order.OrderUtilTest; @RunWith(MockitoJUnitRunner.class) public class ServiceRequestTranslatorImplTest { @@ -197,14 +198,15 @@ public void toFhirResource_shouldTranslateOpenmrsTestOrderToFhirServiceRequest() } @Test - public void toFhirResource_shouldTranslateOrderFromRequestedTaskToActiveServiceRequest() { + public void toFhirResource_shouldTranslateOrderFromOnlyDateActivatedToActiveServiceRequest() { TestOrder newOrder = new TestOrder(); - newOrder.setUuid(SERVICE_REQUEST_UUID); - - List<Task> tasks = setUpBasedOnScenario(Task.TaskStatus.REQUESTED); when(taskService.searchForTasks(any(), any(), any(), any(), any(), any())) - .thenReturn(new MockIBundleProvider<>(tasks, PREFERRED_PAGE_SIZE, COUNT)); + .thenReturn(new MockIBundleProvider<>(Collections.emptyList(), PREFERRED_PAGE_SIZE, COUNT)); + + Calendar activationDate = Calendar.getInstance(); + activationDate.set(2000, Calendar.APRIL, 16); + newOrder.setDateActivated(activationDate.getTime()); ServiceRequest result = translator.toFhirResource(newOrder); @@ -213,30 +215,40 @@ public void toFhirResource_shouldTranslateOrderFromRequestedTaskToActiveServiceR } @Test - public void toFhirResource_shouldTranslateOrderFromRejectedTaskToRevokedServiceRequest() { + public void toFhirResource_shouldTranslateOrderFromAutoExpireToCompleteServiceRequest() throws Exception { TestOrder newOrder = new TestOrder(); - newOrder.setUuid(SERVICE_REQUEST_UUID); - - List<Task> tasks = setUpBasedOnScenario(Task.TaskStatus.REJECTED); when(taskService.searchForTasks(any(), any(), any(), any(), any(), any())) - .thenReturn(new MockIBundleProvider<>(tasks, PREFERRED_PAGE_SIZE, COUNT)); + .thenReturn(new MockIBundleProvider<>(Collections.emptyList(), PREFERRED_PAGE_SIZE, COUNT)); + + Calendar date = Calendar.getInstance(); + date.set(2000, Calendar.APRIL, 16); + newOrder.setDateActivated(date.getTime()); + date.set(2070, Calendar.APRIL, 16); + newOrder.setAutoExpireDate(date.getTime()); + date.set(2010, Calendar.APRIL, 16); + OrderUtilTest.setDateStopped(newOrder, date.getTime()); ServiceRequest result = translator.toFhirResource(newOrder); assertThat(result, notNullValue()); - assertThat(result.getStatus(), equalTo(ServiceRequest.ServiceRequestStatus.REVOKED)); + assertThat(result.getStatus(), equalTo(ServiceRequest.ServiceRequestStatus.COMPLETED)); } @Test - public void toFhirResource_shouldTranslateOrderFromAcceptedTaskToActiveServiceRequest() { + public void toFhirResource_shouldTranslateOrderToActiveServiceRequest() throws Exception { TestOrder newOrder = new TestOrder(); - newOrder.setUuid(SERVICE_REQUEST_UUID); - - List<Task> tasks = setUpBasedOnScenario(Task.TaskStatus.ACCEPTED); when(taskService.searchForTasks(any(), any(), any(), any(), any(), any())) - .thenReturn(new MockIBundleProvider<>(tasks, PREFERRED_PAGE_SIZE, COUNT)); + .thenReturn(new MockIBundleProvider<>(Collections.emptyList(), PREFERRED_PAGE_SIZE, COUNT)); + + Calendar date = Calendar.getInstance(); + date.set(2000, Calendar.APRIL, 16); + newOrder.setDateActivated(date.getTime()); + date.set(2070, Calendar.APRIL, 16); + newOrder.setAutoExpireDate(date.getTime()); + date.set(2069, Calendar.APRIL, 16); + OrderUtilTest.setDateStopped(newOrder, date.getTime()); ServiceRequest result = translator.toFhirResource(newOrder); @@ -245,14 +257,19 @@ public void toFhirResource_shouldTranslateOrderFromAcceptedTaskToActiveServiceRe } @Test - public void toFhirResource_shouldTranslateOrderFromCompletedTaskToCompletedServiceRequest() { + public void toFhirResource_shouldTranslateOrderToCompletedServiceRequest() throws Exception { TestOrder newOrder = new TestOrder(); - newOrder.setUuid(SERVICE_REQUEST_UUID); - - List<Task> tasks = setUpBasedOnScenario(Task.TaskStatus.COMPLETED); when(taskService.searchForTasks(any(), any(), any(), any(), any(), any())) - .thenReturn(new MockIBundleProvider<>(tasks, PREFERRED_PAGE_SIZE, COUNT)); + .thenReturn(new MockIBundleProvider<>(Collections.emptyList(), PREFERRED_PAGE_SIZE, COUNT)); + + Calendar date = Calendar.getInstance(); + date.set(2000, Calendar.APRIL, 16); + newOrder.setDateActivated(date.getTime()); + date.set(2011, Calendar.APRIL, 16); + newOrder.setAutoExpireDate(date.getTime()); + date.set(2010, Calendar.APRIL, 16); + OrderUtilTest.setDateStopped(newOrder, date.getTime()); ServiceRequest result = translator.toFhirResource(newOrder); @@ -261,14 +278,20 @@ public void toFhirResource_shouldTranslateOrderFromCompletedTaskToCompletedServi } @Test - public void toFhirResource_shouldTranslateOrderFromOtherTaskToUnknownServiceRequest() { + public void toFhirResource_shouldTranslateWrongOrderFromActiveToUnknownServiceRequest() throws Exception { TestOrder newOrder = new TestOrder(); - newOrder.setUuid(SERVICE_REQUEST_UUID); - - List<Task> tasks = setUpBasedOnScenario(Task.TaskStatus.DRAFT); when(taskService.searchForTasks(any(), any(), any(), any(), any(), any())) - .thenReturn(new MockIBundleProvider<>(tasks, PREFERRED_PAGE_SIZE, COUNT)); + .thenReturn(new MockIBundleProvider<>(Collections.emptyList(), PREFERRED_PAGE_SIZE, COUNT)); + + Calendar date = Calendar.getInstance(); + date.set(2000, Calendar.APRIL, 16); + newOrder.setDateActivated(date.getTime()); + date.set(2015, Calendar.APRIL, 16); + newOrder.setAutoExpireDate(date.getTime()); + date.set(2010, Calendar.APRIL, 16); + newOrder.setAction(Order.Action.DISCONTINUE); + OrderUtilTest.setDateStopped(newOrder, date.getTime()); ServiceRequest result = translator.toFhirResource(newOrder); @@ -277,43 +300,76 @@ public void toFhirResource_shouldTranslateOrderFromOtherTaskToUnknownServiceRequ } @Test - public void toFhirResource_shouldTranslateOrderWithoutTaskToUnknownServiceRequest() { + public void toFhirResource_shouldTranslateWrongOrderFromCompleteToUnknownServiceRequest() throws Exception { TestOrder newOrder = new TestOrder(); - newOrder.setUuid(SERVICE_REQUEST_UUID); when(taskService.searchForTasks(any(), any(), any(), any(), any(), any())) .thenReturn(new MockIBundleProvider<>(Collections.emptyList(), PREFERRED_PAGE_SIZE, COUNT)); + Calendar date = Calendar.getInstance(); + date.set(2000, Calendar.APRIL, 16); + newOrder.setDateActivated(date.getTime()); + date.set(2070, Calendar.APRIL, 16); + newOrder.setAutoExpireDate(date.getTime()); + date.set(2069, Calendar.APRIL, 16); + newOrder.setAction(Order.Action.DISCONTINUE); + OrderUtilTest.setDateStopped(newOrder, date.getTime()); + ServiceRequest result = translator.toFhirResource(newOrder); assertThat(result, notNullValue()); - assertThat(result.getStatus(), equalTo(ServiceRequest.ServiceRequestStatus.UNKNOWN)); + assertThat(result.getStatus(), equalTo(ServiceRequest.ServiceRequestStatus.REVOKED)); + } + + @Test + public void toFhirResource_shouldTranslateOrderFromOnlyAutoExpireToCompleteServiceRequest() throws Exception { + TestOrder newOrder = new TestOrder(); + + when(taskService.searchForTasks(any(), any(), any(), any(), any(), any())) + .thenReturn(new MockIBundleProvider<>(Collections.emptyList(), PREFERRED_PAGE_SIZE, COUNT)); + + Calendar date = Calendar.getInstance(); + date.set(2000, Calendar.APRIL, 16); + newOrder.setDateActivated(date.getTime()); + date.set(2015, Calendar.APRIL, 16); + newOrder.setAutoExpireDate(date.getTime()); + + ServiceRequest result = translator.toFhirResource(newOrder); + + assertThat(result, notNullValue()); + assertThat(result.getStatus(), equalTo(ServiceRequest.ServiceRequestStatus.COMPLETED)); } @Test - public void toFhirResource_shouldTranslateOrderWithMultipleTasksToUnknownServiceRequest() { + public void toFhirResource_shouldTranslateOrderFromOnlyDateStoppedToCompleteServiceRequest() throws Exception { TestOrder newOrder = new TestOrder(); - newOrder.setUuid(SERVICE_REQUEST_UUID); - Task firstTask = new Task(); - Task secondTask = new Task(); + when(taskService.searchForTasks(any(), any(), any(), any(), any(), any())) + .thenReturn(new MockIBundleProvider<>(Collections.emptyList(), PREFERRED_PAGE_SIZE, COUNT)); - Reference basedOnRef = new Reference(); - basedOnRef.setReference("ServiceRequest/" + SERVICE_REQUEST_UUID); - basedOnRef.setType("ServiceRequest"); + Calendar date = Calendar.getInstance(); + date.set(2000, Calendar.APRIL, 16); + newOrder.setDateActivated(date.getTime()); + date.set(2015, Calendar.APRIL, 16); + OrderUtilTest.setDateStopped(newOrder, date.getTime()); - firstTask.addBasedOn(basedOnRef); - secondTask.addBasedOn(basedOnRef); + ServiceRequest result = translator.toFhirResource(newOrder); - List<Task> tasks = Arrays.asList(firstTask, secondTask); + assertThat(result, notNullValue()); + assertThat(result.getStatus(), equalTo(ServiceRequest.ServiceRequestStatus.COMPLETED)); + } + + @Test + public void toFhirResource_shouldTranslateFromNoDataToActiveServiceRequest() { + TestOrder newOrder = new TestOrder(); when(taskService.searchForTasks(any(), any(), any(), any(), any(), any())) - .thenReturn(new MockIBundleProvider<>(tasks, PREFERRED_PAGE_SIZE, COUNT)); + .thenReturn(new MockIBundleProvider<>(Collections.emptyList(), PREFERRED_PAGE_SIZE, COUNT)); ServiceRequest result = translator.toFhirResource(newOrder); assertThat(result, notNullValue()); - assertThat(result.getStatus(), equalTo(ServiceRequest.ServiceRequestStatus.UNKNOWN)); + assertThat(result.getStatus(), equalTo(ServiceRequest.ServiceRequestStatus.ACTIVE)); } @Test From 9e024e10854e76298c4bdb8936431599744d9772 Mon Sep 17 00:00:00 2001 From: Mutesasira Moses <mozzymutesa@gmail.com> Date: Mon, 1 Feb 2021 17:42:51 +0300 Subject: [PATCH 07/18] FM2-307: Implementing Condition Resource for OpenMRS 2.1 And Below (#308) --- .../dao/impl/FhirConditionDaoImpl_2_2.java | 120 --- .../impl/FhirConditionDaoImpl_2_2Test.java | 6 - .../ConditionSearchQueryImpl_2_2Test.java | 2 +- .../openmrs/module/fhir2/FhirConstants.java | 2 + .../module/fhir2/api/dao/impl/BaseDao.java | 112 +++ .../api/dao/impl/FhirConditionDaoImpl.java | 135 +++ .../api/impl/FhirConditionServiceImpl.java | 73 +- .../fhir2/api/search/SearchQueryInclude.java | 5 + ...ConditionClinicalStatusTranslatorImpl.java | 34 + .../impl/ConditionTranslatorImpl.java | 127 +++ .../MockedCalendarFactoryConfiguration.java | 27 + .../dao/impl/FhirConditionDaoImplTest.java | 170 ++++ .../impl/FhirConditionServiceImplTest.java | 223 ++++- .../api/search/ConditionSearchQueryTest.java | 871 ++++++++++++++++++ .../impl/ConditionTranslatorImplTest.java | 302 ++++++ ...ditionResourceProviderIntegrationTest.java | 2 + ...onFhirResourceProviderIntegrationTest.java | 441 +++++++++ ...onFhirResourceProviderIntegrationTest.java | 436 +++++++++ ...irObsConditionDaoImplTest_initial_data.xml | 27 + .../providers/ConditionWebTest_create_r3.json | 2 +- .../providers/ConditionWebTest_create_r3.xml | 4 +- 21 files changed, 2933 insertions(+), 188 deletions(-) create mode 100644 api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirConditionDaoImpl.java create mode 100644 api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/ConditionClinicalStatusTranslatorImpl.java create mode 100644 api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/ConditionTranslatorImpl.java create mode 100644 api/src/test/java/org/openmrs/module/fhir2/MockedCalendarFactoryConfiguration.java create mode 100644 api/src/test/java/org/openmrs/module/fhir2/api/dao/impl/FhirConditionDaoImplTest.java create mode 100644 api/src/test/java/org/openmrs/module/fhir2/api/search/ConditionSearchQueryTest.java create mode 100644 api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/ConditionTranslatorImplTest.java create mode 100644 integration-tests/src/test/java/org/openmrs/module/fhir2/providers/r3/ConditionFhirResourceProviderIntegrationTest.java create mode 100644 integration-tests/src/test/java/org/openmrs/module/fhir2/providers/r4/ConditionFhirResourceProviderIntegrationTest.java create mode 100644 test-data/src/test/resources/org/openmrs/module/fhir2/api/dao/impl/FhirObsConditionDaoImplTest_initial_data.xml diff --git a/api-2.2/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirConditionDaoImpl_2_2.java b/api-2.2/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirConditionDaoImpl_2_2.java index 868fe4dec..c6d17c80a 100644 --- a/api-2.2/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirConditionDaoImpl_2_2.java +++ b/api-2.2/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirConditionDaoImpl_2_2.java @@ -9,30 +9,15 @@ */ package org.openmrs.module.fhir2.api.dao.impl; -import static org.hibernate.criterion.Restrictions.and; import static org.hibernate.criterion.Restrictions.eq; -import static org.hibernate.criterion.Restrictions.ge; -import static org.hibernate.criterion.Restrictions.le; -import static org.hibernate.criterion.Restrictions.not; import javax.annotation.Nonnull; -import java.math.BigDecimal; -import java.time.Duration; -import java.time.LocalDateTime; -import java.time.Period; -import java.time.ZoneId; -import java.time.temporal.ChronoUnit; -import java.time.temporal.TemporalAmount; -import java.time.temporal.TemporalUnit; -import java.util.Date; import java.util.List; import java.util.Optional; import ca.uhn.fhir.rest.param.DateRangeParam; -import ca.uhn.fhir.rest.param.ParamPrefixEnum; import ca.uhn.fhir.rest.param.QuantityAndListParam; -import ca.uhn.fhir.rest.param.QuantityParam; import ca.uhn.fhir.rest.param.ReferenceAndListParam; import ca.uhn.fhir.rest.param.TokenAndListParam; import lombok.AccessLevel; @@ -46,9 +31,7 @@ import org.openmrs.module.fhir2.FhirConstants; import org.openmrs.module.fhir2.api.dao.FhirConditionDao; import org.openmrs.module.fhir2.api.search.param.SearchParameterMap; -import org.openmrs.module.fhir2.api.util.LocalDateTimeFactory; import org.openmrs.util.PrivilegeConstants; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Primary; import org.springframework.stereotype.Component; @@ -58,9 +41,6 @@ @OpenmrsProfile(openmrsPlatformVersion = "2.2.* - 2.*") public class FhirConditionDaoImpl_2_2 extends BaseFhirDao<Condition> implements FhirConditionDao<Condition> { - @Autowired - private LocalDateTimeFactory localDateTimeFactory; - @Override @Authorized(PrivilegeConstants.GET_CONDITIONS) public Condition get(@Nonnull String uuid) { @@ -99,106 +79,6 @@ private ConditionClinicalStatus convertStatus(String status) { return ConditionClinicalStatus.INACTIVE; } - private Optional<Criterion> handleAgeByDateProperty(@Nonnull String datePropertyName, @Nonnull QuantityParam age) { - BigDecimal value = age.getValue(); - if (value == null) { - throw new IllegalArgumentException("Age value should be provided in " + age); - } - - String unit = age.getUnits(); - if (unit == null) { - throw new IllegalArgumentException("Age unit should be provided in " + age); - } - - LocalDateTime localDateTime = localDateTimeFactory.now(); - - TemporalAmount temporalAmount; - TemporalUnit temporalUnit; - // TODO check if HAPI FHIR already defines these constant strings. These are mostly from - // http://www.hl7.org/fhir/valueset-age-units.html with the exception of "s" which is not - // listed but was seen in FHIR examples: http://www.hl7.org/fhir/datatypes-examples.html#Quantity - switch (unit) { - case "s": - temporalUnit = ChronoUnit.SECONDS; - temporalAmount = Duration.ofSeconds(value.longValue()); - break; - case "min": - temporalUnit = ChronoUnit.MINUTES; - temporalAmount = Duration.ofMinutes(value.longValue()); - break; - case "h": - temporalUnit = ChronoUnit.HOURS; - temporalAmount = Duration.ofHours(value.longValue()); - break; - case "d": - temporalUnit = ChronoUnit.DAYS; - temporalAmount = Period.ofDays(value.intValue()); - break; - case "wk": - temporalUnit = ChronoUnit.WEEKS; - temporalAmount = Period.ofWeeks(value.intValue()); - break; - case "mo": - temporalUnit = ChronoUnit.MONTHS; - temporalAmount = Period.ofMonths(value.intValue()); - break; - case "a": - temporalUnit = ChronoUnit.YEARS; - temporalAmount = Period.ofYears(value.intValue()); - break; - default: - throw new IllegalArgumentException( - "Invalid unit " + unit + " in age " + age + " should be one of 'min', 'h', 'd', 'wk', 'mo', 'a'"); - } - - localDateTime = localDateTime.minus(temporalAmount); - - ParamPrefixEnum prefix = age.getPrefix(); - if (prefix == null) { - prefix = ParamPrefixEnum.EQUAL; - } - - if (prefix == ParamPrefixEnum.EQUAL || prefix == ParamPrefixEnum.NOT_EQUAL) { - // Create a range for the targeted unit; the interval length is determined by the unit and - // its center is `offsetSeconds` in the past. - final long offset; - - // Duration only supports hours as a chunk of seconds - if (temporalUnit == ChronoUnit.HOURS) { - offset = temporalAmount.get(ChronoUnit.SECONDS) / (2 * 3600); - } else { - offset = temporalAmount.get(temporalUnit) / 2; - } - - LocalDateTime lowerBoundDateTime = LocalDateTime.from(localDateTime).minus(Duration.of(offset, temporalUnit)); - Date lowerBound = Date.from(lowerBoundDateTime.atZone(ZoneId.systemDefault()).toInstant()); - - LocalDateTime upperBoundDateTime = LocalDateTime.from(localDateTime).plus(offset, temporalUnit); - Date upperBound = Date.from(upperBoundDateTime.atZone(ZoneId.systemDefault()).toInstant()); - - if (prefix == ParamPrefixEnum.EQUAL) { - return Optional.of(and(ge(datePropertyName, lowerBound), le(datePropertyName, upperBound))); - } else { - return Optional.of(not(and(ge(datePropertyName, lowerBound), le(datePropertyName, upperBound)))); - } - } - - switch (prefix) { - case LESSTHAN_OR_EQUALS: - case LESSTHAN: - case STARTS_AFTER: - return Optional - .of(ge(datePropertyName, Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant()))); - case GREATERTHAN_OR_EQUALS: - case GREATERTHAN: - return Optional - .of(le(datePropertyName, Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant()))); - // Ignoring ENDS_BEFORE as it is not meaningful for age. - } - - return Optional.empty(); - } - @Override protected void setupSearchParams(Criteria criteria, SearchParameterMap theParams) { theParams.getParameters().forEach(entry -> { diff --git a/api-2.2/src/test/java/org/openmrs/module/fhir2/api/dao/impl/FhirConditionDaoImpl_2_2Test.java b/api-2.2/src/test/java/org/openmrs/module/fhir2/api/dao/impl/FhirConditionDaoImpl_2_2Test.java index 993b0cf8e..55870daf1 100644 --- a/api-2.2/src/test/java/org/openmrs/module/fhir2/api/dao/impl/FhirConditionDaoImpl_2_2Test.java +++ b/api-2.2/src/test/java/org/openmrs/module/fhir2/api/dao/impl/FhirConditionDaoImpl_2_2Test.java @@ -20,7 +20,6 @@ import org.hibernate.SessionFactory; import org.junit.Before; import org.junit.Test; -import org.mockito.Mock; import org.openmrs.CodedOrFreeText; import org.openmrs.Condition; import org.openmrs.ConditionClinicalStatus; @@ -28,7 +27,6 @@ import org.openmrs.api.ConceptService; import org.openmrs.api.PatientService; import org.openmrs.module.fhir2.TestFhirSpringConfiguration; -import org.openmrs.module.fhir2.api.util.LocalDateTimeFactory; import org.openmrs.test.BaseModuleContextSensitiveTest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -64,16 +62,12 @@ public class FhirConditionDaoImpl_2_2Test extends BaseModuleContextSensitiveTest @Autowired private ConceptService conceptService; - @Mock - private LocalDateTimeFactory localDateTimeFactory; - private FhirConditionDaoImpl_2_2 dao; @Before public void setUp() { dao = new FhirConditionDaoImpl_2_2(); dao.setSessionFactory(sessionFactory); - dao.setLocalDateTimeFactory(localDateTimeFactory); executeDataSet(CONDITION_INITIAL_DATA_XML); } diff --git a/api-2.2/src/test/java/org/openmrs/module/fhir2/api/search/ConditionSearchQueryImpl_2_2Test.java b/api-2.2/src/test/java/org/openmrs/module/fhir2/api/search/ConditionSearchQueryImpl_2_2Test.java index c1958ec2f..3b371c990 100644 --- a/api-2.2/src/test/java/org/openmrs/module/fhir2/api/search/ConditionSearchQueryImpl_2_2Test.java +++ b/api-2.2/src/test/java/org/openmrs/module/fhir2/api/search/ConditionSearchQueryImpl_2_2Test.java @@ -393,7 +393,7 @@ public void searchForConditions_shouldReturnEmptyListOfConditionsByMultiplePatie } @Test - public void searchForConditions_shouldReturnConditionByPatientNotFoundName() { + public void searchForConditions_shouldReturnEmptyListOfConditionByPatientNotFoundName() { ReferenceParam patientReference = new ReferenceParam(Patient.SP_GIVEN, PATIENT_NOT_FOUND_NAME); ReferenceAndListParam patientList = new ReferenceAndListParam(); patientList.addValue(new ReferenceOrListParam().add(patientReference)); 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 c6d537c23..451c7d266 100644 --- a/api/src/main/java/org/openmrs/module/fhir2/FhirConstants.java +++ b/api/src/main/java/org/openmrs/module/fhir2/FhirConstants.java @@ -277,4 +277,6 @@ public class FhirConstants { public static final String INCLUDE_RESULT_PARAM = "result"; public static final String REVERSE_INCLUDE_SEARCH_HANDLER = "_revinclude.search.handler"; + + public static final String CONDITION_OBSERVATION_CONCEPT_UUID = "1284AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; } diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/BaseDao.java b/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/BaseDao.java index 3f0ff5106..9d64ccfac 100644 --- a/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/BaseDao.java +++ b/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/BaseDao.java @@ -28,6 +28,13 @@ import javax.annotation.Nonnull; import java.math.BigDecimal; +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.Period; +import java.time.ZoneId; +import java.time.temporal.ChronoUnit; +import java.time.temporal.TemporalAmount; +import java.time.temporal.TemporalUnit; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; @@ -80,7 +87,9 @@ import org.hl7.fhir.r4.model.codesystems.AdministrativeGender; import org.openmrs.module.fhir2.FhirConstants; import org.openmrs.module.fhir2.api.search.param.PropParam; +import org.openmrs.module.fhir2.api.util.LocalDateTimeFactory; import org.openmrs.module.fhir2.model.FhirConceptSource; +import org.springframework.beans.factory.annotation.Autowired; /** * <p> @@ -159,6 +168,9 @@ public abstract class BaseDao { private static final BigDecimal APPROX_RANGE = new BigDecimal("0.1"); + @Autowired + private LocalDateTimeFactory localDateTimeFactory; + /** * Converts an {@link Iterable} to a {@link Stream} * @@ -1038,4 +1050,104 @@ public static final class SortState { private String parameter; } + protected Optional<Criterion> handleAgeByDateProperty(@Nonnull String datePropertyName, @Nonnull QuantityParam age) { + BigDecimal value = age.getValue(); + if (value == null) { + throw new IllegalArgumentException("Age value should be provided in " + age); + } + + String unit = age.getUnits(); + if (unit == null) { + throw new IllegalArgumentException("Age unit should be provided in " + age); + } + + LocalDateTime localDateTime = localDateTimeFactory.now(); + + TemporalAmount temporalAmount; + TemporalUnit temporalUnit; + // TODO check if HAPI FHIR already defines these constant strings. These are mostly from + // http://www.hl7.org/fhir/valueset-age-units.html with the exception of "s" which is not + // listed but was seen in FHIR examples: http://www.hl7.org/fhir/datatypes-examples.html#Quantity + switch (unit) { + case "s": + temporalUnit = ChronoUnit.SECONDS; + temporalAmount = Duration.ofSeconds(value.longValue()); + break; + case "min": + temporalUnit = ChronoUnit.MINUTES; + temporalAmount = Duration.ofMinutes(value.longValue()); + break; + case "h": + temporalUnit = ChronoUnit.HOURS; + temporalAmount = Duration.ofHours(value.longValue()); + break; + case "d": + temporalUnit = ChronoUnit.DAYS; + temporalAmount = Period.ofDays(value.intValue()); + break; + case "wk": + temporalUnit = ChronoUnit.WEEKS; + temporalAmount = Period.ofWeeks(value.intValue()); + break; + case "mo": + temporalUnit = ChronoUnit.MONTHS; + temporalAmount = Period.ofMonths(value.intValue()); + break; + case "a": + temporalUnit = ChronoUnit.YEARS; + temporalAmount = Period.ofYears(value.intValue()); + break; + default: + throw new IllegalArgumentException( + "Invalid unit " + unit + " in age " + age + " should be one of 'min', 'h', 'd', 'wk', 'mo', 'a'"); + } + + localDateTime = localDateTime.minus(temporalAmount); + + ParamPrefixEnum prefix = age.getPrefix(); + if (prefix == null) { + prefix = ParamPrefixEnum.EQUAL; + } + + if (prefix == ParamPrefixEnum.EQUAL || prefix == ParamPrefixEnum.NOT_EQUAL) { + // Create a range for the targeted unit; the interval length is determined by the unit and + // its center is `offsetSeconds` in the past. + final long offset; + + // Duration only supports hours as a chunk of seconds + if (temporalUnit == ChronoUnit.HOURS) { + offset = temporalAmount.get(ChronoUnit.SECONDS) / (2 * 3600); + } else { + offset = temporalAmount.get(temporalUnit) / 2; + } + + LocalDateTime lowerBoundDateTime = LocalDateTime.from(localDateTime).minus(Duration.of(offset, temporalUnit)); + Date lowerBound = Date.from(lowerBoundDateTime.atZone(ZoneId.systemDefault()).toInstant()); + + LocalDateTime upperBoundDateTime = LocalDateTime.from(localDateTime).plus(offset, temporalUnit); + Date upperBound = Date.from(upperBoundDateTime.atZone(ZoneId.systemDefault()).toInstant()); + + if (prefix == ParamPrefixEnum.EQUAL) { + return Optional.of(and(ge(datePropertyName, lowerBound), le(datePropertyName, upperBound))); + } else { + return Optional.of(not(and(ge(datePropertyName, lowerBound), le(datePropertyName, upperBound)))); + } + } + + switch (prefix) { + case LESSTHAN_OR_EQUALS: + case LESSTHAN: + case STARTS_AFTER: + return Optional + .of(ge(datePropertyName, Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant()))); + case GREATERTHAN_OR_EQUALS: + case GREATERTHAN: + return Optional + .of(le(datePropertyName, Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant()))); + // Ignoring ENDS_BEFORE as it is not meaningful for age. + } + + return Optional.empty(); + } + } diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirConditionDaoImpl.java b/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirConditionDaoImpl.java new file mode 100644 index 000000000..9d4243021 --- /dev/null +++ b/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirConditionDaoImpl.java @@ -0,0 +1,135 @@ +/* + * 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.api.dao.impl; + +import static org.hibernate.criterion.Restrictions.eq; + +import javax.annotation.Nonnull; + +import java.util.List; +import java.util.Optional; + +import ca.uhn.fhir.rest.param.DateRangeParam; +import ca.uhn.fhir.rest.param.QuantityAndListParam; +import ca.uhn.fhir.rest.param.ReferenceAndListParam; +import ca.uhn.fhir.rest.param.TokenAndListParam; +import lombok.AccessLevel; +import lombok.Setter; +import org.hibernate.Criteria; +import org.hibernate.SessionFactory; +import org.hibernate.criterion.Criterion; +import org.openmrs.Obs; +import org.openmrs.annotation.Authorized; +import org.openmrs.annotation.OpenmrsProfile; +import org.openmrs.module.fhir2.FhirConstants; +import org.openmrs.module.fhir2.api.dao.FhirConditionDao; +import org.openmrs.module.fhir2.api.search.param.SearchParameterMap; +import org.openmrs.util.PrivilegeConstants; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Component; + +@Component +@Setter(AccessLevel.PUBLIC) +@OpenmrsProfile(openmrsPlatformVersion = "2.0.5 - 2.1.*") +public class FhirConditionDaoImpl extends BaseFhirDao<Obs> implements FhirConditionDao<Obs> { + + @Qualifier("sessionFactory") + @Autowired + private SessionFactory sessionFactory; + + @Override + @Authorized(PrivilegeConstants.GET_OBS) + public Obs get(@Nonnull String uuid) { + return super.get(uuid); + } + + @Override + @Authorized(PrivilegeConstants.EDIT_OBS) + public Obs createOrUpdate(@Nonnull Obs newEntry) { + return super.createOrUpdate(newEntry); + } + + @Override + @Authorized(PrivilegeConstants.DELETE_OBS) + public Obs delete(@Nonnull String uuid) { + return super.delete(uuid); + } + + @Override + @Authorized(PrivilegeConstants.GET_OBS) + public List<String> getSearchResultUuids(@Nonnull SearchParameterMap theParams) { + return super.getSearchResultUuids(theParams); + } + + @Override + @Authorized(PrivilegeConstants.GET_OBS) + public List<Obs> getSearchResults(@Nonnull SearchParameterMap theParams, @Nonnull List<String> matchingResourceUuids, + int firstResult, int lastResult) { + return super.getSearchResults(theParams, matchingResourceUuids, firstResult, lastResult); + } + + @Override + protected void setupSearchParams(Criteria criteria, SearchParameterMap theParams) { + criteria.createAlias("concept", "c"); + criteria.add(eq("c.uuid", FhirConstants.CONDITION_OBSERVATION_CONCEPT_UUID)); + theParams.getParameters().forEach(entry -> { + switch (entry.getKey()) { + case FhirConstants.PATIENT_REFERENCE_SEARCH_HANDLER: + entry.getValue().forEach( + param -> handlePatientReference(criteria, (ReferenceAndListParam) param.getParam(), "person")); + break; + case FhirConstants.CODED_SEARCH_HANDLER: + entry.getValue().forEach(param -> handleCode(criteria, (TokenAndListParam) param.getParam())); + break; + case FhirConstants.DATE_RANGE_SEARCH_HANDLER: + entry.getValue() + .forEach(param -> handleDateRange(param.getPropertyName(), (DateRangeParam) param.getParam()) + .ifPresent(criteria::add)); + break; + case FhirConstants.QUANTITY_SEARCH_HANDLER: + entry.getValue().forEach(param -> handleOnsetAge(criteria, (QuantityAndListParam) param.getParam())); + break; + case FhirConstants.COMMON_SEARCH_HANDLER: + handleCommonSearchParameters(entry.getValue()).ifPresent(criteria::add); + break; + } + }); + } + + private void handleCode(Criteria criteria, TokenAndListParam code) { + if (code != null) { + criteria.createAlias("valueCoded", "vc"); + handleCodeableConcept(criteria, code, "vc", "map", "term").ifPresent(criteria::add); + } + } + + private void handleOnsetAge(Criteria criteria, QuantityAndListParam onsetAge) { + handleAndListParam(onsetAge, onsetAgeParam -> handleAgeByDateProperty("obsDatetime", onsetAgeParam)) + .ifPresent(criteria::add); + } + + @Override + protected Optional<Criterion> handleLastUpdated(DateRangeParam param) { + return super.handleLastUpdatedImmutable(param); + } + + @Override + protected String paramToProp(@Nonnull String param) { + switch (param) { + case org.hl7.fhir.r4.model.Condition.SP_ONSET_DATE: + return "obsDatetime"; + case org.hl7.fhir.r4.model.Condition.SP_RECORDED_DATE: + return "dateCreated"; + } + + return super.paramToProp(param); + } +} diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/impl/FhirConditionServiceImpl.java b/api/src/main/java/org/openmrs/module/fhir2/api/impl/FhirConditionServiceImpl.java index 0b88891ac..0a4b0ee04 100644 --- a/api/src/main/java/org/openmrs/module/fhir2/api/impl/FhirConditionServiceImpl.java +++ b/api/src/main/java/org/openmrs/module/fhir2/api/impl/FhirConditionServiceImpl.java @@ -9,69 +9,68 @@ */ package org.openmrs.module.fhir2.api.impl; -import javax.annotation.Nonnull; +import javax.transaction.Transactional; import java.util.HashSet; import ca.uhn.fhir.model.api.Include; +import ca.uhn.fhir.rest.annotation.Sort; import ca.uhn.fhir.rest.api.SortSpec; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.param.DateRangeParam; import ca.uhn.fhir.rest.param.QuantityAndListParam; import ca.uhn.fhir.rest.param.ReferenceAndListParam; import ca.uhn.fhir.rest.param.TokenAndListParam; -import ca.uhn.fhir.rest.server.exceptions.NotImplementedOperationException; import lombok.AccessLevel; import lombok.Getter; +import lombok.Setter; import org.hl7.fhir.r4.model.Condition; -import org.openmrs.Auditable; -import org.openmrs.OpenmrsObject; +import org.openmrs.Obs; +import org.openmrs.annotation.OpenmrsProfile; +import org.openmrs.module.fhir2.FhirConstants; import org.openmrs.module.fhir2.api.FhirConditionService; -import org.openmrs.module.fhir2.api.dao.FhirDao; -import org.openmrs.module.fhir2.api.translators.OpenmrsFhirTranslator; +import org.openmrs.module.fhir2.api.dao.FhirConditionDao; +import org.openmrs.module.fhir2.api.search.SearchQuery; +import org.openmrs.module.fhir2.api.search.SearchQueryInclude; +import org.openmrs.module.fhir2.api.search.param.SearchParameterMap; +import org.openmrs.module.fhir2.api.translators.ConditionTranslator; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component +@Transactional @Getter(AccessLevel.PROTECTED) -public class FhirConditionServiceImpl<U extends OpenmrsObject & Auditable> extends BaseFhirService<Condition, U> implements FhirConditionService { +@Setter(AccessLevel.PACKAGE) +@OpenmrsProfile(openmrsPlatformVersion = "2.0.5 - 2.1.*") +public class FhirConditionServiceImpl extends BaseFhirService<Condition, Obs> implements FhirConditionService { - private static final String MESSAGE = "Condition is not implemented for this version of OpenMRS"; + @Autowired + private FhirConditionDao<org.openmrs.Obs> dao; - @Override - public Condition get(@Nonnull String uuid) { - throw new NotImplementedOperationException(MESSAGE); - } - - @Override - protected FhirDao<U> getDao() { - throw new NotImplementedOperationException(MESSAGE); - } + @Autowired + private ConditionTranslator<org.openmrs.Obs> translator; - @Override - public Condition create(@Nonnull Condition newResource) { - throw new NotImplementedOperationException(MESSAGE); - } + @Autowired + private SearchQueryInclude<Condition> searchQueryInclude; - @Override - public Condition update(@Nonnull String uuid, @Nonnull Condition updatedResource) { - throw new NotImplementedOperationException(MESSAGE); - } - - @Override - public Condition delete(@Nonnull String uuid) { - throw new NotImplementedOperationException(MESSAGE); - } - - @Override - protected OpenmrsFhirTranslator<U, Condition> getTranslator() { - throw new NotImplementedOperationException(MESSAGE); - } + @Autowired + private SearchQuery<org.openmrs.Obs, Condition, FhirConditionDao<org.openmrs.Obs>, ConditionTranslator<org.openmrs.Obs>, SearchQueryInclude<Condition>> searchQuery; @Override public IBundleProvider searchConditions(ReferenceAndListParam patientParam, TokenAndListParam code, TokenAndListParam clinicalStatus, DateRangeParam onsetDate, QuantityAndListParam onsetAge, - DateRangeParam recordedDate, TokenAndListParam id, DateRangeParam lastUpdated, SortSpec sort, + DateRangeParam recordedDate, TokenAndListParam id, DateRangeParam lastUpdated, @Sort SortSpec sort, HashSet<Include> includes) { - throw new NotImplementedOperationException(MESSAGE); + + SearchParameterMap theParams = new SearchParameterMap() + .addParameter(FhirConstants.PATIENT_REFERENCE_SEARCH_HANDLER, patientParam) + .addParameter(FhirConstants.CODED_SEARCH_HANDLER, code) + .addParameter(FhirConstants.QUANTITY_SEARCH_HANDLER, onsetAge) + .addParameter(FhirConstants.DATE_RANGE_SEARCH_HANDLER, "obsDatetime", onsetDate) + .addParameter(FhirConstants.DATE_RANGE_SEARCH_HANDLER, "dateCreated", recordedDate) + .addParameter(FhirConstants.COMMON_SEARCH_HANDLER, FhirConstants.ID_PROPERTY, id) + .addParameter(FhirConstants.COMMON_SEARCH_HANDLER, FhirConstants.LAST_UPDATED_PROPERTY, lastUpdated) + .addParameter(FhirConstants.INCLUDE_SEARCH_HANDLER, includes).setSortSpec(sort); + return searchQuery.getQueryResults(theParams, dao, translator, searchQueryInclude); } } diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/search/SearchQueryInclude.java b/api/src/main/java/org/openmrs/module/fhir2/api/search/SearchQueryInclude.java index 49f16660b..d4afa5dde 100644 --- a/api/src/main/java/org/openmrs/module/fhir2/api/search/SearchQueryInclude.java +++ b/api/src/main/java/org/openmrs/module/fhir2/api/search/SearchQueryInclude.java @@ -25,6 +25,7 @@ import org.apache.commons.collections.CollectionUtils; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.AllergyIntolerance; +import org.hl7.fhir.r4.model.Condition; import org.hl7.fhir.r4.model.DiagnosticReport; import org.hl7.fhir.r4.model.Encounter; import org.hl7.fhir.r4.model.Location; @@ -443,6 +444,10 @@ private Set<IBaseResource> handlePatientInclude(List<U> resourceList, String par resourceList.forEach( resource -> uniquePatientUUIDs.add(getIdFromReference(((ServiceRequest) resource).getSubject()))); break; + case FhirConstants.CONDITION: + resourceList.forEach( + resource -> uniquePatientUUIDs.add(getIdFromReference(((Condition) resource).getSubject()))); + break; } uniquePatientUUIDs.removeIf(Objects::isNull); diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/ConditionClinicalStatusTranslatorImpl.java b/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/ConditionClinicalStatusTranslatorImpl.java new file mode 100644 index 000000000..485f195e4 --- /dev/null +++ b/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/ConditionClinicalStatusTranslatorImpl.java @@ -0,0 +1,34 @@ +/* + * 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.api.translators.impl; + +import org.hl7.fhir.r4.model.CodeableConcept; +import org.openmrs.Obs; +import org.openmrs.annotation.OpenmrsProfile; +import org.openmrs.module.fhir2.api.translators.ConditionClinicalStatusTranslator; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Component; + +@Primary +@Component +@OpenmrsProfile(openmrsPlatformVersion = "2.0.5 - 2.1.*") +public class ConditionClinicalStatusTranslatorImpl implements ConditionClinicalStatusTranslator<Obs> { + + @Override + public CodeableConcept toFhirResource(Obs clinicalStatus) { + return null; + } + + @Override + public Obs toOpenmrsType(CodeableConcept codeableConcept) { + return null; + } + +} diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/ConditionTranslatorImpl.java b/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/ConditionTranslatorImpl.java new file mode 100644 index 000000000..0e43e64fc --- /dev/null +++ b/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/ConditionTranslatorImpl.java @@ -0,0 +1,127 @@ +/* + * 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.api.translators.impl; + +import static org.apache.commons.lang3.Validate.notNull; + +import javax.annotation.Nonnull; + +import java.util.Date; + +import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import lombok.AccessLevel; +import lombok.Setter; +import org.hibernate.proxy.HibernateProxy; +import org.hl7.fhir.r4.model.CodeableConcept; +import org.hl7.fhir.r4.model.DateTimeType; +import org.openmrs.Concept; +import org.openmrs.Obs; +import org.openmrs.Patient; +import org.openmrs.Person; +import org.openmrs.User; +import org.openmrs.annotation.OpenmrsProfile; +import org.openmrs.api.ConceptService; +import org.openmrs.api.db.hibernate.HibernateUtil; +import org.openmrs.module.fhir2.FhirConstants; +import org.openmrs.module.fhir2.api.translators.ConceptTranslator; +import org.openmrs.module.fhir2.api.translators.ConditionClinicalStatusTranslator; +import org.openmrs.module.fhir2.api.translators.ConditionTranslator; +import org.openmrs.module.fhir2.api.translators.PatientReferenceTranslator; +import org.openmrs.module.fhir2.api.translators.PractitionerReferenceTranslator; +import org.openmrs.module.fhir2.api.translators.ProvenanceTranslator; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Setter(AccessLevel.PACKAGE) +@Component +@OpenmrsProfile(openmrsPlatformVersion = "2.0.5 - 2.1.*") +public class ConditionTranslatorImpl implements ConditionTranslator<Obs> { + + @Autowired + private PatientReferenceTranslator patientReferenceTranslator; + + @Autowired + private PractitionerReferenceTranslator<User> practitionerReferenceTranslator; + + @Autowired + private ConceptTranslator conceptTranslator; + + @Autowired + private ProvenanceTranslator<Obs> provenanceTranslator; + + @Autowired + private ConceptService conceptService; + + @Autowired + private ConditionClinicalStatusTranslator<Obs> conditionClinicalStatusTranslator; + + @Override + public org.hl7.fhir.r4.model.Condition toFhirResource(@Nonnull Obs obsCondition) { + notNull(obsCondition, "The Openmrs Condition object should not be null"); + + org.hl7.fhir.r4.model.Condition fhirCondition = new org.hl7.fhir.r4.model.Condition(); + fhirCondition.setId(obsCondition.getUuid()); + + Person obsPerson = obsCondition.getPerson(); + if (obsPerson != null) { + if (obsPerson instanceof HibernateProxy) { + obsPerson = HibernateUtil.getRealObjectFromProxy(obsPerson); + } + + if (obsPerson instanceof Patient) { + fhirCondition.setSubject(patientReferenceTranslator.toFhirResource((Patient) obsPerson)); + } + } + if (obsCondition.getValueCoded() != null) { + fhirCondition.setCode(conceptTranslator.toFhirResource(obsCondition.getValueCoded())); + } + fhirCondition.setOnset(new DateTimeType().setValue(obsCondition.getObsDatetime())); + fhirCondition.setRecorder(practitionerReferenceTranslator.toFhirResource(obsCondition.getCreator())); + fhirCondition.setRecordedDate(obsCondition.getDateCreated()); + fhirCondition.getMeta().setLastUpdated(obsCondition.getDateChanged()); + fhirCondition.addContained(provenanceTranslator.getCreateProvenance(obsCondition)); + fhirCondition.addContained(provenanceTranslator.getUpdateProvenance(obsCondition)); + + return fhirCondition; + } + + @Override + public Obs toOpenmrsType(@Nonnull org.hl7.fhir.r4.model.Condition condition) { + notNull(condition, "The Condition object should not be null"); + return this.toOpenmrsType(new Obs(), condition); + } + + @Override + public Obs toOpenmrsType(@Nonnull Obs existingObsCondition, @Nonnull org.hl7.fhir.r4.model.Condition condition) { + notNull(existingObsCondition, "The existing Openmrs Obs Condition object should not be null"); + notNull(condition, "The Condition object should not be null"); + existingObsCondition.setUuid(condition.getIdElement().getIdPart()); + CodeableConcept codeableConcept = condition.getCode(); + existingObsCondition.setValueCoded(conceptTranslator.toOpenmrsType(codeableConcept)); + existingObsCondition.setPerson(patientReferenceTranslator.toOpenmrsType(condition.getSubject())); + Concept problemList = conceptService.getConceptByUuid(FhirConstants.CONDITION_OBSERVATION_CONCEPT_UUID); + if (problemList != null) { + existingObsCondition.setConcept(problemList); + } else { + throw new InternalErrorException( + "Concept " + FhirConstants.CONDITION_OBSERVATION_CONCEPT_UUID + " ProblemList Not found"); + } + Date onsetTime = condition.getOnsetDateTimeType().getValue(); + Date recordTime = condition.getRecordedDateElement().getValue(); + if (onsetTime != null) { + existingObsCondition.setObsDatetime(onsetTime); + } else if (recordTime != null) { + existingObsCondition.setObsDatetime(recordTime); + } + existingObsCondition.setCreator(practitionerReferenceTranslator.toOpenmrsType(condition.getRecorder())); + + return existingObsCondition; + } +} diff --git a/api/src/test/java/org/openmrs/module/fhir2/MockedCalendarFactoryConfiguration.java b/api/src/test/java/org/openmrs/module/fhir2/MockedCalendarFactoryConfiguration.java new file mode 100644 index 000000000..36e2d2b37 --- /dev/null +++ b/api/src/test/java/org/openmrs/module/fhir2/MockedCalendarFactoryConfiguration.java @@ -0,0 +1,27 @@ +/* + * 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; + +import org.mockito.Mockito; +import org.openmrs.module.fhir2.api.util.LocalDateTimeFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; + +@Configuration +public class MockedCalendarFactoryConfiguration { + + @Bean + @Primary + public LocalDateTimeFactory getCalendarFactory() { + return Mockito.mock(LocalDateTimeFactory.class); + } + +} diff --git a/api/src/test/java/org/openmrs/module/fhir2/api/dao/impl/FhirConditionDaoImplTest.java b/api/src/test/java/org/openmrs/module/fhir2/api/dao/impl/FhirConditionDaoImplTest.java new file mode 100644 index 000000000..3949b6099 --- /dev/null +++ b/api/src/test/java/org/openmrs/module/fhir2/api/dao/impl/FhirConditionDaoImplTest.java @@ -0,0 +1,170 @@ +/* + * 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.api.dao.impl; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import java.util.Collection; +import java.util.Date; +import java.util.List; + +import ca.uhn.fhir.rest.param.TokenAndListParam; +import ca.uhn.fhir.rest.param.TokenParam; +import org.junit.Before; +import org.junit.Test; +import org.openmrs.Obs; +import org.openmrs.api.ConceptService; +import org.openmrs.api.PatientService; +import org.openmrs.module.fhir2.FhirConstants; +import org.openmrs.module.fhir2.TestFhirSpringConfiguration; +import org.openmrs.module.fhir2.api.dao.FhirConditionDao; +import org.openmrs.module.fhir2.api.search.param.SearchParameterMap; +import org.openmrs.test.BaseModuleContextSensitiveTest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; + +@ContextConfiguration(classes = TestFhirSpringConfiguration.class, inheritLocations = false) +public class FhirConditionDaoImplTest extends BaseModuleContextSensitiveTest { + + private static final String VOIDED_OBS_CONDITION_UUID = "94dhs003-a55d-43c4-ac7a-bd6d1ba63388"; + + private static final String EXISTING_OBS_CONDITION_UUID = "86sgf-1f7d-4394-a316-0a458edf28c4"; + + private static final String WRONG_OBS_CONDITION_UUID = "430bbb70-6a9c-4e1e-badb-9d1034b1b5e9"; + + private static final String NEW_OBS_CONDITION_UUID = "NEWbbb70-6a9c-4e1e-badb-9d1034b1b5e9"; + + private static final String OBS_CONDITION_INITIAL_DATA_XML = "org/openmrs/module/fhir2/api/dao/impl/FhirObsConditionDaoImplTest_initial_data.xml"; + + private static final String OBS_CONDITION_CONCEPT_ID = "116128"; + + private static final String OBS_CONDITION_CODED_CONCEPT_ID = "1284"; + + private static final String WORNG_OBS_CONDITION_CONCEPT_ID = "116145"; + + private static final Integer PATIENT_ID = 6; + + @Autowired + private FhirConditionDao<Obs> dao; + + @Autowired + private ConceptService conceptService; + + @Autowired + private PatientService patientService; + + @Before + public void setUp() throws Exception { + executeDataSet(OBS_CONDITION_INITIAL_DATA_XML); + } + + @Test + public void get_shouldGetObsConditionByUuid() { + Obs result = dao.get(EXISTING_OBS_CONDITION_UUID); + assertThat(result, notNullValue()); + assertThat(result.getUuid(), equalTo(EXISTING_OBS_CONDITION_UUID)); + } + + @Test + public void get_shouldReturnNullIfObsNotFoundByUuid() { + Obs result = dao.get(WRONG_OBS_CONDITION_UUID); + assertThat(result, nullValue()); + } + + @Test + public void search_shouldReturnConditonResourceUuidsWithObsCoded_1284() { + TokenAndListParam code = new TokenAndListParam(); + TokenParam codingToken = new TokenParam(); + codingToken.setValue(OBS_CONDITION_CONCEPT_ID); + code.addAnd(codingToken); + + SearchParameterMap theParams = new SearchParameterMap(); + theParams.addParameter(FhirConstants.CODED_SEARCH_HANDLER, code); + + List<String> matchingResourceUuids = dao.getSearchResultUuids(theParams); + assertEquals(2, matchingResourceUuids.size()); + } + + @Test + public void search_shouldReturnNoConditonResourceUuidsWhenNoWrongConceptIdIsUsed() { + TokenAndListParam code = new TokenAndListParam(); + TokenParam codingToken = new TokenParam(); + codingToken.setValue(WORNG_OBS_CONDITION_CONCEPT_ID); + code.addAnd(codingToken); + + SearchParameterMap theParams = new SearchParameterMap(); + theParams.addParameter(FhirConstants.CODED_SEARCH_HANDLER, code); + + List<String> matchingResourceUuids = dao.getSearchResultUuids(theParams); + assertEquals(matchingResourceUuids.size(), 0); + } + + @Test + public void search_shouldReturnNoVoidedConditonResourceUuids() { + TokenAndListParam code = new TokenAndListParam(); + TokenParam codingToken = new TokenParam(); + codingToken.setValue(OBS_CONDITION_CONCEPT_ID); + code.addAnd(codingToken); + + SearchParameterMap theParams = new SearchParameterMap(); + theParams.addParameter(FhirConstants.CODED_SEARCH_HANDLER, code); + + List<String> matchingResourceUuids = dao.getSearchResultUuids(theParams); + assertFalse(matchingResourceUuids.contains(VOIDED_OBS_CONDITION_UUID)); + } + + @Test + public void search_shouldReturnSearchQuery() { + TokenAndListParam code = new TokenAndListParam(); + TokenParam codingToken = new TokenParam(); + codingToken.setValue(OBS_CONDITION_CONCEPT_ID); + code.addAnd(codingToken); + + SearchParameterMap theParams = new SearchParameterMap(); + theParams.addParameter(FhirConstants.CODED_SEARCH_HANDLER, code); + + List<String> matchingResourceUuids = dao.getSearchResultUuids(theParams); + Collection<Obs> obs = dao.getSearchResults(theParams, matchingResourceUuids); + assertThat(obs, notNullValue()); + assertEquals(obs.size(), 2); + } + + @Test + public void shouldSaveNewObsCondition() { + Obs obsCondition = new Obs(); + obsCondition.setUuid(NEW_OBS_CONDITION_UUID); + obsCondition.setObsDatetime(new Date()); + obsCondition.setConcept(conceptService.getConcept(OBS_CONDITION_CODED_CONCEPT_ID)); + + org.openmrs.Patient patient = patientService.getPatient(PATIENT_ID); + obsCondition.setPerson(patient); + obsCondition.setValueCoded(conceptService.getConcept(OBS_CONDITION_CONCEPT_ID)); + + dao.createOrUpdate(obsCondition); + + Obs result = dao.get(NEW_OBS_CONDITION_UUID); + assertThat(result, notNullValue()); + assertThat(result.getUuid(), equalTo(NEW_OBS_CONDITION_UUID)); + } + + @Test + public void shouldDeleteObsCondition() { + Obs result = dao.delete(EXISTING_OBS_CONDITION_UUID); + assertThat(result, notNullValue()); + assertThat(result.getUuid(), equalTo(EXISTING_OBS_CONDITION_UUID)); + assertThat(result.getVoided(), equalTo(true)); + + } +} diff --git a/api/src/test/java/org/openmrs/module/fhir2/api/impl/FhirConditionServiceImplTest.java b/api/src/test/java/org/openmrs/module/fhir2/api/impl/FhirConditionServiceImplTest.java index f4a62d20c..4948571dc 100644 --- a/api/src/test/java/org/openmrs/module/fhir2/api/impl/FhirConditionServiceImplTest.java +++ b/api/src/test/java/org/openmrs/module/fhir2/api/impl/FhirConditionServiceImplTest.java @@ -9,62 +9,243 @@ */ package org.openmrs.module.fhir2.api.impl; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.notNullValue; import static org.junit.Assert.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.when; -import ca.uhn.fhir.rest.server.exceptions.NotImplementedOperationException; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; + +import ca.uhn.fhir.model.api.Include; +import ca.uhn.fhir.rest.api.SortSpec; +import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.rest.param.DateRangeParam; +import ca.uhn.fhir.rest.param.QuantityAndListParam; +import ca.uhn.fhir.rest.param.QuantityOrListParam; +import ca.uhn.fhir.rest.param.QuantityParam; +import ca.uhn.fhir.rest.param.ReferenceAndListParam; +import ca.uhn.fhir.rest.param.ReferenceOrListParam; +import ca.uhn.fhir.rest.param.ReferenceParam; +import ca.uhn.fhir.rest.param.TokenAndListParam; +import ca.uhn.fhir.rest.param.TokenOrListParam; +import ca.uhn.fhir.rest.param.TokenParam; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Condition; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; -import org.openmrs.OpenmrsObject; +import org.openmrs.Obs; +import org.openmrs.module.fhir2.FhirConstants; +import org.openmrs.module.fhir2.api.FhirGlobalPropertyService; +import org.openmrs.module.fhir2.api.dao.FhirConditionDao; +import org.openmrs.module.fhir2.api.search.SearchQuery; +import org.openmrs.module.fhir2.api.search.SearchQueryBundleProvider; +import org.openmrs.module.fhir2.api.search.SearchQueryInclude; +import org.openmrs.module.fhir2.api.search.param.SearchParameterMap; +import org.openmrs.module.fhir2.api.translators.ConditionTranslator; @RunWith(MockitoJUnitRunner.class) public class FhirConditionServiceImplTest { - private static final String BAD_CONDITION_UUID = "90378769-f1a4-46af-b08b-d9fe8a09034j"; + private static final String OBS_UUID = "12345-abcde-12345"; + + private static final String WRONG_OBS_CONDITION_UUID = "90378769-f1a4-46af-034j"; + + private static final String LAST_UPDATED_DATE = "2020-09-03"; + + private static final int START_INDEX = 0; + + private static final int END_INDEX = 10; + + @Mock + private FhirConditionDao<Obs> dao; + + @Mock + private FhirGlobalPropertyService globalPropertyService; - private FhirConditionServiceImpl<?> conditionService; + @Mock + private SearchQueryInclude<Condition> searchQueryInclude; - private Condition condition; + @Mock + private SearchQuery<org.openmrs.Obs, Condition, FhirConditionDao<org.openmrs.Obs>, ConditionTranslator<org.openmrs.Obs>, SearchQueryInclude<Condition>> searchQuery; + + @Mock + private ConditionTranslator<Obs> translator;; + + private FhirConditionServiceImpl fhirConditionService; + + private Obs obsCondition; + + private org.hl7.fhir.r4.model.Condition condition; @Before - @SuppressWarnings("rawtypes") public void setup() { - conditionService = new FhirConditionServiceImpl() { + fhirConditionService = new FhirConditionServiceImpl() { @Override - protected void validateObject(OpenmrsObject object) { + protected void validateObject(Obs object) { } }; - condition = new Condition(); - condition.setId(BAD_CONDITION_UUID); + fhirConditionService.setDao(dao); + fhirConditionService.setTranslator(translator); + fhirConditionService.setSearchQueryInclude(searchQueryInclude); + fhirConditionService.setSearchQuery(searchQuery); + + obsCondition = new Obs(); + obsCondition.setUuid(OBS_UUID); + + condition = new org.hl7.fhir.r4.model.Condition(); + condition.setId(OBS_UUID); + } + + private List<IBaseResource> get(IBundleProvider results) { + return results.getResources(START_INDEX, END_INDEX); + } + + @Test + public void getObsConditionByUuid_shouldReturnConditionByUuid() { + when(dao.get(OBS_UUID)).thenReturn(obsCondition); + when(translator.toFhirResource(obsCondition)).thenReturn(condition); + + Condition result = fhirConditionService.get(OBS_UUID); + assertThat(result, notNullValue()); + assertThat(result.getId(), equalTo(OBS_UUID)); + } + + @Test + public void shouldThrowExceptionWhenGetMissingUuid() { + assertThrows(ResourceNotFoundException.class, () -> fhirConditionService.get(WRONG_OBS_CONDITION_UUID)); + } + + @Test + public void create_shouldCreateNewCondition() { + when(translator.toFhirResource(obsCondition)).thenReturn(condition); + when(dao.createOrUpdate(obsCondition)).thenReturn(obsCondition); + when(translator.toOpenmrsType(condition)).thenReturn(obsCondition); + + org.hl7.fhir.r4.model.Condition result = fhirConditionService.create(condition); + + assertThat(result, notNullValue()); + assertThat(result.getId(), notNullValue()); + assertThat(result.getId(), equalTo(OBS_UUID)); } @Test - public void get_shouldThrowNotImplementedOperationException() { - assertThrows(NotImplementedOperationException.class, () -> conditionService.get(BAD_CONDITION_UUID)); + public void update_shouldUpdateExistingObsCondition() { + when(dao.get(OBS_UUID)).thenReturn(obsCondition); + when(translator.toFhirResource(obsCondition)).thenReturn(condition); + when(dao.createOrUpdate(obsCondition)).thenReturn(obsCondition); + when(translator.toOpenmrsType(any(Obs.class), any(org.hl7.fhir.r4.model.Condition.class))).thenReturn(obsCondition); + + org.hl7.fhir.r4.model.Condition result = fhirConditionService.update(OBS_UUID, condition); + + assertThat(result, notNullValue()); + assertThat(result.getId(), notNullValue()); + assertThat(result.getId(), equalTo(OBS_UUID)); } @Test - public void create_shouldThrowNotImplementedOperationException() { - assertThrows(NotImplementedOperationException.class, () -> conditionService.create(condition)); + public void update_shouldThrowExceptionWhenIdIsNull() { + org.hl7.fhir.r4.model.Condition condition = new org.hl7.fhir.r4.model.Condition(); + + assertThrows(InvalidRequestException.class, () -> fhirConditionService.update(null, condition)); } @Test - public void update_shouldThrowNotImplementedOperationException() { - assertThrows(NotImplementedOperationException.class, () -> conditionService.update(BAD_CONDITION_UUID, condition)); + public void update_shouldThrowExceptionWhenConditionIsNull() { + assertThrows(InvalidRequestException.class, () -> fhirConditionService.update(OBS_UUID, null)); + } + + @Test + public void update_shouldThrowExceptionWhenConditionIdDoesNotMatchCurrentId() { + org.hl7.fhir.r4.model.Condition condition = new org.hl7.fhir.r4.model.Condition(); + condition.setId(OBS_UUID); + + assertThrows(InvalidRequestException.class, () -> fhirConditionService.update(WRONG_OBS_CONDITION_UUID, condition)); } @Test - public void delete_shouldThrowNotImplementedOperationException() { - assertThrows(NotImplementedOperationException.class, () -> conditionService.delete(BAD_CONDITION_UUID)); + public void delete_shouldDeleteExistingCondition() { + when(dao.delete(OBS_UUID)).thenReturn(obsCondition); + when(translator.toFhirResource(obsCondition)).thenReturn(condition); + + org.hl7.fhir.r4.model.Condition result = fhirConditionService.delete(OBS_UUID); + + assertThat(result, notNullValue()); } @Test - public void searchConditions_shouldThrowNotImplementedOperationException() { - assertThrows(NotImplementedOperationException.class, - () -> conditionService.searchConditions(null, null, null, null, null, null, null, null, null, null)); + public void delete_shouldThrowExceptionWhenIdIsNull() { + assertThrows(InvalidRequestException.class, () -> fhirConditionService.delete(null)); } + + @Test + public void searchConditions_shouldReturnTranslatedConditionReturnedByDao() { + ReferenceAndListParam patientReference = new ReferenceAndListParam(); + patientReference.addValue( + new ReferenceOrListParam().add(new ReferenceParam(org.hl7.fhir.r4.model.Patient.SP_GIVEN, "patient name"))); + + TokenAndListParam codeList = new TokenAndListParam(); + codeList.addValue(new TokenOrListParam().add(new TokenParam("test code"))); + + TokenAndListParam clinicalList = new TokenAndListParam(); + clinicalList.addValue(new TokenOrListParam().add(new TokenParam("test clinical"))); + + DateRangeParam onsetDate = new DateRangeParam().setLowerBound("lower date").setUpperBound("upper date"); + + QuantityAndListParam onsetAge = new QuantityAndListParam(); + onsetAge.addValue(new QuantityOrListParam().add(new QuantityParam(12))); + + DateRangeParam recordDate = new DateRangeParam().setLowerBound("lower record date") + .setUpperBound("upper record date"); + + TokenAndListParam uuid = new TokenAndListParam().addAnd(new TokenParam(OBS_UUID)); + + DateRangeParam lastUpdated = new DateRangeParam().setLowerBound(LAST_UPDATED_DATE).setUpperBound(LAST_UPDATED_DATE); + + SortSpec sort = new SortSpec("sort param"); + + HashSet<Include> includes = new HashSet<>(); + + SearchParameterMap theParams = new SearchParameterMap() + .addParameter(FhirConstants.PATIENT_REFERENCE_SEARCH_HANDLER, patientReference) + .addParameter(FhirConstants.CODED_SEARCH_HANDLER, codeList) + .addParameter(FhirConstants.QUANTITY_SEARCH_HANDLER, onsetAge) + .addParameter(FhirConstants.DATE_RANGE_SEARCH_HANDLER, "onsetDate", onsetDate) + .addParameter(FhirConstants.DATE_RANGE_SEARCH_HANDLER, "dateCreated", recordDate) + .addParameter(FhirConstants.COMMON_SEARCH_HANDLER, FhirConstants.ID_PROPERTY, uuid) + .addParameter(FhirConstants.COMMON_SEARCH_HANDLER, FhirConstants.LAST_UPDATED_PROPERTY, lastUpdated) + .setSortSpec(sort); + + when(dao.getSearchResultUuids(any())).thenReturn(Collections.singletonList(OBS_UUID)); + when(dao.getSearchResults(any(), any(), anyInt(), anyInt())).thenReturn(Collections.singletonList(obsCondition)); + when(searchQuery.getQueryResults(any(), any(), any(), any())).thenReturn( + new SearchQueryBundleProvider<>(theParams, dao, translator, globalPropertyService, searchQueryInclude)); + when(searchQueryInclude.getIncludedResources(any(), any())).thenReturn(Collections.emptySet()); + when(translator.toFhirResource(obsCondition)).thenReturn(condition); + + IBundleProvider result = fhirConditionService.searchConditions(patientReference, codeList, clinicalList, onsetDate, + onsetAge, recordDate, uuid, lastUpdated, sort, includes); + + List<IBaseResource> resultList = get(result); + + assertThat(result, notNullValue()); + assertThat(resultList, not(empty())); + assertThat(resultList, hasSize(greaterThanOrEqualTo(1))); + } + } diff --git a/api/src/test/java/org/openmrs/module/fhir2/api/search/ConditionSearchQueryTest.java b/api/src/test/java/org/openmrs/module/fhir2/api/search/ConditionSearchQueryTest.java new file mode 100644 index 000000000..262bcc553 --- /dev/null +++ b/api/src/test/java/org/openmrs/module/fhir2/api/search/ConditionSearchQueryTest.java @@ -0,0 +1,871 @@ +/* + * 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.api.search; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.endsWith; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.hasProperty; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.when; + +import java.time.LocalDateTime; +import java.time.Month; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import ca.uhn.fhir.model.api.Include; +import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.rest.param.DateParam; +import ca.uhn.fhir.rest.param.DateRangeParam; +import ca.uhn.fhir.rest.param.ParamPrefixEnum; +import ca.uhn.fhir.rest.param.QuantityAndListParam; +import ca.uhn.fhir.rest.param.QuantityOrListParam; +import ca.uhn.fhir.rest.param.QuantityParam; +import ca.uhn.fhir.rest.param.ReferenceAndListParam; +import ca.uhn.fhir.rest.param.ReferenceOrListParam; +import ca.uhn.fhir.rest.param.ReferenceParam; +import ca.uhn.fhir.rest.param.TokenAndListParam; +import ca.uhn.fhir.rest.param.TokenOrListParam; +import ca.uhn.fhir.rest.param.TokenParam; +import org.hamcrest.Matchers; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r4.model.Condition; +import org.hl7.fhir.r4.model.Patient; +import org.junit.Before; +import org.junit.Test; +import org.openmrs.api.ConceptService; +import org.openmrs.api.PatientService; +import org.openmrs.module.fhir2.FhirConstants; +import org.openmrs.module.fhir2.TestFhirSpringConfiguration; +import org.openmrs.module.fhir2.api.dao.FhirConditionDao; +import org.openmrs.module.fhir2.api.search.param.SearchParameterMap; +import org.openmrs.module.fhir2.api.translators.ConditionTranslator; +import org.openmrs.module.fhir2.api.util.LocalDateTimeFactory; +import org.openmrs.test.BaseModuleContextSensitiveTest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; + +@ContextConfiguration(classes = TestFhirSpringConfiguration.class, inheritLocations = false) +public class ConditionSearchQueryTest extends BaseModuleContextSensitiveTest { + + private static final String OBS_CONDITION_INITIAL_DATA_XML = "org/openmrs/module/fhir2/api/dao/impl/FhirObsConditionDaoImplTest_initial_data.xml"; + + private static final int START_INDEX = 0; + + private static final int END_INDEX = 10; + + private static final String PATIENT_IDENTIFIER = "6TS-4"; + + private static final String PATIENT_WRONG_IDENTIFIER = "Wrong Identifier"; + + private static final String PATIENT_UUID = "5946f880-b197-400b-9caa-a3c661d23041"; + + private static final String PATIENT_WRONG_UUID = "c2299800-cca9-11e0-9572-abcdef0c9a66"; + + private static final String PATIENT_GIVEN_NAME = "Collet"; + + private static final String PATIENT_WRONG_GIVEN_NAME = "Wrong given name"; + + private static final String PATIENT_PARTIAL_NAME = "Test"; + + private static final String PATIENT_FAMILY_NAME = "Chebaskwony"; + + private static final String PATIENT_WRONG_FAMILY_NAME = "Wrong family name"; + + private static final String PATIENT_NOT_FOUND_NAME = "Igor"; + + private static final String ONSET_DATE_TIME = "2008-07-01T00:00:00"; + + private static final String ONSET_START_DATE = "2008-05-01T00:00:00"; + + private static final String ONSET_END_DATE = "2008-08-01T00:00:00"; + + private static final String ONSET_DATE = "2008-07-01"; + + private static final String RECORDED_DATE_TIME = "2008-08-18T14:09:35.0"; + + private static final String DATE_CREATED = "2008-08-18T14:09:35.0"; + + private static final String RECORDED_START_DATE = "2008-05-18T14:09:35.0"; + + private static final String RECORDED_END_DATE = "2008-10-18T14:09:35.0"; + + private static final String DATE_VOIDED = "2008-12-18T14:09:35.0"; + + private static final String RECORDED_DATE = "2008-08-18"; + + private static final String EXISTING_OBS_CONDITION_UUID = "86sgf-1f7d-4394-a316-0a458edf28c4"; + + private static final String CODE_SYSTEM_1 = "http://made_up_concepts.info/sct"; + + private static final String CODE_VALUE_1 = "C00"; + + private static final String CONCEPT_ID_1 = "116128AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + + private static final String CODE_SYSTEM_2 = "http://made_up_concepts.info/sct"; + + private static final String CODE_VALUE_2 = "WGT234"; + + private static final String CONCEPT_ID_2 = "c607c80f-1ea9-4da3-bb88-6276ce8868dd"; + + @Autowired + private FhirConditionDao<org.openmrs.Obs> dao; + + @Autowired + private ConditionTranslator<org.openmrs.Obs> translator; + + @Autowired + private SearchQueryInclude<Condition> searchQueryInclude; + + @Autowired + private SearchQuery<org.openmrs.Obs, Condition, FhirConditionDao<org.openmrs.Obs>, ConditionTranslator<org.openmrs.Obs>, SearchQueryInclude<Condition>> searchQuery; + + @Autowired + PatientService patientService; + + @Autowired + ConceptService conceptService; + + @Autowired + private LocalDateTimeFactory localDateTimeFactory; + + @Before + public void setup() throws Exception { + executeDataSet(OBS_CONDITION_INITIAL_DATA_XML); + } + + private IBundleProvider search(SearchParameterMap theParams) { + return searchQuery.getQueryResults(theParams, dao, translator, searchQueryInclude); + } + + private List<IBaseResource> get(IBundleProvider results) { + return results.getResources(START_INDEX, END_INDEX); + } + + @Test + public void searchForObsConditions_shouldReturnConditionByPatientIdentifier() { + ReferenceParam patientReference = new ReferenceParam(Patient.SP_IDENTIFIER, PATIENT_IDENTIFIER); + ReferenceAndListParam patientList = new ReferenceAndListParam(); + patientList.addAnd(new ReferenceOrListParam().addOr(patientReference)); + + SearchParameterMap theParams = new SearchParameterMap().addParameter(FhirConstants.PATIENT_REFERENCE_SEARCH_HANDLER, + patientList); + + IBundleProvider results = search(theParams); + + List<IBaseResource> resultList = get(results); + + assertThat(results, notNullValue()); + assertThat(resultList, not(empty())); + assertEquals(resultList.size(), 2); + assertThat(((org.hl7.fhir.r4.model.Condition) resultList.iterator().next()).getSubject().getReference(), + endsWith(PATIENT_UUID)); + } + + @Test + public void searchForObsConditions_shouldSearchForConditionsByMultiplePatientIdentifierOr() { + ReferenceAndListParam referenceParam = new ReferenceAndListParam(); + ReferenceParam patient = new ReferenceParam(); + + patient.setValue(PATIENT_IDENTIFIER); + patient.setChain(Patient.SP_IDENTIFIER); + + ReferenceParam badPatient = new ReferenceParam(); + + badPatient.setValue(PATIENT_WRONG_IDENTIFIER); + badPatient.setChain(Patient.SP_IDENTIFIER); + + referenceParam.addValue(new ReferenceOrListParam().add(patient).add(badPatient)); + + SearchParameterMap theParams = new SearchParameterMap().addParameter(FhirConstants.PATIENT_REFERENCE_SEARCH_HANDLER, + referenceParam); + + IBundleProvider results = search(theParams); + + List<IBaseResource> resultList = get(results); + + assertThat(results, notNullValue()); + assertThat(resultList, not(empty())); + assertEquals(resultList.size(), 2); + assertThat(((org.hl7.fhir.r4.model.Condition) resultList.iterator().next()).getSubject().getReference(), + endsWith(PATIENT_UUID)); + } + + @Test + public void searchForObsConditions_shouldReturnEmptyListOfConditionsByMultiplePatientIdentifierAnd() { + ReferenceAndListParam referenceParam = new ReferenceAndListParam(); + ReferenceParam patient = new ReferenceParam(); + + patient.setValue(PATIENT_IDENTIFIER); + patient.setChain(Patient.SP_IDENTIFIER); + + ReferenceParam badPatient = new ReferenceParam(); + + badPatient.setValue(PATIENT_WRONG_IDENTIFIER); + badPatient.setChain(Patient.SP_IDENTIFIER); + + referenceParam.addValue(new ReferenceOrListParam().add(patient)).addAnd(new ReferenceOrListParam().add(badPatient)); + + SearchParameterMap theParams = new SearchParameterMap().addParameter(FhirConstants.PATIENT_REFERENCE_SEARCH_HANDLER, + referenceParam); + IBundleProvider results = search(theParams); + + List<IBaseResource> resultList = get(results); + + assertThat(results, notNullValue()); + assertThat(resultList, empty()); + } + + @Test + public void searchForObsConditions_shouldReturnConditionByPatientUuid() { + ReferenceParam patientReference = new ReferenceParam(null, PATIENT_UUID); + ReferenceAndListParam patientList = new ReferenceAndListParam(); + patientList.addAnd(new ReferenceOrListParam().addOr(patientReference)); + + SearchParameterMap theParams = new SearchParameterMap().addParameter(FhirConstants.PATIENT_REFERENCE_SEARCH_HANDLER, + patientList); + + IBundleProvider results = search(theParams); + + List<IBaseResource> resultList = get(results); + + assertThat(results, notNullValue()); + assertThat(resultList, not(empty())); + assertEquals(resultList.size(), 2); + assertThat( + ((org.hl7.fhir.r4.model.Condition) resultList.iterator().next()).getSubject().getReferenceElement().getIdPart(), + equalTo(PATIENT_UUID)); + } + + @Test + public void searchForObsConditions_shouldSearchForConditionsByMultiplePatientUuidOr() { + ReferenceAndListParam referenceParam = new ReferenceAndListParam(); + ReferenceParam patient = new ReferenceParam(); + + patient.setValue(PATIENT_UUID); + + ReferenceParam badPatient = new ReferenceParam(); + + badPatient.setValue(PATIENT_WRONG_UUID); + + referenceParam.addValue(new ReferenceOrListParam().add(patient).add(badPatient)); + + SearchParameterMap theParams = new SearchParameterMap().addParameter(FhirConstants.PATIENT_REFERENCE_SEARCH_HANDLER, + referenceParam); + + IBundleProvider results = search(theParams); + + List<IBaseResource> resultList = get(results); + + assertThat(results, notNullValue()); + assertThat(resultList, not(empty())); + assertEquals(resultList.size(), 2); + assertThat( + ((org.hl7.fhir.r4.model.Condition) resultList.iterator().next()).getSubject().getReferenceElement().getIdPart(), + equalTo(PATIENT_UUID)); + } + + @Test + public void searchForObsConditions_shouldReturnEmptyListOfConditionsByMultiplePatientUuidAnd() { + ReferenceAndListParam referenceParam = new ReferenceAndListParam(); + ReferenceParam patient = new ReferenceParam(); + + patient.setValue(PATIENT_UUID); + + ReferenceParam badPatient = new ReferenceParam(); + + badPatient.setValue(PATIENT_WRONG_UUID); + + referenceParam.addValue(new ReferenceOrListParam().add(patient)).addAnd(new ReferenceOrListParam().add(badPatient)); + + SearchParameterMap theParams = new SearchParameterMap().addParameter(FhirConstants.PATIENT_REFERENCE_SEARCH_HANDLER, + referenceParam); + IBundleProvider results = search(theParams); + + List<IBaseResource> resultList = get(results); + + assertThat(results, notNullValue()); + assertThat(resultList, empty()); + } + + @Test + public void searchForObsConditions_shouldReturnConditionByPatientGivenName() { + ReferenceParam patientReference = new ReferenceParam(Patient.SP_GIVEN, PATIENT_GIVEN_NAME); + ReferenceAndListParam patientList = new ReferenceAndListParam(); + patientList.addValue(new ReferenceOrListParam().add(patientReference)); + + SearchParameterMap theParams = new SearchParameterMap().addParameter(FhirConstants.PATIENT_REFERENCE_SEARCH_HANDLER, + patientList); + + IBundleProvider results = search(theParams); + + List<IBaseResource> resultList = get(results); + + assertThat(results, notNullValue()); + assertThat(resultList, not(empty())); + assertEquals(resultList.size(), 2); + assertThat( + ((org.hl7.fhir.r4.model.Condition) resultList.iterator().next()).getSubject().getReferenceElement().getIdPart(), + equalTo(PATIENT_UUID)); + } + + @Test + public void searchForObsConditions_shouldReturnUniqueConditionsByPatientGivenName() { + ReferenceParam patientReference = new ReferenceParam(Patient.SP_GIVEN, PATIENT_GIVEN_NAME); + ReferenceAndListParam patientList = new ReferenceAndListParam(); + patientList.addValue(new ReferenceOrListParam().add(patientReference)); + + SearchParameterMap theParams = new SearchParameterMap().addParameter(FhirConstants.PATIENT_REFERENCE_SEARCH_HANDLER, + patientList); + + IBundleProvider results = search(theParams); + + assertThat(results, notNullValue()); + assertThat(results.size(), equalTo(2)); + Set<String> resultSet = new HashSet<>(dao.getSearchResultUuids(theParams)); + assertThat(resultSet.size(), equalTo(2)); + } + + @Test + public void searchForObsConditions_shouldSearchForConditionsByMultiplePatientGivenNameOr() { + ReferenceAndListParam referenceParam = new ReferenceAndListParam(); + ReferenceParam patient = new ReferenceParam(); + + patient.setValue(PATIENT_GIVEN_NAME); + patient.setChain(Patient.SP_GIVEN); + + ReferenceParam badPatient = new ReferenceParam(); + + badPatient.setValue(PATIENT_WRONG_GIVEN_NAME); + badPatient.setChain(Patient.SP_GIVEN); + + referenceParam.addValue(new ReferenceOrListParam().add(patient).add(badPatient)); + + SearchParameterMap theParams = new SearchParameterMap().addParameter(FhirConstants.PATIENT_REFERENCE_SEARCH_HANDLER, + referenceParam); + IBundleProvider results = search(theParams); + + List<IBaseResource> resultList = get(results); + + assertThat(results, notNullValue()); + assertThat(resultList, not(empty())); + assertEquals(resultList.size(), 2); + + } + + @Test + public void searchForConditions_shouldReturnEmptyListOfConditionsByMultiplePatientGivenNameAnd() { + ReferenceAndListParam referenceParam = new ReferenceAndListParam(); + ReferenceParam patient = new ReferenceParam(); + + patient.setValue(PATIENT_GIVEN_NAME); + patient.setChain(Patient.SP_GIVEN); + + ReferenceParam badPatient = new ReferenceParam(); + + badPatient.setValue(PATIENT_WRONG_GIVEN_NAME); + badPatient.setChain(Patient.SP_GIVEN); + + referenceParam.addValue(new ReferenceOrListParam().add(patient)).addAnd(new ReferenceOrListParam().add(badPatient)); + + SearchParameterMap theParams = new SearchParameterMap().addParameter(FhirConstants.PATIENT_REFERENCE_SEARCH_HANDLER, + referenceParam); + IBundleProvider results = search(theParams); + + List<IBaseResource> resultList = get(results); + + assertThat(results, notNullValue()); + assertThat(resultList, empty()); + } + + @Test + public void searchForConditions_shouldReturnEmptyListOfConditionByPatientNotFoundName() { + ReferenceParam patientReference = new ReferenceParam(Patient.SP_GIVEN, PATIENT_NOT_FOUND_NAME); + ReferenceAndListParam patientList = new ReferenceAndListParam(); + patientList.addValue(new ReferenceOrListParam().add(patientReference)); + + SearchParameterMap theParams = new SearchParameterMap().addParameter(FhirConstants.PATIENT_REFERENCE_SEARCH_HANDLER, + patientList); + + IBundleProvider results = search(theParams); + + List<IBaseResource> resultList = get(results); + + assertThat(results, notNullValue()); + assertThat(resultList, empty()); + } + + @Test + public void searchForConditions_shouldReturnConditionByPatientFamilyName() { + ReferenceParam patientReference = new ReferenceParam(Patient.SP_FAMILY, PATIENT_FAMILY_NAME); + ReferenceAndListParam patientList = new ReferenceAndListParam(); + patientList.addValue(new ReferenceOrListParam().add(patientReference)); + + SearchParameterMap theParams = new SearchParameterMap().addParameter(FhirConstants.PATIENT_REFERENCE_SEARCH_HANDLER, + patientList); + + IBundleProvider results = search(theParams); + + List<IBaseResource> resultList = get(results); + + assertThat(results, notNullValue()); + assertThat(resultList, not(empty())); + assertThat(resultList.size(), equalTo(2)); + assertThat( + ((org.hl7.fhir.r4.model.Condition) resultList.iterator().next()).getSubject().getReferenceElement().getIdPart(), + equalTo(PATIENT_UUID)); + } + + @Test + public void searchForObsConditions_shouldReturnUniqueConditionsByPatientFamilyName() { + ReferenceParam patientReference = new ReferenceParam(Patient.SP_FAMILY, PATIENT_FAMILY_NAME); + ReferenceAndListParam patientList = new ReferenceAndListParam(); + patientList.addValue(new ReferenceOrListParam().add(patientReference)); + + SearchParameterMap theParams = new SearchParameterMap().addParameter(FhirConstants.PATIENT_REFERENCE_SEARCH_HANDLER, + patientList); + + IBundleProvider results = search(theParams); + + assertThat(results, notNullValue()); + assertThat(results.size(), equalTo(2)); + Set<String> resultSet = new HashSet<>(dao.getSearchResultUuids(theParams)); + assertThat(resultSet.size(), equalTo(2)); + } + + @Test + public void searchForConditions_shouldSearchForConditionsByMultiplePatientFamilyNameOr() { + ReferenceAndListParam referenceParam = new ReferenceAndListParam(); + ReferenceParam patient = new ReferenceParam(); + + patient.setValue(PATIENT_FAMILY_NAME); + patient.setChain(Patient.SP_FAMILY); + + ReferenceParam badPatient = new ReferenceParam(); + + badPatient.setValue(PATIENT_WRONG_FAMILY_NAME); + badPatient.setChain(Patient.SP_FAMILY); + + referenceParam.addValue(new ReferenceOrListParam().add(patient).add(badPatient)); + + SearchParameterMap theParams = new SearchParameterMap().addParameter(FhirConstants.PATIENT_REFERENCE_SEARCH_HANDLER, + referenceParam); + IBundleProvider results = search(theParams); + + List<IBaseResource> resultList = get(results); + + assertThat(results, notNullValue()); + assertThat(resultList, not(empty())); + assertThat(resultList.size(), equalTo(2)); + } + + @Test + public void searchForConditions_shouldReturnEmptyListOfConditionsByMultiplePatientFamilyNameAnd() { + ReferenceAndListParam referenceParam = new ReferenceAndListParam(); + ReferenceParam patient = new ReferenceParam(); + + patient.setValue(PATIENT_FAMILY_NAME); + patient.setChain(Patient.SP_FAMILY); + + ReferenceParam badPatient = new ReferenceParam(); + + badPatient.setValue(PATIENT_WRONG_FAMILY_NAME); + badPatient.setChain(Patient.SP_FAMILY); + + referenceParam.addValue(new ReferenceOrListParam().add(patient)).addAnd(new ReferenceOrListParam().add(badPatient)); + + SearchParameterMap theParams = new SearchParameterMap().addParameter(FhirConstants.PATIENT_REFERENCE_SEARCH_HANDLER, + referenceParam); + IBundleProvider results = search(theParams); + + List<IBaseResource> resultList = get(results); + + assertThat(results, notNullValue()); + assertThat(resultList, empty()); + } + + @Test + public void searchForObsConditions_shouldReturnConditionByPatientName() { + ReferenceParam patientReference = new ReferenceParam(Patient.SP_NAME, PATIENT_PARTIAL_NAME); + ReferenceAndListParam patientList = new ReferenceAndListParam(); + patientList.addValue(new ReferenceOrListParam().add(patientReference)); + + SearchParameterMap theParams = new SearchParameterMap().addParameter(FhirConstants.PATIENT_REFERENCE_SEARCH_HANDLER, + patientList); + + IBundleProvider results = search(theParams); + + List<IBaseResource> resultList = get(results); + + assertThat(results, notNullValue()); + assertThat(resultList, not(empty())); + assertThat( + ((org.hl7.fhir.r4.model.Condition) resultList.iterator().next()).getSubject().getReferenceElement().getIdPart(), + equalTo(PATIENT_UUID)); + assertThat(resultList.size(), equalTo(2)); + } + + @Test + public void searchForConditions_shouldReturnUniqueConditionsByPatientName() { + ReferenceParam patientReference = new ReferenceParam(Patient.SP_NAME, PATIENT_PARTIAL_NAME); + ReferenceAndListParam patientList = new ReferenceAndListParam(); + patientList.addValue(new ReferenceOrListParam().add(patientReference)); + + SearchParameterMap theParams = new SearchParameterMap().addParameter(FhirConstants.PATIENT_REFERENCE_SEARCH_HANDLER, + patientList); + + IBundleProvider results = search(theParams); + + assertThat(results, notNullValue()); + assertThat(results.size(), equalTo(2)); + List<String> resultSet = dao.getSearchResultUuids(theParams); + assertThat(resultSet.size(), equalTo(2)); + } + + @Test + public void searchForConditions_shouldSearchForConditionsByMultiplePatientNameOr() { + ReferenceAndListParam referenceParam = new ReferenceAndListParam(); + ReferenceParam patient = new ReferenceParam(); + + patient.setValue(PATIENT_PARTIAL_NAME); + patient.setChain(Patient.SP_NAME); + + ReferenceParam badPatient = new ReferenceParam(); + + badPatient.setValue(PATIENT_NOT_FOUND_NAME); + badPatient.setChain(Patient.SP_NAME); + + referenceParam.addValue(new ReferenceOrListParam().add(patient).add(badPatient)); + + SearchParameterMap theParams = new SearchParameterMap().addParameter(FhirConstants.PATIENT_REFERENCE_SEARCH_HANDLER, + referenceParam); + IBundleProvider results = search(theParams); + + List<IBaseResource> resultList = get(results); + + assertThat(results, notNullValue()); + assertThat(resultList, not(empty())); + assertThat(resultList.size(), equalTo(2)); + } + + @Test + public void searchForConditions_shouldReturnEmptyListOfConditionsByMultiplePatientNameAnd() { + ReferenceAndListParam referenceParam = new ReferenceAndListParam(); + ReferenceParam patient = new ReferenceParam(); + + patient.setValue(PATIENT_PARTIAL_NAME); + patient.setChain(Patient.SP_NAME); + + ReferenceParam badPatient = new ReferenceParam(); + + badPatient.setValue(PATIENT_NOT_FOUND_NAME); + badPatient.setChain(Patient.SP_NAME); + + referenceParam.addValue(new ReferenceOrListParam().add(patient)).addAnd(new ReferenceOrListParam().add(badPatient)); + + SearchParameterMap theParams = new SearchParameterMap().addParameter(FhirConstants.PATIENT_REFERENCE_SEARCH_HANDLER, + referenceParam); + IBundleProvider results = search(theParams); + + List<IBaseResource> resultList = get(results); + + assertThat(results, notNullValue()); + assertThat(resultList, empty()); + } + + @Test + public void searchForObsConditions_shouldReturnConditionByOnsetDate() { + DateRangeParam onsetDate = new DateRangeParam(new DateParam("eq" + ONSET_DATE_TIME)); + + SearchParameterMap theParams = new SearchParameterMap().addParameter(FhirConstants.DATE_RANGE_SEARCH_HANDLER, + "obsDatetime", onsetDate); + + IBundleProvider results = search(theParams); + + List<IBaseResource> resultList = get(results); + + assertThat(results, notNullValue()); + assertThat(resultList, not(empty())); + assertThat( + ((org.hl7.fhir.r4.model.Condition) resultList.iterator().next()).getOnsetDateTimeType().getValue().toString(), + containsString(ONSET_DATE)); + assertThat(resultList.size(), equalTo(2)); + } + + @Test + public void searchForConditions_shouldReturnConditionByOnsetDateRange() { + DateRangeParam onsetDate = new DateRangeParam(new DateParam(ONSET_START_DATE), new DateParam(ONSET_END_DATE)); + + SearchParameterMap theParams = new SearchParameterMap().addParameter(FhirConstants.DATE_RANGE_SEARCH_HANDLER, + "obsDatetime", onsetDate); + + IBundleProvider results = search(theParams); + + List<IBaseResource> resultList = get(results); + + assertThat(results, notNullValue()); + assertThat(resultList, not(empty())); + assertThat( + ((org.hl7.fhir.r4.model.Condition) resultList.iterator().next()).getOnsetDateTimeType().getValue().toString(), + containsString(ONSET_DATE)); + assertThat(resultList.size(), equalTo(2)); + } + + @Test + public void searchForConditions_shouldReturnConditionByUnboundedOnsetDate() { + DateRangeParam onsetDate = new DateRangeParam(new DateParam("gt" + ONSET_START_DATE)); + + SearchParameterMap theParams = new SearchParameterMap().addParameter(FhirConstants.DATE_RANGE_SEARCH_HANDLER, + "obsDatetime", onsetDate); + + IBundleProvider results = search(theParams); + + List<IBaseResource> resultList = get(results); + + assertThat(results, notNullValue()); + assertThat(resultList, not(empty())); + assertThat(resultList.size(), equalTo(2)); + } + + @Test + public void searchForConditions_shouldReturnConditionByRecordedDate() { + DateRangeParam recordedDate = new DateRangeParam(new DateParam("eq" + RECORDED_DATE_TIME)); + + SearchParameterMap theParams = new SearchParameterMap().addParameter(FhirConstants.DATE_RANGE_SEARCH_HANDLER, + "dateCreated", recordedDate); + + IBundleProvider results = search(theParams); + + List<IBaseResource> resultList = get(results); + + assertThat(results, notNullValue()); + assertThat(resultList, not(empty())); + assertThat(((org.hl7.fhir.r4.model.Condition) resultList.iterator().next()).getRecordedDate().toString(), + containsString(RECORDED_DATE)); + assertThat(resultList.size(), equalTo(2)); + } + + @Test + public void searchForConditions_shouldReturnConditionByRecordedDateRange() { + DateRangeParam onsetDate = new DateRangeParam(new DateParam(RECORDED_START_DATE), new DateParam(RECORDED_END_DATE)); + + SearchParameterMap theParams = new SearchParameterMap().addParameter(FhirConstants.DATE_RANGE_SEARCH_HANDLER, + "dateCreated", onsetDate); + + IBundleProvider results = search(theParams); + + List<IBaseResource> resultList = get(results); + + assertThat(results, notNullValue()); + assertThat(resultList, not(empty())); + assertThat(((org.hl7.fhir.r4.model.Condition) resultList.iterator().next()).getRecordedDate().toString(), + containsString(RECORDED_DATE)); + assertThat(resultList.size(), equalTo(2)); + + } + + @Test + public void searchForObsConditions_shouldReturnConditionByCode() { + TokenAndListParam listParam = new TokenAndListParam(); + listParam.addValue(new TokenOrListParam().add(new TokenParam(CODE_SYSTEM_1, CODE_VALUE_1))); + + SearchParameterMap theParams = new SearchParameterMap().addParameter(FhirConstants.CODED_SEARCH_HANDLER, listParam); + + IBundleProvider results = search(theParams); + + List<IBaseResource> resultList = get(results); + + assertThat(results, notNullValue()); + assertThat(resultList, not(empty())); + assertThat(((org.hl7.fhir.r4.model.Condition) resultList.iterator().next()).getCode().getCodingFirstRep().getCode(), + equalTo(CONCEPT_ID_1)); + assertThat(resultList.size(), equalTo(2)); + } + + @Test + public void searchForObsConditions_shouldReturnMultipleConditionsByCodeList() { + TokenAndListParam listParam = new TokenAndListParam(); + + listParam.addValue(new TokenOrListParam().add(new TokenParam(CODE_SYSTEM_1, CODE_VALUE_1)) + .add(new TokenParam(CODE_SYSTEM_2, CODE_VALUE_2))); + + SearchParameterMap theParams = new SearchParameterMap().addParameter(FhirConstants.CODED_SEARCH_HANDLER, listParam); + + IBundleProvider results = search(theParams); + + List<IBaseResource> resultList = get(results); + + assertThat(results, notNullValue()); + assertThat(resultList, not(empty())); + assertThat(resultList.size(), equalTo(2)); + } + + @Test + public void searchForObsConditions_shouldReturnConditionByCodeAndNoSystem() { + TokenAndListParam listParam = new TokenAndListParam(); + listParam.addValue(new TokenOrListParam().add(new TokenParam(CONCEPT_ID_1))); + + SearchParameterMap theParams = new SearchParameterMap().addParameter(FhirConstants.CODED_SEARCH_HANDLER, listParam); + + IBundleProvider results = search(theParams); + + List<IBaseResource> resultList = get(results); + + assertThat(results, notNullValue()); + assertThat(resultList, not(empty())); + assertThat(((org.hl7.fhir.r4.model.Condition) resultList.iterator().next()).getCode().getCodingFirstRep().getCode(), + equalTo(CONCEPT_ID_1)); + assertThat(resultList.size(), equalTo(2)); + } + + @Test + public void searchForObsConditions_shouldReturnMultipleConditionsByCodeListAndNoSystem() { + TokenAndListParam listParam = new TokenAndListParam(); + listParam.addValue(new TokenOrListParam().add(new TokenParam(CONCEPT_ID_1)).add(new TokenParam(CONCEPT_ID_2))); + + SearchParameterMap theParams = new SearchParameterMap().addParameter(FhirConstants.CODED_SEARCH_HANDLER, listParam); + + IBundleProvider results = search(theParams); + + List<IBaseResource> resultList = get(results); + + assertThat(results, notNullValue()); + assertThat(resultList, not(empty())); + assertThat(resultList.size(), equalTo(2)); + } + + @Test + public void searchForObsConditions_shouldSearchForConditionsByUuid() { + TokenAndListParam uuid = new TokenAndListParam().addAnd(new TokenParam(EXISTING_OBS_CONDITION_UUID)); + + SearchParameterMap theParams = new SearchParameterMap().addParameter(FhirConstants.COMMON_SEARCH_HANDLER, + FhirConstants.ID_PROPERTY, uuid); + + IBundleProvider results = search(theParams); + + List<IBaseResource> resultList = get(results); + + assertThat(results, notNullValue()); + assertThat(resultList, not(empty())); + assertThat(resultList, hasSize(equalTo(1))); + assertThat(((org.hl7.fhir.r4.model.Condition) resultList.iterator().next()).getIdElement().getIdPart(), + equalTo(EXISTING_OBS_CONDITION_UUID)); + } + + @Test + public void searchForObsConditions_shouldSearchForConditionsByLastUpdatedDateCreated() { + DateRangeParam lastUpdated = new DateRangeParam().setUpperBound(DATE_CREATED).setLowerBound(DATE_CREATED); + + SearchParameterMap theParams = new SearchParameterMap().addParameter(FhirConstants.COMMON_SEARCH_HANDLER, + FhirConstants.LAST_UPDATED_PROPERTY, lastUpdated); + + IBundleProvider results = search(theParams); + + List<IBaseResource> resultList = get(results); + + assertThat(results, notNullValue()); + assertThat(resultList, not(empty())); + assertThat(resultList, hasSize(equalTo(2))); + } + + @Test + public void searchForObsConditions_shouldSearchForConditionsByMatchingUuidAndLastUpdated() { + TokenAndListParam uuid = new TokenAndListParam().addAnd(new TokenParam(EXISTING_OBS_CONDITION_UUID)); + DateRangeParam lastUpdated = new DateRangeParam().setUpperBound(DATE_CREATED).setLowerBound(DATE_CREATED); + + SearchParameterMap theParams = new SearchParameterMap() + .addParameter(FhirConstants.COMMON_SEARCH_HANDLER, FhirConstants.ID_PROPERTY, uuid) + .addParameter(FhirConstants.COMMON_SEARCH_HANDLER, FhirConstants.LAST_UPDATED_PROPERTY, lastUpdated); + + IBundleProvider results = search(theParams); + + List<IBaseResource> resultList = get(results); + + assertThat(results, notNullValue()); + assertThat(resultList, not(empty())); + assertThat(resultList, hasSize(equalTo(1))); + assertThat(((org.hl7.fhir.r4.model.Condition) resultList.iterator().next()).getIdElement().getIdPart(), + equalTo(EXISTING_OBS_CONDITION_UUID)); + } + + @Test + public void searchForObsConditions_shouldReturnEmptyListByMismatchingUuidAndLastUpdated() { + TokenAndListParam uuid = new TokenAndListParam().addAnd(new TokenParam(EXISTING_OBS_CONDITION_UUID)); + DateRangeParam lastUpdated = new DateRangeParam().setUpperBound(DATE_VOIDED).setLowerBound(DATE_VOIDED); + + SearchParameterMap theParams = new SearchParameterMap() + .addParameter(FhirConstants.COMMON_SEARCH_HANDLER, FhirConstants.ID_PROPERTY, uuid) + .addParameter(FhirConstants.COMMON_SEARCH_HANDLER, FhirConstants.LAST_UPDATED_PROPERTY, lastUpdated); + + IBundleProvider results = search(theParams); + + List<IBaseResource> resultList = get(results); + + assertThat(results, notNullValue()); + assertThat(resultList, empty()); + } + + @Test + public void searchForObsConditions_shouldAddNotNullPatientToReturnedResults() { + HashSet<Include> includes = new HashSet<>(); + Include include = new Include("Condition:patient"); + includes.add(include); + + TokenAndListParam uuid = new TokenAndListParam().addAnd(new TokenParam(EXISTING_OBS_CONDITION_UUID)); + + SearchParameterMap theParams = new SearchParameterMap().addParameter(FhirConstants.INCLUDE_SEARCH_HANDLER, includes) + .addParameter(FhirConstants.COMMON_SEARCH_HANDLER, FhirConstants.ID_PROPERTY, uuid); + + IBundleProvider results = search(theParams); + assertThat(results.size(), Matchers.equalTo(1)); + + List<IBaseResource> resultList = get(results); + + assertThat(results, Matchers.notNullValue()); + assertThat(resultList.size(), Matchers.equalTo(2)); // included resource added as part of the result list + + org.hl7.fhir.r4.model.Condition returnedCondition = (org.hl7.fhir.r4.model.Condition) resultList.iterator().next(); + assertThat(resultList, hasItem(allOf(is(instanceOf(Patient.class)), + hasProperty("id", Matchers.equalTo(returnedCondition.getSubject().getReferenceElement().getIdPart()))))); + } + + @Test + public void searchForConditions_shouldReturnConditionByOnsetAgeEqualHour() { + QuantityOrListParam orList = new QuantityOrListParam(); + orList.addOr(new QuantityParam(ParamPrefixEnum.EQUAL, 2, "", "h")); + QuantityAndListParam onsetAgeParam = new QuantityAndListParam().addAnd(orList); + + when(localDateTimeFactory.now()).thenReturn(LocalDateTime.of(2008, Month.JULY, 1, 3, 0, 0)); + + SearchParameterMap theParams = new SearchParameterMap().addParameter(FhirConstants.QUANTITY_SEARCH_HANDLER, + onsetAgeParam); + + IBundleProvider results = search(theParams); + + List<IBaseResource> resultList = get(results); + + assertThat(results, notNullValue()); + assertThat(resultList, hasSize(greaterThanOrEqualTo(1))); + assertThat(((org.hl7.fhir.r4.model.Condition) resultList.iterator().next()).getIdElement().getIdPart(), + equalTo(EXISTING_OBS_CONDITION_UUID)); + } +} diff --git a/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/ConditionTranslatorImplTest.java b/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/ConditionTranslatorImplTest.java new file mode 100644 index 000000000..19518f02f --- /dev/null +++ b/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/ConditionTranslatorImplTest.java @@ -0,0 +1,302 @@ +/* + * 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.api.translators.impl; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.empty; +import static org.mockito.Mockito.when; + +import java.util.Collections; +import java.util.Date; +import java.util.List; + +import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import org.exparity.hamcrest.date.DateMatchers; +import org.hamcrest.CoreMatchers; +import org.hamcrest.Matchers; +import org.hl7.fhir.r4.model.CodeableConcept; +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.Condition; +import org.hl7.fhir.r4.model.DateTimeType; +import org.hl7.fhir.r4.model.IdType; +import org.hl7.fhir.r4.model.Provenance; +import org.hl7.fhir.r4.model.Reference; +import org.hl7.fhir.r4.model.Resource; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.openmrs.Concept; +import org.openmrs.Obs; +import org.openmrs.Patient; +import org.openmrs.User; +import org.openmrs.api.ConceptService; +import org.openmrs.module.fhir2.FhirConstants; +import org.openmrs.module.fhir2.api.translators.ConceptTranslator; +import org.openmrs.module.fhir2.api.translators.ConditionClinicalStatusTranslator; +import org.openmrs.module.fhir2.api.translators.PatientReferenceTranslator; +import org.openmrs.module.fhir2.api.translators.PractitionerReferenceTranslator; +import org.openmrs.module.fhir2.api.translators.ProvenanceTranslator; +import org.openmrs.module.fhir2.api.util.FhirUtils; + +@RunWith(MockitoJUnitRunner.class) +public class ConditionTranslatorImplTest { + + private static final String CONDITION_UUID = "36aa91ad-66f3-455b-b28a-71beb6ca3195"; + + private static final Integer CONDITION_ID = 1284; + + private static final String PATIENT_UUID = "fc8b217b-2ed4-4dde-b9f7-a5334347e7ca"; + + private static final String PATIENT_REF = "Patient/" + PATIENT_UUID; + + private static final String SYSTEM = "https://openconceptlab.org/orgs/CIEL/sources/CIEL"; + + private static final Integer CODE = 102309; + + private static final String CONCEPT_UUID = "31d754f5-3e9e-4ca3-805c-87f97a1f5e4b"; + + private static final String PRACTITIONER_UUID = "2ffb1a5f-bcd3-4243-8f40-78edc2642789"; + + private static final String PRACTITIONER_REFERENCE = FhirConstants.PRACTITIONER + "/" + PRACTITIONER_UUID; + + @Mock + private PatientReferenceTranslator patientReferenceTranslator; + + @Mock + private ProvenanceTranslator<Obs> provenanceTranslator; + + @Mock + ConceptService conceptService; + + @Mock + private ConceptTranslator conceptTranslator; + + @Mock + private PractitionerReferenceTranslator<User> creatorReferenceTranslator; + + @Mock + private ConditionClinicalStatusTranslator<Obs> conditionClinicalStatusTranslator; + + private ConditionTranslatorImpl conditionTranslator; + + private Condition fhirCondition; + + private Obs openmrsCondition; + + private Patient patient; + + private Reference patientRef; + + private Concept concept; + + @Before + public void setup() { + conditionTranslator = new ConditionTranslatorImpl(); + conditionTranslator.setPatientReferenceTranslator(patientReferenceTranslator); + conditionTranslator.setConceptTranslator(conceptTranslator); + conditionTranslator.setPractitionerReferenceTranslator(creatorReferenceTranslator); + conditionTranslator.setProvenanceTranslator(provenanceTranslator); + conditionTranslator.setConceptService(conceptService); + conditionTranslator.setConditionClinicalStatusTranslator(conditionClinicalStatusTranslator); + + patient = new Patient(); + patient.setUuid(PATIENT_UUID); + + patientRef = new Reference(); + patientRef.setReference(PATIENT_REF); + + concept = new Concept(); + concept.setUuid(CONDITION_UUID); + concept.setConceptId(CONDITION_ID); + + Concept valueCoded = new Concept(); + concept.setUuid(CONDITION_UUID); + concept.setConceptId(CODE); + + openmrsCondition = new Obs(); + openmrsCondition.setUuid(CONDITION_UUID); + openmrsCondition.setPerson(patient); + openmrsCondition.setConcept(concept); + openmrsCondition.setValueCoded(valueCoded); + + fhirCondition = new Condition(); + fhirCondition.setId(CONDITION_UUID); + fhirCondition.setSubject(patientRef); + } + + @Test + public void shouldTranslateConditionIdToFhirType() { + Condition condition = conditionTranslator.toFhirResource(openmrsCondition); + assertThat(condition, notNullValue()); + assertThat(condition.getId(), equalTo(CONDITION_UUID)); + } + + @Test + public void shouldTranslateConditionUuidToOpenMrsType() { + when(conceptService.getConceptByUuid(FhirConstants.CONDITION_OBSERVATION_CONCEPT_UUID)).thenReturn(concept); + Obs obsCondition = conditionTranslator.toOpenmrsType(fhirCondition); + assertThat(obsCondition, notNullValue()); + assertThat(obsCondition.getUuid(), equalTo(CONDITION_UUID)); + } + + @Test(expected = NullPointerException.class) + public void toFhirResource_shouldThrowExceptionIfConditionToTranslateIsNull() { + conditionTranslator.toFhirResource(null); + } + + @Test(expected = InternalErrorException.class) + public void toFhirOpenmrsType_shouldThrowExceptionIfConceptProblemListIsNotFoun() { + when(patientReferenceTranslator.toOpenmrsType(patientRef)).thenReturn(patient); + Obs obsCondition = conditionTranslator.toOpenmrsType(fhirCondition); + assertThat(obsCondition, notNullValue()); + assertThat(obsCondition.getPerson(), notNullValue()); + assertThat(obsCondition.getPerson().getUuid(), equalTo(PATIENT_UUID)); + } + + @Test + public void shouldTranslateConditionSubjectToOpenMrsType() { + when(conceptService.getConceptByUuid(FhirConstants.CONDITION_OBSERVATION_CONCEPT_UUID)).thenReturn(concept); + when(patientReferenceTranslator.toOpenmrsType(patientRef)).thenReturn(patient); + Obs obsCondition = conditionTranslator.toOpenmrsType(fhirCondition); + assertThat(obsCondition, notNullValue()); + assertThat(obsCondition.getPerson(), notNullValue()); + assertThat(obsCondition.getPerson().getUuid(), equalTo(PATIENT_UUID)); + } + + @Test + public void shouldTranslateConditionPatientToFhirType() { + when(patientReferenceTranslator.toFhirResource(patient)).thenReturn(patientRef); + Condition condition = conditionTranslator.toFhirResource(openmrsCondition); + assertThat(condition, notNullValue()); + assertThat(condition.getSubject(), notNullValue()); + assertThat(condition.getSubject(), equalTo(patientRef)); + assertThat(condition.getSubject().getReference(), equalTo(PATIENT_REF)); + } + + @Test + public void shouldTranslateOpenMrsConditionOnsetDateToFhirType() { + openmrsCondition.setObsDatetime(new Date()); + org.hl7.fhir.r4.model.Condition condition = conditionTranslator.toFhirResource(openmrsCondition); + assertThat(condition, notNullValue()); + assertThat(condition.getOnsetDateTimeType().getValue(), notNullValue()); + assertThat(condition.getOnsetDateTimeType().getValue(), DateMatchers.sameDay(new Date())); + } + + @Test + public void shouldTranslateFhirConditionOnsetToOpenMrsOnsetDate() { + when(conceptService.getConceptByUuid(FhirConstants.CONDITION_OBSERVATION_CONCEPT_UUID)).thenReturn(concept); + DateTimeType theDateTime = new DateTimeType(); + theDateTime.setValue(new Date()); + fhirCondition.setOnset(theDateTime); + Obs condition = conditionTranslator.toOpenmrsType(fhirCondition); + assertThat(condition, notNullValue()); + assertThat(condition.getObsDatetime(), notNullValue()); + assertThat(condition.getObsDatetime(), DateMatchers.sameDay(new Date())); + } + + @Test + public void shouldTranslateConditionCodeToOpenMrsConcept() { + CodeableConcept codeableConcept = new CodeableConcept(); + Coding coding = new Coding(); + coding.setCode(CODE.toString()); + coding.setSystem(SYSTEM); + codeableConcept.addCoding(coding); + fhirCondition.setCode(codeableConcept); + Concept concept = new Concept(); + concept.setUuid(CONCEPT_UUID); + concept.setConceptId(CODE); + when(conceptService.getConceptByUuid(FhirConstants.CONDITION_OBSERVATION_CONCEPT_UUID)).thenReturn(concept); + when(conceptTranslator.toOpenmrsType(codeableConcept)).thenReturn(concept); + Obs condition = conditionTranslator.toOpenmrsType(fhirCondition); + assertThat(condition, notNullValue()); + assertThat(condition.getValueCoded(), notNullValue()); + assertThat(condition.getValueCoded().getConceptId(), equalTo(CODE)); + } + + @Test + public void shouldTranslateConditionConceptToFhirType() { + Concept concept = new Concept(); + concept.setUuid(CONCEPT_UUID); + concept.setConceptId(CODE); + CodeableConcept codeableConcept = new CodeableConcept(); + Coding coding = new Coding(); + coding.setCode(CODE.toString()); + coding.setSystem(SYSTEM); + codeableConcept.addCoding(coding); + openmrsCondition.setValueCoded(concept); + when(conceptTranslator.toFhirResource(concept)).thenReturn(codeableConcept); + org.hl7.fhir.r4.model.Condition condition = conditionTranslator.toFhirResource(openmrsCondition); + assertThat(condition, notNullValue()); + assertThat(condition.getCode(), notNullValue()); + assertThat(condition.getCode().getCoding(), not(Collections.emptyList())); + assertThat(condition.getCode().getCoding().get(0).getCode(), equalTo(CODE.toString())); + assertThat(condition.getCode().getCoding().get(0).getSystem(), equalTo(SYSTEM)); + } + + @Test + public void shouldTranslateConditionDateCreatedToRecordedDateFhirType() { + openmrsCondition.setDateCreated(new Date()); + org.hl7.fhir.r4.model.Condition condition = conditionTranslator.toFhirResource(openmrsCondition); + assertThat(condition, notNullValue()); + assertThat(condition.getRecordedDate(), notNullValue()); + assertThat(condition.getRecordedDate(), DateMatchers.sameDay(new Date())); + } + + @Test + public void shouldTranslateConditionRecorderToOpenmrsUser() { + Reference userRef = new Reference(); + userRef.setReference(FhirConstants.PRACTITIONER + "/" + PRACTITIONER_UUID); + fhirCondition.setRecorder(userRef); + User user = new User(); + user.setUuid(PRACTITIONER_UUID); + when(conceptService.getConceptByUuid(FhirConstants.CONDITION_OBSERVATION_CONCEPT_UUID)).thenReturn(concept); + when(creatorReferenceTranslator.toOpenmrsType(userRef)).thenReturn(user); + Obs condition = conditionTranslator.toOpenmrsType(fhirCondition); + assertThat(condition, notNullValue()); + assertThat(condition.getCreator(), notNullValue()); + assertThat(condition.getCreator().getUuid(), equalTo(PRACTITIONER_UUID)); + } + + @Test + public void shouldTranslateConditionCreatorToRecorderFhirType() { + User user = new User(); + user.setUuid(PRACTITIONER_UUID); + Reference userRef = new Reference(); + userRef.setReference(PRACTITIONER_REFERENCE); + openmrsCondition.setCreator(user); + when(creatorReferenceTranslator.toFhirResource(user)).thenReturn(userRef); + org.hl7.fhir.r4.model.Condition condition = conditionTranslator.toFhirResource(openmrsCondition); + assertThat(condition, notNullValue()); + assertThat(condition.getRecorder(), notNullValue()); + assertThat(condition.getRecorder().getReference(), equalTo(PRACTITIONER_REFERENCE)); + } + + @Test + public void shouldAddProvenanceToConditionResource() { + Provenance provenance = new Provenance(); + provenance.setId(new IdType(FhirUtils.newUuid())); + when(provenanceTranslator.getCreateProvenance(openmrsCondition)).thenReturn(provenance); + when(provenanceTranslator.getUpdateProvenance(openmrsCondition)).thenReturn(provenance); + + org.hl7.fhir.r4.model.Condition result = conditionTranslator.toFhirResource(openmrsCondition); + List<Resource> resources = result.getContained(); + assertThat(resources, Matchers.notNullValue()); + assertThat(resources, Matchers.not(empty())); + assertThat(resources.stream().findAny().isPresent(), CoreMatchers.is(true)); + assertThat(resources.stream().findAny().get().isResource(), CoreMatchers.is(true)); + assertThat(resources.stream().findAny().get().getResourceType().name(), + Matchers.equalTo(Provenance.class.getSimpleName())); + } +} diff --git a/integration-tests-2.2/src/test/java/org/openmrs/module/fhir2/provider/r3/ConditionResourceProviderIntegrationTest.java b/integration-tests-2.2/src/test/java/org/openmrs/module/fhir2/provider/r3/ConditionResourceProviderIntegrationTest.java index a5d6d917a..86ce645a7 100644 --- a/integration-tests-2.2/src/test/java/org/openmrs/module/fhir2/provider/r3/ConditionResourceProviderIntegrationTest.java +++ b/integration-tests-2.2/src/test/java/org/openmrs/module/fhir2/provider/r3/ConditionResourceProviderIntegrationTest.java @@ -182,6 +182,7 @@ public void shouldCreateNewPatientAsJson() throws Exception { assertThat(condition, notNullValue()); assertThat(condition.getIdElement().getIdPart(), notNullValue()); assertThat(condition.getClinicalStatus(), notNullValue()); + assertThat(condition.getOnsetDateTimeType(), notNullValue()); assertThat(condition.getClinicalStatus().toCode(), equalTo("active")); assertThat(condition.getCode(), notNullValue()); assertThat(condition.getCode().getCoding(), @@ -221,6 +222,7 @@ public void shouldCreateNewConditionAsXML() throws Exception { assertThat(condition, notNullValue()); assertThat(condition.getIdElement().getIdPart(), notNullValue()); assertThat(condition.getClinicalStatus(), notNullValue()); + assertThat(condition.getOnsetDateTimeType(), notNullValue()); assertThat(condition.getClinicalStatus().toCode(), equalTo("active")); assertThat(condition.getCode().getCoding(), hasItem(hasProperty("code", equalTo("116128AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")))); diff --git a/integration-tests/src/test/java/org/openmrs/module/fhir2/providers/r3/ConditionFhirResourceProviderIntegrationTest.java b/integration-tests/src/test/java/org/openmrs/module/fhir2/providers/r3/ConditionFhirResourceProviderIntegrationTest.java new file mode 100644 index 000000000..3caa77dc0 --- /dev/null +++ b/integration-tests/src/test/java/org/openmrs/module/fhir2/providers/r3/ConditionFhirResourceProviderIntegrationTest.java @@ -0,0 +1,441 @@ +/* + * 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.providers.r3; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInRelativeOrder; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.endsWith; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.everyItem; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.hasProperty; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.startsWith; + +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.sql.Date; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.List; + +import lombok.AccessLevel; +import lombok.Getter; +import org.apache.commons.io.IOUtils; +import org.hl7.fhir.dstu3.model.Bundle; +import org.hl7.fhir.dstu3.model.Condition; +import org.hl7.fhir.dstu3.model.OperationOutcome; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.mock.web.MockHttpServletResponse; + +public class ConditionFhirResourceProviderIntegrationTest extends BaseFhirR3IntegrationTest<ConditionFhirResourceProvider, Condition> { + + private static final String CONDITION_DATA_SET_FILE = "org/openmrs/module/fhir2/api/dao/impl/FhirObsConditionDaoImplTest_initial_data.xml"; + + private static final String JSON_CREATE_CONDITION_DOCUMENT = "org/openmrs/module/fhir2/providers/ConditionWebTest_create_r3.json"; + + private static final String XML_CREATE_CONDITION_DOCUMENT = "org/openmrs/module/fhir2/providers/ConditionWebTest_create_r3.xml"; + + private static final String CONDITION_UUID = "86sgf-1f7d-4394-a316-0a458edf28c4"; + + private static final String WRONG_CONDITION_UUID = "950d965d-a935-429f-945f-75a502a90188"; + + private static final String CONDITION_SUBJECT_UUID = "da7f524f-27ce-4bb2-86d6-6d1d05312bd5"; + + private static final String EXISTING_OBS_CONDITION_SUBJECT_UUID = "5946f880-b197-400b-9caa-a3c661d23041"; + + @Getter(AccessLevel.PUBLIC) + @Autowired + private ConditionFhirResourceProvider resourceProvider; + + @Before + @Override + public void setup() throws Exception { + super.setup(); + executeDataSet(CONDITION_DATA_SET_FILE); + } + + @Test + public void shouldReturnConditionAsJson() throws Exception { + MockHttpServletResponse response = get("/Condition/" + CONDITION_UUID).accept(FhirMediaTypes.JSON).go(); + + assertThat(response, isOk()); + assertThat(response.getContentType(), is(FhirMediaTypes.JSON.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + Condition condition = readResponse(response); + + assertThat(condition, notNullValue()); + assertThat(condition.getIdElement().getIdPart(), equalTo(CONDITION_UUID)); + + assertThat(condition.getOnsetDateTimeType().getValue(), + equalTo(Date.from(LocalDateTime.of(2008, 07, 1, 0, 0, 0).atZone(ZoneId.systemDefault()).toInstant()))); + + assertThat(condition.hasSubject(), is(true)); + assertThat(condition.getSubject().getReference(), equalTo("Patient/" + EXISTING_OBS_CONDITION_SUBJECT_UUID)); + + assertThat(condition, validResource()); + } + + @Test + public void shouldReturnNotFoundWhenConditionNotFoundAsJson() throws Exception { + MockHttpServletResponse response = get("/Condition/" + WRONG_CONDITION_UUID).accept(FhirMediaTypes.JSON).go(); + + assertThat(response, isNotFound()); + assertThat(response.getContentType(), is(FhirMediaTypes.JSON.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + OperationOutcome operationOutcome = readOperationOutcome(response); + + assertThat(operationOutcome, notNullValue()); + assertThat(operationOutcome.hasIssue(), is(true)); + } + + @Test + public void shouldReturnConditionAsXML() throws Exception { + MockHttpServletResponse response = get("/Condition/" + CONDITION_UUID).accept(FhirMediaTypes.XML).go(); + + assertThat(response, isOk()); + assertThat(response.getContentType(), is(FhirMediaTypes.XML.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + Condition condition = readResponse(response); + + assertThat(condition, notNullValue()); + assertThat(condition.getIdElement().getIdPart(), equalTo(CONDITION_UUID)); + + assertThat(condition.getOnsetDateTimeType().getValue(), + equalTo(Date.from(LocalDateTime.of(2008, 07, 01, 0, 0, 0).atZone(ZoneId.systemDefault()).toInstant()))); + + assertThat(condition.hasSubject(), is(true)); + assertThat(condition.getSubject().getReference(), equalTo("Patient/" + EXISTING_OBS_CONDITION_SUBJECT_UUID)); + + assertThat(condition, validResource()); + } + + @Test + public void shouldReturnNotFoundWhenConditionNotFoundAsXML() throws Exception { + MockHttpServletResponse response = get("/Condition/" + WRONG_CONDITION_UUID).accept(FhirMediaTypes.XML).go(); + + assertThat(response, isNotFound()); + assertThat(response.getContentType(), is(FhirMediaTypes.XML.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + OperationOutcome operationOutcome = readOperationOutcome(response); + + assertThat(operationOutcome, notNullValue()); + assertThat(operationOutcome.hasIssue(), is(true)); + } + + @Test + public void shouldCreateNewConditionAsJson() throws Exception { + String jsonCondition; + try (InputStream is = this.getClass().getClassLoader().getResourceAsStream(JSON_CREATE_CONDITION_DOCUMENT)) { + assertThat(is, notNullValue()); + jsonCondition = IOUtils.toString(is, StandardCharsets.UTF_8); + assertThat(jsonCondition, notNullValue()); + } + + MockHttpServletResponse response = post("/Condition").accept(FhirMediaTypes.JSON).jsonContent(jsonCondition).go(); + + assertThat(response, isCreated()); + assertThat(response.getHeader("Location"), containsString("/Condition/")); + assertThat(response.getContentType(), is(FhirMediaTypes.JSON.toString())); + assertThat(response.getContentType(), notNullValue()); + + Condition condition = readResponse(response); + + assertThat(condition, notNullValue()); + assertThat(condition.getIdElement().getIdPart(), notNullValue()); + assertThat(condition.getCode(), notNullValue()); + assertThat(condition.getCode().getCoding(), + hasItem(hasProperty("code", equalTo("116128AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")))); + assertThat(condition.getSubject(), notNullValue()); + assertThat(condition.getSubject().getReference(), endsWith(CONDITION_SUBJECT_UUID)); + + assertThat(condition, validResource()); + + response = get("/Condition/" + condition.getIdElement().getIdPart()).accept(FhirMediaTypes.JSON).go(); + + assertThat(response, isOk()); + + Condition newCondition = readResponse(response); + + assertThat(newCondition.getId(), equalTo(condition.getId())); + } + + @Test + public void shouldCreateNewConditionAsXML() throws Exception { + String xmlCondition; + try (InputStream is = this.getClass().getClassLoader().getResourceAsStream(XML_CREATE_CONDITION_DOCUMENT)) { + assertThat(is, notNullValue()); + xmlCondition = IOUtils.toString(is, StandardCharsets.UTF_8); + assertThat(xmlCondition, notNullValue()); + } + + MockHttpServletResponse response = post("/Condition").accept(FhirMediaTypes.XML).xmlContext(xmlCondition).go(); + + assertThat(response, isCreated()); + assertThat(response.getHeader("Location"), containsString("/Condition/")); + assertThat(response.getContentType(), is(FhirMediaTypes.XML.toString())); + assertThat(response.getContentType(), notNullValue()); + + Condition condition = readResponse(response); + + assertThat(condition, notNullValue()); + assertThat(condition.getIdElement().getIdPart(), notNullValue()); + assertThat(condition.getOnsetDateTimeType(), notNullValue()); + assertThat(condition.getCode().getCoding(), + hasItem(hasProperty("code", equalTo("116128AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")))); + assertThat(condition.getSubject().getReference(), endsWith(CONDITION_SUBJECT_UUID)); + + assertThat(condition, validResource()); + + response = get("/Condition/" + condition.getIdElement().getIdPart()).accept(FhirMediaTypes.JSON).go(); + + assertThat(response, isOk()); + + Condition newCondition = readResponse(response); + + assertThat(newCondition.getId(), equalTo(condition.getId())); + } + + @Test + public void shouldReturnBadRequestWhenDocumentIdDoesNotMatchConditionIdAsJson() throws Exception { + MockHttpServletResponse response = get("/Condition/" + CONDITION_UUID).accept(FhirMediaTypes.JSON).go(); + + assertThat(response, isOk()); + assertThat(response.getContentType(), equalTo(FhirMediaTypes.JSON.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + Condition condition = readResponse(response); + + condition.setId(WRONG_CONDITION_UUID); + + response = put("/Condition/" + CONDITION_UUID).jsonContent(toJson(condition)).accept(FhirMediaTypes.JSON).go(); + + assertThat(response, isBadRequest()); + assertThat(response.getContentType(), is(FhirMediaTypes.JSON.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + OperationOutcome operationOutcome = readOperationOutcome(response); + + assertThat(operationOutcome, notNullValue()); + assertThat(operationOutcome.hasIssue(), is(true)); + } + + @Test + public void shouldReturnNotFoundWhenUpdatingNonExistentConditionAsJson() throws Exception { + MockHttpServletResponse response = get("/Condition/" + CONDITION_UUID).accept(FhirMediaTypes.JSON).go(); + + assertThat(response, isOk()); + assertThat(response.getContentType(), equalTo(FhirMediaTypes.JSON.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + Condition condition = readResponse(response); + + condition.setId(WRONG_CONDITION_UUID); + + response = put("/Condition/" + WRONG_CONDITION_UUID).jsonContent(toJson(condition)).accept(FhirMediaTypes.JSON).go(); + + assertThat(response, isNotFound()); + assertThat(response.getContentType(), is(FhirMediaTypes.JSON.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + OperationOutcome operationOutcome = readOperationOutcome(response); + + assertThat(operationOutcome, notNullValue()); + assertThat(operationOutcome.hasIssue(), is(true)); + } + + @Test + public void shouldReturnBadRequestWhenDocumentIdDoesNotMatchConditionIdAsXML() throws Exception { + MockHttpServletResponse response = get("/Condition/" + CONDITION_UUID).accept(FhirMediaTypes.XML).go(); + + assertThat(response, isOk()); + assertThat(response.getContentType(), equalTo(FhirMediaTypes.XML.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + Condition condition = readResponse(response); + + condition.setId(WRONG_CONDITION_UUID); + + response = put("/Condition/" + CONDITION_UUID).xmlContext(toXML(condition)).accept(FhirMediaTypes.XML).go(); + + assertThat(response, isBadRequest()); + assertThat(response.getContentType(), is(FhirMediaTypes.XML.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + OperationOutcome operationOutcome = readOperationOutcome(response); + + assertThat(operationOutcome, notNullValue()); + assertThat(operationOutcome.hasIssue(), is(true)); + } + + @Test + public void shouldReturnNotFoundWhenUpdatingNonExistentConditionAsXML() throws Exception { + MockHttpServletResponse response = get("/Condition/" + CONDITION_UUID).accept(FhirMediaTypes.XML).go(); + + assertThat(response, isOk()); + assertThat(response.getContentType(), equalTo(FhirMediaTypes.XML.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + Condition condition = readResponse(response); + + condition.setId(WRONG_CONDITION_UUID); + + response = put("/Condition/" + WRONG_CONDITION_UUID).xmlContext(toXML(condition)).accept(FhirMediaTypes.XML).go(); + + assertThat(response, isNotFound()); + assertThat(response.getContentType(), is(FhirMediaTypes.XML.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + OperationOutcome operationOutcome = readOperationOutcome(response); + + assertThat(operationOutcome, notNullValue()); + assertThat(operationOutcome.hasIssue(), is(true)); + } + + @Test + public void shouldDeleteExistingCondition() throws Exception { + MockHttpServletResponse response = delete("/Condition/" + CONDITION_UUID).accept(FhirMediaTypes.JSON).go(); + + assertThat(response, isOk()); + + response = get("/Condition/" + CONDITION_UUID).accept(FhirMediaTypes.JSON).go(); + + assertThat(response, statusEquals(HttpStatus.GONE)); + } + + @Test + public void shouldReturnNotFoundWhenDeletingNonExistentCondition() throws Exception { + MockHttpServletResponse response = delete("/Condition/" + WRONG_CONDITION_UUID).accept(FhirMediaTypes.JSON).go(); + + assertThat(response, isNotFound()); + assertThat(response.getContentType(), is(FhirMediaTypes.JSON.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + OperationOutcome operationOutcome = readOperationOutcome(response); + + assertThat(operationOutcome, notNullValue()); + assertThat(operationOutcome.hasIssue(), is(true)); + } + + @Test + public void shouldSearchForAllConditionsAsJson() throws Exception { + MockHttpServletResponse response = get("/Condition").accept(FhirMediaTypes.JSON).go(); + + assertThat(response, isOk()); + assertThat(response.getContentType(), is(FhirMediaTypes.JSON.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + Bundle results = readBundleResponse(response); + + assertThat(results, notNullValue()); + assertThat(results.getType(), equalTo(Bundle.BundleType.SEARCHSET)); + assertThat(results.hasEntry(), is(true)); + + List<Bundle.BundleEntryComponent> entries = results.getEntry(); + + assertThat(entries, everyItem(hasProperty("fullUrl", startsWith("http://localhost/ws/fhir2/R3/Condition/")))); + assertThat(entries, everyItem(hasResource(instanceOf(Condition.class)))); + assertThat(entries, everyItem(hasResource(validResource()))); + assertThat(entries.size(), equalTo(2)); + } + + @Test + public void shouldReturnSortedAndFilteredSearchResultsForConditionsAsJson() throws Exception { + MockHttpServletResponse response = get("/Condition?clinical-status=active&onset-date=2008&_sort=-onset-date") + .accept(FhirMediaTypes.JSON).go(); + + assertThat(response, isOk()); + assertThat(response.getContentType(), is(FhirMediaTypes.JSON.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + Bundle results = readBundleResponse(response); + + assertThat(results, notNullValue()); + assertThat(results.getType(), equalTo(Bundle.BundleType.SEARCHSET)); + assertThat(results.hasEntry(), is(true)); + + List<Bundle.BundleEntryComponent> entries = results.getEntry(); + + assertThat(entries.size(), equalTo(2)); + assertThat(entries, + containsInRelativeOrder( + hasResource(hasProperty( + "onsetDateTimeType", + hasProperty( + "value", + equalTo( + Date.from(LocalDateTime.of(2008, 7, 1, 0, 0, 0).atZone(ZoneId.systemDefault()).toInstant()))))), + hasResource(hasProperty("onsetDateTimeType", hasProperty("value", equalTo( + Date.from(LocalDateTime.of(2008, 7, 1, 0, 0, 0).atZone(ZoneId.systemDefault()).toInstant()))))))); + } + + @Test + public void shouldSearchForAllConditionsAsXML() throws Exception { + MockHttpServletResponse response = get("/Condition").accept(FhirMediaTypes.XML).go(); + + assertThat(response, isOk()); + assertThat(response.getContentType(), is(FhirMediaTypes.XML.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + Bundle results = readBundleResponse(response); + + assertThat(results, notNullValue()); + assertThat(results.getType(), equalTo(Bundle.BundleType.SEARCHSET)); + assertThat(results.hasEntry(), is(true)); + + List<Bundle.BundleEntryComponent> entries = results.getEntry(); + + assertThat(entries, everyItem(hasProperty("fullUrl", startsWith("http://localhost/ws/fhir2/R3/Condition/")))); + assertThat(entries, everyItem(hasResource(instanceOf(Condition.class)))); + assertThat(entries, everyItem(hasResource(validResource()))); + assertThat(entries.size(), equalTo(2)); + } + + @Test + public void shouldReturnSortedAndFilteredSearchResultsForConditionsAsXML() throws Exception { + MockHttpServletResponse response = get("/Condition?clinical-status=active&onset-date=2008&_sort=-onset-date") + .accept(FhirMediaTypes.XML).go(); + + assertThat(response, isOk()); + assertThat(response.getContentType(), is(FhirMediaTypes.XML.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + Bundle results = readBundleResponse(response); + + assertThat(results, notNullValue()); + assertThat(results.getType(), equalTo(Bundle.BundleType.SEARCHSET)); + assertThat(results.hasEntry(), is(true)); + + List<Bundle.BundleEntryComponent> entries = results.getEntry(); + + assertThat(entries.size(), equalTo(2)); + assertThat(entries, + containsInRelativeOrder( + hasResource(hasProperty( + "onsetDateTimeType", + hasProperty( + "value", + equalTo( + Date.from(LocalDateTime.of(2008, 7, 1, 0, 0, 0).atZone(ZoneId.systemDefault()).toInstant()))))), + hasResource(hasProperty("onsetDateTimeType", hasProperty("value", equalTo( + Date.from(LocalDateTime.of(2008, 7, 1, 0, 0, 0).atZone(ZoneId.systemDefault()).toInstant()))))))); + } + +} diff --git a/integration-tests/src/test/java/org/openmrs/module/fhir2/providers/r4/ConditionFhirResourceProviderIntegrationTest.java b/integration-tests/src/test/java/org/openmrs/module/fhir2/providers/r4/ConditionFhirResourceProviderIntegrationTest.java new file mode 100644 index 000000000..ba58fd9b0 --- /dev/null +++ b/integration-tests/src/test/java/org/openmrs/module/fhir2/providers/r4/ConditionFhirResourceProviderIntegrationTest.java @@ -0,0 +1,436 @@ +/* + * 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.providers.r4; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsInRelativeOrder; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.endsWith; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.everyItem; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.hasProperty; +import static org.hamcrest.Matchers.instanceOf; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.startsWith; + +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.sql.Date; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.List; + +import lombok.AccessLevel; +import lombok.Getter; +import org.apache.commons.io.IOUtils; +import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.Condition; +import org.hl7.fhir.r4.model.OperationOutcome; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.mock.web.MockHttpServletResponse; + +public class ConditionFhirResourceProviderIntegrationTest extends BaseFhirR4IntegrationTest<ConditionFhirResourceProvider, Condition> { + + private static final String CONDITION_UUID = "86sgf-1f7d-4394-a316-0a458edf28c4"; + + private static final String OBS_CONDITION_INITIAL_DATA_XML = "org/openmrs/module/fhir2/api/dao/impl/FhirObsConditionDaoImplTest_initial_data.xml"; + + private static final String CONDITION_SUBJECT_UUID = "da7f524f-27ce-4bb2-86d6-6d1d05312bd5"; + + private static final String EXISTING_OBS_CONDITION_SUBJECT_UUID = "5946f880-b197-400b-9caa-a3c661d23041"; + + private static final String WRONG_CONDITION_UUID = "950d965d-a935-429f-945f-75a502a90188"; + + private static final String JSON_CREATE_CONDITION_DOCUMENT = "org/openmrs/module/fhir2/providers/ConditionWebTest_create.json"; + + private static final String XML_CREATE_CONDITION_DOCUMENT = "org/openmrs/module/fhir2/providers/ConditionWebTest_create.xml"; + + @Getter(AccessLevel.PUBLIC) + @Autowired + private ConditionFhirResourceProvider resourceProvider; + + @Before + public void setUp() throws Exception { + super.setup(); + executeDataSet(OBS_CONDITION_INITIAL_DATA_XML); + } + + @Test + public void shouldReturnConditionAsJson() throws Exception { + MockHttpServletResponse response = get("/Condition/" + CONDITION_UUID).accept(FhirMediaTypes.JSON).go(); + + assertThat(response, isOk()); + assertThat(response.getContentType(), is(FhirMediaTypes.JSON.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + Condition condition = readResponse(response); + + assertThat(condition, notNullValue()); + assertThat(condition.getIdElement().getIdPart(), equalTo(CONDITION_UUID)); + + assertThat(condition.getOnsetDateTimeType().getValue(), + equalTo(Date.from(LocalDateTime.of(2008, 07, 01, 0, 0, 0).atZone(ZoneId.systemDefault()).toInstant()))); + + assertThat(condition.hasSubject(), is(true)); + assertThat(condition.getSubject().getReference(), equalTo("Patient/" + EXISTING_OBS_CONDITION_SUBJECT_UUID)); + + assertThat(condition, validResource()); + } + + @Test + public void shouldReturnNotFoundWhenConditionNotFoundAsJson() throws Exception { + MockHttpServletResponse response = get("/Condition/" + WRONG_CONDITION_UUID).accept(FhirMediaTypes.JSON).go(); + + assertThat(response, isNotFound()); + assertThat(response.getContentType(), is(FhirMediaTypes.JSON.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + OperationOutcome operationOutcome = readOperationOutcome(response); + + assertThat(operationOutcome, notNullValue()); + assertThat(operationOutcome.hasIssue(), is(true)); + } + + @Test + public void shouldReturnConditionAsXML() throws Exception { + MockHttpServletResponse response = get("/Condition/" + CONDITION_UUID).accept(FhirMediaTypes.XML).go(); + + assertThat(response, isOk()); + assertThat(response.getContentType(), is(FhirMediaTypes.XML.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + Condition condition = readResponse(response); + + assertThat(condition, notNullValue()); + assertThat(condition.getIdElement().getIdPart(), equalTo(CONDITION_UUID)); + + assertThat(condition.getOnsetDateTimeType().getValue(), + equalTo(Date.from(LocalDateTime.of(2008, 07, 01, 0, 0, 0).atZone(ZoneId.systemDefault()).toInstant()))); + + assertThat(condition.hasSubject(), is(true)); + assertThat(condition.getSubject().getReference(), equalTo("Patient/" + EXISTING_OBS_CONDITION_SUBJECT_UUID)); + + assertThat(condition, validResource()); + } + + @Test + public void shouldReturnNotFoundWhenConditionNotFoundAsXML() throws Exception { + MockHttpServletResponse response = get("/Condition/" + WRONG_CONDITION_UUID).accept(FhirMediaTypes.XML).go(); + + assertThat(response, isNotFound()); + assertThat(response.getContentType(), is(FhirMediaTypes.XML.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + OperationOutcome operationOutcome = readOperationOutcome(response); + + assertThat(operationOutcome, notNullValue()); + assertThat(operationOutcome.hasIssue(), is(true)); + } + + @Test + public void shouldCreateNewConditionAsJson() throws Exception { + String jsonCondition; + try (InputStream is = this.getClass().getClassLoader().getResourceAsStream(JSON_CREATE_CONDITION_DOCUMENT)) { + assertThat(is, notNullValue()); + jsonCondition = IOUtils.toString(is, StandardCharsets.UTF_8); + assertThat(jsonCondition, notNullValue()); + } + + MockHttpServletResponse response = post("/Condition").accept(FhirMediaTypes.JSON).jsonContent(jsonCondition).go(); + + assertThat(response, isCreated()); + assertThat(response.getHeader("Location"), containsString("/Condition/")); + assertThat(response.getContentType(), is(FhirMediaTypes.JSON.toString())); + assertThat(response.getContentType(), notNullValue()); + + Condition condition = readResponse(response); + + assertThat(condition, notNullValue()); + assertThat(condition.getIdElement().getIdPart(), notNullValue()); + assertThat(condition.getCode(), notNullValue()); + assertThat(condition.getCode().getCoding(), + hasItem(hasProperty("code", equalTo("116128AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")))); + assertThat(condition.getSubject(), notNullValue()); + assertThat(condition.getSubject().getReference(), endsWith(CONDITION_SUBJECT_UUID)); + + assertThat(condition, validResource()); + + response = get("/Condition/" + condition.getIdElement().getIdPart()).accept(FhirMediaTypes.JSON).go(); + assertThat(response, isOk()); + Condition newCondition = readResponse(response); + assertThat(newCondition.getId(), equalTo(condition.getId())); + } + + @Test + public void shouldCreateNewConditionAsXML() throws Exception { + String xmlCondition; + try (InputStream is = this.getClass().getClassLoader().getResourceAsStream(XML_CREATE_CONDITION_DOCUMENT)) { + assertThat(is, notNullValue()); + xmlCondition = IOUtils.toString(is, StandardCharsets.UTF_8); + assertThat(xmlCondition, notNullValue()); + } + + MockHttpServletResponse response = post("/Condition").accept(FhirMediaTypes.XML).xmlContext(xmlCondition).go(); + + assertThat(response, isCreated()); + assertThat(response.getHeader("Location"), containsString("/Condition/")); + assertThat(response.getContentType(), is(FhirMediaTypes.XML.toString())); + assertThat(response.getContentType(), notNullValue()); + + Condition condition = readResponse(response); + + assertThat(condition, notNullValue()); + assertThat(condition.getIdElement().getIdPart(), notNullValue()); + assertThat(condition.getCode().getCoding(), + hasItem(hasProperty("code", equalTo("116128AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")))); + assertThat(condition.getSubject().getReference(), endsWith(CONDITION_SUBJECT_UUID)); + + assertThat(condition, validResource()); + + response = get("/Condition/" + condition.getIdElement().getIdPart()).accept(FhirMediaTypes.JSON).go(); + + assertThat(response, isOk()); + + Condition newCondition = readResponse(response); + + assertThat(newCondition.getId(), equalTo(condition.getId())); + } + + @Test + public void shouldReturnBadRequestWhenDocumentIdDoesNotMatchConditionIdAsJson() throws Exception { + MockHttpServletResponse response = get("/Condition/" + CONDITION_UUID).accept(FhirMediaTypes.JSON).go(); + + assertThat(response, isOk()); + assertThat(response.getContentType(), equalTo(FhirMediaTypes.JSON.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + Condition condition = readResponse(response); + + condition.setId(WRONG_CONDITION_UUID); + + response = put("/Condition/" + CONDITION_UUID).jsonContent(toJson(condition)).accept(FhirMediaTypes.JSON).go(); + + assertThat(response, isBadRequest()); + assertThat(response.getContentType(), is(FhirMediaTypes.JSON.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + OperationOutcome operationOutcome = readOperationOutcome(response); + + assertThat(operationOutcome, notNullValue()); + assertThat(operationOutcome.hasIssue(), is(true)); + } + + @Test + public void shouldReturnNotFoundWhenUpdatingNonExistentConditionAsJson() throws Exception { + MockHttpServletResponse response = get("/Condition/" + CONDITION_UUID).accept(FhirMediaTypes.JSON).go(); + + assertThat(response, isOk()); + assertThat(response.getContentType(), equalTo(FhirMediaTypes.JSON.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + Condition condition = readResponse(response); + + condition.setId(WRONG_CONDITION_UUID); + + response = put("/Condition/" + WRONG_CONDITION_UUID).jsonContent(toJson(condition)).accept(FhirMediaTypes.JSON).go(); + + assertThat(response, isNotFound()); + assertThat(response.getContentType(), is(FhirMediaTypes.JSON.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + OperationOutcome operationOutcome = readOperationOutcome(response); + + assertThat(operationOutcome, notNullValue()); + assertThat(operationOutcome.hasIssue(), is(true)); + } + + @Test + public void shouldReturnBadRequestWhenDocumentIdDoesNotMatchConditionIdAsXML() throws Exception { + MockHttpServletResponse response = get("/Condition/" + CONDITION_UUID).accept(FhirMediaTypes.XML).go(); + + assertThat(response, isOk()); + assertThat(response.getContentType(), equalTo(FhirMediaTypes.XML.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + Condition condition = readResponse(response); + + condition.setId(WRONG_CONDITION_UUID); + + response = put("/Condition/" + CONDITION_UUID).xmlContext(toXML(condition)).accept(FhirMediaTypes.XML).go(); + + assertThat(response, isBadRequest()); + assertThat(response.getContentType(), is(FhirMediaTypes.XML.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + OperationOutcome operationOutcome = readOperationOutcome(response); + + assertThat(operationOutcome, notNullValue()); + assertThat(operationOutcome.hasIssue(), is(true)); + } + + @Test + public void shouldReturnNotFoundWhenUpdatingNonExistentConditionAsXML() throws Exception { + MockHttpServletResponse response = get("/Condition/" + CONDITION_UUID).accept(FhirMediaTypes.XML).go(); + + assertThat(response, isOk()); + assertThat(response.getContentType(), equalTo(FhirMediaTypes.XML.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + Condition condition = readResponse(response); + + condition.setId(WRONG_CONDITION_UUID); + + response = put("/Condition/" + WRONG_CONDITION_UUID).xmlContext(toXML(condition)).accept(FhirMediaTypes.XML).go(); + + assertThat(response, isNotFound()); + assertThat(response.getContentType(), is(FhirMediaTypes.XML.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + OperationOutcome operationOutcome = readOperationOutcome(response); + + assertThat(operationOutcome, notNullValue()); + assertThat(operationOutcome.hasIssue(), is(true)); + } + + @Test + public void shouldDeleteExistingCondition() throws Exception { + MockHttpServletResponse response = delete("/Condition/" + CONDITION_UUID).accept(FhirMediaTypes.JSON).go(); + + assertThat(response, isOk()); + + response = get("/Condition/" + CONDITION_UUID).accept(FhirMediaTypes.JSON).go(); + + assertThat(response, statusEquals(HttpStatus.GONE)); + } + + @Test + public void shouldReturnNotFoundWhenDeletingNonExistentCondition() throws Exception { + MockHttpServletResponse response = delete("/Condition/" + WRONG_CONDITION_UUID).accept(FhirMediaTypes.JSON).go(); + + assertThat(response, isNotFound()); + assertThat(response.getContentType(), is(FhirMediaTypes.JSON.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + OperationOutcome operationOutcome = readOperationOutcome(response); + + assertThat(operationOutcome, notNullValue()); + assertThat(operationOutcome.hasIssue(), is(true)); + } + + @Test + public void shouldSearchForAllConditionsAsJson() throws Exception { + MockHttpServletResponse response = get("/Condition").accept(FhirMediaTypes.JSON).go(); + + assertThat(response, isOk()); + assertThat(response.getContentType(), is(FhirMediaTypes.JSON.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + Bundle results = readBundleResponse(response); + + assertThat(results, notNullValue()); + assertThat(results.getType(), equalTo(Bundle.BundleType.SEARCHSET)); + assertThat(results.hasEntry(), is(true)); + + List<Bundle.BundleEntryComponent> entries = results.getEntry(); + + assertThat(entries, everyItem(hasProperty("fullUrl", startsWith("http://localhost/ws/fhir2/R4/Condition/")))); + assertThat(entries, everyItem(hasResource(instanceOf(Condition.class)))); + assertThat(entries, everyItem(hasResource(validResource()))); + assertThat(entries.size(), equalTo(2)); + } + + @Test + public void shouldReturnSortedAndFilteredSearchResultsForConditionsAsJson() throws Exception { + MockHttpServletResponse response = get("/Condition?clinical-status=active&onset-date=2008&_sort=-onset-date") + .accept(FhirMediaTypes.JSON).go(); + + assertThat(response, isOk()); + assertThat(response.getContentType(), is(FhirMediaTypes.JSON.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + Bundle results = readBundleResponse(response); + + assertThat(results, notNullValue()); + assertThat(results.getType(), equalTo(Bundle.BundleType.SEARCHSET)); + assertThat(results.hasEntry(), is(true)); + + List<Bundle.BundleEntryComponent> entries = results.getEntry(); + + assertThat(entries.size(), equalTo(2)); + assertThat(entries, + containsInRelativeOrder( + hasResource(hasProperty( + "onsetDateTimeType", + hasProperty( + "value", + equalTo( + Date.from(LocalDateTime.of(2008, 7, 1, 0, 0, 0).atZone(ZoneId.systemDefault()).toInstant()))))), + hasResource(hasProperty("onsetDateTimeType", hasProperty("value", equalTo( + Date.from(LocalDateTime.of(2008, 7, 1, 0, 0, 0).atZone(ZoneId.systemDefault()).toInstant()))))))); + } + + @Test + public void shouldSearchForAllConditionsAsXML() throws Exception { + MockHttpServletResponse response = get("/Condition").accept(FhirMediaTypes.XML).go(); + + assertThat(response, isOk()); + assertThat(response.getContentType(), is(FhirMediaTypes.XML.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + Bundle results = readBundleResponse(response); + + assertThat(results, notNullValue()); + assertThat(results.getType(), equalTo(Bundle.BundleType.SEARCHSET)); + assertThat(results.hasEntry(), is(true)); + + List<Bundle.BundleEntryComponent> entries = results.getEntry(); + + assertThat(entries, everyItem(hasProperty("fullUrl", startsWith("http://localhost/ws/fhir2/R4/Condition/")))); + assertThat(entries, everyItem(hasResource(instanceOf(Condition.class)))); + assertThat(entries, everyItem(hasResource(validResource()))); + assertThat(entries.size(), equalTo(2)); + } + + @Test + public void shouldReturnSortedAndFilteredSearchResultsForConditionsAsXML() throws Exception { + MockHttpServletResponse response = get("/Condition?clinical-status=active&onset-date=2008&_sort=-onset-date") + .accept(FhirMediaTypes.XML).go(); + + assertThat(response, isOk()); + assertThat(response.getContentType(), is(FhirMediaTypes.XML.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + Bundle results = readBundleResponse(response); + + assertThat(results, notNullValue()); + assertThat(results.getType(), equalTo(Bundle.BundleType.SEARCHSET)); + assertThat(results.hasEntry(), is(true)); + + List<Bundle.BundleEntryComponent> entries = results.getEntry(); + + assertThat(entries.size(), equalTo(2)); + assertThat(entries, + containsInRelativeOrder( + hasResource(hasProperty( + "onsetDateTimeType", + hasProperty( + "value", + equalTo( + Date.from(LocalDateTime.of(2008, 7, 1, 0, 0, 0).atZone(ZoneId.systemDefault()).toInstant()))))), + hasResource(hasProperty("onsetDateTimeType", hasProperty("value", equalTo( + Date.from(LocalDateTime.of(2008, 7, 1, 0, 0, 0).atZone(ZoneId.systemDefault()).toInstant()))))))); + } + +} diff --git a/test-data/src/test/resources/org/openmrs/module/fhir2/api/dao/impl/FhirObsConditionDaoImplTest_initial_data.xml b/test-data/src/test/resources/org/openmrs/module/fhir2/api/dao/impl/FhirObsConditionDaoImplTest_initial_data.xml new file mode 100644 index 000000000..0b921663a --- /dev/null +++ b/test-data/src/test/resources/org/openmrs/module/fhir2/api/dao/impl/FhirObsConditionDaoImplTest_initial_data.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!-- + 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. +--> +<dataset> + <concept concept_id="116128" retired="false" datatype_id="4" class_id="4" is_set="false" creator="1" date_created="2008-08-15 15:27:51.0" version="" uuid="116128AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"/> + <concept_description concept_description_id="116128" concept_id="116128" description="Malaria" locale="en_GB" creator="1" date_created="2008-08-15 15:27:51.0" uuid="4639FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"/> + <concept_name concept_name_id="116128" concept_id="116128" name="Malaria" locale="en_GB" concept_name_type="FULLY_SPECIFIED" locale_preferred="1" voided="false" creator="1" date_created="2008-08-15 15:27:51.0" uuid="16603BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"/> + <concept concept_id="1284" retired="false" datatype_id="4" class_id="4" is_set="false" creator="1" date_created="2008-08-15 15:27:51.0" version="" uuid="1284AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"/> + <concept_description concept_description_id="1284" concept_id="1284" description="PROBLEM LIST" locale="en_GB" creator="1" date_created="2008-08-15 15:27:51.0" uuid="1284FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"/> + <concept_name concept_name_id="1284" concept_id="1284" name="PROBLEM LIST" locale="en_GB" concept_name_type="FULLY_SPECIFIED" locale_preferred="1" voided="false" creator="1" date_created="2008-08-15 15:27:51.0" uuid="1284BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"/> + <concept_map_type concept_map_type_id="1" name="SAME-AS" creator="1" date_created="2020-04-02 00:00:00.0" changed_by="1" is_hidden="false" retired="false" retired_by="1" uuid="35543629-7d8c-11e1-909d-c80aa9edcf4ex"/> + <concept_reference_source concept_source_id="1" name="ICD-10-WHO" description="SNOMED Preferred mapping" hl7_code="SCT" creator="1" date_created="2009-11-19 20:19:16.0" retired="false" retired_by="1" uuid="1ADDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD" /> + <concept_reference_term concept_reference_term_id="1" concept_source_id="1" code="C00" creator="1" date_created="2010-04-25 00:00:00.0" changed_by="1" retired="false" retired_by="1" uuid="8211fc83-6c63-38c1-b2ce-108aeee34ea6"/> + <concept_reference_map concept_map_id="93972" creator="1" date_created="2010-04-25 11:39:31.0" concept_id="116128" uuid="93972ABBBBBBBBBBBBBBBBBBBBBBBBBBBBBB" concept_reference_term_id="1" concept_map_type_id="1" changed_by="1"/> + <obs obs_id="30" person_id="7" concept_id="1284" encounter_id="3" obs_datetime="2008-07-01 00:00:00.0" location_id="1" comments="" creator="1" date_created="2008-08-18 14:09:35.0" voided="false" value_coded="116128" uuid="86sgf-1f7d-4394-a316-0a458edf28c4"/> + <obs obs_id="31" person_id="7" concept_id="1284" encounter_id="3" obs_datetime="2008-07-01 00:00:00.0" location_id="1" comments="" creator="1" date_created="2008-08-18 14:09:35.0" voided="false" value_coded="116128" uuid="86sgf-1f7d-4394-a316-0a458edf28c7"/> + <obs obs_id="32" person_id="7" concept_id="116128" encounter_id="3" obs_datetime="2008-07-01 10:00:00.0" location_id="1" comments="" creator="1" date_created="2008-08-18 14:09:35.0" voided="false" value_coded="116128" uuid="942ec003-a55d-43c4-ac7a-bd6d1ba63382"/> + <obs obs_id="33" person_id="7" concept_id="1284" encounter_id="3" obs_datetime="2008-07-01 10:00:00.0" location_id="1" comments="" creator="1" date_voided="2010-09-03 14:09:35.0" date_created="2008-08-18 14:09:35.0" voided="true" value_coded="116128" uuid="94dhs003-a55d-43c4-ac7a-bd6d1ba63388"/> + <fhir_concept_source fhir_concept_source_id="1" name="Some Made-up Terminology" url="http://made_up_concepts.info/sct" concept_source_id="1" creator="1" date_created="2005-01-01 00:00:00.0" retired="0" uuid="0d30bea3-4ba2-4ab4-ac7b-5525840bde20" /> +</dataset> diff --git a/test-data/src/test/resources/org/openmrs/module/fhir2/providers/ConditionWebTest_create_r3.json b/test-data/src/test/resources/org/openmrs/module/fhir2/providers/ConditionWebTest_create_r3.json index 01dc52966..e50376b37 100644 --- a/test-data/src/test/resources/org/openmrs/module/fhir2/providers/ConditionWebTest_create_r3.json +++ b/test-data/src/test/resources/org/openmrs/module/fhir2/providers/ConditionWebTest_create_r3.json @@ -22,5 +22,5 @@ }, "display": "Horatio Hornblower (OpenMRS ID:101-6)" }, - "recordedDate": "2019-06-18T06:37:26+03:00" + "onsetDateTime": "2019-06-18T06:37:26+03:00" } diff --git a/test-data/src/test/resources/org/openmrs/module/fhir2/providers/ConditionWebTest_create_r3.xml b/test-data/src/test/resources/org/openmrs/module/fhir2/providers/ConditionWebTest_create_r3.xml index 9fb064c25..2b947bc5f 100644 --- a/test-data/src/test/resources/org/openmrs/module/fhir2/providers/ConditionWebTest_create_r3.xml +++ b/test-data/src/test/resources/org/openmrs/module/fhir2/providers/ConditionWebTest_create_r3.xml @@ -27,5 +27,5 @@ </identifier> <display value="Horatio Hornblower (OpenMRS ID:101-6)"/> </subject> - <recordedDate value="2019-06-18T06:37:26+03:00"/> -</Condition> \ No newline at end of file + <onsetDateTime value="2019-06-18T06:37:26+03:00"/> +</Condition> From 355e5ce87e46f9d15a66d6f2d9e5a0fa6692f5a5 Mon Sep 17 00:00:00 2001 From: Ankit kumar <49350053+theanandankit@users.noreply.github.com> Date: Mon, 1 Feb 2021 20:16:47 +0530 Subject: [PATCH 08/18] FM-189: Add Timing.TimingRepeatComponent.DurationUnit property (#310) --- .../fhir2/api/mappings/DurationUnitMap.java | 49 ++++ .../translators/DurationUnitTranslator.java | 21 ++ .../impl/DurationUnitTranslatorImpl.java | 46 ++++ .../fhir2/model/FhirDurationUnitMap.java | 90 +++++++ api/src/main/resources/liquibase.xml | 219 ++++++++++++++++++ .../impl/DurationUnitTranslatorImplTest.java | 153 ++++++++++++ ...urationUnitTranslatorTest_initial_data.xml | 19 ++ 7 files changed, 597 insertions(+) create mode 100644 api/src/main/java/org/openmrs/module/fhir2/api/mappings/DurationUnitMap.java create mode 100644 api/src/main/java/org/openmrs/module/fhir2/api/translators/DurationUnitTranslator.java create mode 100644 api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/DurationUnitTranslatorImpl.java create mode 100644 api/src/main/java/org/openmrs/module/fhir2/model/FhirDurationUnitMap.java create mode 100644 api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/DurationUnitTranslatorImplTest.java create mode 100644 test-data/src/test/resources/org/openmrs/module/fhir2/mapping/FhirDurationUnitTranslatorTest_initial_data.xml diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/mappings/DurationUnitMap.java b/api/src/main/java/org/openmrs/module/fhir2/api/mappings/DurationUnitMap.java new file mode 100644 index 000000000..7561e4b47 --- /dev/null +++ b/api/src/main/java/org/openmrs/module/fhir2/api/mappings/DurationUnitMap.java @@ -0,0 +1,49 @@ +/* + * 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.api.mappings; + +import static org.hibernate.criterion.Restrictions.eq; + +import javax.annotation.Nonnull; + +import lombok.AccessLevel; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.hibernate.NonUniqueResultException; +import org.hibernate.SessionFactory; +import org.hibernate.criterion.Projections; +import org.hl7.fhir.r4.model.Timing; +import org.openmrs.module.fhir2.model.FhirDurationUnitMap; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +@Setter(AccessLevel.PUBLIC) +public class DurationUnitMap { + + @Autowired + @Qualifier("sessionFactory") + private SessionFactory sessionFactory; + + public Timing.UnitsOfTime getDurationUnit(@Nonnull String conceptUuid) { + + try { + return (Timing.UnitsOfTime) sessionFactory.getCurrentSession().createCriteria(FhirDurationUnitMap.class) + .createAlias("concept", "c").add(eq("c.uuid", conceptUuid)) + .setProjection(Projections.property("unit_of_time")).uniqueResult(); + } + catch (NonUniqueResultException e) { + log.error("Exception caught while trying to load DurationUnit for concept '{}'", conceptUuid, e); + } + return null; + } +} diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/translators/DurationUnitTranslator.java b/api/src/main/java/org/openmrs/module/fhir2/api/translators/DurationUnitTranslator.java new file mode 100644 index 000000000..0e4750c65 --- /dev/null +++ b/api/src/main/java/org/openmrs/module/fhir2/api/translators/DurationUnitTranslator.java @@ -0,0 +1,21 @@ +/* + * 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.api.translators; + +import javax.annotation.Nonnull; + +import org.hl7.fhir.r4.model.Timing; +import org.openmrs.Concept; + +public interface DurationUnitTranslator extends ToFhirTranslator<Concept, Timing.UnitsOfTime> { + + @Override + Timing.UnitsOfTime toFhirResource(@Nonnull Concept concept); +} diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/DurationUnitTranslatorImpl.java b/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/DurationUnitTranslatorImpl.java new file mode 100644 index 000000000..530cd7d81 --- /dev/null +++ b/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/DurationUnitTranslatorImpl.java @@ -0,0 +1,46 @@ +/* + * 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.api.translators.impl; + +import javax.annotation.Nonnull; + +import lombok.AccessLevel; +import lombok.Setter; +import org.hl7.fhir.r4.model.Timing; +import org.openmrs.Concept; +import org.openmrs.module.fhir2.api.mappings.DurationUnitMap; +import org.openmrs.module.fhir2.api.translators.DurationUnitTranslator; +import org.springframework.beans.factory.annotation.Autowired; + +@Setter(AccessLevel.PACKAGE) +public class DurationUnitTranslatorImpl implements DurationUnitTranslator { + + Timing.UnitsOfTime unitsOfTime; + + @Autowired + private DurationUnitMap durationUnitMap; + + @Override + public Timing.UnitsOfTime toFhirResource(@Nonnull Concept concept) { + + if (concept.getUuid() == null) { + return null; + } + + unitsOfTime = durationUnitMap.getDurationUnit(concept.getUuid()); + + if (unitsOfTime == null) { + return Timing.UnitsOfTime.NULL; + } + + return unitsOfTime; + } +} diff --git a/api/src/main/java/org/openmrs/module/fhir2/model/FhirDurationUnitMap.java b/api/src/main/java/org/openmrs/module/fhir2/model/FhirDurationUnitMap.java new file mode 100644 index 000000000..92e854eee --- /dev/null +++ b/api/src/main/java/org/openmrs/module/fhir2/model/FhirDurationUnitMap.java @@ -0,0 +1,90 @@ +/* + * 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.model; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; + +import java.util.Date; +import java.util.UUID; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.hibernate.search.annotations.Field; +import org.hl7.fhir.r4.model.Timing; +import org.openmrs.Auditable; +import org.openmrs.Concept; +import org.openmrs.Retireable; +import org.openmrs.User; + +@Data +@EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = false) +@Entity +@Table(name = "fhir_duration_unit_map") +public class FhirDurationUnitMap implements Auditable, Retireable { + + @EqualsAndHashCode.Include + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + @Column(name = "duration_unit_map_id") + private Integer id; + + @ManyToOne(optional = false) + @JoinColumn(nullable = false, name = "concept_id") + private Concept concept; + + @Column(nullable = false, name = "unit_of_time") + @Enumerated(EnumType.STRING) + private Timing.UnitsOfTime unit_of_time; + + @ManyToOne(optional = false) + @JoinColumn(name = "creator", updatable = false) + protected User creator; + + @Column(name = "date_created", nullable = false, updatable = false) + private Date dateCreated; + + @ManyToOne + @JoinColumn(name = "changed_by") + private User changedBy; + + @Column(name = "date_changed") + private Date dateChanged; + + @Column(name = "retired", nullable = false) + @Field + private Boolean retired = Boolean.FALSE; + + @Column(name = "date_retired") + private Date dateRetired; + + @ManyToOne + @JoinColumn(name = "retired_by") + private User retiredBy; + + @Column(name = "retire_reason") + private String retireReason; + + @Column(name = "uuid", unique = true, nullable = false, length = 36) + private String uuid = UUID.randomUUID().toString(); + + @Override + public Boolean isRetired() { + return retired; + } +} diff --git a/api/src/main/resources/liquibase.xml b/api/src/main/resources/liquibase.xml index a6d18b98e..fcdd7783c 100644 --- a/api/src/main/resources/liquibase.xml +++ b/api/src/main/resources/liquibase.xml @@ -696,4 +696,223 @@ ]]> </sql> </changeSet> + + <changeSet id="add_fhir_duration_unit_map_20200930" author="ibacher"> + <preConditions onFail="MARK_RAN" onError="WARN"> + <not> + <tableExists tableName="fhir_duration_unit_map"/> + </not> + </preConditions> + <createTable tableName="fhir_duration_unit_map"> + <column name="duration_unit_map_id" type="int" autoIncrement="true"> + <constraints primaryKey="true"/> + </column> + <column name="concept_id" type="int"/> + <column name="unit_of_time" type="varchar(20)"> + <constraints nullable="false"/> + </column> + <column name="creator" type="int"> + <constraints nullable="false"/> + </column> + <column name="date_created" type="datetime"> + <constraints nullable="false"/> + </column> + <column name="changed_by" type="int"/> + <column name="date_changed" type="datetime"/> + <column name="retired" type="boolean" defaultValueBoolean="false"> + <constraints nullable="false"/> + </column> + <column name="retired_by" type="int"/> + <column name="date_retired" type="datetime"/> + <column name="retired_reason" type="varchar(255)" defaultValue="null"/> + <column name="uuid" type="char(36)"> + <constraints nullable="false" unique="true"/> + </column> + </createTable> + <addForeignKeyConstraint constraintName="fhir_duration_unit_map_creator" + baseTableName="fhir_duration_unit_map" baseColumnNames="creator" + referencedTableName="users" referencedColumnNames="user_id"/> + <addForeignKeyConstraint constraintName="fhir_duration_unit_map_changed_by" + baseTableName="fhir_duration_unit_map" baseColumnNames="changed_by" + referencedTableName="users" referencedColumnNames="user_id"/> + <addForeignKeyConstraint constraintName="fhir_duration_unit_map_retired_by" + baseTableName="fhir_duration_unit_map" baseColumnNames="retired_by" + referencedTableName="users" referencedColumnNames="user_id"/> + <addForeignKeyConstraint constraintName="fhir_duration_unit_map_concept" + baseTableName="fhir_duration_unit_map" baseColumnNames="concept_id" + referencedTableName="concept" referencedColumnNames="concept_id"/> + <createIndex tableName="fhir_duration_unit_map" + indexName="fhir_duration_unit_map_unit_of_time"> + <column name="unit_of_time"/> + </createIndex> + </changeSet> + + <changeSet id="add_default_duration_unit_20200930" author="ibacher" dbms="mysql,mariadb"> + <preConditions onFail="MARK_RAN" onError="WARN"> + <and> + <tableExists tableName="concept"/> + <tableExists tableName="fhir_duration_unit_map"/> + <sqlCheck expectedResult="0"> + select count(*) from fhir_duration_unit_map + </sqlCheck> + <sqlCheck expectedResult="1"> + select 1 from concept where uuid = '162583AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + </sqlCheck> + <sqlCheck expectedResult="1"> + select 1 from concept where uuid = '1733AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + </sqlCheck> + <sqlCheck expectedResult="1"> + select 1 from concept where uuid = '1822AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + </sqlCheck> + <sqlCheck expectedResult="1"> + select 1 from concept where uuid = '1072AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + </sqlCheck> + <sqlCheck expectedResult="1"> + select 1 from concept where uuid = '1073AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + </sqlCheck> + <sqlCheck expectedResult="1"> + select 1 from concept where uuid = '1074AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + </sqlCheck> + <sqlCheck expectedResult="1"> + select 1 from concept where uuid = '1734AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + </sqlCheck> + </and> + </preConditions> + <sql><![CDATA[ + insert into fhir_duration_unit_map (concept_id, unit_of_time, creator, date_created, uuid) + select concept_id, 'S', 1, now(), uuid() + from concept + where uuid = '162583AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + ]]> + </sql> + <sql><![CDATA[ + insert into fhir_duration_unit_map (concept_id, unit_of_time, creator, date_created, uuid) + select concept_id, 'MIN', 1, now(), uuid() + from concept + where uuid = '1733AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + ]]> + </sql> + <sql><![CDATA[ + insert into fhir_duration_unit_map (concept_id, unit_of_time, creator, date_created, uuid) + select concept_id, 'H', 1, now(), uuid() + from concept + where uuid = '1822AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + ]]> + </sql> + <sql><![CDATA[ + insert into fhir_duration_unit_map (concept_id, unit_of_time, creator, date_created, uuid) + select concept_id, 'D', 1, now(), uuid() + from concept + where uuid = '1072AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + ]]> + </sql> + <sql><![CDATA[ + insert into fhir_duration_unit_map (concept_id, unit_of_time, creator, date_created, uuid) + select concept_id, 'WK', 1, now(), uuid() + from concept + where uuid = '1073AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + ]]> + </sql> + <sql><![CDATA[ + insert into fhir_duration_unit_map (concept_id, unit_of_time, creator, date_created, uuid) + select concept_id, 'MO', 1, now(), uuid() + from concept + where uuid = '1074AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + ]]> + </sql> + <sql><![CDATA[ + insert into fhir_duration_unit_map (concept_id, unit_of_time, creator, date_created, uuid) + select concept_id, 'A' ,1, now(), uuid() + from concept + where uuid = '1734AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + ]]> + </sql> + </changeSet> + + <changeSet id="add_default_duration_unit_20200930" author="ibacher" dbms="postgresql"> + <preConditions onFail="MARK_RAN" onError="WARN"> + <and> + <tableExists tableName="fhir_duration_unit_map"/> + <sqlCheck expectedResult="0"> + select count(*) from fhir_duration_unit_map + </sqlCheck> + <sqlCheck expectedResult="1"> + select 1 from concept where uuid = '162583AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + </sqlCheck> + <sqlCheck expectedResult="1"> + select 1 from concept where uuid = '1733AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + </sqlCheck> + <sqlCheck expectedResult="1"> + select 1 from concept where uuid = '1072AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + </sqlCheck> + <sqlCheck expectedResult="1"> + select 1 from concept where uuid = '1073AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + </sqlCheck> + <sqlCheck expectedResult="1"> + select 1 from concept where uuid = '1073AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + </sqlCheck> + <sqlCheck expectedResult="1"> + select 1 from concept where uuid = '1074AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + </sqlCheck> + <sqlCheck expectedResult="1"> + select 1 from concept where uuid = '1734AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + </sqlCheck> + + </and> + </preConditions> + <!-- UUID function taken from here: https://stackoverflow.com/a/21327318 --> + <sql><![CDATA[ + insert into fhir_duration_unit_map (concept_id, unit_of_time, creator, date_created, uuid) + select concept_id, "S", 1, now(), uuid_in(overlay(overlay(md5(random()::text || ':' || clock_timestamp()::text) placing '4' from 13) placing to_hex(floor(random()*(11-8+1) + 8)::int)::text from 17)::cstring) + from concept + where uuid = '162583AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + ]]> + </sql> + <sql><![CDATA[ + insert into fhir_duration_unit_map (concept_id, unit_of_time, creator, date_created, uuid) + select concept_id, 'MIN', 1, now(), uuid_in(overlay(overlay(md5(random()::text || ':' || clock_timestamp()::text) placing '4' from 13) placing to_hex(floor(random()*(11-8+1) + 8)::int)::text from 17)::cstring) + from concept + where uuid = '1733AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + ]]> + </sql> + <sql><![CDATA[ + insert into fhir_duration_unit_map (concept_id, unit_of_time, creator, date_created, uuid) + select concept_id, 'H', 1, now(), uuid_in(overlay(overlay(md5(random()::text || ':' || clock_timestamp()::text) placing '4' from 13) placing to_hex(floor(random()*(11-8+1) + 8)::int)::text from 17)::cstring) + from concept + where uuid = '1822AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + ]]> + </sql> + + <sql><![CDATA[ + insert into fhir_duration_unit_map (concept_id, unit_of_time, creator, date_created, uuid) + select concept_id, 'D', 1, now(), uuid_in(overlay(overlay(md5(random()::text || ':' || clock_timestamp()::text) placing '4' from 13) placing to_hex(floor(random()*(11-8+1) + 8)::int)::text from 17)::cstring) + from concept + where uuid = '1072AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + ]]> + </sql> + + <sql><![CDATA[ + insert into fhir_duration_unit_map (concept_id, unit_of_time, creator, date_created, uuid) + select concept_id, 'WK', 1, now(), uuid_in(overlay(overlay(md5(random()::text || ':' || clock_timestamp()::text) placing '4' from 13) placing to_hex(floor(random()*(11-8+1) + 8)::int)::text from 17)::cstring) + from concept + where uuid = '1073AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + ]]> + </sql> + + <sql><![CDATA[ + insert into fhir_duration_unit_map (concept_id, unit_of_time, creator, date_created, uuid) + select concept_id, 'MO', 1, now(), uuid_in(overlay(overlay(md5(random()::text || ':' || clock_timestamp()::text) placing '4' from 13) placing to_hex(floor(random()*(11-8+1) + 8)::int)::text from 17)::cstring) + from concept + where uuid = '1074AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + ]]> + </sql> + + <sql><![CDATA[ + insert into fhir_duration_unit_map (concept_id, unit_of_time, creator, date_created, uuid) + select concept_id, 'A', 1, now(), uuid_in(overlay(overlay(md5(random()::text || ':' || clock_timestamp()::text) placing '4' from 13) placing to_hex(floor(random()*(11-8+1) + 8)::int)::text from 17)::cstring) + from concept + where uuid = '1734AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' + ]]> + </sql> + </changeSet> </databaseChangeLog> diff --git a/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/DurationUnitTranslatorImplTest.java b/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/DurationUnitTranslatorImplTest.java new file mode 100644 index 000000000..5dc621f3b --- /dev/null +++ b/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/DurationUnitTranslatorImplTest.java @@ -0,0 +1,153 @@ +/* + * 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.api.translators.impl; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.notNullValue; + +import org.hibernate.SessionFactory; +import org.hl7.fhir.r4.model.Timing; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.openmrs.Concept; +import org.openmrs.module.fhir2.TestFhirSpringConfiguration; +import org.openmrs.module.fhir2.api.mappings.DurationUnitMap; +import org.openmrs.test.BaseModuleContextSensitiveTest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; + +@ContextConfiguration(classes = TestFhirSpringConfiguration.class, inheritLocations = false) +public class DurationUnitTranslatorImplTest extends BaseModuleContextSensitiveTest { + + private static final String DURATION_UNIT_CONCEPT_DATA = "org/openmrs/module/fhir2/mapping/FhirDurationUnitTranslatorTest_initial_data.xml"; + + private static final String SECONDS_UUID = "162583AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + + private static final String MINUTES_UUID = "1733AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + + private static final String HOUR_UUID = "1822AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + + private static final String DAYS_UUID = "1072AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + + private static final String WEEKS_UUID = "1073AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + + private static final String MONTHS_UUID = "1074AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + + private static final String YEARS_UUID = "1734AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + + private static final String WRONG_UUID = "2909AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + + @Mock + private DurationUnitMap durationUnitMap; + + @Autowired + private SessionFactory sessionFactory; + + private Concept concept; + + private Timing.UnitsOfTime result; + + private DurationUnitTranslatorImpl durationUnitTranslator; + + @Before + public void setup() throws Exception { + durationUnitTranslator = new DurationUnitTranslatorImpl(); + durationUnitMap = new DurationUnitMap(); + concept = new Concept(); + durationUnitTranslator.setDurationUnitMap(durationUnitMap); + durationUnitMap.setSessionFactory(sessionFactory); + + executeDataSet(DURATION_UNIT_CONCEPT_DATA); + } + + @Test + public void toFhirResource_shouldTranslateDrugOrderToUnitTimeIsNull() { + concept.setUuid(WRONG_UUID); + + result = durationUnitTranslator.toFhirResource(concept); + + assertThat(result, notNullValue()); + assertThat(result, equalTo(Timing.UnitsOfTime.NULL)); + + } + + @Test + public void toFhirResource_shouldTranslateDrugOrderToUnitsOfTimeIsSeconds() { + concept.setUuid(SECONDS_UUID); + + result = durationUnitTranslator.toFhirResource(concept); + + assertThat(result, notNullValue()); + assertThat(result, equalTo(Timing.UnitsOfTime.S)); + } + + @Test + public void toFhirResource_shouldTranslateDrugOrderToUnitsOfTimeIsMinutes() { + concept.setUuid(MINUTES_UUID); + + result = durationUnitTranslator.toFhirResource(concept); + + assertThat(result, notNullValue()); + assertThat(result, equalTo(Timing.UnitsOfTime.MIN)); + + } + + @Test + public void toFhirResource_shouldTranslateDrugOrderToUnitsOfTimeIsHours() { + concept.setUuid(HOUR_UUID); + + result = durationUnitTranslator.toFhirResource(concept); + + assertThat(result, notNullValue()); + assertThat(result, equalTo(Timing.UnitsOfTime.H)); + } + + @Test + public void toFhirResource_shouldTranslateDrugOrderToUnitsOfTimeIsDays() { + concept.setUuid(DAYS_UUID); + + result = durationUnitTranslator.toFhirResource(concept); + + assertThat(result, notNullValue()); + assertThat(result, equalTo(Timing.UnitsOfTime.D)); + } + + @Test + public void toFhirResource_shouldTranslateDrugOrderToUnitsOfTimeIsWeeks() { + concept.setUuid(WEEKS_UUID); + + result = durationUnitTranslator.toFhirResource(concept); + + assertThat(result, notNullValue()); + assertThat(result, equalTo(Timing.UnitsOfTime.WK)); + } + + @Test + public void toFhirResource_shouldTranslateDrugOrderToUnitsOfTimeIsMonths() { + concept.setUuid(MONTHS_UUID); + + result = durationUnitTranslator.toFhirResource(concept); + + assertThat(result, notNullValue()); + assertThat(result, equalTo(Timing.UnitsOfTime.MO)); + } + + @Test + public void toFhirResource_shouldTranslateDrugOrderToUnitsOfTimeIsYears() { + concept.setUuid(YEARS_UUID); + + result = durationUnitTranslator.toFhirResource(concept); + + assertThat(result, notNullValue()); + assertThat(result, equalTo(Timing.UnitsOfTime.A)); + } +} diff --git a/test-data/src/test/resources/org/openmrs/module/fhir2/mapping/FhirDurationUnitTranslatorTest_initial_data.xml b/test-data/src/test/resources/org/openmrs/module/fhir2/mapping/FhirDurationUnitTranslatorTest_initial_data.xml new file mode 100644 index 000000000..a5d7d4e34 --- /dev/null +++ b/test-data/src/test/resources/org/openmrs/module/fhir2/mapping/FhirDurationUnitTranslatorTest_initial_data.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8" ?> + +<dataset> + <concept concept_id = "162583" retired= "0" datatype_id= "4" class_id= "11" is_set= "0" creator= "1" date_created= "2014-09-18 15:14:53" changed_by= "1" date_changed= "2016-01-27 01:41:48" retired_by= "1" uuid= "162583AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" /> + <concept concept_id = "1733" retired= "0" datatype_id= "4" class_id= "11" is_set= "0" creator= "1" date_created= "2009-06-24 16:57:06" changed_by= "1" date_changed= "2014-12-01 21:22:02" retired_by= "1" uuid= "1733AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" /> + <concept concept_id = "1822" retired= "0" datatype_id= "4" class_id= "11" is_set= "0" creator= "1" date_created= "2009-07-06 17:27:04" changed_by= "1" date_changed= "2014-12-01 21:22:19" retired_by= "1" uuid= "1822AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" /> + <concept concept_id = "1072" retired= "0" datatype_id= "4" class_id= "11" is_set= "0" creator= "1" date_created= "2005-01-06 00:00:00" changed_by= "1" date_changed= "2016-04-07 04:19:51" retired_by= "1" uuid= "1072AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" /> + <concept concept_id = "1073" retired= "0" datatype_id= "4" class_id= "11" is_set= "0" creator= "1" date_created= "2005-01-06 00:00:00" changed_by= "1" date_changed= "2016-04-07 04:19:51" retired_by= "1" uuid= "1073AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" /> + <concept concept_id = "1074" retired= "0" datatype_id= "4" class_id= "11" is_set= "0" creator= "1" date_created= "2005-01-06 00:00:00" changed_by= "1" date_changed= "2016-04-07 04:18:42" retired_by= "1" uuid= "1074AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" /> + <concept concept_id = "1734" retired= "0" datatype_id= "4" class_id= "11" is_set= "0" creator= "1" date_created= "2009-06-24 16:57:26" changed_by= "1" date_changed= "2016-07-21 02:13:50" retired_by= "1" uuid= "1734AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" /> + + <fhir_duration_unit_map duration_unit_map_id= "1" concept_id= "162583" unit_of_time= "S" creator= "1" date_created= "2020-12-18 00:00:00" retired= "0" uuid= "dd12f704-411a-11eb-81d3-84fdd1f54722" /> + <fhir_duration_unit_map duration_unit_map_id= "2" concept_id= "1733" unit_of_time= "MIN" creator= "1" date_created= "2020-12-18 00:00:00" retired= "0" uuid= "dd130d5e-411a-11eb-81d3-84fdd1f54722" /> + <fhir_duration_unit_map duration_unit_map_id= "3" concept_id= "1822" unit_of_time= "H" creator= "1" date_created= "2020-12-18 00:00:00" retired= "0" uuid= "dd131c04-411a-11eb-81d3-84fdd1f54722" /> + <fhir_duration_unit_map duration_unit_map_id= "4" concept_id= "1072" unit_of_time= "D" creator= "1" date_created= "2020-12-18 00:00:00" retired= "0" uuid= "dd137515-411a-11eb-81d3-84fdd1f54722" /> + <fhir_duration_unit_map duration_unit_map_id= "5" concept_id= "1073" unit_of_time= "WK" creator= "1" date_created= "2020-12-18 00:00:00" retired= "0" uuid= "dd137eb6-411a-11eb-81d3-84fdd1f54722" /> + <fhir_duration_unit_map duration_unit_map_id= "6" concept_id= "1074" unit_of_time= "MO" creator= "1" date_created= "2020-12-18 00:00:00" retired= "0" uuid= "dd138741-411a-11eb-81d3-84fdd1f54722" /> + <fhir_duration_unit_map duration_unit_map_id= "7" concept_id= "1734" unit_of_time= "A" creator= "1" date_created= "2020-12-18 00:00:00" retired= "0" uuid= "dd138fa6-411a-11eb-81d3-84fdd1f54722" /> +</dataset> From 4a4d7a058873d14b1f51974d3847051fd73fec37 Mon Sep 17 00:00:00 2001 From: Ankit kumar <49350053+theanandankit@users.noreply.github.com> Date: Mon, 1 Feb 2021 20:20:14 +0530 Subject: [PATCH 09/18] FM2-326: Add Tests for ObservationCategoryTranslatorImpl (#313) --- .../api/mappings/ObservationCategoryMap.java | 3 + .../ObservationCategoryTranslatorImpl.java | 3 + ...ObservationCategoryTranslatorImplTest.java | 110 ++++++++++++++++++ ...irObservationCategoryTest_initial_data.xml | 10 ++ 4 files changed, 126 insertions(+) create mode 100644 api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/ObservationCategoryTranslatorImplTest.java create mode 100644 test-data/src/test/resources/org/openmrs/module/fhir2/mapping/FhirObservationCategoryTest_initial_data.xml diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/mappings/ObservationCategoryMap.java b/api/src/main/java/org/openmrs/module/fhir2/api/mappings/ObservationCategoryMap.java index f30a48089..3ed01f176 100644 --- a/api/src/main/java/org/openmrs/module/fhir2/api/mappings/ObservationCategoryMap.java +++ b/api/src/main/java/org/openmrs/module/fhir2/api/mappings/ObservationCategoryMap.java @@ -13,6 +13,8 @@ import javax.annotation.Nonnull; +import lombok.AccessLevel; +import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.hibernate.HibernateException; import org.hibernate.SessionFactory; @@ -24,6 +26,7 @@ @Component @Slf4j +@Setter(AccessLevel.PUBLIC) public class ObservationCategoryMap { @Autowired diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/ObservationCategoryTranslatorImpl.java b/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/ObservationCategoryTranslatorImpl.java index 4f6462233..0cc55c823 100644 --- a/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/ObservationCategoryTranslatorImpl.java +++ b/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/ObservationCategoryTranslatorImpl.java @@ -11,6 +11,8 @@ import javax.annotation.Nonnull; +import lombok.AccessLevel; +import lombok.Setter; import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.r4.model.CodeableConcept; import org.openmrs.Concept; @@ -21,6 +23,7 @@ import org.springframework.stereotype.Component; @Component +@Setter(AccessLevel.PACKAGE) public class ObservationCategoryTranslatorImpl implements ObservationCategoryTranslator { @Autowired diff --git a/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/ObservationCategoryTranslatorImplTest.java b/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/ObservationCategoryTranslatorImplTest.java new file mode 100644 index 000000000..60d7f5d82 --- /dev/null +++ b/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/ObservationCategoryTranslatorImplTest.java @@ -0,0 +1,110 @@ +/* + * 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.api.translators.impl; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.notNullValue; + +import org.hibernate.SessionFactory; +import org.hl7.fhir.r4.model.CodeableConcept; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.openmrs.Concept; +import org.openmrs.ConceptClass; +import org.openmrs.module.fhir2.TestFhirSpringConfiguration; +import org.openmrs.module.fhir2.api.mappings.ObservationCategoryMap; +import org.openmrs.test.BaseModuleContextSensitiveTest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; + +@ContextConfiguration(classes = TestFhirSpringConfiguration.class, inheritLocations = false) +public class ObservationCategoryTranslatorImplTest extends BaseModuleContextSensitiveTest { + + private final String OBSERVATION_CATEGORY_CONCEPT_CLASS_DATA = "org/openmrs/module/fhir2/mapping/FhirObservationCategoryTest_initial_data.xml"; + + private final String LABORATORY_CONCEPT_CLASS_UUID = "8d4907b2-c2cc-11de-8d13-0010c6dffd0f"; + + private final String PROCEDURE_CONCEPT_CLASS_UUID = "8d490bf4-c2cc-11de-8d13-0010c6dffd0f"; + + private final String EXAM_CONCEPT_CLASS_UUID = "8d491a9a-c2cc-11de-8d13-0010c6dffd0f"; + + @Mock + private ObservationCategoryMap categoryMap; + + @Autowired + private SessionFactory sessionFactory; + + private Concept concept; + + private CodeableConcept codeableConcept; + + private ObservationCategoryTranslatorImpl observationCategoryTranslator; + + @Before + public void setup() throws Exception { + observationCategoryTranslator = new ObservationCategoryTranslatorImpl(); + categoryMap = new ObservationCategoryMap(); + concept = new Concept(); + categoryMap.setSessionFactory(sessionFactory); + observationCategoryTranslator.setCategoryMap(categoryMap); + + executeDataSet(OBSERVATION_CATEGORY_CONCEPT_CLASS_DATA); + } + + @Test + public void shouldTranslateConceptClassToCodeableConceptIsnull() { + ConceptClass conceptClass = new ConceptClass(); + // wrong uuid which is not in dataSet + conceptClass.setUuid("0"); + concept.setConceptClass(conceptClass); + + codeableConcept = observationCategoryTranslator.toFhirResource(concept); + + assertThat(codeableConcept, equalTo(null)); + } + + @Test + public void shouldTranslateConceptClassToCodeableConceptIsLaboratory() { + ConceptClass conceptClass = new ConceptClass(); + conceptClass.setUuid(LABORATORY_CONCEPT_CLASS_UUID); + concept.setConceptClass(conceptClass); + + codeableConcept = observationCategoryTranslator.toFhirResource(concept); + + assertThat(codeableConcept, notNullValue()); + assertThat(codeableConcept.getCoding().get(0).getDisplay(), equalTo("Laboratory")); + } + + @Test + public void shouldTranslateConceptClassToCodeableConceptIsProcedure() { + ConceptClass conceptClass = new ConceptClass(); + conceptClass.setUuid(PROCEDURE_CONCEPT_CLASS_UUID); + concept.setConceptClass(conceptClass); + + codeableConcept = observationCategoryTranslator.toFhirResource(concept); + + assertThat(codeableConcept, notNullValue()); + assertThat(codeableConcept.getCoding().get(0).getDisplay(), equalTo("Procedure")); + } + + @Test + public void shouldTranslateConceptClassToCodeableConceptIsExam() { + ConceptClass conceptClass = new ConceptClass(); + conceptClass.setUuid(EXAM_CONCEPT_CLASS_UUID); + concept.setConceptClass(conceptClass); + + codeableConcept = observationCategoryTranslator.toFhirResource(concept); + + assertThat(codeableConcept, notNullValue()); + assertThat(codeableConcept.getCoding().get(0).getDisplay(), equalTo("Exam")); + } +} diff --git a/test-data/src/test/resources/org/openmrs/module/fhir2/mapping/FhirObservationCategoryTest_initial_data.xml b/test-data/src/test/resources/org/openmrs/module/fhir2/mapping/FhirObservationCategoryTest_initial_data.xml new file mode 100644 index 000000000..d1f7f9c38 --- /dev/null +++ b/test-data/src/test/resources/org/openmrs/module/fhir2/mapping/FhirObservationCategoryTest_initial_data.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8" ?> + +<dataset> + <fhir_observation_category_map observation_category_map_id="1" concept_class_id="1" observation_category="laboratory" creator="1" date_created="2020-12-16 19:20:53" retired="0" uuid="b6b107d8-3fa5-11eb-81d3-84fdd1f54722"/> + <fhir_observation_category_map observation_category_map_id="2" concept_class_id="2" observation_category="procedure" creator="1" date_created="2020-12-16 19:20:53" retired="0" uuid="b6b13299-3fa5-11eb-81d3-84fdd1f54722"/> + <fhir_observation_category_map observation_category_map_id="3" concept_class_id="5" observation_category="exam" creator="1" date_created="2020-12-16 19:20:53" retired="0" uuid="b6b16756-3fa5-11eb-81d3-84fdd1f54722"/> + <concept_class concept_class_id="1" name="Test" description="Acq. during patient encounter (vitals, labs, etc.)" creator="1" date_created="2004-02-02 00:00:00" retired="0" uuid="8d4907b2-c2cc-11de-8d13-0010c6dffd0f"/> + <concept_class concept_class_id="2" name="Procedure" description="Describes a clinical procedure" creator="1" date_created="2004-02-02 00:00:00" retired="0" uuid="8d490bf4-c2cc-11de-8d13-0010c6dffd0f"/> + <concept_class concept_class_id="5" name="Finding" description="Practitioner observation/finding" creator="1" date_created="2004-02-02 00:00:00" retired="0" uuid="8d491a9a-c2cc-11de-8d13-0010c6dffd0f"/> +</dataset> From a39cba64f816fc67e47dc3c617b2109245e3c11b Mon Sep 17 00:00:00 2001 From: Ankit kumar <49350053+theanandankit@users.noreply.github.com> Date: Thu, 4 Feb 2021 11:29:49 +0530 Subject: [PATCH 10/18] FM2-340: Add Support of DurationUnit for the MedicationRequestTimingComponent (#322) * Add Support of DurationUnit for the TimingRepeatComponent * Small change in MedicationRequestTimingComponentTranslatorImpl * Improve integration test of MedicationRequestFhirResourceProvider * Small change in Test Data --- .../impl/DurationUnitTranslatorImpl.java | 2 + ...nRequestTimingComponentTranslatorImpl.java | 21 ++- ...uestTimingComponentTranslatorImplTest.java | 122 ++++++++++++++++++ ...rMedicationRequestDaoImpl_initial_data.xml | 23 +++- 4 files changed, 156 insertions(+), 12 deletions(-) diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/DurationUnitTranslatorImpl.java b/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/DurationUnitTranslatorImpl.java index 530cd7d81..39ac9e5cb 100644 --- a/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/DurationUnitTranslatorImpl.java +++ b/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/DurationUnitTranslatorImpl.java @@ -19,7 +19,9 @@ import org.openmrs.module.fhir2.api.mappings.DurationUnitMap; import org.openmrs.module.fhir2.api.translators.DurationUnitTranslator; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +@Component @Setter(AccessLevel.PACKAGE) public class DurationUnitTranslatorImpl implements DurationUnitTranslator { diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/MedicationRequestTimingComponentTranslatorImpl.java b/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/MedicationRequestTimingComponentTranslatorImpl.java index 1049d25ca..64ac0fe1e 100644 --- a/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/MedicationRequestTimingComponentTranslatorImpl.java +++ b/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/MedicationRequestTimingComponentTranslatorImpl.java @@ -11,29 +11,36 @@ import javax.annotation.Nonnull; +import lombok.AccessLevel; +import lombok.Setter; import org.hl7.fhir.r4.model.Timing; import org.openmrs.DrugOrder; import org.openmrs.OrderFrequency; +import org.openmrs.module.fhir2.api.translators.DurationUnitTranslator; import org.openmrs.module.fhir2.api.translators.MedicationRequestTimingComponentTranslator; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component +@Setter(AccessLevel.PACKAGE) public class MedicationRequestTimingComponentTranslatorImpl implements MedicationRequestTimingComponentTranslator { + @Autowired + private DurationUnitTranslator durationUnitTranslator; + @Override public Timing.TimingRepeatComponent toFhirResource(@Nonnull DrugOrder drugOrder) { if (drugOrder == null) { return null; } Timing.TimingRepeatComponent repeatComponent = new Timing.TimingRepeatComponent(); - if (drugOrder.getDuration() != null) + if (drugOrder.getDuration() != null) { repeatComponent.setDuration(drugOrder.getDuration()); - /* - * TODO - * Figure out how to map DurationUnit to UnitsOfTime since openMrs duration units is concept - * which differs across implementation. Make use of concept mappings - */ - //repeatComponent.setDurationUnit(drugOrder.getDurationUnits()); + } + + if (drugOrder.getDurationUnits() != null) { + repeatComponent.setDurationUnit(durationUnitTranslator.toFhirResource(drugOrder.getDurationUnits())); + } OrderFrequency frequency = drugOrder.getFrequency(); if (frequency != null) { if (frequency.getFrequencyPerDay() != null) { diff --git a/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/MedicationRequestTimingComponentTranslatorImplTest.java b/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/MedicationRequestTimingComponentTranslatorImplTest.java index 5a9d28855..c4937ae2e 100644 --- a/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/MedicationRequestTimingComponentTranslatorImplTest.java +++ b/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/MedicationRequestTimingComponentTranslatorImplTest.java @@ -13,6 +13,7 @@ import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; +import static org.mockito.Mockito.when; import java.math.BigDecimal; @@ -20,13 +21,35 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; +import org.openmrs.Concept; import org.openmrs.DrugOrder; import org.openmrs.OrderFrequency; +import org.openmrs.module.fhir2.api.translators.DurationUnitTranslator; @RunWith(MockitoJUnitRunner.class) public class MedicationRequestTimingComponentTranslatorImplTest { + private static final String SECONDS_UUID = "162583AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + + private static final String MINUTES_UUID = "1733AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + + private static final String HOUR_UUID = "1822AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + + private static final String DAYS_UUID = "1072AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + + private static final String WEEKS_UUID = "1073AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + + private static final String MONTHS_UUID = "1074AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + + private static final String YEARS_UUID = "1734AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + + private static final String WRONG_UUID = "2909AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + + @Mock + private DurationUnitTranslator durationUnitTranslator; + private static final int DURATION = 2; private static final double FREQUENCY_PER_DAY = 3.0; @@ -35,10 +58,14 @@ public class MedicationRequestTimingComponentTranslatorImplTest { private DrugOrder drugOrder; + private Concept concept; + @Before public void setup() { requestTimingComponentTranslator = new MedicationRequestTimingComponentTranslatorImpl(); + requestTimingComponentTranslator.setDurationUnitTranslator(durationUnitTranslator); + concept = new Concept(); drugOrder = new DrugOrder(); drugOrder.setDuration(DURATION); @@ -103,4 +130,99 @@ public void toFhirResource_shouldReturnNullIfDrugOrderIsNull() { assertThat(requestTimingComponentTranslator.toFhirResource(null), nullValue()); } + @Test + public void toFhirResource_shouldTranslateConceptToUnitOfTimeSeconds() { + concept.setUuid(SECONDS_UUID); + drugOrder.setDurationUnits(concept); + + when(durationUnitTranslator.toFhirResource(concept)).thenReturn(Timing.UnitsOfTime.S); + Timing.TimingRepeatComponent result = requestTimingComponentTranslator.toFhirResource(drugOrder); + + assertThat(result, notNullValue()); + assertThat(result.getDurationUnit(), equalTo(Timing.UnitsOfTime.S)); + } + + @Test + public void toFhirResource_shouldTranslateConceptToUnitOfTimeMinutes() { + concept.setUuid(MINUTES_UUID); + drugOrder.setDurationUnits(concept); + + when(durationUnitTranslator.toFhirResource(concept)).thenReturn(Timing.UnitsOfTime.MIN); + Timing.TimingRepeatComponent result = requestTimingComponentTranslator.toFhirResource(drugOrder); + + assertThat(result, notNullValue()); + assertThat(result.getDurationUnit(), equalTo(Timing.UnitsOfTime.MIN)); + } + + @Test + public void toFhirResource_shouldTranslateConceptToUnitOfTimeHours() { + concept.setUuid(HOUR_UUID); + drugOrder.setDurationUnits(concept); + + when(durationUnitTranslator.toFhirResource(concept)).thenReturn(Timing.UnitsOfTime.H); + Timing.TimingRepeatComponent result = requestTimingComponentTranslator.toFhirResource(drugOrder); + + assertThat(result, notNullValue()); + assertThat(result.getDurationUnit(), equalTo(Timing.UnitsOfTime.H)); + } + + @Test + public void toFhirResource_shouldTranslateConceptToUnitOfTimeDays() { + concept.setUuid(DAYS_UUID); + drugOrder.setDurationUnits(concept); + + when(durationUnitTranslator.toFhirResource(concept)).thenReturn(Timing.UnitsOfTime.D); + Timing.TimingRepeatComponent result = requestTimingComponentTranslator.toFhirResource(drugOrder); + + assertThat(result, notNullValue()); + assertThat(result.getDurationUnit(), equalTo(Timing.UnitsOfTime.D)); + } + + @Test + public void toFhirResource_shouldTranslateConceptToUnitOfTimeWeeks() { + concept.setUuid(WEEKS_UUID); + drugOrder.setDurationUnits(concept); + + when(durationUnitTranslator.toFhirResource(concept)).thenReturn(Timing.UnitsOfTime.WK); + Timing.TimingRepeatComponent result = requestTimingComponentTranslator.toFhirResource(drugOrder); + + assertThat(result, notNullValue()); + assertThat(result.getDurationUnit(), equalTo(Timing.UnitsOfTime.WK)); + } + + @Test + public void toFhirResource_shouldTranslateConceptToUnitOfTimeMonths() { + concept.setUuid(MONTHS_UUID); + drugOrder.setDurationUnits(concept); + + when(durationUnitTranslator.toFhirResource(concept)).thenReturn(Timing.UnitsOfTime.MO); + Timing.TimingRepeatComponent result = requestTimingComponentTranslator.toFhirResource(drugOrder); + + assertThat(result, notNullValue()); + assertThat(result.getDurationUnit(), equalTo(Timing.UnitsOfTime.MO)); + } + + @Test + public void toFhirResource_shouldTranslateConceptToUnitOfTimeYears() { + concept.setUuid(YEARS_UUID); + drugOrder.setDurationUnits(concept); + + when(durationUnitTranslator.toFhirResource(concept)).thenReturn(Timing.UnitsOfTime.A); + Timing.TimingRepeatComponent result = requestTimingComponentTranslator.toFhirResource(drugOrder); + + assertThat(result, notNullValue()); + assertThat(result.getDurationUnit(), equalTo(Timing.UnitsOfTime.A)); + } + + @Test + public void toFhirResource_shouldTranslateConceptToUnitOfTimeSecondNull() { + concept.setUuid(WRONG_UUID); + drugOrder.setDurationUnits(concept); + + when(durationUnitTranslator.toFhirResource(concept)).thenReturn(Timing.UnitsOfTime.NULL); + Timing.TimingRepeatComponent result = requestTimingComponentTranslator.toFhirResource(drugOrder); + + assertThat(result, notNullValue()); + assertThat(result.getDurationUnit(), equalTo(Timing.UnitsOfTime.NULL)); + } } diff --git a/test-data/src/test/resources/org/openmrs/module/fhir2/api/dao/impl/FhirMedicationRequestDaoImpl_initial_data.xml b/test-data/src/test/resources/org/openmrs/module/fhir2/api/dao/impl/FhirMedicationRequestDaoImpl_initial_data.xml index bd28672de..52146e9c4 100644 --- a/test-data/src/test/resources/org/openmrs/module/fhir2/api/dao/impl/FhirMedicationRequestDaoImpl_initial_data.xml +++ b/test-data/src/test/resources/org/openmrs/module/fhir2/api/dao/impl/FhirMedicationRequestDaoImpl_initial_data.xml @@ -14,7 +14,7 @@ <fhir_concept_source fhir_concept_source_id="1" name="SNOMED CT" url="http://snomed.info/sct" concept_source_id="2" creator="1" date_created="2005-01-01 00:00:00.0" retired="0" uuid="0d30bea3-4ba2-4ab4-ac7b-5525840bde20" /> <fhir_concept_source fhir_concept_source_id="2" name="CIEL" url="https://openconceptlab.org/orgs/CIEL/sources/CIEL" concept_source_id="21" creator="1" date_created="2005-01-01 00:00:00.0" retired="0" uuid="b824bbee-d5aa-4ede-b538-f8b0107a74e4" /> <fhir_concept_source fhir_concept_source_id="3" name="LOINC" url="http://loinc.org" concept_source_id="6" creator="1" date_created="2005-01-01 00:00:00.0" retired="0" uuid="30a5aa84-2df5-46da-aed7-451bafe5593b" /> - + <!--Patient Information --> <person person_id="102" gender="M" dead="false" creator="1" birthdate_estimated="0" date_created="2008-08-15 15:57:09.0" voided="false" uuid="86526ed5-3c11-11de-a0ba-001e3766667a"/> @@ -38,6 +38,12 @@ <concept concept_id="4022" retired="false" datatype_id="4" class_id="11" is_set="false" creator="501" date_created="2004-08-12 00:00:00.0" version="" changed_by="1" date_changed="2005-02-25 11:43:43.0" uuid="c902c80f-1yz9-4da3-bb88-8122ce889111"/> + <concept concept_id="162583" retired="0" datatype_id="4" class_id="11" is_set="0" creator="1" + date_created="2014-09-18 15:14:53" changed_by="1" date_changed="2016-01-27 01:41:48" retired_by="1" + uuid="162583AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"/> + <concept concept_id="1733" retired="0" datatype_id="4" class_id="11" is_set="0" creator="1" + date_created="2009-06-24 16:57:06" changed_by="1" date_changed="2014-12-01 21:22:02" retired_by="1" + uuid="1733AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"/> <concept_name concept_id="4020" concept_name_id="1420" name="Bedaquiline" locale="en" creator="1" date_created="2004-08-12 00:00:00.0" concept_name_type="FULLY_SPECIFIED" locale_preferred="1" @@ -80,15 +86,22 @@ <drug drug_id="2003" concept_id="4022" dosage_form="51" name="Rifampicin" combination="true" creator="1" date_created="2005-02-24 00:00:00.0" retired="false" uuid="yuf00b94-26fe-102b-80bc-0017a4787b12"/> - <drug_order order_id="1005" drug_inventory_id="2001" duration="6" duration_units="27" dispense_as_written="true" + <drug_order order_id="1005" drug_inventory_id="2001" duration="6" duration_units="162583" dispense_as_written="true" as_needed="false" frequency="2"/> - <drug_order order_id="1006" drug_inventory_id="2002" duration="6" duration_units="27" dispense_as_written="true" + <drug_order order_id="1006" drug_inventory_id="2002" duration="6" duration_units="162583" dispense_as_written="true" as_needed="false" frequency="1"/> - <drug_order order_id="1007" drug_inventory_id="2003" duration="6" duration_units="27" dispense_as_written="true" + <drug_order order_id="1007" drug_inventory_id="2003" duration="6" duration_units="162583" dispense_as_written="true" as_needed="false" frequency="2"/> - <drug_order order_id="8" drug_inventory_id="2003" duration="8" duration_units="29" dispense_as_written="true" + <drug_order order_id="8" drug_inventory_id="2003" duration="8" duration_units="1733" dispense_as_written="true" as_needed="false" frequency="3"/> + + <fhir_duration_unit_map duration_unit_map_id="1" concept_id="162583" unit_of_time="S" creator="1" + date_created="2020-12-18 00:00:00" retired="0" uuid="dd12f704-411a-11eb-81d3-84fdd1f54722"/> + + <fhir_duration_unit_map duration_unit_map_id="2" concept_id="1733" unit_of_time="MIN" creator="1" + date_created="2020-12-18 00:00:00" retired="0" uuid="dd130d5e-411a-11eb-81d3-84fdd1f54722"/> + </dataset> From 3a9194218d120f6c44d8709186e05109eb948e5b Mon Sep 17 00:00:00 2001 From: Ian <ian_bacher@brown.edu> Date: Fri, 5 Feb 2021 10:36:13 -0500 Subject: [PATCH 11/18] Mark FHIR2 aware of some basic configuration modules --- omod/src/main/resources/config.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/omod/src/main/resources/config.xml b/omod/src/main/resources/config.xml index 077dacbc1..5136336fd 100644 --- a/omod/src/main/resources/config.xml +++ b/omod/src/main/resources/config.xml @@ -47,6 +47,8 @@ <aware_of_modules> <aware_of_module>org.openmrs.module.legacyui</aware_of_module> + <aware_of_module>org.openmrs.module.referencemetadata</aware_of_module> + <aware_of_module>org.openmrs.module.initializer</aware_of_module> </aware_of_modules> <servlet> From f49d99f5b051719fbfc5f372829798ca9089637b Mon Sep 17 00:00:00 2001 From: "Kipchumba C. Bett" <kbett68@gmail.com> Date: Fri, 5 Feb 2021 20:33:09 +0300 Subject: [PATCH 12/18] FM2-337: Implement Group resource (#319) --- .../impl/GroupMemberTranslatorImpl_2_1.java | 91 ++++ .../impl/GroupTranslatorImpl_2_1.java | 83 ++++ .../GroupMemberTranslatorImpl_2_1Test.java | 243 +++++++++++ .../impl/GroupTranslatorImpl_2_1Test.java | 199 +++++++++ .../openmrs/module/fhir2/FhirConstants.java | 2 + .../module/fhir2/api/FhirGroupService.java | 14 + .../module/fhir2/api/FhirPatientService.java | 6 + .../module/fhir2/api/dao/FhirGroupDao.java | 39 ++ .../module/fhir2/api/dao/FhirPatientDao.java | 4 + .../fhir2/api/dao/impl/BaseFhirDao.java | 2 +- .../fhir2/api/dao/impl/FhirGroupDaoImpl.java | 22 + .../api/dao/impl/FhirPatientDaoImpl.java | 7 + .../fhir2/api/impl/FhirGroupServiceImpl.java | 35 ++ .../api/impl/FhirPatientServiceImpl.java | 16 + .../translators/GroupMemberTranslator.java | 45 ++ .../api/translators/GroupTranslator.java | 46 ++ .../translators/impl/BaseGroupTranslator.java | 70 +++ .../impl/GroupMemberTranslatorImpl.java | 60 +++ .../translators/impl/GroupTranslatorImpl.java | 70 +++ .../r3/GroupFhirResourceProvider.java | 87 ++++ .../r4/GroupFhirResourceProvider.java | 85 ++++ .../api/dao/impl/FhirGroupDaoImplTest.java | 106 +++++ .../api/impl/FhirGroupServiceImplTest.java | 158 +++++++ .../api/impl/FhirPatientServiceImplTest.java | 27 ++ .../impl/GroupMemberTranslatorImplTest.java | 94 ++++ .../impl/GroupTranslatorImplTest.java | 200 +++++++++ .../r3/GroupFhirResourceProviderTest.java | 166 +++++++ .../r4/GroupFhirResourceProviderTest.java | 163 +++++++ ...upFhirResourceProviderIntegrationTest.java | 390 +++++++++++++++++ ...upFhirResourceProviderIntegrationTest.java | 404 ++++++++++++++++++ .../FhirCohortDaoImplTest_initial_data.xml | 14 + .../fhir2/providers/GroupWebTest_create.json | 27 ++ .../fhir2/providers/GroupWebTest_create.xml | 29 ++ .../fhir2/providers/GroupWebTest_update.json | 28 ++ .../fhir2/providers/GroupWebTest_update.xml | 30 ++ 35 files changed, 3061 insertions(+), 1 deletion(-) create mode 100644 api-2.1/src/main/java/org/openmrs/module/fhir2/api/translators/impl/GroupMemberTranslatorImpl_2_1.java create mode 100644 api-2.1/src/main/java/org/openmrs/module/fhir2/api/translators/impl/GroupTranslatorImpl_2_1.java create mode 100644 api-2.1/src/test/java/org/openmrs/module/fhir2/api/translators/impl/GroupMemberTranslatorImpl_2_1Test.java create mode 100644 api-2.1/src/test/java/org/openmrs/module/fhir2/api/translators/impl/GroupTranslatorImpl_2_1Test.java create mode 100644 api/src/main/java/org/openmrs/module/fhir2/api/FhirGroupService.java create mode 100644 api/src/main/java/org/openmrs/module/fhir2/api/dao/FhirGroupDao.java create mode 100644 api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirGroupDaoImpl.java create mode 100644 api/src/main/java/org/openmrs/module/fhir2/api/impl/FhirGroupServiceImpl.java create mode 100644 api/src/main/java/org/openmrs/module/fhir2/api/translators/GroupMemberTranslator.java create mode 100644 api/src/main/java/org/openmrs/module/fhir2/api/translators/GroupTranslator.java create mode 100644 api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/BaseGroupTranslator.java create mode 100644 api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/GroupMemberTranslatorImpl.java create mode 100644 api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/GroupTranslatorImpl.java create mode 100644 api/src/main/java/org/openmrs/module/fhir2/providers/r3/GroupFhirResourceProvider.java create mode 100644 api/src/main/java/org/openmrs/module/fhir2/providers/r4/GroupFhirResourceProvider.java create mode 100644 api/src/test/java/org/openmrs/module/fhir2/api/dao/impl/FhirGroupDaoImplTest.java create mode 100644 api/src/test/java/org/openmrs/module/fhir2/api/impl/FhirGroupServiceImplTest.java create mode 100644 api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/GroupMemberTranslatorImplTest.java create mode 100644 api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/GroupTranslatorImplTest.java create mode 100644 api/src/test/java/org/openmrs/module/fhir2/providers/r3/GroupFhirResourceProviderTest.java create mode 100644 api/src/test/java/org/openmrs/module/fhir2/providers/r4/GroupFhirResourceProviderTest.java create mode 100644 integration-tests/src/test/java/org/openmrs/module/fhir2/providers/r3/GroupFhirResourceProviderIntegrationTest.java create mode 100644 integration-tests/src/test/java/org/openmrs/module/fhir2/providers/r4/GroupFhirResourceProviderIntegrationTest.java create mode 100644 test-data/src/test/resources/org/openmrs/module/fhir2/api/dao/impl/FhirCohortDaoImplTest_initial_data.xml create mode 100644 test-data/src/test/resources/org/openmrs/module/fhir2/providers/GroupWebTest_create.json create mode 100644 test-data/src/test/resources/org/openmrs/module/fhir2/providers/GroupWebTest_create.xml create mode 100644 test-data/src/test/resources/org/openmrs/module/fhir2/providers/GroupWebTest_update.json create mode 100644 test-data/src/test/resources/org/openmrs/module/fhir2/providers/GroupWebTest_update.xml diff --git a/api-2.1/src/main/java/org/openmrs/module/fhir2/api/translators/impl/GroupMemberTranslatorImpl_2_1.java b/api-2.1/src/main/java/org/openmrs/module/fhir2/api/translators/impl/GroupMemberTranslatorImpl_2_1.java new file mode 100644 index 000000000..56c85bc76 --- /dev/null +++ b/api-2.1/src/main/java/org/openmrs/module/fhir2/api/translators/impl/GroupMemberTranslatorImpl_2_1.java @@ -0,0 +1,91 @@ +/* + * 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.api.translators.impl; + +import static org.apache.commons.lang3.Validate.notNull; + +import javax.annotation.Nonnull; + +import lombok.AccessLevel; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.hl7.fhir.r4.model.Group; +import org.hl7.fhir.r4.model.Period; +import org.openmrs.CohortMembership; +import org.openmrs.Patient; +import org.openmrs.annotation.OpenmrsProfile; +import org.openmrs.module.fhir2.api.dao.FhirPatientDao; +import org.openmrs.module.fhir2.api.translators.GroupMemberTranslator; +import org.openmrs.module.fhir2.api.translators.PatientReferenceTranslator; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +@Setter(AccessLevel.MODULE) +@OpenmrsProfile(openmrsPlatformVersion = "2.1.* - 2.*") +public class GroupMemberTranslatorImpl_2_1 implements GroupMemberTranslator<CohortMembership> { + + @Autowired + private PatientReferenceTranslator patientReferenceTranslator; + + @Autowired + private FhirPatientDao patientDao; + + @Override + public Group.GroupMemberComponent toFhirResource(@Nonnull CohortMembership cohortMember) { + notNull(cohortMember, "CohortMember object should not be null"); + Group.GroupMemberComponent groupMemberComponent = new Group.GroupMemberComponent(); + groupMemberComponent.setId(cohortMember.getUuid()); + groupMemberComponent.setInactive(!cohortMember.isActive()); + + Patient patient = patientDao.getPatientById(cohortMember.getPatientId()); + if (patient != null) { + groupMemberComponent.setEntity(patientReferenceTranslator.toFhirResource(patient)); + } + + Period period = new Period(); + period.setStart(cohortMember.getStartDate()); + period.setEnd(cohortMember.getEndDate()); + groupMemberComponent.setPeriod(period); + + return groupMemberComponent; + } + + @Override + public CohortMembership toOpenmrsType(@Nonnull Group.GroupMemberComponent groupMemberComponent) { + notNull(groupMemberComponent, "GroupMemberComponent object should not be null"); + return toOpenmrsType(new CohortMembership(), groupMemberComponent); + } + + @Override + public CohortMembership toOpenmrsType(@Nonnull CohortMembership existingCohort, + @Nonnull Group.GroupMemberComponent groupMemberComponent) { + notNull(groupMemberComponent, "GroupMemberComponent object should not be null"); + notNull(existingCohort, "ExistingCohort object should not be null"); + + if (groupMemberComponent.hasEntity()) { + existingCohort + .setPatientId(patientReferenceTranslator.toOpenmrsType(groupMemberComponent.getEntity()).getPatientId()); + } + + if (groupMemberComponent.hasPeriod()) { + existingCohort.setStartDate(groupMemberComponent.getPeriod().getStart()); + existingCohort.setEndDate(groupMemberComponent.getPeriod().getEnd()); + } + + if (groupMemberComponent.hasInactive()) { + existingCohort.setVoided(groupMemberComponent.getInactive()); + existingCohort.setVoidReason("Voided via FHIR API"); + } + + return existingCohort; + } +} diff --git a/api-2.1/src/main/java/org/openmrs/module/fhir2/api/translators/impl/GroupTranslatorImpl_2_1.java b/api-2.1/src/main/java/org/openmrs/module/fhir2/api/translators/impl/GroupTranslatorImpl_2_1.java new file mode 100644 index 000000000..3b935ba7e --- /dev/null +++ b/api-2.1/src/main/java/org/openmrs/module/fhir2/api/translators/impl/GroupTranslatorImpl_2_1.java @@ -0,0 +1,83 @@ +/* + * 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.api.translators.impl; + +import static org.apache.commons.lang3.Validate.notNull; + +import javax.annotation.Nonnull; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import lombok.AccessLevel; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.hl7.fhir.r4.model.Group; +import org.openmrs.Cohort; +import org.openmrs.CohortMembership; +import org.openmrs.annotation.OpenmrsProfile; +import org.openmrs.module.fhir2.api.translators.GroupMemberTranslator; +import org.openmrs.module.fhir2.api.translators.GroupTranslator; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Component; + +@Slf4j +@Primary +@Component +@Setter(AccessLevel.MODULE) +@OpenmrsProfile(openmrsPlatformVersion = "2.1.* - 2.*") +public class GroupTranslatorImpl_2_1 extends BaseGroupTranslator implements GroupTranslator { + + @Autowired + private GroupMemberTranslator<CohortMembership> groupMemberTranslator; + + @Override + public Group toFhirResource(@Nonnull Cohort cohort) { + notNull(cohort, "Cohort object should not be null"); + Group group = super.toFhirResource(cohort); + + Collection<CohortMembership> memberships = cohort.getMemberships(); + log.info("Number of members {} ", memberships.size()); + group.setQuantity(memberships.size()); + memberships.forEach(membership -> group.addMember(groupMemberTranslator.toFhirResource(membership))); + + return group; + } + + @Override + public Cohort toOpenmrsType(@Nonnull Group group) { + notNull(group, "Group resource should not be null"); + return toOpenmrsType(new Cohort(), group); + } + + @Override + public Cohort toOpenmrsType(@Nonnull Cohort existingCohort, @Nonnull Group group) { + notNull(group, "group resource object should not be null"); + notNull(existingCohort, "ExistingCohort object should not be null"); + + Cohort finalExistingCohort = super.toOpenmrsType(existingCohort, group); + + if (group.hasMember()) { + Set<CohortMembership> memberships = new HashSet<>(); + group.getMember().forEach(member -> memberships.add(this.setCohort(existingCohort, member))); + existingCohort.setMemberships(memberships); + } + + return finalExistingCohort; + } + + private CohortMembership setCohort(Cohort cohort, Group.GroupMemberComponent groupMember) { + CohortMembership cohortMembership = groupMemberTranslator.toOpenmrsType(groupMember); + cohortMembership.setCohort(cohort); + return cohortMembership; + } +} diff --git a/api-2.1/src/test/java/org/openmrs/module/fhir2/api/translators/impl/GroupMemberTranslatorImpl_2_1Test.java b/api-2.1/src/test/java/org/openmrs/module/fhir2/api/translators/impl/GroupMemberTranslatorImpl_2_1Test.java new file mode 100644 index 000000000..d601b01c1 --- /dev/null +++ b/api-2.1/src/test/java/org/openmrs/module/fhir2/api/translators/impl/GroupMemberTranslatorImpl_2_1Test.java @@ -0,0 +1,243 @@ +/* + * 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.api.translators.impl; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.time.Instant; +import java.util.Date; + +import org.exparity.hamcrest.date.DateMatchers; +import org.hl7.fhir.r4.model.Group; +import org.hl7.fhir.r4.model.Period; +import org.hl7.fhir.r4.model.Reference; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.openmrs.CohortMembership; +import org.openmrs.Patient; +import org.openmrs.module.fhir2.api.dao.FhirPatientDao; +import org.openmrs.module.fhir2.api.translators.PatientReferenceTranslator; +import org.openmrs.module.fhir2.api.translators.PatientTranslator; + +@RunWith(MockitoJUnitRunner.class) +public class GroupMemberTranslatorImpl_2_1Test { + + private static final String COHORT_UUID = "787e12bd-314e-4cc4-9b4d-1cdff9be9545"; + + private static final String COHORT_NAME = " John's patientList"; + + @Mock + private PatientReferenceTranslator patientReferenceTranslator; + + @Mock + private FhirPatientDao patientDao; + + @Mock + private PatientTranslator patientTranslator; + + private GroupMemberTranslatorImpl_2_1 groupMemberTranslator; + + @Before + public void setup() { + groupMemberTranslator = new GroupMemberTranslatorImpl_2_1(); + groupMemberTranslator.setPatientDao(patientDao); + groupMemberTranslator.setPatientReferenceTranslator(patientReferenceTranslator); + } + + @Test + public void shouldTranslateCohortMemberUuidToFHIRType() { + CohortMembership cohortMembership = mock(CohortMembership.class); + Patient patient = mock(Patient.class); + Reference patientReference = mock(Reference.class); + + when(patientReferenceTranslator.toFhirResource(patient)).thenReturn(patientReference); + when(patientDao.getPatientById(anyInt())).thenReturn(patient); + when(cohortMembership.getUuid()).thenReturn(COHORT_UUID); + + Group.GroupMemberComponent component = groupMemberTranslator.toFhirResource(cohortMembership); + assertThat(component, notNullValue()); + assertThat(component.hasId(), notNullValue()); + assertThat(component.getId(), is(COHORT_UUID)); + } + + @Test + public void shouldTranslateCohortMemberInactiveToFHIRType() { + CohortMembership cohortMembership = mock(CohortMembership.class); + Patient patient = mock(Patient.class); + Reference patientReference = mock(Reference.class); + + when(patientReferenceTranslator.toFhirResource(patient)).thenReturn(patientReference); + when(patientDao.getPatientById(anyInt())).thenReturn(patient); + when(cohortMembership.isActive()).thenReturn(true); + + Group.GroupMemberComponent component = groupMemberTranslator.toFhirResource(cohortMembership); + assertThat(component, notNullValue()); + assertThat(component.hasInactive(), is(true)); + assertThat(component.getInactive(), is(false)); + } + + @Test + public void shouldTranslateCohortMemberStartDateToFHIRType() { + CohortMembership cohortMembership = mock(CohortMembership.class); + Patient patient = mock(Patient.class); + Reference patientReference = mock(Reference.class); + + when(patientReferenceTranslator.toFhirResource(patient)).thenReturn(patientReference); + when(patientDao.getPatientById(anyInt())).thenReturn(patient); + when(cohortMembership.getStartDate()).thenReturn(Date.from(Instant.now())); + + Group.GroupMemberComponent component = groupMemberTranslator.toFhirResource(cohortMembership); + assertThat(component, notNullValue()); + assertThat(component.hasPeriod(), is(true)); + assertThat(component.getPeriod().getStart(), notNullValue()); + assertThat(Date.from(Instant.now()), DateMatchers.sameDay(component.getPeriod().getStart())); + } + + @Test + public void shouldTranslateCohortMemberEndDateToFHIRType() { + CohortMembership cohortMembership = mock(CohortMembership.class); + Patient patient = mock(Patient.class); + Reference patientReference = mock(Reference.class); + + when(patientReferenceTranslator.toFhirResource(patient)).thenReturn(patientReference); + when(patientDao.getPatientById(anyInt())).thenReturn(patient); + when(cohortMembership.getEndDate()).thenReturn(Date.from(Instant.now())); + + Group.GroupMemberComponent component = groupMemberTranslator.toFhirResource(cohortMembership); + assertThat(component, notNullValue()); + assertThat(component.hasPeriod(), is(true)); + assertThat(component.getPeriod().getEnd(), notNullValue()); + assertThat(Date.from(Instant.now()), DateMatchers.sameDay(component.getPeriod().getEnd())); + } + + @Test + public void shouldTranslateCohortMemberToFHIRGroupEntity() { + CohortMembership cohortMembership = mock(CohortMembership.class); + Patient patient = mock(Patient.class); + Reference patientReference = mock(Reference.class); + + when(patientReferenceTranslator.toFhirResource(patient)).thenReturn(patientReference); + when(patientDao.getPatientById(anyInt())).thenReturn(patient); + when(cohortMembership.getPatientId()).thenReturn(1); + + Group.GroupMemberComponent component = groupMemberTranslator.toFhirResource(cohortMembership); + assertThat(component, notNullValue()); + assertThat(component.hasEntity(), is(true)); + assertThat(component.getEntity(), is(patientReference)); + } + + @Test + public void shouldGroupEntityToCohortPatientIdOpenMRSType() { + Reference patientReference = mock(Reference.class); + Patient patient = mock(Patient.class); + Group.GroupMemberComponent component = mock(Group.GroupMemberComponent.class); + + when(component.hasEntity()).thenReturn(true); + when(component.getEntity()).thenReturn(patientReference); + when(patient.getPatientId()).thenReturn(1); + when(patientReferenceTranslator.toOpenmrsType(patientReference)).thenReturn(patient); + + CohortMembership membership = groupMemberTranslator.toOpenmrsType(component); + assertThat(membership, notNullValue()); + assertThat(membership.getPatientId(), notNullValue()); + assertThat(membership.getPatientId(), equalTo(1)); + } + + @Test + public void shouldGroupPeriodToCohortStartAndEndDateOpenMRSType() { + Period period = mock(Period.class); + Group.GroupMemberComponent component = mock(Group.GroupMemberComponent.class); + + when(component.hasPeriod()).thenReturn(true); + when(component.getPeriod()).thenReturn(period); + when(period.getStart()).thenReturn(Date.from(Instant.now())); + when(period.getEnd()).thenReturn(Date.from(Instant.now())); + + CohortMembership membership = groupMemberTranslator.toOpenmrsType(component); + assertThat(membership, notNullValue()); + assertThat(membership.getStartDate(), notNullValue()); + assertThat(membership.getEndDate(), notNullValue()); + assertThat(membership.getStartDate(), DateMatchers.sameDay(Date.from(Instant.now()))); + assertThat(membership.getEndDate(), DateMatchers.sameDay(Date.from(Instant.now()))); + } + + @Test + public void shouldUpdatedGroupEntityOrCohortMembersOpenMRSType() { + Reference patientReference = mock(Reference.class); + Patient patient = mock(Patient.class); + Group.GroupMemberComponent component = mock(Group.GroupMemberComponent.class); + + when(component.hasEntity()).thenReturn(true); + when(component.getEntity()).thenReturn(patientReference); + when(patient.getPatientId()).thenReturn(4); + when(patientReferenceTranslator.toOpenmrsType(patientReference)).thenReturn(patient); + + //Existing cohortMembership with patient id 3 + CohortMembership cohortMembership = new CohortMembership(); + cohortMembership.setPatientId(3); + + CohortMembership membership = groupMemberTranslator.toOpenmrsType(cohortMembership, component); + assertThat(membership, notNullValue()); + assertThat(membership.getPatientId(), notNullValue()); + // Updated cohortMembership with patient id 4 + assertThat(membership.getPatientId(), equalTo(4)); + } + + @Test + public void shouldUpdateCohortMembershipEndDate() { + Period period = mock(Period.class); + Group.GroupMemberComponent component = mock(Group.GroupMemberComponent.class); + + // Existing cohortMembership + CohortMembership cohortMembership = new CohortMembership(); + cohortMembership.setEndDate(Date.from(Instant.parse("2020-12-04T08:07:00Z"))); + + when(component.hasPeriod()).thenReturn(true); + when(component.getPeriod()).thenReturn(period); + + // Mocked updated date is today + when(period.getEnd()).thenReturn(Date.from(Instant.now())); + + CohortMembership membership = groupMemberTranslator.toOpenmrsType(component); + assertThat(membership, notNullValue()); + assertThat(membership.getEndDate(), notNullValue()); + assertThat(membership.getEndDate(), DateMatchers.sameDay(Date.from(Instant.now()))); + } + + @Test + public void shouldUpdateCohortMembershipStartDate() { + Period period = mock(Period.class); + Group.GroupMemberComponent component = mock(Group.GroupMemberComponent.class); + + // Existing cohortMembership + CohortMembership cohortMembership = new CohortMembership(); + cohortMembership.setStartDate(Date.from(Instant.parse("2020-12-04T08:07:00Z"))); + + when(component.hasPeriod()).thenReturn(true); + when(component.getPeriod()).thenReturn(period); + + // Mocked updated date is today + when(period.getStart()).thenReturn(Date.from(Instant.now())); + + CohortMembership membership = groupMemberTranslator.toOpenmrsType(component); + assertThat(membership, notNullValue()); + assertThat(membership.getStartDate(), notNullValue()); + assertThat(membership.getStartDate(), DateMatchers.sameDay(Date.from(Instant.now()))); + } +} diff --git a/api-2.1/src/test/java/org/openmrs/module/fhir2/api/translators/impl/GroupTranslatorImpl_2_1Test.java b/api-2.1/src/test/java/org/openmrs/module/fhir2/api/translators/impl/GroupTranslatorImpl_2_1Test.java new file mode 100644 index 000000000..08916989b --- /dev/null +++ b/api-2.1/src/test/java/org/openmrs/module/fhir2/api/translators/impl/GroupTranslatorImpl_2_1Test.java @@ -0,0 +1,199 @@ +/* + * 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.api.translators.impl; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.Arrays; + +import org.hl7.fhir.r4.model.Group; +import org.hl7.fhir.r4.model.Reference; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.openmrs.Cohort; +import org.openmrs.CohortMembership; +import org.openmrs.module.fhir2.api.translators.GroupMemberTranslator; + +@RunWith(MockitoJUnitRunner.class) +public class GroupTranslatorImpl_2_1Test { + + private static final String COHORT_UUID = "787e12bd-314e-4cc4-9b4d-1cdff9be9545"; + + private static final String NEW_COHORT_UUID = "787e12bd-314e-4cc4-9b4d-1cdff9be9545"; + + private static final String COHORT_NAME = "Patient with VL > 2"; + + @Mock + private GroupMemberTranslator<CohortMembership> groupMemberTranslator; + + private GroupTranslatorImpl_2_1 groupTranslator; + + @Before + public void setup() { + groupTranslator = new GroupTranslatorImpl_2_1(); + groupTranslator.setGroupMemberTranslator(groupMemberTranslator); + } + + @Test + public void shouldTranslateUuidToIdFHIRType() { + Cohort cohort = mock(Cohort.class); + when(cohort.getUuid()).thenReturn(COHORT_UUID); + + Group group = groupTranslator.toFhirResource(cohort); + assertThat(group, notNullValue()); + assertThat(group.getId(), is(COHORT_UUID)); + } + + @Test + public void shouldTranslateNameToNameFHIRType() { + Cohort cohort = mock(Cohort.class); + when(cohort.getName()).thenReturn(COHORT_NAME); + + Group group = groupTranslator.toFhirResource(cohort); + assertThat(group, notNullValue()); + assertThat(group.getName(), is(COHORT_NAME)); + } + + @Test + public void shouldTranslateNameOpenMRSTypeToNameFHIRType() { + Group group = mock(Group.class); + when(group.hasName()).thenReturn(true); + when(group.getName()).thenReturn("Mr. Moon's patient list"); + + Cohort cohort = groupTranslator.toOpenmrsType(group); + assertThat(cohort, notNullValue()); + assertThat(cohort.getName(), notNullValue()); + assertThat(cohort.getName(), is("Mr. Moon's patient list")); + } + + @Test + public void shouldReturnUpdatedNameOpenMRSType() { + Cohort cohort = new Cohort(); + cohort.setName("Moon's patient list"); + + Group group = mock(Group.class); + when(group.hasName()).thenReturn(true); + when(group.getName()).thenReturn("Mr. Moon's patient list"); + + Cohort updateCohort = groupTranslator.toOpenmrsType(cohort, group); + assertThat(updateCohort, notNullValue()); + assertThat(updateCohort.getName(), notNullValue()); + assertThat(updateCohort.getName(), is("Mr. Moon's patient list")); + } + + @Test + public void shouldTranslateIsVoidedToIsActiveFHIRType() { + Cohort cohort = mock(Cohort.class); + when(cohort.getVoided()).thenReturn(false); + + Group group = groupTranslator.toFhirResource(cohort); + assertThat(group, notNullValue()); + assertThat(group.getActive(), is(true)); + } + + @Test + public void shouldTranslateActiveFHIRTypeToIsVoidedOpenMRSType() { + Group group = mock(Group.class); + when(group.hasActive()).thenReturn(true); + when(group.getActive()).thenReturn(true); + + Cohort cohort = groupTranslator.toOpenmrsType(group); + assertThat(cohort, notNullValue()); + assertThat(cohort.getVoided(), is(false)); + } + + @Test + public void shouldUpdateIsVoidedOpenMRSType() { + Cohort cohort = new Cohort(); + cohort.setVoided(false); + + Group group = mock(Group.class); + when(group.hasActive()).thenReturn(true); + when(group.getActive()).thenReturn(false); + + Cohort updateCohort = groupTranslator.toOpenmrsType(cohort, group); + assertThat(updateCohort, notNullValue()); + assertThat(updateCohort.getVoided(), is(true)); + } + + @Test + public void shouldTranslateGroupTypeToAlwaysPerson() { + Cohort cohort = mock(Cohort.class); + + Group group = groupTranslator.toFhirResource(cohort); + assertThat(group, notNullValue()); + assertThat(group.getType(), is(Group.GroupType.PERSON)); + } + + @Test + public void shouldTranslateCohortMembersToFHIRGroupMembers() { + Cohort cohort = mock(Cohort.class); + CohortMembership cohortMembership = mock(CohortMembership.class); + Reference patientReference = mock(Reference.class); + Group.GroupMemberComponent groupMemberComponent = mock(Group.GroupMemberComponent.class); + + when(cohort.getMemberships()).thenReturn(Arrays.asList(cohortMembership, cohortMembership)); + when(groupMemberTranslator.toFhirResource(cohortMembership)).thenReturn(groupMemberComponent); + when(groupMemberComponent.hasEntity()).thenReturn(true); + when(groupMemberComponent.getEntity()).thenReturn(patientReference); + + Group group = groupTranslator.toFhirResource(cohort); + assertThat(group, notNullValue()); + assertThat(group.hasMember(), is(true)); + assertThat(group.getMemberFirstRep().hasEntity(), is(true)); + assertThat(group.getMemberFirstRep().getEntity(), is(patientReference)); + } + + @Test + public void shouldTranslateFHIRGroupMembersToOpenMRSCohortMembers() { + Group group = mock(Group.class); + CohortMembership cohortMembership = mock(CohortMembership.class); + Group.GroupMemberComponent groupMemberComponent = mock(Group.GroupMemberComponent.class); + + when(group.hasMember()).thenReturn(true); + when(group.getMember()).thenReturn(Arrays.asList(groupMemberComponent, groupMemberComponent)); + when(groupMemberTranslator.toOpenmrsType(groupMemberComponent)).thenReturn(cohortMembership); + + Cohort cohort = groupTranslator.toOpenmrsType(group); + assertThat(cohort, notNullValue()); + assertThat(cohort.getMemberships().isEmpty(), is(false)); + assertThat(cohort.getMemberships(), hasSize(1)); + assertThat(cohort.getMemberships().iterator().next(), is(cohortMembership)); + } + + @Test + public void shouldUpdateMemberList() { + CohortMembership cohortMembership = mock(CohortMembership.class); + Group.GroupMemberComponent component = mock(Group.GroupMemberComponent.class); + + Cohort existingCohort = new Cohort(); + existingCohort.setUuid(COHORT_UUID); + existingCohort.setVoided(false); + existingCohort.setMemberships(Arrays.asList(cohortMembership, cohortMembership)); + + Group group = mock(Group.class); + when(group.hasMember()).thenReturn(true); + when(groupMemberTranslator.toOpenmrsType(component)).thenReturn(cohortMembership); + when(group.getMember()).thenReturn(Arrays.asList(component, component)); + + Cohort updateCohort = groupTranslator.toOpenmrsType(existingCohort, group); + assertThat(updateCohort, notNullValue()); + assertThat(updateCohort.getMemberships(), notNullValue()); + assertThat(updateCohort.getMemberships(), hasSize(1)); + } +} 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 451c7d266..2bb6f6269 100644 --- a/api/src/main/java/org/openmrs/module/fhir2/FhirConstants.java +++ b/api/src/main/java/org/openmrs/module/fhir2/FhirConstants.java @@ -75,6 +75,8 @@ public class FhirConstants { public static final String OPENMRS_FHIR_EXT_NAME = OPENMRS_FHIR_EXT_PREFIX + "/name"; + public static final String OPENMRS_FHIR_EXT_GROUP_DESCRIPTION = OPENMRS_FHIR_EXT_PREFIX + "/group/description"; + public static final String OPENMRS_FHIR_EXT_ADDRESS = OPENMRS_FHIR_EXT_PREFIX + "/address"; public static final String OPENMRS_FHIR_EXT_NON_CODED_CONDITION = OPENMRS_FHIR_EXT_PREFIX + "/non-coded-condition"; diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/FhirGroupService.java b/api/src/main/java/org/openmrs/module/fhir2/api/FhirGroupService.java new file mode 100644 index 000000000..f6ef77ffe --- /dev/null +++ b/api/src/main/java/org/openmrs/module/fhir2/api/FhirGroupService.java @@ -0,0 +1,14 @@ +/* + * 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.api; + +import org.hl7.fhir.r4.model.Group; + +public interface FhirGroupService extends FhirService<Group> {} diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/FhirPatientService.java b/api/src/main/java/org/openmrs/module/fhir2/api/FhirPatientService.java index 19521ae2c..5c92c93fb 100644 --- a/api/src/main/java/org/openmrs/module/fhir2/api/FhirPatientService.java +++ b/api/src/main/java/org/openmrs/module/fhir2/api/FhirPatientService.java @@ -11,7 +11,9 @@ import javax.annotation.Nonnull; +import java.util.Collection; import java.util.HashSet; +import java.util.List; import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.rest.api.SortSpec; @@ -28,6 +30,10 @@ public interface FhirPatientService extends FhirService<Patient> { @Override Patient get(@Nonnull String uuid); + List<Patient> getPatientsByIds(@Nonnull Collection<Integer> ids); + + Patient getById(@Nonnull Integer id); + PatientIdentifierType getPatientIdentifierTypeByIdentifier(Identifier identifier); IBundleProvider searchForPatients(StringAndListParam name, StringAndListParam given, StringAndListParam family, diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/dao/FhirGroupDao.java b/api/src/main/java/org/openmrs/module/fhir2/api/dao/FhirGroupDao.java new file mode 100644 index 000000000..ccf3cc0f3 --- /dev/null +++ b/api/src/main/java/org/openmrs/module/fhir2/api/dao/FhirGroupDao.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.api.dao; + +import javax.annotation.Nonnull; + +import java.util.Collection; +import java.util.List; + +import org.openmrs.Cohort; +import org.openmrs.annotation.Authorized; +import org.openmrs.util.PrivilegeConstants; + +public interface FhirGroupDao extends FhirDao<Cohort> { + + @Override + @Authorized(PrivilegeConstants.GET_PATIENT_COHORTS) + Cohort get(@Nonnull String uuid); + + @Override + @Authorized(PrivilegeConstants.GET_PATIENT_COHORTS) + List<Cohort> get(@Nonnull Collection<String> uuids); + + @Override + @Authorized(PrivilegeConstants.ADD_COHORTS) + Cohort createOrUpdate(@Nonnull Cohort newEntry); + + @Override + @Authorized(PrivilegeConstants.DELETE_COHORTS) + Cohort delete(@Nonnull String uuid); + +} diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/dao/FhirPatientDao.java b/api/src/main/java/org/openmrs/module/fhir2/api/dao/FhirPatientDao.java index 27ca088d5..7932340e0 100644 --- a/api/src/main/java/org/openmrs/module/fhir2/api/dao/FhirPatientDao.java +++ b/api/src/main/java/org/openmrs/module/fhir2/api/dao/FhirPatientDao.java @@ -11,6 +11,7 @@ import javax.annotation.Nonnull; +import java.util.Collection; import java.util.List; import org.openmrs.Patient; @@ -24,6 +25,9 @@ public interface FhirPatientDao extends FhirDao<Patient> { @Authorized(PrivilegeConstants.GET_PATIENTS) Patient getPatientById(@Nonnull Integer id); + @Authorized(PrivilegeConstants.GET_PATIENTS) + List<Patient> getPatientsByIds(@Nonnull Collection<Integer> ids); + @Override @Authorized(PrivilegeConstants.GET_PATIENTS) Patient get(@Nonnull String uuid); diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/BaseFhirDao.java b/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/BaseFhirDao.java index 6243696dd..43465c9f6 100644 --- a/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/BaseFhirDao.java +++ b/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/BaseFhirDao.java @@ -107,7 +107,7 @@ public T get(@Nonnull String uuid) { @Override @Transactional(readOnly = true) @SuppressWarnings("unchecked") - public List<T> get(Collection<String> uuids) { + public List<T> get(@Nonnull Collection<String> uuids) { Criteria criteria = sessionFactory.getCurrentSession().createCriteria(typeToken.getRawType()); criteria.add(in("uuid", uuids)); diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirGroupDaoImpl.java b/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirGroupDaoImpl.java new file mode 100644 index 000000000..182e6a7e1 --- /dev/null +++ b/api/src/main/java/org/openmrs/module/fhir2/api/dao/impl/FhirGroupDaoImpl.java @@ -0,0 +1,22 @@ +/* + * 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.api.dao.impl; + +import lombok.AccessLevel; +import lombok.Setter; +import org.openmrs.Cohort; +import org.openmrs.module.fhir2.api.dao.FhirGroupDao; +import org.springframework.stereotype.Component; + +@Component +@Setter(AccessLevel.PACKAGE) +public class FhirGroupDaoImpl extends BaseFhirDao<Cohort> implements FhirGroupDao { + +} 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 e283c7525..efb873329 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 @@ -17,6 +17,7 @@ import javax.annotation.Nonnull; +import java.util.Collection; import java.util.List; import java.util.NoSuchElementException; import java.util.Optional; @@ -44,6 +45,12 @@ public Patient getPatientById(@Nonnull Integer id) { .uniqueResult(); } + @Override + @SuppressWarnings("unchecked") + public List<Patient> getPatientsByIds(@Nonnull Collection<Integer> ids) { + return getSessionFactory().getCurrentSession().createCriteria(Patient.class).add(in("id", ids)).list(); + } + @Override @SuppressWarnings("unchecked") public PatientIdentifierType getPatientIdentifierTypeByNameOrUuid(String name, String uuid) { diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/impl/FhirGroupServiceImpl.java b/api/src/main/java/org/openmrs/module/fhir2/api/impl/FhirGroupServiceImpl.java new file mode 100644 index 000000000..1319349b2 --- /dev/null +++ b/api/src/main/java/org/openmrs/module/fhir2/api/impl/FhirGroupServiceImpl.java @@ -0,0 +1,35 @@ +/* + * 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.api.impl; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; +import org.hl7.fhir.r4.model.Group; +import org.openmrs.Cohort; +import org.openmrs.module.fhir2.api.FhirGroupService; +import org.openmrs.module.fhir2.api.dao.FhirGroupDao; +import org.openmrs.module.fhir2.api.translators.GroupTranslator; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +@Component +@Transactional +@Setter(AccessLevel.PACKAGE) +@Getter(AccessLevel.PROTECTED) +public class FhirGroupServiceImpl extends BaseFhirService<Group, Cohort> implements FhirGroupService { + + @Autowired + private FhirGroupDao dao; + + @Autowired + private GroupTranslator translator; +} diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/impl/FhirPatientServiceImpl.java b/api/src/main/java/org/openmrs/module/fhir2/api/impl/FhirPatientServiceImpl.java index dbb181c5e..a2a05f938 100644 --- a/api/src/main/java/org/openmrs/module/fhir2/api/impl/FhirPatientServiceImpl.java +++ b/api/src/main/java/org/openmrs/module/fhir2/api/impl/FhirPatientServiceImpl.java @@ -9,7 +9,12 @@ */ package org.openmrs.module.fhir2.api.impl; +import javax.annotation.Nonnull; + +import java.util.Collection; import java.util.HashSet; +import java.util.List; +import java.util.stream.Collectors; import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.rest.api.SortSpec; @@ -53,6 +58,17 @@ public class FhirPatientServiceImpl extends BaseFhirService<Patient, org.openmrs @Autowired private SearchQuery<org.openmrs.Patient, Patient, FhirPatientDao, PatientTranslator, SearchQueryInclude<Patient>> searchQuery; + @Override + public List<Patient> getPatientsByIds(@Nonnull Collection<Integer> ids) { + List<org.openmrs.Patient> patients = dao.getPatientsByIds(ids); + return patients.stream().map(translator::toFhirResource).collect(Collectors.toList()); + } + + @Override + public Patient getById(@Nonnull Integer id) { + return translator.toFhirResource(dao.getPatientById(id)); + } + @Override @Transactional(readOnly = true) public PatientIdentifierType getPatientIdentifierTypeByIdentifier(Identifier identifier) { diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/translators/GroupMemberTranslator.java b/api/src/main/java/org/openmrs/module/fhir2/api/translators/GroupMemberTranslator.java new file mode 100644 index 000000000..570587a43 --- /dev/null +++ b/api/src/main/java/org/openmrs/module/fhir2/api/translators/GroupMemberTranslator.java @@ -0,0 +1,45 @@ +/* + * 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.api.translators; + +import javax.annotation.Nonnull; + +import org.hl7.fhir.r4.model.Group; + +public interface GroupMemberTranslator<T> extends OpenmrsFhirUpdatableTranslator<T, Group.GroupMemberComponent> { + + /** + * Maps an OpenMRS data element to a FHIR resource + * + * @param cohortMember the OpenMRS data element to translate + * @return the corresponding FHIR resource + */ + @Override + Group.GroupMemberComponent toFhirResource(@Nonnull T cohortMember); + + /** + * Maps a FHIR resource to an OpenMRS data element + * + * @param groupMemberComponent the FHIR resource to translate + * @return the corresponding OpenMRS data element + */ + @Override + T toOpenmrsType(@Nonnull Group.GroupMemberComponent groupMemberComponent); + + /** + * Maps a FHIR resource to an existing OpenMRS data element + * + * @param existingCohort the existingObject to update + * @param groupMemberComponent the resource to map + * @return an updated version of the existingObject + */ + @Override + T toOpenmrsType(@Nonnull T existingCohort, @Nonnull Group.GroupMemberComponent groupMemberComponent); +} diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/translators/GroupTranslator.java b/api/src/main/java/org/openmrs/module/fhir2/api/translators/GroupTranslator.java new file mode 100644 index 000000000..36313e6e2 --- /dev/null +++ b/api/src/main/java/org/openmrs/module/fhir2/api/translators/GroupTranslator.java @@ -0,0 +1,46 @@ +/* + * 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.api.translators; + +import javax.annotation.Nonnull; + +import org.hl7.fhir.r4.model.Group; +import org.openmrs.Cohort; + +public interface GroupTranslator extends OpenmrsFhirUpdatableTranslator<Cohort, Group> { + + /** + * Maps an OpenMRS cohort to a FHIR group resource + * + * @param cohort the OpenMRS cohort to translate + * @return the corresponding FHIR resource + */ + @Override + Group toFhirResource(@Nonnull Cohort cohort); + + /** + * Maps a FHIR group resource to an OpenMRS cohort + * + * @param group the FHIR group resource to translate + * @return the corresponding OpenMRS cohort + */ + @Override + Cohort toOpenmrsType(@Nonnull Group group); + + /** + * Maps a FHIR group resource to an existing OpenMRS cohort + * + * @param existingCohort the existingCohort to update + * @param group the group resource to map + * @return an updated version of the existing cohort + */ + @Override + Cohort toOpenmrsType(@Nonnull Cohort existingCohort, @Nonnull Group group); +} diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/BaseGroupTranslator.java b/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/BaseGroupTranslator.java new file mode 100644 index 000000000..2584f6cdf --- /dev/null +++ b/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/BaseGroupTranslator.java @@ -0,0 +1,70 @@ +/* + * 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.api.translators.impl; + +import static org.apache.commons.lang3.Validate.notNull; + +import javax.annotation.Nonnull; + +import org.hl7.fhir.r4.model.Extension; +import org.hl7.fhir.r4.model.Group; +import org.hl7.fhir.r4.model.StringType; +import org.openmrs.Cohort; +import org.openmrs.module.fhir2.FhirConstants; + +public abstract class BaseGroupTranslator { + + public Group toFhirResource(@Nonnull Cohort cohort) { + notNull(cohort, "Cohort object should not be null"); + Group group = new Group(); + group.setId(cohort.getUuid()); + group.setActive(!cohort.getVoided()); + + /* + * Apparently, cohort.description is a required field + */ + group.addExtension(new Extension().setUrl(FhirConstants.OPENMRS_FHIR_EXT_GROUP_DESCRIPTION) + .setValue(new StringType(cohort.getDescription()))); + + // Not sure about this, It's either actual or descriptive + // I will set actual - true temporarily as it required - valid resource. + group.setActual(true); + + // Set to always person for now + group.setType(Group.GroupType.PERSON); + group.setName(cohort.getName()); + + return group; + } + + public Cohort toOpenmrsType(@Nonnull Cohort existingCohort, @Nonnull Group group) { + notNull(group, "group resource object should not be null"); + notNull(existingCohort, "ExistingCohort object should not be null"); + + if (group.hasId()) { + existingCohort.setUuid(group.getId()); + } + + if (group.hasName()) { + existingCohort.setName(group.getName()); + } + + if (group.hasActive()) { + existingCohort.setVoided(!group.getActive()); + } + + Extension extension = group.getExtensionByUrl(FhirConstants.OPENMRS_FHIR_EXT_GROUP_DESCRIPTION); + if (extension != null && extension.hasValue()) { + existingCohort.setDescription(extension.getValue().toString()); + } + + return existingCohort; + } +} diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/GroupMemberTranslatorImpl.java b/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/GroupMemberTranslatorImpl.java new file mode 100644 index 000000000..61c639091 --- /dev/null +++ b/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/GroupMemberTranslatorImpl.java @@ -0,0 +1,60 @@ +/* + * 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.api.translators.impl; + +import static org.apache.commons.lang3.Validate.notNull; + +import javax.annotation.Nonnull; + +import lombok.AccessLevel; +import lombok.Setter; +import org.hl7.fhir.r4.model.Group; +import org.openmrs.annotation.OpenmrsProfile; +import org.openmrs.module.fhir2.api.dao.FhirPatientDao; +import org.openmrs.module.fhir2.api.translators.GroupMemberTranslator; +import org.openmrs.module.fhir2.api.translators.PatientReferenceTranslator; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +@Setter(AccessLevel.MODULE) +@OpenmrsProfile(openmrsPlatformVersion = "2.0.* - 2.1") +public class GroupMemberTranslatorImpl implements GroupMemberTranslator<Integer> { + + @Autowired + private FhirPatientDao patientDao; + + @Autowired + private PatientReferenceTranslator patientReferenceTranslator; + + @Override + public Group.GroupMemberComponent toFhirResource(@Nonnull Integer memberId) { + notNull(memberId, "MemberId should not be null"); + return new Group.GroupMemberComponent() + .setEntity(patientReferenceTranslator.toFhirResource(patientDao.getPatientById(memberId))); + } + + @Override + public Integer toOpenmrsType(@Nonnull Group.GroupMemberComponent groupMemberComponent) { + notNull(groupMemberComponent, "GroupComponent object cannot not be null"); + return toOpenmrsType(-1, groupMemberComponent); + } + + @Override + public Integer toOpenmrsType(@Nonnull Integer existingMemberId, + @Nonnull Group.GroupMemberComponent groupMemberComponent) { + notNull(existingMemberId, "Existing memberId should not be null"); + notNull(groupMemberComponent, "GroupMemberComponent Object should not be null"); + if (groupMemberComponent.hasEntity()) { + existingMemberId = patientReferenceTranslator.toOpenmrsType(groupMemberComponent.getEntity()).getPatientId(); + } + return existingMemberId; + } +} diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/GroupTranslatorImpl.java b/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/GroupTranslatorImpl.java new file mode 100644 index 000000000..338adecf9 --- /dev/null +++ b/api/src/main/java/org/openmrs/module/fhir2/api/translators/impl/GroupTranslatorImpl.java @@ -0,0 +1,70 @@ +/* + * 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.api.translators.impl; + +import static org.apache.commons.lang3.Validate.notNull; + +import javax.annotation.Nonnull; + +import java.util.Set; + +import lombok.AccessLevel; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.hl7.fhir.r4.model.Group; +import org.openmrs.Cohort; +import org.openmrs.annotation.OpenmrsProfile; +import org.openmrs.module.fhir2.api.translators.GroupMemberTranslator; +import org.openmrs.module.fhir2.api.translators.GroupTranslator; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +@Setter(AccessLevel.MODULE) +@OpenmrsProfile(openmrsPlatformVersion = "2.0.*") +public class GroupTranslatorImpl extends BaseGroupTranslator implements GroupTranslator { + + @Autowired + private GroupMemberTranslator<Integer> groupMemberTranslator; + + @Override + public Group toFhirResource(@Nonnull Cohort cohort) { + notNull(cohort, "Cohort object should not be null"); + Group group = super.toFhirResource(cohort); + + Set<Integer> memberIds = cohort.getMemberIds(); + log.info("Number of members {} ", memberIds.size()); + group.setQuantity(cohort.size()); + memberIds.forEach(id -> group.addMember(groupMemberTranslator.toFhirResource(id))); + + return group; + } + + @Override + public Cohort toOpenmrsType(@Nonnull Group group) { + notNull(group, "Group resource should not be null"); + return toOpenmrsType(new Cohort(), group); + } + + @Override + public Cohort toOpenmrsType(@Nonnull Cohort existingCohort, @Nonnull Group group) { + notNull(group, "group resource object should not be null"); + notNull(existingCohort, "ExistingCohort object should not be null"); + + Cohort finalExistingCohort = super.toOpenmrsType(existingCohort, group); + + if (group.hasMember()) { + group.getMember().forEach(member -> finalExistingCohort.addMember(groupMemberTranslator.toOpenmrsType(member))); + } + + return finalExistingCohort; + } +} diff --git a/api/src/main/java/org/openmrs/module/fhir2/providers/r3/GroupFhirResourceProvider.java b/api/src/main/java/org/openmrs/module/fhir2/providers/r3/GroupFhirResourceProvider.java new file mode 100644 index 000000000..25f38b863 --- /dev/null +++ b/api/src/main/java/org/openmrs/module/fhir2/providers/r3/GroupFhirResourceProvider.java @@ -0,0 +1,87 @@ +/* + * 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.providers.r3; + +import javax.annotation.Nonnull; + +import ca.uhn.fhir.rest.annotation.Create; +import ca.uhn.fhir.rest.annotation.Delete; +import ca.uhn.fhir.rest.annotation.IdParam; +import ca.uhn.fhir.rest.annotation.Read; +import ca.uhn.fhir.rest.annotation.ResourceParam; +import ca.uhn.fhir.rest.annotation.Update; +import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.rest.server.IResourceProvider; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import lombok.AccessLevel; +import lombok.Setter; +import org.hl7.fhir.convertors.conv30_40.Group30_40; +import org.hl7.fhir.dstu3.model.Group; +import org.hl7.fhir.dstu3.model.IdType; +import org.hl7.fhir.dstu3.model.OperationOutcome; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.openmrs.module.fhir2.api.FhirGroupService; +import org.openmrs.module.fhir2.providers.util.FhirProviderUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Component; + +@Component("GroupFhirR3ResourceProvider") +@Qualifier("fhirR3Resources") +@Setter(AccessLevel.PACKAGE) +public class GroupFhirResourceProvider implements IResourceProvider { + + @Autowired + private FhirGroupService groupService; + + @Override + public Class<? extends IBaseResource> getResourceType() { + return Group.class; + } + + @Read + public Group getGroupByUuid(@IdParam @Nonnull IdType id) { + org.hl7.fhir.r4.model.Group group = groupService.get(id.getIdPart()); + if (group == null) { + throw new ResourceNotFoundException("Could not find Group with Id " + id.getIdPart()); + } + return Group30_40.convertGroup(group); + } + + @Create + @SuppressWarnings("unused") + public MethodOutcome createGroup(@ResourceParam Group group) { + return FhirProviderUtils.buildCreate(Group30_40.convertGroup(groupService.create(Group30_40.convertGroup(group)))); + } + + @Update + @SuppressWarnings("unused") + public MethodOutcome updateGroup(@IdParam IdType id, @ResourceParam Group group) { + if (id == null || id.getIdPart() == null) { + throw new InvalidRequestException("id must be specified to update"); + } + + group.setId(id.getIdPart()); + + return FhirProviderUtils + .buildUpdate(Group30_40.convertGroup(groupService.update(id.getIdPart(), Group30_40.convertGroup(group)))); + } + + @Delete + @SuppressWarnings("unused") + public OperationOutcome deleteGroup(@IdParam @Nonnull IdType id) { + org.hl7.fhir.r4.model.Group group = (groupService.delete(id.getIdPart())); + if (group == null) { + throw new ResourceNotFoundException("Could not find group to update with id " + id.getIdPart()); + } + return FhirProviderUtils.buildDelete(Group30_40.convertGroup(group)); + } +} diff --git a/api/src/main/java/org/openmrs/module/fhir2/providers/r4/GroupFhirResourceProvider.java b/api/src/main/java/org/openmrs/module/fhir2/providers/r4/GroupFhirResourceProvider.java new file mode 100644 index 000000000..cc872146f --- /dev/null +++ b/api/src/main/java/org/openmrs/module/fhir2/providers/r4/GroupFhirResourceProvider.java @@ -0,0 +1,85 @@ +/* + * 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.providers.r4; + +import javax.annotation.Nonnull; + +import ca.uhn.fhir.rest.annotation.Create; +import ca.uhn.fhir.rest.annotation.Delete; +import ca.uhn.fhir.rest.annotation.IdParam; +import ca.uhn.fhir.rest.annotation.Read; +import ca.uhn.fhir.rest.annotation.ResourceParam; +import ca.uhn.fhir.rest.annotation.Update; +import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.rest.server.IResourceProvider; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import lombok.AccessLevel; +import lombok.Setter; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r4.model.Group; +import org.hl7.fhir.r4.model.IdType; +import org.hl7.fhir.r4.model.OperationOutcome; +import org.openmrs.module.fhir2.api.FhirGroupService; +import org.openmrs.module.fhir2.providers.util.FhirProviderUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Component; + +@Component("GroupFhirR4ResourceProvider") +@Qualifier("fhirResources") +@Setter(AccessLevel.PACKAGE) +public class GroupFhirResourceProvider implements IResourceProvider { + + @Autowired + private FhirGroupService groupService; + + @Override + public Class<? extends IBaseResource> getResourceType() { + return Group.class; + } + + @Read + public Group getGroupByUuid(@IdParam @Nonnull IdType id) { + Group group = groupService.get(id.getIdPart()); + if (group == null) { + throw new ResourceNotFoundException("Could not find Group with Id " + id.getIdPart()); + } + return group; + } + + @Create + @SuppressWarnings("unused") + public MethodOutcome createGroup(@ResourceParam Group group) { + return FhirProviderUtils.buildCreate(groupService.create(group)); + } + + @Update + @SuppressWarnings("unused") + public MethodOutcome updateGroup(@IdParam IdType id, @ResourceParam Group group) { + if (id == null || id.getIdPart() == null) { + throw new InvalidRequestException("id must be specified to update"); + } + + group.setId(id.getIdPart()); + + return FhirProviderUtils.buildUpdate(groupService.update(id.getIdPart(), group)); + } + + @Delete + @SuppressWarnings("unused") + public OperationOutcome deleteGroup(@IdParam @Nonnull IdType id) { + Group group = groupService.delete(id.getIdPart()); + if (group == null) { + throw new ResourceNotFoundException("Could not find group to update with id " + id.getIdPart()); + } + return FhirProviderUtils.buildDelete(group); + } +} diff --git a/api/src/test/java/org/openmrs/module/fhir2/api/dao/impl/FhirGroupDaoImplTest.java b/api/src/test/java/org/openmrs/module/fhir2/api/dao/impl/FhirGroupDaoImplTest.java new file mode 100644 index 000000000..b52e4f37c --- /dev/null +++ b/api/src/test/java/org/openmrs/module/fhir2/api/dao/impl/FhirGroupDaoImplTest.java @@ -0,0 +1,106 @@ +/* + * 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.api.dao.impl; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; + +import org.hibernate.SessionFactory; +import org.junit.Before; +import org.junit.Test; +import org.openmrs.Cohort; +import org.openmrs.module.fhir2.TestFhirSpringConfiguration; +import org.openmrs.test.BaseModuleContextSensitiveTest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.test.context.ContextConfiguration; + +@ContextConfiguration(classes = TestFhirSpringConfiguration.class, inheritLocations = false) +public class FhirGroupDaoImplTest extends BaseModuleContextSensitiveTest { + + private static final String COHORT_UUID = "985ff1a2-c2ef-49fd-836f-8a1d936d9ef9"; + + private static final String NEW_COHORT_UUID = "111ff1a2-c2ef-49fd-836f-8a1d936d9ef0"; + + private static final String BAD_COHORT_UUID = "005ff1a0-c2ef-49fd-836f-8a1d936d9ef7"; + + private static final String COHORT_INITIAL_DATA_XML = "org/openmrs/module/fhir2/api/dao/impl/FhirCohortDaoImplTest_initial_data.xml"; + + private static final String COHORT_NAME = "John's patientList"; + + private FhirGroupDaoImpl dao; + + @Autowired + @Qualifier("sessionFactory") + private SessionFactory sessionFactory; + + @Before + public void setup() throws Exception { + dao = new FhirGroupDaoImpl(); + dao.setSessionFactory(sessionFactory); + executeDataSet(COHORT_INITIAL_DATA_XML); + } + + @Test + public void getCohortByUuid_shouldReturnMatchingCohort() { + Cohort cohort = dao.get(COHORT_UUID); + assertThat(cohort, notNullValue()); + assertThat(cohort.getUuid(), equalTo(COHORT_UUID)); + assertThat(cohort.getName(), equalTo(COHORT_NAME)); + } + + @Test + public void getCohortByWithWrongUuid_shouldReturnNullCohort() { + Cohort cohort = dao.get(BAD_COHORT_UUID); + assertThat(cohort, nullValue()); + } + + @Test + public void shouldSaveGroup() { + Cohort cohort = new Cohort(); + cohort.setUuid(NEW_COHORT_UUID); + cohort.setName(COHORT_NAME); + cohort.setDescription("Test cohort"); + + Cohort result = dao.createOrUpdate(cohort); + assertThat(result, notNullValue()); + assertThat(result.getUuid(), equalTo(NEW_COHORT_UUID)); + assertThat(result.getName(), equalTo(COHORT_NAME)); + } + + @Test + public void shouldUpdateGroupCorrectly() { + Cohort cohort = dao.get(COHORT_UUID); + cohort.setName("Update cohort name"); + + Cohort result = dao.createOrUpdate(cohort); + assertThat(result, notNullValue()); + assertThat(result.getName(), equalTo("Update cohort name")); + } + + @Test + public void shouldDeleteGroup() { + Cohort result = dao.delete(COHORT_UUID); + + assertThat(result, notNullValue()); + assertThat(result.getVoided(), is(true)); + assertThat(result.getVoidReason(), equalTo("Voided via FHIR API")); + } + + @Test + public void shouldReturnNullIfGroupToDeleteDoesNotExist() { + Cohort result = dao.delete(BAD_COHORT_UUID); + + assertThat(result, nullValue()); + } +} diff --git a/api/src/test/java/org/openmrs/module/fhir2/api/impl/FhirGroupServiceImplTest.java b/api/src/test/java/org/openmrs/module/fhir2/api/impl/FhirGroupServiceImplTest.java new file mode 100644 index 000000000..2f55c0d81 --- /dev/null +++ b/api/src/test/java/org/openmrs/module/fhir2/api/impl/FhirGroupServiceImplTest.java @@ -0,0 +1,158 @@ +/* + * 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.api.impl; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertThrows; +import static org.mockito.Mockito.when; + +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import org.hl7.fhir.r4.model.Group; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.openmrs.Cohort; +import org.openmrs.module.fhir2.api.dao.FhirGroupDao; +import org.openmrs.module.fhir2.api.translators.GroupTranslator; + +@RunWith(MockitoJUnitRunner.class) +public class FhirGroupServiceImplTest { + + private static final String COHORT_UUID = "1359f03d-55d9-4961-b8f8-9a59eddc1f59"; + + private static final String BAD_COHORT_UUID = "02ed36f0-6167-4372-a641-d27b92f7deae"; + + @Mock + private FhirGroupDao dao; + + @Mock + private GroupTranslator translator; + + private FhirGroupServiceImpl groupService; + + private Group group; + + private Cohort cohort; + + @Before + public void setup() { + groupService = new FhirGroupServiceImpl() { + + @Override + protected void validateObject(Cohort object) { + } + }; + groupService.setDao(dao); + groupService.setTranslator(translator); + + group = new Group(); + group.setId(COHORT_UUID); + + cohort = new Cohort(); + cohort.setUuid(COHORT_UUID); + } + + @Test + public void getGroupByUuid_shouldGetGroupByUuid() { + when(dao.get(COHORT_UUID)).thenReturn(cohort); + when(translator.toFhirResource(cohort)).thenReturn(group); + + Group group = groupService.get(COHORT_UUID); + assertThat(group, notNullValue()); + assertThat(group.getId(), notNullValue()); + assertThat(group.getId(), equalTo(COHORT_UUID)); + } + + @Test + public void getGroupByUuid_shouldThrowResourceNotFoundWhenCalledWithUnknownUuid() { + assertThrows(ResourceNotFoundException.class, () -> groupService.get(BAD_COHORT_UUID)); + } + + @Test + public void shouldSaveNewGroup() { + Cohort cohort = new Cohort(); + cohort.setUuid(COHORT_UUID); + + Group group = new Group(); + group.setId(COHORT_UUID); + + when(translator.toFhirResource(cohort)).thenReturn(group); + when(translator.toOpenmrsType(group)).thenReturn(cohort); + when(dao.createOrUpdate(cohort)).thenReturn(cohort); + + Group result = groupService.create(group); + assertThat(result, notNullValue()); + assertThat(result.getId(), equalTo(COHORT_UUID)); + } + + @Test(expected = InvalidRequestException.class) + public void updateGroupShouldThrowInvalidRequestExceptionIfIdIsNull() { + Group group = new Group(); + group.setId(COHORT_UUID); + + groupService.update(null, group); + } + + @Test(expected = InvalidRequestException.class) + public void updateGroupShouldThrowInvalidRequestExceptionIfIdIsBad() { + Group group = new Group(); + group.setId(COHORT_UUID); + + groupService.update(BAD_COHORT_UUID, group); + } + + @Test(expected = ResourceNotFoundException.class) + public void updateGroupShouldThrowResourceNotFoundException() { + Group group = new Group(); + group.setId(COHORT_UUID); + + groupService.update(COHORT_UUID, group); + } + + @Test + public void shouldUpdateGroup() { + Cohort cohort = new Cohort(); + cohort.setUuid(COHORT_UUID); + cohort.setVoided(false); + + Group group = new Group(); + group.setId(COHORT_UUID); + group.setActive(false); + + when(dao.get(COHORT_UUID)).thenReturn(cohort); + when(translator.toFhirResource(cohort)).thenReturn(group); + when(translator.toOpenmrsType(cohort, group)).thenReturn(cohort); + when(dao.createOrUpdate(cohort)).thenReturn(cohort); + + Group result = groupService.update(COHORT_UUID, group); + assertThat(result, notNullValue()); + assertThat(result.getActive(), is(false)); + } + + @Test + public void shouldDeleteGroup() { + Group group = new Group(); + group.setId(COHORT_UUID); + + when(dao.delete(COHORT_UUID)).thenReturn(cohort); + when(translator.toFhirResource(cohort)).thenReturn(group); + + Group result = groupService.delete(COHORT_UUID); + assertThat(result, notNullValue()); + assertThat(result.getId(), equalTo(COHORT_UUID)); + assertThat(result.getActive(), is(false)); + } +} diff --git a/api/src/test/java/org/openmrs/module/fhir2/api/impl/FhirPatientServiceImplTest.java b/api/src/test/java/org/openmrs/module/fhir2/api/impl/FhirPatientServiceImplTest.java index f5bd2fa12..f813c8caf 100644 --- a/api/src/test/java/org/openmrs/module/fhir2/api/impl/FhirPatientServiceImplTest.java +++ b/api/src/test/java/org/openmrs/module/fhir2/api/impl/FhirPatientServiceImplTest.java @@ -21,12 +21,14 @@ import static org.hamcrest.Matchers.not; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anySet; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.HashSet; @@ -173,6 +175,31 @@ public void getPatientByUuid_shouldRetrievePatientByUuid() { assertThat(result.getId(), equalTo(PATIENT_UUID)); } + @Test + public void getById_shouldReturnPatientById() { + when(dao.getPatientById(1)).thenReturn(patient); + when(patientTranslator.toFhirResource(patient)).thenReturn(fhirPatient); + + org.hl7.fhir.r4.model.Patient result = patientService.getById(1); + + assertThat(result, notNullValue()); + assertThat(result.getId(), notNullValue()); + assertThat(result.getId(), equalTo(PATIENT_UUID)); + } + + @Test + public void getByIds_shouldRetrievePatientsByIds() { + when(dao.getPatientsByIds(anySet())).thenReturn(Arrays.asList(patient, patient, patient)); + when(patientTranslator.toFhirResource(patient)).thenReturn(fhirPatient); + + List<org.hl7.fhir.r4.model.Patient> patientList = patientService + .getPatientsByIds(new HashSet<>(Arrays.asList(1, 2, 3))); + + assertThat(patientList, notNullValue()); + assertThat(patientList, not(empty())); + assertThat(patientList, hasSize(3)); + } + @Test public void searchForPatients_shouldSearchForPatientsByName() { List<Patient> patients = new ArrayList<>(); diff --git a/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/GroupMemberTranslatorImplTest.java b/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/GroupMemberTranslatorImplTest.java new file mode 100644 index 000000000..b69ac361d --- /dev/null +++ b/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/GroupMemberTranslatorImplTest.java @@ -0,0 +1,94 @@ +/* + * 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.api.translators.impl; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import org.hl7.fhir.r4.model.Group; +import org.hl7.fhir.r4.model.Reference; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.openmrs.Cohort; +import org.openmrs.Patient; +import org.openmrs.module.fhir2.api.dao.FhirPatientDao; +import org.openmrs.module.fhir2.api.translators.PatientReferenceTranslator; + +@RunWith(MockitoJUnitRunner.class) +public class GroupMemberTranslatorImplTest { + + private static final String COHORT_UUID = "787e12bd-314e-4cc4-9b4d-1cdff9be9545"; + + private static final String COHORT_NAME = "Patient with VL > 2"; + + @Mock + private PatientReferenceTranslator patientReferenceTranslator; + + @Mock + private FhirPatientDao patientDao; + + private GroupMemberTranslatorImpl groupMemberTranslator; + + private Cohort cohort; + + private Group.GroupMemberComponent groupMemberComponent; + + @Before + public void setup() { + groupMemberTranslator = new GroupMemberTranslatorImpl(); + groupMemberTranslator.setPatientDao(patientDao); + groupMemberTranslator.setPatientReferenceTranslator(patientReferenceTranslator); + } + + @Before + public void init() { + cohort = new Cohort(); + cohort.setUuid(COHORT_UUID); + cohort.setName(COHORT_NAME); + + groupMemberComponent = new Group.GroupMemberComponent(); + groupMemberComponent.setId(COHORT_UUID); + } + + @Test + public void shouldTranslateCohortMemberToFHIRType() { + Reference patientReference = mock(Reference.class); + Patient patient = mock(Patient.class); + when(patientReferenceTranslator.toFhirResource(patient)).thenReturn(patientReference); + when(patientDao.getPatientById(1)).thenReturn(patient); + + Group.GroupMemberComponent component = groupMemberTranslator.toFhirResource(1); + assertThat(component, notNullValue()); + assertThat(component.getEntity(), notNullValue()); + assertThat(component.hasEntity(), is(true)); + } + + @Test + public void shouldTranslateGroupMemberComponentToOpenMRSType() { + Reference patientReference = mock(Reference.class); + Patient patient = mock(Patient.class); + when(patient.getPatientId()).thenReturn(1); + when(patientReferenceTranslator.toOpenmrsType(patientReference)).thenReturn(patient); + + Group.GroupMemberComponent component = new Group.GroupMemberComponent(); + component.setEntity(patientReference); + + Integer patientId = groupMemberTranslator.toOpenmrsType(component); + assertThat(patientId, notNullValue()); + assertThat(patientId, is(1)); + } + +} diff --git a/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/GroupTranslatorImplTest.java b/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/GroupTranslatorImplTest.java new file mode 100644 index 000000000..fff5fd4dd --- /dev/null +++ b/api/src/test/java/org/openmrs/module/fhir2/api/translators/impl/GroupTranslatorImplTest.java @@ -0,0 +1,200 @@ +/* + * 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.api.translators.impl; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.HashSet; + +import org.hl7.fhir.r4.model.Group; +import org.hl7.fhir.r4.model.Reference; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.openmrs.Cohort; +import org.openmrs.module.fhir2.api.translators.GroupMemberTranslator; + +@RunWith(MockitoJUnitRunner.class) +public class GroupTranslatorImplTest { + + private static final String COHORT_UUID = "787e12bd-314e-4cc4-9b4d-1cdff9be9545"; + + private static final String COHORT_NAME = "Patient with VL > 2"; + + @Mock + private GroupMemberTranslator<Integer> groupMemberTranslator; + + private GroupTranslatorImpl groupTranslator; + + @Before + public void setup() { + groupTranslator = new GroupTranslatorImpl(); + groupTranslator.setGroupMemberTranslator(groupMemberTranslator); + } + + @Test + public void shouldTranslateUuidToIdFHIRType() { + Cohort cohort = mock(Cohort.class); + when(cohort.getUuid()).thenReturn(COHORT_UUID); + + Group group = groupTranslator.toFhirResource(cohort); + assertThat(group, notNullValue()); + assertThat(group.getId(), is(COHORT_UUID)); + } + + @Test + public void shouldTranslateNameToNameFHIRType() { + Cohort cohort = mock(Cohort.class); + when(cohort.getName()).thenReturn(COHORT_NAME); + + Group group = groupTranslator.toFhirResource(cohort); + assertThat(group, notNullValue()); + assertThat(group.getName(), is(COHORT_NAME)); + } + + @Test + public void shouldTranslateNameOpenMRSTypeToNameFHIRType() { + Group group = mock(Group.class); + when(group.hasName()).thenReturn(true); + when(group.getName()).thenReturn("Mr. Moon's patient list"); + + Cohort cohort = groupTranslator.toOpenmrsType(group); + assertThat(cohort, notNullValue()); + assertThat(cohort.getName(), notNullValue()); + assertThat(cohort.getName(), is("Mr. Moon's patient list")); + } + + @Test + public void shouldReturnUpdatedNameOpenMRSType() { + Cohort cohort = new Cohort(); + cohort.setName("Moon's patient list"); + + Group group = mock(Group.class); + when(group.hasName()).thenReturn(true); + when(group.getName()).thenReturn("Mr. Moon's patient list"); + + Cohort updateCohort = groupTranslator.toOpenmrsType(cohort, group); + assertThat(updateCohort, notNullValue()); + assertThat(updateCohort.getName(), notNullValue()); + assertThat(updateCohort.getName(), is("Mr. Moon's patient list")); + } + + @Test + public void shouldTranslateIsVoidedToIsActiveFHIRType() { + Cohort cohort = mock(Cohort.class); + when(cohort.getVoided()).thenReturn(false); + + Group group = groupTranslator.toFhirResource(cohort); + assertThat(group, notNullValue()); + assertThat(group.getActive(), is(true)); + } + + @Test + public void shouldTranslateActiveFHIRTypeToIsVoidedOpenMRSType() { + Group group = mock(Group.class); + when(group.hasActive()).thenReturn(true); + when(group.getActive()).thenReturn(true); + + Cohort cohort = groupTranslator.toOpenmrsType(group); + assertThat(cohort, notNullValue()); + assertThat(cohort.getVoided(), is(false)); + } + + @Test + public void shouldUpdateIsVoidedOpenMRSType() { + Cohort cohort = new Cohort(); + cohort.setVoided(false); + + Group group = mock(Group.class); + when(group.hasActive()).thenReturn(true); + when(group.getActive()).thenReturn(false); + + Cohort updateCohort = groupTranslator.toOpenmrsType(cohort, group); + assertThat(updateCohort, notNullValue()); + assertThat(updateCohort.getVoided(), is(true)); + } + + @Test + public void shouldTranslateGroupTypeToAlwaysPerson() { + Cohort cohort = mock(Cohort.class); + + Group group = groupTranslator.toFhirResource(cohort); + assertThat(group, notNullValue()); + assertThat(group.getType(), is(Group.GroupType.PERSON)); + } + + @Test + public void shouldTranslateCohortMembersToFHIRGroupMembers() { + Cohort cohort = mock(Cohort.class); + Reference patientReference = mock(Reference.class); + Group.GroupMemberComponent groupMemberComponent = mock(Group.GroupMemberComponent.class); + when(cohort.getMemberIds()).thenReturn(new HashSet<>(Arrays.asList(1, 2, 3))); + when(groupMemberTranslator.toFhirResource(anyInt())).thenReturn(groupMemberComponent); + when(groupMemberComponent.hasEntity()).thenReturn(true); + when(groupMemberComponent.getEntity()).thenReturn(patientReference); + + Group group = groupTranslator.toFhirResource(cohort); + assertThat(group, notNullValue()); + assertThat(group.hasMember(), is(true)); + assertThat(group.getMemberFirstRep().hasEntity(), is(true)); + assertThat(group.getMemberFirstRep().getEntity(), is(patientReference)); + } + + @Test + public void shouldTranslateFHIRGroupMembersToOpenMRSCohortMembers() { + Group group = mock(Group.class); + Group.GroupMemberComponent groupMemberComponent = mock(Group.GroupMemberComponent.class); + + when(group.hasMember()).thenReturn(true); + when(group.getMember()).thenReturn(Arrays.asList(groupMemberComponent, groupMemberComponent)); + when(groupMemberTranslator.toOpenmrsType(groupMemberComponent)).thenReturn(1); + + Cohort cohort = groupTranslator.toOpenmrsType(group); + assertThat(cohort, notNullValue()); + assertThat(cohort.getMemberIds().isEmpty(), is(false)); + assertThat(cohort.getMemberIds(), hasSize(1)); + assertThat(cohort.getMemberIds().iterator().next(), is(1)); + } + + @Test + public void shouldUpdateMemberList() { + Cohort cohort = new Cohort(); + cohort.setVoided(false); + cohort.setMemberIds(new HashSet<>(Arrays.asList(1, 2, 3))); + + Group group = mock(Group.class); + when(group.hasMember()).thenReturn(true); + + Group.GroupMemberComponent component1 = mock(Group.GroupMemberComponent.class); + Group.GroupMemberComponent component2 = mock(Group.GroupMemberComponent.class); + Group.GroupMemberComponent component3 = mock(Group.GroupMemberComponent.class); + Group.GroupMemberComponent component4 = mock(Group.GroupMemberComponent.class); + + when(groupMemberTranslator.toOpenmrsType(component1)).thenReturn(1); + when(groupMemberTranslator.toOpenmrsType(component2)).thenReturn(2); + when(groupMemberTranslator.toOpenmrsType(component3)).thenReturn(3); + when(groupMemberTranslator.toOpenmrsType(component4)).thenReturn(4); + when(group.getMember()).thenReturn(Arrays.asList(component1, component2, component3, component4)); + + Cohort updateCohort = groupTranslator.toOpenmrsType(cohort, group); + assertThat(updateCohort, notNullValue()); + assertThat(updateCohort.getMemberIds(), notNullValue()); + assertThat(updateCohort.getMemberIds(), hasSize(4)); + } +} diff --git a/api/src/test/java/org/openmrs/module/fhir2/providers/r3/GroupFhirResourceProviderTest.java b/api/src/test/java/org/openmrs/module/fhir2/providers/r3/GroupFhirResourceProviderTest.java new file mode 100644 index 000000000..bad3e4a44 --- /dev/null +++ b/api/src/test/java/org/openmrs/module/fhir2/providers/r3/GroupFhirResourceProviderTest.java @@ -0,0 +1,166 @@ +/* + * 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.providers.r3; + +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException; +import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import org.hl7.fhir.convertors.conv30_40.Group30_40; +import org.hl7.fhir.dstu3.model.Group; +import org.hl7.fhir.dstu3.model.IdType; +import org.hl7.fhir.dstu3.model.OperationOutcome; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.openmrs.module.fhir2.api.FhirGroupService; + +@RunWith(MockitoJUnitRunner.class) +public class GroupFhirResourceProviderTest { + + private static final String COHORT_UUID = "ce8bfad7-c87e-4af0-80cd-c2015c7dff93"; + + private static final String BAD_COHORT_UUID = "51f069dc-e204-40f4-90d6-080385bed91f"; + + @Mock + private FhirGroupService fhirGroupService; + + private GroupFhirResourceProvider resourceProvider; + + private org.hl7.fhir.r4.model.Group group; + + @Before + public void setup() { + resourceProvider = new GroupFhirResourceProvider(); + resourceProvider.setGroupService(fhirGroupService); + + group = new org.hl7.fhir.r4.model.Group(); + group.setId(COHORT_UUID); + } + + @Test + public void getResourceType_shouldReturnResourceType() { + assertThat(resourceProvider.getResourceType(), equalTo(Group.class)); + assertThat(resourceProvider.getResourceType().getName(), equalTo(Group.class.getName())); + } + + @Test + public void getGroupByUuid_shouldReturnMatchingGroup() { + when(fhirGroupService.get(COHORT_UUID)).thenReturn(group); + + IdType id = new IdType(); + id.setValue(COHORT_UUID); + Group group = resourceProvider.getGroupByUuid(id); + assertThat(group, notNullValue()); + assertThat(group.getId(), notNullValue()); + assertThat(group.getId(), equalTo(COHORT_UUID)); + } + + @Test(expected = ResourceNotFoundException.class) + public void getGroupByUuid_shouldThrowResourceNotFoundException() { + IdType id = new IdType(); + id.setValue(BAD_COHORT_UUID); + Group group = resourceProvider.getGroupByUuid(id); + assertThat(group, nullValue()); + } + + @Test + public void shouldCreateNewGroup() { + when(fhirGroupService.create(any(org.hl7.fhir.r4.model.Group.class))).thenReturn(group); + + MethodOutcome result = resourceProvider.createGroup(Group30_40.convertGroup(group)); + + assertThat(result, notNullValue()); + assertThat(result.getCreated(), is(true)); + assertThat(result.getResource(), notNullValue()); + assertThat(result.getResource().getIdElement().getIdPart(), equalTo(group.getId())); + assertThat(result.getResource().getStructureFhirVersionEnum(), equalTo(FhirVersionEnum.DSTU3)); + } + + @Test + public void shouldUpdateExistingGroup() { + org.hl7.fhir.r4.model.Group.GroupMemberComponent groupMemberComponent = mock( + org.hl7.fhir.r4.model.Group.GroupMemberComponent.class); + + group.setActual(false); + group.addMember(groupMemberComponent); + + when(fhirGroupService.update(eq(COHORT_UUID), any(org.hl7.fhir.r4.model.Group.class))).thenReturn(group); + + MethodOutcome result = resourceProvider.updateGroup(new IdType().setValue(COHORT_UUID), + Group30_40.convertGroup(group)); + + assertThat(result, notNullValue()); + assertThat(result.getResource(), notNullValue()); + assertThat(result.getResource().getIdElement().getIdPart(), equalTo(group.getId())); + assertThat(result.getResource().getStructureFhirVersionEnum(), equalTo(FhirVersionEnum.DSTU3)); + } + + @Test(expected = InvalidRequestException.class) + public void updateGroupShouldThrowInvalidRequestForUuidMismatch() { + when(fhirGroupService.update(eq(BAD_COHORT_UUID), any(org.hl7.fhir.r4.model.Group.class))) + .thenThrow(InvalidRequestException.class); + + resourceProvider.updateGroup(new IdType().setValue(BAD_COHORT_UUID), Group30_40.convertGroup(group)); + } + + @Test(expected = InvalidRequestException.class) + public void ShouldThrowInvalidRequestForMissingIdInGroupToUpdate() { + org.hl7.fhir.r4.model.Group noIdGroup = new org.hl7.fhir.r4.model.Group(); + + when(fhirGroupService.update(eq(COHORT_UUID), any(org.hl7.fhir.r4.model.Group.class))) + .thenThrow(InvalidRequestException.class); + + resourceProvider.updateGroup(new IdType().setValue(COHORT_UUID), Group30_40.convertGroup(noIdGroup)); + } + + @Test(expected = MethodNotAllowedException.class) + public void shouldThrowMethodNotAllowedIfGroupToUpdateDoesNotExist() { + org.hl7.fhir.r4.model.Group wrongGroup = new org.hl7.fhir.r4.model.Group(); + wrongGroup.setId(BAD_COHORT_UUID); + + when(fhirGroupService.update(eq(BAD_COHORT_UUID), any(org.hl7.fhir.r4.model.Group.class))) + .thenThrow(MethodNotAllowedException.class); + + resourceProvider.updateGroup(new IdType().setValue(BAD_COHORT_UUID), Group30_40.convertGroup(wrongGroup)); + } + + @Test + public void shouldDeleteRequestedGroup() { + when(fhirGroupService.delete(COHORT_UUID)).thenReturn(group); + + OperationOutcome result = resourceProvider.deleteGroup(new IdType().setValue(COHORT_UUID)); + + assertThat(result, notNullValue()); + assertThat(result.getIssue(), notNullValue()); + assertThat(result.getIssueFirstRep().getSeverity(), equalTo(OperationOutcome.IssueSeverity.INFORMATION)); + assertThat(result.getIssueFirstRep().getDetails().getCodingFirstRep().getCode(), equalTo("MSG_DELETED")); + } + + @Test(expected = ResourceNotFoundException.class) + public void shouldThrowResourceNotFoundExceptionWhenIdRefersToNonExistentGroup() { + when(fhirGroupService.delete(BAD_COHORT_UUID)).thenReturn(null); + + resourceProvider.deleteGroup(new IdType().setValue(BAD_COHORT_UUID)); + } +} diff --git a/api/src/test/java/org/openmrs/module/fhir2/providers/r4/GroupFhirResourceProviderTest.java b/api/src/test/java/org/openmrs/module/fhir2/providers/r4/GroupFhirResourceProviderTest.java new file mode 100644 index 000000000..e1a1dca97 --- /dev/null +++ b/api/src/test/java/org/openmrs/module/fhir2/providers/r4/GroupFhirResourceProviderTest.java @@ -0,0 +1,163 @@ +/* + * 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.providers.r4; + +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException; +import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import org.hl7.fhir.r4.model.Group; +import org.hl7.fhir.r4.model.IdType; +import org.hl7.fhir.r4.model.OperationOutcome; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; +import org.openmrs.module.fhir2.api.FhirGroupService; + +@RunWith(MockitoJUnitRunner.class) +public class GroupFhirResourceProviderTest { + + private static final String COHORT_UUID = "ce8bfad7-c87e-4af0-80cd-c2015c7dff93"; + + private static final String BAD_COHORT_UUID = "51f069dc-e204-40f4-90d6-080385bed91f"; + + @Mock + private FhirGroupService fhirGroupService; + + GroupFhirResourceProvider resourceProvider; + + Group group; + + @Before + public void setup() { + resourceProvider = new GroupFhirResourceProvider(); + resourceProvider.setGroupService(fhirGroupService); + + group = new Group(); + group.setId(COHORT_UUID); + } + + @Test + public void getResourceType_shouldReturnResourceType() { + assertThat(resourceProvider.getResourceType(), equalTo(Group.class)); + assertThat(resourceProvider.getResourceType().getName(), equalTo(Group.class.getName())); + } + + @Test + public void getGroupByUuid_shouldReturnMatchingGroup() { + when(fhirGroupService.get(COHORT_UUID)).thenReturn(group); + + IdType id = new IdType(); + id.setValue(COHORT_UUID); + Group group = resourceProvider.getGroupByUuid(id); + assertThat(group, notNullValue()); + assertThat(group.getId(), notNullValue()); + assertThat(group.getId(), equalTo(COHORT_UUID)); + } + + @Test(expected = ResourceNotFoundException.class) + public void getGroupByUuid_shouldThrowResourceNotFoundException() { + IdType id = new IdType(); + id.setValue(BAD_COHORT_UUID); + Group group = resourceProvider.getGroupByUuid(id); + assertThat(group, nullValue()); + } + + @Test + public void shouldCreateNewGroup() { + when(fhirGroupService.create(any(org.hl7.fhir.r4.model.Group.class))).thenReturn(group); + + MethodOutcome result = resourceProvider.createGroup(group); + + assertThat(result, notNullValue()); + assertThat(result.getCreated(), is(true)); + assertThat(result.getResource(), notNullValue()); + assertThat(result.getResource().getIdElement().getIdPart(), equalTo(group.getId())); + assertThat(result.getResource().getStructureFhirVersionEnum(), equalTo(FhirVersionEnum.R4)); + } + + @Test + public void shouldUpdateExistingGroup() { + Group.GroupMemberComponent groupMemberComponent = mock(Group.GroupMemberComponent.class); + + group.setActual(false); + group.addMember(groupMemberComponent); + + when(fhirGroupService.update(eq(COHORT_UUID), any(org.hl7.fhir.r4.model.Group.class))).thenReturn(group); + + MethodOutcome result = resourceProvider.updateGroup(new IdType().setValue(COHORT_UUID), group); + + assertThat(result, notNullValue()); + assertThat(result.getResource(), notNullValue()); + assertThat(result.getResource().getIdElement().getIdPart(), equalTo(group.getId())); + assertThat(result.getResource().getStructureFhirVersionEnum(), equalTo(FhirVersionEnum.R4)); + } + + @Test(expected = InvalidRequestException.class) + public void updateGroupShouldThrowInvalidRequestForUuidMismatch() { + when(fhirGroupService.update(eq(BAD_COHORT_UUID), any(org.hl7.fhir.r4.model.Group.class))) + .thenThrow(InvalidRequestException.class); + + resourceProvider.updateGroup(new IdType().setValue(BAD_COHORT_UUID), group); + } + + @Test(expected = InvalidRequestException.class) + public void ShouldThrowInvalidRequestForMissingIdInGroupToUpdate() { + org.hl7.fhir.r4.model.Group noIdGroup = new org.hl7.fhir.r4.model.Group(); + + when(fhirGroupService.update(eq(COHORT_UUID), any(org.hl7.fhir.r4.model.Group.class))) + .thenThrow(InvalidRequestException.class); + + resourceProvider.updateGroup(new IdType().setValue(COHORT_UUID), noIdGroup); + } + + @Test(expected = MethodNotAllowedException.class) + public void shouldThrowMethodNotAllowedIfGroupToUpdateDoesNotExist() { + org.hl7.fhir.r4.model.Group wrongGroup = new org.hl7.fhir.r4.model.Group(); + wrongGroup.setId(BAD_COHORT_UUID); + + when(fhirGroupService.update(eq(BAD_COHORT_UUID), any(org.hl7.fhir.r4.model.Group.class))) + .thenThrow(MethodNotAllowedException.class); + + resourceProvider.updateGroup(new IdType().setValue(BAD_COHORT_UUID), wrongGroup); + } + + @Test + public void shouldDeleteRequestedGroup() { + when(fhirGroupService.delete(COHORT_UUID)).thenReturn(group); + + OperationOutcome result = resourceProvider.deleteGroup(new IdType().setValue(COHORT_UUID)); + + assertThat(result, notNullValue()); + assertThat(result.getIssue(), notNullValue()); + assertThat(result.getIssueFirstRep().getSeverity(), equalTo(OperationOutcome.IssueSeverity.INFORMATION)); + assertThat(result.getIssueFirstRep().getDetails().getCodingFirstRep().getCode(), equalTo("MSG_DELETED")); + } + + @Test(expected = ResourceNotFoundException.class) + public void shouldThrowResourceNotFoundExceptionWhenIdRefersToNonExistentGroup() { + when(fhirGroupService.delete(BAD_COHORT_UUID)).thenReturn(null); + + resourceProvider.deleteGroup(new IdType().setValue(BAD_COHORT_UUID)); + } +} diff --git a/integration-tests/src/test/java/org/openmrs/module/fhir2/providers/r3/GroupFhirResourceProviderIntegrationTest.java b/integration-tests/src/test/java/org/openmrs/module/fhir2/providers/r3/GroupFhirResourceProviderIntegrationTest.java new file mode 100644 index 000000000..f777fd39a --- /dev/null +++ b/integration-tests/src/test/java/org/openmrs/module/fhir2/providers/r3/GroupFhirResourceProviderIntegrationTest.java @@ -0,0 +1,390 @@ +/* + * 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.providers.r3; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; + +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.IOUtils; +import org.hl7.fhir.dstu3.model.Group; +import org.hl7.fhir.dstu3.model.OperationOutcome; +import org.junit.Before; +import org.junit.Test; +import org.openmrs.module.fhir2.BaseFhirIntegrationTest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.mock.web.MockHttpServletResponse; + +@Slf4j +public class GroupFhirResourceProviderIntegrationTest extends BaseFhirR3IntegrationTest<GroupFhirResourceProvider, Group> { + + private static final String COHORT_UUID = "1d64befb-3b2e-48e5-85f5-353d43e23e46"; + + private static final String BAD_COHORT_UUID = "5c9d032b-6092-4052-93d2-a04202b98462"; + + private static final String COHORT_DATA_XML = "org/openmrs/module/fhir2/api/dao/impl/FhirCohortDaoImplTest_initial_data.xml"; + + private static final String PATIENT_DATA_XML = "org/openmrs/module/fhir2/api/dao/impl/FhirPatientDaoImplTest_initial_data.xml"; + + private static final String JSON_CREATE_GROUP_DOCUMENT = "org/openmrs/module/fhir2/providers/GroupWebTest_create.json"; + + private static final String XML_CREATE_GROUP_DOCUMENT = "org/openmrs/module/fhir2/providers/GroupWebTest_create.xml"; + + private static final String JSON_UPDATE_GROUP_DOCUMENT = "org/openmrs/module/fhir2/providers/GroupWebTest_update.json"; + + private static final String XML_UPDATE_GROUP_DOCUMENT = "org/openmrs/module/fhir2/providers/GroupWebTest_update.xml"; + + @Autowired + @Getter(AccessLevel.PUBLIC) + private GroupFhirResourceProvider resourceProvider; + + @Before + @Override + public void setup() throws Exception { + super.setup(); + executeDataSet(PATIENT_DATA_XML); + executeDataSet(COHORT_DATA_XML); + } + + @Test + public void shouldReturnExistingGroupAsJson() throws Exception { + MockHttpServletResponse response = get("/Group/" + COHORT_UUID).accept(BaseFhirIntegrationTest.FhirMediaTypes.JSON) + .go(); + + assertThat(response, isOk()); + assertThat(response.getContentType(), is(BaseFhirIntegrationTest.FhirMediaTypes.JSON.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + Group group = readResponse(response); + + assertThat(group, notNullValue()); + assertThat(group.getIdElement().getIdPart(), equalTo(COHORT_UUID)); + assertThat(group, validResource()); + } + + @Test + public void shouldThrow404ForNonExistingGroupAsJson() throws Exception { + MockHttpServletResponse response = get("/Group/" + BAD_COHORT_UUID) + .accept(BaseFhirIntegrationTest.FhirMediaTypes.JSON).go(); + + assertThat(response, isNotFound()); + assertThat(response.getContentType(), is(BaseFhirIntegrationTest.FhirMediaTypes.JSON.toString())); + assertThat(response.getContentAsString(), notNullValue()); + } + + @Test + public void shouldReturnExistingGroupAsXML() throws Exception { + MockHttpServletResponse response = get("/Group/" + COHORT_UUID).accept(BaseFhirIntegrationTest.FhirMediaTypes.XML) + .go(); + + assertThat(response, isOk()); + assertThat(response.getContentType(), is(BaseFhirIntegrationTest.FhirMediaTypes.XML.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + Group group = readResponse(response); + + assertThat(group, notNullValue()); + assertThat(group.getIdElement().getIdPart(), equalTo(COHORT_UUID)); + assertThat(group, validResource()); + } + + @Test + public void shouldThrow404ForNonExistingGroupAsXML() throws Exception { + MockHttpServletResponse response = get("/Group/" + BAD_COHORT_UUID) + .accept(BaseFhirIntegrationTest.FhirMediaTypes.XML).go(); + + assertThat(response, isNotFound()); + assertThat(response.getContentType(), is(BaseFhirIntegrationTest.FhirMediaTypes.XML.toString())); + assertThat(response.getContentAsString(), notNullValue()); + } + + @Test + public void shouldCreateNewGroupAsJson() throws Exception { + // read JSON record + String jsonGroup; + try (InputStream is = this.getClass().getClassLoader().getResourceAsStream(JSON_CREATE_GROUP_DOCUMENT)) { + Objects.requireNonNull(is); + jsonGroup = IOUtils.toString(is, StandardCharsets.UTF_8); + } + + // create group + MockHttpServletResponse response = post("/Group").accept(FhirMediaTypes.JSON).jsonContent(jsonGroup).go(); + + // verify created correctly + assertThat(response, isCreated()); + assertThat(response.getContentType(), is(FhirMediaTypes.JSON.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + Group group = readResponse(response); + assertThat(group, notNullValue()); + assertThat(group.getActive(), is(true)); + assertThat(group.hasMember(), is(true)); + + // try to get new group + response = get(group.getId()).accept(FhirMediaTypes.JSON).go(); + + assertThat(response, isOk()); + + Group newGroup = readResponse(response); + + assertThat(newGroup.getId(), equalTo(group.getId())); + assertThat(newGroup.getActive(), equalTo(true)); + + } + + @Test + public void shouldCreateNewGroupAsXML() throws Exception { + // read JSON record + String xmlGroup; + try (InputStream is = this.getClass().getClassLoader().getResourceAsStream(XML_CREATE_GROUP_DOCUMENT)) { + Objects.requireNonNull(is); + xmlGroup = IOUtils.toString(is, StandardCharsets.UTF_8); + } + + // create group + MockHttpServletResponse response = post("/Group").accept(FhirMediaTypes.XML).xmlContext(xmlGroup).go(); + + // verify created correctly + assertThat(response, isCreated()); + assertThat(response.getContentType(), is(FhirMediaTypes.XML.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + Group group = readResponse(response); + assertThat(group, notNullValue()); + assertThat(group.getActive(), is(true)); + assertThat(group.hasMember(), is(true)); + + // try to get new group + response = get(group.getId()).accept(FhirMediaTypes.XML).go(); + + assertThat(response, isOk()); + + Group newGroup = readResponse(response); + + assertThat(newGroup.getId(), equalTo(group.getId())); + assertThat(newGroup.getActive(), equalTo(true)); + } + + @Test + public void shouldUpdateExistingGroupAsJson() throws Exception { + //Before update + MockHttpServletResponse response = get("/Group/" + COHORT_UUID).accept(FhirMediaTypes.JSON).go(); + + assertThat(response, isOk()); + assertThat(response.getContentType(), is(FhirMediaTypes.JSON.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + Group group = readResponse(response); + + assertThat(group, notNullValue()); + assertThat(group, validResource()); + assertThat(group.getIdElement().getIdPart(), equalTo(COHORT_UUID)); + + // Get existing group with updated name + String jsonGroup; + try (InputStream is = this.getClass().getClassLoader().getResourceAsStream(JSON_UPDATE_GROUP_DOCUMENT)) { + Objects.requireNonNull(is); + jsonGroup = IOUtils.toString(is, StandardCharsets.UTF_8); + } + + //Update + response = put("/Group/" + COHORT_UUID).jsonContent(jsonGroup).accept(FhirMediaTypes.JSON).go(); + + assertThat(response, isOk()); + assertThat(response.getContentType(), is(FhirMediaTypes.JSON.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + // read updated record + group = readResponse(response); + + assertThat(group, notNullValue()); + assertThat(group.getIdElement().getIdPart(), equalTo(COHORT_UUID)); + assertThat(group.getActive(), is(true)); + assertThat(group, validResource()); + + // Double-check via get + response = get("/Group/" + COHORT_UUID).accept(FhirMediaTypes.JSON).go(); + + Group updatedGroup = readResponse(response); + + assertThat(updatedGroup, validResource()); + assertThat(updatedGroup, notNullValue()); + assertThat(updatedGroup.getActive(), is(true)); + } + + @Test + public void shouldUpdateExistingGroupAsXML() throws Exception { + //Before update + MockHttpServletResponse response = get("/Group/" + COHORT_UUID).accept(FhirMediaTypes.XML).go(); + + assertThat(response, isOk()); + assertThat(response.getContentType(), is(FhirMediaTypes.XML.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + Group group = readResponse(response); + + assertThat(group, notNullValue()); + assertThat(group, validResource()); + assertThat(group.getIdElement().getIdPart(), equalTo(COHORT_UUID)); + + // Get existing group with updated name + String xmlGroup; + try (InputStream is = this.getClass().getClassLoader().getResourceAsStream(XML_UPDATE_GROUP_DOCUMENT)) { + Objects.requireNonNull(is); + xmlGroup = IOUtils.toString(is, StandardCharsets.UTF_8); + } + + //Update + response = put("/Group/" + COHORT_UUID).xmlContext(xmlGroup).accept(FhirMediaTypes.XML).go(); + + assertThat(response, isOk()); + assertThat(response.getContentType(), is(FhirMediaTypes.XML.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + // read updated record + group = readResponse(response); + + assertThat(group, notNullValue()); + assertThat(group.getIdElement().getIdPart(), equalTo(COHORT_UUID)); + assertThat(group.getActive(), is(true)); + assertThat(group, validResource()); + + // Double-check via get + response = get("/Group/" + COHORT_UUID).accept(FhirMediaTypes.XML).go(); + + Group updatedGroup = readResponse(response); + + assertThat(updatedGroup, validResource()); + assertThat(updatedGroup, notNullValue()); + assertThat(updatedGroup.getActive(), is(true)); + } + + @Test + public void shouldReturnBadRequestWhenDocumentIdDoesNotMatchGroupIdAsXML() throws Exception { + // get the existing record + MockHttpServletResponse response = get("/Group/" + COHORT_UUID).accept(FhirMediaTypes.XML).go(); + Group group = readResponse(response); + + // update the existing record + group.setId(BAD_COHORT_UUID); + + // send the update to the server + response = put("/Group/" + COHORT_UUID).xmlContext(toXML(group)).accept(FhirMediaTypes.XML).go(); + + assertThat(response, isBadRequest()); + assertThat(response.getContentType(), is(FhirMediaTypes.XML.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + OperationOutcome operationOutcome = readOperationOutcome(response); + + assertThat(operationOutcome, notNullValue()); + assertThat(operationOutcome.hasIssue(), is(true)); + } + + @Test + public void shouldReturnNotFoundWhenUpdatingNonExistentGroupAsXML() throws Exception { + // get the existing record + MockHttpServletResponse response = get("/Group/" + COHORT_UUID).accept(FhirMediaTypes.XML).go(); + Group group = readResponse(response); + + // update the existing record + group.setId(BAD_COHORT_UUID); + + // send the update to the server + response = put("/Group/" + BAD_COHORT_UUID).xmlContext(toXML(group)).accept(FhirMediaTypes.XML).go(); + + assertThat(response, isNotFound()); + assertThat(response.getContentType(), is(FhirMediaTypes.XML.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + OperationOutcome operationOutcome = readOperationOutcome(response); + + assertThat(operationOutcome, notNullValue()); + assertThat(operationOutcome.hasIssue(), is(true)); + } + + @Test + public void shouldReturnBadRequestWhenDocumentIdDoesNotMatchGroupIdAsJSON() throws Exception { + // get the existing record + MockHttpServletResponse response = get("/Group/" + COHORT_UUID).accept(FhirMediaTypes.JSON).go(); + Group group = readResponse(response); + + // update the existing record + group.setId(BAD_COHORT_UUID); + + // send the update to the server + response = put("/Group/" + COHORT_UUID).jsonContent(toJson(group)).accept(FhirMediaTypes.JSON).go(); + + assertThat(response, isBadRequest()); + assertThat(response.getContentType(), is(FhirMediaTypes.JSON.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + OperationOutcome operationOutcome = readOperationOutcome(response); + + assertThat(operationOutcome, notNullValue()); + assertThat(operationOutcome.hasIssue(), is(true)); + } + + @Test + public void shouldReturnNotFoundWhenUpdatingNonExistentGroupAsJSON() throws Exception { + // get the existing record + MockHttpServletResponse response = get("/Group/" + COHORT_UUID).accept(FhirMediaTypes.JSON).go(); + Group group = readResponse(response); + + // update the existing record + group.setId(BAD_COHORT_UUID); + + // send the update to the server + response = put("/Group/" + BAD_COHORT_UUID).jsonContent(toJson(group)).accept(FhirMediaTypes.JSON).go(); + + assertThat(response, isNotFound()); + assertThat(response.getContentType(), is(FhirMediaTypes.JSON.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + OperationOutcome operationOutcome = readOperationOutcome(response); + + assertThat(operationOutcome, notNullValue()); + assertThat(operationOutcome.hasIssue(), is(true)); + } + + @Test + public void shouldDeleteExistingGroup() throws Exception { + MockHttpServletResponse response = delete("/Group/" + COHORT_UUID).accept(FhirMediaTypes.JSON).go(); + + assertThat(response, isOk()); + + response = get("/Group/" + COHORT_UUID).accept(FhirMediaTypes.JSON).go(); + + assertThat(response, statusEquals(HttpStatus.GONE)); + } + + @Test + public void shouldReturnNotFoundWhenDeletingNonExistentGroup() throws Exception { + MockHttpServletResponse response = delete("/Group/" + BAD_COHORT_UUID).accept(FhirMediaTypes.JSON).go(); + + assertThat(response, isNotFound()); + assertThat(response.getContentType(), is(FhirMediaTypes.JSON.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + OperationOutcome operationOutcome = readOperationOutcome(response); + + assertThat(operationOutcome, notNullValue()); + assertThat(operationOutcome.hasIssue(), is(true)); + } +} diff --git a/integration-tests/src/test/java/org/openmrs/module/fhir2/providers/r4/GroupFhirResourceProviderIntegrationTest.java b/integration-tests/src/test/java/org/openmrs/module/fhir2/providers/r4/GroupFhirResourceProviderIntegrationTest.java new file mode 100644 index 000000000..50a9e9809 --- /dev/null +++ b/integration-tests/src/test/java/org/openmrs/module/fhir2/providers/r4/GroupFhirResourceProviderIntegrationTest.java @@ -0,0 +1,404 @@ +/* + * 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.providers.r4; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; + +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.IOUtils; +import org.hl7.fhir.r4.model.Extension; +import org.hl7.fhir.r4.model.Group; +import org.hl7.fhir.r4.model.OperationOutcome; +import org.junit.Before; +import org.junit.Test; +import org.openmrs.module.fhir2.BaseFhirIntegrationTest; +import org.openmrs.module.fhir2.FhirConstants; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.mock.web.MockHttpServletResponse; + +@Slf4j +public class GroupFhirResourceProviderIntegrationTest extends BaseFhirR4IntegrationTest<org.openmrs.module.fhir2.providers.r4.GroupFhirResourceProvider, Group> { + + private static final String COHORT_UUID = "1d64befb-3b2e-48e5-85f5-353d43e23e46"; + + private static final String BAD_COHORT_UUID = "5c9d032b-6092-4052-93d2-a04202b98462"; + + private static final String COHORT_DATA_XML = "org/openmrs/module/fhir2/api/dao/impl/FhirCohortDaoImplTest_initial_data.xml"; + + private static final String PATIENT_DATA_XML = "org/openmrs/module/fhir2/api/dao/impl/FhirPatientDaoImplTest_initial_data.xml"; + + private static final String JSON_CREATE_GROUP_DOCUMENT = "org/openmrs/module/fhir2/providers/GroupWebTest_create.json"; + + private static final String XML_CREATE_GROUP_DOCUMENT = "org/openmrs/module/fhir2/providers/GroupWebTest_create.xml"; + + private static final String JSON_UPDATE_GROUP_DOCUMENT = "org/openmrs/module/fhir2/providers/GroupWebTest_update.json"; + + private static final String XML_UPDATE_GROUP_DOCUMENT = "org/openmrs/module/fhir2/providers/GroupWebTest_update.xml"; + + @Autowired + @Getter(AccessLevel.PUBLIC) + private org.openmrs.module.fhir2.providers.r4.GroupFhirResourceProvider resourceProvider; + + @Before + @Override + public void setup() throws Exception { + super.setup(); + executeDataSet(PATIENT_DATA_XML); + executeDataSet(COHORT_DATA_XML); + } + + @Test + public void shouldReturnExistingGroupAsJson() throws Exception { + MockHttpServletResponse response = get("/Group/" + COHORT_UUID).accept(BaseFhirIntegrationTest.FhirMediaTypes.JSON) + .go(); + + assertThat(response, isOk()); + assertThat(response.getContentType(), is(BaseFhirIntegrationTest.FhirMediaTypes.JSON.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + Group group = readResponse(response); + + assertThat(group, notNullValue()); + assertThat(group.getIdElement().getIdPart(), equalTo(COHORT_UUID)); + assertThat(group, validResource()); + } + + @Test + public void shouldThrow404ForNonExistingGroupAsJson() throws Exception { + MockHttpServletResponse response = get("/Group/" + BAD_COHORT_UUID) + .accept(BaseFhirIntegrationTest.FhirMediaTypes.JSON).go(); + + assertThat(response, isNotFound()); + assertThat(response.getContentType(), is(BaseFhirIntegrationTest.FhirMediaTypes.JSON.toString())); + assertThat(response.getContentAsString(), notNullValue()); + } + + @Test + public void shouldReturnExistingGroupAsXML() throws Exception { + MockHttpServletResponse response = get("/Group/" + COHORT_UUID).accept(BaseFhirIntegrationTest.FhirMediaTypes.XML) + .go(); + + assertThat(response, isOk()); + assertThat(response.getContentType(), is(BaseFhirIntegrationTest.FhirMediaTypes.XML.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + Group group = readResponse(response); + + assertThat(group, notNullValue()); + assertThat(group.getIdElement().getIdPart(), equalTo(COHORT_UUID)); + assertThat(group, validResource()); + } + + @Test + public void shouldThrow404ForNonExistingGroupAsXML() throws Exception { + MockHttpServletResponse response = get("/Group/" + BAD_COHORT_UUID) + .accept(BaseFhirIntegrationTest.FhirMediaTypes.XML).go(); + + assertThat(response, isNotFound()); + assertThat(response.getContentType(), is(BaseFhirIntegrationTest.FhirMediaTypes.XML.toString())); + assertThat(response.getContentAsString(), notNullValue()); + } + + @Test + public void shouldCreateNewGroupAsJson() throws Exception { + // read JSON record + String jsonGroup; + try (InputStream is = this.getClass().getClassLoader().getResourceAsStream(JSON_CREATE_GROUP_DOCUMENT)) { + Objects.requireNonNull(is); + jsonGroup = IOUtils.toString(is, StandardCharsets.UTF_8); + } + + // create group + MockHttpServletResponse response = post("/Group").accept(FhirMediaTypes.JSON).jsonContent(jsonGroup).go(); + + // verify created correctly + assertThat(response, isCreated()); + assertThat(response.getContentType(), is(FhirMediaTypes.JSON.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + Group group = readResponse(response); + assertThat(group, notNullValue()); + assertThat(group.getActive(), is(true)); + assertThat(group.hasMember(), is(true)); + + // try to get new group + response = get(group.getId()).accept(FhirMediaTypes.JSON).go(); + + assertThat(response, isOk()); + + Group newGroup = readResponse(response); + + assertThat(newGroup.getId(), equalTo(group.getId())); + assertThat(newGroup.getActive(), equalTo(true)); + + } + + @Test + public void shouldCreateNewGroupAsXML() throws Exception { + // read JSON record + String xmlGroup; + try (InputStream is = this.getClass().getClassLoader().getResourceAsStream(XML_CREATE_GROUP_DOCUMENT)) { + Objects.requireNonNull(is); + xmlGroup = IOUtils.toString(is, StandardCharsets.UTF_8); + } + + // create group + MockHttpServletResponse response = post("/Group").accept(FhirMediaTypes.XML).xmlContext(xmlGroup).go(); + + // verify created correctly + assertThat(response, isCreated()); + assertThat(response.getContentType(), is(FhirMediaTypes.XML.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + Group group = readResponse(response); + assertThat(group, notNullValue()); + assertThat(group.getActive(), is(true)); + assertThat(group.hasMember(), is(true)); + + // try to get new group + response = get(group.getId()).accept(FhirMediaTypes.XML).go(); + + assertThat(response, isOk()); + + Group newGroup = readResponse(response); + + assertThat(newGroup.getId(), equalTo(group.getId())); + assertThat(newGroup.getActive(), equalTo(true)); + } + + @Test + public void shouldUpdateExistingGroupAsJson() throws Exception { + //Before update + MockHttpServletResponse response = get("/Group/" + COHORT_UUID).accept(FhirMediaTypes.JSON).go(); + + assertThat(response, isOk()); + assertThat(response.getContentType(), is(FhirMediaTypes.JSON.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + Group group = readResponse(response); + Extension descExtension = group.getExtensionByUrl(FhirConstants.OPENMRS_FHIR_EXT_GROUP_DESCRIPTION); + + assertThat(group, notNullValue()); + assertThat(group, validResource()); + assertThat(group.getIdElement().getIdPart(), equalTo(COHORT_UUID)); + assertThat(descExtension.getValue().toString(), equalTo("Covid19 patients")); + + // Get existing group with updated name + String jsonGroup; + try (InputStream is = this.getClass().getClassLoader().getResourceAsStream(JSON_UPDATE_GROUP_DOCUMENT)) { + Objects.requireNonNull(is); + jsonGroup = IOUtils.toString(is, StandardCharsets.UTF_8); + } + + //Update + response = put("/Group/" + COHORT_UUID).jsonContent(jsonGroup).accept(FhirMediaTypes.JSON).go(); + + assertThat(response, isOk()); + assertThat(response.getContentType(), is(FhirMediaTypes.JSON.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + // read updated record + group = readResponse(response); + descExtension = group.getExtensionByUrl(FhirConstants.OPENMRS_FHIR_EXT_GROUP_DESCRIPTION); + + assertThat(group, notNullValue()); + assertThat(group.getIdElement().getIdPart(), equalTo(COHORT_UUID)); + assertThat(group.getActive(), is(true)); + assertThat(descExtension.getValue().toString(), equalTo("Patients with at least one encounter")); + assertThat(group, validResource()); + + // Double-check via get + response = get("/Group/" + COHORT_UUID).accept(FhirMediaTypes.JSON).go(); + + Group updatedGroup = readResponse(response); + descExtension = updatedGroup.getExtensionByUrl(FhirConstants.OPENMRS_FHIR_EXT_GROUP_DESCRIPTION); + + assertThat(updatedGroup, validResource()); + assertThat(updatedGroup, notNullValue()); + assertThat(updatedGroup.getActive(), is(true)); + assertThat(descExtension.getValue().toString(), equalTo("Patients with at least one encounter")); + } + + @Test + public void shouldUpdateExistingGroupAsXML() throws Exception { + //Before update + MockHttpServletResponse response = get("/Group/" + COHORT_UUID).accept(FhirMediaTypes.XML).go(); + + assertThat(response, isOk()); + assertThat(response.getContentType(), is(FhirMediaTypes.XML.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + Group group = readResponse(response); + Extension descExtension = group.getExtensionByUrl(FhirConstants.OPENMRS_FHIR_EXT_GROUP_DESCRIPTION); + + assertThat(group, notNullValue()); + assertThat(group, validResource()); + assertThat(group.getIdElement().getIdPart(), equalTo(COHORT_UUID)); + assertThat(descExtension.getValue().toString(), equalTo("Covid19 patients")); + + // Get existing group with updated name + String xmlGroup; + try (InputStream is = this.getClass().getClassLoader().getResourceAsStream(XML_UPDATE_GROUP_DOCUMENT)) { + Objects.requireNonNull(is); + xmlGroup = IOUtils.toString(is, StandardCharsets.UTF_8); + } + + //Update + response = put("/Group/" + COHORT_UUID).xmlContext(xmlGroup).accept(FhirMediaTypes.XML).go(); + + assertThat(response, isOk()); + assertThat(response.getContentType(), is(FhirMediaTypes.XML.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + // read updated record + group = readResponse(response); + descExtension = group.getExtensionByUrl(FhirConstants.OPENMRS_FHIR_EXT_GROUP_DESCRIPTION); + + assertThat(group, notNullValue()); + assertThat(group.getIdElement().getIdPart(), equalTo(COHORT_UUID)); + assertThat(group.getActive(), is(true)); + assertThat(descExtension.getValue().toString(), equalTo("Patients with at least one encounter")); + assertThat(group, validResource()); + + // Double-check via get + response = get("/Group/" + COHORT_UUID).accept(FhirMediaTypes.XML).go(); + + Group updatedGroup = readResponse(response); + descExtension = updatedGroup.getExtensionByUrl(FhirConstants.OPENMRS_FHIR_EXT_GROUP_DESCRIPTION); + + assertThat(updatedGroup, validResource()); + assertThat(updatedGroup, notNullValue()); + assertThat(updatedGroup.getActive(), is(true)); + assertThat(descExtension.getValue().toString(), equalTo("Patients with at least one encounter")); + } + + @Test + public void shouldReturnBadRequestWhenDocumentIdDoesNotMatchGroupIdAsXML() throws Exception { + // get the existing record + MockHttpServletResponse response = get("/Group/" + COHORT_UUID).accept(FhirMediaTypes.XML).go(); + Group group = readResponse(response); + + // update the existing record + group.setId(BAD_COHORT_UUID); + + // send the update to the server + response = put("/Group/" + COHORT_UUID).xmlContext(toXML(group)).accept(FhirMediaTypes.XML).go(); + + assertThat(response, isBadRequest()); + assertThat(response.getContentType(), is(FhirMediaTypes.XML.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + OperationOutcome operationOutcome = readOperationOutcome(response); + + assertThat(operationOutcome, notNullValue()); + assertThat(operationOutcome.hasIssue(), is(true)); + } + + @Test + public void shouldReturnNotFoundWhenUpdatingNonExistentGroupAsXML() throws Exception { + // get the existing record + MockHttpServletResponse response = get("/Group/" + COHORT_UUID).accept(FhirMediaTypes.XML).go(); + Group group = readResponse(response); + + // update the existing record + group.setId(BAD_COHORT_UUID); + + // send the update to the server + response = put("/Group/" + BAD_COHORT_UUID).xmlContext(toXML(group)).accept(FhirMediaTypes.XML).go(); + + assertThat(response, isNotFound()); + assertThat(response.getContentType(), is(FhirMediaTypes.XML.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + OperationOutcome operationOutcome = readOperationOutcome(response); + + assertThat(operationOutcome, notNullValue()); + assertThat(operationOutcome.hasIssue(), is(true)); + } + + @Test + public void shouldReturnBadRequestWhenDocumentIdDoesNotMatchGroupIdAsJSON() throws Exception { + // get the existing record + MockHttpServletResponse response = get("/Group/" + COHORT_UUID).accept(FhirMediaTypes.JSON).go(); + Group group = readResponse(response); + + // update the existing record + group.setId(BAD_COHORT_UUID); + + // send the update to the server + response = put("/Group/" + COHORT_UUID).jsonContent(toJson(group)).accept(FhirMediaTypes.JSON).go(); + + assertThat(response, isBadRequest()); + assertThat(response.getContentType(), is(FhirMediaTypes.JSON.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + OperationOutcome operationOutcome = readOperationOutcome(response); + + assertThat(operationOutcome, notNullValue()); + assertThat(operationOutcome.hasIssue(), is(true)); + } + + @Test + public void shouldReturnNotFoundWhenUpdatingNonExistentGroupAsJSON() throws Exception { + // get the existing record + MockHttpServletResponse response = get("/Group/" + COHORT_UUID).accept(FhirMediaTypes.JSON).go(); + Group group = readResponse(response); + + // update the existing record + group.setId(BAD_COHORT_UUID); + + // send the update to the server + response = put("/Group/" + BAD_COHORT_UUID).jsonContent(toJson(group)).accept(FhirMediaTypes.JSON).go(); + + assertThat(response, isNotFound()); + assertThat(response.getContentType(), is(FhirMediaTypes.JSON.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + OperationOutcome operationOutcome = readOperationOutcome(response); + + assertThat(operationOutcome, notNullValue()); + assertThat(operationOutcome.hasIssue(), is(true)); + } + + @Test + public void shouldDeleteExistingGroup() throws Exception { + MockHttpServletResponse response = delete("/Group/" + COHORT_UUID).accept(FhirMediaTypes.JSON).go(); + + assertThat(response, isOk()); + + response = get("/Group/" + COHORT_UUID).accept(FhirMediaTypes.JSON).go(); + + assertThat(response, statusEquals(HttpStatus.GONE)); + } + + @Test + public void shouldReturnNotFoundWhenDeletingNonExistentGroup() throws Exception { + MockHttpServletResponse response = delete("/Group/" + BAD_COHORT_UUID).accept(FhirMediaTypes.JSON).go(); + + assertThat(response, isNotFound()); + assertThat(response.getContentType(), is(FhirMediaTypes.JSON.toString())); + assertThat(response.getContentAsString(), notNullValue()); + + OperationOutcome operationOutcome = readOperationOutcome(response); + + assertThat(operationOutcome, notNullValue()); + assertThat(operationOutcome.hasIssue(), is(true)); + } +} diff --git a/test-data/src/test/resources/org/openmrs/module/fhir2/api/dao/impl/FhirCohortDaoImplTest_initial_data.xml b/test-data/src/test/resources/org/openmrs/module/fhir2/api/dao/impl/FhirCohortDaoImplTest_initial_data.xml new file mode 100644 index 000000000..4527f4c00 --- /dev/null +++ b/test-data/src/test/resources/org/openmrs/module/fhir2/api/dao/impl/FhirCohortDaoImplTest_initial_data.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!-- + 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. +--> +<dataset> + <cohort cohort_id="1" name="John's patientList" description="cohort voided" creator="1" date_created="2005-01-01 00:00:00.0" voided="true" voided_by="1" date_voided="2005-01-01 00:00:00.0" void_reason="This is voided" uuid="985ff1a2-c2ef-49fd-836f-8a1d936d9ef9"/> + <cohort cohort_id="2" name="Covid19 patients" description="Covid19 patients" creator="1" date_created="2005-01-01 00:00:00.0" voided="false" uuid="1d64befb-3b2e-48e5-85f5-353d43e23e46"/> +</dataset> diff --git a/test-data/src/test/resources/org/openmrs/module/fhir2/providers/GroupWebTest_create.json b/test-data/src/test/resources/org/openmrs/module/fhir2/providers/GroupWebTest_create.json new file mode 100644 index 000000000..af17e553b --- /dev/null +++ b/test-data/src/test/resources/org/openmrs/module/fhir2/providers/GroupWebTest_create.json @@ -0,0 +1,27 @@ +{ + "resourceType": "Group", + "extension": [ + { + "url": "http://fhir.openmrs.org/ext/group/description", + "valueString": "Patients with at least one encounter" + } + ], + "active": true, + "type": "person", + "actual": true, + "name": "Test group", + "quantity": 1, + "member": [ + { + "id": "d275f94d-a093-42c8-a4b1-24f90f7c0fb9", + "entity": { + "reference": "Patient/61b38324-e2fd-4feb-95b7-9e9a2a4400df", + "display": "John Doe (OpenMRS ID: 1234-4)" + }, + "period": { + "start": "2019-10-16T00:00:28+03:00" + }, + "inactive": false + } + ] +} diff --git a/test-data/src/test/resources/org/openmrs/module/fhir2/providers/GroupWebTest_create.xml b/test-data/src/test/resources/org/openmrs/module/fhir2/providers/GroupWebTest_create.xml new file mode 100644 index 000000000..34b805827 --- /dev/null +++ b/test-data/src/test/resources/org/openmrs/module/fhir2/providers/GroupWebTest_create.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!-- + 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. +--> +<Group xmlns="http://hl7.org/fhir"> + <extension url="http://fhir.openmrs.org/ext/group/description"> + <valueString value="Patients with at least one encounter"/> + </extension> + <active value="true"/> + <type value="person"/> + <actual value="true"/> + <name value="Test group"/> + <quantity value="1"/> + <member> + <entity> + <reference value="Patient/61b38324-e2fd-4feb-95b7-9e9a2a4400df" /> + </entity> + <period> + <start value="2019-10-16T00:00:28+03:00"/> + </period> + <inactive value="false" /> + </member> +</Group> diff --git a/test-data/src/test/resources/org/openmrs/module/fhir2/providers/GroupWebTest_update.json b/test-data/src/test/resources/org/openmrs/module/fhir2/providers/GroupWebTest_update.json new file mode 100644 index 000000000..b692046ee --- /dev/null +++ b/test-data/src/test/resources/org/openmrs/module/fhir2/providers/GroupWebTest_update.json @@ -0,0 +1,28 @@ +{ + "resourceType": "Group", + "id": "1d64befb-3b2e-48e5-85f5-353d43e23e46", + "extension": [ + { + "url": "http://fhir.openmrs.org/ext/group/description", + "valueString": "Patients with at least one encounter" + } + ], + "active": true, + "type": "person", + "actual": true, + "name": "Test group", + "quantity": 1, + "member": [ + { + "id": "d275f94d-a093-42c8-a4b1-24f90f7c0fb9", + "entity": { + "reference": "Patient/61b38324-e2fd-4feb-95b7-9e9a2a4400df", + "display": "John Doe (OpenMRS ID: 1234-4)" + }, + "period": { + "start": "2019-10-16T00:00:28+03:00" + }, + "inactive": false + } + ] +} diff --git a/test-data/src/test/resources/org/openmrs/module/fhir2/providers/GroupWebTest_update.xml b/test-data/src/test/resources/org/openmrs/module/fhir2/providers/GroupWebTest_update.xml new file mode 100644 index 000000000..12b7e2c90 --- /dev/null +++ b/test-data/src/test/resources/org/openmrs/module/fhir2/providers/GroupWebTest_update.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!-- + 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. +--> +<Group xmlns="http://hl7.org/fhir"> + <id value="1d64befb-3b2e-48e5-85f5-353d43e23e46"/> + <extension url="http://fhir.openmrs.org/ext/group/description"> + <valueString value="Patients with at least one encounter"/> + </extension> + <active value="true"/> + <type value="person"/> + <actual value="true"/> + <name value="Test group"/> + <quantity value="1"/> + <member> + <entity> + <reference value="Patient/61b38324-e2fd-4feb-95b7-9e9a2a4400df" /> + </entity> + <period> + <start value="2019-10-16T00:00:28+03:00"/> + </period> + <inactive value="false" /> + </member> +</Group> From 6227bee029284b776df57aa8f88380d781d3197c Mon Sep 17 00:00:00 2001 From: Ian <52504170+ibacher@users.noreply.github.com> Date: Fri, 5 Feb 2021 17:57:46 +0000 Subject: [PATCH 13/18] Update to HAPI 5.2.1 (#323) --- api/pom.xml | 4 ++++ pom.xml | 8 +++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/api/pom.xml b/api/pom.xml index 0f54b0d6b..9c91e63d7 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -36,6 +36,10 @@ <groupId>ca.uhn.hapi.fhir</groupId> <artifactId>hapi-fhir-client</artifactId> </dependency> + <dependency> + <groupId>org.fhir</groupId> + <artifactId>ucum</artifactId> + </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> diff --git a/pom.xml b/pom.xml index 70d247924..1c99d4905 100644 --- a/pom.xml +++ b/pom.xml @@ -273,6 +273,11 @@ <version>${hapifhirVersion}</version> <scope>test</scope> </dependency> + <dependency> + <groupId>org.fhir</groupId> + <artifactId>ucum</artifactId> + <version>${ucumVersion}</version> + </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> @@ -724,7 +729,8 @@ <lombokVersion>1.18.16</lombokVersion> <openmrsPlatformVersion>2.0.5</openmrsPlatformVersion> <openmrsPlatformToolsVersion>2.0.5</openmrsPlatformToolsVersion> - <hapifhirVersion>5.0.0</hapifhirVersion> + <hapifhirVersion>5.2.1</hapifhirVersion> + <ucumVersion>1.0.3</ucumVersion> </properties> <profiles> From c1519ea6049abae0278eb052db897620e2444db3 Mon Sep 17 00:00:00 2001 From: Ian <ian_bacher@brown.edu> Date: Mon, 8 Feb 2021 09:08:48 -0500 Subject: [PATCH 14/18] Return empty list when requesting results after last Previously, this code always returned the last item --- .../api/search/SearchQueryBundleProvider.java | 8 +++-- .../search/TwoSearchQueryBundleProvider.java | 9 ++++-- .../search/SearchQueryBundleProviderTest.java | 29 +++++++++++++++++-- 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/search/SearchQueryBundleProvider.java b/api/src/main/java/org/openmrs/module/fhir2/api/search/SearchQueryBundleProvider.java index a605d9d52..ddb8b8e5f 100644 --- a/api/src/main/java/org/openmrs/module/fhir2/api/search/SearchQueryBundleProvider.java +++ b/api/src/main/java/org/openmrs/module/fhir2/api/search/SearchQueryBundleProvider.java @@ -91,10 +91,14 @@ public List<IBaseResource> getResources(int fromIndex, int toIndex) { firstResult = fromIndex; } + Integer size = size(); + if (size != null && firstResult > size) { + return Collections.emptyList(); + } + // NPE-safe unboxing int lastResult = Integer.MAX_VALUE; - Integer lastResultHolder = size(); - lastResult = lastResultHolder == null ? lastResult : lastResultHolder; + lastResult = size == null ? lastResult : size; if (toIndex - firstResult > 0) { lastResult = Math.min(lastResult, toIndex); diff --git a/api/src/main/java/org/openmrs/module/fhir2/api/search/TwoSearchQueryBundleProvider.java b/api/src/main/java/org/openmrs/module/fhir2/api/search/TwoSearchQueryBundleProvider.java index a957d0b59..f40a8767d 100644 --- a/api/src/main/java/org/openmrs/module/fhir2/api/search/TwoSearchQueryBundleProvider.java +++ b/api/src/main/java/org/openmrs/module/fhir2/api/search/TwoSearchQueryBundleProvider.java @@ -13,6 +13,7 @@ import javax.annotation.Nullable; import java.util.ArrayList; +import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Optional; @@ -69,10 +70,14 @@ public List<IBaseResource> getResources(int fromIndex, int toIndex) { firstResult = fromIndex; } + Integer size = size(); + if (size != null && firstResult > size) { + return Collections.emptyList(); + } + // NPE-safe unboxing int lastResult = Integer.MAX_VALUE; - Integer lastResultHolder = size(); - lastResult = lastResultHolder == null ? lastResult : lastResultHolder; + lastResult = size == null ? lastResult : size; if (toIndex - firstResult > 0) { lastResult = Math.min(lastResult, toIndex); diff --git a/api/src/test/java/org/openmrs/module/fhir2/api/search/SearchQueryBundleProviderTest.java b/api/src/test/java/org/openmrs/module/fhir2/api/search/SearchQueryBundleProviderTest.java index 270afaddc..46def8dc2 100644 --- a/api/src/test/java/org/openmrs/module/fhir2/api/search/SearchQueryBundleProviderTest.java +++ b/api/src/test/java/org/openmrs/module/fhir2/api/search/SearchQueryBundleProviderTest.java @@ -10,15 +10,22 @@ package org.openmrs.module.fhir2.api.search; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.notNullValue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; +import java.util.Arrays; +import java.util.Collections; import java.util.Date; +import java.util.List; import org.exparity.hamcrest.date.DateMatchers; +import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.r4.model.Observation; import org.junit.Before; @@ -31,6 +38,7 @@ import org.openmrs.module.fhir2.api.dao.FhirObservationDao; import org.openmrs.module.fhir2.api.search.param.SearchParameterMap; import org.openmrs.module.fhir2.api.translators.ObservationTranslator; +import org.openmrs.module.fhir2.api.util.FhirUtils; @RunWith(MockitoJUnitRunner.class) public class SearchQueryBundleProviderTest { @@ -51,8 +59,7 @@ public class SearchQueryBundleProviderTest { @Before public void setup() { - SearchParameterMap theParams = new SearchParameterMap(); - searchQueryBundleProvider = new SearchQueryBundleProvider<>(theParams, observationDao, translator, + searchQueryBundleProvider = new SearchQueryBundleProvider<>(new SearchParameterMap(), observationDao, translator, globalPropertyService, searchQueryInclude); } @@ -72,7 +79,23 @@ public void shouldGetDatePublished() { } @Test - public void shouldReturnRandomUuid() { + public void shouldReturnEmptyListWhenNoResults() { + when(observationDao.getSearchResultUuids(any())).thenReturn(Collections.emptyList()); + List<IBaseResource> resources = searchQueryBundleProvider.getResources(0, 10); + assertThat(resources, empty()); + } + + @Test + public void shouldReturnEmptyListWhenRequestingTooManyResults() { + when(observationDao.getSearchResultUuids(any())).thenReturn(Arrays.asList(FhirUtils.newUuid(), FhirUtils.newUuid())); + List<IBaseResource> resources = searchQueryBundleProvider.getResources(3, 13); + assertThat(resources, empty()); + } + + @Test + public void shouldReturnDifferentUuid() { assertThat(searchQueryBundleProvider.getUuid(), notNullValue()); + assertThat(searchQueryBundleProvider.getUuid(), not(equalTo(new SearchQueryBundleProvider<>(new SearchParameterMap(), + observationDao, translator, globalPropertyService, searchQueryInclude).getUuid()))); } } From 8b859f9b55d588489d2216f6cabe92e4ea5bd61e Mon Sep 17 00:00:00 2001 From: Ian <ian_bacher@brown.edu> Date: Mon, 8 Feb 2021 09:15:56 -0500 Subject: [PATCH 15/18] Bug fix for upgrade to HAPI 5.2.1 --- pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pom.xml b/pom.xml index 1c99d4905..698eac8d9 100644 --- a/pom.xml +++ b/pom.xml @@ -212,6 +212,12 @@ <groupId>ca.uhn.hapi.fhir</groupId> <artifactId>hapi-fhir-server</artifactId> <version>${hapifhirVersion}</version> + <exclusions> + <exclusion> + <groupId>org.springframework</groupId> + <artifactId>spring-messaging</artifactId> + </exclusion> + </exclusions> </dependency> <dependency> <groupId>ca.uhn.hapi.fhir</groupId> From ef7a47490f4827cf6733600e45cdb9ac2a89685e Mon Sep 17 00:00:00 2001 From: Ian <ian_bacher@brown.edu> Date: Tue, 9 Feb 2021 08:24:53 -0500 Subject: [PATCH 16/18] Revert "Mark FHIR2 aware of some basic configuration modules" This reverts commit 3a9194218d120f6c44d8709186e05109eb948e5b. --- omod/src/main/resources/config.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/omod/src/main/resources/config.xml b/omod/src/main/resources/config.xml index 5136336fd..077dacbc1 100644 --- a/omod/src/main/resources/config.xml +++ b/omod/src/main/resources/config.xml @@ -47,8 +47,6 @@ <aware_of_modules> <aware_of_module>org.openmrs.module.legacyui</aware_of_module> - <aware_of_module>org.openmrs.module.referencemetadata</aware_of_module> - <aware_of_module>org.openmrs.module.initializer</aware_of_module> </aware_of_modules> <servlet> From 6ca1460973d07208a0bf213fa3b7320ac499934d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 Feb 2021 13:21:42 +0000 Subject: [PATCH 17/18] Bump jackson-databind from 2.9.0 to 2.9.10.7 in /integration-tests-2.2 (#318) Bumps [jackson-databind](https://github.com/FasterXML/jackson) from 2.9.0 to 2.9.10.7. - [Release notes](https://github.com/FasterXML/jackson/releases) - [Commits](https://github.com/FasterXML/jackson/commits) Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- integration-tests-2.2/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration-tests-2.2/pom.xml b/integration-tests-2.2/pom.xml index 06d12ae08..508a3f265 100644 --- a/integration-tests-2.2/pom.xml +++ b/integration-tests-2.2/pom.xml @@ -76,7 +76,7 @@ <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> - <version>2.9.0</version> + <version>2.9.10.7</version> <scope>provided</scope> </dependency> </dependencies> From cb69c3e9557b74fe69eb86be2f49b07657ac9c27 Mon Sep 17 00:00:00 2001 From: Ankit kumar <49350053+theanandankit@users.noreply.github.com> Date: Wed, 10 Feb 2021 18:53:26 +0530 Subject: [PATCH 18/18] FM2-327: Improve the Tests for FhirRelatedPersonDaoImplTest (#315) Co-authored-by: Ian <52504170+ibacher@users.noreply.github.com> --- .../api/dao/impl/FhirRelatedPersonDaoImplTest.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/api/src/test/java/org/openmrs/module/fhir2/api/dao/impl/FhirRelatedPersonDaoImplTest.java b/api/src/test/java/org/openmrs/module/fhir2/api/dao/impl/FhirRelatedPersonDaoImplTest.java index 80d24915a..2970736fd 100644 --- a/api/src/test/java/org/openmrs/module/fhir2/api/dao/impl/FhirRelatedPersonDaoImplTest.java +++ b/api/src/test/java/org/openmrs/module/fhir2/api/dao/impl/FhirRelatedPersonDaoImplTest.java @@ -31,6 +31,10 @@ public class FhirRelatedPersonDaoImplTest extends BaseModuleContextSensitiveTest private static final String BAD_RELATIONSHIP_UUID = "d4c91630-8563-481b-8efa-48e10c139w6e"; + private static final String PERSON_A_UUID = "61b38324-e2fd-4feb-95b7-9e9a2a4400df"; + + private static final String PERSON_B_UUID = "5c521595-4e12-46b0-8248-b8f2d3697766"; + private static final String RELATIONSHIP_DATA_XML = "org/openmrs/module/fhir2/api/dao/impl/FhirRelatedPersonDaoImplTest_intial_data.xml"; @Autowired @@ -60,4 +64,14 @@ public void getRelationshipWithWrongUuid_shouldReturnNull() { assertThat(relationship, nullValue()); } + @Test + public void getRelationshipWithUuid_shouldReturnPersonAAndPersonB() { + Relationship relationship = relatedPersonDao.get(RELATIONSHIP_UUID); + assertThat(relationship, notNullValue()); + assertThat(relationship.getPersonA(), notNullValue()); + assertThat(relationship.getPersonB(), notNullValue()); + assertThat(relationship.getPersonA().getUuid(), equalTo(PERSON_A_UUID)); + assertThat(relationship.getPersonB().getUuid(), equalTo(PERSON_B_UUID)); + } + }