Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,18 +1,9 @@
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;
Expand All @@ -26,53 +17,4 @@
ElasticsearchConfig.class,
StarterIpsConfig.class
})
public class FhirServerConfigR4 {

@Bean(name = "myHybridRemoteValidationSupportChain")
@Conditional({OnR4Condition.class, OnRemoteTerminologyPresent.class})
public IValidationSupport addRemoteValidation(
ValidationSupportChain theValidationSupport, FhirContext theFhirContext, AppProperties theAppProperties) {
var values = theAppProperties.getRemoteTerminologyServicesMap().values();

// If the remote terminology service is "*" and is the only one then forward all requests to the remote
// terminology service
if (values.size() == 1 && "*".equalsIgnoreCase(values.iterator().next().getSystem())) {
var remoteSystem = values.iterator().next();
theValidationSupport.addValidationSupport(
0, new RemoteTerminologyServiceValidationSupport(theFhirContext, remoteSystem.getUrl()));
return theValidationSupport;

// If there are multiple remote terminology services, then add each one to the validation chain
} else {
values.forEach((remoteSystem) -> theValidationSupport.addValidationSupport(
0, new RemoteTerminologyServiceValidationSupport(theFhirContext, remoteSystem.getUrl()) {
@Override
public boolean isCodeSystemSupported(
ValidationSupportContext theValidationSupportContext, String theSystem) {
return remoteSystem.getSystem().equalsIgnoreCase(theSystem);
}

@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;
}
}
public class FhirServerConfigR4 {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package ca.uhn.fhir.jpa.starter.terminology;

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.starter.AppProperties;
import ca.uhn.fhir.jpa.starter.common.StarterJpaConfig;
import ca.uhn.fhir.jpa.starter.common.validation.OnRemoteTerminologyPresent;
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;

@Configuration
@Conditional(OnRemoteTerminologyPresent.class)
@Import(StarterJpaConfig.class)
public class TerminologyConfig {

@Bean(name = "myHybridRemoteValidationSupportChain")
public IValidationSupport addRemoteValidation(
ValidationSupportChain theValidationSupport, FhirContext theFhirContext, AppProperties theAppProperties) {
var values = theAppProperties.getRemoteTerminologyServicesMap().values();

// If the remote terminology service is "*" and is the only one then forward all requests to the remote
// terminology service
if (values.size() == 1 && "*".equalsIgnoreCase(values.iterator().next().getSystem())) {
var remoteSystem = values.iterator().next();
theValidationSupport.addValidationSupport(
0, new RemoteTerminologyServiceValidationSupport(theFhirContext, remoteSystem.getUrl()));
return theValidationSupport;

// If there are multiple remote terminology services, then add each one to the validation chain
} else {
values.forEach((remoteSystem) -> theValidationSupport.addValidationSupport(
0, new RemoteTerminologyServiceValidationSupport(theFhirContext, remoteSystem.getUrl()) {
@Override
public boolean isCodeSystemSupported(
ValidationSupportContext theValidationSupportContext, String theSystem) {
return remoteSystem.getSystem().equalsIgnoreCase(theSystem);
}

@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;
}
}
27 changes: 20 additions & 7 deletions src/test/java/ca/uhn/fhir/jpa/starter/ExampleServerR5IT.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,7 @@
import jakarta.websocket.Session;
import jakarta.websocket.WebSocketContainer;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r5.model.Bundle;
import org.hl7.fhir.r5.model.Enumerations;
import org.hl7.fhir.r5.model.Observation;
import org.hl7.fhir.r5.model.Patient;
import org.hl7.fhir.r5.model.Subscription;
import org.hl7.fhir.r5.model.SubscriptionTopic;
import org.hl7.fhir.r5.model.*;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
Expand All @@ -36,7 +31,9 @@
"spring.datasource.url=jdbc:h2:mem:dbr5",
"hapi.fhir.fhir_version=r5",
"hapi.fhir.cr_enabled=false",
"hapi.fhir.subscription.websocket_enabled=true"
"hapi.fhir.subscription.websocket_enabled=true",
"hapi.fhir.remote_terminology_service.snomed.system=http://snomed.info/sct",
"hapi.fhir.remote_terminology_service.snomed.url=https://tx.fhir.org/r5"
})
public class ExampleServerR5IT {

Expand Down Expand Up @@ -156,6 +153,22 @@ void testWebsocketSubscription() throws Exception {
ourClient.delete().resourceById(mySubscriptionId).execute();
}


@Test
void testValidateRemoteTerminology() {

String testCodeSystem = "http://foo/cs";
String testValueSet = "http://foo/vs";
ourClient.create().resource(new CodeSystem().setUrl(testCodeSystem).addConcept(new CodeSystem.ConceptDefinitionComponent().setCode("yes")).addConcept(new CodeSystem.ConceptDefinitionComponent().setCode("no"))).execute();
ourClient.create().resource(new ValueSet().setUrl(testValueSet).setCompose(new ValueSet.ValueSetComposeComponent().addInclude(new ValueSet.ConceptSetComponent().setSystem(testValueSet)))).execute();

Parameters remoteResult = ourClient.operation().onType(ValueSet.class).named("$validate-code").withParameter(Parameters.class, "code", new StringType("22298006")).andParameter("system", new UriType("http://snomed.info/sct")).execute();
assertEquals(true, ((BooleanType) remoteResult.getParameterValue("result")).getValue());
assertEquals("Myocardial infarction", ((StringType) remoteResult.getParameterValue("display")).getValue());

Parameters localResult = ourClient.operation().onType(CodeSystem.class).named("$validate-code").withParameter(Parameters.class, "url", new UrlType(testCodeSystem)).andParameter("coding", new Coding(testCodeSystem, "yes", null)).execute();
}

@BeforeEach
void beforeEach() {

Expand Down
Loading