From 9cf166e765415c8cb185ab0ac031a404c7a27618 Mon Sep 17 00:00:00 2001 From: Jens Kristian Villadsen Date: Wed, 19 Mar 2025 13:46:51 +0100 Subject: [PATCH] Added support for hybrid remote terminology --- .../uhn/fhir/jpa/starter/AppProperties.java | 30 ++++++++++++++++++ .../starter/common/FhirServerConfigR4.java | 31 ++++++++++++++++++- .../OnRemoteTerminologyPresent.java | 20 ++++++++++++ src/main/resources/application.yaml | 7 +++++ 4 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 src/main/java/ca/uhn/fhir/jpa/starter/common/validation/OnRemoteTerminologyPresent.java diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java b/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java index 25ad070..87b1aea 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/AppProperties.java @@ -106,6 +106,8 @@ public class AppProperties { private Integer pre_expand_value_sets_max_count = 1000; private Integer maximum_expansion_size = 1000; + private Map remote_terminology_service = null; + public List getCustomInterceptorClasses() { return custom_interceptor_classes; } @@ -723,6 +725,14 @@ public class AppProperties { this.maximum_expansion_size = maximum_expansion_size; } + public Map getRemoteTerminologyServicesMap() { + return remote_terminology_service; + } + + public void setRemote_terminology_service(Map remote_terminology_service) { + this.remote_terminology_service = remote_terminology_service; + } + public static class Cors { private Boolean allow_Credentials = true; private List allowed_origin = List.of("*"); @@ -918,6 +928,26 @@ public class AppProperties { request_tenant_partitioning_mode = theRequest_tenant_partitioning_mode; } } + public static class RemoteSystem{ + private String system; + private String url; + + public String getSystem() { + return system; + } + + public void setSystem(String system) { + this.system = system; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + } public static class Subscription { diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigR4.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigR4.java index 55dce56..ca0ca8e 100644 --- a/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigR4.java +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/FhirServerConfigR4.java @@ -1,9 +1,18 @@ package ca.uhn.fhir.jpa.starter.common; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.support.ConceptValidationOptions; +import ca.uhn.fhir.context.support.IValidationSupport; +import ca.uhn.fhir.context.support.ValidationSupportContext; import ca.uhn.fhir.jpa.config.r4.JpaR4Config; +import ca.uhn.fhir.jpa.starter.AppProperties; import ca.uhn.fhir.jpa.starter.annotations.OnR4Condition; +import ca.uhn.fhir.jpa.starter.common.validation.OnRemoteTerminologyPresent; import ca.uhn.fhir.jpa.starter.cr.StarterCrR4Config; import ca.uhn.fhir.jpa.starter.ips.StarterIpsConfig; +import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport; +import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -17,4 +26,24 @@ import org.springframework.context.annotation.Import; ElasticsearchConfig.class, StarterIpsConfig.class }) -public class FhirServerConfigR4 {} +public class FhirServerConfigR4 { + + @Bean(name = "myHybridRemoteValidationSupportChain") + @Conditional({OnR4Condition.class, OnRemoteTerminologyPresent.class}) + public IValidationSupport addRemoteValidation(ValidationSupportChain theValidationSupport, FhirContext theFhirContext, AppProperties theAppProperties) + { + theAppProperties.getRemoteTerminologyServicesMap().forEach((key, remoteSystem) -> { + theValidationSupport.addValidationSupport(0 , new RemoteTerminologyServiceValidationSupport(theFhirContext, remoteSystem.getUrl()){ + @Override + public CodeValidationResult validateCode(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) { + if (remoteSystem.getSystem().equalsIgnoreCase(theCodeSystem)) { + return super.validateCode(theValidationSupportContext, theOptions, theCodeSystem, theCode, theDisplay, theValueSetUrl); + } + return null; + } + }); + }); + + return theValidationSupport; + } +} diff --git a/src/main/java/ca/uhn/fhir/jpa/starter/common/validation/OnRemoteTerminologyPresent.java b/src/main/java/ca/uhn/fhir/jpa/starter/common/validation/OnRemoteTerminologyPresent.java new file mode 100644 index 0000000..03e444b --- /dev/null +++ b/src/main/java/ca/uhn/fhir/jpa/starter/common/validation/OnRemoteTerminologyPresent.java @@ -0,0 +1,20 @@ +package ca.uhn.fhir.jpa.starter.common.validation; + +import ca.uhn.fhir.jpa.starter.AppProperties; +import org.springframework.boot.context.properties.bind.Binder; +import org.springframework.context.annotation.Condition; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.core.type.AnnotatedTypeMetadata; + +public class OnRemoteTerminologyPresent implements Condition { + @Override + public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata) { + + AppProperties config = Binder.get(conditionContext.getEnvironment()) + .bind("hapi.fhir", AppProperties.class) + .orElse(null); + if (config == null) return false; + if (config.getRemoteTerminologyServicesMap() == null) return false; + return !config.getRemoteTerminologyServicesMap().isEmpty(); + } +} diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index a0756d6..cd66379 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -289,6 +289,13 @@ hapi: # max_page_size: 200 # retain_cached_searches_mins: 60 # reuse_cached_search_results_millis: 60000 + remote_terminology_service: + snomed: + system: 'http://snomed.info/sct' + url: 'https://tx.fhir.org/r4/' + loinc: + system: 'http://loinc.org' + url: 'https://fhir.loinc.org/' tester: home: name: Local Tester