From a378de06e9ae7d2a5e5ff9b2406b8425b45ac229 Mon Sep 17 00:00:00 2001 From: Basilio Bogado <541149+basiliskus@users.noreply.github.com> Date: Fri, 10 May 2024 07:36:15 -0700 Subject: [PATCH 01/13] Refactor transformation helpers (#1085) * Refactored to create more generic helper functions and move logic to transformation method * Fixed failing tests * Refactored to consolidate hapi helper methods * Fixed test * Moved oml coding contanst to hapi helper --- .../addContactSectionToPatientResource.java | 30 +++- .../custom/addEtorProcessingTag.java | 10 +- .../custom/convertToOmlOrder.java | 4 +- .../hapi/HapiMessageConverterHelper.java | 37 ----- .../hapi/HapiOrderConverterHelper.java | 46 ------ .../FhirBundleHelper.groovy | 7 - ...ontactSectionToPatientResourceTest.groovy} | 60 ++------ ...sformationRuleEngineIntegrationTest.groovy | 21 +-- .../TransformationRuleTest.groovy | 10 +- .../custom/HappyPathMockClass.groovy | 15 +- .../HapiMessageConverterHelperTest.groovy | 111 -------------- .../external/hapi/HapiTestHelper.groovy | 12 -- .../external/hapi/HapiHelper.java | 44 +++++- .../external/hapi/HapiHelperTest.groovy | 144 ++++++++++++++++++ 14 files changed, 262 insertions(+), 289 deletions(-) delete mode 100644 etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiMessageConverterHelper.java delete mode 100644 etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiOrderConverterHelper.java rename etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/{external/hapi/HapiOrderConverterHelperTest.groovy => etor/ruleengine/transformation/AddContactSectionToPatientResourceTest.groovy} (72%) delete mode 100644 etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/hapi/HapiMessageConverterHelperTest.groovy delete mode 100644 etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/hapi/HapiTestHelper.groovy diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/addContactSectionToPatientResource.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/addContactSectionToPatientResource.java index 6ab116edd..942d0528c 100644 --- a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/addContactSectionToPatientResource.java +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/addContactSectionToPatientResource.java @@ -4,10 +4,13 @@ import gov.hhs.cdc.trustedintermediary.etor.metadata.EtorMetadataStep; import gov.hhs.cdc.trustedintermediary.etor.ruleengine.FhirResource; import gov.hhs.cdc.trustedintermediary.etor.ruleengine.transformation.CustomFhirTransformation; -import gov.hhs.cdc.trustedintermediary.external.hapi.HapiOrderConverterHelper; +import gov.hhs.cdc.trustedintermediary.external.hapi.HapiHelper; import gov.hhs.cdc.trustedintermediary.wrappers.MetricMetadata; +import java.util.List; import java.util.Map; import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.Patient; /** Custom transformation to add a contact section to a patient resource. */ public class addContactSectionToPatientResource implements CustomFhirTransformation { @@ -18,7 +21,30 @@ public class addContactSectionToPatientResource implements CustomFhirTransformat @Override public void transform(FhirResource resource, Map args) { Bundle bundle = (Bundle) resource.getUnderlyingResource(); - HapiOrderConverterHelper.addContactSectionToPatientResource(bundle); + + HapiHelper.resourcesInBundle(bundle, Patient.class) + .forEach( + p -> { + var myContact = p.addContact(); + var motherRelationship = myContact.addRelationship(); + motherRelationship.setCoding( + List.of( + new Coding( + "http://terminology.hl7.org/CodeSystem/v3-RoleCode", + "MTH", + "mother"))); + + var mothersMaidenNameExtension = + p.getExtensionByUrl( + "http://hl7.org/fhir/StructureDefinition/patient-mothersMaidenName"); + if (mothersMaidenNameExtension != null) { + myContact.setName( + p.castToHumanName(mothersMaidenNameExtension.getValue())); + } + myContact.setTelecom(p.getTelecom()); + myContact.setAddress(p.getAddressFirstRep()); + }); + metadata.put(bundle.getId(), EtorMetadataStep.CONTACT_SECTION_ADDED_TO_PATIENT); } } diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/addEtorProcessingTag.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/addEtorProcessingTag.java index f01087a8a..ae8b0461b 100644 --- a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/addEtorProcessingTag.java +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/addEtorProcessingTag.java @@ -4,7 +4,7 @@ import gov.hhs.cdc.trustedintermediary.etor.metadata.EtorMetadataStep; import gov.hhs.cdc.trustedintermediary.etor.ruleengine.FhirResource; import gov.hhs.cdc.trustedintermediary.etor.ruleengine.transformation.CustomFhirTransformation; -import gov.hhs.cdc.trustedintermediary.external.hapi.HapiMessageConverterHelper; +import gov.hhs.cdc.trustedintermediary.external.hapi.HapiHelper; import gov.hhs.cdc.trustedintermediary.wrappers.MetricMetadata; import java.util.Map; import org.hl7.fhir.r4.model.Bundle; @@ -18,7 +18,13 @@ public class addEtorProcessingTag implements CustomFhirTransformation { @Override public void transform(FhirResource resource, Map args) { Bundle bundle = (Bundle) resource.getUnderlyingResource(); - HapiMessageConverterHelper.addEtorTagToBundle(bundle); + + var system = "http://localcodes.org/ETOR"; + var code = "ETOR"; + var display = "Processed by ETOR"; + + HapiHelper.addMetaTag(bundle, system, code, display); + metadata.put(bundle.getId(), EtorMetadataStep.ETOR_PROCESSING_TAG_ADDED_TO_MESSAGE_HEADER); } } diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/convertToOmlOrder.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/convertToOmlOrder.java index 80d7dfb02..e869d2056 100644 --- a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/convertToOmlOrder.java +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/convertToOmlOrder.java @@ -4,7 +4,7 @@ import gov.hhs.cdc.trustedintermediary.etor.metadata.EtorMetadataStep; import gov.hhs.cdc.trustedintermediary.etor.ruleengine.FhirResource; import gov.hhs.cdc.trustedintermediary.etor.ruleengine.transformation.CustomFhirTransformation; -import gov.hhs.cdc.trustedintermediary.external.hapi.HapiOrderConverterHelper; +import gov.hhs.cdc.trustedintermediary.external.hapi.HapiHelper; import gov.hhs.cdc.trustedintermediary.wrappers.MetricMetadata; import java.util.Map; import org.hl7.fhir.r4.model.Bundle; @@ -18,7 +18,7 @@ public class convertToOmlOrder implements CustomFhirTransformation { @Override public void transform(FhirResource resource, Map args) { Bundle bundle = (Bundle) resource.getUnderlyingResource(); - HapiOrderConverterHelper.convertToOmlOrder(bundle); + HapiHelper.setMessageTypeCoding(bundle, HapiHelper.OML_CODING); metadata.put(bundle.getId(), EtorMetadataStep.ORDER_CONVERTED_TO_OML); } } diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiMessageConverterHelper.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiMessageConverterHelper.java deleted file mode 100644 index 4547ec78a..000000000 --- a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiMessageConverterHelper.java +++ /dev/null @@ -1,37 +0,0 @@ -package gov.hhs.cdc.trustedintermediary.external.hapi; - -import org.hl7.fhir.r4.model.Bundle; -import org.hl7.fhir.r4.model.Coding; -import org.hl7.fhir.r4.model.MessageHeader; -import org.hl7.fhir.r4.model.Meta; - -/** Helper class with transformation methods that take a FHIR Bundle and modifies it */ -public class HapiMessageConverterHelper { - - private HapiMessageConverterHelper() {} - - public static void addEtorTagToBundle(Bundle messageBundle) { - var messageHeader = findOrInitializeMessageHeader(messageBundle); - var meta = messageHeader.hasMeta() ? messageHeader.getMeta() : new Meta(); - - var systemValue = "http://localcodes.org/ETOR"; - var codeValue = "ETOR"; - var displayValue = "Processed by ETOR"; - - if (meta.getTag(systemValue, codeValue) == null) { - meta.addTag(new Coding(systemValue, codeValue, displayValue)); - } - - messageHeader.setMeta(meta); - } - - public static MessageHeader findOrInitializeMessageHeader(Bundle bundle) { - var messageHeader = - HapiHelper.resourcesInBundle(bundle, MessageHeader.class).findFirst().orElse(null); - if (messageHeader == null) { - messageHeader = new MessageHeader(); - bundle.addEntry(new Bundle.BundleEntryComponent().setResource(messageHeader)); - } - return messageHeader; - } -} diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiOrderConverterHelper.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiOrderConverterHelper.java deleted file mode 100644 index ff69ee0f8..000000000 --- a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiOrderConverterHelper.java +++ /dev/null @@ -1,46 +0,0 @@ -package gov.hhs.cdc.trustedintermediary.external.hapi; - -import java.util.List; -import org.hl7.fhir.r4.model.Bundle; -import org.hl7.fhir.r4.model.Coding; -import org.hl7.fhir.r4.model.Patient; - -/** Helper class with transformation methods that take an order Bundle and modifies it */ -public class HapiOrderConverterHelper { - - private static final Coding OML_CODING = - new Coding( - "http://terminology.hl7.org/CodeSystem/v2-0003", - "O21", - "OML - Laboratory order"); - - private static final List CODING_LIST = - List.of( - new Coding( - "http://terminology.hl7.org/CodeSystem/v3-RoleCode", "MTH", "mother")); - - public static void convertToOmlOrder(Bundle order) { - var messageHeader = HapiMessageConverterHelper.findOrInitializeMessageHeader(order); - messageHeader.setEvent(OML_CODING); - } - - public static void addContactSectionToPatientResource(Bundle order) { - HapiHelper.resourcesInBundle(order, Patient.class) - .forEach( - p -> { - var myContact = p.addContact(); - var motherRelationship = myContact.addRelationship(); - motherRelationship.setCoding(CODING_LIST); - - var mothersMaidenNameExtension = - p.getExtensionByUrl( - "http://hl7.org/fhir/StructureDefinition/patient-mothersMaidenName"); - if (mothersMaidenNameExtension != null) { - myContact.setName( - p.castToHumanName(mothersMaidenNameExtension.getValue())); - } - myContact.setTelecom(p.getTelecom()); - myContact.setAddress(p.getAddressFirstRep()); - }); - } -} diff --git a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/FhirBundleHelper.groovy b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/FhirBundleHelper.groovy index 6c2245bad..a4bdededd 100644 --- a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/FhirBundleHelper.groovy +++ b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/FhirBundleHelper.groovy @@ -30,11 +30,4 @@ class FhirBundleHelper { bundle.addEntry().setFullUrl(receiverOrganizationFullUrl).setResource(receiverOrganization) return bundle } - - static resourceInBundle(Bundle bundle, Class resourceType) { - return bundle.entry.stream() - .map { it.getResource() } - .filter { it.class == resourceType } - .findFirst().orElse(null) - } } diff --git a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/hapi/HapiOrderConverterHelperTest.groovy b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/AddContactSectionToPatientResourceTest.groovy similarity index 72% rename from etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/hapi/HapiOrderConverterHelperTest.groovy rename to etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/AddContactSectionToPatientResourceTest.groovy index fecfd41f2..18c969624 100644 --- a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/hapi/HapiOrderConverterHelperTest.groovy +++ b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/AddContactSectionToPatientResourceTest.groovy @@ -1,22 +1,23 @@ -package gov.hhs.cdc.trustedintermediary.external.hapi +package gov.hhs.cdc.trustedintermediary.etor.ruleengine.transformation +import gov.hhs.cdc.trustedintermediary.etor.ruleengine.transformation.custom.addContactSectionToPatientResource import gov.hhs.cdc.trustedintermediary.OrderMock import gov.hhs.cdc.trustedintermediary.context.TestApplicationContext - +import gov.hhs.cdc.trustedintermediary.external.hapi.HapiFhirResource +import gov.hhs.cdc.trustedintermediary.external.hapi.HapiHelper +import gov.hhs.cdc.trustedintermediary.wrappers.MetricMetadata import org.hl7.fhir.r4.model.Address +import org.hl7.fhir.r4.model.Bundle import org.hl7.fhir.r4.model.ContactPoint import org.hl7.fhir.r4.model.Extension import org.hl7.fhir.r4.model.HumanName -import org.hl7.fhir.r4.model.StringType - -import org.hl7.fhir.r4.model.Bundle -import org.hl7.fhir.r4.model.Coding -import org.hl7.fhir.r4.model.MessageHeader import org.hl7.fhir.r4.model.Patient +import org.hl7.fhir.r4.model.StringType import spock.lang.Specification -class HapiOrderConverterHelperTest extends Specification { +class AddContactSectionToPatientResourceTest extends Specification { + def transformClass Patient mockPatient Bundle mockOrderBundle OrderMock mockOrder @@ -24,46 +25,15 @@ class HapiOrderConverterHelperTest extends Specification { def setup() { TestApplicationContext.reset() TestApplicationContext.init() - TestApplicationContext.register(HapiMessageHelper, HapiMessageHelper.getInstance()) + TestApplicationContext.register(MetricMetadata, Mock(MetricMetadata)) TestApplicationContext.injectRegisteredImplementations() mockPatient = new Patient() mockOrderBundle = new Bundle().addEntry(new Bundle.BundleEntryComponent().setResource(mockPatient)) mockOrder = new OrderMock("fhirResourceId", "patientId", mockOrderBundle, null, null, null, null, null) - } - - def "convert the pre-existing message header to specify OML"() { - given: - mockOrderBundle.addEntry( - new Bundle.BundleEntryComponent().setResource( - new MessageHeader().setEvent(new Coding( - "http://terminology.hl7.org/CodeSystem/v2-0003", - "O01", - "ORM")))) - when: - HapiOrderConverterHelper.convertToOmlOrder(mockOrder.getUnderlyingResource()) - - then: - def convertedMessageHeader = - HapiHelper.resourcesInBundle(mockOrder.getUnderlyingResource(), MessageHeader.class).findFirst().orElse(null) - - convertedMessageHeader != null - convertedMessageHeader.getEventCoding().getCode() == "O21" - convertedMessageHeader.getEventCoding().getDisplay().contains("OML") - } - - def "adds the message header to specify OML"() { - when: - HapiOrderConverterHelper.convertToOmlOrder(mockOrder.getUnderlyingResource()) - - then: - def convertedMessageHeader = - HapiHelper.resourcesInBundle(mockOrder.getUnderlyingResource(), MessageHeader.class).findFirst().orElse(null) - convertedMessageHeader != null - convertedMessageHeader.getEventCoding().getCode() == "O21" - convertedMessageHeader.getEventCoding().getDisplay().contains("OML") + transformClass = new addContactSectionToPatientResource() } def "add contact section to patient resource"() { @@ -80,10 +50,10 @@ class HapiOrderConverterHelperTest extends Specification { mockOrderBundle.setEntry(entryList) when: - HapiOrderConverterHelper.addContactSectionToPatientResource(mockOrder.getUnderlyingResource()) + transformClass.transform(new HapiFhirResource(mockOrder.getUnderlyingResource()), null) then: - def convertedPatient = HapiHelper.resourcesInBundle(mockOrder.getUnderlyingResource(), Patient.class).findFirst().orElse(null) + def convertedPatient = HapiHelper.resourceInBundle(mockOrder.getUnderlyingResource(), Patient.class) as Patient def contactSection = convertedPatient.getContact()[0] contactSection != null @@ -124,10 +94,10 @@ class HapiOrderConverterHelperTest extends Specification { mockOrderBundle.setEntry(entryList) when: - HapiOrderConverterHelper.addContactSectionToPatientResource(mockOrderBundle) + transformClass.transform(new HapiFhirResource(mockOrder.getUnderlyingResource()), null) then: - def convertedPatient = HapiHelper.resourcesInBundle(mockOrderBundle, Patient.class).findFirst().orElse(null) + def convertedPatient = HapiHelper.resourceInBundle(mockOrderBundle, Patient.class) as Patient def contactSection = convertedPatient.getContact().first() !contactSection.hasName() diff --git a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/TransformationRuleEngineIntegrationTest.groovy b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/TransformationRuleEngineIntegrationTest.groovy index d86f55d3d..7dab2cb8c 100644 --- a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/TransformationRuleEngineIntegrationTest.groovy +++ b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/TransformationRuleEngineIntegrationTest.groovy @@ -7,6 +7,7 @@ import gov.hhs.cdc.trustedintermediary.context.TestApplicationContext import gov.hhs.cdc.trustedintermediary.etor.ruleengine.RuleLoader import gov.hhs.cdc.trustedintermediary.external.hapi.HapiFhirImplementation import gov.hhs.cdc.trustedintermediary.external.hapi.HapiFhirResource +import gov.hhs.cdc.trustedintermediary.external.hapi.HapiHelper import gov.hhs.cdc.trustedintermediary.external.jackson.Jackson import gov.hhs.cdc.trustedintermediary.wrappers.HapiFhir import gov.hhs.cdc.trustedintermediary.wrappers.Logger @@ -69,11 +70,11 @@ class TransformationRuleEngineIntegrationTest extends Specification { def rule = RuleEngineHelper.getRuleByName(engine.rules, ruleName) expect: - FhirBundleHelper.resourceInBundle(bundle, MessageHeader) == null + HapiHelper.resourceInBundle(bundle, MessageHeader) == null when: rule.runRule(new HapiFhirResource(bundle)) - def messageHeader = FhirBundleHelper.resourceInBundle(bundle, MessageHeader) + def messageHeader = HapiHelper.resourceInBundle(bundle, MessageHeader) then: 0 * mockLogger.logError(_ as String, _ as Exception) @@ -88,11 +89,11 @@ class TransformationRuleEngineIntegrationTest extends Specification { def rule = RuleEngineHelper.getRuleByName(engine.rules, ruleName) expect: - FhirBundleHelper.resourceInBundle(bundle, MessageHeader).event.code == 'O01' + HapiHelper.resourceInBundle(bundle, MessageHeader).event.code == 'O01' when: rule.runRule(new HapiFhirResource(bundle)) - def messageHeader = FhirBundleHelper.resourceInBundle(bundle, MessageHeader) + def messageHeader = HapiHelper.resourceInBundle(bundle, MessageHeader) as MessageHeader then: 0 * mockLogger.logError(_ as String, _ as Exception) @@ -108,11 +109,11 @@ class TransformationRuleEngineIntegrationTest extends Specification { def rule = RuleEngineHelper.getRuleByName(engine.rules, ruleName) expect: - FhirBundleHelper.resourceInBundle(bundle, Patient).contact.isEmpty() + HapiHelper.resourceInBundle(bundle, Patient).contact.isEmpty() when: rule.runRule(new HapiFhirResource(bundle)) - def patient = FhirBundleHelper.resourceInBundle(bundle, Patient) + def patient = HapiHelper.resourceInBundle(bundle, Patient) then: 0 * mockLogger.logError(_ as String, _ as Exception) @@ -130,16 +131,16 @@ class TransformationRuleEngineIntegrationTest extends Specification { def bundle = (Bundle) fhirResource.getUnderlyingResource() expect: - FhirBundleHelper.resourceInBundle(bundle, MessageHeader).event.code == 'O01' - FhirBundleHelper.resourceInBundle(bundle, Patient).contact.isEmpty() + HapiHelper.resourceInBundle(bundle, MessageHeader).event.code == 'O01' + HapiHelper.resourceInBundle(bundle, Patient).contact.isEmpty() when: transformationsToApply.each { ruleName -> def rule = RuleEngineHelper.getRuleByName(engine.rules, ruleName) rule.runRule(fhirResource) } - def messageHeader = FhirBundleHelper.resourceInBundle(bundle, MessageHeader) - def patient = FhirBundleHelper.resourceInBundle(bundle, Patient) + def messageHeader = HapiHelper.resourceInBundle(bundle, MessageHeader) as MessageHeader + def patient = HapiHelper.resourceInBundle(bundle, Patient) as Patient then: 0 * mockLogger.logError(_ as String, _ as Exception) diff --git a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/TransformationRuleTest.groovy b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/TransformationRuleTest.groovy index 95e5f7e0f..325843f9b 100644 --- a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/TransformationRuleTest.groovy +++ b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/TransformationRuleTest.groovy @@ -3,7 +3,7 @@ package gov.hhs.cdc.trustedintermediary.etor.ruleengine.transformation import gov.hhs.cdc.trustedintermediary.FhirBundleHelper import gov.hhs.cdc.trustedintermediary.FhirResourceMock import gov.hhs.cdc.trustedintermediary.context.TestApplicationContext -import gov.hhs.cdc.trustedintermediary.external.hapi.HapiTestHelper +import gov.hhs.cdc.trustedintermediary.external.hapi.HapiHelper import gov.hhs.cdc.trustedintermediary.wrappers.HapiFhir import gov.hhs.cdc.trustedintermediary.wrappers.Logger import org.hl7.fhir.r4.model.Bundle @@ -55,15 +55,15 @@ class TransformationRuleTest extends Specification { ] TestApplicationContext.register(HapiFhir, Mock(HapiFhir)) - when: def rule = new TransformationRule(ruleName, ruleDescription, ruleMessage, ruleConditions, ruleActions) def fhirResource = new FhirResourceMock(FhirBundleHelper.createMessageBundle(new HashMap())) + + when: rule.runRule(fhirResource) then: - def messageHeaderStream = HapiTestHelper.resourceInBundle(fhirResource.getUnderlyingResource() as Bundle, MessageHeader.class) - def actualMessageHeader = messageHeaderStream.filter{resource -> resource.getEventCoding().getCode().equals("mock_code")}.findFirst().orElse(null) - actualMessageHeader.getEventCoding().getCode() == "mock_code" + def messageHeader = HapiHelper.resourceInBundle(fhirResource.getUnderlyingResource() as Bundle, MessageHeader.class) as MessageHeader + messageHeader.getEventCoding().getCode() == "mock_code" } def "runRule() throws RuntimeException when an invalid class is given as input"() { diff --git a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/HappyPathMockClass.groovy b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/HappyPathMockClass.groovy index 561c69b55..5cdfa98ab 100644 --- a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/HappyPathMockClass.groovy +++ b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/HappyPathMockClass.groovy @@ -2,6 +2,7 @@ package gov.hhs.cdc.trustedintermediary.etor.ruleengine.transformation.custom import gov.hhs.cdc.trustedintermediary.etor.ruleengine.FhirResource import gov.hhs.cdc.trustedintermediary.etor.ruleengine.transformation.CustomFhirTransformation +import gov.hhs.cdc.trustedintermediary.external.hapi.HapiHelper import org.hl7.fhir.r4.model.Bundle import org.hl7.fhir.r4.model.Coding import org.hl7.fhir.r4.model.MessageHeader @@ -11,14 +12,12 @@ class HappyPathMockClass implements CustomFhirTransformation { @Override public void transform(final FhirResource resource, final Map args) { Bundle bundle = (Bundle) resource.getUnderlyingResource() - MessageHeader messageHeader = new MessageHeader() - String messageTypeCode = "mock_code" - Coding eventCoding = new Coding() - eventCoding.setCode(messageTypeCode) - eventCoding.setDisplay(String.format("mock^code^%s", messageTypeCode)) - eventCoding.setSystem("http://terminology.hl7.org/CodeSystem/v2-0003") - messageHeader.setEvent(eventCoding) - bundle.addEntry(new Bundle.BundleEntryComponent().setResource(messageHeader)) + def system = "http://terminology.hl7.org/CodeSystem/v2-0003" + def code = "mock_code" + def display = String.format("mock^code^%s", code) + + Coding eventCoding = new Coding(system, code, display) + HapiHelper.setMessageTypeCoding(bundle, eventCoding) } } diff --git a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/hapi/HapiMessageConverterHelperTest.groovy b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/hapi/HapiMessageConverterHelperTest.groovy deleted file mode 100644 index 12e60c49c..000000000 --- a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/hapi/HapiMessageConverterHelperTest.groovy +++ /dev/null @@ -1,111 +0,0 @@ -package gov.hhs.cdc.trustedintermediary.external.hapi - - -import gov.hhs.cdc.trustedintermediary.context.TestApplicationContext -import org.hl7.fhir.r4.model.Bundle -import org.hl7.fhir.r4.model.Coding -import org.hl7.fhir.r4.model.MessageHeader -import org.hl7.fhir.r4.model.Meta -import org.hl7.fhir.r4.model.Patient -import spock.lang.Specification - -class HapiMessageConverterHelperTest extends Specification { - - Bundle mockBundle - Patient mockPatient - def expectedSystem = "http://localcodes.org/ETOR" - def expectedCode = "ETOR" - def expectedDisplay = "Processed by ETOR" - - def setup() { - mockBundle = null - TestApplicationContext.reset() - TestApplicationContext.init() - TestApplicationContext.injectRegisteredImplementations() - - mockPatient = new Patient() - mockBundle = new Bundle().addEntry(new Bundle.BundleEntryComponent().setResource(mockPatient)) - } - - def "addEtorTag adds the ETOR message header tag to any Bundle"() { - given: - def messageHeader = new MessageHeader() - messageHeader.setId(UUID.randomUUID().toString()) - def messageHeaderEntry = new Bundle.BundleEntryComponent().setResource(messageHeader) - mockBundle.getEntry().add(messageHeaderEntry) - - when: - HapiMessageConverterHelper.addEtorTagToBundle(mockBundle) - - then: - def messageHeaders = mockBundle.getEntry().get(1).getResource() as MessageHeader - def actualMessageTag = messageHeaders.getMeta().getTag()[0] - - actualMessageTag.getSystem() == expectedSystem - actualMessageTag.getCode() == expectedCode - actualMessageTag.getDisplay() == expectedDisplay - } - - def "addEtorTag adds the ETOR message header tag to any Bundle when message header is missing"() { - when: - HapiMessageConverterHelper.addEtorTagToBundle(mockBundle) - - then: - def messageHeaders = mockBundle.getEntry().get(1).getResource() as MessageHeader - def actualMessageTag = messageHeaders.getMeta().getTag()[0] - - actualMessageTag.getSystem() == expectedSystem - actualMessageTag.getCode() == expectedCode - actualMessageTag.getDisplay() == expectedDisplay - } - - def "addEtorTag adds the ETOR message header tag to any Bundle with existing Meta tag"() { - given: - def firstExpectedSystem = "a system" - def firstExpectedCode = "test" - def firstExpectedDisplay = "Processed by tests" - - def messageHeader = new MessageHeader() - messageHeader.setId(UUID.randomUUID().toString()) - def meta = new Meta() - meta.addTag(new Coding(firstExpectedSystem, firstExpectedCode, firstExpectedDisplay)) - messageHeader.setMeta(meta) - def messageHeaderEntry = new Bundle.BundleEntryComponent().setResource(messageHeader) - mockBundle.getEntry().add(messageHeaderEntry) - - when: - HapiMessageConverterHelper.addEtorTagToBundle(mockBundle) - - then: - def messageHeaders = mockBundle.getEntry().get(1).getResource() as MessageHeader - def firstActualMessageTag = messageHeaders.getMeta().getTag()[0] - def secondActualMessageTag = messageHeaders.getMeta().getTag()[1] - - firstActualMessageTag.getSystem() == firstExpectedSystem - firstActualMessageTag.getCode() == firstExpectedCode - firstActualMessageTag.getDisplay() == firstExpectedDisplay - - secondActualMessageTag.getSystem() == expectedSystem - secondActualMessageTag.getCode() == expectedCode - secondActualMessageTag.getDisplay() == expectedDisplay - } - - - def "addEtorTag adds the ETOR header tag only once"() { - given: - def etorTag = new Coding(expectedSystem, expectedCode, expectedDisplay) - - def messageHeader = new MessageHeader() - messageHeader.setId(UUID.randomUUID().toString()) - def messageHeaderEntry = new Bundle.BundleEntryComponent().setResource(messageHeader) - mockBundle.getEntry().add(messageHeaderEntry) - - when: - HapiMessageConverterHelper.addEtorTagToBundle(mockBundle) - messageHeader.getMeta().getTag().findAll {it.system == etorTag.system}.size() == 1 - HapiMessageConverterHelper.addEtorTagToBundle(mockBundle) - - then: - messageHeader.getMeta().getTag().findAll {it.system == etorTag.system}.size() == 1 - } -} diff --git a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/hapi/HapiTestHelper.groovy b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/hapi/HapiTestHelper.groovy deleted file mode 100644 index dfb5ba9e8..000000000 --- a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/hapi/HapiTestHelper.groovy +++ /dev/null @@ -1,12 +0,0 @@ -package gov.hhs.cdc.trustedintermediary.external.hapi - -import org.hl7.fhir.r4.model.Bundle -import org.hl7.fhir.r4.model.Resource - -import java.util.stream.Stream - -class HapiTestHelper { - static Stream resourceInBundle(Bundle bundle, Class resourceType) { - return HapiHelper.resourcesInBundle(bundle, resourceType) - } -} diff --git a/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiHelper.java b/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiHelper.java index 9f4854509..1ab275c6e 100644 --- a/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiHelper.java +++ b/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiHelper.java @@ -2,13 +2,22 @@ import java.util.stream.Stream; import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.MessageHeader; +import org.hl7.fhir.r4.model.Meta; import org.hl7.fhir.r4.model.Resource; /** Helper class that works on HapiFHIR constructs. */ -class HapiHelper { // not a public class so it is package-private (meaning only other classes in - // this package can access it +public class HapiHelper { + private HapiHelper() {} + public static final Coding OML_CODING = + new Coding( + "http://terminology.hl7.org/CodeSystem/v2-0003", + "O21", + "OML - Laboratory order"); + /** * Returns a {@link Stream} of FHIR resources inside the provided {@link Bundle} that match the * given resource type. @@ -25,4 +34,35 @@ public static Stream resourcesInBundle( .filter(resource -> resource.getClass().equals(resourceType)) .map(resource -> ((T) resource)); } + + public static Resource resourceInBundle( + Bundle bundle, Class resourceType) { + return resourcesInBundle(bundle, resourceType).findFirst().orElse(null); + } + + public static void addMetaTag( + Bundle messageBundle, String system, String code, String display) { + var messageHeader = findOrInitializeMessageHeader(messageBundle); + var meta = messageHeader.hasMeta() ? messageHeader.getMeta() : new Meta(); + + if (meta.getTag(system, code) == null) { + meta.addTag(new Coding(system, code, display)); + } + + messageHeader.setMeta(meta); + } + + public static void setMessageTypeCoding(Bundle order, Coding coding) { + var messageHeader = findOrInitializeMessageHeader(order); + messageHeader.setEvent(coding); + } + + public static MessageHeader findOrInitializeMessageHeader(Bundle bundle) { + var messageHeader = resourceInBundle(bundle, MessageHeader.class); + if (messageHeader == null) { + messageHeader = new MessageHeader(); + bundle.addEntry(new Bundle.BundleEntryComponent().setResource(messageHeader)); + } + return (MessageHeader) messageHeader; + } } diff --git a/shared/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/hapi/HapiHelperTest.groovy b/shared/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/hapi/HapiHelperTest.groovy index e63854271..4c5180c8e 100644 --- a/shared/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/hapi/HapiHelperTest.groovy +++ b/shared/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/hapi/HapiHelperTest.groovy @@ -1,6 +1,9 @@ package gov.hhs.cdc.trustedintermediary.external.hapi import org.hl7.fhir.r4.model.Bundle +import org.hl7.fhir.r4.model.Coding +import org.hl7.fhir.r4.model.MessageHeader +import org.hl7.fhir.r4.model.Meta import org.hl7.fhir.r4.model.Patient import org.hl7.fhir.r4.model.Provenance import org.hl7.fhir.r4.model.ResourceType @@ -8,6 +11,7 @@ import org.hl7.fhir.r4.model.ServiceRequest import spock.lang.Specification class HapiHelperTest extends Specification { + def "resourcesInBundle return a stream of a specific type of resources in a FHIR Bundle"() { given: def patients = [ @@ -37,4 +41,144 @@ class HapiHelperTest extends Specification { then: patientStream.allMatch {patients.contains(it) && it.getResourceType() == ResourceType.Patient } } + + def "addMetaTag adds message header tag to any Bundle"() { + given: + def expectedSystem = "expectedSystem" + def expectedCode = "expectedCode" + def expectedDisplay = "expectedDisplay" + + def mockBundle = new Bundle() + HapiHelper.findOrInitializeMessageHeader(mockBundle) + + when: + HapiHelper.addMetaTag(mockBundle, expectedSystem, expectedCode, expectedDisplay) + + then: + def messageHeaders = HapiHelper.resourceInBundle(mockBundle, MessageHeader) + def actualMessageTag = messageHeaders.getMeta().getTag()[0] + + actualMessageTag.getSystem() == expectedSystem + actualMessageTag.getCode() == expectedCode + actualMessageTag.getDisplay() == expectedDisplay + } + + def "addMetaTag adds message header tag to any Bundle when message header is missing"() { + given: + def expectedSystem = "expectedSystem" + def expectedCode = "expectedCode" + def expectedDisplay = "expectedDisplay" + def mockBundle = new Bundle() + + when: + HapiHelper.addMetaTag(mockBundle, expectedSystem, expectedCode, expectedDisplay) + + then: + def messageHeaders = HapiHelper.resourceInBundle(mockBundle, MessageHeader) + def actualMessageTag = messageHeaders.getMeta().getTag()[0] + + actualMessageTag.getSystem() == expectedSystem + actualMessageTag.getCode() == expectedCode + actualMessageTag.getDisplay() == expectedDisplay + } + + def "addMetaTag adds the message header tag to any Bundle with existing Meta tag"() { + given: + def firstExpectedSystem = "firstExpectedSystem" + def firstExpectedCode = "firstExpectedCode" + def firstExpectedDisplay = "firstExpectedDisplay" + def expectedSystem = "expectedSystem" + def expectedCode = "expectedCode" + def expectedDisplay = "expectedDisplay" + + def mockBundle = new Bundle() + def messageHeader = new MessageHeader() + messageHeader.setId(UUID.randomUUID().toString()) + def meta = new Meta() + meta.addTag(new Coding(firstExpectedSystem, firstExpectedCode, firstExpectedDisplay)) + messageHeader.setMeta(meta) + def messageHeaderEntry = new Bundle.BundleEntryComponent().setResource(messageHeader) + mockBundle.getEntry().add(messageHeaderEntry) + + when: + HapiHelper.addMetaTag(mockBundle, expectedSystem, expectedCode, expectedDisplay) + + then: + def messageHeaders = HapiHelper.resourceInBundle(mockBundle, MessageHeader) + def firstActualMessageTag = messageHeaders.getMeta().getTag()[0] + def secondActualMessageTag = messageHeaders.getMeta().getTag()[1] + + firstActualMessageTag.getSystem() == firstExpectedSystem + firstActualMessageTag.getCode() == firstExpectedCode + firstActualMessageTag.getDisplay() == firstExpectedDisplay + + secondActualMessageTag.getSystem() == expectedSystem + secondActualMessageTag.getCode() == expectedCode + secondActualMessageTag.getDisplay() == expectedDisplay + } + + def "addMetaTag adds the message header tag only once"() { + given: + def expectedSystem = "expectedSystem" + def expectedCode = "expectedCode" + def expectedDisplay = "expectedDisplay" + def mockBundle = new Bundle() + def etorTag = new Coding(expectedSystem, expectedCode, expectedDisplay) + + def messageHeader = new MessageHeader() + messageHeader.setId(UUID.randomUUID().toString()) + def messageHeaderEntry = new Bundle.BundleEntryComponent().setResource(messageHeader) + mockBundle.getEntry().add(messageHeaderEntry) + + when: + HapiHelper.addMetaTag(mockBundle, expectedSystem, expectedCode, expectedDisplay) + messageHeader.getMeta().getTag().findAll {it.system == etorTag.system}.size() == 1 + HapiHelper.addMetaTag(mockBundle, expectedSystem, expectedCode, expectedDisplay) + + then: + messageHeader.getMeta().getTag().findAll {it.system == etorTag.system}.size() == 1 + } + + def "convert the pre-existing message type"() { + given: + def expectedSystem = "expectedSystem" + def expectedCode = "expectedCode" + def expectedDisplay = "expectedDisplay" + def expectedCoding = new Coding(expectedSystem, expectedCode, expectedDisplay) + def coding = new Coding("system", "code", "display") + def mockBundle = new Bundle() + mockBundle.addEntry( + new Bundle.BundleEntryComponent().setResource( + new MessageHeader().setEvent(coding))) + + when: + HapiHelper.setMessageTypeCoding(mockBundle, expectedCoding) + def convertedMessageHeader = HapiHelper.resourceInBundle(mockBundle, MessageHeader.class) as MessageHeader + + then: + convertedMessageHeader != null + convertedMessageHeader.getEventCoding().getSystem() == expectedSystem + convertedMessageHeader.getEventCoding().getCode() == expectedCode + convertedMessageHeader.getEventCoding().getDisplay() == expectedDisplay + } + + def "adds the message type when it doesn't exist"() { + given: + def expectedSystem = "expectedSystem" + def expectedCode = "expectedCode" + def expectedDisplay = "expectedDisplay" + def expectedCoding = new Coding(expectedSystem, expectedCode, expectedDisplay) + def mockBundle = new Bundle() + + when: + HapiHelper.setMessageTypeCoding(mockBundle, expectedCoding) + def convertedMessageHeader = + HapiHelper.resourceInBundle(mockBundle, MessageHeader.class) as MessageHeader + + then: + convertedMessageHeader != null + convertedMessageHeader.getEventCoding().getSystem() == expectedSystem + convertedMessageHeader.getEventCoding().getCode() == expectedCode + convertedMessageHeader.getEventCoding().getDisplay() == expectedDisplay + } } From a98e69fdd00ffaa95cf6a8cf96361caaf521c9d7 Mon Sep 17 00:00:00 2001 From: tjohnson7021 <86614374+tjohnson7021@users.noreply.github.com> Date: Fri, 10 May 2024 11:25:02 -0400 Subject: [PATCH 02/13] Modified FHIR Path Mapping enum to include HL7v2 values - Updated references to fhir paths in other places - created placeholders for HL7v2 values --- .../external/hapi/HapiMessageHelper.java | 28 ++++++------ .../path/{FhirPath.java => MappingPath.java} | 45 +++++++++++-------- 2 files changed, 40 insertions(+), 33 deletions(-) rename etor/src/main/java/gov/hhs/cdc/trustedintermediary/plugin/path/{FhirPath.java => MappingPath.java} (83%) diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiMessageHelper.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiMessageHelper.java index d117e4d33..8c0a3cedc 100644 --- a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiMessageHelper.java +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiMessageHelper.java @@ -1,6 +1,6 @@ package gov.hhs.cdc.trustedintermediary.external.hapi; -import gov.hhs.cdc.trustedintermediary.plugin.path.FhirPath; +import gov.hhs.cdc.trustedintermediary.plugin.path.MappingPath; import gov.hhs.cdc.trustedintermediary.wrappers.HapiFhir; import javax.inject.Inject; import org.hl7.fhir.r4.model.Bundle; @@ -24,66 +24,66 @@ private HapiMessageHelper() {} public String extractPlacerOrderNumber(Bundle messageBundle) { return fhirEngine.getStringFromFhirPath( - messageBundle, FhirPath.PLACER_ORDER_NUMBER.getPath()); + messageBundle, MappingPath.PLACER_ORDER_NUMBER.getFhirPath()); } public String extractSendingApplicationNamespace(Bundle messageBundle) { return fhirEngine.getStringFromFhirPath( - messageBundle, FhirPath.SENDING_APPLICATION_NAMESPACE.getPath()); + messageBundle, MappingPath.SENDING_APPLICATION_NAMESPACE.getFhirPath()); } public String extractSendingApplicationUniversalId(Bundle messageBundle) { return fhirEngine.getStringFromFhirPath( - messageBundle, FhirPath.SENDING_APPLICATION_UNIVERSAL_ID.getPath()); + messageBundle, MappingPath.SENDING_APPLICATION_UNIVERSAL_ID.getFhirPath()); } public String extractSendingApplicationUniversalIdType(Bundle messageBundle) { return fhirEngine.getStringFromFhirPath( - messageBundle, FhirPath.SENDING_APPLICATION_UNIVERSAL_ID_TYPE.getPath()); + messageBundle, MappingPath.SENDING_APPLICATION_UNIVERSAL_ID_TYPE.getFhirPath()); } public String extractSendingFacilityNamespace(Bundle messageBundle) { return fhirEngine.getStringFromFhirPath( - messageBundle, FhirPath.SENDING_FACILITY_NAMESPACE.getPath()); + messageBundle, MappingPath.SENDING_FACILITY_NAMESPACE.getFhirPath()); } public String extractSendingFacilityUniversalId(Bundle messageBundle) { return fhirEngine.getStringFromFhirPath( - messageBundle, FhirPath.SENDING_FACILITY_UNIVERSAL_ID.getPath()); + messageBundle, MappingPath.SENDING_FACILITY_UNIVERSAL_ID.getFhirPath()); } public String extractSendingFacilityUniversalIdType(Bundle messageBundle) { return fhirEngine.getStringFromFhirPath( - messageBundle, FhirPath.SENDING_FACILITY_UNIVERSAL_ID_TYPE.getPath()); + messageBundle, MappingPath.SENDING_FACILITY_UNIVERSAL_ID_TYPE.getFhirPath()); } public String extractReceivingApplicationNamespace(Bundle messageBundle) { return fhirEngine.getStringFromFhirPath( - messageBundle, FhirPath.RECEIVING_APPLICATION_NAMESPACE.getPath()); + messageBundle, MappingPath.RECEIVING_APPLICATION_NAMESPACE.getFhirPath()); } public String extractReceivingApplicationUniversalId(Bundle messageBundle) { return fhirEngine.getStringFromFhirPath( - messageBundle, FhirPath.RECEIVING_APPLICATION_UNIVERSAL_ID.getPath()); + messageBundle, MappingPath.RECEIVING_APPLICATION_UNIVERSAL_ID.getFhirPath()); } public String extractReceivingApplicationUniversalIdType(Bundle messageBundle) { return fhirEngine.getStringFromFhirPath( - messageBundle, FhirPath.RECEIVING_APPLICATION_UNIVERSAL_ID_TYPE.getPath()); + messageBundle, MappingPath.RECEIVING_APPLICATION_UNIVERSAL_ID_TYPE.getFhirPath()); } public String extractReceivingFacilityNamespace(Bundle messageBundle) { return fhirEngine.getStringFromFhirPath( - messageBundle, FhirPath.RECEIVING_FACILITY_NAMESPACE.getPath()); + messageBundle, MappingPath.RECEIVING_FACILITY_NAMESPACE.getFhirPath()); } public String extractReceivingFacilityUniversalId(Bundle messageBundle) { return fhirEngine.getStringFromFhirPath( - messageBundle, FhirPath.RECEIVING_FACILITY_UNIVERSAL_ID.getPath()); + messageBundle, MappingPath.RECEIVING_FACILITY_UNIVERSAL_ID.getFhirPath()); } public String extractReceivingFacilityUniversalIdType(Bundle messageBundle) { return fhirEngine.getStringFromFhirPath( - messageBundle, FhirPath.RECEIVING_FACILITY_UNIVERSAL_ID_TYPE.getPath()); + messageBundle, MappingPath.RECEIVING_FACILITY_UNIVERSAL_ID_TYPE.getFhirPath()); } } diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/plugin/path/FhirPath.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/plugin/path/MappingPath.java similarity index 83% rename from etor/src/main/java/gov/hhs/cdc/trustedintermediary/plugin/path/FhirPath.java rename to etor/src/main/java/gov/hhs/cdc/trustedintermediary/plugin/path/MappingPath.java index c28bbf6e8..7efa44c36 100644 --- a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/plugin/path/FhirPath.java +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/plugin/path/MappingPath.java @@ -5,85 +5,92 @@ * be used to extract specific pieces of data from a FHIR message, such as identifiers, namespaces, * and codes related to sending and receiving facilities and applications. */ -public enum FhirPath { +public enum MappingPath { PLACER_ORDER_NUMBER( - """ + "ORC-2", """ Bundle.entry.resource.ofType(ServiceRequest).identifier.where(type.coding.code = 'PLAC').value """), SENDING_FACILITY_NAMESPACE( - """ + "?-?", """ Bundle.entry.resource.ofType(MessageHeader).sender.resolve().identifier.where( extension.url = 'https://reportstream.cdc.gov/fhir/StructureDefinition/hl7v2Field' and extension.value = 'HD.1' ).value """), SENDING_FACILITY_UNIVERSAL_ID( - """ + "?-?",""" Bundle.entry.resource.ofType(MessageHeader).sender.resolve().identifier.where( extension.url = 'https://reportstream.cdc.gov/fhir/StructureDefinition/hl7v2Field' and extension.value = 'HD.2,HD.3' ).value """), SENDING_FACILITY_UNIVERSAL_ID_TYPE( - """ + "?-?",""" Bundle.entry.resource.ofType(MessageHeader).sender.resolve().identifier.where( extension.url = 'https://reportstream.cdc.gov/fhir/StructureDefinition/hl7v2Field' and extension.value = 'HD.2,HD.3' ).type.coding.code """), SENDING_APPLICATION_NAMESPACE( - """ + "?-?",""" Bundle.entry.resource.ofType(MessageHeader).source.extension.where(url = 'https://reportstream.cdc.gov/fhir/StructureDefinition/namespace-id').value """), SENDING_APPLICATION_UNIVERSAL_ID( - """ + "?-?",""" Bundle.entry.resource.ofType(MessageHeader).source.extension.where(url = 'https://reportstream.cdc.gov/fhir/StructureDefinition/universal-id').value """), SENDING_APPLICATION_UNIVERSAL_ID_TYPE( - """ + "?-?",""" Bundle.entry.resource.ofType(MessageHeader).source.extension.where(url = 'https://reportstream.cdc.gov/fhir/StructureDefinition/universal-id-type').value """), RECEIVING_FACILITY_NAMESPACE( - """ + "?-?",""" Bundle.entry.resource.ofType(MessageHeader).destination.receiver.resolve().identifier.where( extension.url = 'https://reportstream.cdc.gov/fhir/StructureDefinition/hl7v2Field' and extension.value = 'HD.1' ).value """), RECEIVING_FACILITY_UNIVERSAL_ID( - """ + "?-?",""" Bundle.entry.resource.ofType(MessageHeader).destination.receiver.resolve().identifier.where( extension.url = 'https://reportstream.cdc.gov/fhir/StructureDefinition/hl7v2Field' and extension.value = 'HD.2,HD.3' ).value """), RECEIVING_FACILITY_UNIVERSAL_ID_TYPE( - """ + "?-?",""" Bundle.entry.resource.ofType(MessageHeader).destination.receiver.resolve().identifier.where( extension.url = 'https://reportstream.cdc.gov/fhir/StructureDefinition/hl7v2Field' and extension.value = 'HD.2,HD.3' ).type.coding.code """), RECEIVING_APPLICATION_NAMESPACE( - """ + "?-?",""" Bundle.entry.resource.ofType(MessageHeader).destination.name """), RECEIVING_APPLICATION_UNIVERSAL_ID( - """ + "?-?",""" Bundle.entry.resource.ofType(MessageHeader).destination.extension.where(url = 'https://reportstream.cdc.gov/fhir/StructureDefinition/universal-id').value """), RECEIVING_APPLICATION_UNIVERSAL_ID_TYPE( - """ + "?-?",""" Bundle.entry.resource.ofType(MessageHeader).destination.extension.where(url = 'https://reportstream.cdc.gov/fhir/StructureDefinition/universal-id-type').value """); - private final String path; + private final String fhirPath; + private final String hl7v2Path; - FhirPath(String path) { - this.path = path; + MappingPath(String hl7v2Path, String fhirPath) { + this.hl7v2Path = hl7v2Path; + this.fhirPath = fhirPath; } - public String getPath() { - return path; + public String getHl7v2Path() { + return hl7v2Path; } + + public String getFhirPath() { + return fhirPath; + } +} } From 5a7e138e33e088f725daa6cec0066a0cc82d9999 Mon Sep 17 00:00:00 2001 From: tjohnson7021 <86614374+tjohnson7021@users.noreply.github.com> Date: Fri, 10 May 2024 11:54:07 -0400 Subject: [PATCH 03/13] Rename file --- .../hhs/cdc/trustedintermediary/plugin/path/MappingPath.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/plugin/path/MappingPath.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/plugin/path/MappingPath.java index 7efa44c36..cb36fd13c 100644 --- a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/plugin/path/MappingPath.java +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/plugin/path/MappingPath.java @@ -93,4 +93,4 @@ public String getFhirPath() { return fhirPath; } } -} + From c9ae7034eac0f99b608946b204bb96e135a5dd28 Mon Sep 17 00:00:00 2001 From: tjohnson7021 <86614374+tjohnson7021@users.noreply.github.com> Date: Fri, 10 May 2024 12:06:43 -0400 Subject: [PATCH 04/13] Update class - update class name - removed hl7 placeholders --- .../external/hapi/HapiMessageHelper.java | 28 +++++++-------- ...ppingPath.java => Hl7FhirMappingPath.java} | 36 +++++++++---------- 2 files changed, 32 insertions(+), 32 deletions(-) rename etor/src/main/java/gov/hhs/cdc/trustedintermediary/plugin/path/{MappingPath.java => Hl7FhirMappingPath.java} (87%) diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiMessageHelper.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiMessageHelper.java index 8c0a3cedc..43055f56d 100644 --- a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiMessageHelper.java +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiMessageHelper.java @@ -1,6 +1,6 @@ package gov.hhs.cdc.trustedintermediary.external.hapi; -import gov.hhs.cdc.trustedintermediary.plugin.path.MappingPath; +import gov.hhs.cdc.trustedintermediary.plugin.path.Hl7FhirMappingPath; import gov.hhs.cdc.trustedintermediary.wrappers.HapiFhir; import javax.inject.Inject; import org.hl7.fhir.r4.model.Bundle; @@ -24,66 +24,66 @@ private HapiMessageHelper() {} public String extractPlacerOrderNumber(Bundle messageBundle) { return fhirEngine.getStringFromFhirPath( - messageBundle, MappingPath.PLACER_ORDER_NUMBER.getFhirPath()); + messageBundle, Hl7FhirMappingPath.PLACER_ORDER_NUMBER.getFhirPath()); } public String extractSendingApplicationNamespace(Bundle messageBundle) { return fhirEngine.getStringFromFhirPath( - messageBundle, MappingPath.SENDING_APPLICATION_NAMESPACE.getFhirPath()); + messageBundle, Hl7FhirMappingPath.SENDING_APPLICATION_NAMESPACE.getFhirPath()); } public String extractSendingApplicationUniversalId(Bundle messageBundle) { return fhirEngine.getStringFromFhirPath( - messageBundle, MappingPath.SENDING_APPLICATION_UNIVERSAL_ID.getFhirPath()); + messageBundle, Hl7FhirMappingPath.SENDING_APPLICATION_UNIVERSAL_ID.getFhirPath()); } public String extractSendingApplicationUniversalIdType(Bundle messageBundle) { return fhirEngine.getStringFromFhirPath( - messageBundle, MappingPath.SENDING_APPLICATION_UNIVERSAL_ID_TYPE.getFhirPath()); + messageBundle, Hl7FhirMappingPath.SENDING_APPLICATION_UNIVERSAL_ID_TYPE.getFhirPath()); } public String extractSendingFacilityNamespace(Bundle messageBundle) { return fhirEngine.getStringFromFhirPath( - messageBundle, MappingPath.SENDING_FACILITY_NAMESPACE.getFhirPath()); + messageBundle, Hl7FhirMappingPath.SENDING_FACILITY_NAMESPACE.getFhirPath()); } public String extractSendingFacilityUniversalId(Bundle messageBundle) { return fhirEngine.getStringFromFhirPath( - messageBundle, MappingPath.SENDING_FACILITY_UNIVERSAL_ID.getFhirPath()); + messageBundle, Hl7FhirMappingPath.SENDING_FACILITY_UNIVERSAL_ID.getFhirPath()); } public String extractSendingFacilityUniversalIdType(Bundle messageBundle) { return fhirEngine.getStringFromFhirPath( - messageBundle, MappingPath.SENDING_FACILITY_UNIVERSAL_ID_TYPE.getFhirPath()); + messageBundle, Hl7FhirMappingPath.SENDING_FACILITY_UNIVERSAL_ID_TYPE.getFhirPath()); } public String extractReceivingApplicationNamespace(Bundle messageBundle) { return fhirEngine.getStringFromFhirPath( - messageBundle, MappingPath.RECEIVING_APPLICATION_NAMESPACE.getFhirPath()); + messageBundle, Hl7FhirMappingPath.RECEIVING_APPLICATION_NAMESPACE.getFhirPath()); } public String extractReceivingApplicationUniversalId(Bundle messageBundle) { return fhirEngine.getStringFromFhirPath( - messageBundle, MappingPath.RECEIVING_APPLICATION_UNIVERSAL_ID.getFhirPath()); + messageBundle, Hl7FhirMappingPath.RECEIVING_APPLICATION_UNIVERSAL_ID.getFhirPath()); } public String extractReceivingApplicationUniversalIdType(Bundle messageBundle) { return fhirEngine.getStringFromFhirPath( - messageBundle, MappingPath.RECEIVING_APPLICATION_UNIVERSAL_ID_TYPE.getFhirPath()); + messageBundle, Hl7FhirMappingPath.RECEIVING_APPLICATION_UNIVERSAL_ID_TYPE.getFhirPath()); } public String extractReceivingFacilityNamespace(Bundle messageBundle) { return fhirEngine.getStringFromFhirPath( - messageBundle, MappingPath.RECEIVING_FACILITY_NAMESPACE.getFhirPath()); + messageBundle, Hl7FhirMappingPath.RECEIVING_FACILITY_NAMESPACE.getFhirPath()); } public String extractReceivingFacilityUniversalId(Bundle messageBundle) { return fhirEngine.getStringFromFhirPath( - messageBundle, MappingPath.RECEIVING_FACILITY_UNIVERSAL_ID.getFhirPath()); + messageBundle, Hl7FhirMappingPath.RECEIVING_FACILITY_UNIVERSAL_ID.getFhirPath()); } public String extractReceivingFacilityUniversalIdType(Bundle messageBundle) { return fhirEngine.getStringFromFhirPath( - messageBundle, MappingPath.RECEIVING_FACILITY_UNIVERSAL_ID_TYPE.getFhirPath()); + messageBundle, Hl7FhirMappingPath.RECEIVING_FACILITY_UNIVERSAL_ID_TYPE.getFhirPath()); } } diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/plugin/path/MappingPath.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/plugin/path/Hl7FhirMappingPath.java similarity index 87% rename from etor/src/main/java/gov/hhs/cdc/trustedintermediary/plugin/path/MappingPath.java rename to etor/src/main/java/gov/hhs/cdc/trustedintermediary/plugin/path/Hl7FhirMappingPath.java index cb36fd13c..976971432 100644 --- a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/plugin/path/MappingPath.java +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/plugin/path/Hl7FhirMappingPath.java @@ -5,88 +5,88 @@ * be used to extract specific pieces of data from a FHIR message, such as identifiers, namespaces, * and codes related to sending and receiving facilities and applications. */ -public enum MappingPath { +public enum Hl7FhirMappingPath { PLACER_ORDER_NUMBER( - "ORC-2", """ + "ORC.2", """ Bundle.entry.resource.ofType(ServiceRequest).identifier.where(type.coding.code = 'PLAC').value """), SENDING_FACILITY_NAMESPACE( - "?-?", """ + "", """ Bundle.entry.resource.ofType(MessageHeader).sender.resolve().identifier.where( extension.url = 'https://reportstream.cdc.gov/fhir/StructureDefinition/hl7v2Field' and extension.value = 'HD.1' ).value """), SENDING_FACILITY_UNIVERSAL_ID( - "?-?",""" + "",""" Bundle.entry.resource.ofType(MessageHeader).sender.resolve().identifier.where( extension.url = 'https://reportstream.cdc.gov/fhir/StructureDefinition/hl7v2Field' and extension.value = 'HD.2,HD.3' ).value """), SENDING_FACILITY_UNIVERSAL_ID_TYPE( - "?-?",""" + "",""" Bundle.entry.resource.ofType(MessageHeader).sender.resolve().identifier.where( extension.url = 'https://reportstream.cdc.gov/fhir/StructureDefinition/hl7v2Field' and extension.value = 'HD.2,HD.3' ).type.coding.code """), SENDING_APPLICATION_NAMESPACE( - "?-?",""" + "",""" Bundle.entry.resource.ofType(MessageHeader).source.extension.where(url = 'https://reportstream.cdc.gov/fhir/StructureDefinition/namespace-id').value """), SENDING_APPLICATION_UNIVERSAL_ID( - "?-?",""" + "",""" Bundle.entry.resource.ofType(MessageHeader).source.extension.where(url = 'https://reportstream.cdc.gov/fhir/StructureDefinition/universal-id').value """), SENDING_APPLICATION_UNIVERSAL_ID_TYPE( - "?-?",""" + "",""" Bundle.entry.resource.ofType(MessageHeader).source.extension.where(url = 'https://reportstream.cdc.gov/fhir/StructureDefinition/universal-id-type').value """), RECEIVING_FACILITY_NAMESPACE( - "?-?",""" + "",""" Bundle.entry.resource.ofType(MessageHeader).destination.receiver.resolve().identifier.where( extension.url = 'https://reportstream.cdc.gov/fhir/StructureDefinition/hl7v2Field' and extension.value = 'HD.1' ).value """), RECEIVING_FACILITY_UNIVERSAL_ID( - "?-?",""" + "",""" Bundle.entry.resource.ofType(MessageHeader).destination.receiver.resolve().identifier.where( extension.url = 'https://reportstream.cdc.gov/fhir/StructureDefinition/hl7v2Field' and extension.value = 'HD.2,HD.3' ).value """), RECEIVING_FACILITY_UNIVERSAL_ID_TYPE( - "?-?",""" + "",""" Bundle.entry.resource.ofType(MessageHeader).destination.receiver.resolve().identifier.where( extension.url = 'https://reportstream.cdc.gov/fhir/StructureDefinition/hl7v2Field' and extension.value = 'HD.2,HD.3' ).type.coding.code """), RECEIVING_APPLICATION_NAMESPACE( - "?-?",""" + "",""" Bundle.entry.resource.ofType(MessageHeader).destination.name """), RECEIVING_APPLICATION_UNIVERSAL_ID( - "?-?",""" + "",""" Bundle.entry.resource.ofType(MessageHeader).destination.extension.where(url = 'https://reportstream.cdc.gov/fhir/StructureDefinition/universal-id').value """), RECEIVING_APPLICATION_UNIVERSAL_ID_TYPE( - "?-?",""" + "",""" Bundle.entry.resource.ofType(MessageHeader).destination.extension.where(url = 'https://reportstream.cdc.gov/fhir/StructureDefinition/universal-id-type').value """); private final String fhirPath; - private final String hl7v2Path; + private final String hl7Path; - MappingPath(String hl7v2Path, String fhirPath) { - this.hl7v2Path = hl7v2Path; + Hl7FhirMappingPath(String hl7Path, String fhirPath) { + this.hl7Path = hl7Path; this.fhirPath = fhirPath; } public String getHl7v2Path() { - return hl7v2Path; + return hl7Path; } public String getFhirPath() { From 3605aa4cd6d78d4a2b8d0ab9ee5462184d5606e3 Mon Sep 17 00:00:00 2001 From: tjohnson7021 <86614374+tjohnson7021@users.noreply.github.com> Date: Fri, 10 May 2024 12:10:18 -0400 Subject: [PATCH 05/13] Spotless Apply --- .../external/hapi/HapiMessageHelper.java | 9 +++-- .../plugin/path/Hl7FhirMappingPath.java | 39 ++++++++++++------- 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiMessageHelper.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiMessageHelper.java index 43055f56d..9c9dacf68 100644 --- a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiMessageHelper.java +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiMessageHelper.java @@ -39,7 +39,8 @@ public String extractSendingApplicationUniversalId(Bundle messageBundle) { public String extractSendingApplicationUniversalIdType(Bundle messageBundle) { return fhirEngine.getStringFromFhirPath( - messageBundle, Hl7FhirMappingPath.SENDING_APPLICATION_UNIVERSAL_ID_TYPE.getFhirPath()); + messageBundle, + Hl7FhirMappingPath.SENDING_APPLICATION_UNIVERSAL_ID_TYPE.getFhirPath()); } public String extractSendingFacilityNamespace(Bundle messageBundle) { @@ -69,7 +70,8 @@ public String extractReceivingApplicationUniversalId(Bundle messageBundle) { public String extractReceivingApplicationUniversalIdType(Bundle messageBundle) { return fhirEngine.getStringFromFhirPath( - messageBundle, Hl7FhirMappingPath.RECEIVING_APPLICATION_UNIVERSAL_ID_TYPE.getFhirPath()); + messageBundle, + Hl7FhirMappingPath.RECEIVING_APPLICATION_UNIVERSAL_ID_TYPE.getFhirPath()); } public String extractReceivingFacilityNamespace(Bundle messageBundle) { @@ -84,6 +86,7 @@ public String extractReceivingFacilityUniversalId(Bundle messageBundle) { public String extractReceivingFacilityUniversalIdType(Bundle messageBundle) { return fhirEngine.getStringFromFhirPath( - messageBundle, Hl7FhirMappingPath.RECEIVING_FACILITY_UNIVERSAL_ID_TYPE.getFhirPath()); + messageBundle, + Hl7FhirMappingPath.RECEIVING_FACILITY_UNIVERSAL_ID_TYPE.getFhirPath()); } } diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/plugin/path/Hl7FhirMappingPath.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/plugin/path/Hl7FhirMappingPath.java index 976971432..5261b370e 100644 --- a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/plugin/path/Hl7FhirMappingPath.java +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/plugin/path/Hl7FhirMappingPath.java @@ -7,73 +7,85 @@ */ public enum Hl7FhirMappingPath { PLACER_ORDER_NUMBER( - "ORC.2", """ + "ORC.2", + """ Bundle.entry.resource.ofType(ServiceRequest).identifier.where(type.coding.code = 'PLAC').value """), SENDING_FACILITY_NAMESPACE( - "", """ + "", + """ Bundle.entry.resource.ofType(MessageHeader).sender.resolve().identifier.where( extension.url = 'https://reportstream.cdc.gov/fhir/StructureDefinition/hl7v2Field' and extension.value = 'HD.1' ).value """), SENDING_FACILITY_UNIVERSAL_ID( - "",""" + "", + """ Bundle.entry.resource.ofType(MessageHeader).sender.resolve().identifier.where( extension.url = 'https://reportstream.cdc.gov/fhir/StructureDefinition/hl7v2Field' and extension.value = 'HD.2,HD.3' ).value """), SENDING_FACILITY_UNIVERSAL_ID_TYPE( - "",""" + "", + """ Bundle.entry.resource.ofType(MessageHeader).sender.resolve().identifier.where( extension.url = 'https://reportstream.cdc.gov/fhir/StructureDefinition/hl7v2Field' and extension.value = 'HD.2,HD.3' ).type.coding.code """), SENDING_APPLICATION_NAMESPACE( - "",""" + "", + """ Bundle.entry.resource.ofType(MessageHeader).source.extension.where(url = 'https://reportstream.cdc.gov/fhir/StructureDefinition/namespace-id').value """), SENDING_APPLICATION_UNIVERSAL_ID( - "",""" + "", + """ Bundle.entry.resource.ofType(MessageHeader).source.extension.where(url = 'https://reportstream.cdc.gov/fhir/StructureDefinition/universal-id').value """), SENDING_APPLICATION_UNIVERSAL_ID_TYPE( - "",""" + "", + """ Bundle.entry.resource.ofType(MessageHeader).source.extension.where(url = 'https://reportstream.cdc.gov/fhir/StructureDefinition/universal-id-type').value """), RECEIVING_FACILITY_NAMESPACE( - "",""" + "", + """ Bundle.entry.resource.ofType(MessageHeader).destination.receiver.resolve().identifier.where( extension.url = 'https://reportstream.cdc.gov/fhir/StructureDefinition/hl7v2Field' and extension.value = 'HD.1' ).value """), RECEIVING_FACILITY_UNIVERSAL_ID( - "",""" + "", + """ Bundle.entry.resource.ofType(MessageHeader).destination.receiver.resolve().identifier.where( extension.url = 'https://reportstream.cdc.gov/fhir/StructureDefinition/hl7v2Field' and extension.value = 'HD.2,HD.3' ).value """), RECEIVING_FACILITY_UNIVERSAL_ID_TYPE( - "",""" + "", + """ Bundle.entry.resource.ofType(MessageHeader).destination.receiver.resolve().identifier.where( extension.url = 'https://reportstream.cdc.gov/fhir/StructureDefinition/hl7v2Field' and extension.value = 'HD.2,HD.3' ).type.coding.code """), RECEIVING_APPLICATION_NAMESPACE( - "",""" + "", """ Bundle.entry.resource.ofType(MessageHeader).destination.name """), RECEIVING_APPLICATION_UNIVERSAL_ID( - "",""" + "", + """ Bundle.entry.resource.ofType(MessageHeader).destination.extension.where(url = 'https://reportstream.cdc.gov/fhir/StructureDefinition/universal-id').value """), RECEIVING_APPLICATION_UNIVERSAL_ID_TYPE( - "",""" + "", + """ Bundle.entry.resource.ofType(MessageHeader).destination.extension.where(url = 'https://reportstream.cdc.gov/fhir/StructureDefinition/universal-id-type').value """); @@ -93,4 +105,3 @@ public String getFhirPath() { return fhirPath; } } - From 39df799062d22c9e80d0bf4c9fda8af7e00d8d6b Mon Sep 17 00:00:00 2001 From: tjohnson7021 <86614374+tjohnson7021@users.noreply.github.com> Date: Fri, 10 May 2024 12:15:14 -0400 Subject: [PATCH 06/13] Updated getter name --- .../cdc/trustedintermediary/plugin/path/Hl7FhirMappingPath.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/plugin/path/Hl7FhirMappingPath.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/plugin/path/Hl7FhirMappingPath.java index 5261b370e..c071c26f0 100644 --- a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/plugin/path/Hl7FhirMappingPath.java +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/plugin/path/Hl7FhirMappingPath.java @@ -97,7 +97,7 @@ public enum Hl7FhirMappingPath { this.fhirPath = fhirPath; } - public String getHl7v2Path() { + public String getHl7Path() { return hl7Path; } From 24c3baff7e8e7e4b6c8811b43106a69085f4d7a3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 10 May 2024 14:36:49 +0000 Subject: [PATCH 07/13] Update Terraform azurerm to v3.103.0 --- operations/environments/dev/main.tf | 2 +- operations/environments/internal/main.tf | 2 +- operations/environments/pr/main.tf | 2 +- operations/environments/prd/main.tf | 2 +- operations/environments/stg/main.tf | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/operations/environments/dev/main.tf b/operations/environments/dev/main.tf index 87c974e59..1e7c16f5b 100644 --- a/operations/environments/dev/main.tf +++ b/operations/environments/dev/main.tf @@ -2,7 +2,7 @@ terraform { required_providers { azurerm = { source = "hashicorp/azurerm" - version = "3.102.0" + version = "3.103.0" } } diff --git a/operations/environments/internal/main.tf b/operations/environments/internal/main.tf index aa126963a..361432b2c 100644 --- a/operations/environments/internal/main.tf +++ b/operations/environments/internal/main.tf @@ -2,7 +2,7 @@ terraform { required_providers { azurerm = { source = "hashicorp/azurerm" - version = "3.102.0" + version = "3.103.0" } } diff --git a/operations/environments/pr/main.tf b/operations/environments/pr/main.tf index 895eef645..ce04d492d 100644 --- a/operations/environments/pr/main.tf +++ b/operations/environments/pr/main.tf @@ -2,7 +2,7 @@ terraform { required_providers { azurerm = { source = "hashicorp/azurerm" - version = "3.102.0" + version = "3.103.0" } } diff --git a/operations/environments/prd/main.tf b/operations/environments/prd/main.tf index e5acdf8bc..ab1c82475 100644 --- a/operations/environments/prd/main.tf +++ b/operations/environments/prd/main.tf @@ -2,7 +2,7 @@ terraform { required_providers { azurerm = { source = "hashicorp/azurerm" - version = "3.102.0" + version = "3.103.0" } } diff --git a/operations/environments/stg/main.tf b/operations/environments/stg/main.tf index 3ebef69e5..a8d92e6ce 100644 --- a/operations/environments/stg/main.tf +++ b/operations/environments/stg/main.tf @@ -2,7 +2,7 @@ terraform { required_providers { azurerm = { source = "hashicorp/azurerm" - version = "3.102.0" + version = "3.103.0" } } From c75e18a11ca4cd877ed7d30c29a0dbe67c5c7de2 Mon Sep 17 00:00:00 2001 From: tjohnson7021 <86614374+tjohnson7021@users.noreply.github.com> Date: Fri, 10 May 2024 12:21:54 -0400 Subject: [PATCH 08/13] Update java docs --- .../trustedintermediary/plugin/path/Hl7FhirMappingPath.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/plugin/path/Hl7FhirMappingPath.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/plugin/path/Hl7FhirMappingPath.java index c071c26f0..dbabd88aa 100644 --- a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/plugin/path/Hl7FhirMappingPath.java +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/plugin/path/Hl7FhirMappingPath.java @@ -1,9 +1,9 @@ package gov.hhs.cdc.trustedintermediary.plugin.path; /** - * Enumerates FHIR path expressions for various data elements within a FHIR message. These paths can + * Enumerates FHIR and HL7 path expressions for various data elements within a FHIR message. These paths can * be used to extract specific pieces of data from a FHIR message, such as identifiers, namespaces, - * and codes related to sending and receiving facilities and applications. + * and codes related to sending and receiving facilities and applications. It also defines the HL7 field names. */ public enum Hl7FhirMappingPath { PLACER_ORDER_NUMBER( From 1d66c2018ff234b980115a0a74c3b7df53c1b96d Mon Sep 17 00:00:00 2001 From: tjohnson7021 <86614374+tjohnson7021@users.noreply.github.com> Date: Fri, 10 May 2024 12:23:49 -0400 Subject: [PATCH 09/13] Spotless apply --- .../plugin/path/Hl7FhirMappingPath.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/plugin/path/Hl7FhirMappingPath.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/plugin/path/Hl7FhirMappingPath.java index dbabd88aa..5d0336e30 100644 --- a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/plugin/path/Hl7FhirMappingPath.java +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/plugin/path/Hl7FhirMappingPath.java @@ -1,9 +1,10 @@ package gov.hhs.cdc.trustedintermediary.plugin.path; /** - * Enumerates FHIR and HL7 path expressions for various data elements within a FHIR message. These paths can - * be used to extract specific pieces of data from a FHIR message, such as identifiers, namespaces, - * and codes related to sending and receiving facilities and applications. It also defines the HL7 field names. + * Enumerates FHIR and HL7 path expressions for various data elements within a FHIR message. These + * paths can be used to extract specific pieces of data from a FHIR message, such as identifiers, + * namespaces, and codes related to sending and receiving facilities and applications. It also + * defines the HL7 field names. */ public enum Hl7FhirMappingPath { PLACER_ORDER_NUMBER( From df2186fa83d1d016e2411be278241f2a8440e5da Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 14 May 2024 01:32:44 +0000 Subject: [PATCH 10/13] Update patch dependencies --- operations/environments/dev/main.tf | 2 +- operations/environments/internal/main.tf | 2 +- operations/environments/pr/main.tf | 2 +- operations/environments/prd/main.tf | 2 +- operations/environments/stg/main.tf | 2 +- shared/build.gradle | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/operations/environments/dev/main.tf b/operations/environments/dev/main.tf index 1e7c16f5b..f59dc8a3b 100644 --- a/operations/environments/dev/main.tf +++ b/operations/environments/dev/main.tf @@ -2,7 +2,7 @@ terraform { required_providers { azurerm = { source = "hashicorp/azurerm" - version = "3.103.0" + version = "3.103.1" } } diff --git a/operations/environments/internal/main.tf b/operations/environments/internal/main.tf index 361432b2c..1f3866080 100644 --- a/operations/environments/internal/main.tf +++ b/operations/environments/internal/main.tf @@ -2,7 +2,7 @@ terraform { required_providers { azurerm = { source = "hashicorp/azurerm" - version = "3.103.0" + version = "3.103.1" } } diff --git a/operations/environments/pr/main.tf b/operations/environments/pr/main.tf index ce04d492d..ab2d49765 100644 --- a/operations/environments/pr/main.tf +++ b/operations/environments/pr/main.tf @@ -2,7 +2,7 @@ terraform { required_providers { azurerm = { source = "hashicorp/azurerm" - version = "3.103.0" + version = "3.103.1" } } diff --git a/operations/environments/prd/main.tf b/operations/environments/prd/main.tf index ab1c82475..7b2a0eeae 100644 --- a/operations/environments/prd/main.tf +++ b/operations/environments/prd/main.tf @@ -2,7 +2,7 @@ terraform { required_providers { azurerm = { source = "hashicorp/azurerm" - version = "3.103.0" + version = "3.103.1" } } diff --git a/operations/environments/stg/main.tf b/operations/environments/stg/main.tf index a8d92e6ce..adc4b8368 100644 --- a/operations/environments/stg/main.tf +++ b/operations/environments/stg/main.tf @@ -2,7 +2,7 @@ terraform { required_providers { azurerm = { source = "hashicorp/azurerm" - version = "3.103.0" + version = "3.103.1" } } diff --git a/shared/build.gradle b/shared/build.gradle index 9cf588210..b38169869 100644 --- a/shared/build.gradle +++ b/shared/build.gradle @@ -44,7 +44,7 @@ dependencies { runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.5' // azure sdk - implementation 'com.azure:azure-security-keyvault-secrets:4.8.2' + implementation 'com.azure:azure-security-keyvault-secrets:4.8.3' implementation 'com.azure:azure-identity:1.12.1' testImplementation 'org.apache.groovy:groovy:4.0.21' From 9cbca55e9aab06dfb4c616160c43cd2abda65325 Mon Sep 17 00:00:00 2001 From: jcrichlake <145698165+jcrichlake@users.noreply.github.com> Date: Tue, 14 May 2024 12:43:39 -0400 Subject: [PATCH 11/13] Adding logging for getting a token from Azure to assist with troubleshooting (#1094) --- .../external/azure/AzureDatabaseCredentialsProvider.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/azure/AzureDatabaseCredentialsProvider.java b/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/azure/AzureDatabaseCredentialsProvider.java index 73d46440f..8e6ea25a7 100644 --- a/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/azure/AzureDatabaseCredentialsProvider.java +++ b/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/azure/AzureDatabaseCredentialsProvider.java @@ -2,7 +2,9 @@ import com.azure.core.credential.TokenRequestContext; import com.azure.identity.DefaultAzureCredentialBuilder; +import gov.hhs.cdc.trustedintermediary.wrappers.Logger; import gov.hhs.cdc.trustedintermediary.wrappers.database.DatabaseCredentialsProvider; +import javax.inject.Inject; /** * AzureDatabaseCredentialsProvider is a class responsible for providing credentials for a database @@ -19,8 +21,13 @@ public static AzureDatabaseCredentialsProvider getInstance() { private AzureDatabaseCredentialsProvider() {} + @Inject Logger logger; + @Override public String getPassword() { + + logger.logInfo("Fetching credentials from Azure"); + return new DefaultAzureCredentialBuilder() .build() .getTokenSync( From 82e065265aca575823ae4004a18273abb1e12f32 Mon Sep 17 00:00:00 2001 From: Sylvie Date: Wed, 15 May 2024 14:52:45 -0500 Subject: [PATCH 12/13] Use a (local) test DB for load tests so it doesn't wipe out the regular local DB (#1074) * Use a (local) test DB for load tests so it doesn't wipe out the regular DB * split out the ports and volumes between test and not * Update README.md --- README.md | 2 ++ docker-compose.postgres-test.yml | 17 +++++++++++++++++ load-execute.sh | 14 +++++++------- 3 files changed, 26 insertions(+), 7 deletions(-) create mode 100644 docker-compose.postgres-test.yml diff --git a/README.md b/README.md index 27edf4f49..fa14542bd 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,8 @@ running... ``` This will run the API for you, so no need to run it manually. +**If you are already running the API, stop it before running the load tests or the cleanup steps won't work.** +The load tests will also spin up (and clean up) a local test DB on port 5434 that should not interfere with the local dev DB. The `locustfile.py` that specifies the load test is located at [`./operations/locustfile.py`](./operations/locustfile.py). diff --git a/docker-compose.postgres-test.yml b/docker-compose.postgres-test.yml new file mode 100644 index 000000000..23c3b1804 --- /dev/null +++ b/docker-compose.postgres-test.yml @@ -0,0 +1,17 @@ +version: "3.7" + +services: + postgresql-test: + image: postgres:16 + restart: unless-stopped + environment: + POSTGRES_DB: "intermediary-test" + POSTGRES_PASSWORD: "changeIT!" # pragma: allowlist secret + POSTGRES_USER: "intermediary" + ports: + - 5434:5432 + volumes: + - ti_postgres_test_data:/var/lib/postgresql/data + +volumes: + ti_postgres_test_data: diff --git a/load-execute.sh b/load-execute.sh index ca81fcd9a..812e701d5 100755 --- a/load-execute.sh +++ b/load-execute.sh @@ -4,8 +4,8 @@ set -e start_api() { echo 'Starting API' export DB_URL=localhost - export DB_PORT=5433 - export DB_NAME=intermediary + export DB_PORT=5434 + export DB_NAME=intermediary-test export DB_USER=intermediary export DB_PASS=changeIT! export DB_SSL=require @@ -16,14 +16,14 @@ start_api() { start_database() { echo 'Starting database' - docker compose -f docker-compose.postgres.yml up -d + docker compose -f docker-compose.postgres-test.yml up -d sleep 2 echo "Database started" } migrate_database() { echo 'Migrating database' - liquibase update --changelog-file ./etor/databaseMigrations/root.yml --url jdbc:postgresql://localhost:5433/intermediary --username intermediary --password 'changeIT!' --label-filter '!azure' + liquibase update --changelog-file ./etor/databaseMigrations/root.yml --url jdbc:postgresql://localhost:5434/intermediary-test --username intermediary --password 'changeIT!' --label-filter '!azure' echo "Database migrated" } @@ -54,9 +54,9 @@ cleanup() { kill "${API_PID}" echo "PID ${API_PID} killed" echo "Stopping and deleting database" - docker stop trusted-intermediary-postgresql-1 - docker rm -f trusted-intermediary-postgresql-1 - docker volume rm trusted-intermediary_ti_postgres_data + docker stop trusted-intermediary-postgresql-test-1 + docker rm -f trusted-intermediary-postgresql-test-1 + docker volume rm trusted-intermediary_ti_postgres_test_data echo "Database stopped and deleted" } From 3e674cddb00c8015210535fbbada9f388d2918c6 Mon Sep 17 00:00:00 2001 From: halprin Date: Wed, 15 May 2024 16:27:18 -0600 Subject: [PATCH 13/13] Get the linked metadata even when using the outbound submission ID --- .../etor/EtorDomainRegistration.java | 11 +++-- .../etor/EtorDomainRegistrationTest.groovy | 41 +++++++++++++++++++ 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/EtorDomainRegistration.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/EtorDomainRegistration.java index 94ee5a494..ce0f14a62 100644 --- a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/EtorDomainRegistration.java +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/EtorDomainRegistration.java @@ -182,20 +182,23 @@ DomainResponse handleResults(DomainRequest request) { DomainResponse handleMetadata(DomainRequest request) { try { String metadataId = request.getPathParams().get("id"); - Optional metadata = + Optional metadataOptional = partnerMetadataOrchestrator.getMetadata(metadataId); - if (metadata.isEmpty()) { + if (metadataOptional.isEmpty()) { return domainResponseHelper.constructErrorResponse( 404, "Metadata not found for ID: " + metadataId); } + var metadata = metadataOptional.get(); + Set messageIdsToLink = - partnerMetadataOrchestrator.findMessagesIdsToLink(metadataId); + partnerMetadataOrchestrator.findMessagesIdsToLink( + metadata.receivedSubmissionId()); FhirMetadata responseObject = partnerMetadataConverter.extractPublicMetadataToOperationOutcome( - metadata.get(), metadataId, messageIdsToLink); + metadata, metadataId, messageIdsToLink); return domainResponseHelper.constructOkResponseFromString( fhir.encodeResourceToJson(responseObject.getUnderlyingOutcome())); diff --git a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/EtorDomainRegistrationTest.groovy b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/EtorDomainRegistrationTest.groovy index dda90c6c0..a36120936 100644 --- a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/EtorDomainRegistrationTest.groovy +++ b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/EtorDomainRegistrationTest.groovy @@ -208,6 +208,47 @@ class EtorDomainRegistrationTest extends Specification { 1 * mockResponseHelper.constructOkResponseFromString(_ as String) >> new DomainResponse(expectedStatusCode) } + def "metadata endpoint returns metadata even when the submitted ID is different from ID used for linking"() { + given: + def expectedStatusCode = 200 + def receivedSubmissionId = "receivedSubmissionId" + def sentSubmissionId = "sentSubmissionId" + def metadata = new PartnerMetadata(receivedSubmissionId, "hash", PartnerMetadataMessageType.ORDER, sendingApp, sendingFacility, receivingApp, receivingFacility, "placer_order_number").withSentSubmissionId(sentSubmissionId) + def linkedMessageIds = Set.of(receivedSubmissionId, "Test1", "Test2") + + def connector = new EtorDomainRegistration() + TestApplicationContext.register(EtorDomainRegistration, connector) + + def request = new DomainRequest() + request.setPathParams(["id": sentSubmissionId]) + + def mockPartnerMetadataOrchestrator = Mock(PartnerMetadataOrchestrator) + TestApplicationContext.register(PartnerMetadataOrchestrator, mockPartnerMetadataOrchestrator) + + def mockResponseHelper = Mock(DomainResponseHelper) + TestApplicationContext.register(DomainResponseHelper, mockResponseHelper) + + def mockPartnerMetadataConverter = Mock(PartnerMetadataConverter) + TestApplicationContext.register(PartnerMetadataConverter, mockPartnerMetadataConverter) + + def mockFhir = Mock(HapiFhir) + mockFhir.encodeResourceToJson(_) >> "" + TestApplicationContext.register(HapiFhir, mockFhir) + + TestApplicationContext.injectRegisteredImplementations() + + when: + def res = connector.handleMetadata(request) + def actualStatusCode = res.statusCode + + then: + actualStatusCode == expectedStatusCode + 1 * mockPartnerMetadataOrchestrator.getMetadata(sentSubmissionId) >> Optional.ofNullable(metadata) + 1 * mockPartnerMetadataOrchestrator.findMessagesIdsToLink(receivedSubmissionId) >> linkedMessageIds + 1 * mockPartnerMetadataConverter.extractPublicMetadataToOperationOutcome(_ as PartnerMetadata, _ as String, linkedMessageIds) >> Mock(FhirMetadata) + 1 * mockResponseHelper.constructOkResponseFromString(_ as String) >> new DomainResponse(expectedStatusCode) + } + def "metadata endpoint returns a 404 response when metadata id is not found"() { given: def expectedStatusCode = 404