From 06a5b3ae7c13646233a971745cfca5e45b08a754 Mon Sep 17 00:00:00 2001 From: Luis Pabon Date: Wed, 27 Mar 2024 18:53:41 -0400 Subject: [PATCH 01/13] Update HapiFhirImplementation.java Testing different engine implementation to support resolve --- .../external/hapi/HapiFhirImplementation.java | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiFhirImplementation.java b/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiFhirImplementation.java index 09ed6b310..57927fb32 100644 --- a/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiFhirImplementation.java +++ b/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiFhirImplementation.java @@ -1,19 +1,21 @@ package gov.hhs.cdc.trustedintermediary.external.hapi; import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.fhirpath.IFhirPath; import ca.uhn.fhir.parser.IParser; import gov.hhs.cdc.trustedintermediary.wrappers.FhirParseException; import gov.hhs.cdc.trustedintermediary.wrappers.HapiFhir; import org.hl7.fhir.instance.model.api.IBaseResource; -import org.hl7.fhir.r4.model.BooleanType; +import org.hl7.fhir.r4.hapi.ctx.HapiWorkerContext; +import org.hl7.fhir.r4.model.Resource; +import org.hl7.fhir.r4.utils.FHIRPathEngine; /** Concrete implementation that calls the Hapi FHIR library. */ public class HapiFhirImplementation implements HapiFhir { private static final HapiFhirImplementation INSTANCE = new HapiFhirImplementation(); private static final FhirContext CONTEXT = FhirContext.forR4(); - private static final IFhirPath PATH_ENGINE = CONTEXT.newFhirPath(); + private static final FHIRPathEngine PATH_ENGINE = + new FHIRPathEngine(new HapiWorkerContext(CONTEXT, CONTEXT.getValidationSupport())); private HapiFhirImplementation() {} @@ -56,8 +58,16 @@ public String encodeResourceToJson(Object resource) { */ @Override public Boolean evaluateCondition(Object resource, String expression) { + var node = PATH_ENGINE.parse(expression); + var result = - PATH_ENGINE.evaluateFirst((IBaseResource) resource, expression, BooleanType.class); - return result.map(BooleanType::booleanValue).orElse(false); + PATH_ENGINE.evaluateToBoolean( + (Resource) resource, (Resource) resource, (Resource) resource, node); + + // var result = + // PATH_ENGINE.evaluateFirst((IBaseResource) resource, expression, + // BooleanType.class); + // return result.map(BooleanType::booleanValue).orElse(false); + return result; } } From 89c158a3fd9140041ef8448f2461cfe9143cc505 Mon Sep 17 00:00:00 2001 From: Luis Pabon Date: Thu, 28 Mar 2024 12:04:45 -0400 Subject: [PATCH 02/13] Create HapiFhirCustomEvaluationContext.java adding dummy file --- .../hapi/HapiFhirCustomEvaluationContext.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiFhirCustomEvaluationContext.java diff --git a/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiFhirCustomEvaluationContext.java b/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiFhirCustomEvaluationContext.java new file mode 100644 index 000000000..1056863e8 --- /dev/null +++ b/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiFhirCustomEvaluationContext.java @@ -0,0 +1,15 @@ +package gov.hhs.cdc.trustedintermediary.external.hapi; + +import ca.uhn.fhir.fhirpath.IFhirPathEvaluationContext; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import org.hl7.fhir.instance.model.api.IBase; +import org.hl7.fhir.instance.model.api.IIdType; + +public class HapiFhirCustomEvaluationContext implements IFhirPathEvaluationContext { + + @Override + public IBase resolveReference(@Nonnull IIdType theReference, @Nullable IBase theContext) { + return IFhirPathEvaluationContext.super.resolveReference(theReference, theContext); + } +} From 296e9a304b0a8dae1b1dafdb84dea9a7b945d539 Mon Sep 17 00:00:00 2001 From: Luis Pabon Date: Thu, 28 Mar 2024 12:45:25 -0400 Subject: [PATCH 03/13] Added resolveReference implementation --- .../hapi/HapiFhirCustomEvaluationContext.java | 11 +++++++- .../external/hapi/HapiFhirImplementation.java | 27 +++++++++---------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiFhirCustomEvaluationContext.java b/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiFhirCustomEvaluationContext.java index 1056863e8..af4f84de6 100644 --- a/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiFhirCustomEvaluationContext.java +++ b/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiFhirCustomEvaluationContext.java @@ -5,11 +5,20 @@ import jakarta.annotation.Nullable; import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r4.model.Reference; public class HapiFhirCustomEvaluationContext implements IFhirPathEvaluationContext { + /** + * @param theReference Id-based reference to the resource we're attempting to resolve. + * @param theContext Internally converted resource version of theReference. + * @return Reference resource + */ @Override public IBase resolveReference(@Nonnull IIdType theReference, @Nullable IBase theContext) { - return IFhirPathEvaluationContext.super.resolveReference(theReference, theContext); + if (theContext != null) { + return ((Reference) theContext).getResource(); + } + return IFhirPathEvaluationContext.super.resolveReference(theReference, null); } } diff --git a/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiFhirImplementation.java b/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiFhirImplementation.java index 57927fb32..7f8d44808 100644 --- a/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiFhirImplementation.java +++ b/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiFhirImplementation.java @@ -1,24 +1,29 @@ package gov.hhs.cdc.trustedintermediary.external.hapi; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.fhirpath.IFhirPath; import ca.uhn.fhir.parser.IParser; import gov.hhs.cdc.trustedintermediary.wrappers.FhirParseException; import gov.hhs.cdc.trustedintermediary.wrappers.HapiFhir; import org.hl7.fhir.instance.model.api.IBaseResource; -import org.hl7.fhir.r4.hapi.ctx.HapiWorkerContext; -import org.hl7.fhir.r4.model.Resource; -import org.hl7.fhir.r4.utils.FHIRPathEngine; +import org.hl7.fhir.r4.model.BooleanType; /** Concrete implementation that calls the Hapi FHIR library. */ public class HapiFhirImplementation implements HapiFhir { private static final HapiFhirImplementation INSTANCE = new HapiFhirImplementation(); private static final FhirContext CONTEXT = FhirContext.forR4(); - private static final FHIRPathEngine PATH_ENGINE = - new FHIRPathEngine(new HapiWorkerContext(CONTEXT, CONTEXT.getValidationSupport())); + + private static final IFhirPath PATH_ENGINE = createEngine(); private HapiFhirImplementation() {} + private static IFhirPath createEngine() { + var engine = CONTEXT.newFhirPath(); + engine.setEvaluationContext(new HapiFhirCustomEvaluationContext()); + return engine; + } + public static HapiFhirImplementation getInstance() { return INSTANCE; } @@ -58,16 +63,8 @@ public String encodeResourceToJson(Object resource) { */ @Override public Boolean evaluateCondition(Object resource, String expression) { - var node = PATH_ENGINE.parse(expression); - var result = - PATH_ENGINE.evaluateToBoolean( - (Resource) resource, (Resource) resource, (Resource) resource, node); - - // var result = - // PATH_ENGINE.evaluateFirst((IBaseResource) resource, expression, - // BooleanType.class); - // return result.map(BooleanType::booleanValue).orElse(false); - return result; + PATH_ENGINE.evaluateFirst((IBaseResource) resource, expression, BooleanType.class); + return result.map(BooleanType::booleanValue).orElse(false); } } From 5b8cc06271709df0a2c1b4e7bcc2770bfe16cfcc Mon Sep 17 00:00:00 2001 From: Basilio Bogado <541149+basiliskus@users.noreply.github.com> Date: Thu, 28 Mar 2024 09:59:48 -0700 Subject: [PATCH 04/13] Fixed tests --- .../external/hapi/HapiFhirImplementationTest.groovy | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/shared/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/hapi/HapiFhirImplementationTest.groovy b/shared/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/hapi/HapiFhirImplementationTest.groovy index c22bc2518..d388ab047 100644 --- a/shared/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/hapi/HapiFhirImplementationTest.groovy +++ b/shared/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/hapi/HapiFhirImplementationTest.groovy @@ -75,7 +75,7 @@ class HapiFhirImplementationTest extends Specification { result == false } - def "evaluateCondition throws FhirPathExecutionException on empty string"() { + def "evaluateCondition throws Exception on empty string"() { given: def path = "" @@ -83,10 +83,10 @@ class HapiFhirImplementationTest extends Specification { fhir.evaluateCondition(bundle as IBaseResource, path) then: - thrown(FhirPathExecutionException) + thrown(Exception) } - def "evaluateCondition throws FhirPathExecutionException on fake method"() { + def "evaluateCondition throws Exception on fake method"() { given: def path = "Bundle.entry[0].resource.BadMethod('blah')" @@ -94,7 +94,7 @@ class HapiFhirImplementationTest extends Specification { fhir.evaluateCondition(bundle as IBaseResource, path) then: - thrown(FhirPathExecutionException) + thrown(Exception) } def "parseResource can convert a valid string to Bundle"() { From 68e8a14d45098eeb3436635d88d99943ede4dc8c Mon Sep 17 00:00:00 2001 From: Luis Pabon Date: Thu, 28 Mar 2024 13:24:17 -0400 Subject: [PATCH 05/13] Adding docs --- .../external/hapi/HapiFhirCustomEvaluationContext.java | 5 ++++- .../external/hapi/HapiFhirImplementation.java | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiFhirCustomEvaluationContext.java b/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiFhirCustomEvaluationContext.java index af4f84de6..ecb1fdd86 100644 --- a/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiFhirCustomEvaluationContext.java +++ b/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiFhirCustomEvaluationContext.java @@ -10,9 +10,12 @@ public class HapiFhirCustomEvaluationContext implements IFhirPathEvaluationContext { /** + * When a FHIR path includes the "resolve()" method, this function is called to parse that into + * a Resource. + * * @param theReference Id-based reference to the resource we're attempting to resolve. * @param theContext Internally converted resource version of theReference. - * @return Reference resource + * @return Reference resource if available, else null. */ @Override public IBase resolveReference(@Nonnull IIdType theReference, @Nullable IBase theContext) { diff --git a/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiFhirImplementation.java b/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiFhirImplementation.java index 7f8d44808..2aaffc657 100644 --- a/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiFhirImplementation.java +++ b/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiFhirImplementation.java @@ -18,6 +18,11 @@ public class HapiFhirImplementation implements HapiFhir { private HapiFhirImplementation() {} + /** + * Creates FHIRPath engine with a custom evaluation context. + * + * @return Configured engine. + */ private static IFhirPath createEngine() { var engine = CONTEXT.newFhirPath(); engine.setEvaluationContext(new HapiFhirCustomEvaluationContext()); From 6a3953f9ecd797b30a837e2e8412fbea2662dd5f Mon Sep 17 00:00:00 2001 From: Basilio Bogado <541149+basiliskus@users.noreply.github.com> Date: Thu, 28 Mar 2024 10:48:05 -0700 Subject: [PATCH 06/13] Added tests and method that goes through all fhir files in examples/test --- .../RuleEngineIntegrationTest.groovy | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/RuleEngineIntegrationTest.groovy b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/RuleEngineIntegrationTest.groovy index 42f0731cd..f3f099b1c 100644 --- a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/RuleEngineIntegrationTest.groovy +++ b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/RuleEngineIntegrationTest.groovy @@ -18,8 +18,6 @@ class RuleEngineIntegrationTest extends Specification { def engine = RuleEngine.getInstance() def mockLogger = Mock(Logger) - String fhirBody - def setup() { TestApplicationContext.reset() TestApplicationContext.init() @@ -33,15 +31,36 @@ class RuleEngineIntegrationTest extends Specification { TestApplicationContext.injectRegisteredImplementations() } - def "validation logs a warning when a validation fails"() { + def "validation logs a warning when at least one validation fails"() { given: - fhirBody = Files.readString(Path.of("../examples/Test/Orders/001_OML_O21_short.fhir")) - def bundle = fhir.parseResource(fhirBody, Bundle) + def bundle = new Bundle() when: engine.validate(new HapiFhirResource(bundle)) then: - 1 * mockLogger.logWarning(_ as String) + (1.._) * mockLogger.logWarning(_ as String) + } + + def "validation doesn't break for any of the sample test messages"() { + given: + def exampleFhirFiles = getExampleFhirFiles("Orders") + + when: + exampleFhirFiles.each { fhirString -> + def bundle = fhir.parseResource(fhirString as String, Bundle) + engine.validate(new HapiFhirResource(bundle)) + } + + then: + noExceptionThrown() + } + + def getExampleFhirFiles(String messageType = "") { + def exampleFilesPath = "../examples/Test" + return Files.walk(Path.of(exampleFilesPath, messageType)) + .filter { it.toString().endsWith(".fhir") } + .map { Files.readString(it) } + .collect() } } From 688b63f60a1045e362d0d794ed5c0148db1799fd Mon Sep 17 00:00:00 2001 From: Basilio Bogado <541149+basiliskus@users.noreply.github.com> Date: Thu, 28 Mar 2024 17:25:23 -0700 Subject: [PATCH 07/13] Fixed rules names --- etor/src/main/resources/rule_definitions.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/etor/src/main/resources/rule_definitions.json b/etor/src/main/resources/rule_definitions.json index a6a381e4b..93c2ebb3e 100644 --- a/etor/src/main/resources/rule_definitions.json +++ b/etor/src/main/resources/rule_definitions.json @@ -1,6 +1,6 @@ { "rules": [ { - "name": "requiredReceiverId", + "name": "messageHasRequiredReceiverId", "description": "Message has required receiver id", "violationMessage": "Message doesn't have required receiver id", "conditions": [ ], @@ -9,7 +9,7 @@ ] }, { - "name": "requiredReceiverId", + "name": "ORMHasRequiredCardNumber", "description": "ORM has required Card Number", "violationMessage": "ORM doesn't have required Card Number", "conditions": [ From a0334555b91b593f54669d47458820dc2866f6c5 Mon Sep 17 00:00:00 2001 From: Basilio Bogado <541149+basiliskus@users.noreply.github.com> Date: Thu, 28 Mar 2024 17:26:20 -0700 Subject: [PATCH 08/13] Added integration tests --- .../RuleEngineIntegrationTest.groovy | 71 ++++++++++++++++--- 1 file changed, 63 insertions(+), 8 deletions(-) diff --git a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/RuleEngineIntegrationTest.groovy b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/RuleEngineIntegrationTest.groovy index f3f099b1c..ab5919cb2 100644 --- a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/RuleEngineIntegrationTest.groovy +++ b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/RuleEngineIntegrationTest.groovy @@ -14,6 +14,7 @@ import java.nio.file.Files import java.nio.file.Path class RuleEngineIntegrationTest extends Specification { + def testExampleFilesPath = "../examples/Test" def fhir = HapiFhirImplementation.getInstance() def engine = RuleEngine.getInstance() def mockLogger = Mock(Logger) @@ -44,23 +45,77 @@ class RuleEngineIntegrationTest extends Specification { def "validation doesn't break for any of the sample test messages"() { given: - def exampleFhirFiles = getExampleFhirFiles("Orders") + def exampleFhirResources = getExampleFhirResources("Orders") when: - exampleFhirFiles.each { fhirString -> - def bundle = fhir.parseResource(fhirString as String, Bundle) - engine.validate(new HapiFhirResource(bundle)) + exampleFhirResources.each { resource -> + engine.validate(resource) } then: noExceptionThrown() } - def getExampleFhirFiles(String messageType = "") { - def exampleFilesPath = "../examples/Test" - return Files.walk(Path.of(exampleFilesPath, messageType)) + def "validation rule with resolve() works as expected"() { + given: + def fhirResource = getExampleFhirResource("Orders/001_OML_O21_short.fhir") + def validation = "Bundle.entry.resource.ofType(MessageHeader).focus.resolve().category.exists()" + def rule = createValidationRule([], [validation]) + + when: + def applies = rule.isValid(fhirResource) + + then: + applies + } + + def "validation rules pass for test files"() { + given: + def fhirResource = getExampleFhirResource(testFile) + def rule = createValidationRule([], [validation]) + + expect: + rule.isValid(fhirResource) + + where: + testFile | validation + "Orders/001_OML_O21_short.fhir" | "Bundle.entry.resource.ofType(MessageHeader).focus.resolve().category.exists()" + "Orders/003_AL_ORM_O01_NBS_Fully_Populated_1_hl7_translation.fhir" | "Bundle.entry.resource.ofType(MessageHeader).destination.receiver.resolve().identifier.value.exists()" + // "Orders/003_AL_ORM_O01_NBS_Fully_Populated_1_hl7_translation.fhir" | "Bundle.entry.resource.ofType(Observation).where(code.coding.code = '57723-9').value.coding.code.exists()" + } + + def "validation rules fail for test files"() { + given: + def fhirResource = getExampleFhirResource(testFile) + def rule = createValidationRule([], [validation]) + + expect: + !rule.isValid(fhirResource) + + where: + testFile | validation + "Orders/001_OML_O21_short.fhir" | "Bundle.entry.resource.ofType(MessageHeader).destination.receiver.resolve().identifier.value.exists()" + "Orders/001_OML_O21_short.fhir" | "Bundle.entry.resource.ofType(Observation).where(code.coding.code = '57723-9').value.coding.code.exists()" + } + + Rule createValidationRule(List ruleConditions, List ruleValidations) { + return new ValidationRule( + name: "Rule name", + description: "Rule description", + violationMessage: "Rule warning message", + conditions: ruleConditions, + validations: ruleValidations, + ) + } + + List getExampleFhirResources(String messageType = "") { + return Files.walk(Path.of(testExampleFilesPath, messageType)) .filter { it.toString().endsWith(".fhir") } - .map { Files.readString(it) } + .map { new HapiFhirResource(fhir.parseResource(Files.readString(it), Bundle)) } .collect() } + + HapiFhirResource getExampleFhirResource(String relativeFilePath) { + return new HapiFhirResource(fhir.parseResource(Files.readString(Path.of(testExampleFilesPath, relativeFilePath)), Bundle)) + } } From e46ccf535ca6c0f3ec3dff73f9427e310cb4b820 Mon Sep 17 00:00:00 2001 From: Basilio Bogado <541149+basiliskus@users.noreply.github.com> Date: Thu, 28 Mar 2024 17:32:22 -0700 Subject: [PATCH 09/13] Added comment --- .../etor/ruleengine/RuleEngineIntegrationTest.groovy | 1 + 1 file changed, 1 insertion(+) diff --git a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/RuleEngineIntegrationTest.groovy b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/RuleEngineIntegrationTest.groovy index ab5919cb2..9dc336ac6 100644 --- a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/RuleEngineIntegrationTest.groovy +++ b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/RuleEngineIntegrationTest.groovy @@ -81,6 +81,7 @@ class RuleEngineIntegrationTest extends Specification { testFile | validation "Orders/001_OML_O21_short.fhir" | "Bundle.entry.resource.ofType(MessageHeader).focus.resolve().category.exists()" "Orders/003_AL_ORM_O01_NBS_Fully_Populated_1_hl7_translation.fhir" | "Bundle.entry.resource.ofType(MessageHeader).destination.receiver.resolve().identifier.value.exists()" + // Once we fix the mapping for ORM from story #900, we can uncomment this line // "Orders/003_AL_ORM_O01_NBS_Fully_Populated_1_hl7_translation.fhir" | "Bundle.entry.resource.ofType(Observation).where(code.coding.code = '57723-9').value.coding.code.exists()" } From 7f8d0f2cd99f9c552c10bb2844a48a8c1d5ee7a9 Mon Sep 17 00:00:00 2001 From: Basilio Bogado <541149+basiliskus@users.noreply.github.com> Date: Fri, 29 Mar 2024 08:26:00 -0700 Subject: [PATCH 10/13] Updated comment --- .../etor/ruleengine/RuleEngineIntegrationTest.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/RuleEngineIntegrationTest.groovy b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/RuleEngineIntegrationTest.groovy index 9dc336ac6..1443cd964 100644 --- a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/RuleEngineIntegrationTest.groovy +++ b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/RuleEngineIntegrationTest.groovy @@ -81,7 +81,7 @@ class RuleEngineIntegrationTest extends Specification { testFile | validation "Orders/001_OML_O21_short.fhir" | "Bundle.entry.resource.ofType(MessageHeader).focus.resolve().category.exists()" "Orders/003_AL_ORM_O01_NBS_Fully_Populated_1_hl7_translation.fhir" | "Bundle.entry.resource.ofType(MessageHeader).destination.receiver.resolve().identifier.value.exists()" - // Once we fix the mapping for ORM from story #900, we can uncomment this line + // Once we fix the mapping for ORM from story #900 and update the FHIR files in /examples/Test/Orders, we can uncomment the below line // "Orders/003_AL_ORM_O01_NBS_Fully_Populated_1_hl7_translation.fhir" | "Bundle.entry.resource.ofType(Observation).where(code.coding.code = '57723-9').value.coding.code.exists()" } From c00216d34f005753de88e29c4303c73c00a723e5 Mon Sep 17 00:00:00 2001 From: Luis Pabon Date: Fri, 29 Mar 2024 11:53:18 -0400 Subject: [PATCH 11/13] Create HapiFhirCustomEvaluationContextTest.groovy --- ...HapiFhirCustomEvaluationContextTest.groovy | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 shared/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/hapi/HapiFhirCustomEvaluationContextTest.groovy diff --git a/shared/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/hapi/HapiFhirCustomEvaluationContextTest.groovy b/shared/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/hapi/HapiFhirCustomEvaluationContextTest.groovy new file mode 100644 index 000000000..1e5db9af2 --- /dev/null +++ b/shared/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/hapi/HapiFhirCustomEvaluationContextTest.groovy @@ -0,0 +1,43 @@ +package gov.hhs.cdc.trustedintermediary.external.hapi + +import ca.uhn.fhir.model.primitive.IdDt +import org.hl7.fhir.r4.model.Organization +import org.hl7.fhir.r4.model.Reference +import spock.lang.Specification + +class HapiFhirCustomEvaluationContextTest extends Specification { + def context = new HapiFhirCustomEvaluationContext() + + def "resolveReference returns null if the context is NOT provided"() { + when: + def result = context.resolveReference(new IdDt(), null) + + then: + result == null + } + + def "resolveReference attempts to get a resource if the context is provided and is a reference"() { + given: + def refId = new IdDt() + def org = new Organization() + def theContext = new Reference(org) + + when: + def result = context.resolveReference(refId, theContext) + + then: + result == org + } + + def "resolveReference returns the given context if it's NOT a reference"() { + given: + def refId = new IdDt() + def org = new Organization() + + when: + def result = context.resolveReference(refId, org) + + then: + result == org + } +} From 47b74bd3d9ec80f232c6ccd0777860a5415c2514 Mon Sep 17 00:00:00 2001 From: Luis Pabon Date: Fri, 29 Mar 2024 11:53:51 -0400 Subject: [PATCH 12/13] Update HapiFhirCustomEvaluationContext.java Handle reference resolving if the input is provided but not a reference --- .../external/hapi/HapiFhirCustomEvaluationContext.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiFhirCustomEvaluationContext.java b/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiFhirCustomEvaluationContext.java index ecb1fdd86..509307d6b 100644 --- a/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiFhirCustomEvaluationContext.java +++ b/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiFhirCustomEvaluationContext.java @@ -20,7 +20,10 @@ public class HapiFhirCustomEvaluationContext implements IFhirPathEvaluationConte @Override public IBase resolveReference(@Nonnull IIdType theReference, @Nullable IBase theContext) { if (theContext != null) { - return ((Reference) theContext).getResource(); + if (theContext.getClass() == Reference.class) { + return ((Reference) theContext).getResource(); + } + return theContext; } return IFhirPathEvaluationContext.super.resolveReference(theReference, null); } From b979d6b422ef1f36d225ddf450a594646915b784 Mon Sep 17 00:00:00 2001 From: Basilio Bogado <541149+basiliskus@users.noreply.github.com> Date: Fri, 29 Mar 2024 13:48:08 -0700 Subject: [PATCH 13/13] Moved method based on feedback --- .../external/hapi/HapiFhirImplementation.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiFhirImplementation.java b/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiFhirImplementation.java index 2aaffc657..d0ae43926 100644 --- a/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiFhirImplementation.java +++ b/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiFhirImplementation.java @@ -18,6 +18,10 @@ public class HapiFhirImplementation implements HapiFhir { private HapiFhirImplementation() {} + public static HapiFhirImplementation getInstance() { + return INSTANCE; + } + /** * Creates FHIRPath engine with a custom evaluation context. * @@ -29,10 +33,6 @@ private static IFhirPath createEngine() { return engine; } - public static HapiFhirImplementation getInstance() { - return INSTANCE; - } - @Override public T parseResource( final String fhirResource, final Class clazz) throws FhirParseException {