3136 - Provide RemoteTerminologyServiceValidationSupport Implementation For Operation $validate-code (#3142)
* 3136 - Send validate-code requests to an Instance of IValidationSupport instead of directly to the DAO. * 3136 - Added support for R4 validate-code calls to a Remote Terminology Server. * Added more Unit Tests. * Added NULL checks based on code analysis. * Adding some cleanup code to the Unit Tests since some other unrelated Unit Tests were failing when running in an Azure Pipeline. * Adding @Ignore to both of my Unit Test classes to try and see if this is the cause of other unrelated Unit Test failures in the Azure Pipeline. * Commented out ALL CODE in my Unit Tests to see if this fixes the other Unit Tests. * Added 1 Unit Tester called RemoteTerminologyServiceResourceProviderR4Test to see if it causes other Unit Test failures. * Added a Unit Tester called ResourceProviderR4RemoteTerminologyTest with the code that creates a RemoteTerminologyServiceValidationSupport instance commented out for now, just to see if this allows all other Unit Tests to pass. * Added a Unit test back in to verify how the RemoteTerminologyServiceValidationSupport works when inserted into the front of the IValidation chain. Lets see if this will mess up any other Unit Tests in the Azure Pipeline... * Added an @AfterEach method to remove the RemoteTerminologyServiceValidationSupport instance from the ValidationSupportChain to see if it allows all the other Unit Tests to work as before. * Added more Unit tests. * Added more Unit Tests to increase code coverage. * Added some NULL checks. * Added a changelog for this feature add. * Removed the decision logic in the REST APIs and also the isRemoteTerminologyServiceConfigured() method, and letting the ValidationSupportChain figure out how to perform the validate-code Operation. * Removed a NULL check that was not needed. * Reverting the previous change where I tried using the IValidationSupportChain for each and every incoming Operation request. Splitting the implementation between local and remote seems to be required at this time.
This commit is contained in:
parent
35dedb8628
commit
3c2b4bb397
|
@ -215,6 +215,15 @@ public interface IValidationSupport {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if a Remote Terminology Service is currently configured
|
||||
*
|
||||
* @return Returns <code>true</code> if a Remote Terminology Service is currently configured
|
||||
*/
|
||||
default boolean isRemoteTerminologyServiceConfigured() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the given ValueSet by URL
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
type: add
|
||||
issue: 3136
|
||||
title: "Provided a Remote Terminology Service implementation for the $validate-code Operation."
|
|
@ -20,11 +20,14 @@ package ca.uhn.fhir.jpa.provider;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
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.context.support.ValueSetExpansionOptions;
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoValueSet;
|
||||
import ca.uhn.fhir.jpa.config.BaseConfig;
|
||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermReadSvc;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
|
@ -34,6 +37,7 @@ import ca.uhn.fhir.rest.api.server.RequestDetails;
|
|||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.provider.ProviderConstants;
|
||||
import ca.uhn.fhir.util.ParametersUtil;
|
||||
import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain;
|
||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.ICompositeType;
|
||||
|
@ -42,6 +46,7 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
|||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
|
@ -56,6 +61,9 @@ public class ValueSetOperationProvider extends BaseJpaProvider {
|
|||
private DaoRegistry myDaoRegistry;
|
||||
@Autowired
|
||||
private ITermReadSvc myTermReadSvc;
|
||||
@Autowired
|
||||
@Qualifier(BaseConfig.JPA_VALIDATION_SUPPORT_CHAIN)
|
||||
private ValidationSupportChain myValidationSupportChain;
|
||||
|
||||
public void setDaoConfig(DaoConfig theDaoConfig) {
|
||||
myDaoConfig = theDaoConfig;
|
||||
|
@ -142,24 +150,37 @@ public class ValueSetOperationProvider extends BaseJpaProvider {
|
|||
RequestDetails theRequestDetails
|
||||
) {
|
||||
|
||||
IValidationSupport.CodeValidationResult result;
|
||||
startRequest(theServletRequest);
|
||||
try {
|
||||
IFhirResourceDaoValueSet<IBaseResource, ICompositeType, ICompositeType> dao = getDao();
|
||||
IPrimitiveType<String> valueSetIdentifier;
|
||||
if (theValueSetVersion != null) {
|
||||
valueSetIdentifier = (IPrimitiveType<String>) getContext().getElementDefinition("uri").newInstance();
|
||||
valueSetIdentifier.setValue(theValueSetUrl.getValue() + "|" + theValueSetVersion);
|
||||
// If a Remote Terminology Server has been configured, use it
|
||||
if (myValidationSupportChain.isRemoteTerminologyServiceConfigured()) {
|
||||
String theSystemString = (theSystem != null && theSystem.hasValue()) ? theSystem.getValueAsString() : null;
|
||||
String theCodeString = (theCode != null && theCode.hasValue()) ? theCode.getValueAsString() : null;
|
||||
String theDisplayString = (theDisplay != null && theDisplay.hasValue()) ? theDisplay.getValueAsString() : null;
|
||||
String theValueSetUrlString = (theValueSetUrl != null && theValueSetUrl.hasValue()) ?
|
||||
theValueSetUrl.getValueAsString() : null;
|
||||
result = myValidationSupportChain.validateCode(new ValidationSupportContext(myValidationSupportChain),
|
||||
new ConceptValidationOptions(), theSystemString, theCodeString, theDisplayString, theValueSetUrlString);
|
||||
} else {
|
||||
valueSetIdentifier = theValueSetUrl;
|
||||
// Otherwise, use the local DAO layer to validate the code
|
||||
IFhirResourceDaoValueSet<IBaseResource, ICompositeType, ICompositeType> dao = getDao();
|
||||
IPrimitiveType<String> valueSetIdentifier;
|
||||
if (theValueSetUrl != null && theValueSetVersion != null) {
|
||||
valueSetIdentifier = (IPrimitiveType<String>) getContext().getElementDefinition("uri").newInstance();
|
||||
valueSetIdentifier.setValue(theValueSetUrl.getValue() + "|" + theValueSetVersion);
|
||||
} else {
|
||||
valueSetIdentifier = theValueSetUrl;
|
||||
}
|
||||
IPrimitiveType<String> codeSystemIdentifier;
|
||||
if (theSystem != null && theSystemVersion != null) {
|
||||
codeSystemIdentifier = (IPrimitiveType<String>) getContext().getElementDefinition("uri").newInstance();
|
||||
codeSystemIdentifier.setValue(theSystem.getValue() + "|" + theSystemVersion);
|
||||
} else {
|
||||
codeSystemIdentifier = theSystem;
|
||||
}
|
||||
result = dao.validateCode(valueSetIdentifier, theId, theCode, codeSystemIdentifier, theDisplay, theCoding, theCodeableConcept, theRequestDetails);
|
||||
}
|
||||
IPrimitiveType<String> codeSystemIdentifier;
|
||||
if (theSystemVersion != null) {
|
||||
codeSystemIdentifier = (IPrimitiveType<String>) getContext().getElementDefinition("uri").newInstance();
|
||||
codeSystemIdentifier.setValue(theSystem.getValue() + "|" + theSystemVersion);
|
||||
} else {
|
||||
codeSystemIdentifier = theSystem;
|
||||
}
|
||||
IValidationSupport.CodeValidationResult result = dao.validateCode(valueSetIdentifier, theId, theCode, codeSystemIdentifier, theDisplay, theCoding, theCodeableConcept, theRequestDetails);
|
||||
return BaseJpaResourceProviderValueSetDstu2.toValidateCodeResult(getContext(), result);
|
||||
} finally {
|
||||
endRequest(theServletRequest);
|
||||
|
|
|
@ -1,13 +1,19 @@
|
|||
package ca.uhn.fhir.jpa.provider.r4;
|
||||
|
||||
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.api.dao.IFhirResourceDaoCodeSystem;
|
||||
import ca.uhn.fhir.jpa.config.BaseConfig;
|
||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||
import ca.uhn.fhir.jpa.provider.BaseJpaResourceProviderValueSetDstu2;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermReadSvcR4;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain;
|
||||
import org.hl7.fhir.r4.model.BooleanType;
|
||||
import org.hl7.fhir.r4.model.CodeSystem;
|
||||
import org.hl7.fhir.r4.model.CodeType;
|
||||
|
@ -17,6 +23,8 @@ import org.hl7.fhir.r4.model.IdType;
|
|||
import org.hl7.fhir.r4.model.Parameters;
|
||||
import org.hl7.fhir.r4.model.StringType;
|
||||
import org.hl7.fhir.r4.model.UriType;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.List;
|
||||
|
@ -43,6 +51,13 @@ import java.util.List;
|
|||
|
||||
public class BaseJpaResourceProviderCodeSystemR4 extends JpaResourceProviderR4<CodeSystem> {
|
||||
|
||||
@Autowired
|
||||
@Qualifier(BaseConfig.JPA_VALIDATION_SUPPORT_CHAIN)
|
||||
private ValidationSupportChain myValidationSupportChain;
|
||||
|
||||
@Autowired
|
||||
protected ITermReadSvcR4 myTermSvc;
|
||||
|
||||
/**
|
||||
* $lookup operation
|
||||
*/
|
||||
|
@ -133,11 +148,35 @@ public class BaseJpaResourceProviderCodeSystemR4 extends JpaResourceProviderR4<C
|
|||
RequestDetails theRequestDetails
|
||||
) {
|
||||
|
||||
IValidationSupport.CodeValidationResult result = null;
|
||||
startRequest(theServletRequest);
|
||||
try {
|
||||
IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept> dao = (IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept>) getDao();
|
||||
|
||||
IValidationSupport.CodeValidationResult result = dao.validateCode(theId, theCodeSystemUrl, theVersion, theCode, theDisplay, theCoding, theCodeableConcept, theRequestDetails);
|
||||
// If a Remote Terminology Server has been configured, use it
|
||||
if (myValidationSupportChain.isRemoteTerminologyServiceConfigured()) {
|
||||
String codeSystemUrl = (theCodeSystemUrl != null && theCodeSystemUrl.hasValue()) ?
|
||||
theCodeSystemUrl.asStringValue() : null;
|
||||
String code = (theCode != null && theCode.hasValue()) ? theCode.asStringValue() : null;
|
||||
String display = (theDisplay != null && theDisplay.hasValue()) ? theDisplay.asStringValue() : null;
|
||||
|
||||
if (theCoding != null) {
|
||||
if (theCoding.hasSystem()) {
|
||||
if (codeSystemUrl != null && !codeSystemUrl.equalsIgnoreCase(theCoding.getSystem())) {
|
||||
throw new InvalidRequestException("Coding.system '" + theCoding.getSystem() + "' does not equal param url '" + theCodeSystemUrl + "'. Unable to validate-code.");
|
||||
}
|
||||
codeSystemUrl = theCoding.getSystem();
|
||||
code = theCoding.getCode();
|
||||
display = theCoding.getDisplay();
|
||||
|
||||
result = myValidationSupportChain.validateCode(
|
||||
new ValidationSupportContext(myValidationSupportChain), new ConceptValidationOptions(),
|
||||
codeSystemUrl, code, display, null);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Otherwise, use the local DAO layer to validate the code
|
||||
IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept> dao = (IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept>) getDao();
|
||||
result = dao.validateCode(theId, theCodeSystemUrl, theVersion, theCode, theDisplay, theCoding, theCodeableConcept, theRequestDetails);
|
||||
}
|
||||
return (Parameters) BaseJpaResourceProviderValueSetDstu2.toValidateCodeResult(getContext(), result);
|
||||
} finally {
|
||||
endRequest(theServletRequest);
|
||||
|
|
|
@ -0,0 +1,257 @@
|
|||
package ca.uhn.fhir.jpa.provider.r4;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
||||
import ca.uhn.fhir.rest.annotation.Search;
|
||||
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
|
||||
import ca.uhn.fhir.rest.param.UriParam;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
||||
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r4.model.BooleanType;
|
||||
import org.hl7.fhir.r4.model.CodeSystem;
|
||||
import org.hl7.fhir.r4.model.CodeType;
|
||||
import org.hl7.fhir.r4.model.IdType;
|
||||
import org.hl7.fhir.r4.model.Parameters;
|
||||
import org.hl7.fhir.r4.model.StringType;
|
||||
import org.hl7.fhir.r4.model.UriType;
|
||||
import org.hl7.fhir.r4.model.ValueSet;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
/*
|
||||
* This set of Unit Tests simulates the call to a remote server and therefore, only tests the code in the
|
||||
* {@link org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport#invokeRemoteValidateCode}
|
||||
* method, before and after it makes that remote client call.
|
||||
*/
|
||||
public class RemoteTerminologyServiceResourceProviderR4Test {
|
||||
private static final String DISPLAY = "DISPLAY";
|
||||
private static final String CODE_SYSTEM = "CODE_SYS";
|
||||
private static final String CODE = "CODE";
|
||||
private static final String VALUE_SET_URL = "http://value.set/url";
|
||||
private static final String SAMPLE_MESSAGE = "This is a sample message";
|
||||
private static FhirContext ourCtx = FhirContext.forR4();
|
||||
private MyCodeSystemProvider myCodeSystemProvider = new MyCodeSystemProvider();
|
||||
private MyValueSetProvider myValueSetProvider = new MyValueSetProvider();
|
||||
|
||||
@RegisterExtension
|
||||
public RestfulServerExtension myRestfulServerExtension = new RestfulServerExtension(ourCtx, myCodeSystemProvider,
|
||||
myValueSetProvider);
|
||||
|
||||
private RemoteTerminologyServiceValidationSupport mySvc;
|
||||
|
||||
@BeforeEach
|
||||
public void before_ConfigureService() {
|
||||
String myBaseUrl = "http://localhost:" + myRestfulServerExtension.getPort();
|
||||
mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx, myBaseUrl);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void after_UnregisterProviders() {
|
||||
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE);
|
||||
myRestfulServerExtension.getRestfulServer().getInterceptorService().unregisterAllInterceptors();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateCodeInCodeSystem_BlankCode_ReturnsNull() {
|
||||
IValidationSupport.CodeValidationResult outcome = mySvc
|
||||
.validateCode(null, null, CODE_SYSTEM, null, DISPLAY, null);
|
||||
assertNull(outcome);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateCodeInCodeSystem_ProvidingMinimalInputs_ReturnsSuccess() {
|
||||
createNextCodeSystemReturnParameters(true, null, null);
|
||||
|
||||
IValidationSupport.CodeValidationResult outcome = mySvc
|
||||
.validateCode(null, null, CODE_SYSTEM, CODE, null, null);
|
||||
assertEquals(CODE, outcome.getCode());
|
||||
assertEquals(null, outcome.getSeverity());
|
||||
assertEquals(null, outcome.getMessage());
|
||||
|
||||
assertEquals(CODE, myCodeSystemProvider.myLastCode.getCode());
|
||||
assertEquals(CODE_SYSTEM, myCodeSystemProvider.myLastUrl.getValueAsString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateCodeInCodeSystem_WithMessageValue_ReturnsMessage() {
|
||||
createNextCodeSystemReturnParameters(true, DISPLAY, SAMPLE_MESSAGE);
|
||||
|
||||
IValidationSupport.CodeValidationResult outcome = mySvc
|
||||
.validateCode(null, null, CODE_SYSTEM, CODE, DISPLAY, null);
|
||||
assertEquals(CODE, outcome.getCode());
|
||||
assertEquals(DISPLAY, outcome.getDisplay());
|
||||
assertEquals(null, outcome.getSeverity());
|
||||
assertEquals(null, outcome.getMessage());
|
||||
|
||||
assertEquals(CODE, myCodeSystemProvider.myLastCode.getCode());
|
||||
assertEquals(DISPLAY, myCodeSystemProvider.myLastDisplay.getValue());
|
||||
assertEquals(CODE_SYSTEM, myCodeSystemProvider.myLastUrl.getValueAsString());
|
||||
assertEquals(SAMPLE_MESSAGE, myCodeSystemProvider.myNextReturnParams.getParameter("message").toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateCodeInCodeSystem_AssumeFailure_ReturnsFailureCodeAndFailureMessage() {
|
||||
createNextCodeSystemReturnParameters(false, null, SAMPLE_MESSAGE);
|
||||
|
||||
IValidationSupport.CodeValidationResult outcome = mySvc
|
||||
.validateCode(null, null, CODE_SYSTEM, CODE, null, null);
|
||||
assertEquals(IValidationSupport.IssueSeverity.ERROR, outcome.getSeverity());
|
||||
assertEquals(SAMPLE_MESSAGE, outcome.getMessage());
|
||||
|
||||
assertEquals(false, ((BooleanType)myCodeSystemProvider.myNextReturnParams.getParameter("result")).booleanValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateCodeInValueSet_ProvidingMinimalInputs_ReturnsSuccess() {
|
||||
createNextValueSetReturnParameters(true, null, null);
|
||||
|
||||
IValidationSupport.CodeValidationResult outcome = mySvc
|
||||
.validateCode(null, null, CODE_SYSTEM, CODE, null, VALUE_SET_URL);
|
||||
assertEquals(CODE, outcome.getCode());
|
||||
assertEquals(null, outcome.getSeverity());
|
||||
assertEquals(null, outcome.getMessage());
|
||||
|
||||
assertEquals(CODE, myValueSetProvider.myLastCode.getCode());
|
||||
assertEquals(VALUE_SET_URL, myValueSetProvider.myLastUrl.getValueAsString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateCodeInValueSet_WithMessageValue_ReturnsMessage() {
|
||||
createNextValueSetReturnParameters(true, DISPLAY, SAMPLE_MESSAGE);
|
||||
|
||||
IValidationSupport.CodeValidationResult outcome = mySvc
|
||||
.validateCode(null, null, CODE_SYSTEM, CODE, DISPLAY, VALUE_SET_URL);
|
||||
assertEquals(CODE, outcome.getCode());
|
||||
assertEquals(DISPLAY, outcome.getDisplay());
|
||||
assertEquals(null, outcome.getSeverity());
|
||||
assertEquals(null, outcome.getMessage());
|
||||
|
||||
assertEquals(CODE, myValueSetProvider.myLastCode.getCode());
|
||||
assertEquals(DISPLAY, myValueSetProvider.myLastDisplay.getValue());
|
||||
assertEquals(VALUE_SET_URL, myValueSetProvider.myLastUrl.getValueAsString());
|
||||
assertEquals(SAMPLE_MESSAGE, myValueSetProvider.myNextReturnParams.getParameter("message").toString());
|
||||
}
|
||||
|
||||
private void createNextCodeSystemReturnParameters(boolean theResult, String theDisplay, String theMessage) {
|
||||
myCodeSystemProvider.myNextReturnParams = new Parameters();
|
||||
myCodeSystemProvider.myNextReturnParams.addParameter("result", theResult);
|
||||
myCodeSystemProvider.myNextReturnParams.addParameter("display", theDisplay);
|
||||
if (theMessage != null) {
|
||||
myCodeSystemProvider.myNextReturnParams.addParameter("message", theMessage);
|
||||
}
|
||||
}
|
||||
|
||||
private void createNextValueSetReturnParameters(boolean theResult, String theDisplay, String theMessage) {
|
||||
myValueSetProvider.myNextReturnParams = new Parameters();
|
||||
myValueSetProvider.myNextReturnParams.addParameter("result", theResult);
|
||||
myValueSetProvider.myNextReturnParams.addParameter("display", theDisplay);
|
||||
if (theMessage != null) {
|
||||
myValueSetProvider.myNextReturnParams.addParameter("message", theMessage);
|
||||
}
|
||||
}
|
||||
|
||||
private static class MyCodeSystemProvider implements IResourceProvider {
|
||||
private UriParam myLastUrlParam;
|
||||
private List<CodeSystem> myNextReturnCodeSystems;
|
||||
private int myInvocationCount;
|
||||
private UriType myLastUrl;
|
||||
private CodeType myLastCode;
|
||||
private StringType myLastDisplay;
|
||||
private Parameters myNextReturnParams;
|
||||
|
||||
@Operation(name = "validate-code", idempotent = true, returnParameters = {
|
||||
@OperationParam(name = "result", type = BooleanType.class, min = 1),
|
||||
@OperationParam(name = "message", type = StringType.class),
|
||||
@OperationParam(name = "display", type = StringType.class)
|
||||
})
|
||||
public Parameters validateCode(
|
||||
HttpServletRequest theServletRequest,
|
||||
@IdParam(optional = true) IdType theId,
|
||||
@OperationParam(name = "url", min = 0, max = 1) UriType theCodeSystemUrl,
|
||||
@OperationParam(name = "code", min = 0, max = 1) CodeType theCode,
|
||||
@OperationParam(name = "display", min = 0, max = 1) StringType theDisplay
|
||||
) {
|
||||
myInvocationCount++;
|
||||
myLastUrl = theCodeSystemUrl;
|
||||
myLastCode = theCode;
|
||||
myLastDisplay = theDisplay;
|
||||
return myNextReturnParams;
|
||||
|
||||
}
|
||||
|
||||
@Search
|
||||
public List<CodeSystem> find(@RequiredParam(name = "url") UriParam theUrlParam) {
|
||||
myLastUrlParam = theUrlParam;
|
||||
assert myNextReturnCodeSystems != null;
|
||||
return myNextReturnCodeSystems;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return CodeSystem.class;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class MyValueSetProvider implements IResourceProvider {
|
||||
private Parameters myNextReturnParams;
|
||||
private List<ValueSet> myNextReturnValueSets;
|
||||
private UriType myLastUrl;
|
||||
private CodeType myLastCode;
|
||||
private int myInvocationCount;
|
||||
private UriType myLastSystem;
|
||||
private StringType myLastDisplay;
|
||||
private ValueSet myLastValueSet;
|
||||
private UriParam myLastUrlParam;
|
||||
|
||||
@Operation(name = "validate-code", idempotent = true, returnParameters = {
|
||||
@OperationParam(name = "result", type = BooleanType.class, min = 1),
|
||||
@OperationParam(name = "message", type = StringType.class),
|
||||
@OperationParam(name = "display", type = StringType.class)
|
||||
})
|
||||
public Parameters validateCode(
|
||||
HttpServletRequest theServletRequest,
|
||||
@IdParam(optional = true) IdType theId,
|
||||
@OperationParam(name = "url", min = 0, max = 1) UriType theValueSetUrl,
|
||||
@OperationParam(name = "code", min = 0, max = 1) CodeType theCode,
|
||||
@OperationParam(name = "system", min = 0, max = 1) UriType theSystem,
|
||||
@OperationParam(name = "display", min = 0, max = 1) StringType theDisplay,
|
||||
@OperationParam(name = "valueSet") ValueSet theValueSet
|
||||
) {
|
||||
myInvocationCount++;
|
||||
myLastUrl = theValueSetUrl;
|
||||
myLastCode = theCode;
|
||||
myLastSystem = theSystem;
|
||||
myLastDisplay = theDisplay;
|
||||
myLastValueSet = theValueSet;
|
||||
return myNextReturnParams;
|
||||
}
|
||||
|
||||
@Search
|
||||
public List<ValueSet> find(@RequiredParam(name = "url") UriParam theUrlParam) {
|
||||
myLastUrlParam = theUrlParam;
|
||||
assert myNextReturnValueSets != null;
|
||||
return myNextReturnValueSets;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return ValueSet.class;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,270 @@
|
|||
package ca.uhn.fhir.jpa.provider.r4;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.config.BaseConfig;
|
||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
||||
import ca.uhn.fhir.rest.annotation.Search;
|
||||
import ca.uhn.fhir.rest.param.UriParam;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
||||
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
|
||||
import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r4.model.BooleanType;
|
||||
import org.hl7.fhir.r4.model.CodeSystem;
|
||||
import org.hl7.fhir.r4.model.CodeType;
|
||||
import org.hl7.fhir.r4.model.Coding;
|
||||
import org.hl7.fhir.r4.model.IdType;
|
||||
import org.hl7.fhir.r4.model.Parameters;
|
||||
import org.hl7.fhir.r4.model.StringType;
|
||||
import org.hl7.fhir.r4.model.UriType;
|
||||
import org.hl7.fhir.r4.model.ValueSet;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
/*
|
||||
* This set of Unit Tests instantiates and injects an instance of
|
||||
* {@link org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport}
|
||||
* into the ValidationSupportChain, which tests the logic of dynamically selecting the correct Remote Terminology
|
||||
* implementation. It also exercises the code found in
|
||||
* {@link org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport#invokeRemoteValidateCode}
|
||||
*/
|
||||
public class ResourceProviderR4RemoteTerminologyTest extends BaseResourceProviderR4Test {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderR4RemoteTerminologyTest.class);
|
||||
private static final String DISPLAY = "DISPLAY";
|
||||
private static final String DISPLAY_BODY_MASS_INDEX = "Body mass index (BMI) [Ratio]";
|
||||
private static final String CODE_BODY_MASS_INDEX = "39156-5";
|
||||
private static FhirContext ourCtx = FhirContext.forR4();
|
||||
private MyCodeSystemProvider myCodeSystemProvider = new MyCodeSystemProvider();
|
||||
private MyValueSetProvider myValueSetProvider = new MyValueSetProvider();
|
||||
|
||||
@RegisterExtension
|
||||
public RestfulServerExtension myRestfulServerExtension = new RestfulServerExtension(ourCtx, myCodeSystemProvider,
|
||||
myValueSetProvider);
|
||||
|
||||
private RemoteTerminologyServiceValidationSupport mySvc;
|
||||
|
||||
@Autowired
|
||||
@Qualifier(BaseConfig.JPA_VALIDATION_SUPPORT_CHAIN)
|
||||
private ValidationSupportChain myValidationSupportChain;
|
||||
|
||||
@BeforeEach
|
||||
public void before_addRemoteTerminologySupport() throws Exception {
|
||||
String baseUrl = "http://localhost:" + myRestfulServerExtension.getPort();
|
||||
mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx, baseUrl);
|
||||
myValidationSupportChain.addValidationSupport(0, mySvc);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void after_removeRemoteTerminologySupport() {
|
||||
myValidationSupportChain.removeValidationSupport(mySvc);
|
||||
myRestfulServerExtension.getRestfulServer().getInterceptorService().unregisterAllInterceptors();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateCodeOperationOnCodeSystem_ByCodingAndUrlWhereSystemIsDifferent_ThrowsException() {
|
||||
assertThrows(InvalidRequestException.class, () -> {
|
||||
Parameters respParam = myClient
|
||||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named(JpaConstants.OPERATION_VALIDATE_CODE)
|
||||
.withParameter(Parameters.class, "coding", new Coding().setSystem("http://terminology.hl7.org/CodeSystem/v2-0247").setCode("P"))
|
||||
.andParameter("url", new UriType("http://terminology.hl7.org/CodeSystem/INVALID-CODESYSTEM"))
|
||||
.execute();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateCodeOperationOnCodeSystem_ByCodingAndUrl_UsingBuiltInCodeSystems() {
|
||||
myCodeSystemProvider.myNextReturnCodeSystems = new ArrayList<>();
|
||||
myCodeSystemProvider.myNextReturnCodeSystems.add((CodeSystem) new CodeSystem().setId("CodeSystem/v2-0247"));
|
||||
createNextCodeSystemReturnParameters(true, DISPLAY, null);
|
||||
|
||||
Parameters respParam = myClient
|
||||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named(JpaConstants.OPERATION_VALIDATE_CODE)
|
||||
.withParameter(Parameters.class, "coding", new Coding().setSystem("http://terminology.hl7.org/CodeSystem/v2-0247").setCode("P"))
|
||||
.andParameter("url", new UriType("http://terminology.hl7.org/CodeSystem/v2-0247"))
|
||||
.execute();
|
||||
|
||||
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertEquals(true, ((BooleanType)respParam.getParameter("result")).booleanValue());
|
||||
assertEquals(DISPLAY, respParam.getParameter("display").toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateCodeOperationOnValueSet_ByUrlAndSystem_UsingBuiltInCodeSystems() {
|
||||
myCodeSystemProvider.myNextReturnCodeSystems = new ArrayList<>();
|
||||
myCodeSystemProvider.myNextReturnCodeSystems.add((CodeSystem) new CodeSystem().setId("CodeSystem/list-example-use-codes"));
|
||||
myValueSetProvider.myNextReturnValueSets = new ArrayList<>();
|
||||
myValueSetProvider.myNextReturnValueSets.add((ValueSet) new ValueSet().setId("ValueSet/list-example-codes"));
|
||||
createNextValueSetReturnParameters(true, DISPLAY, null);
|
||||
|
||||
Parameters respParam = myClient
|
||||
.operation()
|
||||
.onType(ValueSet.class)
|
||||
.named(JpaConstants.OPERATION_VALIDATE_CODE)
|
||||
.withParameter(Parameters.class, "code", new CodeType("alerts"))
|
||||
.andParameter("system", new UriType("http://terminology.hl7.org/CodeSystem/list-example-use-codes"))
|
||||
.andParameter("url", new UriType("http://hl7.org/fhir/ValueSet/list-example-codes"))
|
||||
.useHttpGet()
|
||||
.execute();
|
||||
|
||||
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertEquals(true, ((BooleanType)respParam.getParameter("result")).booleanValue());
|
||||
assertEquals(DISPLAY, respParam.getParameter("display").toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateCodeOperationOnValueSet_ByUrlSystemAndCode() {
|
||||
myCodeSystemProvider.myNextReturnCodeSystems = new ArrayList<>();
|
||||
myCodeSystemProvider.myNextReturnCodeSystems.add((CodeSystem) new CodeSystem().setId("CodeSystem/list-example-use-codes"));
|
||||
myValueSetProvider.myNextReturnValueSets = new ArrayList<>();
|
||||
myValueSetProvider.myNextReturnValueSets.add((ValueSet) new ValueSet().setId("ValueSet/list-example-codes"));
|
||||
createNextValueSetReturnParameters(true, DISPLAY_BODY_MASS_INDEX, null);
|
||||
|
||||
Parameters respParam = myClient
|
||||
.operation()
|
||||
.onType(ValueSet.class)
|
||||
.named(JpaConstants.OPERATION_VALIDATE_CODE)
|
||||
.withParameter(Parameters.class, "code", new CodeType(CODE_BODY_MASS_INDEX))
|
||||
.andParameter("url", new UriType("https://loinc.org"))
|
||||
.andParameter("system", new UriType("http://loinc.org"))
|
||||
.execute();
|
||||
|
||||
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertEquals(true, ((BooleanType)respParam.getParameter("result")).booleanValue());
|
||||
assertEquals(DISPLAY_BODY_MASS_INDEX, respParam.getParameter("display").toString());
|
||||
}
|
||||
|
||||
private void createNextCodeSystemReturnParameters(boolean theResult, String theDisplay, String theMessage) {
|
||||
myCodeSystemProvider.myNextReturnParams = new Parameters();
|
||||
myCodeSystemProvider.myNextReturnParams.addParameter("result", theResult);
|
||||
myCodeSystemProvider.myNextReturnParams.addParameter("display", theDisplay);
|
||||
if (theMessage != null) {
|
||||
myCodeSystemProvider.myNextReturnParams.addParameter("message", theMessage);
|
||||
}
|
||||
}
|
||||
|
||||
private void createNextValueSetReturnParameters(boolean theResult, String theDisplay, String theMessage) {
|
||||
myValueSetProvider.myNextReturnParams = new Parameters();
|
||||
myValueSetProvider.myNextReturnParams.addParameter("result", theResult);
|
||||
myValueSetProvider.myNextReturnParams.addParameter("display", theDisplay);
|
||||
if (theMessage != null) {
|
||||
myValueSetProvider.myNextReturnParams.addParameter("message", theMessage);
|
||||
}
|
||||
}
|
||||
|
||||
private static class MyCodeSystemProvider implements IResourceProvider {
|
||||
|
||||
private UriParam myLastUrlParam;
|
||||
private List<CodeSystem> myNextReturnCodeSystems;
|
||||
private int myInvocationCount;
|
||||
private UriType myLastUrl;
|
||||
private CodeType myLastCode;
|
||||
private StringType myLastDisplay;
|
||||
private Parameters myNextReturnParams;
|
||||
|
||||
@Operation(name = "validate-code", idempotent = true, returnParameters = {
|
||||
@OperationParam(name = "result", type = BooleanType.class, min = 1),
|
||||
@OperationParam(name = "message", type = StringType.class),
|
||||
@OperationParam(name = "display", type = StringType.class)
|
||||
})
|
||||
public Parameters validateCode(
|
||||
HttpServletRequest theServletRequest,
|
||||
@IdParam(optional = true) IdType theId,
|
||||
@OperationParam(name = "url", min = 0, max = 1) UriType theCodeSystemUrl,
|
||||
@OperationParam(name = "code", min = 0, max = 1) CodeType theCode,
|
||||
@OperationParam(name = "display", min = 0, max = 1) StringType theDisplay
|
||||
) {
|
||||
myInvocationCount++;
|
||||
myLastUrl = theCodeSystemUrl;
|
||||
myLastCode = theCode;
|
||||
myLastDisplay = theDisplay;
|
||||
return myNextReturnParams;
|
||||
}
|
||||
|
||||
@Search
|
||||
public List<CodeSystem> find(@RequiredParam(name = "url") UriParam theUrlParam) {
|
||||
myLastUrlParam = theUrlParam;
|
||||
assert myNextReturnCodeSystems != null;
|
||||
return myNextReturnCodeSystems;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return CodeSystem.class;
|
||||
}
|
||||
}
|
||||
|
||||
private static class MyValueSetProvider implements IResourceProvider {
|
||||
private Parameters myNextReturnParams;
|
||||
private List<ValueSet> myNextReturnValueSets;
|
||||
private UriType myLastUrl;
|
||||
private CodeType myLastCode;
|
||||
private int myInvocationCount;
|
||||
private UriType myLastSystem;
|
||||
private StringType myLastDisplay;
|
||||
private ValueSet myLastValueSet;
|
||||
private UriParam myLastUrlParam;
|
||||
|
||||
@Operation(name = "validate-code", idempotent = true, returnParameters = {
|
||||
@OperationParam(name = "result", type = BooleanType.class, min = 1),
|
||||
@OperationParam(name = "message", type = StringType.class),
|
||||
@OperationParam(name = "display", type = StringType.class)
|
||||
})
|
||||
public Parameters validateCode(
|
||||
HttpServletRequest theServletRequest,
|
||||
@IdParam(optional = true) IdType theId,
|
||||
@OperationParam(name = "url", min = 0, max = 1) UriType theValueSetUrl,
|
||||
@OperationParam(name = "code", min = 0, max = 1) CodeType theCode,
|
||||
@OperationParam(name = "system", min = 0, max = 1) UriType theSystem,
|
||||
@OperationParam(name = "display", min = 0, max = 1) StringType theDisplay,
|
||||
@OperationParam(name = "valueSet") ValueSet theValueSet
|
||||
) {
|
||||
myInvocationCount++;
|
||||
myLastUrl = theValueSetUrl;
|
||||
myLastCode = theCode;
|
||||
myLastSystem = theSystem;
|
||||
myLastDisplay = theDisplay;
|
||||
myLastValueSet = theValueSet;
|
||||
return myNextReturnParams;
|
||||
}
|
||||
|
||||
@Search
|
||||
public List<ValueSet> find(@RequiredParam(name = "url") UriParam theUrlParam) {
|
||||
myLastUrlParam = theUrlParam;
|
||||
assert myNextReturnValueSets != null;
|
||||
return myNextReturnValueSets;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return ValueSet.class;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -49,6 +49,11 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
|
|||
super(theFhirContext);
|
||||
}
|
||||
|
||||
public RemoteTerminologyServiceValidationSupport(FhirContext theFhirContext, String theBaseUrl) {
|
||||
super(theFhirContext);
|
||||
myBaseUrl = theBaseUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodeValidationResult validateCode(ValidationSupportContext theValidationSupportContext, ConceptValidationOptions theOptions, String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl) {
|
||||
return invokeRemoteValidateCode(theCodeSystem, theCode, theDisplay, theValueSetUrl, null);
|
||||
|
|
|
@ -17,6 +17,7 @@ import java.util.ArrayList;
|
|||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
|
@ -171,6 +172,17 @@ public class ValidationSupportChain implements IValidationSupport {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRemoteTerminologyServiceConfigured() {
|
||||
if (myChain != null) {
|
||||
Optional<IValidationSupport> remoteTerminologyService = myChain.stream().filter(RemoteTerminologyServiceValidationSupport.class::isInstance).findFirst();
|
||||
if (remoteTerminologyService.isPresent()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IBaseResource> fetchAllConformanceResources() {
|
||||
List<IBaseResource> retVal = new ArrayList<>();
|
||||
|
|
Loading…
Reference in New Issue