Add support for response parameter issues for `validate-code` operation (#6360)
* Add support for response parameter issues for validate-code operation. Implement workaround for missing error for invalid codes by adding an issue when it is absent. * address minor code review comments * revert functionality of adding original code from remote validate-code call to the CodeValidationResult * revert change to FhirInstanceValidatorR4Test * Revert some of the code review addressal after responding. * Remove populating message in success scenarios. * Fix compilation failure in test after reverting method rename. * Revert change to populate the code from the response again as it implies more refactoring. --------- Co-authored-by: nathaniel.doef <nathaniel.doef@smilecdr.com>
This commit is contained in:
parent
b911ec77e1
commit
8fc5274894
|
@ -727,7 +727,7 @@ public interface IValidationSupport {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
String getCodeSystemName() {
|
public String getCodeSystemName() {
|
||||||
return myCodeSystemName;
|
return myCodeSystemName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,7 @@ import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
@ -58,20 +59,20 @@ public class ParametersUtil {
|
||||||
public static Optional<String> getNamedParameterValueAsString(
|
public static Optional<String> getNamedParameterValueAsString(
|
||||||
FhirContext theCtx, IBaseParameters theParameters, String theParameterName) {
|
FhirContext theCtx, IBaseParameters theParameters, String theParameterName) {
|
||||||
Function<IPrimitiveType<?>, String> mapper = t -> defaultIfBlank(t.getValueAsString(), null);
|
Function<IPrimitiveType<?>, String> mapper = t -> defaultIfBlank(t.getValueAsString(), null);
|
||||||
return extractNamedParameters(theCtx, theParameters, theParameterName, mapper).stream()
|
return extractNamedParameterValues(theCtx, theParameters, theParameterName, mapper).stream()
|
||||||
.findFirst();
|
.findFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<String> getNamedParameterValuesAsString(
|
public static List<String> getNamedParameterValuesAsString(
|
||||||
FhirContext theCtx, IBaseParameters theParameters, String theParameterName) {
|
FhirContext theCtx, IBaseParameters theParameters, String theParameterName) {
|
||||||
Function<IPrimitiveType<?>, String> mapper = t -> defaultIfBlank(t.getValueAsString(), null);
|
Function<IPrimitiveType<?>, String> mapper = t -> defaultIfBlank(t.getValueAsString(), null);
|
||||||
return extractNamedParameters(theCtx, theParameters, theParameterName, mapper);
|
return extractNamedParameterValues(theCtx, theParameters, theParameterName, mapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<Integer> getNamedParameterValuesAsInteger(
|
public static List<Integer> getNamedParameterValuesAsInteger(
|
||||||
FhirContext theCtx, IBaseParameters theParameters, String theParameterName) {
|
FhirContext theCtx, IBaseParameters theParameters, String theParameterName) {
|
||||||
Function<IPrimitiveType<?>, Integer> mapper = t -> (Integer) t.getValue();
|
Function<IPrimitiveType<?>, Integer> mapper = t -> (Integer) t.getValue();
|
||||||
return extractNamedParameters(theCtx, theParameters, theParameterName, mapper);
|
return extractNamedParameterValues(theCtx, theParameters, theParameterName, mapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Optional<Integer> getNamedParameterValueAsInteger(
|
public static Optional<Integer> getNamedParameterValueAsInteger(
|
||||||
|
@ -80,6 +81,19 @@ public class ParametersUtil {
|
||||||
.findFirst();
|
.findFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the resource within a parameter.
|
||||||
|
* @param theCtx thr FHIR context
|
||||||
|
* @param theParameters the parameters instance where to look for the resource
|
||||||
|
* @param theParameterName the parameter name
|
||||||
|
* @return the resource
|
||||||
|
*/
|
||||||
|
public static Optional<IBaseResource> getNamedParameterResource(
|
||||||
|
FhirContext theCtx, IBaseParameters theParameters, String theParameterName) {
|
||||||
|
return extractNamedParameterResources(theCtx, theParameters, theParameterName).stream()
|
||||||
|
.findFirst();
|
||||||
|
}
|
||||||
|
|
||||||
public static Optional<IBase> getNamedParameter(
|
public static Optional<IBase> getNamedParameter(
|
||||||
FhirContext theCtx, IBaseResource theParameters, String theParameterName) {
|
FhirContext theCtx, IBaseResource theParameters, String theParameterName) {
|
||||||
return getNamedParameters(theCtx, theParameters, theParameterName).stream()
|
return getNamedParameters(theCtx, theParameters, theParameterName).stream()
|
||||||
|
@ -153,7 +167,7 @@ public class ParametersUtil {
|
||||||
.map(t -> (Integer) t);
|
.map(t -> (Integer) t);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static <T> List<T> extractNamedParameters(
|
private static <T> List<T> extractNamedParameterValues(
|
||||||
FhirContext theCtx,
|
FhirContext theCtx,
|
||||||
IBaseParameters theParameters,
|
IBaseParameters theParameters,
|
||||||
String theParameterName,
|
String theParameterName,
|
||||||
|
@ -170,7 +184,25 @@ public class ParametersUtil {
|
||||||
.filter(t -> t instanceof IPrimitiveType<?>)
|
.filter(t -> t instanceof IPrimitiveType<?>)
|
||||||
.map(t -> ((IPrimitiveType<?>) t))
|
.map(t -> ((IPrimitiveType<?>) t))
|
||||||
.map(theMapper)
|
.map(theMapper)
|
||||||
.filter(t -> t != null)
|
.filter(Objects::nonNull)
|
||||||
|
.forEach(retVal::add);
|
||||||
|
}
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<IBaseResource> extractNamedParameterResources(
|
||||||
|
FhirContext theCtx, IBaseParameters theParameters, String theParameterName) {
|
||||||
|
List<IBaseResource> retVal = new ArrayList<>();
|
||||||
|
|
||||||
|
List<IBase> namedParameters = getNamedParameters(theCtx, theParameters, theParameterName);
|
||||||
|
for (IBase nextParameter : namedParameters) {
|
||||||
|
BaseRuntimeElementCompositeDefinition<?> nextParameterDef =
|
||||||
|
(BaseRuntimeElementCompositeDefinition<?>) theCtx.getElementDefinition(nextParameter.getClass());
|
||||||
|
BaseRuntimeChildDefinition resourceChild = nextParameterDef.getChildByName("resource");
|
||||||
|
List<IBase> resourceValues = resourceChild.getAccessor().getValues(nextParameter);
|
||||||
|
resourceValues.stream()
|
||||||
|
.filter(IBaseResource.class::isInstance)
|
||||||
|
.map(t -> ((IBaseResource) t))
|
||||||
.forEach(retVal::add);
|
.forEach(retVal::add);
|
||||||
}
|
}
|
||||||
return retVal;
|
return retVal;
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
type: fix
|
||||||
|
issue: 6359
|
||||||
|
title: "After upgrading org.hl7.fhir.core from 6.1.2.2 to 6.3.11, the $validate-code operation stopped returning an
|
||||||
|
error for invalid codes using remote terminology. This has been fixed."
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
type: add
|
||||||
|
issue: 6359
|
||||||
|
title: "Remote Terminology validation has been enhanced to support output parameter `issues` for the $validate-code
|
||||||
|
operation."
|
|
@ -1,7 +1,5 @@
|
||||||
package ca.uhn.fhir.jpa.provider.r4;
|
package ca.uhn.fhir.jpa.provider.r4;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.jpa.config.JpaConfig;
|
import ca.uhn.fhir.jpa.config.JpaConfig;
|
||||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||||
|
@ -15,6 +13,7 @@ import ca.uhn.fhir.rest.param.UriParam;
|
||||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
|
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
|
||||||
import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain;
|
import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
@ -34,74 +33,81 @@ import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Qualifier;
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
|
||||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType;
|
import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This set of Unit Tests instantiates and injects an instance of
|
* This set of integration tests that instantiates and injects an instance of
|
||||||
* {@link org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport}
|
* {@link org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport}
|
||||||
* into the ValidationSupportChain, which tests the logic of dynamically selecting the correct Remote Terminology
|
* into the ValidationSupportChain, which tests the logic of dynamically selecting the correct Remote Terminology
|
||||||
* implementation. It also exercises the code found in
|
* implementation. It also exercises the code found in
|
||||||
* {@link org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport#invokeRemoteValidateCode}
|
* {@link org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport#invokeRemoteValidateCode}
|
||||||
*/
|
*/
|
||||||
public class ResourceProviderR4RemoteTerminologyTest extends BaseResourceProviderR4Test {
|
public class ValidateCodeOperationWithRemoteTerminologyR4Test extends BaseResourceProviderR4Test {
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderR4RemoteTerminologyTest.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ValidateCodeOperationWithRemoteTerminologyR4Test.class);
|
||||||
private static final String DISPLAY = "DISPLAY";
|
private static final String DISPLAY = "DISPLAY";
|
||||||
private static final String DISPLAY_BODY_MASS_INDEX = "Body mass index (BMI) [Ratio]";
|
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 final String CODE_BODY_MASS_INDEX = "39156-5";
|
||||||
private static final String CODE_SYSTEM_V2_0247_URI = "http://terminology.hl7.org/CodeSystem/v2-0247";
|
private static final String CODE_SYSTEM_V2_0247_URI = "http://terminology.hl7.org/CodeSystem/v2-0247";
|
||||||
private static final String INVALID_CODE_SYSTEM_URI = "http://terminology.hl7.org/CodeSystem/INVALID-CODESYSTEM";
|
private static final String INVALID_CODE_SYSTEM_URI = "http://terminology.hl7.org/CodeSystem/INVALID-CODESYSTEM";
|
||||||
private static final String UNKNOWN_VALUE_SYSTEM_URI = "http://hl7.org/fhir/ValueSet/unknown-value-set";
|
private static final String UNKNOWN_VALUE_SYSTEM_URI = "http://hl7.org/fhir/ValueSet/unknown-value-set";
|
||||||
private static FhirContext ourCtx = FhirContext.forR4();
|
private static final FhirContext ourCtx = FhirContext.forR4();
|
||||||
private MyCodeSystemProvider myCodeSystemProvider = new MyCodeSystemProvider();
|
|
||||||
private MyValueSetProvider myValueSetProvider = new MyValueSetProvider();
|
|
||||||
|
|
||||||
@RegisterExtension
|
@RegisterExtension
|
||||||
public RestfulServerExtension myRestfulServerExtension = new RestfulServerExtension(ourCtx, myCodeSystemProvider,
|
protected static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx);
|
||||||
myValueSetProvider);
|
|
||||||
|
|
||||||
private RemoteTerminologyServiceValidationSupport mySvc;
|
private RemoteTerminologyServiceValidationSupport mySvc;
|
||||||
|
private MyCodeSystemProvider myCodeSystemProvider;
|
||||||
|
private MyValueSetProvider myValueSetProvider;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
@Qualifier(JpaConfig.JPA_VALIDATION_SUPPORT_CHAIN)
|
@Qualifier(JpaConfig.JPA_VALIDATION_SUPPORT_CHAIN)
|
||||||
private ValidationSupportChain myValidationSupportChain;
|
private ValidationSupportChain myValidationSupportChain;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void before_addRemoteTerminologySupport() throws Exception {
|
public void before() throws Exception {
|
||||||
String baseUrl = "http://localhost:" + myRestfulServerExtension.getPort();
|
String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
|
||||||
mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx, baseUrl);
|
mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx, baseUrl);
|
||||||
myValidationSupportChain.addValidationSupport(0, mySvc);
|
myValidationSupportChain.addValidationSupport(0, mySvc);
|
||||||
|
myCodeSystemProvider = new MyCodeSystemProvider();
|
||||||
|
myValueSetProvider = new MyValueSetProvider();
|
||||||
|
ourRestfulServerExtension.registerProvider(myCodeSystemProvider);
|
||||||
|
ourRestfulServerExtension.registerProvider(myValueSetProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
public void after_removeRemoteTerminologySupport() {
|
public void after() {
|
||||||
myValidationSupportChain.removeValidationSupport(mySvc);
|
myValidationSupportChain.removeValidationSupport(mySvc);
|
||||||
myRestfulServerExtension.getRestfulServer().getInterceptorService().unregisterAllInterceptors();
|
ourRestfulServerExtension.getRestfulServer().getInterceptorService().unregisterAllInterceptors();
|
||||||
|
ourRestfulServerExtension.unregisterProvider(myCodeSystemProvider);
|
||||||
|
ourRestfulServerExtension.unregisterProvider(myValueSetProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testValidateCodeOperationOnCodeSystem_byCodingAndUrlWhereSystemIsDifferent_throwsException() {
|
public void validateCodeOperationOnCodeSystem_byCodingAndUrlWhereSystemIsDifferent_throwsException() {
|
||||||
assertThatExceptionOfType(InvalidRequestException.class).isThrownBy(() -> {
|
assertThatExceptionOfType(InvalidRequestException.class).isThrownBy(() -> myClient
|
||||||
Parameters respParam = myClient
|
|
||||||
.operation()
|
.operation()
|
||||||
.onType(CodeSystem.class)
|
.onType(CodeSystem.class)
|
||||||
.named(JpaConstants.OPERATION_VALIDATE_CODE)
|
.named(JpaConstants.OPERATION_VALIDATE_CODE)
|
||||||
.withParameter(Parameters.class, "coding", new Coding().setSystem(CODE_SYSTEM_V2_0247_URI).setCode("P"))
|
.withParameter(Parameters.class, "coding", new Coding().setSystem(CODE_SYSTEM_V2_0247_URI).setCode("P"))
|
||||||
.andParameter("url", new UriType(INVALID_CODE_SYSTEM_URI))
|
.andParameter("url", new UriType(INVALID_CODE_SYSTEM_URI))
|
||||||
.execute();
|
.execute());
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testValidateCodeOperationOnCodeSystem_byCodingAndUrl_usingBuiltInCodeSystems() {
|
public void validateCodeOperationOnCodeSystem_byCodingAndUrl_usingBuiltInCodeSystems() {
|
||||||
myCodeSystemProvider.myNextReturnCodeSystems = new ArrayList<>();
|
myCodeSystemProvider.myReturnCodeSystems = new ArrayList<>();
|
||||||
myCodeSystemProvider.myNextReturnCodeSystems.add((CodeSystem) new CodeSystem().setId("CodeSystem/v2-0247"));
|
myCodeSystemProvider.myReturnCodeSystems.add((CodeSystem) new CodeSystem().setId("CodeSystem/v2-0247"));
|
||||||
createNextCodeSystemReturnParameters(true, DISPLAY, null);
|
myCodeSystemProvider.myReturnParams = new Parameters();
|
||||||
|
myCodeSystemProvider.myReturnParams.addParameter("result", true);
|
||||||
|
myCodeSystemProvider.myReturnParams.addParameter("display", DISPLAY);
|
||||||
|
|
||||||
logAllConcepts();
|
logAllConcepts();
|
||||||
|
|
||||||
|
@ -116,13 +122,13 @@ public class ResourceProviderR4RemoteTerminologyTest extends BaseResourceProvide
|
||||||
String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||||
ourLog.info(resp);
|
ourLog.info(resp);
|
||||||
|
|
||||||
assertEquals(true, ((BooleanType) respParam.getParameterValue("result")).booleanValue());
|
assertTrue(((BooleanType) respParam.getParameterValue("result")).booleanValue());
|
||||||
assertEquals(DISPLAY, respParam.getParameterValue("display").toString());
|
assertEquals(DISPLAY, respParam.getParameterValue("display").toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testValidateCodeOperationOnCodeSystem_byCodingAndUrlWhereCodeSystemIsUnknown_returnsFalse() {
|
public void validateCodeOperationOnCodeSystem_byCodingAndUrlWhereCodeSystemIsUnknown_returnsFalse() {
|
||||||
myCodeSystemProvider.myNextReturnCodeSystems = new ArrayList<>();
|
myCodeSystemProvider.myReturnCodeSystems = new ArrayList<>();
|
||||||
|
|
||||||
Parameters respParam = myClient
|
Parameters respParam = myClient
|
||||||
.operation()
|
.operation()
|
||||||
|
@ -142,7 +148,7 @@ public class ResourceProviderR4RemoteTerminologyTest extends BaseResourceProvide
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testValidateCodeOperationOnValueSet_byCodingAndUrlWhereSystemIsDifferent_throwsException() {
|
public void validateCodeOperationOnValueSet_byCodingAndUrlWhereSystemIsDifferent_throwsException() {
|
||||||
try {
|
try {
|
||||||
myClient.operation()
|
myClient.operation()
|
||||||
.onType(ValueSet.class)
|
.onType(ValueSet.class)
|
||||||
|
@ -159,12 +165,14 @@ public class ResourceProviderR4RemoteTerminologyTest extends BaseResourceProvide
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testValidateCodeOperationOnValueSet_byUrlAndSystem_usingBuiltInCodeSystems() {
|
public void validateCodeOperationOnValueSet_byUrlAndSystem_usingBuiltInCodeSystems() {
|
||||||
myCodeSystemProvider.myNextReturnCodeSystems = new ArrayList<>();
|
myCodeSystemProvider.myReturnCodeSystems = new ArrayList<>();
|
||||||
myCodeSystemProvider.myNextReturnCodeSystems.add((CodeSystem) new CodeSystem().setId("CodeSystem/list-example-use-codes"));
|
myCodeSystemProvider.myReturnCodeSystems.add((CodeSystem) new CodeSystem().setId("CodeSystem/list-example-use-codes"));
|
||||||
myValueSetProvider.myNextReturnValueSets = new ArrayList<>();
|
myValueSetProvider.myReturnValueSets = new ArrayList<>();
|
||||||
myValueSetProvider.myNextReturnValueSets.add((ValueSet) new ValueSet().setId("ValueSet/list-example-codes"));
|
myValueSetProvider.myReturnValueSets.add((ValueSet) new ValueSet().setId("ValueSet/list-example-codes"));
|
||||||
createNextValueSetReturnParameters(true, DISPLAY, null);
|
myValueSetProvider.myReturnParams = new Parameters();
|
||||||
|
myValueSetProvider.myReturnParams.addParameter("result", true);
|
||||||
|
myValueSetProvider.myReturnParams.addParameter("display", DISPLAY);
|
||||||
|
|
||||||
Parameters respParam = myClient
|
Parameters respParam = myClient
|
||||||
.operation()
|
.operation()
|
||||||
|
@ -179,17 +187,19 @@ public class ResourceProviderR4RemoteTerminologyTest extends BaseResourceProvide
|
||||||
String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||||
ourLog.info(resp);
|
ourLog.info(resp);
|
||||||
|
|
||||||
assertEquals(true, ((BooleanType) respParam.getParameterValue("result")).booleanValue());
|
assertTrue(((BooleanType) respParam.getParameterValue("result")).booleanValue());
|
||||||
assertEquals(DISPLAY, respParam.getParameterValue("display").toString());
|
assertEquals(DISPLAY, respParam.getParameterValue("display").toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testValidateCodeOperationOnValueSet_byUrlSystemAndCode() {
|
public void validateCodeOperationOnValueSet_byUrlSystemAndCode() {
|
||||||
myCodeSystemProvider.myNextReturnCodeSystems = new ArrayList<>();
|
myCodeSystemProvider.myReturnCodeSystems = new ArrayList<>();
|
||||||
myCodeSystemProvider.myNextReturnCodeSystems.add((CodeSystem) new CodeSystem().setId("CodeSystem/list-example-use-codes"));
|
myCodeSystemProvider.myReturnCodeSystems.add((CodeSystem) new CodeSystem().setId("CodeSystem/list-example-use-codes"));
|
||||||
myValueSetProvider.myNextReturnValueSets = new ArrayList<>();
|
myValueSetProvider.myReturnValueSets = new ArrayList<>();
|
||||||
myValueSetProvider.myNextReturnValueSets.add((ValueSet) new ValueSet().setId("ValueSet/list-example-codes"));
|
myValueSetProvider.myReturnValueSets.add((ValueSet) new ValueSet().setId("ValueSet/list-example-codes"));
|
||||||
createNextValueSetReturnParameters(true, DISPLAY_BODY_MASS_INDEX, null);
|
myValueSetProvider.myReturnParams = new Parameters();
|
||||||
|
myValueSetProvider.myReturnParams.addParameter("result", true);
|
||||||
|
myValueSetProvider.myReturnParams.addParameter("display", DISPLAY_BODY_MASS_INDEX);
|
||||||
|
|
||||||
Parameters respParam = myClient
|
Parameters respParam = myClient
|
||||||
.operation()
|
.operation()
|
||||||
|
@ -203,13 +213,13 @@ public class ResourceProviderR4RemoteTerminologyTest extends BaseResourceProvide
|
||||||
String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||||
ourLog.info(resp);
|
ourLog.info(resp);
|
||||||
|
|
||||||
assertEquals(true, ((BooleanType) respParam.getParameterValue("result")).booleanValue());
|
assertTrue(((BooleanType) respParam.getParameterValue("result")).booleanValue());
|
||||||
assertEquals(DISPLAY_BODY_MASS_INDEX, respParam.getParameterValue("display").toString());
|
assertEquals(DISPLAY_BODY_MASS_INDEX, respParam.getParameterValue("display").toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testValidateCodeOperationOnValueSet_byCodingAndUrlWhereValueSetIsUnknown_returnsFalse() {
|
public void validateCodeOperationOnValueSet_byCodingAndUrlWhereValueSetIsUnknown_returnsFalse() {
|
||||||
myValueSetProvider.myNextReturnValueSets = new ArrayList<>();
|
myValueSetProvider.myReturnValueSets = new ArrayList<>();
|
||||||
|
|
||||||
Parameters respParam = myClient
|
Parameters respParam = myClient
|
||||||
.operation()
|
.operation()
|
||||||
|
@ -228,33 +238,10 @@ public class ResourceProviderR4RemoteTerminologyTest extends BaseResourceProvide
|
||||||
" - Unknown or unusable ValueSet[" + UNKNOWN_VALUE_SYSTEM_URI + "]");
|
" - Unknown or unusable ValueSet[" + UNKNOWN_VALUE_SYSTEM_URI + "]");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createNextCodeSystemReturnParameters(boolean theResult, String theDisplay, String theMessage) {
|
@SuppressWarnings("unused")
|
||||||
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 static class MyCodeSystemProvider implements IResourceProvider {
|
||||||
|
private List<CodeSystem> myReturnCodeSystems;
|
||||||
private UriParam myLastUrlParam;
|
private Parameters myReturnParams;
|
||||||
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 = {
|
@Operation(name = "validate-code", idempotent = true, returnParameters = {
|
||||||
@OperationParam(name = "result", type = BooleanType.class, min = 1),
|
@OperationParam(name = "result", type = BooleanType.class, min = 1),
|
||||||
|
@ -268,18 +255,13 @@ public class ResourceProviderR4RemoteTerminologyTest extends BaseResourceProvide
|
||||||
@OperationParam(name = "code", min = 0, max = 1) CodeType theCode,
|
@OperationParam(name = "code", min = 0, max = 1) CodeType theCode,
|
||||||
@OperationParam(name = "display", min = 0, max = 1) StringType theDisplay
|
@OperationParam(name = "display", min = 0, max = 1) StringType theDisplay
|
||||||
) {
|
) {
|
||||||
myInvocationCount++;
|
return myReturnParams;
|
||||||
myLastUrl = theCodeSystemUrl;
|
|
||||||
myLastCode = theCode;
|
|
||||||
myLastDisplay = theDisplay;
|
|
||||||
return myNextReturnParams;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Search
|
@Search
|
||||||
public List<CodeSystem> find(@RequiredParam(name = "url") UriParam theUrlParam) {
|
public List<CodeSystem> find(@RequiredParam(name = "url") UriParam theUrlParam) {
|
||||||
myLastUrlParam = theUrlParam;
|
assert myReturnCodeSystems != null;
|
||||||
assert myNextReturnCodeSystems != null;
|
return myReturnCodeSystems;
|
||||||
return myNextReturnCodeSystems;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -288,16 +270,10 @@ public class ResourceProviderR4RemoteTerminologyTest extends BaseResourceProvide
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
private static class MyValueSetProvider implements IResourceProvider {
|
private static class MyValueSetProvider implements IResourceProvider {
|
||||||
private Parameters myNextReturnParams;
|
private Parameters myReturnParams;
|
||||||
private List<ValueSet> myNextReturnValueSets;
|
private List<ValueSet> myReturnValueSets;
|
||||||
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 = {
|
@Operation(name = "validate-code", idempotent = true, returnParameters = {
|
||||||
@OperationParam(name = "result", type = BooleanType.class, min = 1),
|
@OperationParam(name = "result", type = BooleanType.class, min = 1),
|
||||||
|
@ -313,20 +289,13 @@ public class ResourceProviderR4RemoteTerminologyTest extends BaseResourceProvide
|
||||||
@OperationParam(name = "display", min = 0, max = 1) StringType theDisplay,
|
@OperationParam(name = "display", min = 0, max = 1) StringType theDisplay,
|
||||||
@OperationParam(name = "valueSet") ValueSet theValueSet
|
@OperationParam(name = "valueSet") ValueSet theValueSet
|
||||||
) {
|
) {
|
||||||
myInvocationCount++;
|
return myReturnParams;
|
||||||
myLastUrl = theValueSetUrl;
|
|
||||||
myLastCode = theCode;
|
|
||||||
myLastSystem = theSystem;
|
|
||||||
myLastDisplay = theDisplay;
|
|
||||||
myLastValueSet = theValueSet;
|
|
||||||
return myNextReturnParams;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Search
|
@Search
|
||||||
public List<ValueSet> find(@RequiredParam(name = "url") UriParam theUrlParam) {
|
public List<ValueSet> find(@RequiredParam(name = "url") UriParam theUrlParam) {
|
||||||
myLastUrlParam = theUrlParam;
|
assert myReturnValueSets != null;
|
||||||
assert myNextReturnValueSets != null;
|
return myReturnValueSets;
|
||||||
return myNextReturnValueSets;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
|
@ -3,7 +3,9 @@ package ca.uhn.fhir.util;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.r4.model.IntegerType;
|
import org.hl7.fhir.r4.model.IntegerType;
|
||||||
|
import org.hl7.fhir.r4.model.OperationOutcome;
|
||||||
import org.hl7.fhir.r4.model.Parameters;
|
import org.hl7.fhir.r4.model.Parameters;
|
||||||
import org.hl7.fhir.r4.model.StringType;
|
import org.hl7.fhir.r4.model.StringType;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
@ -17,7 +19,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
public class ParametersUtilR4Test {
|
public class ParametersUtilR4Test {
|
||||||
private static final String TEST_PERSON_ID = "Person/32768";
|
private static final String TEST_PERSON_ID = "Person/32768";
|
||||||
private static FhirContext ourFhirContext = FhirContext.forR4();
|
private static final FhirContext ourFhirContext = FhirContext.forR4Cached();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreateParameters() {
|
public void testCreateParameters() {
|
||||||
|
@ -47,7 +49,7 @@ public class ParametersUtilR4Test {
|
||||||
p.addParameter()
|
p.addParameter()
|
||||||
.setValue(new StringType("VALUE4"));
|
.setValue(new StringType("VALUE4"));
|
||||||
|
|
||||||
List<String> values = ParametersUtil.getNamedParameterValuesAsString(FhirContext.forR4(), p, "foo");
|
List<String> values = ParametersUtil.getNamedParameterValuesAsString(ourFhirContext, p, "foo");
|
||||||
assertThat(values).containsExactly("VALUE1", "VALUE2");
|
assertThat(values).containsExactly("VALUE1", "VALUE2");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +60,7 @@ public class ParametersUtilR4Test {
|
||||||
.setName("foo")
|
.setName("foo")
|
||||||
.setValue(new IntegerType(123));
|
.setValue(new IntegerType(123));
|
||||||
|
|
||||||
Optional<Integer> value = ParametersUtil.getNamedParameterValueAsInteger(FhirContext.forR4(), p, "foo");
|
Optional<Integer> value = ParametersUtil.getNamedParameterValueAsInteger(ourFhirContext, p, "foo");
|
||||||
assertThat(value).isPresent();
|
assertThat(value).isPresent();
|
||||||
assertEquals(123, value.get().intValue());
|
assertEquals(123, value.get().intValue());
|
||||||
}
|
}
|
||||||
|
@ -80,7 +82,7 @@ public class ParametersUtilR4Test {
|
||||||
@Test
|
@Test
|
||||||
public void testAddPartDecimalNoScientificNotation() {
|
public void testAddPartDecimalNoScientificNotation() {
|
||||||
// setup
|
// setup
|
||||||
Double decimalValue = Double.valueOf("10000000");
|
double decimalValue = 10000000;
|
||||||
IBaseParameters parameters = ParametersUtil.newInstance(ourFhirContext);
|
IBaseParameters parameters = ParametersUtil.newInstance(ourFhirContext);
|
||||||
IBase resultPart = ParametersUtil.addParameterToParameters(ourFhirContext, parameters, "link");
|
IBase resultPart = ParametersUtil.addParameterToParameters(ourFhirContext, parameters, "link");
|
||||||
|
|
||||||
|
@ -92,4 +94,20 @@ public class ParametersUtilR4Test {
|
||||||
List<String> results = ParametersUtil.getNamedParameterPartAsString(ourFhirContext, parameters, "link", "linkCreated");
|
List<String> results = ParametersUtil.getNamedParameterPartAsString(ourFhirContext, parameters, "link", "linkCreated");
|
||||||
assertEquals(expected, results.get(0));
|
assertEquals(expected, results.get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetNamedParameterResource() {
|
||||||
|
OperationOutcome outcome = new OperationOutcome();
|
||||||
|
outcome.addIssue().setSeverity(OperationOutcome.IssueSeverity.ERROR).setDiagnostics("An error was found.");
|
||||||
|
Parameters p = new Parameters();
|
||||||
|
p.addParameter().setName("foo").setResource(outcome);
|
||||||
|
p.addParameter().setName("bar").setValue(new StringType("value1"));
|
||||||
|
|
||||||
|
Optional<IBaseResource> fooResource = ParametersUtil.getNamedParameterResource(ourFhirContext,p, "foo");
|
||||||
|
assertThat(fooResource).isPresent();
|
||||||
|
assertThat(fooResource.get()).isEqualTo(outcome);
|
||||||
|
|
||||||
|
Optional<IBaseResource> barResource = ParametersUtil.getNamedParameterResource(ourFhirContext,p, "bar");
|
||||||
|
assertThat(barResource).isEmpty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,12 +22,14 @@ import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseDatatype;
|
import org.hl7.fhir.instance.model.api.IBaseDatatype;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.r4.model.Base;
|
import org.hl7.fhir.r4.model.Base;
|
||||||
import org.hl7.fhir.r4.model.CodeSystem;
|
import org.hl7.fhir.r4.model.CodeSystem;
|
||||||
import org.hl7.fhir.r4.model.CodeType;
|
import org.hl7.fhir.r4.model.CodeType;
|
||||||
import org.hl7.fhir.r4.model.Coding;
|
import org.hl7.fhir.r4.model.Coding;
|
||||||
|
import org.hl7.fhir.r4.model.OperationOutcome;
|
||||||
import org.hl7.fhir.r4.model.Parameters;
|
import org.hl7.fhir.r4.model.Parameters;
|
||||||
import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent;
|
import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent;
|
||||||
import org.hl7.fhir.r4.model.Property;
|
import org.hl7.fhir.r4.model.Property;
|
||||||
|
@ -37,9 +39,14 @@ import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static ca.uhn.fhir.util.ParametersUtil.getNamedParameterResource;
|
||||||
|
import static ca.uhn.fhir.util.ParametersUtil.getNamedParameterValueAsString;
|
||||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
|
||||||
|
@ -52,6 +59,9 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
public class RemoteTerminologyServiceValidationSupport extends BaseValidationSupport implements IValidationSupport {
|
public class RemoteTerminologyServiceValidationSupport extends BaseValidationSupport implements IValidationSupport {
|
||||||
private static final Logger ourLog = LoggerFactory.getLogger(RemoteTerminologyServiceValidationSupport.class);
|
private static final Logger ourLog = LoggerFactory.getLogger(RemoteTerminologyServiceValidationSupport.class);
|
||||||
|
|
||||||
|
public static final String ERROR_CODE_UNKNOWN_CODE_IN_CODE_SYSTEM = "unknownCodeInSystem";
|
||||||
|
public static final String ERROR_CODE_UNKNOWN_CODE_IN_VALUE_SET = "unknownCodeInValueSet";
|
||||||
|
|
||||||
private String myBaseUrl;
|
private String myBaseUrl;
|
||||||
private final List<Object> myClientInterceptors = new ArrayList<>();
|
private final List<Object> myClientInterceptors = new ArrayList<>();
|
||||||
|
|
||||||
|
@ -192,8 +202,8 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
|
||||||
// e.g. ClientResponseInterceptorModificationTemplate
|
// e.g. ClientResponseInterceptorModificationTemplate
|
||||||
ourLog.error(e.getMessage(), e);
|
ourLog.error(e.getMessage(), e);
|
||||||
LookupCodeResult result = LookupCodeResult.notFound(system, code);
|
LookupCodeResult result = LookupCodeResult.notFound(system, code);
|
||||||
result.setErrorMessage(
|
result.setErrorMessage(getErrorMessage(
|
||||||
getErrorMessage("unknownCodeInSystem", system, code, client.getServerBase(), e.getMessage()));
|
ERROR_CODE_UNKNOWN_CODE_IN_CODE_SYSTEM, system, code, getBaseUrl(), e.getMessage()));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
if (outcome != null && !outcome.isEmpty()) {
|
if (outcome != null && !outcome.isEmpty()) {
|
||||||
|
@ -575,6 +585,21 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
|
||||||
|
|
||||||
IGenericClient client = provideClient();
|
IGenericClient client = provideClient();
|
||||||
|
|
||||||
|
// this message builder can be removed once we introduce a parameter object like CodeValidationRequest
|
||||||
|
ValidationErrorMessageBuilder errorMessageBuilder = theServerMessage -> {
|
||||||
|
if (theValueSetUrl == null && theValueSet == null) {
|
||||||
|
return getErrorMessage(
|
||||||
|
ERROR_CODE_UNKNOWN_CODE_IN_CODE_SYSTEM, theCodeSystem, theCode, getBaseUrl(), theServerMessage);
|
||||||
|
}
|
||||||
|
return getErrorMessage(
|
||||||
|
ERROR_CODE_UNKNOWN_CODE_IN_VALUE_SET,
|
||||||
|
theCodeSystem,
|
||||||
|
theCode,
|
||||||
|
theValueSetUrl,
|
||||||
|
getBaseUrl(),
|
||||||
|
theServerMessage);
|
||||||
|
};
|
||||||
|
|
||||||
IBaseParameters input =
|
IBaseParameters input =
|
||||||
buildValidateCodeInputParameters(theCodeSystem, theCode, theDisplay, theValueSetUrl, theValueSet);
|
buildValidateCodeInputParameters(theCodeSystem, theCode, theDisplay, theValueSetUrl, theValueSet);
|
||||||
|
|
||||||
|
@ -583,93 +608,167 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
|
||||||
resourceType = "CodeSystem";
|
resourceType = "CodeSystem";
|
||||||
}
|
}
|
||||||
|
|
||||||
IBaseParameters output;
|
|
||||||
try {
|
try {
|
||||||
output = client.operation()
|
IBaseParameters output = client.operation()
|
||||||
.onType(resourceType)
|
.onType(resourceType)
|
||||||
.named("validate-code")
|
.named("validate-code")
|
||||||
.withParameters(input)
|
.withParameters(input)
|
||||||
.execute();
|
.execute();
|
||||||
|
return createCodeValidationResult(output, errorMessageBuilder, theCode);
|
||||||
} catch (ResourceNotFoundException | InvalidRequestException ex) {
|
} catch (ResourceNotFoundException | InvalidRequestException ex) {
|
||||||
ourLog.error(ex.getMessage(), ex);
|
ourLog.error(ex.getMessage(), ex);
|
||||||
CodeValidationResult result = new CodeValidationResult();
|
String errorMessage = errorMessageBuilder.buildErrorMessage(ex.getMessage());
|
||||||
result.setSeverity(IssueSeverity.ERROR);
|
CodeValidationIssueCode issueCode = ex instanceof ResourceNotFoundException
|
||||||
String errorMessage = buildErrorMessage(
|
? CodeValidationIssueCode.NOT_FOUND
|
||||||
theCodeSystem, theCode, theValueSetUrl, theValueSet, client.getServerBase(), ex.getMessage());
|
: CodeValidationIssueCode.CODE_INVALID;
|
||||||
result.setMessage(errorMessage);
|
return createErrorCodeValidationResult(issueCode, errorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private CodeValidationResult createErrorCodeValidationResult(
|
||||||
|
CodeValidationIssueCode theIssueCode, String theMessage) {
|
||||||
|
IssueSeverity severity = IssueSeverity.ERROR;
|
||||||
|
return new CodeValidationResult()
|
||||||
|
.setSeverity(severity)
|
||||||
|
.setMessage(theMessage)
|
||||||
|
.addCodeValidationIssue(new CodeValidationIssue(
|
||||||
|
theMessage, severity, theIssueCode, CodeValidationIssueCoding.INVALID_CODE));
|
||||||
|
}
|
||||||
|
|
||||||
|
private CodeValidationResult createCodeValidationResult(
|
||||||
|
IBaseParameters theOutput, ValidationErrorMessageBuilder theMessageBuilder, String theCode) {
|
||||||
|
final FhirContext fhirContext = getFhirContext();
|
||||||
|
Optional<String> resultValue = getNamedParameterValueAsString(fhirContext, theOutput, "result");
|
||||||
|
|
||||||
|
if (!resultValue.isPresent()) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
Msg.code(2560) + "Parameter `result` is missing from the $validate-code response.");
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean success = resultValue.get().equalsIgnoreCase("true");
|
||||||
|
|
||||||
|
CodeValidationResult result = new CodeValidationResult();
|
||||||
|
|
||||||
|
// TODO MM: avoid passing the code and only retrieve it from the response
|
||||||
|
// that implies larger changes, like adding the result boolean to CodeValidationResult
|
||||||
|
// since CodeValidationResult#isOk() relies on code being populated to determine the result/success
|
||||||
|
if (success) {
|
||||||
|
result.setCode(theCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<String> systemValue = getNamedParameterValueAsString(fhirContext, theOutput, "system");
|
||||||
|
systemValue.ifPresent(result::setCodeSystemName);
|
||||||
|
Optional<String> versionValue = getNamedParameterValueAsString(fhirContext, theOutput, "version");
|
||||||
|
versionValue.ifPresent(result::setCodeSystemVersion);
|
||||||
|
Optional<String> displayValue = getNamedParameterValueAsString(fhirContext, theOutput, "display");
|
||||||
|
displayValue.ifPresent(result::setDisplay);
|
||||||
|
|
||||||
|
// in theory the message and the issues should not be populated when result=false
|
||||||
|
if (success) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> resultValues = ParametersUtil.getNamedParameterValuesAsString(getFhirContext(), output, "result");
|
// for now assume severity ERROR, we may need to process the following for success cases as well
|
||||||
if (resultValues.isEmpty() || isBlank(resultValues.get(0))) {
|
result.setSeverity(IssueSeverity.ERROR);
|
||||||
return null;
|
|
||||||
}
|
|
||||||
Validate.isTrue(resultValues.size() == 1, "Response contained %d 'result' values", resultValues.size());
|
|
||||||
|
|
||||||
boolean success = "true".equalsIgnoreCase(resultValues.get(0));
|
Optional<String> messageValue = getNamedParameterValueAsString(fhirContext, theOutput, "message");
|
||||||
|
messageValue.ifPresent(value -> result.setMessage(theMessageBuilder.buildErrorMessage(value)));
|
||||||
CodeValidationResult retVal = new CodeValidationResult();
|
|
||||||
if (success) {
|
|
||||||
|
|
||||||
retVal.setCode(theCode);
|
|
||||||
List<String> displayValues =
|
|
||||||
ParametersUtil.getNamedParameterValuesAsString(getFhirContext(), output, "display");
|
|
||||||
if (!displayValues.isEmpty()) {
|
|
||||||
retVal.setDisplay(displayValues.get(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
Optional<IBaseResource> issuesValue = getNamedParameterResource(fhirContext, theOutput, "issues");
|
||||||
|
if (issuesValue.isPresent()) {
|
||||||
|
// it seems to be safe to cast to IBaseOperationOutcome as any other type would not reach this point
|
||||||
|
createCodeValidationIssues(
|
||||||
|
(IBaseOperationOutcome) issuesValue.get(),
|
||||||
|
fhirContext.getVersion().getVersion())
|
||||||
|
.ifPresent(i -> i.forEach(result::addCodeValidationIssue));
|
||||||
} else {
|
} else {
|
||||||
|
// create a validation issue out of the message
|
||||||
retVal.setSeverity(IssueSeverity.ERROR);
|
// this is a workaround to overcome an issue in the FHIR Validator library
|
||||||
List<String> messageValues =
|
// where ValueSet bindings are only reading issues but not messages
|
||||||
ParametersUtil.getNamedParameterValuesAsString(getFhirContext(), output, "message");
|
// @see https://github.com/hapifhir/org.hl7.fhir.core/issues/1766
|
||||||
if (!messageValues.isEmpty()) {
|
result.addCodeValidationIssue(createCodeValidationIssue(result.getMessage()));
|
||||||
retVal.setMessage(messageValues.get(0));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return retVal;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String buildErrorMessage(
|
/**
|
||||||
String theCodeSystem,
|
* Creates a list of {@link ca.uhn.fhir.context.support.IValidationSupport.CodeValidationIssue} from the issues
|
||||||
String theCode,
|
* returned by the $validate-code operation.
|
||||||
String theValueSetUrl,
|
* Please note that this method should only be used for Remote Terminology for now as it only translates
|
||||||
IBaseResource theValueSet,
|
* issues text/message and assumes all other fields.
|
||||||
String theServerUrl,
|
* When issues will be supported across all validators in hapi-fhir, a proper generic conversion method should
|
||||||
String theServerMessage) {
|
* be available and this method will be deleted.
|
||||||
if (theValueSetUrl == null && theValueSet == null) {
|
*
|
||||||
return getErrorMessage("unknownCodeInSystem", theCodeSystem, theCode, theServerUrl, theServerMessage);
|
* @param theOperationOutcome the outcome of the $validate-code operation
|
||||||
} else {
|
* @param theFhirVersion the FHIR version
|
||||||
return getErrorMessage(
|
* @return the list of {@link ca.uhn.fhir.context.support.IValidationSupport.CodeValidationIssue}
|
||||||
"unknownCodeInValueSet", theCodeSystem, theCode, theValueSetUrl, theServerUrl, theServerMessage);
|
*/
|
||||||
|
public static Optional<Collection<CodeValidationIssue>> createCodeValidationIssues(
|
||||||
|
IBaseOperationOutcome theOperationOutcome, FhirVersionEnum theFhirVersion) {
|
||||||
|
if (theFhirVersion == FhirVersionEnum.R4) {
|
||||||
|
return Optional.of(createCodeValidationIssuesR4((OperationOutcome) theOperationOutcome));
|
||||||
}
|
}
|
||||||
|
if (theFhirVersion == FhirVersionEnum.DSTU3) {
|
||||||
|
return Optional.of(
|
||||||
|
createCodeValidationIssuesDstu3((org.hl7.fhir.dstu3.model.OperationOutcome) theOperationOutcome));
|
||||||
|
}
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Collection<CodeValidationIssue> createCodeValidationIssuesR4(OperationOutcome theOperationOutcome) {
|
||||||
|
return theOperationOutcome.getIssue().stream()
|
||||||
|
.map(issueComponent ->
|
||||||
|
createCodeValidationIssue(issueComponent.getDetails().getText()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Collection<CodeValidationIssue> createCodeValidationIssuesDstu3(
|
||||||
|
org.hl7.fhir.dstu3.model.OperationOutcome theOperationOutcome) {
|
||||||
|
return theOperationOutcome.getIssue().stream()
|
||||||
|
.map(issueComponent ->
|
||||||
|
createCodeValidationIssue(issueComponent.getDetails().getText()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CodeValidationIssue createCodeValidationIssue(String theMessage) {
|
||||||
|
return new CodeValidationIssue(
|
||||||
|
theMessage,
|
||||||
|
// assume issue type is OperationOutcome.IssueType#CODEINVALID as it is the only match
|
||||||
|
IssueSeverity.ERROR,
|
||||||
|
CodeValidationIssueCode.INVALID,
|
||||||
|
CodeValidationIssueCoding.INVALID_CODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface ValidationErrorMessageBuilder {
|
||||||
|
String buildErrorMessage(String theServerMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected IBaseParameters buildValidateCodeInputParameters(
|
protected IBaseParameters buildValidateCodeInputParameters(
|
||||||
String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl, IBaseResource theValueSet) {
|
String theCodeSystem, String theCode, String theDisplay, String theValueSetUrl, IBaseResource theValueSet) {
|
||||||
IBaseParameters params = ParametersUtil.newInstance(getFhirContext());
|
final FhirContext fhirContext = getFhirContext();
|
||||||
|
IBaseParameters params = ParametersUtil.newInstance(fhirContext);
|
||||||
|
|
||||||
if (theValueSet == null && theValueSetUrl == null) {
|
if (theValueSet == null && theValueSetUrl == null) {
|
||||||
ParametersUtil.addParameterToParametersUri(getFhirContext(), params, "url", theCodeSystem);
|
ParametersUtil.addParameterToParametersUri(fhirContext, params, "url", theCodeSystem);
|
||||||
ParametersUtil.addParameterToParametersString(getFhirContext(), params, "code", theCode);
|
ParametersUtil.addParameterToParametersString(fhirContext, params, "code", theCode);
|
||||||
if (isNotBlank(theDisplay)) {
|
if (isNotBlank(theDisplay)) {
|
||||||
ParametersUtil.addParameterToParametersString(getFhirContext(), params, "display", theDisplay);
|
ParametersUtil.addParameterToParametersString(fhirContext, params, "display", theDisplay);
|
||||||
}
|
}
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isNotBlank(theValueSetUrl)) {
|
if (isNotBlank(theValueSetUrl)) {
|
||||||
ParametersUtil.addParameterToParametersUri(getFhirContext(), params, "url", theValueSetUrl);
|
ParametersUtil.addParameterToParametersUri(fhirContext, params, "url", theValueSetUrl);
|
||||||
}
|
}
|
||||||
ParametersUtil.addParameterToParametersString(getFhirContext(), params, "code", theCode);
|
ParametersUtil.addParameterToParametersString(fhirContext, params, "code", theCode);
|
||||||
if (isNotBlank(theCodeSystem)) {
|
if (isNotBlank(theCodeSystem)) {
|
||||||
ParametersUtil.addParameterToParametersUri(getFhirContext(), params, "system", theCodeSystem);
|
ParametersUtil.addParameterToParametersUri(fhirContext, params, "system", theCodeSystem);
|
||||||
}
|
}
|
||||||
if (isNotBlank(theDisplay)) {
|
if (isNotBlank(theDisplay)) {
|
||||||
ParametersUtil.addParameterToParametersString(getFhirContext(), params, "display", theDisplay);
|
ParametersUtil.addParameterToParametersString(fhirContext, params, "display", theDisplay);
|
||||||
}
|
}
|
||||||
if (theValueSet != null) {
|
if (theValueSet != null) {
|
||||||
ParametersUtil.addParameterToParameters(getFhirContext(), params, "valueSet", theValueSet);
|
ParametersUtil.addParameterToParameters(fhirContext, params, "valueSet", theValueSet);
|
||||||
}
|
}
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ import ca.uhn.fhir.context.support.IValidationSupport.GroupConceptProperty;
|
||||||
import ca.uhn.fhir.context.support.IValidationSupport.LookupCodeResult;
|
import ca.uhn.fhir.context.support.IValidationSupport.LookupCodeResult;
|
||||||
import ca.uhn.fhir.context.support.IValidationSupport.StringConceptProperty;
|
import ca.uhn.fhir.context.support.IValidationSupport.StringConceptProperty;
|
||||||
import ca.uhn.fhir.context.support.LookupCodeRequest;
|
import ca.uhn.fhir.context.support.LookupCodeRequest;
|
||||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseDatatype;
|
import org.hl7.fhir.instance.model.api.IBaseDatatype;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
@ -22,6 +21,12 @@ import static ca.uhn.fhir.context.support.IValidationSupport.TYPE_GROUP;
|
||||||
import static ca.uhn.fhir.context.support.IValidationSupport.TYPE_STRING;
|
import static ca.uhn.fhir.context.support.IValidationSupport.TYPE_STRING;
|
||||||
import static java.util.stream.IntStream.range;
|
import static java.util.stream.IntStream.range;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.hl7.fhir.common.hapi.validation.IValidationProviders.CODE;
|
||||||
|
import static org.hl7.fhir.common.hapi.validation.IValidationProviders.CODE_SYSTEM;
|
||||||
|
import static org.hl7.fhir.common.hapi.validation.IValidationProviders.CODE_SYSTEM_NAME;
|
||||||
|
import static org.hl7.fhir.common.hapi.validation.IValidationProviders.CODE_SYSTEM_VERSION;
|
||||||
|
import static org.hl7.fhir.common.hapi.validation.IValidationProviders.DISPLAY;
|
||||||
|
import static org.hl7.fhir.common.hapi.validation.IValidationProviders.LANGUAGE;
|
||||||
import static org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport.createConceptProperty;
|
import static org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport.createConceptProperty;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
@ -40,20 +45,15 @@ import static org.junit.jupiter.api.Assertions.fail;
|
||||||
* e.g. assertEqualConceptProperty
|
* e.g. assertEqualConceptProperty
|
||||||
*/
|
*/
|
||||||
public interface ILookupCodeTest {
|
public interface ILookupCodeTest {
|
||||||
String DISPLAY = "DISPLAY";
|
|
||||||
String LANGUAGE = "en";
|
|
||||||
String CODE_SYSTEM = "CODE_SYS";
|
|
||||||
String CODE_SYSTEM_VERSION = "CODE_SYS_VERSION";
|
|
||||||
String CODE_SYSTEM_NAME = "Code System";
|
|
||||||
String CODE = "CODE";
|
|
||||||
|
|
||||||
IValidationSupport getService();
|
IValidationSupport getService();
|
||||||
IMyCodeSystemProvider getCodeSystemProvider();
|
IValidationProviders.IMyLookupCodeProvider getLookupCodeProvider();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
default void lookupCode_forCodeSystemWithBlankCode_throwsException() {
|
default void lookupCode_forCodeSystemWithBlankCode_throwsException() {
|
||||||
|
IValidationSupport service = getService();
|
||||||
|
LookupCodeRequest request = new LookupCodeRequest(CODE_SYSTEM, "");
|
||||||
try {
|
try {
|
||||||
getService().lookupCode(null, new LookupCodeRequest(CODE_SYSTEM, ""));
|
service.lookupCode(null, request);
|
||||||
fail();
|
fail();
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
assertEquals("theCode must be provided", e.getMessage());
|
assertEquals("theCode must be provided", e.getMessage());
|
||||||
|
@ -70,11 +70,14 @@ public interface ILookupCodeTest {
|
||||||
return "someUnsupportedType";
|
return "someUnsupportedType";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
getCodeSystemProvider().setLookupCodeResult(result);
|
getLookupCodeProvider().setLookupCodeResult(result);
|
||||||
|
|
||||||
|
IValidationSupport service = getService();
|
||||||
|
LookupCodeRequest request = new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, null);
|
||||||
|
|
||||||
// test and verify
|
// test and verify
|
||||||
try {
|
try {
|
||||||
getService().lookupCode(null, new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, null));
|
service.lookupCode(null, request);
|
||||||
fail();
|
fail();
|
||||||
} catch (InternalErrorException e) {
|
} catch (InternalErrorException e) {
|
||||||
assertThat(e.getMessage()).contains("HAPI-1739: Don't know how to handle ");
|
assertThat(e.getMessage()).contains("HAPI-1739: Don't know how to handle ");
|
||||||
|
@ -88,7 +91,7 @@ public interface ILookupCodeTest {
|
||||||
result.setCodeSystemVersion(CODE_SYSTEM_VERSION);
|
result.setCodeSystemVersion(CODE_SYSTEM_VERSION);
|
||||||
result.setCodeSystemDisplayName(CODE_SYSTEM_NAME);
|
result.setCodeSystemDisplayName(CODE_SYSTEM_NAME);
|
||||||
result.setCodeDisplay(DISPLAY);
|
result.setCodeDisplay(DISPLAY);
|
||||||
getCodeSystemProvider().setLookupCodeResult(result);
|
getLookupCodeProvider().setLookupCodeResult(result);
|
||||||
|
|
||||||
// test and verify
|
// test and verify
|
||||||
LookupCodeRequest request = new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, null);
|
LookupCodeRequest request = new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, null);
|
||||||
|
@ -107,7 +110,7 @@ public interface ILookupCodeTest {
|
||||||
result.setFound(true);
|
result.setFound(true);
|
||||||
result.getDesignations().add(designation1);
|
result.getDesignations().add(designation1);
|
||||||
result.getDesignations().add(designation2);
|
result.getDesignations().add(designation2);
|
||||||
getCodeSystemProvider().setLookupCodeResult(result);
|
getLookupCodeProvider().setLookupCodeResult(result);
|
||||||
|
|
||||||
// test and verify
|
// test and verify
|
||||||
LookupCodeRequest request = new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, null);
|
LookupCodeRequest request = new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, null);
|
||||||
|
@ -120,7 +123,7 @@ public interface ILookupCodeTest {
|
||||||
BaseConceptProperty property = createConceptProperty(propertyName, thePropertyValue);
|
BaseConceptProperty property = createConceptProperty(propertyName, thePropertyValue);
|
||||||
LookupCodeResult result = new LookupCodeResult();
|
LookupCodeResult result = new LookupCodeResult();
|
||||||
result.getProperties().add(property);
|
result.getProperties().add(property);
|
||||||
getCodeSystemProvider().setLookupCodeResult(result);
|
getLookupCodeProvider().setLookupCodeResult(result);
|
||||||
|
|
||||||
// test
|
// test
|
||||||
LookupCodeRequest request = new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, List.of(propertyName));
|
LookupCodeRequest request = new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, List.of(propertyName));
|
||||||
|
@ -148,7 +151,7 @@ public interface ILookupCodeTest {
|
||||||
propertyNamesToFilter.add(currentPropertyName);
|
propertyNamesToFilter.add(currentPropertyName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getCodeSystemProvider().setLookupCodeResult(result);
|
getLookupCodeProvider().setLookupCodeResult(result);
|
||||||
|
|
||||||
// test
|
// test
|
||||||
LookupCodeRequest request = new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, propertyNamesToFilter);
|
LookupCodeRequest request = new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, propertyNamesToFilter);
|
||||||
|
@ -172,7 +175,7 @@ public interface ILookupCodeTest {
|
||||||
group.addSubProperty(createConceptProperty(subPropertyName + i, thePropertyValues.get(i)));
|
group.addSubProperty(createConceptProperty(subPropertyName + i, thePropertyValues.get(i)));
|
||||||
}
|
}
|
||||||
result.getProperties().add(group);
|
result.getProperties().add(group);
|
||||||
getCodeSystemProvider().setLookupCodeResult(result);
|
getLookupCodeProvider().setLookupCodeResult(result);
|
||||||
|
|
||||||
// test and verify
|
// test and verify
|
||||||
LookupCodeRequest request = new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, List.of(groupName));
|
LookupCodeRequest request = new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, List.of(groupName));
|
||||||
|
@ -186,8 +189,8 @@ public interface ILookupCodeTest {
|
||||||
|
|
||||||
// verify
|
// verify
|
||||||
assertNotNull(outcome);
|
assertNotNull(outcome);
|
||||||
assertEquals(theRequest.getCode(), getCodeSystemProvider().getCode());
|
assertEquals(theRequest.getCode(), getLookupCodeProvider().getCode());
|
||||||
assertEquals(theRequest.getSystem(), getCodeSystemProvider().getSystem());
|
assertEquals(theRequest.getSystem(), getLookupCodeProvider().getSystem());
|
||||||
assertEquals(theExpectedResult.isFound(), outcome.isFound());
|
assertEquals(theExpectedResult.isFound(), outcome.isFound());
|
||||||
assertEquals(theExpectedResult.getErrorMessage(), outcome.getErrorMessage());
|
assertEquals(theExpectedResult.getErrorMessage(), outcome.getErrorMessage());
|
||||||
assertEquals(theExpectedResult.getCodeSystemDisplayName(), outcome.getCodeSystemDisplayName());
|
assertEquals(theExpectedResult.getCodeSystemDisplayName(), outcome.getCodeSystemDisplayName());
|
||||||
|
@ -207,7 +210,7 @@ public interface ILookupCodeTest {
|
||||||
LookupCodeResult result = new LookupCodeResult();
|
LookupCodeResult result = new LookupCodeResult();
|
||||||
result.setFound(true);
|
result.setFound(true);
|
||||||
result.getDesignations().add(theConceptDesignation);
|
result.getDesignations().add(theConceptDesignation);
|
||||||
getCodeSystemProvider().setLookupCodeResult(result);
|
getLookupCodeProvider().setLookupCodeResult(result);
|
||||||
|
|
||||||
// test and verify
|
// test and verify
|
||||||
LookupCodeRequest request = new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, null);
|
LookupCodeRequest request = new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, null);
|
||||||
|
@ -247,11 +250,4 @@ public interface ILookupCodeTest {
|
||||||
assertEquals(theActualDesignation.getUseSystem(), theExpectedDesignation.getUseSystem());
|
assertEquals(theActualDesignation.getUseSystem(), theExpectedDesignation.getUseSystem());
|
||||||
assertEquals(theActualDesignation.getUseDisplay(), theExpectedDesignation.getUseDisplay());
|
assertEquals(theActualDesignation.getUseDisplay(), theExpectedDesignation.getUseDisplay());
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IMyCodeSystemProvider extends IResourceProvider {
|
|
||||||
String getCode();
|
|
||||||
String getSystem();
|
|
||||||
|
|
||||||
void setLookupCodeResult(LookupCodeResult theLookupCodeResult);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ public interface IRemoteTerminologyLookupCodeTest extends ILookupCodeTest {
|
||||||
default void lookupCode_forCodeSystemWithCodeNotFound_returnsNotFound() {
|
default void lookupCode_forCodeSystemWithCodeNotFound_returnsNotFound() {
|
||||||
String baseUrl = getService().getBaseUrl();
|
String baseUrl = getService().getBaseUrl();
|
||||||
final String codeNotFound = "a";
|
final String codeNotFound = "a";
|
||||||
final String system = CODE_SYSTEM;
|
final String system = IValidationProviders.CODE_SYSTEM;
|
||||||
final String codeAndSystem = system + "#" + codeNotFound;
|
final String codeAndSystem = system + "#" + codeNotFound;
|
||||||
final String exceptionMessage = MessageFormat.format(MESSAGE_RESPONSE_NOT_FOUND, codeNotFound);
|
final String exceptionMessage = MessageFormat.format(MESSAGE_RESPONSE_NOT_FOUND, codeNotFound);
|
||||||
LookupCodeResult result = new LookupCodeResult()
|
LookupCodeResult result = new LookupCodeResult()
|
||||||
|
@ -31,7 +31,7 @@ public interface IRemoteTerminologyLookupCodeTest extends ILookupCodeTest {
|
||||||
.setSearchedForCode(codeNotFound)
|
.setSearchedForCode(codeNotFound)
|
||||||
.setSearchedForSystem(system)
|
.setSearchedForSystem(system)
|
||||||
.setErrorMessage("Unknown code \"" + codeAndSystem + "\". The Remote Terminology server " + baseUrl + " returned HTTP 404 Not Found: " + exceptionMessage);
|
.setErrorMessage("Unknown code \"" + codeAndSystem + "\". The Remote Terminology server " + baseUrl + " returned HTTP 404 Not Found: " + exceptionMessage);
|
||||||
getCodeSystemProvider().setLookupCodeResult(result);
|
getLookupCodeProvider().setLookupCodeResult(result);
|
||||||
|
|
||||||
LookupCodeRequest request = new LookupCodeRequest(system, codeNotFound, null, null);
|
LookupCodeRequest request = new LookupCodeRequest(system, codeNotFound, null, null);
|
||||||
verifyLookupCodeResult(request, result);
|
verifyLookupCodeResult(request, result);
|
||||||
|
@ -49,9 +49,11 @@ public interface IRemoteTerminologyLookupCodeTest extends ILookupCodeTest {
|
||||||
.setSearchedForCode(codeNotFound)
|
.setSearchedForCode(codeNotFound)
|
||||||
.setSearchedForSystem(system)
|
.setSearchedForSystem(system)
|
||||||
.setErrorMessage("Unknown code \"" + codeAndSystem + "\". The Remote Terminology server " + baseUrl + " returned HTTP 400 Bad Request: " + exceptionMessage);
|
.setErrorMessage("Unknown code \"" + codeAndSystem + "\". The Remote Terminology server " + baseUrl + " returned HTTP 400 Bad Request: " + exceptionMessage);
|
||||||
getCodeSystemProvider().setLookupCodeResult(result);
|
getLookupCodeProvider().setLookupCodeResult(result);
|
||||||
|
|
||||||
LookupCodeRequest request = new LookupCodeRequest(system, codeNotFound, null, null);
|
LookupCodeRequest request = new LookupCodeRequest(system, codeNotFound, null, null);
|
||||||
verifyLookupCodeResult(request, result);
|
verifyLookupCodeResult(request, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
package org.hl7.fhir.common.hapi.validation;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||||
|
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public interface IRemoteTerminologyValidateCodeTest extends IValidateCodeTest {
|
||||||
|
default List<IValidationSupport.CodeValidationIssue> getCodeValidationIssues(IBaseOperationOutcome theOperationOutcome) {
|
||||||
|
// this method should be removed once support for issues is fully implemented across all validator types
|
||||||
|
Optional<Collection<IValidationSupport.CodeValidationIssue>> issues = RemoteTerminologyServiceValidationSupport.createCodeValidationIssues(theOperationOutcome, getService().getFhirContext().getVersion().getVersion());
|
||||||
|
return issues.map(theCodeValidationIssues -> theCodeValidationIssues.stream().toList()).orElseGet(List::of);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,331 @@
|
||||||
|
package org.hl7.fhir.common.hapi.validation;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||||
|
import ca.uhn.fhir.context.support.IValidationSupport.CodeValidationResult;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static ca.uhn.fhir.context.support.IValidationSupport.IssueSeverity.ERROR;
|
||||||
|
import static org.hl7.fhir.common.hapi.validation.IValidationProviders.CODE;
|
||||||
|
import static org.hl7.fhir.common.hapi.validation.IValidationProviders.CODE_SYSTEM;
|
||||||
|
import static org.hl7.fhir.common.hapi.validation.IValidationProviders.CODE_SYSTEM_VERSION;
|
||||||
|
import static org.hl7.fhir.common.hapi.validation.IValidationProviders.DISPLAY;
|
||||||
|
import static org.hl7.fhir.common.hapi.validation.IValidationProviders.ERROR_MESSAGE;
|
||||||
|
import static org.hl7.fhir.common.hapi.validation.IValidationProviders.VALUE_SET_URL;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
|
||||||
|
public interface IValidateCodeTest {
|
||||||
|
|
||||||
|
IValidationProviders.IMyCodeSystemProvider getCodeSystemProvider();
|
||||||
|
IValidationProviders.IMyValueSetProvider getValueSetProvider();
|
||||||
|
IValidationSupport getService();
|
||||||
|
IBaseParameters createParameters(Boolean theResult, String theDisplay, String theMessage, IBaseResource theIssuesResource);
|
||||||
|
String getCodeSystemError();
|
||||||
|
String getValueSetError();
|
||||||
|
IBaseOperationOutcome getCodeSystemInvalidCodeOutcome();
|
||||||
|
IBaseOperationOutcome getValueSetInvalidCodeOutcome();
|
||||||
|
|
||||||
|
default void createCodeSystemReturnParameters(Boolean theResult, String theDisplay, String theMessage, IBaseResource theIssuesResource) {
|
||||||
|
getCodeSystemProvider().setReturnParams(createParameters(theResult, theDisplay, theMessage, theIssuesResource));
|
||||||
|
}
|
||||||
|
|
||||||
|
default void createValueSetReturnParameters(Boolean theResult, String theDisplay, String theMessage, IBaseResource theIssuesResource) {
|
||||||
|
getValueSetProvider().setReturnParams(createParameters(theResult, theDisplay, theMessage, theIssuesResource));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
default void validateCode_withCodeSystemBlankCode_ReturnsNull() {
|
||||||
|
CodeValidationResult outcome = getService()
|
||||||
|
.validateCode(null, null, CODE_SYSTEM, null, DISPLAY, null);
|
||||||
|
assertNull(outcome);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
default void validateCode_withValueSetBlankCode_returnsNull() {
|
||||||
|
CodeValidationResult outcome = getService().validateCode(null, null, CODE_SYSTEM, "", DISPLAY, VALUE_SET_URL);
|
||||||
|
assertNull(outcome);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Stream<Arguments> getRemoteTerminologyServerResponses() {
|
||||||
|
return Stream.of(
|
||||||
|
Arguments.of(new ResourceNotFoundException("System Not Present"), "404 Not Found: System Not Present",
|
||||||
|
"Unknown code \"null#CODE\". The Remote Terminology server", null, null),
|
||||||
|
Arguments.of(new InvalidRequestException("Invalid Request"), "400 Bad Request: Invalid Request",
|
||||||
|
"Unknown code \"null#CODE\". The Remote Terminology server", null, null),
|
||||||
|
Arguments.of(new ResourceNotFoundException("System Not Present"), "404 Not Found: System Not Present",
|
||||||
|
"Unknown code \"NotFoundSystem#CODE\". The Remote Terminology server", "NotFoundSystem", null),
|
||||||
|
Arguments.of(new InvalidRequestException("Invalid Request"), "400 Bad Request: Invalid Request",
|
||||||
|
"Unknown code \"InvalidSystem#CODE\". The Remote Terminology server", "InvalidSystem", null),
|
||||||
|
Arguments.of(new ResourceNotFoundException("System Not Present"), "404 Not Found: System Not Present",
|
||||||
|
"Unknown code \"null#CODE\" for ValueSet with URL \"NotFoundValueSetUrl\". The Remote Terminology server",
|
||||||
|
null, "NotFoundValueSetUrl"),
|
||||||
|
Arguments.of(new InvalidRequestException("Invalid Request"), "400 Bad Request: Invalid Request",
|
||||||
|
"Unknown code \"null#CODE\" for ValueSet with URL \"InvalidValueSetUrl\". The Remote Terminology server", null, "InvalidValueSetUrl"),
|
||||||
|
Arguments.of(new ResourceNotFoundException("System Not Present"), "404 Not Found: System Not Present",
|
||||||
|
"Unknown code \"NotFoundSystem#CODE\" for ValueSet with URL \"NotFoundValueSetUrl\". The Remote Terminology server",
|
||||||
|
"NotFoundSystem", "NotFoundValueSetUrl"),
|
||||||
|
Arguments.of(new InvalidRequestException("Invalid Request"), "400 Bad Request: Invalid Request",
|
||||||
|
"Unknown code \"InvalidSystem#CODE\" for ValueSet with URL \"InvalidValueSetUrl\". The Remote Terminology server", "InvalidSystem", "InvalidValueSetUrl")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource(value = "getRemoteTerminologyServerResponses")
|
||||||
|
default void validateCode_codeSystemAndValueSetUrlAreIncorrect_returnsValidationResultWithError(Exception theException,
|
||||||
|
String theServerMessage,
|
||||||
|
String theValidationMessage,
|
||||||
|
String theCodeSystem,
|
||||||
|
String theValueSetUrl) {
|
||||||
|
getCodeSystemProvider().setException(theException);
|
||||||
|
getValueSetProvider().setException(theException);
|
||||||
|
CodeValidationResult outcome = getService().validateCode(null, null, theCodeSystem, CODE, DISPLAY, theValueSetUrl);
|
||||||
|
|
||||||
|
verifyErrorResultFromException(outcome, theValidationMessage, theServerMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
default void verifyErrorResultFromException(CodeValidationResult outcome, String... theMessages) {
|
||||||
|
assertNotNull(outcome);
|
||||||
|
assertEquals(ERROR, outcome.getSeverity());
|
||||||
|
assertNotNull(outcome.getMessage());
|
||||||
|
for (String message : theMessages) {
|
||||||
|
assertTrue(outcome.getMessage().contains(message));
|
||||||
|
}
|
||||||
|
assertFalse(outcome.getCodeValidationIssues().isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
default void validateCode_withMissingResult_returnsCorrectly() {
|
||||||
|
createCodeSystemReturnParameters(null, null, null, null);
|
||||||
|
IValidationSupport service = getService();
|
||||||
|
try {
|
||||||
|
service.validateCode(null, null, CODE_SYSTEM, CODE, null, null);
|
||||||
|
fail();
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
assertEquals("HAPI-2560: Parameter `result` is missing from the $validate-code response.", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
default void validateCode_withValueSetSuccess_returnsCorrectly() {
|
||||||
|
createValueSetReturnParameters(true, DISPLAY, null, null);
|
||||||
|
|
||||||
|
CodeValidationResult outcome = getService().validateCode(null, null, CODE_SYSTEM, CODE, DISPLAY, VALUE_SET_URL);
|
||||||
|
assertNotNull(outcome);
|
||||||
|
assertEquals(CODE, outcome.getCode());
|
||||||
|
assertEquals(DISPLAY, outcome.getDisplay());
|
||||||
|
assertNull(outcome.getSeverity());
|
||||||
|
assertNull(outcome.getMessage());
|
||||||
|
assertTrue(outcome.getCodeValidationIssues().isEmpty());
|
||||||
|
|
||||||
|
assertEquals(CODE, getValueSetProvider().getCode());
|
||||||
|
assertEquals(DISPLAY, getValueSetProvider().getDisplay());
|
||||||
|
assertEquals(VALUE_SET_URL, getValueSetProvider().getValueSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
default void validateCode_withCodeSystemSuccess_returnsCorrectly() {
|
||||||
|
createCodeSystemReturnParameters(true, DISPLAY, null, null);
|
||||||
|
|
||||||
|
CodeValidationResult outcome = getService().validateCode(null, null, CODE_SYSTEM, CODE, DISPLAY, null);
|
||||||
|
assertNotNull(outcome);
|
||||||
|
assertEquals(CODE, outcome.getCode());
|
||||||
|
assertEquals(DISPLAY, outcome.getDisplay());
|
||||||
|
assertNull(outcome.getSeverity());
|
||||||
|
assertNull(outcome.getMessage());
|
||||||
|
assertTrue(outcome.getCodeValidationIssues().isEmpty());
|
||||||
|
|
||||||
|
assertEquals(CODE, getCodeSystemProvider().getCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
default void validateCode_withCodeSystemProvidingMinimalInputs_ReturnsSuccess() {
|
||||||
|
createCodeSystemReturnParameters(true, null, null, null);
|
||||||
|
|
||||||
|
CodeValidationResult outcome = getService()
|
||||||
|
.validateCode(null, null, CODE_SYSTEM, CODE, null, null);
|
||||||
|
assertNotNull(outcome);
|
||||||
|
assertEquals(CODE_SYSTEM, outcome.getCodeSystemName());
|
||||||
|
assertEquals(CODE_SYSTEM_VERSION, outcome.getCodeSystemVersion());
|
||||||
|
assertEquals(CODE, outcome.getCode());
|
||||||
|
assertNull(outcome.getDisplay());
|
||||||
|
assertNull(outcome.getSeverity());
|
||||||
|
assertNull(outcome.getMessage());
|
||||||
|
assertTrue(outcome.getCodeValidationIssues().isEmpty());
|
||||||
|
|
||||||
|
assertEquals(CODE, getCodeSystemProvider().getCode());
|
||||||
|
assertEquals(CODE_SYSTEM, getCodeSystemProvider().getSystem());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
default void validateCode_withCodeSystemSuccessWithMessageValue_returnsCorrectly() {
|
||||||
|
createCodeSystemReturnParameters(true, DISPLAY, null, null);
|
||||||
|
|
||||||
|
CodeValidationResult outcome = getService()
|
||||||
|
.validateCode(null, null, CODE_SYSTEM, CODE, DISPLAY, null);
|
||||||
|
assertNotNull(outcome);
|
||||||
|
assertEquals(CODE_SYSTEM, outcome.getCodeSystemName());
|
||||||
|
assertEquals(CODE_SYSTEM_VERSION, outcome.getCodeSystemVersion());
|
||||||
|
assertEquals(CODE, outcome.getCode());
|
||||||
|
assertEquals(DISPLAY, outcome.getDisplay());
|
||||||
|
assertNull(outcome.getSeverity());
|
||||||
|
assertNull(outcome.getMessage());
|
||||||
|
assertTrue(outcome.getCodeValidationIssues().isEmpty());
|
||||||
|
|
||||||
|
assertEquals(CODE, getCodeSystemProvider().getCode());
|
||||||
|
assertEquals(DISPLAY, getCodeSystemProvider().getDisplay());
|
||||||
|
assertEquals(CODE_SYSTEM, getCodeSystemProvider().getSystem());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
default void validateCode_withCodeSystemError_returnsCorrectly() {
|
||||||
|
IBaseOperationOutcome invalidCodeOutcome = getCodeSystemInvalidCodeOutcome();
|
||||||
|
createCodeSystemReturnParameters(false, null, ERROR_MESSAGE, invalidCodeOutcome);
|
||||||
|
|
||||||
|
CodeValidationResult outcome = getService()
|
||||||
|
.validateCode(null, null, CODE_SYSTEM, CODE, null, null);
|
||||||
|
assertNotNull(outcome);
|
||||||
|
assertEquals(CODE_SYSTEM, outcome.getCodeSystemName());
|
||||||
|
assertEquals(CODE_SYSTEM_VERSION, outcome.getCodeSystemVersion());
|
||||||
|
// assertEquals(CODE, outcome.getCode());
|
||||||
|
assertEquals(ERROR, outcome.getSeverity());
|
||||||
|
assertEquals(getCodeSystemError(), outcome.getMessage());
|
||||||
|
assertFalse(outcome.getCodeValidationIssues().isEmpty());
|
||||||
|
verifyIssues(invalidCodeOutcome, outcome);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
default void validateCode_withCodeSystemErrorAndIssues_returnsCorrectly() {
|
||||||
|
createCodeSystemReturnParameters(false, null, ERROR_MESSAGE, null);
|
||||||
|
|
||||||
|
CodeValidationResult outcome = getService()
|
||||||
|
.validateCode(null, null, CODE_SYSTEM, CODE, null, null);
|
||||||
|
|
||||||
|
String expectedError = getCodeSystemError();
|
||||||
|
assertNotNull(outcome);
|
||||||
|
assertEquals(CODE_SYSTEM, outcome.getCodeSystemName());
|
||||||
|
assertEquals(CODE_SYSTEM_VERSION, outcome.getCodeSystemVersion());
|
||||||
|
// assertEquals(CODE, outcome.getCode());
|
||||||
|
assertNull(outcome.getDisplay());
|
||||||
|
assertEquals(ERROR, outcome.getSeverity());
|
||||||
|
assertEquals(expectedError, outcome.getMessage());
|
||||||
|
assertFalse(outcome.getCodeValidationIssues().isEmpty());
|
||||||
|
assertEquals(1, outcome.getCodeValidationIssues().size());
|
||||||
|
assertEquals(expectedError, outcome.getCodeValidationIssues().get(0).getMessage());
|
||||||
|
assertEquals(ERROR, outcome.getCodeValidationIssues().get(0).getSeverity());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
default void validateCode_withValueSetProvidingMinimalInputsSuccess_returnsCorrectly() {
|
||||||
|
createValueSetReturnParameters(true, null, null, null);
|
||||||
|
|
||||||
|
CodeValidationResult outcome = getService()
|
||||||
|
.validateCode(null, null, CODE_SYSTEM, CODE, null, VALUE_SET_URL);
|
||||||
|
assertNotNull(outcome);
|
||||||
|
assertEquals(CODE_SYSTEM, outcome.getCodeSystemName());
|
||||||
|
assertEquals(CODE_SYSTEM_VERSION, outcome.getCodeSystemVersion());
|
||||||
|
assertEquals(CODE, outcome.getCode());
|
||||||
|
assertNull(outcome.getDisplay());
|
||||||
|
assertNull(outcome.getSeverity());
|
||||||
|
assertNull(outcome.getMessage());
|
||||||
|
assertTrue(outcome.getCodeValidationIssues().isEmpty());
|
||||||
|
|
||||||
|
assertEquals(CODE, getValueSetProvider().getCode());
|
||||||
|
assertEquals(VALUE_SET_URL, getValueSetProvider().getValueSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
default void validateCode_withValueSetSuccessWithMessage_returnsCorrectly() {
|
||||||
|
createValueSetReturnParameters(true, DISPLAY, null, null);
|
||||||
|
|
||||||
|
CodeValidationResult outcome = getService()
|
||||||
|
.validateCode(null, null, CODE_SYSTEM, CODE, DISPLAY, VALUE_SET_URL);
|
||||||
|
assertNotNull(outcome);
|
||||||
|
assertEquals(CODE_SYSTEM, outcome.getCodeSystemName());
|
||||||
|
assertEquals(CODE_SYSTEM_VERSION, outcome.getCodeSystemVersion());
|
||||||
|
assertEquals(CODE, outcome.getCode());
|
||||||
|
assertEquals(DISPLAY, outcome.getDisplay());
|
||||||
|
assertNull(outcome.getSeverity());
|
||||||
|
assertNull(outcome.getMessage());
|
||||||
|
assertTrue(outcome.getCodeValidationIssues().isEmpty());
|
||||||
|
|
||||||
|
assertEquals(CODE, getValueSetProvider().getCode());
|
||||||
|
assertEquals(DISPLAY, getValueSetProvider().getDisplay());
|
||||||
|
assertEquals(VALUE_SET_URL, getValueSetProvider().getValueSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
default void validateCode_withValueSetError_returnsCorrectly() {
|
||||||
|
createValueSetReturnParameters(false, DISPLAY, ERROR_MESSAGE, null);
|
||||||
|
|
||||||
|
CodeValidationResult outcome = getService()
|
||||||
|
.validateCode(null, null, CODE_SYSTEM, CODE, DISPLAY, VALUE_SET_URL);
|
||||||
|
|
||||||
|
String expectedError = getValueSetError();
|
||||||
|
assertNotNull(outcome);
|
||||||
|
assertEquals(CODE_SYSTEM, outcome.getCodeSystemName());
|
||||||
|
assertEquals(CODE_SYSTEM_VERSION, outcome.getCodeSystemVersion());
|
||||||
|
// assertEquals(CODE, outcome.getCode());
|
||||||
|
assertEquals(DISPLAY, outcome.getDisplay());
|
||||||
|
assertEquals(ERROR, outcome.getSeverity());
|
||||||
|
assertEquals(expectedError, outcome.getMessage());
|
||||||
|
assertEquals(1, outcome.getCodeValidationIssues().size());
|
||||||
|
assertEquals(expectedError, outcome.getCodeValidationIssues().get(0).getMessage());
|
||||||
|
assertEquals(ERROR, outcome.getCodeValidationIssues().get(0).getSeverity());
|
||||||
|
|
||||||
|
assertEquals(CODE, getValueSetProvider().getCode());
|
||||||
|
assertEquals(DISPLAY, getValueSetProvider().getDisplay());
|
||||||
|
assertEquals(VALUE_SET_URL, getValueSetProvider().getValueSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
default void validateCode_withValueSetErrorWithIssues_returnsCorrectly() {
|
||||||
|
IBaseOperationOutcome invalidCodeOutcome = getValueSetInvalidCodeOutcome();
|
||||||
|
createValueSetReturnParameters(false, DISPLAY, ERROR_MESSAGE, invalidCodeOutcome);
|
||||||
|
|
||||||
|
CodeValidationResult outcome = getService()
|
||||||
|
.validateCode(null, null, CODE_SYSTEM, CODE, DISPLAY, VALUE_SET_URL);
|
||||||
|
assertNotNull(outcome);
|
||||||
|
assertEquals(CODE_SYSTEM, outcome.getCodeSystemName());
|
||||||
|
assertEquals(CODE_SYSTEM_VERSION, outcome.getCodeSystemVersion());
|
||||||
|
// assertEquals(CODE, outcome.getCode());
|
||||||
|
assertEquals(DISPLAY, outcome.getDisplay());
|
||||||
|
assertEquals(ERROR, outcome.getSeverity());
|
||||||
|
assertEquals(getValueSetError(), outcome.getMessage());
|
||||||
|
assertFalse(outcome.getCodeValidationIssues().isEmpty());
|
||||||
|
verifyIssues(invalidCodeOutcome, outcome);
|
||||||
|
|
||||||
|
assertEquals(CODE, getValueSetProvider().getCode());
|
||||||
|
assertEquals(DISPLAY, getValueSetProvider().getDisplay());
|
||||||
|
assertEquals(VALUE_SET_URL, getValueSetProvider().getValueSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
default void verifyIssues(IBaseOperationOutcome theOperationOutcome, CodeValidationResult theResult) {
|
||||||
|
List<IValidationSupport.CodeValidationIssue> issues = getCodeValidationIssues(theOperationOutcome);
|
||||||
|
assertEquals(issues.size(), theResult.getCodeValidationIssues().size());
|
||||||
|
for (int i = 0; i < issues.size(); i++) {
|
||||||
|
IValidationSupport.CodeValidationIssue expectedIssue = issues.get(i);
|
||||||
|
IValidationSupport.CodeValidationIssue actualIssue = theResult.getCodeValidationIssues().get(i);
|
||||||
|
assertEquals(expectedIssue.getCode(), actualIssue.getCode());
|
||||||
|
assertEquals(expectedIssue.getSeverity(), actualIssue.getSeverity());
|
||||||
|
assertEquals(expectedIssue.getCoding(), actualIssue.getCoding());
|
||||||
|
assertEquals(expectedIssue.getMessage(), actualIssue.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<IValidationSupport.CodeValidationIssue> getCodeValidationIssues(IBaseOperationOutcome theOperationOutcome);
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
package org.hl7.fhir.common.hapi.validation;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||||
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||||
|
|
||||||
|
public interface IValidationProviders {
|
||||||
|
String CODE_SYSTEM = "http://code.system/url";
|
||||||
|
String CODE_SYSTEM_VERSION = "1.0.0";
|
||||||
|
String CODE_SYSTEM_NAME = "Test Code System";
|
||||||
|
String CODE = "CODE";
|
||||||
|
String VALUE_SET_URL = "http://value.set/url";
|
||||||
|
String DISPLAY = "Explanation for code TestCode.";
|
||||||
|
String LANGUAGE = "en";
|
||||||
|
String ERROR_MESSAGE = "This is an error message";
|
||||||
|
|
||||||
|
interface IMyCodeSystemProvider extends IResourceProvider {
|
||||||
|
String getCode();
|
||||||
|
String getSystem();
|
||||||
|
String getDisplay();
|
||||||
|
void setException(Exception theException);
|
||||||
|
void setReturnParams(IBaseParameters theParameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IMyLookupCodeProvider extends IResourceProvider {
|
||||||
|
String getCode();
|
||||||
|
String getSystem();
|
||||||
|
void setLookupCodeResult(IValidationSupport.LookupCodeResult theLookupCodeResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IMyValueSetProvider extends IResourceProvider {
|
||||||
|
String getCode();
|
||||||
|
String getSystem();
|
||||||
|
String getDisplay();
|
||||||
|
String getValueSet();
|
||||||
|
void setException(Exception theException);
|
||||||
|
void setReturnParams(IBaseParameters theParameters);
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,8 +21,8 @@ import org.hl7.fhir.common.hapi.validation.support.InMemoryTerminologyServerVali
|
||||||
import org.hl7.fhir.common.hapi.validation.support.SnapshotGeneratingValidationSupport;
|
import org.hl7.fhir.common.hapi.validation.support.SnapshotGeneratingValidationSupport;
|
||||||
import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain;
|
import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain;
|
||||||
import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator;
|
import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator;
|
||||||
import org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext;
|
|
||||||
import org.hl7.fhir.dstu3.fhirpath.FHIRPathEngine;
|
import org.hl7.fhir.dstu3.fhirpath.FHIRPathEngine;
|
||||||
|
import org.hl7.fhir.dstu3.hapi.ctx.HapiWorkerContext;
|
||||||
import org.hl7.fhir.dstu3.model.Base;
|
import org.hl7.fhir.dstu3.model.Base;
|
||||||
import org.hl7.fhir.dstu3.model.BooleanType;
|
import org.hl7.fhir.dstu3.model.BooleanType;
|
||||||
import org.hl7.fhir.dstu3.model.Bundle;
|
import org.hl7.fhir.dstu3.model.Bundle;
|
||||||
|
@ -55,7 +55,6 @@ import org.hl7.fhir.dstu3.model.StructureDefinition.StructureDefinitionKind;
|
||||||
import org.hl7.fhir.dstu3.model.ValueSet;
|
import org.hl7.fhir.dstu3.model.ValueSet;
|
||||||
import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionComponent;
|
import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionComponent;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
|
||||||
import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor;
|
import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor;
|
||||||
import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher;
|
import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher;
|
||||||
import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy;
|
import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy;
|
||||||
|
@ -65,6 +64,8 @@ import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Disabled;
|
import org.junit.jupiter.api.Disabled;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
import org.mockito.invocation.InvocationOnMock;
|
import org.mockito.invocation.InvocationOnMock;
|
||||||
import org.mockito.quality.Strictness;
|
import org.mockito.quality.Strictness;
|
||||||
import org.mockito.stubbing.Answer;
|
import org.mockito.stubbing.Answer;
|
||||||
|
@ -110,6 +111,7 @@ public class FhirInstanceValidatorDstu3Test extends BaseValidationTestWithInline
|
||||||
private FhirValidator myVal;
|
private FhirValidator myVal;
|
||||||
private ArrayList<String> myValidConcepts;
|
private ArrayList<String> myValidConcepts;
|
||||||
private Set<String> myValidSystems = new HashSet<>();
|
private Set<String> myValidSystems = new HashSet<>();
|
||||||
|
private Set<String> myValidSystemsNotReturningIssues = new HashSet<>();
|
||||||
private HashMap<String, StructureDefinition> myStructureDefinitions;
|
private HashMap<String, StructureDefinition> myStructureDefinitions;
|
||||||
private HashMap<String, CodeSystem> myCodeSystems;
|
private HashMap<String, CodeSystem> myCodeSystems;
|
||||||
private HashMap<String, ValueSet> myValueSets;
|
private HashMap<String, ValueSet> myValueSets;
|
||||||
|
@ -117,7 +119,15 @@ public class FhirInstanceValidatorDstu3Test extends BaseValidationTestWithInline
|
||||||
private CachingValidationSupport myValidationSupport;
|
private CachingValidationSupport myValidationSupport;
|
||||||
|
|
||||||
private void addValidConcept(String theSystem, String theCode) {
|
private void addValidConcept(String theSystem, String theCode) {
|
||||||
myValidSystems.add(theSystem);
|
addValidConcept(theSystem, theCode, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addValidConcept(String theSystem, String theCode, boolean theShouldSystemReturnIssuesForInvalidCode) {
|
||||||
|
if (theShouldSystemReturnIssuesForInvalidCode) {
|
||||||
|
myValidSystems.add(theSystem);
|
||||||
|
} else {
|
||||||
|
myValidSystemsNotReturningIssues.add(theSystem);
|
||||||
|
}
|
||||||
myValidConcepts.add(theSystem + "___" + theCode);
|
myValidConcepts.add(theSystem + "___" + theCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -219,9 +229,10 @@ public class FhirInstanceValidatorDstu3Test extends BaseValidationTestWithInline
|
||||||
retVal = new IValidationSupport.CodeValidationResult().setCode(code);
|
retVal = new IValidationSupport.CodeValidationResult().setCode(code);
|
||||||
} else if (myValidSystems.contains(system)) {
|
} else if (myValidSystems.contains(system)) {
|
||||||
final String message = "Unknown code (for '" + system + "#" + code + "')";
|
final String message = "Unknown code (for '" + system + "#" + code + "')";
|
||||||
|
retVal = new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.ERROR.toCode()).setMessage(message).setCodeValidationIssues(Collections.singletonList(new IValidationSupport.CodeValidationIssue(message, IValidationSupport.IssueSeverity.ERROR, IValidationSupport.CodeValidationIssueCode.CODE_INVALID, IValidationSupport.CodeValidationIssueCoding.INVALID_CODE)));
|
||||||
return new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.ERROR.toCode()).setMessage(message).setCodeValidationIssues(Collections.singletonList(new IValidationSupport.CodeValidationIssue(message, IValidationSupport.IssueSeverity.ERROR, IValidationSupport.CodeValidationIssueCode.CODE_INVALID, IValidationSupport.CodeValidationIssueCoding.INVALID_CODE)));
|
} else if (myValidSystemsNotReturningIssues.contains(system)) {
|
||||||
|
final String message = "Unknown code (for '" + system + "#" + code + "')";
|
||||||
|
retVal = new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.ERROR.toCode()).setMessage(message);
|
||||||
} else if (myCodeSystems.containsKey(system)) {
|
} else if (myCodeSystems.containsKey(system)) {
|
||||||
CodeSystem cs = myCodeSystems.get(system);
|
CodeSystem cs = myCodeSystems.get(system);
|
||||||
Optional<ConceptDefinitionComponent> found = cs.getConcept().stream().filter(t -> t.getCode().equals(code)).findFirst();
|
Optional<ConceptDefinitionComponent> found = cs.getConcept().stream().filter(t -> t.getCode().equals(code)).findFirst();
|
||||||
|
@ -1161,9 +1172,11 @@ public class FhirInstanceValidatorDstu3Test extends BaseValidationTestWithInline
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
// TODO: uncomment value false when https://github.com/hapifhir/org.hl7.fhir.core/issues/1766 is fixed
|
||||||
public void testValidateResourceContainingLoincCode() {
|
@ParameterizedTest
|
||||||
addValidConcept("http://loinc.org", "1234567");
|
@ValueSource(booleans = {true, /*false*/})
|
||||||
|
public void testValidateResourceContainingLoincCode(boolean theShouldSystemReturnIssuesForInvalidCode) {
|
||||||
|
addValidConcept("http://loinc.org", "1234567", theShouldSystemReturnIssuesForInvalidCode);
|
||||||
|
|
||||||
Observation input = new Observation();
|
Observation input = new Observation();
|
||||||
// input.getMeta().addProfile("http://hl7.org/fhir/StructureDefinition/devicemetricobservation");
|
// input.getMeta().addProfile("http://hl7.org/fhir/StructureDefinition/devicemetricobservation");
|
||||||
|
|
|
@ -0,0 +1,159 @@
|
||||||
|
package org.hl7.fhir.dstu3.hapi.validation;
|
||||||
|
|
||||||
|
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.api.server.RequestDetails;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import org.hl7.fhir.common.hapi.validation.IValidationProviders;
|
||||||
|
import org.hl7.fhir.dstu3.model.BooleanType;
|
||||||
|
import org.hl7.fhir.dstu3.model.CodeSystem;
|
||||||
|
import org.hl7.fhir.dstu3.model.CodeType;
|
||||||
|
import org.hl7.fhir.dstu3.model.Coding;
|
||||||
|
import org.hl7.fhir.dstu3.model.IdType;
|
||||||
|
import org.hl7.fhir.dstu3.model.Parameters;
|
||||||
|
import org.hl7.fhir.dstu3.model.StringType;
|
||||||
|
import org.hl7.fhir.dstu3.model.UriType;
|
||||||
|
import org.hl7.fhir.dstu3.model.ValueSet;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface IValidateCodeProvidersDstu3 {
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
class MyCodeSystemProviderDstu3 implements IValidationProviders.IMyCodeSystemProvider {
|
||||||
|
private UriType mySystemUrl;
|
||||||
|
private CodeType myCode;
|
||||||
|
private StringType myDisplay;
|
||||||
|
private Exception myException;
|
||||||
|
private Parameters myReturnParams;
|
||||||
|
|
||||||
|
@Operation(name = "validate-code", idempotent = true, returnParameters = {
|
||||||
|
@OperationParam(name = "result", type = org.hl7.fhir.dstu3.model.BooleanType.class, min = 1),
|
||||||
|
@OperationParam(name = "message", type = org.hl7.fhir.dstu3.model.StringType.class),
|
||||||
|
@OperationParam(name = "display", type = org.hl7.fhir.dstu3.model.StringType.class)
|
||||||
|
})
|
||||||
|
public org.hl7.fhir.dstu3.model.Parameters validateCode(
|
||||||
|
HttpServletRequest theServletRequest,
|
||||||
|
@IdParam(optional = true) org.hl7.fhir.dstu3.model.IdType theId,
|
||||||
|
@OperationParam(name = "url", min = 0, max = 1) org.hl7.fhir.dstu3.model.UriType theCodeSystemUrl,
|
||||||
|
@OperationParam(name = "code", min = 0, max = 1) org.hl7.fhir.dstu3.model.CodeType theCode,
|
||||||
|
@OperationParam(name = "display", min = 0, max = 1) org.hl7.fhir.dstu3.model.StringType theDisplay
|
||||||
|
) throws Exception {
|
||||||
|
mySystemUrl = theCodeSystemUrl;
|
||||||
|
myCode = theCode;
|
||||||
|
myDisplay = theDisplay;
|
||||||
|
if (myException != null) {
|
||||||
|
throw myException;
|
||||||
|
}
|
||||||
|
return myReturnParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters= {
|
||||||
|
@OperationParam(name = "name", type = org.hl7.fhir.dstu3.model.StringType.class, min = 1),
|
||||||
|
@OperationParam(name = "version", type = org.hl7.fhir.dstu3.model.StringType.class),
|
||||||
|
@OperationParam(name = "display", type = org.hl7.fhir.dstu3.model.StringType.class, min = 1),
|
||||||
|
@OperationParam(name = "abstract", type = org.hl7.fhir.dstu3.model.BooleanType.class, min = 1),
|
||||||
|
@OperationParam(name = "property", type = org.hl7.fhir.dstu3.model.StringType.class, min = 0, max = OperationParam.MAX_UNLIMITED)
|
||||||
|
})
|
||||||
|
public IBaseParameters lookup(
|
||||||
|
HttpServletRequest theServletRequest,
|
||||||
|
@OperationParam(name = "code", max = 1) org.hl7.fhir.dstu3.model.CodeType theCode,
|
||||||
|
@OperationParam(name = "system",max = 1) org.hl7.fhir.dstu3.model.UriType theSystem,
|
||||||
|
@OperationParam(name = "coding", max = 1) Coding theCoding,
|
||||||
|
@OperationParam(name = "version", max = 1) org.hl7.fhir.dstu3.model.StringType theVersion,
|
||||||
|
@OperationParam(name = "displayLanguage", max = 1) org.hl7.fhir.dstu3.model.CodeType theDisplayLanguage,
|
||||||
|
@OperationParam(name = "property", max = OperationParam.MAX_UNLIMITED) List<org.hl7.fhir.dstu3.model.CodeType> thePropertyNames,
|
||||||
|
RequestDetails theRequestDetails
|
||||||
|
) {
|
||||||
|
myCode = theCode;
|
||||||
|
return myReturnParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends IBaseResource> getResourceType() {
|
||||||
|
return CodeSystem.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setException(Exception theException) {
|
||||||
|
myException = theException;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void setReturnParams(IBaseParameters theParameters) {
|
||||||
|
myReturnParams = (Parameters) theParameters;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public String getCode() {
|
||||||
|
return myCode != null ? myCode.getValueAsString() : null;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public String getSystem() {
|
||||||
|
return mySystemUrl != null ? mySystemUrl.getValueAsString() : null;
|
||||||
|
}
|
||||||
|
public String getDisplay() {
|
||||||
|
return myDisplay != null ? myDisplay.getValue() : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
class MyValueSetProviderDstu3 implements IValidationProviders.IMyValueSetProvider {
|
||||||
|
private Exception myException;
|
||||||
|
private Parameters myReturnParams;
|
||||||
|
private UriType mySystemUrl;
|
||||||
|
private UriType myValueSetUrl;
|
||||||
|
private CodeType myCode;
|
||||||
|
private StringType myDisplay;
|
||||||
|
|
||||||
|
@Operation(name = "validate-code", idempotent = true, returnParameters = {
|
||||||
|
@OperationParam(name = "result", type = BooleanType.class, min = 1),
|
||||||
|
@OperationParam(name = "message", type = org.hl7.fhir.dstu3.model.StringType.class),
|
||||||
|
@OperationParam(name = "display", type = org.hl7.fhir.dstu3.model.StringType.class)
|
||||||
|
})
|
||||||
|
public Parameters validateCode(
|
||||||
|
HttpServletRequest theServletRequest,
|
||||||
|
@IdParam(optional = true) IdType theId,
|
||||||
|
@OperationParam(name = "url", min = 0, max = 1) org.hl7.fhir.dstu3.model.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") org.hl7.fhir.dstu3.model.ValueSet theValueSet
|
||||||
|
) throws Exception {
|
||||||
|
mySystemUrl = theSystem;
|
||||||
|
myValueSetUrl = theValueSetUrl;
|
||||||
|
myCode = theCode;
|
||||||
|
myDisplay = theDisplay;
|
||||||
|
if (myException != null) {
|
||||||
|
throw myException;
|
||||||
|
}
|
||||||
|
return myReturnParams;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public Class<? extends IBaseResource> getResourceType() {
|
||||||
|
return ValueSet.class;
|
||||||
|
}
|
||||||
|
public void setException(Exception theException) {
|
||||||
|
myException = theException;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void setReturnParams(IBaseParameters theParameters) {
|
||||||
|
myReturnParams = (Parameters) theParameters;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public String getCode() {
|
||||||
|
return myCode != null ? myCode.getValueAsString() : null;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public String getSystem() {
|
||||||
|
return mySystemUrl != null ? mySystemUrl.getValueAsString() : null;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public String getValueSet() {
|
||||||
|
return myValueSetUrl != null ? myValueSetUrl.getValueAsString() : null;
|
||||||
|
}
|
||||||
|
public String getDisplay() {
|
||||||
|
return myDisplay != null ? myDisplay.getValue() : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,6 +14,7 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||||
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import org.hl7.fhir.common.hapi.validation.IRemoteTerminologyLookupCodeTest;
|
import org.hl7.fhir.common.hapi.validation.IRemoteTerminologyLookupCodeTest;
|
||||||
|
import org.hl7.fhir.common.hapi.validation.IValidationProviders;
|
||||||
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
|
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
|
||||||
import org.hl7.fhir.dstu3.model.BooleanType;
|
import org.hl7.fhir.dstu3.model.BooleanType;
|
||||||
import org.hl7.fhir.dstu3.model.CodeSystem;
|
import org.hl7.fhir.dstu3.model.CodeSystem;
|
||||||
|
@ -29,6 +30,7 @@ import org.hl7.fhir.r4.model.DateTimeType;
|
||||||
import org.hl7.fhir.r4.model.DecimalType;
|
import org.hl7.fhir.r4.model.DecimalType;
|
||||||
import org.hl7.fhir.r4.model.InstantType;
|
import org.hl7.fhir.r4.model.InstantType;
|
||||||
import org.hl7.fhir.r4.model.IntegerType;
|
import org.hl7.fhir.r4.model.IntegerType;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
@ -49,20 +51,25 @@ public class RemoteTerminologyLookupCodeDstu3Test implements IRemoteTerminologyL
|
||||||
@RegisterExtension
|
@RegisterExtension
|
||||||
public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx);
|
public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx);
|
||||||
private final RemoteTerminologyServiceValidationSupport mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx);
|
private final RemoteTerminologyServiceValidationSupport mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx);
|
||||||
|
private IValidationProviders.IMyLookupCodeProvider myLookupCodeProvider;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void before() {
|
public void before() {
|
||||||
String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
|
String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
|
||||||
mySvc.setBaseUrl(baseUrl);
|
mySvc.setBaseUrl(baseUrl);
|
||||||
mySvc.addClientInterceptor(new LoggingInterceptor(true));
|
mySvc.addClientInterceptor(new LoggingInterceptor(true));
|
||||||
ourRestfulServerExtension.getRestfulServer().registerProvider(myCodeSystemProvider);
|
myLookupCodeProvider = new MyLookupCodeProviderDstu3();
|
||||||
|
ourRestfulServerExtension.getRestfulServer().registerProvider(myLookupCodeProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final MyCodeSystemProviderDstu3 myCodeSystemProvider = new MyCodeSystemProviderDstu3();
|
@AfterEach
|
||||||
|
public void after() {
|
||||||
|
ourRestfulServerExtension.getRestfulServer().unregisterProvider(myLookupCodeProvider);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IMyCodeSystemProvider getCodeSystemProvider() {
|
public IValidationProviders.IMyLookupCodeProvider getLookupCodeProvider() {
|
||||||
return myCodeSystemProvider;
|
return myLookupCodeProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -83,7 +90,7 @@ public class RemoteTerminologyLookupCodeDstu3Test implements IRemoteTerminologyL
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@MethodSource(value = "getEmptyPropertyValues")
|
@MethodSource(value = "getEmptyPropertyValues")
|
||||||
public void lookupCode_forCodeSystemWithPropertyEmptyValue_returnsCorrectParameters(IBaseDatatype thePropertyValue) {
|
void lookupCode_forCodeSystemWithPropertyEmptyValue_returnsCorrectParameters(IBaseDatatype thePropertyValue) {
|
||||||
verifyLookupWithEmptyPropertyValue(thePropertyValue);
|
verifyLookupWithEmptyPropertyValue(thePropertyValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,20 +129,20 @@ public class RemoteTerminologyLookupCodeDstu3Test implements IRemoteTerminologyL
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@MethodSource(value = "getPropertyValueArguments")
|
@MethodSource(value = "getPropertyValueArguments")
|
||||||
public void lookupCode_forCodeSystemWithProperty_returnsCorrectProperty(IBaseDatatype thePropertyValue) {
|
void lookupCode_forCodeSystemWithProperty_returnsCorrectProperty(IBaseDatatype thePropertyValue) {
|
||||||
verifyLookupWithProperty(List.of(thePropertyValue), List.of());
|
verifyLookupWithProperty(List.of(thePropertyValue), List.of());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@MethodSource(value = "getPropertyValueListArguments")
|
@MethodSource(value = "getPropertyValueListArguments")
|
||||||
public void lookupCode_forCodeSystemWithPropertyFilter_returnsCorrectProperty(List<IBaseDatatype> thePropertyValues) {
|
void lookupCode_forCodeSystemWithPropertyFilter_returnsCorrectProperty(List<IBaseDatatype> thePropertyValues) {
|
||||||
verifyLookupWithProperty(thePropertyValues, List.of());
|
verifyLookupWithProperty(thePropertyValues, List.of());
|
||||||
verifyLookupWithProperty(thePropertyValues, List.of(thePropertyValues.size() - 1));
|
verifyLookupWithProperty(thePropertyValues, List.of(thePropertyValues.size() - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@MethodSource(value = "getPropertyValueListArguments")
|
@MethodSource(value = "getPropertyValueListArguments")
|
||||||
public void lookupCode_forCodeSystemWithPropertyGroup_returnsCorrectProperty(List<IBaseDatatype> thePropertyValues) {
|
void lookupCode_forCodeSystemWithPropertyGroup_returnsCorrectProperty(List<IBaseDatatype> thePropertyValues) {
|
||||||
verifyLookupWithSubProperties(thePropertyValues);
|
verifyLookupWithSubProperties(thePropertyValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,7 +162,8 @@ public class RemoteTerminologyLookupCodeDstu3Test implements IRemoteTerminologyL
|
||||||
verifyLookupWithConceptDesignation(theConceptDesignation);
|
verifyLookupWithConceptDesignation(theConceptDesignation);
|
||||||
}
|
}
|
||||||
|
|
||||||
static class MyCodeSystemProviderDstu3 implements IMyCodeSystemProvider {
|
@SuppressWarnings("unused")
|
||||||
|
static class MyLookupCodeProviderDstu3 implements IValidationProviders.IMyLookupCodeProvider {
|
||||||
private UriType mySystemUrl;
|
private UriType mySystemUrl;
|
||||||
private CodeType myCode;
|
private CodeType myCode;
|
||||||
private LookupCodeResult myLookupCodeResult;
|
private LookupCodeResult myLookupCodeResult;
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
package org.hl7.fhir.dstu3.hapi.validation;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||||
|
import ca.uhn.fhir.context.support.LookupCodeRequest;
|
||||||
|
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
||||||
|
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
||||||
|
import ca.uhn.fhir.util.ClasspathUtil;
|
||||||
|
import org.hl7.fhir.common.hapi.validation.IValidationProviders;
|
||||||
|
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
|
||||||
|
import org.hl7.fhir.dstu3.model.Parameters;
|
||||||
|
import org.hl7.fhir.dstu3.model.StringType;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
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 java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
public class RemoteTerminologyLookupCodeWithResponseFileDstu3Test {
|
||||||
|
private static final FhirContext ourCtx = FhirContext.forDstu3Cached();
|
||||||
|
private IValidateCodeProvidersDstu3.MyCodeSystemProviderDstu3 myCodeSystemProvider;
|
||||||
|
@RegisterExtension
|
||||||
|
public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx);
|
||||||
|
|
||||||
|
private RemoteTerminologyServiceValidationSupport mySvc;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void before() {
|
||||||
|
String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
|
||||||
|
mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx, baseUrl);
|
||||||
|
mySvc.addClientInterceptor(new LoggingInterceptor(false).setLogRequestSummary(true).setLogResponseSummary(true));
|
||||||
|
myCodeSystemProvider = new IValidateCodeProvidersDstu3.MyCodeSystemProviderDstu3();
|
||||||
|
ourRestfulServerExtension.getRestfulServer().registerProviders(myCodeSystemProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
public void after() {
|
||||||
|
ourRestfulServerExtension.getRestfulServer().unregisterProvider(List.of(myCodeSystemProvider));
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
void lookupCode_withParametersOutput_convertsCorrectly() {
|
||||||
|
String paramsAsString = ClasspathUtil.loadResource("/terminology/CodeSystem-lookup-output-with-subproperties.json");
|
||||||
|
IBaseResource baseResource = ourCtx.newJsonParser().parseResource(paramsAsString);
|
||||||
|
assertTrue(baseResource instanceof Parameters);
|
||||||
|
Parameters resultParameters = (Parameters) baseResource;
|
||||||
|
myCodeSystemProvider.setReturnParams(resultParameters);
|
||||||
|
|
||||||
|
LookupCodeRequest request = new LookupCodeRequest(IValidationProviders.CODE_SYSTEM, IValidationProviders.CODE, null, List.of("interfaces"));
|
||||||
|
|
||||||
|
// test
|
||||||
|
IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(null, request);
|
||||||
|
assertNotNull(outcome);
|
||||||
|
|
||||||
|
IBaseParameters theActualParameters = outcome.toParameters(ourCtx, request.getPropertyNames().stream().map(StringType::new).toList());
|
||||||
|
String actual = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(theActualParameters);
|
||||||
|
String expected = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(resultParameters);
|
||||||
|
|
||||||
|
assertEquals(expected, actual);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,294 +0,0 @@
|
||||||
package org.hl7.fhir.dstu3.hapi.validation;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
|
||||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
|
||||||
import ca.uhn.fhir.context.support.LookupCodeRequest;
|
|
||||||
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.api.server.RequestDetails;
|
|
||||||
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
|
|
||||||
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
|
||||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
|
||||||
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
|
||||||
import ca.uhn.fhir.util.ClasspathUtil;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
|
|
||||||
import org.hl7.fhir.dstu3.model.BooleanType;
|
|
||||||
import org.hl7.fhir.dstu3.model.CodeSystem;
|
|
||||||
import org.hl7.fhir.dstu3.model.CodeType;
|
|
||||||
import org.hl7.fhir.dstu3.model.Coding;
|
|
||||||
import org.hl7.fhir.dstu3.model.IdType;
|
|
||||||
import org.hl7.fhir.dstu3.model.Parameters;
|
|
||||||
import org.hl7.fhir.dstu3.model.StringType;
|
|
||||||
import org.hl7.fhir.dstu3.model.Type;
|
|
||||||
import org.hl7.fhir.dstu3.model.UriType;
|
|
||||||
import org.hl7.fhir.dstu3.model.ValueSet;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
|
||||||
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 java.util.List;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Version specific tests for validation using RemoteTerminologyValidationSupport.
|
|
||||||
* The tests in this class simulate the call to a remote server and therefore, only tests the code in
|
|
||||||
* the RemoteTerminologyServiceValidationSupport itself. The remote client call is simulated using the test providers.
|
|
||||||
* @see RemoteTerminologyServiceValidationSupport
|
|
||||||
*
|
|
||||||
* Other operations are tested separately.
|
|
||||||
* @see RemoteTerminologyLookupCodeDstu3Test
|
|
||||||
*/
|
|
||||||
public class RemoteTerminologyServiceResourceProviderDstu3Test {
|
|
||||||
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 final FhirContext ourCtx = FhirContext.forDstu3Cached();
|
|
||||||
private static final MyCodeSystemProvider ourCodeSystemProvider = new MyCodeSystemProvider();
|
|
||||||
private static final MyValueSetProvider ourValueSetProvider = new MyValueSetProvider();
|
|
||||||
|
|
||||||
@RegisterExtension
|
|
||||||
public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx, ourCodeSystemProvider,
|
|
||||||
ourValueSetProvider);
|
|
||||||
|
|
||||||
private RemoteTerminologyServiceValidationSupport mySvc;
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
public void before_ConfigureService() {
|
|
||||||
String myBaseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
|
|
||||||
mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx, myBaseUrl);
|
|
||||||
mySvc.addClientInterceptor(new LoggingInterceptor(false).setLogRequestSummary(true).setLogResponseSummary(true));
|
|
||||||
}
|
|
||||||
|
|
||||||
@AfterEach
|
|
||||||
public void after_UnregisterProviders() {
|
|
||||||
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE);
|
|
||||||
ourRestfulServerExtension.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);
|
|
||||||
assertNotNull(outcome);
|
|
||||||
assertEquals(CODE, outcome.getCode());
|
|
||||||
assertNull(outcome.getSeverity());
|
|
||||||
assertNull(outcome.getMessage());
|
|
||||||
|
|
||||||
assertEquals(CODE, ourCodeSystemProvider.myLastCode.getValue());
|
|
||||||
assertEquals(CODE_SYSTEM, ourCodeSystemProvider.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);
|
|
||||||
assertNotNull(outcome);
|
|
||||||
assertEquals(CODE, outcome.getCode());
|
|
||||||
assertEquals(DISPLAY, outcome.getDisplay());
|
|
||||||
assertNull(outcome.getSeverity());
|
|
||||||
assertNull(outcome.getMessage());
|
|
||||||
|
|
||||||
assertEquals(CODE, ourCodeSystemProvider.myLastCode.getValue());
|
|
||||||
assertEquals(DISPLAY, ourCodeSystemProvider.myLastDisplay.getValue());
|
|
||||||
assertEquals(CODE_SYSTEM, ourCodeSystemProvider.myLastUrl.getValueAsString());
|
|
||||||
assertEquals(SAMPLE_MESSAGE, getParameterValue(ourCodeSystemProvider.myNextReturnParams, "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);
|
|
||||||
assertNotNull(outcome);
|
|
||||||
assertEquals(IValidationSupport.IssueSeverity.ERROR, outcome.getSeverity());
|
|
||||||
assertEquals(SAMPLE_MESSAGE, outcome.getMessage());
|
|
||||||
|
|
||||||
assertFalse(((BooleanType) getParameterValue(ourCodeSystemProvider.myNextReturnParams, "result")).booleanValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testValidateCodeInValueSet_ProvidingMinimalInputs_ReturnsSuccess() {
|
|
||||||
ourValueSetProvider.myNextReturnParams = new Parameters();
|
|
||||||
ourValueSetProvider.myNextReturnParams.addParameter().setName("result").setValue(new BooleanType(true));
|
|
||||||
|
|
||||||
IValidationSupport.CodeValidationResult outcome = mySvc
|
|
||||||
.validateCode(null, null, CODE_SYSTEM, CODE, null, VALUE_SET_URL);
|
|
||||||
assertNotNull(outcome);
|
|
||||||
assertEquals(CODE, outcome.getCode());
|
|
||||||
assertNull(outcome.getSeverity());
|
|
||||||
assertNull(outcome.getMessage());
|
|
||||||
|
|
||||||
assertEquals(CODE, ourValueSetProvider.myLastCode.getValue());
|
|
||||||
assertEquals(VALUE_SET_URL, ourValueSetProvider.myLastUrl.getValueAsString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testValidateCodeInValueSet_WithMessageValue_ReturnsMessage() {
|
|
||||||
ourValueSetProvider.myNextReturnParams = new Parameters();
|
|
||||||
ourValueSetProvider.myNextReturnParams.addParameter().setName("result").setValue(new BooleanType(true));
|
|
||||||
ourValueSetProvider.myNextReturnParams.addParameter().setName("display").setValue(new StringType(DISPLAY));
|
|
||||||
ourValueSetProvider.myNextReturnParams.addParameter().setName("message").setValue(new StringType(SAMPLE_MESSAGE));
|
|
||||||
|
|
||||||
IValidationSupport.CodeValidationResult outcome = mySvc
|
|
||||||
.validateCode(null, null, CODE_SYSTEM, CODE, DISPLAY, VALUE_SET_URL);
|
|
||||||
assertNotNull(outcome);
|
|
||||||
assertEquals(CODE, outcome.getCode());
|
|
||||||
assertEquals(DISPLAY, outcome.getDisplay());
|
|
||||||
assertNull(outcome.getSeverity());
|
|
||||||
assertNull(outcome.getMessage());
|
|
||||||
|
|
||||||
assertEquals(CODE, ourValueSetProvider.myLastCode.getValue());
|
|
||||||
assertEquals(DISPLAY, ourValueSetProvider.myLastDisplay.getValue());
|
|
||||||
assertEquals(VALUE_SET_URL, ourValueSetProvider.myLastUrl.getValueAsString());
|
|
||||||
assertEquals(SAMPLE_MESSAGE, getParameterValue(ourValueSetProvider.myNextReturnParams, "message").toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void lookupCode_withParametersOutput_convertsCorrectly() {
|
|
||||||
String paramsAsString = ClasspathUtil.loadResource("/r4/CodeSystem-lookup-output-with-subproperties.json");
|
|
||||||
IBaseResource baseResource = ourCtx.newJsonParser().parseResource(paramsAsString);
|
|
||||||
assertTrue(baseResource instanceof Parameters);
|
|
||||||
Parameters resultParameters = (Parameters) baseResource;
|
|
||||||
ourCodeSystemProvider.myNextReturnParams = resultParameters;
|
|
||||||
|
|
||||||
LookupCodeRequest request = new LookupCodeRequest(CODE_SYSTEM, CODE, null, List.of("interfaces"));
|
|
||||||
|
|
||||||
// test
|
|
||||||
IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(null, request);
|
|
||||||
assertNotNull(outcome);
|
|
||||||
|
|
||||||
IBaseParameters theActualParameters = outcome.toParameters(ourCtx, request.getPropertyNames().stream().map(StringType::new).toList());
|
|
||||||
String actual = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(theActualParameters);
|
|
||||||
String expected = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(resultParameters);
|
|
||||||
|
|
||||||
assertEquals(expected, actual);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createNextCodeSystemReturnParameters(boolean theResult, String theDisplay, String theMessage) {
|
|
||||||
ourCodeSystemProvider.myNextReturnParams = new Parameters();
|
|
||||||
ourCodeSystemProvider.myNextReturnParams.addParameter().setName("result").setValue(new BooleanType(theResult));
|
|
||||||
ourCodeSystemProvider.myNextReturnParams.addParameter().setName("display").setValue(new StringType(theDisplay));
|
|
||||||
if (theMessage != null) {
|
|
||||||
ourCodeSystemProvider.myNextReturnParams.addParameter().setName("message").setValue(new StringType(theMessage));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Type getParameterValue(Parameters theParameters, String theParameterName) {
|
|
||||||
Optional<Parameters.ParametersParameterComponent> paramOpt = theParameters.getParameter()
|
|
||||||
.stream().filter(param -> param.getName().equals(theParameterName)).findFirst();
|
|
||||||
assertTrue(paramOpt.isPresent());
|
|
||||||
return paramOpt.get().getValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class MyCodeSystemProvider implements IResourceProvider {
|
|
||||||
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
|
|
||||||
) {
|
|
||||||
myLastUrl = theCodeSystemUrl;
|
|
||||||
myLastCode = theCode;
|
|
||||||
myLastDisplay = theDisplay;
|
|
||||||
return myNextReturnParams;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters= {
|
|
||||||
@OperationParam(name = "name", type = StringType.class, min = 1),
|
|
||||||
@OperationParam(name = "version", type = StringType.class),
|
|
||||||
@OperationParam(name = "display", type = StringType.class, min = 1),
|
|
||||||
@OperationParam(name = "abstract", type = BooleanType.class, min = 1),
|
|
||||||
@OperationParam(name = "property", type = StringType.class, min = 0, max = OperationParam.MAX_UNLIMITED)
|
|
||||||
})
|
|
||||||
public IBaseParameters lookup(
|
|
||||||
HttpServletRequest theServletRequest,
|
|
||||||
@OperationParam(name = "code", max = 1) CodeType theCode,
|
|
||||||
@OperationParam(name = "system",max = 1) UriType theSystem,
|
|
||||||
@OperationParam(name = "coding", max = 1) Coding theCoding,
|
|
||||||
@OperationParam(name = "version", max = 1) StringType theVersion,
|
|
||||||
@OperationParam(name = "displayLanguage", max = 1) CodeType theDisplayLanguage,
|
|
||||||
@OperationParam(name = "property", max = OperationParam.MAX_UNLIMITED) List<CodeType> thePropertyNames,
|
|
||||||
RequestDetails theRequestDetails
|
|
||||||
) {
|
|
||||||
myLastCode = theCode;
|
|
||||||
return myNextReturnParams;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class<? extends IBaseResource> getResourceType() {
|
|
||||||
return CodeSystem.class;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static class MyValueSetProvider implements IResourceProvider {
|
|
||||||
private Parameters myNextReturnParams;
|
|
||||||
private UriType myLastUrl;
|
|
||||||
private CodeType myLastCode;
|
|
||||||
private StringType myLastDisplay;
|
|
||||||
|
|
||||||
@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
|
|
||||||
) {
|
|
||||||
myLastUrl = theValueSetUrl;
|
|
||||||
myLastCode = theCode;
|
|
||||||
myLastDisplay = theDisplay;
|
|
||||||
return myNextReturnParams;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class<? extends IBaseResource> getResourceType() {
|
|
||||||
return ValueSet.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,126 @@
|
||||||
|
package org.hl7.fhir.dstu3.hapi.validation;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
|
||||||
|
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
||||||
|
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
||||||
|
import ca.uhn.fhir.util.ClasspathUtil;
|
||||||
|
import org.hl7.fhir.common.hapi.validation.IRemoteTerminologyValidateCodeTest;
|
||||||
|
import org.hl7.fhir.common.hapi.validation.IValidationProviders;
|
||||||
|
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
|
||||||
|
import org.hl7.fhir.dstu3.model.BooleanType;
|
||||||
|
import org.hl7.fhir.dstu3.model.OperationOutcome;
|
||||||
|
import org.hl7.fhir.dstu3.model.Parameters;
|
||||||
|
import org.hl7.fhir.dstu3.model.Resource;
|
||||||
|
import org.hl7.fhir.dstu3.model.StringType;
|
||||||
|
import org.hl7.fhir.dstu3.model.UriType;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport.ERROR_CODE_UNKNOWN_CODE_IN_CODE_SYSTEM;
|
||||||
|
import static org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport.ERROR_CODE_UNKNOWN_CODE_IN_VALUE_SET;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Version specific tests for validation using RemoteTerminologyValidationSupport.
|
||||||
|
* The tests in this class simulate the call to a remote server and therefore, only tests the code in
|
||||||
|
* the RemoteTerminologyServiceValidationSupport itself. The remote client call is simulated using the test providers.
|
||||||
|
* @see RemoteTerminologyServiceValidationSupport
|
||||||
|
*
|
||||||
|
* Other operations are tested separately.
|
||||||
|
* @see RemoteTerminologyLookupCodeDstu3Test
|
||||||
|
*/
|
||||||
|
public class RemoteTerminologyValidateCodeDstu3Test implements IRemoteTerminologyValidateCodeTest {
|
||||||
|
private static final FhirContext ourCtx = FhirContext.forDstu3Cached();
|
||||||
|
@RegisterExtension
|
||||||
|
public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx);
|
||||||
|
private IValidateCodeProvidersDstu3.MyCodeSystemProviderDstu3 myCodeSystemProvider;
|
||||||
|
private IValidateCodeProvidersDstu3.MyValueSetProviderDstu3 myValueSetProvider;
|
||||||
|
private RemoteTerminologyServiceValidationSupport mySvc;
|
||||||
|
private String myCodeSystemError, myValueSetError;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void before() {
|
||||||
|
String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
|
||||||
|
myCodeSystemError = ourCtx.getLocalizer().getMessage(
|
||||||
|
RemoteTerminologyServiceValidationSupport.class,
|
||||||
|
ERROR_CODE_UNKNOWN_CODE_IN_CODE_SYSTEM, IValidationProviders.CODE_SYSTEM, IValidationProviders.CODE, baseUrl, IValidationProviders.ERROR_MESSAGE);
|
||||||
|
myValueSetError = ourCtx.getLocalizer().getMessage(
|
||||||
|
RemoteTerminologyServiceValidationSupport.class,
|
||||||
|
ERROR_CODE_UNKNOWN_CODE_IN_VALUE_SET, IValidationProviders.CODE_SYSTEM, IValidationProviders.CODE, IValidationProviders.VALUE_SET_URL, baseUrl, IValidationProviders.ERROR_MESSAGE);
|
||||||
|
mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx, baseUrl);
|
||||||
|
mySvc.addClientInterceptor(new LoggingInterceptor(false).setLogRequestSummary(true).setLogResponseSummary(true));
|
||||||
|
myCodeSystemProvider = new IValidateCodeProvidersDstu3.MyCodeSystemProviderDstu3();
|
||||||
|
myValueSetProvider = new IValidateCodeProvidersDstu3.MyValueSetProviderDstu3();
|
||||||
|
ourRestfulServerExtension.getRestfulServer().registerProviders(myCodeSystemProvider, myValueSetProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
public void after() {
|
||||||
|
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE);
|
||||||
|
ourRestfulServerExtension.getRestfulServer().getInterceptorService().unregisterAllInterceptors();
|
||||||
|
ourRestfulServerExtension.getRestfulServer().unregisterProviders(List.of(myCodeSystemProvider, myValueSetProvider));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RemoteTerminologyServiceValidationSupport getService() {
|
||||||
|
return mySvc;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCodeSystemError() {
|
||||||
|
return myCodeSystemError;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getValueSetError() {
|
||||||
|
return myValueSetError;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IValidateCodeProvidersDstu3.MyCodeSystemProviderDstu3 getCodeSystemProvider() {
|
||||||
|
return myCodeSystemProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IValidateCodeProvidersDstu3.MyValueSetProviderDstu3 getValueSetProvider() {
|
||||||
|
return myValueSetProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBaseOperationOutcome getCodeSystemInvalidCodeOutcome() {
|
||||||
|
return ClasspathUtil.loadResource(getService().getFhirContext(), OperationOutcome.class, "/terminology/OperationOutcome-CodeSystem-invalid-code.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBaseOperationOutcome getValueSetInvalidCodeOutcome() {
|
||||||
|
return ClasspathUtil.loadResource(getService().getFhirContext(), OperationOutcome.class, "/terminology/OperationOutcome-ValueSet-invalid-code.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Parameters createParameters(Boolean theResult, String theDisplay, String theMessage, IBaseResource theIssuesResource) {
|
||||||
|
Parameters parameters = new Parameters();
|
||||||
|
parameters.addParameter().setName("result").setValue(new BooleanType(theResult));
|
||||||
|
parameters.addParameter().setName("code").setValue(new StringType(IValidationProviders.CODE));
|
||||||
|
parameters.addParameter().setName("system").setValue(new UriType(IValidationProviders.CODE_SYSTEM));
|
||||||
|
parameters.addParameter().setName("version").setValue(new StringType(IValidationProviders.CODE_SYSTEM_VERSION));
|
||||||
|
parameters.addParameter().setName("display").setValue(new StringType(theDisplay));
|
||||||
|
parameters.addParameter().setName("message").setValue(new StringType(theMessage));
|
||||||
|
parameters.addParameter().setName("issues").setResource((Resource) theIssuesResource);
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createCodeSystemReturnParameters(Boolean theResult, String theDisplay, String theMessage, IBaseResource theIssuesResource) {
|
||||||
|
myCodeSystemProvider.setReturnParams(createParameters(theResult, theDisplay, theMessage, theIssuesResource));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createValueSetReturnParameters(Boolean theResult, String theDisplay, String theMessage, IBaseResource theIssuesResource) {
|
||||||
|
myValueSetProvider.setReturnParams(createParameters(theResult, theDisplay, theMessage, theIssuesResource));
|
||||||
|
}
|
||||||
|
}
|
|
@ -33,6 +33,7 @@ import org.hl7.fhir.exceptions.FHIRException;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.r4.conformance.ProfileUtilities;
|
import org.hl7.fhir.r4.conformance.ProfileUtilities;
|
||||||
import org.hl7.fhir.r4.context.IWorkerContext;
|
import org.hl7.fhir.r4.context.IWorkerContext;
|
||||||
|
import org.hl7.fhir.r4.fhirpath.FHIRPathEngine;
|
||||||
import org.hl7.fhir.r4.hapi.ctx.HapiWorkerContext;
|
import org.hl7.fhir.r4.hapi.ctx.HapiWorkerContext;
|
||||||
import org.hl7.fhir.r4.model.AllergyIntolerance;
|
import org.hl7.fhir.r4.model.AllergyIntolerance;
|
||||||
import org.hl7.fhir.r4.model.Base;
|
import org.hl7.fhir.r4.model.Base;
|
||||||
|
@ -68,7 +69,6 @@ import org.hl7.fhir.r4.model.StructureDefinition.StructureDefinitionKind;
|
||||||
import org.hl7.fhir.r4.model.ValueSet;
|
import org.hl7.fhir.r4.model.ValueSet;
|
||||||
import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionComponent;
|
import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionComponent;
|
||||||
import org.hl7.fhir.r4.terminologies.ValueSetExpander;
|
import org.hl7.fhir.r4.terminologies.ValueSetExpander;
|
||||||
import org.hl7.fhir.r4.fhirpath.FHIRPathEngine;
|
|
||||||
import org.hl7.fhir.r5.elementmodel.JsonParser;
|
import org.hl7.fhir.r5.elementmodel.JsonParser;
|
||||||
import org.hl7.fhir.r5.test.utils.ClassesLoadedFlags;
|
import org.hl7.fhir.r5.test.utils.ClassesLoadedFlags;
|
||||||
import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor;
|
import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor;
|
||||||
|
@ -83,6 +83,8 @@ import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Disabled;
|
import org.junit.jupiter.api.Disabled;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
import org.mockito.invocation.InvocationOnMock;
|
import org.mockito.invocation.InvocationOnMock;
|
||||||
import org.mockito.stubbing.Answer;
|
import org.mockito.stubbing.Answer;
|
||||||
|
|
||||||
|
@ -128,13 +130,22 @@ public class FhirInstanceValidatorR4Test extends BaseValidationTestWithInlineMoc
|
||||||
private FhirValidator myFhirValidator;
|
private FhirValidator myFhirValidator;
|
||||||
private ArrayList<String> myValidConcepts;
|
private ArrayList<String> myValidConcepts;
|
||||||
private Set<String> myValidSystems = new HashSet<>();
|
private Set<String> myValidSystems = new HashSet<>();
|
||||||
|
private Set<String> myValidSystemsNotReturningIssues = new HashSet<>();
|
||||||
private Set<String> myValidValueSets = new HashSet<>();
|
private Set<String> myValidValueSets = new HashSet<>();
|
||||||
private Map<String, StructureDefinition> myStructureDefinitionMap = new HashMap<>();
|
private Map<String, StructureDefinition> myStructureDefinitionMap = new HashMap<>();
|
||||||
private CachingValidationSupport myValidationSupport;
|
private CachingValidationSupport myValidationSupport;
|
||||||
private IValidationSupport myMockSupport;
|
private IValidationSupport myMockSupport;
|
||||||
|
|
||||||
private void addValidConcept(String theSystem, String theCode) {
|
private void addValidConcept(String theSystem, String theCode) {
|
||||||
myValidSystems.add(theSystem);
|
addValidConcept(theSystem, theCode, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addValidConcept(String theSystem, String theCode, boolean theShouldSystemReturnIssuesForInvalidCode) {
|
||||||
|
if (theShouldSystemReturnIssuesForInvalidCode) {
|
||||||
|
myValidSystems.add(theSystem);
|
||||||
|
} else {
|
||||||
|
myValidSystemsNotReturningIssues.add(theSystem);
|
||||||
|
}
|
||||||
myValidConcepts.add(theSystem + "___" + theCode);
|
myValidConcepts.add(theSystem + "___" + theCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -296,7 +307,10 @@ public class FhirInstanceValidatorR4Test extends BaseValidationTestWithInlineMoc
|
||||||
retVal = new IValidationSupport.CodeValidationResult().setCode(code);
|
retVal = new IValidationSupport.CodeValidationResult().setCode(code);
|
||||||
} else if (myValidSystems.contains(system)) {
|
} else if (myValidSystems.contains(system)) {
|
||||||
final String message = "Unknown code (for '" + system + "#" + code + "')";
|
final String message = "Unknown code (for '" + system + "#" + code + "')";
|
||||||
return new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.ERROR.toCode()).setMessage(message).setCodeValidationIssues(Collections.singletonList(new IValidationSupport.CodeValidationIssue(message, IValidationSupport.IssueSeverity.ERROR, IValidationSupport.CodeValidationIssueCode.CODE_INVALID, IValidationSupport.CodeValidationIssueCoding.INVALID_CODE)));
|
retVal = new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.ERROR.toCode()).setMessage(message).setCodeValidationIssues(Collections.singletonList(new IValidationSupport.CodeValidationIssue(message, IValidationSupport.IssueSeverity.ERROR, IValidationSupport.CodeValidationIssueCode.CODE_INVALID, IValidationSupport.CodeValidationIssueCoding.INVALID_CODE)));
|
||||||
|
} else if (myValidSystemsNotReturningIssues.contains(system)) {
|
||||||
|
final String message = "Unknown code (for '" + system + "#" + code + "')";
|
||||||
|
retVal = new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.ERROR.toCode()).setMessage(message);
|
||||||
} else {
|
} else {
|
||||||
retVal = myDefaultValidationSupport.validateCode(new ValidationSupportContext(myDefaultValidationSupport), options, system, code, display, valueSetUrl);
|
retVal = myDefaultValidationSupport.validateCode(new ValidationSupportContext(myDefaultValidationSupport), options, system, code, display, valueSetUrl);
|
||||||
}
|
}
|
||||||
|
@ -1226,14 +1240,15 @@ public class FhirInstanceValidatorR4Test extends BaseValidationTestWithInlineMoc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
// TODO: uncomment value false when https://github.com/hapifhir/org.hl7.fhir.core/issues/1766 is fixed
|
||||||
public void testValidateResourceContainingLoincCode() {
|
@ParameterizedTest
|
||||||
addValidConcept("http://loinc.org", "1234567");
|
@ValueSource(booleans = {true, /*false*/})
|
||||||
|
public void testValidateResourceContainingLoincCode(boolean theShouldSystemReturnIssuesForInvalidCode) {
|
||||||
|
addValidConcept("http://loinc.org", "1234567", theShouldSystemReturnIssuesForInvalidCode);
|
||||||
|
|
||||||
Observation input = createObservationWithDefaultSubjectPerfomerEffective();
|
Observation input = createObservationWithDefaultSubjectPerfomerEffective();
|
||||||
input.getText().setDiv(new XhtmlNode().setValue("<div>AA</div>")).setStatus(Narrative.NarrativeStatus.GENERATED);
|
input.getText().setDiv(new XhtmlNode().setValue("<div>AA</div>")).setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||||
|
|
||||||
|
|
||||||
input.addIdentifier().setSystem("http://acme").setValue("12345");
|
input.addIdentifier().setSystem("http://acme").setValue("12345");
|
||||||
input.getEncounter().setReference("http://foo.com/Encounter/9");
|
input.getEncounter().setReference("http://foo.com/Encounter/9");
|
||||||
input.setStatus(ObservationStatus.FINAL);
|
input.setStatus(ObservationStatus.FINAL);
|
||||||
|
|
|
@ -0,0 +1,165 @@
|
||||||
|
package org.hl7.fhir.r4.validation;
|
||||||
|
|
||||||
|
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.api.server.RequestDetails;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import org.hl7.fhir.common.hapi.validation.IValidationProviders;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||||
|
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 java.util.List;
|
||||||
|
|
||||||
|
public interface IValidateCodeProvidersR4 {
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
class MyCodeSystemProviderR4 implements IValidationProviders.IMyCodeSystemProvider {
|
||||||
|
private UriType mySystemUrl;
|
||||||
|
private CodeType myCode;
|
||||||
|
private StringType myDisplay;
|
||||||
|
private Exception myException;
|
||||||
|
private Parameters myReturnParams;
|
||||||
|
|
||||||
|
@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
|
||||||
|
) throws Exception {
|
||||||
|
mySystemUrl = theCodeSystemUrl;
|
||||||
|
myCode = theCode;
|
||||||
|
myDisplay = theDisplay;
|
||||||
|
if (myException != null) {
|
||||||
|
throw myException;
|
||||||
|
}
|
||||||
|
return myReturnParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters= {
|
||||||
|
@OperationParam(name = "name", type = StringType.class, min = 1),
|
||||||
|
@OperationParam(name = "version", type = StringType.class),
|
||||||
|
@OperationParam(name = "display", type = StringType.class, min = 1),
|
||||||
|
@OperationParam(name = "abstract", type = BooleanType.class, min = 1),
|
||||||
|
@OperationParam(name = "property", type = StringType.class, min = 0, max = OperationParam.MAX_UNLIMITED)
|
||||||
|
})
|
||||||
|
public IBaseParameters lookup(
|
||||||
|
HttpServletRequest theServletRequest,
|
||||||
|
@OperationParam(name = "code", max = 1) CodeType theCode,
|
||||||
|
@OperationParam(name = "system",max = 1) UriType theSystem,
|
||||||
|
@OperationParam(name = "coding", max = 1) Coding theCoding,
|
||||||
|
@OperationParam(name = "version", max = 1) StringType ignoredTheVersion,
|
||||||
|
@OperationParam(name = "displayLanguage", max = 1) CodeType theDisplayLanguage,
|
||||||
|
@OperationParam(name = "property", max = OperationParam.MAX_UNLIMITED) List<CodeType> thePropertyNames,
|
||||||
|
RequestDetails theRequestDetails
|
||||||
|
) throws Exception {
|
||||||
|
mySystemUrl = theSystem;
|
||||||
|
myCode = theCode;
|
||||||
|
if (myException != null) {
|
||||||
|
throw myException;
|
||||||
|
}
|
||||||
|
return myReturnParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends IBaseResource> getResourceType() {
|
||||||
|
return CodeSystem.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setException(Exception theException) {
|
||||||
|
myException = theException;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void setReturnParams(IBaseParameters theParameters) {
|
||||||
|
myReturnParams = (Parameters) theParameters;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public String getCode() {
|
||||||
|
return myCode != null ? myCode.getValueAsString() : null;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public String getSystem() {
|
||||||
|
return mySystemUrl != null ? mySystemUrl.getValueAsString() : null;
|
||||||
|
}
|
||||||
|
public String getDisplay() {
|
||||||
|
return myDisplay != null ? myDisplay.getValue() : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
class MyValueSetProviderR4 implements IValidationProviders.IMyValueSetProvider {
|
||||||
|
private Exception myException;
|
||||||
|
private Parameters myReturnParams;
|
||||||
|
private UriType mySystemUrl;
|
||||||
|
private UriType myValueSetUrl;
|
||||||
|
private CodeType myCode;
|
||||||
|
private StringType myDisplay;
|
||||||
|
|
||||||
|
@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
|
||||||
|
) throws Exception {
|
||||||
|
mySystemUrl = theSystem;
|
||||||
|
myValueSetUrl = theValueSetUrl;
|
||||||
|
myCode = theCode;
|
||||||
|
myDisplay = theDisplay;
|
||||||
|
if (myException != null) {
|
||||||
|
throw myException;
|
||||||
|
}
|
||||||
|
return myReturnParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends IBaseResource> getResourceType() {
|
||||||
|
return ValueSet.class;
|
||||||
|
}
|
||||||
|
public void setException(Exception theException) {
|
||||||
|
myException = theException;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void setReturnParams(IBaseParameters theParameters) {
|
||||||
|
myReturnParams = (Parameters) theParameters;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public String getCode() {
|
||||||
|
return myCode != null ? myCode.getValueAsString() : null;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public String getSystem() {
|
||||||
|
return mySystemUrl != null ? mySystemUrl.getValueAsString() : null;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public String getValueSet() {
|
||||||
|
return myValueSetUrl != null ? myValueSetUrl.getValueAsString() : null;
|
||||||
|
}
|
||||||
|
public String getDisplay() {
|
||||||
|
return myDisplay != null ? myDisplay.getValue() : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ package org.hl7.fhir.r4.validation;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||||
|
import ca.uhn.fhir.context.support.IValidationSupport.LookupCodeResult;
|
||||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||||
import ca.uhn.fhir.rest.annotation.Operation;
|
import ca.uhn.fhir.rest.annotation.Operation;
|
||||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||||
|
@ -12,6 +13,7 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||||
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import org.hl7.fhir.common.hapi.validation.IRemoteTerminologyLookupCodeTest;
|
import org.hl7.fhir.common.hapi.validation.IRemoteTerminologyLookupCodeTest;
|
||||||
|
import org.hl7.fhir.common.hapi.validation.IValidationProviders;
|
||||||
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
|
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseDatatype;
|
import org.hl7.fhir.instance.model.api.IBaseDatatype;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||||
|
@ -27,6 +29,7 @@ import org.hl7.fhir.r4.model.IntegerType;
|
||||||
import org.hl7.fhir.r4.model.StringType;
|
import org.hl7.fhir.r4.model.StringType;
|
||||||
import org.hl7.fhir.r4.model.Type;
|
import org.hl7.fhir.r4.model.Type;
|
||||||
import org.hl7.fhir.r4.model.UriType;
|
import org.hl7.fhir.r4.model.UriType;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
@ -39,7 +42,6 @@ import java.util.List;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static ca.uhn.fhir.context.support.IValidationSupport.ConceptDesignation;
|
import static ca.uhn.fhir.context.support.IValidationSupport.ConceptDesignation;
|
||||||
import static ca.uhn.fhir.context.support.IValidationSupport.LookupCodeResult;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Version specific tests for CodeSystem $lookup against RemoteTerminologyValidationSupport.
|
* Version specific tests for CodeSystem $lookup against RemoteTerminologyValidationSupport.
|
||||||
|
@ -50,19 +52,27 @@ public class RemoteTerminologyLookupCodeR4Test implements IRemoteTerminologyLook
|
||||||
@RegisterExtension
|
@RegisterExtension
|
||||||
public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx);
|
public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx);
|
||||||
private final RemoteTerminologyServiceValidationSupport mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx);
|
private final RemoteTerminologyServiceValidationSupport mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx);
|
||||||
private final MyCodeSystemProviderR4 myCodeSystemProvider = new MyCodeSystemProviderR4();
|
private IValidateCodeProvidersR4.MyCodeSystemProviderR4 myCodeSystemProvider;
|
||||||
|
private MyLookupCodeProviderR4 myLookupCodeProviderR4;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void before() {
|
public void before() {
|
||||||
String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
|
String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
|
||||||
mySvc.setBaseUrl(baseUrl);
|
mySvc.setBaseUrl(baseUrl);
|
||||||
mySvc.addClientInterceptor(new LoggingInterceptor(true));
|
mySvc.addClientInterceptor(new LoggingInterceptor(true));
|
||||||
ourRestfulServerExtension.getRestfulServer().registerProvider(myCodeSystemProvider);
|
myCodeSystemProvider = new IValidateCodeProvidersR4.MyCodeSystemProviderR4();
|
||||||
|
myLookupCodeProviderR4 = new MyLookupCodeProviderR4();
|
||||||
|
ourRestfulServerExtension.getRestfulServer().registerProviders(myCodeSystemProvider, myLookupCodeProviderR4);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
public void after() {
|
||||||
|
ourRestfulServerExtension.getRestfulServer().unregisterProvider(List.of(myCodeSystemProvider, myLookupCodeProviderR4));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IMyCodeSystemProvider getCodeSystemProvider() {
|
public IValidationProviders.IMyLookupCodeProvider getLookupCodeProvider() {
|
||||||
return myCodeSystemProvider;
|
return myLookupCodeProviderR4;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -87,7 +97,6 @@ public class RemoteTerminologyLookupCodeR4Test implements IRemoteTerminologyLook
|
||||||
verifyLookupWithEmptyPropertyValue(thePropertyValue);
|
verifyLookupWithEmptyPropertyValue(thePropertyValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static Stream<Arguments> getPropertyValueArguments() {
|
public static Stream<Arguments> getPropertyValueArguments() {
|
||||||
return Stream.of(
|
return Stream.of(
|
||||||
// FHIR R4 spec types
|
// FHIR R4 spec types
|
||||||
|
@ -155,7 +164,8 @@ public class RemoteTerminologyLookupCodeR4Test implements IRemoteTerminologyLook
|
||||||
verifyLookupWithConceptDesignation(theConceptDesignation);
|
verifyLookupWithConceptDesignation(theConceptDesignation);
|
||||||
}
|
}
|
||||||
|
|
||||||
static class MyCodeSystemProviderR4 implements IMyCodeSystemProvider {
|
@SuppressWarnings("unused")
|
||||||
|
static class MyLookupCodeProviderR4 implements IValidationProviders.IMyLookupCodeProvider {
|
||||||
private UriType mySystemUrl;
|
private UriType mySystemUrl;
|
||||||
private CodeType myCode;
|
private CodeType myCode;
|
||||||
private LookupCodeResult myLookupCodeResult;
|
private LookupCodeResult myLookupCodeResult;
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
package org.hl7.fhir.r4.validation;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||||
|
import ca.uhn.fhir.context.support.LookupCodeRequest;
|
||||||
|
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
||||||
|
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
||||||
|
import ca.uhn.fhir.util.ClasspathUtil;
|
||||||
|
import org.hl7.fhir.common.hapi.validation.IValidationProviders;
|
||||||
|
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
import org.hl7.fhir.r4.model.Parameters;
|
||||||
|
import org.hl7.fhir.r4.model.StringType;
|
||||||
|
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 java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
public class RemoteTerminologyLookupCodeWithResponseFileR4Test {
|
||||||
|
private static final FhirContext ourCtx = FhirContext.forR4Cached();
|
||||||
|
private IValidateCodeProvidersR4.MyCodeSystemProviderR4 myCodeSystemProvider;
|
||||||
|
@RegisterExtension
|
||||||
|
public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx);
|
||||||
|
|
||||||
|
private RemoteTerminologyServiceValidationSupport mySvc;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void before() {
|
||||||
|
String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
|
||||||
|
mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx, baseUrl);
|
||||||
|
mySvc.addClientInterceptor(new LoggingInterceptor(false).setLogRequestSummary(true).setLogResponseSummary(true));
|
||||||
|
myCodeSystemProvider = new IValidateCodeProvidersR4.MyCodeSystemProviderR4();
|
||||||
|
ourRestfulServerExtension.getRestfulServer().registerProviders(myCodeSystemProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
public void after() {
|
||||||
|
ourRestfulServerExtension.getRestfulServer().unregisterProvider(List.of(myCodeSystemProvider));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void lookupCode_withParametersOutput_convertsCorrectly() {
|
||||||
|
String paramsAsString = ClasspathUtil.loadResource("/terminology/CodeSystem-lookup-output-with-subproperties.json");
|
||||||
|
IBaseResource baseResource = ourCtx.newJsonParser().parseResource(paramsAsString);
|
||||||
|
assertTrue(baseResource instanceof Parameters);
|
||||||
|
Parameters resultParameters = (Parameters) baseResource;
|
||||||
|
myCodeSystemProvider.setReturnParams(resultParameters);
|
||||||
|
|
||||||
|
LookupCodeRequest request = new LookupCodeRequest(IValidationProviders.CODE_SYSTEM, IValidationProviders.CODE, null, List.of("interfaces"));
|
||||||
|
|
||||||
|
// test
|
||||||
|
IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(null, request);
|
||||||
|
assertNotNull(outcome);
|
||||||
|
|
||||||
|
IBaseParameters theActualParameters = outcome.toParameters(ourCtx, request.getPropertyNames().stream().map(StringType::new).toList());
|
||||||
|
String actual = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(theActualParameters);
|
||||||
|
String expected = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(resultParameters);
|
||||||
|
|
||||||
|
assertEquals(expected, actual);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,284 +0,0 @@
|
||||||
package org.hl7.fhir.r4.validation;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
|
||||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
|
||||||
import ca.uhn.fhir.context.support.LookupCodeRequest;
|
|
||||||
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.api.server.RequestDetails;
|
|
||||||
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
|
|
||||||
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
|
||||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
|
||||||
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
|
||||||
import ca.uhn.fhir.util.ClasspathUtil;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
|
||||||
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
|
||||||
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 java.util.List;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Version specific tests for validation using RemoteTerminologyValidationSupport.
|
|
||||||
* The tests in this class simulate the call to a remote server and therefore, only tests the code in
|
|
||||||
* the RemoteTerminologyServiceValidationSupport itself. The remote client call is simulated using the test providers.
|
|
||||||
* @see RemoteTerminologyServiceValidationSupport
|
|
||||||
*
|
|
||||||
* Other operations are tested separately.
|
|
||||||
* @see RemoteTerminologyLookupCodeR4Test
|
|
||||||
* @see RemoteTerminologyServiceValidationSupportR4Test
|
|
||||||
*/
|
|
||||||
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 final FhirContext ourCtx = FhirContext.forR4Cached();
|
|
||||||
private static final MyCodeSystemProvider ourCodeSystemProvider = new MyCodeSystemProvider();
|
|
||||||
private static final MyValueSetProvider ourValueSetProvider = new MyValueSetProvider();
|
|
||||||
|
|
||||||
@RegisterExtension
|
|
||||||
public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx, ourCodeSystemProvider,
|
|
||||||
ourValueSetProvider);
|
|
||||||
|
|
||||||
private RemoteTerminologyServiceValidationSupport mySvc;
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
public void before_ConfigureService() {
|
|
||||||
String myBaseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
|
|
||||||
mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx, myBaseUrl);
|
|
||||||
mySvc.addClientInterceptor(new LoggingInterceptor(false).setLogRequestSummary(true).setLogResponseSummary(true));
|
|
||||||
}
|
|
||||||
|
|
||||||
@AfterEach
|
|
||||||
public void after_UnregisterProviders() {
|
|
||||||
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE);
|
|
||||||
ourRestfulServerExtension.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);
|
|
||||||
assertNotNull(outcome);
|
|
||||||
assertEquals(CODE, outcome.getCode());
|
|
||||||
assertNull(outcome.getSeverity());
|
|
||||||
assertNull(outcome.getMessage());
|
|
||||||
|
|
||||||
assertEquals(CODE, ourCodeSystemProvider.myLastCode.getCode());
|
|
||||||
assertEquals(CODE_SYSTEM, ourCodeSystemProvider.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);
|
|
||||||
assertNotNull(outcome);
|
|
||||||
assertEquals(CODE, outcome.getCode());
|
|
||||||
assertEquals(DISPLAY, outcome.getDisplay());
|
|
||||||
assertNull(outcome.getSeverity());
|
|
||||||
assertNull(outcome.getMessage());
|
|
||||||
|
|
||||||
assertEquals(CODE, ourCodeSystemProvider.myLastCode.getCode());
|
|
||||||
assertEquals(DISPLAY, ourCodeSystemProvider.myLastDisplay.getValue());
|
|
||||||
assertEquals(CODE_SYSTEM, ourCodeSystemProvider.myLastUrl.getValueAsString());
|
|
||||||
assertEquals(SAMPLE_MESSAGE, ourCodeSystemProvider.myNextReturnParams.getParameterValue("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);
|
|
||||||
assertNotNull(outcome);
|
|
||||||
assertEquals(IValidationSupport.IssueSeverity.ERROR, outcome.getSeverity());
|
|
||||||
assertEquals(SAMPLE_MESSAGE, outcome.getMessage());
|
|
||||||
|
|
||||||
assertFalse(((BooleanType) ourCodeSystemProvider.myNextReturnParams.getParameterValue("result")).booleanValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testValidateCodeInValueSet_ProvidingMinimalInputs_ReturnsSuccess() {
|
|
||||||
ourValueSetProvider.myNextReturnParams = new Parameters().addParameter("result", true);
|
|
||||||
|
|
||||||
IValidationSupport.CodeValidationResult outcome = mySvc
|
|
||||||
.validateCode(null, null, CODE_SYSTEM, CODE, null, VALUE_SET_URL);
|
|
||||||
assertNotNull(outcome);
|
|
||||||
assertEquals(CODE, outcome.getCode());
|
|
||||||
assertNull(outcome.getSeverity());
|
|
||||||
assertNull(outcome.getMessage());
|
|
||||||
|
|
||||||
assertEquals(CODE, ourValueSetProvider.myLastCode.getCode());
|
|
||||||
assertEquals(VALUE_SET_URL, ourValueSetProvider.myLastUrl.getValueAsString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testValidateCodeInValueSet_WithMessageValue_ReturnsMessage() {
|
|
||||||
ourValueSetProvider.myNextReturnParams = new Parameters().addParameter("result", true)
|
|
||||||
.addParameter("display", DISPLAY)
|
|
||||||
.addParameter("message", SAMPLE_MESSAGE);
|
|
||||||
|
|
||||||
IValidationSupport.CodeValidationResult outcome = mySvc
|
|
||||||
.validateCode(null, null, CODE_SYSTEM, CODE, DISPLAY, VALUE_SET_URL);
|
|
||||||
assertNotNull(outcome);
|
|
||||||
assertEquals(CODE, outcome.getCode());
|
|
||||||
assertEquals(DISPLAY, outcome.getDisplay());
|
|
||||||
assertNull(outcome.getSeverity());
|
|
||||||
assertNull(outcome.getMessage());
|
|
||||||
|
|
||||||
assertEquals(CODE, ourValueSetProvider.myLastCode.getCode());
|
|
||||||
assertEquals(DISPLAY, ourValueSetProvider.myLastDisplay.getValue());
|
|
||||||
assertEquals(VALUE_SET_URL, ourValueSetProvider.myLastUrl.getValueAsString());
|
|
||||||
assertEquals(SAMPLE_MESSAGE, ourValueSetProvider.myNextReturnParams.getParameterValue("message").toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void lookupCode_withParametersOutput_convertsCorrectly() {
|
|
||||||
String paramsAsString = ClasspathUtil.loadResource("/r4/CodeSystem-lookup-output-with-subproperties.json");
|
|
||||||
IBaseResource baseResource = ourCtx.newJsonParser().parseResource(paramsAsString);
|
|
||||||
assertTrue(baseResource instanceof Parameters);
|
|
||||||
Parameters resultParameters = (Parameters) baseResource;
|
|
||||||
ourCodeSystemProvider.myNextReturnParams = resultParameters;
|
|
||||||
|
|
||||||
LookupCodeRequest request = new LookupCodeRequest(CODE_SYSTEM, CODE, null, List.of("interfaces"));
|
|
||||||
|
|
||||||
// test
|
|
||||||
IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(null, request);
|
|
||||||
assertNotNull(outcome);
|
|
||||||
|
|
||||||
IBaseParameters theActualParameters = outcome.toParameters(ourCtx, request.getPropertyNames().stream().map(StringType::new).toList());
|
|
||||||
String actual = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(theActualParameters);
|
|
||||||
String expected = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(resultParameters);
|
|
||||||
|
|
||||||
assertEquals(expected, actual);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createNextCodeSystemReturnParameters(boolean theResult, String theDisplay, String theMessage) {
|
|
||||||
ourCodeSystemProvider.myNextReturnParams = new Parameters();
|
|
||||||
ourCodeSystemProvider.myNextReturnParams.addParameter("result", theResult);
|
|
||||||
ourCodeSystemProvider.myNextReturnParams.addParameter("display", theDisplay);
|
|
||||||
if (theMessage != null) {
|
|
||||||
ourCodeSystemProvider.myNextReturnParams.addParameter("message", theMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class MyCodeSystemProvider implements IResourceProvider {
|
|
||||||
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
|
|
||||||
) {
|
|
||||||
myLastUrl = theCodeSystemUrl;
|
|
||||||
myLastCode = theCode;
|
|
||||||
myLastDisplay = theDisplay;
|
|
||||||
return myNextReturnParams;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters= {
|
|
||||||
@OperationParam(name = "name", type = StringType.class, min = 1),
|
|
||||||
@OperationParam(name = "version", type = StringType.class),
|
|
||||||
@OperationParam(name = "display", type = StringType.class, min = 1),
|
|
||||||
@OperationParam(name = "abstract", type = BooleanType.class, min = 1),
|
|
||||||
@OperationParam(name = "property", type = StringType.class, min = 0, max = OperationParam.MAX_UNLIMITED)
|
|
||||||
})
|
|
||||||
public IBaseParameters lookup(
|
|
||||||
HttpServletRequest theServletRequest,
|
|
||||||
@OperationParam(name = "code", max = 1) CodeType theCode,
|
|
||||||
@OperationParam(name = "system",max = 1) UriType theSystem,
|
|
||||||
@OperationParam(name = "coding", max = 1) Coding theCoding,
|
|
||||||
@OperationParam(name = "version", max = 1) StringType theVersion,
|
|
||||||
@OperationParam(name = "displayLanguage", max = 1) CodeType theDisplayLanguage,
|
|
||||||
@OperationParam(name = "property", max = OperationParam.MAX_UNLIMITED) List<CodeType> thePropertyNames,
|
|
||||||
RequestDetails theRequestDetails
|
|
||||||
) {
|
|
||||||
myLastCode = theCode;
|
|
||||||
return myNextReturnParams;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class<? extends IBaseResource> getResourceType() {
|
|
||||||
return CodeSystem.class;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static class MyValueSetProvider implements IResourceProvider {
|
|
||||||
private Parameters myNextReturnParams;
|
|
||||||
private UriType myLastUrl;
|
|
||||||
private CodeType myLastCode;
|
|
||||||
private StringType myLastDisplay;
|
|
||||||
|
|
||||||
@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
|
|
||||||
) {
|
|
||||||
myLastUrl = theValueSetUrl;
|
|
||||||
myLastCode = theCode;
|
|
||||||
myLastDisplay = theDisplay;
|
|
||||||
return myNextReturnParams;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class<? extends IBaseResource> getResourceType() {
|
|
||||||
return ValueSet.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +1,11 @@
|
||||||
package org.hl7.fhir.r4.validation;
|
package org.hl7.fhir.r4.validation;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
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.IValidationSupport;
|
||||||
import ca.uhn.fhir.context.support.TranslateConceptResult;
|
import ca.uhn.fhir.context.support.TranslateConceptResult;
|
||||||
import ca.uhn.fhir.context.support.TranslateConceptResults;
|
import ca.uhn.fhir.context.support.TranslateConceptResults;
|
||||||
import ca.uhn.fhir.fhirpath.BaseValidationTestWithInlineMocks;
|
import ca.uhn.fhir.fhirpath.BaseValidationTestWithInlineMocks;
|
||||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||||
import ca.uhn.fhir.parser.IJsonLikeParser;
|
|
||||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||||
import ca.uhn.fhir.rest.annotation.Operation;
|
import ca.uhn.fhir.rest.annotation.Operation;
|
||||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||||
|
@ -15,21 +13,13 @@ import ca.uhn.fhir.rest.annotation.RequiredParam;
|
||||||
import ca.uhn.fhir.rest.annotation.Search;
|
import ca.uhn.fhir.rest.annotation.Search;
|
||||||
import ca.uhn.fhir.rest.api.SummaryEnum;
|
import ca.uhn.fhir.rest.api.SummaryEnum;
|
||||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
import ca.uhn.fhir.rest.client.api.IClientInterceptor;
|
|
||||||
import ca.uhn.fhir.rest.client.api.IHttpRequest;
|
|
||||||
import ca.uhn.fhir.rest.client.api.IHttpResponse;
|
|
||||||
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
||||||
import ca.uhn.fhir.rest.param.UriParam;
|
import ca.uhn.fhir.rest.param.UriParam;
|
||||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
|
||||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
|
||||||
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
||||||
import ca.uhn.fhir.util.ParametersUtil;
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
|
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseCoding;
|
import org.hl7.fhir.instance.model.api.IBaseCoding;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.r4.model.BooleanType;
|
import org.hl7.fhir.r4.model.BooleanType;
|
||||||
import org.hl7.fhir.r4.model.CodeSystem;
|
import org.hl7.fhir.r4.model.CodeSystem;
|
||||||
|
@ -42,22 +32,14 @@ import org.hl7.fhir.r4.model.Parameters;
|
||||||
import org.hl7.fhir.r4.model.StringType;
|
import org.hl7.fhir.r4.model.StringType;
|
||||||
import org.hl7.fhir.r4.model.UriType;
|
import org.hl7.fhir.r4.model.UriType;
|
||||||
import org.hl7.fhir.r4.model.ValueSet;
|
import org.hl7.fhir.r4.model.ValueSet;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Nested;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
|
||||||
import org.junit.jupiter.params.provider.Arguments;
|
|
||||||
import org.junit.jupiter.params.provider.MethodSource;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
@ -72,12 +54,10 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
*
|
*
|
||||||
* Other operations are tested separately.
|
* Other operations are tested separately.
|
||||||
* @see RemoteTerminologyLookupCodeR4Test
|
* @see RemoteTerminologyLookupCodeR4Test
|
||||||
* @see RemoteTerminologyServiceResourceProviderR4Test
|
* @see RemoteTerminologyValidateCodeR4Test
|
||||||
*/
|
*/
|
||||||
public class RemoteTerminologyServiceValidationSupportR4Test extends BaseValidationTestWithInlineMocks {
|
public class RemoteTerminologyServiceValidationSupportR4Test extends BaseValidationTestWithInlineMocks {
|
||||||
private static final String DISPLAY = "DISPLAY";
|
|
||||||
private static final String CODE_SYSTEM = "CODE_SYS";
|
private static final String CODE_SYSTEM = "CODE_SYS";
|
||||||
private static final String CODE_SYSTEM_NAME = "Code System";
|
|
||||||
private static final String CODE = "CODE";
|
private static final String CODE = "CODE";
|
||||||
private static final String VALUE_SET_URL = "http://value.set/url";
|
private static final String VALUE_SET_URL = "http://value.set/url";
|
||||||
private static final String TARGET_SYSTEM = "http://target.system/url";
|
private static final String TARGET_SYSTEM = "http://target.system/url";
|
||||||
|
@ -89,9 +69,7 @@ public class RemoteTerminologyServiceValidationSupportR4Test extends BaseValidat
|
||||||
private static final String TARGET_CODE_DISPLAY = "code";
|
private static final String TARGET_CODE_DISPLAY = "code";
|
||||||
private static final boolean REVERSE = true;
|
private static final boolean REVERSE = true;
|
||||||
private static final String EQUIVALENCE_CODE = "equivalent";
|
private static final String EQUIVALENCE_CODE = "equivalent";
|
||||||
|
|
||||||
private static final String ERROR_MESSAGE = "This is an error message";
|
private static final String ERROR_MESSAGE = "This is an error message";
|
||||||
private static final String SUCCESS_MESSAGE = "This is a success message";
|
|
||||||
|
|
||||||
private static final FhirContext ourCtx = FhirContext.forR4Cached();
|
private static final FhirContext ourCtx = FhirContext.forR4Cached();
|
||||||
|
|
||||||
|
@ -114,83 +92,8 @@ public class RemoteTerminologyServiceValidationSupportR4Test extends BaseValidat
|
||||||
mySvc.addClientInterceptor(new LoggingInterceptor(true));
|
mySvc.addClientInterceptor(new LoggingInterceptor(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterEach
|
|
||||||
public void after() {
|
|
||||||
assertThat(myValueSetProvider.myInvocationCount).isLessThan(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testValidateCode_withBlankCode_returnsNull() {
|
void fetchValueSet_forcesSummaryFalse() {
|
||||||
IValidationSupport.CodeValidationResult outcome = mySvc.validateCode(null, null, CODE_SYSTEM, "", DISPLAY, VALUE_SET_URL);
|
|
||||||
assertNull(outcome);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Stream<Arguments> getRemoteTerminologyServerResponses() {
|
|
||||||
return Stream.of(
|
|
||||||
Arguments.of(new ResourceNotFoundException("System Not Present"), "404 Not Found: System Not Present",
|
|
||||||
"Unknown code \"null#CODE\". The Remote Terminology server", null, null),
|
|
||||||
Arguments.of(new InvalidRequestException("Invalid Request"), "400 Bad Request: Invalid Request",
|
|
||||||
"Unknown code \"null#CODE\". The Remote Terminology server", null, null),
|
|
||||||
Arguments.of(new ResourceNotFoundException("System Not Present"), "404 Not Found: System Not Present",
|
|
||||||
"Unknown code \"NotFoundSystem#CODE\". The Remote Terminology server", "NotFoundSystem", null),
|
|
||||||
Arguments.of(new InvalidRequestException("Invalid Request"), "400 Bad Request: Invalid Request",
|
|
||||||
"Unknown code \"InvalidSystem#CODE\". The Remote Terminology server", "InvalidSystem", null),
|
|
||||||
Arguments.of(new ResourceNotFoundException("System Not Present"), "404 Not Found: System Not Present",
|
|
||||||
"Unknown code \"null#CODE\" for ValueSet with URL \"NotFoundValueSetUrl\". The Remote Terminology server",
|
|
||||||
null, "NotFoundValueSetUrl"),
|
|
||||||
Arguments.of(new InvalidRequestException("Invalid Request"), "400 Bad Request: Invalid Request",
|
|
||||||
"Unknown code \"null#CODE\" for ValueSet with URL \"InvalidValueSetUrl\". The Remote Terminology server", null, "InvalidValueSetUrl"),
|
|
||||||
Arguments.of(new ResourceNotFoundException("System Not Present"), "404 Not Found: System Not Present",
|
|
||||||
"Unknown code \"NotFoundSystem#CODE\" for ValueSet with URL \"NotFoundValueSetUrl\". The Remote Terminology server",
|
|
||||||
"NotFoundSystem", "NotFoundValueSetUrl"),
|
|
||||||
Arguments.of(new InvalidRequestException("Invalid Request"), "400 Bad Request: Invalid Request",
|
|
||||||
"Unknown code \"InvalidSystem#CODE\" for ValueSet with URL \"InvalidValueSetUrl\". The Remote Terminology server", "InvalidSystem", "InvalidValueSetUrl")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@ParameterizedTest
|
|
||||||
@MethodSource(value = "getRemoteTerminologyServerResponses")
|
|
||||||
public void testValidateCode_codeSystemAndValueSetUrlAreIncorrect_returnsValidationResultWithError(Exception theException,
|
|
||||||
String theServerMessage,
|
|
||||||
String theValidationMessage,
|
|
||||||
String theCodeSystem,
|
|
||||||
String theValueSetUrl) {
|
|
||||||
myCodeSystemProvider.myNextValidateCodeException = theException;
|
|
||||||
myValueSetProvider.myNextValidateCodeException = theException;
|
|
||||||
IValidationSupport.CodeValidationResult outcome = mySvc.validateCode(null, null, theCodeSystem, CODE, DISPLAY, theValueSetUrl);
|
|
||||||
|
|
||||||
validateValidationErrorResult(outcome, theValidationMessage, theServerMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void validateValidationErrorResult(IValidationSupport.CodeValidationResult outcome, String... theMessages) {
|
|
||||||
assertNotNull(outcome);
|
|
||||||
assertEquals(IValidationSupport.IssueSeverity.ERROR, outcome.getSeverity());
|
|
||||||
assertNotNull(outcome.getMessage());
|
|
||||||
for (String message : theMessages) {
|
|
||||||
assertTrue(outcome.getMessage().contains(message));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testValidateCode_forValueSet_returnsCorrectly() {
|
|
||||||
createNextValueSetReturnParameters(true, DISPLAY, null);
|
|
||||||
|
|
||||||
IValidationSupport.CodeValidationResult outcome = mySvc.validateCode(null, null, CODE_SYSTEM, CODE, DISPLAY, VALUE_SET_URL);
|
|
||||||
assertNotNull(outcome);
|
|
||||||
assertEquals(CODE, outcome.getCode());
|
|
||||||
assertEquals(DISPLAY, outcome.getDisplay());
|
|
||||||
assertNull(outcome.getSeverity());
|
|
||||||
assertNull(outcome.getMessage());
|
|
||||||
|
|
||||||
assertEquals(CODE, myValueSetProvider.myLastCode.getCode());
|
|
||||||
assertEquals(DISPLAY, myValueSetProvider.myLastDisplay.getValue());
|
|
||||||
assertEquals(CODE_SYSTEM, myValueSetProvider.myLastSystem.getValue());
|
|
||||||
assertEquals(VALUE_SET_URL, myValueSetProvider.myLastUrl.getValue());
|
|
||||||
assertNull(myValueSetProvider.myLastValueSet);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void testFetchValueSet_forcesSummaryFalse() {
|
|
||||||
// given
|
// given
|
||||||
myValueSetProvider.myNextReturnValueSets = new ArrayList<>();
|
myValueSetProvider.myNextReturnValueSets = new ArrayList<>();
|
||||||
|
|
||||||
|
@ -202,67 +105,7 @@ public class RemoteTerminologyServiceValidationSupportR4Test extends BaseValidat
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testValidateCode_forSystemCodeWithError_returnsCorrectly() {
|
void translateCode_AllInParams_AllOutParams() {
|
||||||
createNextValueSetReturnParameters(false, null, ERROR_MESSAGE);
|
|
||||||
|
|
||||||
IValidationSupport.CodeValidationResult outcome = mySvc.validateCode(null, null, CODE_SYSTEM, CODE, DISPLAY, VALUE_SET_URL);
|
|
||||||
assertNotNull(outcome);
|
|
||||||
assertNull(outcome.getCode());
|
|
||||||
assertNull(outcome.getDisplay());
|
|
||||||
assertEquals(IValidationSupport.IssueSeverity.ERROR, outcome.getSeverity());
|
|
||||||
assertEquals(ERROR_MESSAGE, outcome.getMessage());
|
|
||||||
|
|
||||||
assertEquals(CODE, myValueSetProvider.myLastCode.getCode());
|
|
||||||
assertEquals(DISPLAY, myValueSetProvider.myLastDisplay.getValue());
|
|
||||||
assertEquals(CODE_SYSTEM, myValueSetProvider.myLastSystem.getValue());
|
|
||||||
assertEquals(VALUE_SET_URL, myValueSetProvider.myLastUrl.getValue());
|
|
||||||
assertNull(myValueSetProvider.myLastValueSet);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testValidateCode_forCodeSystem_returnsCorrectly() {
|
|
||||||
myCodeSystemProvider.myNextValidationResult = new IValidationSupport.CodeValidationResult();
|
|
||||||
myCodeSystemProvider.myNextValidationResult.setCodeSystemVersion(CODE_SYSTEM);
|
|
||||||
myCodeSystemProvider.myNextValidationResult.setCode(CODE);
|
|
||||||
myCodeSystemProvider.myNextValidationResult.setCodeSystemName(CODE_SYSTEM_NAME);
|
|
||||||
myCodeSystemProvider.myNextValidationResult.setDisplay(DISPLAY);
|
|
||||||
myCodeSystemProvider.myNextValidationResult.setMessage(SUCCESS_MESSAGE);
|
|
||||||
|
|
||||||
IValidationSupport.CodeValidationResult outcome = mySvc.validateCode(null, null, CODE_SYSTEM, CODE, DISPLAY, null);
|
|
||||||
assertNotNull(outcome);
|
|
||||||
assertEquals(CODE, outcome.getCode());
|
|
||||||
assertEquals(DISPLAY, outcome.getDisplay());
|
|
||||||
assertNull(outcome.getSeverity());
|
|
||||||
assertNull(outcome.getMessage());
|
|
||||||
|
|
||||||
assertEquals(CODE, myCodeSystemProvider.myCode.getCode());
|
|
||||||
assertEquals(CODE_SYSTEM, myCodeSystemProvider.mySystemUrl.getValueAsString());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testValidateCodeInValueSet_SystemCodeDisplayVS_Good() {
|
|
||||||
createNextValueSetReturnParameters(true, DISPLAY, null);
|
|
||||||
|
|
||||||
ValueSet valueSet = new ValueSet();
|
|
||||||
valueSet.setUrl(VALUE_SET_URL);
|
|
||||||
|
|
||||||
IValidationSupport.CodeValidationResult outcome = mySvc.validateCodeInValueSet(null, new ConceptValidationOptions(), CODE_SYSTEM, CODE, DISPLAY, valueSet);
|
|
||||||
assertNotNull(outcome);
|
|
||||||
assertEquals(CODE, outcome.getCode());
|
|
||||||
assertEquals(DISPLAY, outcome.getDisplay());
|
|
||||||
assertNull(outcome.getSeverity());
|
|
||||||
assertNull(outcome.getMessage());
|
|
||||||
|
|
||||||
assertEquals(CODE, myValueSetProvider.myLastCode.getCode());
|
|
||||||
assertEquals(DISPLAY, myValueSetProvider.myLastDisplay.getValue());
|
|
||||||
assertEquals(CODE_SYSTEM, myValueSetProvider.myLastSystem.getValue());
|
|
||||||
assertEquals(VALUE_SET_URL, myValueSetProvider.myLastUrl.getValueAsString());
|
|
||||||
assertNull(myValueSetProvider.myLastValueSet);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testTranslateCode_AllInParams_AllOutParams() {
|
|
||||||
myConceptMapProvider.myNextReturnParams = new Parameters();
|
myConceptMapProvider.myNextReturnParams = new Parameters();
|
||||||
myConceptMapProvider.myNextReturnParams.addParameter("result", true);
|
myConceptMapProvider.myNextReturnParams.addParameter("result", true);
|
||||||
myConceptMapProvider.myNextReturnParams.addParameter("message", ERROR_MESSAGE);
|
myConceptMapProvider.myNextReturnParams.addParameter("message", ERROR_MESSAGE);
|
||||||
|
@ -303,7 +146,7 @@ public class RemoteTerminologyServiceValidationSupportR4Test extends BaseValidat
|
||||||
|
|
||||||
assertNotNull(results);
|
assertNotNull(results);
|
||||||
assertTrue(results.getResult());
|
assertTrue(results.getResult());
|
||||||
assertEquals(results.getResults().size(), 2);
|
assertEquals(2, results.getResults().size());
|
||||||
for(TranslateConceptResult result : results.getResults()) {
|
for(TranslateConceptResult result : results.getResults()) {
|
||||||
assertEquals(singleResult, result);
|
assertEquals(singleResult, result);
|
||||||
}
|
}
|
||||||
|
@ -318,7 +161,7 @@ public class RemoteTerminologyServiceValidationSupportR4Test extends BaseValidat
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTranslateCode_NoInParams_NoOutParams() {
|
void translateCode_NoInParams_NoOutParams() {
|
||||||
myConceptMapProvider.myNextReturnParams = new Parameters();
|
myConceptMapProvider.myNextReturnParams = new Parameters();
|
||||||
|
|
||||||
List<IBaseCoding> codings = new ArrayList<>();
|
List<IBaseCoding> codings = new ArrayList<>();
|
||||||
|
@ -328,7 +171,7 @@ public class RemoteTerminologyServiceValidationSupportR4Test extends BaseValidat
|
||||||
TranslateConceptResults results = mySvc.translateConcept(request);
|
TranslateConceptResults results = mySvc.translateConcept(request);
|
||||||
assertNotNull(results);
|
assertNotNull(results);
|
||||||
assertFalse(results.getResult());
|
assertFalse(results.getResult());
|
||||||
assertEquals(results.getResults().size(), 0);
|
assertEquals(0, results.getResults().size());
|
||||||
|
|
||||||
assertNull(myConceptMapProvider.myLastCodeableConcept);
|
assertNull(myConceptMapProvider.myLastCodeableConcept);
|
||||||
assertNull(myConceptMapProvider.myLastTargetCodeSystem);
|
assertNull(myConceptMapProvider.myLastTargetCodeSystem);
|
||||||
|
@ -340,7 +183,7 @@ public class RemoteTerminologyServiceValidationSupportR4Test extends BaseValidat
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testFetchCodeSystem_forcesSummaryFalse() {
|
void fetchCodeSystem_forcesSummaryFalse() {
|
||||||
// given
|
// given
|
||||||
myCodeSystemProvider.myNextReturnCodeSystems = new ArrayList<>();
|
myCodeSystemProvider.myNextReturnCodeSystems = new ArrayList<>();
|
||||||
|
|
||||||
|
@ -360,180 +203,8 @@ public class RemoteTerminologyServiceValidationSupportR4Test extends BaseValidat
|
||||||
matchParam.addPart().setName("source").setValue(new UriType(CONCEPT_MAP_URL));
|
matchParam.addPart().setName("source").setValue(new UriType(CONCEPT_MAP_URL));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Remote terminology services can be used to validate codes when code system is present,
|
|
||||||
* even when inferSystem is true
|
|
||||||
*/
|
|
||||||
@Nested
|
|
||||||
public class ExtractCodeSystemFromValueSet {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testUniqueComposeInclude() {
|
|
||||||
createNextValueSetReturnParameters(true, DISPLAY, null);
|
|
||||||
|
|
||||||
ValueSet valueSet = new ValueSet();
|
|
||||||
valueSet.setUrl(VALUE_SET_URL);
|
|
||||||
String systemUrl = "http://hl7.org/fhir/ValueSet/administrative-gender";
|
|
||||||
valueSet.setCompose(new ValueSet.ValueSetComposeComponent().setInclude(
|
|
||||||
Collections.singletonList(new ValueSet.ConceptSetComponent().setSystem(systemUrl)) ));
|
|
||||||
|
|
||||||
IValidationSupport.CodeValidationResult outcome = mySvc.validateCodeInValueSet(null,
|
|
||||||
new ConceptValidationOptions().setInferSystem(true), null, CODE, DISPLAY, valueSet);
|
|
||||||
|
|
||||||
// validate service doesn't return error message (as when no code system is present)
|
|
||||||
assertNotNull(outcome);
|
|
||||||
assertNull(outcome.getMessage());
|
|
||||||
assertTrue(outcome.isOk());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Nested
|
|
||||||
public class MultiComposeIncludeValueSet {
|
|
||||||
|
|
||||||
public static Stream<Arguments> getRemoteTerminologyServerExceptions() {
|
|
||||||
return Stream.of(
|
|
||||||
Arguments.of(new ResourceNotFoundException("System Not Present"), "404 Not Found: System Not Present"),
|
|
||||||
Arguments.of(new InvalidRequestException("Invalid Request"), "400 Bad Request: Invalid Request")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@ParameterizedTest
|
|
||||||
@MethodSource(value = "getRemoteTerminologyServerExceptions")
|
|
||||||
public void systemNotPresent_returnsValidationResultWithError(Exception theException, String theServerMessage) {
|
|
||||||
myValueSetProvider.myNextValidateCodeException = theException;
|
|
||||||
createNextValueSetReturnParameters(true, DISPLAY, null);
|
|
||||||
|
|
||||||
ValueSet valueSet = new ValueSet();
|
|
||||||
valueSet.setUrl(VALUE_SET_URL);
|
|
||||||
valueSet.setCompose(new ValueSet.ValueSetComposeComponent().setInclude(
|
|
||||||
Lists.newArrayList(new ValueSet.ConceptSetComponent(), new ValueSet.ConceptSetComponent())));
|
|
||||||
|
|
||||||
IValidationSupport.CodeValidationResult outcome = mySvc.validateCodeInValueSet(null,
|
|
||||||
new ConceptValidationOptions().setInferSystem(true), null, CODE, DISPLAY, valueSet);
|
|
||||||
|
|
||||||
String unknownCodeForValueSetError = "Unknown code \"null#CODE\" for ValueSet with URL \"http://value.set/url\". The Remote Terminology server http://";
|
|
||||||
validateValidationErrorResult(outcome, unknownCodeForValueSetError, theServerMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@ParameterizedTest
|
|
||||||
@MethodSource(value = "getRemoteTerminologyServerExceptions")
|
|
||||||
public void systemPresentCodeNotPresent_returnsValidationResultWithError(Exception theException, String theServerMessage) {
|
|
||||||
myValueSetProvider.myNextValidateCodeException = theException;
|
|
||||||
createNextValueSetReturnParameters(true, DISPLAY, null);
|
|
||||||
|
|
||||||
ValueSet valueSet = new ValueSet();
|
|
||||||
valueSet.setUrl(VALUE_SET_URL);
|
|
||||||
String systemUrl = "http://hl7.org/fhir/ValueSet/administrative-gender";
|
|
||||||
String systemUrl2 = "http://hl7.org/fhir/ValueSet/other-valueset";
|
|
||||||
valueSet.setCompose(new ValueSet.ValueSetComposeComponent().setInclude(
|
|
||||||
Lists.newArrayList(
|
|
||||||
new ValueSet.ConceptSetComponent().setSystem(systemUrl),
|
|
||||||
new ValueSet.ConceptSetComponent().setSystem(systemUrl2))));
|
|
||||||
|
|
||||||
IValidationSupport.CodeValidationResult outcome = mySvc.validateCodeInValueSet(null,
|
|
||||||
new ConceptValidationOptions().setInferSystem(true), null, CODE, DISPLAY, valueSet);
|
|
||||||
|
|
||||||
String unknownCodeForValueSetError = "Unknown code \"null#CODE\" for ValueSet with URL \"http://value.set/url\". The Remote Terminology server http://";
|
|
||||||
validateValidationErrorResult(outcome, unknownCodeForValueSetError, theServerMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void SystemPresentCodePresentValidatesOKNoVersioned() {
|
|
||||||
createNextValueSetReturnParameters(true, DISPLAY, null);
|
|
||||||
|
|
||||||
ValueSet valueSet = new ValueSet();
|
|
||||||
valueSet.setUrl(VALUE_SET_URL);
|
|
||||||
String systemUrl = "http://hl7.org/fhir/ValueSet/administrative-gender";
|
|
||||||
String systemUrl2 = "http://hl7.org/fhir/ValueSet/other-valueset";
|
|
||||||
valueSet.setCompose(new ValueSet.ValueSetComposeComponent().setInclude(
|
|
||||||
Lists.newArrayList(
|
|
||||||
new ValueSet.ConceptSetComponent().setSystem(systemUrl),
|
|
||||||
new ValueSet.ConceptSetComponent().setSystem(systemUrl2).setConcept(
|
|
||||||
Lists.newArrayList(
|
|
||||||
new ValueSet.ConceptReferenceComponent().setCode("not-the-code"),
|
|
||||||
new ValueSet.ConceptReferenceComponent().setCode(CODE) )
|
|
||||||
)) ));
|
|
||||||
|
|
||||||
TestClientInterceptor requestInterceptor = new TestClientInterceptor();
|
|
||||||
mySvc.addClientInterceptor(requestInterceptor);
|
|
||||||
|
|
||||||
IValidationSupport.CodeValidationResult outcome = mySvc.validateCodeInValueSet(null,
|
|
||||||
new ConceptValidationOptions().setInferSystem(true), null, CODE, DISPLAY, valueSet);
|
|
||||||
|
|
||||||
assertNotNull(outcome);
|
|
||||||
assertEquals(systemUrl2, requestInterceptor.getCapturedSystemParameter());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void SystemPresentCodePresentValidatesOKVersioned() {
|
|
||||||
createNextValueSetReturnParameters(true, DISPLAY, null);
|
|
||||||
|
|
||||||
ValueSet valueSet = new ValueSet();
|
|
||||||
valueSet.setUrl(VALUE_SET_URL);
|
|
||||||
String systemUrl = "http://hl7.org/fhir/ValueSet/administrative-gender";
|
|
||||||
String systemVersion = "3.0.2";
|
|
||||||
String systemUrl2 = "http://hl7.org/fhir/ValueSet/other-valueset";
|
|
||||||
String system2Version = "4.0.1";
|
|
||||||
valueSet.setCompose(new ValueSet.ValueSetComposeComponent().setInclude(
|
|
||||||
Lists.newArrayList(
|
|
||||||
new ValueSet.ConceptSetComponent().setSystem(systemUrl).setVersion(systemVersion),
|
|
||||||
new ValueSet.ConceptSetComponent().setSystem(systemUrl2).setVersion(system2Version).setConcept(
|
|
||||||
Lists.newArrayList(
|
|
||||||
new ValueSet.ConceptReferenceComponent().setCode("not-the-code"),
|
|
||||||
new ValueSet.ConceptReferenceComponent().setCode(CODE) )
|
|
||||||
)) ));
|
|
||||||
|
|
||||||
TestClientInterceptor requestInterceptor = new TestClientInterceptor();
|
|
||||||
mySvc.addClientInterceptor(requestInterceptor);
|
|
||||||
|
|
||||||
IValidationSupport.CodeValidationResult outcome = mySvc.validateCodeInValueSet(null,
|
|
||||||
new ConceptValidationOptions().setInferSystem(true), null, CODE, DISPLAY, valueSet);
|
|
||||||
|
|
||||||
assertNotNull(outcome);
|
|
||||||
assertEquals(systemUrl2 + "|" + system2Version, requestInterceptor.getCapturedSystemParameter());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Captures the system parameter of the request
|
|
||||||
*/
|
|
||||||
private static class TestClientInterceptor implements IClientInterceptor {
|
|
||||||
|
|
||||||
private String capturedSystemParameter;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void interceptRequest(IHttpRequest theRequest) {
|
|
||||||
try {
|
|
||||||
String content = theRequest.getRequestBodyFromStream();
|
|
||||||
if (content != null) {
|
|
||||||
IJsonLikeParser parser = (IJsonLikeParser) ourCtx.newJsonParser();
|
|
||||||
Parameters params = parser.parseResource(Parameters.class, content);
|
|
||||||
List<String> systemValues = ParametersUtil.getNamedParameterValuesAsString(
|
|
||||||
ourCtx, params, "system");
|
|
||||||
assertThat(systemValues).hasSize(1);
|
|
||||||
capturedSystemParameter = systemValues.get(0);
|
|
||||||
}
|
|
||||||
} catch (IOException theE) {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void interceptResponse(IHttpResponse theResponse) { }
|
|
||||||
|
|
||||||
public String getCapturedSystemParameter() { return capturedSystemParameter; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIsValueSetSupported_False() {
|
void isValueSetSupported_False() {
|
||||||
myValueSetProvider.myNextReturnValueSets = new ArrayList<>();
|
myValueSetProvider.myNextReturnValueSets = new ArrayList<>();
|
||||||
|
|
||||||
boolean outcome = mySvc.isValueSetSupported(null, "http://loinc.org/VS");
|
boolean outcome = mySvc.isValueSetSupported(null, "http://loinc.org/VS");
|
||||||
|
@ -542,7 +213,7 @@ public class RemoteTerminologyServiceValidationSupportR4Test extends BaseValidat
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIsValueSetSupported_True() {
|
void isValueSetSupported_True() {
|
||||||
myValueSetProvider.myNextReturnValueSets = new ArrayList<>();
|
myValueSetProvider.myNextReturnValueSets = new ArrayList<>();
|
||||||
myValueSetProvider.myNextReturnValueSets.add((ValueSet) new ValueSet().setId("ValueSet/123"));
|
myValueSetProvider.myNextReturnValueSets.add((ValueSet) new ValueSet().setId("ValueSet/123"));
|
||||||
|
|
||||||
|
@ -552,7 +223,7 @@ public class RemoteTerminologyServiceValidationSupportR4Test extends BaseValidat
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIsCodeSystemSupported_False() {
|
void isCodeSystemSupported_False() {
|
||||||
myCodeSystemProvider.myNextReturnCodeSystems = new ArrayList<>();
|
myCodeSystemProvider.myNextReturnCodeSystems = new ArrayList<>();
|
||||||
|
|
||||||
boolean outcome = mySvc.isCodeSystemSupported(null, "http://loinc.org");
|
boolean outcome = mySvc.isCodeSystemSupported(null, "http://loinc.org");
|
||||||
|
@ -561,7 +232,7 @@ public class RemoteTerminologyServiceValidationSupportR4Test extends BaseValidat
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIsCodeSystemSupported_True() {
|
void isCodeSystemSupported_True() {
|
||||||
myCodeSystemProvider.myNextReturnCodeSystems = new ArrayList<>();
|
myCodeSystemProvider.myNextReturnCodeSystems = new ArrayList<>();
|
||||||
myCodeSystemProvider.myNextReturnCodeSystems.add((CodeSystem) new CodeSystem().setId("CodeSystem/123"));
|
myCodeSystemProvider.myNextReturnCodeSystems.add((CodeSystem) new CodeSystem().setId("CodeSystem/123"));
|
||||||
|
|
||||||
|
@ -570,47 +241,17 @@ public class RemoteTerminologyServiceValidationSupportR4Test extends BaseValidat
|
||||||
assertEquals("http://loinc.org", myCodeSystemProvider.myLastUrlParam.getValue());
|
assertEquals("http://loinc.org", myCodeSystemProvider.myLastUrlParam.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createNextValueSetReturnParameters(boolean theResult, String theDisplay, String theMessage) {
|
@SuppressWarnings("unused")
|
||||||
myValueSetProvider.myNextReturnParams = new Parameters()
|
private static class MyCodeSystemProvider implements IResourceProvider {
|
||||||
.addParameter("result", theResult)
|
|
||||||
.addParameter("display", theDisplay)
|
|
||||||
.addParameter("message", theMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
static private class MyCodeSystemProvider implements IResourceProvider {
|
|
||||||
private SummaryEnum myLastSummaryParam;
|
private SummaryEnum myLastSummaryParam;
|
||||||
private Exception myNextValidateCodeException;
|
|
||||||
private UriParam myLastUrlParam;
|
private UriParam myLastUrlParam;
|
||||||
private List<CodeSystem> myNextReturnCodeSystems;
|
private List<CodeSystem> myNextReturnCodeSystems;
|
||||||
private UriType mySystemUrl;
|
|
||||||
private CodeType myCode;
|
|
||||||
private IValidationSupport.CodeValidationResult myNextValidationResult;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Class<? extends IBaseResource> getResourceType() {
|
public Class<? extends IBaseResource> getResourceType() {
|
||||||
return CodeSystem.class;
|
return CodeSystem.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
@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 IBaseParameters validateCode(
|
|
||||||
HttpServletRequest theServletRequest,
|
|
||||||
@IdParam(optional = true) IdType theId,
|
|
||||||
@OperationParam(name = "url", min = 0, max = 1) UriType theSystem,
|
|
||||||
@OperationParam(name = "code", min = 0, max = 1) CodeType theCode,
|
|
||||||
@OperationParam(name = "display", min = 0, max = 1) StringType theDisplay
|
|
||||||
) throws Exception {
|
|
||||||
myCode = theCode;
|
|
||||||
mySystemUrl = theSystem;
|
|
||||||
if (myNextValidateCodeException != null) {
|
|
||||||
throw myNextValidateCodeException;
|
|
||||||
}
|
|
||||||
return myNextValidationResult.toParameters(ourCtx);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Search
|
@Search
|
||||||
public List<CodeSystem> find(@RequiredParam(name = "url") UriParam theUrlParam, SummaryEnum theSummaryParam) {
|
public List<CodeSystem> find(@RequiredParam(name = "url") UriParam theUrlParam, SummaryEnum theSummaryParam) {
|
||||||
myLastUrlParam = theUrlParam;
|
myLastUrlParam = theUrlParam;
|
||||||
|
@ -620,46 +261,12 @@ public class RemoteTerminologyServiceValidationSupportR4Test extends BaseValidat
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
private static class MyValueSetProvider implements IResourceProvider {
|
private static class MyValueSetProvider implements IResourceProvider {
|
||||||
private Parameters myNextReturnParams;
|
|
||||||
private Exception myNextValidateCodeException;
|
|
||||||
private List<ValueSet> myNextReturnValueSets;
|
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;
|
private UriParam myLastUrlParam;
|
||||||
private SummaryEnum myLastSummaryParam;
|
private SummaryEnum myLastSummaryParam;
|
||||||
|
|
||||||
@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
|
|
||||||
) throws Exception {
|
|
||||||
myInvocationCount++;
|
|
||||||
myLastUrl = theValueSetUrl;
|
|
||||||
myLastCode = theCode;
|
|
||||||
myLastSystem = theSystem;
|
|
||||||
myLastDisplay = theDisplay;
|
|
||||||
myLastValueSet = theValueSet;
|
|
||||||
if (myNextValidateCodeException != null) {
|
|
||||||
throw myNextValidateCodeException;
|
|
||||||
}
|
|
||||||
return myNextReturnParams;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Search
|
@Search
|
||||||
public List<ValueSet> find(@RequiredParam(name = "url") UriParam theUrlParam, SummaryEnum theSummaryParam) {
|
public List<ValueSet> find(@RequiredParam(name = "url") UriParam theUrlParam, SummaryEnum theSummaryParam) {
|
||||||
myLastUrlParam = theUrlParam;
|
myLastUrlParam = theUrlParam;
|
||||||
|
@ -675,6 +282,7 @@ public class RemoteTerminologyServiceValidationSupportR4Test extends BaseValidat
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
private static class MyConceptMapProvider implements IResourceProvider {
|
private static class MyConceptMapProvider implements IResourceProvider {
|
||||||
private UriType myLastConceptMapUrl;
|
private UriType myLastConceptMapUrl;
|
||||||
private StringType myLastConceptMapVersion;
|
private StringType myLastConceptMapVersion;
|
||||||
|
@ -715,7 +323,5 @@ public class RemoteTerminologyServiceValidationSupportR4Test extends BaseValidat
|
||||||
public Class<? extends IBaseResource> getResourceType() {
|
public Class<? extends IBaseResource> getResourceType() {
|
||||||
return ConceptMap.class;
|
return ConceptMap.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,343 @@
|
||||||
|
package org.hl7.fhir.r4.validation;
|
||||||
|
|
||||||
|
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.IValidationSupport.CodeValidationResult;
|
||||||
|
import ca.uhn.fhir.parser.IJsonLikeParser;
|
||||||
|
import ca.uhn.fhir.rest.client.api.IClientInterceptor;
|
||||||
|
import ca.uhn.fhir.rest.client.api.IHttpRequest;
|
||||||
|
import ca.uhn.fhir.rest.client.api.IHttpResponse;
|
||||||
|
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
|
||||||
|
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||||
|
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
||||||
|
import ca.uhn.fhir.util.ClasspathUtil;
|
||||||
|
import ca.uhn.fhir.util.ParametersUtil;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import org.hl7.fhir.common.hapi.validation.IRemoteTerminologyValidateCodeTest;
|
||||||
|
import org.hl7.fhir.common.hapi.validation.IValidationProviders;
|
||||||
|
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
import org.hl7.fhir.r4.model.OperationOutcome;
|
||||||
|
import org.hl7.fhir.r4.model.Parameters;
|
||||||
|
import org.hl7.fhir.r4.model.Resource;
|
||||||
|
import org.hl7.fhir.r4.model.ValueSet;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Nested;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport.ERROR_CODE_UNKNOWN_CODE_IN_CODE_SYSTEM;
|
||||||
|
import static org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport.ERROR_CODE_UNKNOWN_CODE_IN_VALUE_SET;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Version specific tests for validation using RemoteTerminologyValidationSupport.
|
||||||
|
* The tests in this class simulate the call to a remote server and therefore, only tests the code in
|
||||||
|
* the RemoteTerminologyServiceValidationSupport itself. The remote client call is simulated using the test providers.
|
||||||
|
* @see RemoteTerminologyServiceValidationSupport
|
||||||
|
*
|
||||||
|
* Other operations are tested separately.
|
||||||
|
* @see RemoteTerminologyLookupCodeR4Test
|
||||||
|
* @see RemoteTerminologyServiceValidationSupportR4Test
|
||||||
|
*/
|
||||||
|
public class RemoteTerminologyValidateCodeR4Test implements IRemoteTerminologyValidateCodeTest {
|
||||||
|
private static final FhirContext ourCtx = FhirContext.forR4Cached();
|
||||||
|
@RegisterExtension
|
||||||
|
public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx);
|
||||||
|
private IValidateCodeProvidersR4.MyCodeSystemProviderR4 myCodeSystemProvider;
|
||||||
|
private IValidateCodeProvidersR4.MyValueSetProviderR4 myValueSetProvider;
|
||||||
|
private RemoteTerminologyServiceValidationSupport mySvc;
|
||||||
|
private String myCodeSystemError, myValueSetError;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void before() {
|
||||||
|
String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
|
||||||
|
myCodeSystemError = ourCtx.getLocalizer().getMessage(
|
||||||
|
RemoteTerminologyServiceValidationSupport.class,
|
||||||
|
ERROR_CODE_UNKNOWN_CODE_IN_CODE_SYSTEM, IValidationProviders.CODE_SYSTEM, IValidationProviders.CODE, baseUrl, IValidationProviders.ERROR_MESSAGE);
|
||||||
|
myValueSetError = ourCtx.getLocalizer().getMessage(
|
||||||
|
RemoteTerminologyServiceValidationSupport.class,
|
||||||
|
ERROR_CODE_UNKNOWN_CODE_IN_VALUE_SET, IValidationProviders.CODE_SYSTEM, IValidationProviders.CODE, IValidationProviders.VALUE_SET_URL, baseUrl, IValidationProviders.ERROR_MESSAGE);
|
||||||
|
mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx, baseUrl);
|
||||||
|
mySvc.addClientInterceptor(new LoggingInterceptor(false).setLogRequestSummary(true).setLogResponseSummary(true));
|
||||||
|
myCodeSystemProvider = new IValidateCodeProvidersR4.MyCodeSystemProviderR4();
|
||||||
|
myValueSetProvider = new IValidateCodeProvidersR4.MyValueSetProviderR4();
|
||||||
|
ourRestfulServerExtension.getRestfulServer().registerProviders(myCodeSystemProvider, myValueSetProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
public void after() {
|
||||||
|
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE);
|
||||||
|
ourRestfulServerExtension.getRestfulServer().getInterceptorService().unregisterAllInterceptors();
|
||||||
|
ourRestfulServerExtension.getRestfulServer().unregisterProvider(myCodeSystemProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RemoteTerminologyServiceValidationSupport getService() {
|
||||||
|
return mySvc;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IValidationProviders.IMyCodeSystemProvider getCodeSystemProvider() {
|
||||||
|
return myCodeSystemProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IValidationProviders.IMyValueSetProvider getValueSetProvider() {
|
||||||
|
return myValueSetProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCodeSystemError() {
|
||||||
|
return myCodeSystemError;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getValueSetError() {
|
||||||
|
return myValueSetError;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBaseOperationOutcome getCodeSystemInvalidCodeOutcome() {
|
||||||
|
return ClasspathUtil.loadResource(getService().getFhirContext(), OperationOutcome.class, "/terminology/OperationOutcome-CodeSystem-invalid-code.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBaseOperationOutcome getValueSetInvalidCodeOutcome() {
|
||||||
|
return ClasspathUtil.loadResource(getService().getFhirContext(), OperationOutcome.class, "/terminology/OperationOutcome-ValueSet-invalid-code.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<IValidationSupport.CodeValidationIssue> getCodeValidationIssues(IBaseOperationOutcome theOperationOutcome) {
|
||||||
|
return ((OperationOutcome)theOperationOutcome).getIssue().stream()
|
||||||
|
.map(issueComponent -> new IValidationSupport.CodeValidationIssue(
|
||||||
|
issueComponent.getDetails().getText(),
|
||||||
|
IValidationSupport.IssueSeverity.ERROR,
|
||||||
|
/* assume issue type is OperationOutcome.IssueType#CODEINVALID as it is the only match */
|
||||||
|
IValidationSupport.CodeValidationIssueCode.INVALID,
|
||||||
|
IValidationSupport.CodeValidationIssueCoding.INVALID_CODE))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validateCodeInValueSet_success() {
|
||||||
|
createValueSetReturnParameters(true, IValidationProviders.DISPLAY, null, null);
|
||||||
|
|
||||||
|
ValueSet valueSet = new ValueSet();
|
||||||
|
valueSet.setUrl(IValidationProviders.VALUE_SET_URL);
|
||||||
|
|
||||||
|
CodeValidationResult outcome = mySvc.validateCodeInValueSet(null, new ConceptValidationOptions(), IValidationProviders.CODE_SYSTEM, IValidationProviders.CODE, IValidationProviders.DISPLAY, valueSet);
|
||||||
|
assertNotNull(outcome);
|
||||||
|
assertEquals(IValidationProviders.CODE, outcome.getCode());
|
||||||
|
assertEquals(IValidationProviders.DISPLAY, outcome.getDisplay());
|
||||||
|
assertNull(outcome.getSeverity());
|
||||||
|
assertNull(outcome.getMessage());
|
||||||
|
|
||||||
|
assertEquals(IValidationProviders.CODE, myValueSetProvider.getCode());
|
||||||
|
assertEquals(IValidationProviders.DISPLAY, myValueSetProvider.getDisplay());
|
||||||
|
assertEquals(IValidationProviders.VALUE_SET_URL, myValueSetProvider.getValueSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Parameters createParameters(Boolean theResult, String theDisplay, String theMessage, IBaseResource theIssuesResource) {
|
||||||
|
Parameters parameters = new Parameters()
|
||||||
|
.addParameter("code", IValidationProviders.CODE)
|
||||||
|
.addParameter("system", IValidationProviders.CODE_SYSTEM)
|
||||||
|
.addParameter("version", IValidationProviders.CODE_SYSTEM_VERSION)
|
||||||
|
.addParameter("display", theDisplay)
|
||||||
|
.addParameter("message", theMessage);
|
||||||
|
if (theResult != null) {
|
||||||
|
parameters.addParameter("result", theResult);
|
||||||
|
}
|
||||||
|
if (theIssuesResource != null) {
|
||||||
|
parameters.addParameter().setName("issues").setResource((Resource) theIssuesResource);
|
||||||
|
}
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remote terminology services can be used to validate codes when code system is present,
|
||||||
|
* even when inferSystem is true
|
||||||
|
*/
|
||||||
|
@Nested
|
||||||
|
class ExtractCodeSystemFromValueSet {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validateCodeInValueSet_uniqueComposeInclude() {
|
||||||
|
createValueSetReturnParameters(true, IValidationProviders.DISPLAY, null, null);
|
||||||
|
|
||||||
|
ValueSet valueSet = new ValueSet();
|
||||||
|
valueSet.setUrl(IValidationProviders.VALUE_SET_URL);
|
||||||
|
String systemUrl = "http://hl7.org/fhir/ValueSet/administrative-gender";
|
||||||
|
valueSet.setCompose(new ValueSet.ValueSetComposeComponent().setInclude(
|
||||||
|
Collections.singletonList(new ValueSet.ConceptSetComponent().setSystem(systemUrl)) ));
|
||||||
|
|
||||||
|
CodeValidationResult outcome = mySvc.validateCodeInValueSet(null,
|
||||||
|
new ConceptValidationOptions().setInferSystem(true), null, IValidationProviders.CODE, IValidationProviders.DISPLAY, valueSet);
|
||||||
|
|
||||||
|
// validate service doesn't return error message (as when no code system is present)
|
||||||
|
assertNotNull(outcome);
|
||||||
|
assertNull(outcome.getMessage());
|
||||||
|
assertTrue(outcome.isOk());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
public class MultiComposeIncludeValueSet {
|
||||||
|
|
||||||
|
public static Stream<Arguments> getRemoteTerminologyServerExceptions() {
|
||||||
|
return Stream.of(
|
||||||
|
Arguments.of(new ResourceNotFoundException("System Not Present"), "404 Not Found: System Not Present"),
|
||||||
|
Arguments.of(new InvalidRequestException("Invalid Request"), "400 Bad Request: Invalid Request")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource(value = "getRemoteTerminologyServerExceptions")
|
||||||
|
void validateCodeInValueSet_systemNotPresent_returnsValidationResultWithError(Exception theException, String theServerMessage) {
|
||||||
|
myValueSetProvider.setException(theException);
|
||||||
|
createValueSetReturnParameters(true, IValidationProviders.DISPLAY, null, null);
|
||||||
|
|
||||||
|
ValueSet valueSet = new ValueSet();
|
||||||
|
valueSet.setUrl(IValidationProviders.VALUE_SET_URL);
|
||||||
|
valueSet.setCompose(new ValueSet.ValueSetComposeComponent().setInclude(
|
||||||
|
Lists.newArrayList(new ValueSet.ConceptSetComponent(), new ValueSet.ConceptSetComponent())));
|
||||||
|
|
||||||
|
CodeValidationResult outcome = mySvc.validateCodeInValueSet(null,
|
||||||
|
new ConceptValidationOptions().setInferSystem(true), null, IValidationProviders.CODE, IValidationProviders.DISPLAY, valueSet);
|
||||||
|
|
||||||
|
String unknownCodeForValueSetError = "Unknown code \"null#CODE\" for ValueSet with URL \"http://value.set/url\". The Remote Terminology server http://";
|
||||||
|
verifyErrorResultFromException(outcome, unknownCodeForValueSetError, theServerMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource(value = "getRemoteTerminologyServerExceptions")
|
||||||
|
void validateCodeInValueSet_systemPresentCodeNotPresent_returnsValidationResultWithError(Exception theException, String theServerMessage) {
|
||||||
|
myValueSetProvider.setException(theException);
|
||||||
|
createValueSetReturnParameters(true, IValidationProviders.DISPLAY, null, null);
|
||||||
|
|
||||||
|
ValueSet valueSet = new ValueSet();
|
||||||
|
valueSet.setUrl(IValidationProviders.VALUE_SET_URL);
|
||||||
|
String systemUrl = "http://hl7.org/fhir/ValueSet/administrative-gender";
|
||||||
|
String systemUrl2 = "http://hl7.org/fhir/ValueSet/other-valueset";
|
||||||
|
valueSet.setCompose(new ValueSet.ValueSetComposeComponent().setInclude(
|
||||||
|
Lists.newArrayList(
|
||||||
|
new ValueSet.ConceptSetComponent().setSystem(systemUrl),
|
||||||
|
new ValueSet.ConceptSetComponent().setSystem(systemUrl2))));
|
||||||
|
|
||||||
|
CodeValidationResult outcome = mySvc.validateCodeInValueSet(null,
|
||||||
|
new ConceptValidationOptions().setInferSystem(true), null, IValidationProviders.CODE, IValidationProviders.DISPLAY, valueSet);
|
||||||
|
|
||||||
|
String unknownCodeForValueSetError = "Unknown code \"null#CODE\" for ValueSet with URL \"http://value.set/url\". The Remote Terminology server http://";
|
||||||
|
verifyErrorResultFromException(outcome, unknownCodeForValueSetError, theServerMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validateCodeInValueSet_systemPresentCodePresentValidatesOKNoVersioned() {
|
||||||
|
createValueSetReturnParameters(true, IValidationProviders.DISPLAY, null, null);
|
||||||
|
|
||||||
|
ValueSet valueSet = new ValueSet();
|
||||||
|
valueSet.setUrl(IValidationProviders.VALUE_SET_URL);
|
||||||
|
String systemUrl = "http://hl7.org/fhir/ValueSet/administrative-gender";
|
||||||
|
String systemUrl2 = "http://hl7.org/fhir/ValueSet/other-valueset";
|
||||||
|
valueSet.setCompose(new ValueSet.ValueSetComposeComponent().setInclude(
|
||||||
|
Lists.newArrayList(
|
||||||
|
new ValueSet.ConceptSetComponent().setSystem(systemUrl),
|
||||||
|
new ValueSet.ConceptSetComponent().setSystem(systemUrl2).setConcept(
|
||||||
|
Lists.newArrayList(
|
||||||
|
new ValueSet.ConceptReferenceComponent().setCode("not-the-code"),
|
||||||
|
new ValueSet.ConceptReferenceComponent().setCode(IValidationProviders.CODE) )
|
||||||
|
)) ));
|
||||||
|
|
||||||
|
TestClientInterceptor requestInterceptor = new TestClientInterceptor();
|
||||||
|
mySvc.addClientInterceptor(requestInterceptor);
|
||||||
|
|
||||||
|
CodeValidationResult outcome = mySvc.validateCodeInValueSet(null,
|
||||||
|
new ConceptValidationOptions().setInferSystem(true), null, IValidationProviders.CODE, IValidationProviders.DISPLAY, valueSet);
|
||||||
|
|
||||||
|
assertNotNull(outcome);
|
||||||
|
assertEquals(systemUrl2, requestInterceptor.getCapturedSystemParameter());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void validateCodeInValueSet_systemPresentCodePresentValidatesOKVersioned() {
|
||||||
|
createValueSetReturnParameters(true, IValidationProviders.DISPLAY, null, null);
|
||||||
|
|
||||||
|
ValueSet valueSet = new ValueSet();
|
||||||
|
valueSet.setUrl(IValidationProviders.VALUE_SET_URL);
|
||||||
|
String systemUrl = "http://hl7.org/fhir/ValueSet/administrative-gender";
|
||||||
|
String systemVersion = "3.0.2";
|
||||||
|
String systemUrl2 = "http://hl7.org/fhir/ValueSet/other-valueset";
|
||||||
|
String system2Version = "4.0.1";
|
||||||
|
valueSet.setCompose(new ValueSet.ValueSetComposeComponent().setInclude(
|
||||||
|
Lists.newArrayList(
|
||||||
|
new ValueSet.ConceptSetComponent().setSystem(systemUrl).setVersion(systemVersion),
|
||||||
|
new ValueSet.ConceptSetComponent().setSystem(systemUrl2).setVersion(system2Version).setConcept(
|
||||||
|
Lists.newArrayList(
|
||||||
|
new ValueSet.ConceptReferenceComponent().setCode("not-the-code"),
|
||||||
|
new ValueSet.ConceptReferenceComponent().setCode(IValidationProviders.CODE) )
|
||||||
|
)) ));
|
||||||
|
|
||||||
|
TestClientInterceptor requestInterceptor = new TestClientInterceptor();
|
||||||
|
mySvc.addClientInterceptor(requestInterceptor);
|
||||||
|
|
||||||
|
CodeValidationResult outcome = mySvc.validateCodeInValueSet(null,
|
||||||
|
new ConceptValidationOptions().setInferSystem(true), null, IValidationProviders.CODE, IValidationProviders.DISPLAY, valueSet);
|
||||||
|
|
||||||
|
assertNotNull(outcome);
|
||||||
|
assertEquals(systemUrl2 + "|" + system2Version, requestInterceptor.getCapturedSystemParameter());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Captures the system parameter of the request
|
||||||
|
*/
|
||||||
|
private static class TestClientInterceptor implements IClientInterceptor {
|
||||||
|
|
||||||
|
private String capturedSystemParameter;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void interceptRequest(IHttpRequest theRequest) {
|
||||||
|
try {
|
||||||
|
String content = theRequest.getRequestBodyFromStream();
|
||||||
|
if (content != null) {
|
||||||
|
IJsonLikeParser parser = (IJsonLikeParser) ourCtx.newJsonParser();
|
||||||
|
Parameters params = parser.parseResource(Parameters.class, content);
|
||||||
|
List<String> systemValues = ParametersUtil.getNamedParameterValuesAsString(
|
||||||
|
ourCtx, params, "system");
|
||||||
|
assertThat(systemValues).hasSize(1);
|
||||||
|
capturedSystemParameter = systemValues.get(0);
|
||||||
|
}
|
||||||
|
} catch (IOException theE) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void interceptResponse(IHttpResponse theResponse) { }
|
||||||
|
|
||||||
|
public String getCapturedSystemParameter() { return capturedSystemParameter; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -48,12 +48,15 @@ import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor;
|
||||||
import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher;
|
import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher;
|
||||||
import org.hl7.fhir.r5.utils.validation.constants.BestPracticeWarningLevel;
|
import org.hl7.fhir.r5.utils.validation.constants.BestPracticeWarningLevel;
|
||||||
import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy;
|
import org.hl7.fhir.r5.utils.validation.constants.ReferenceValidationPolicy;
|
||||||
|
import org.hl7.fhir.utilities.validation.ValidationMessage;
|
||||||
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
|
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
|
||||||
import org.junit.jupiter.api.AfterAll;
|
import org.junit.jupiter.api.AfterAll;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Disabled;
|
import org.junit.jupiter.api.Disabled;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
import org.mockito.invocation.InvocationOnMock;
|
import org.mockito.invocation.InvocationOnMock;
|
||||||
import org.mockito.stubbing.Answer;
|
import org.mockito.stubbing.Answer;
|
||||||
|
|
||||||
|
@ -97,10 +100,19 @@ public class FhirInstanceValidatorR5Test extends BaseValidationTestWithInlineMoc
|
||||||
|
|
||||||
private Set<String> mySupportedValueSets = new HashSet<>();
|
private Set<String> mySupportedValueSets = new HashSet<>();
|
||||||
private Set<String> myValidSystems = new HashSet<>();
|
private Set<String> myValidSystems = new HashSet<>();
|
||||||
|
private Set<String> myValidSystemsNotReturningIssues = new HashSet<>();
|
||||||
private CachingValidationSupport myValidationSupport;
|
private CachingValidationSupport myValidationSupport;
|
||||||
|
|
||||||
private void addValidConcept(String theSystem, String theCode) {
|
private void addValidConcept(String theSystem, String theCode) {
|
||||||
myValidSystems.add(theSystem);
|
addValidConcept(theSystem, theCode, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addValidConcept(String theSystem, String theCode, boolean theShouldSystemReturnIssuesForInvalidCode) {
|
||||||
|
if (theShouldSystemReturnIssuesForInvalidCode) {
|
||||||
|
myValidSystems.add(theSystem);
|
||||||
|
} else {
|
||||||
|
myValidSystemsNotReturningIssues.add(theSystem);
|
||||||
|
}
|
||||||
myValidConcepts.add(theSystem + "___" + theCode);
|
myValidConcepts.add(theSystem + "___" + theCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,7 +200,10 @@ public class FhirInstanceValidatorR5Test extends BaseValidationTestWithInlineMoc
|
||||||
retVal = new IValidationSupport.CodeValidationResult().setCode(code);
|
retVal = new IValidationSupport.CodeValidationResult().setCode(code);
|
||||||
} else if (myValidSystems.contains(system)) {
|
} else if (myValidSystems.contains(system)) {
|
||||||
String theMessage = "Unknown code (for '" + system + "#" + code + "')";
|
String theMessage = "Unknown code (for '" + system + "#" + code + "')";
|
||||||
return new IValidationSupport.CodeValidationResult().setSeverity(IValidationSupport.IssueSeverity.ERROR).setMessage(theMessage).setCodeValidationIssues(Collections.singletonList(new IValidationSupport.CodeValidationIssue(theMessage, IValidationSupport.IssueSeverity.ERROR, IValidationSupport.CodeValidationIssueCode.CODE_INVALID, IValidationSupport.CodeValidationIssueCoding.INVALID_CODE)));
|
retVal = new IValidationSupport.CodeValidationResult().setSeverity(IValidationSupport.IssueSeverity.ERROR).setMessage(theMessage).setCodeValidationIssues(Collections.singletonList(new IValidationSupport.CodeValidationIssue(theMessage, IValidationSupport.IssueSeverity.ERROR, IValidationSupport.CodeValidationIssueCode.CODE_INVALID, IValidationSupport.CodeValidationIssueCoding.INVALID_CODE)));
|
||||||
|
} else if (myValidSystemsNotReturningIssues.contains(system)) {
|
||||||
|
final String message = "Unknown code (for '" + system + "#" + code + "')";
|
||||||
|
retVal = new IValidationSupport.CodeValidationResult().setSeverityCode(ValidationMessage.IssueSeverity.ERROR.toCode()).setMessage(message);
|
||||||
} else {
|
} else {
|
||||||
retVal = myDefaultValidationSupport.validateCode(new ValidationSupportContext(myDefaultValidationSupport), options, system, code, display, valueSetUrl);
|
retVal = myDefaultValidationSupport.validateCode(new ValidationSupportContext(myDefaultValidationSupport), options, system, code, display, valueSetUrl);
|
||||||
}
|
}
|
||||||
|
@ -788,9 +803,11 @@ public class FhirInstanceValidatorR5Test extends BaseValidationTestWithInlineMoc
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
// TODO: uncomment value false when https://github.com/hapifhir/org.hl7.fhir.core/issues/1766 is fixed
|
||||||
public void testValidateResourceContainingLoincCode() {
|
@ParameterizedTest
|
||||||
addValidConcept("http://loinc.org", "1234567");
|
@ValueSource(booleans = {true, /*false*/})
|
||||||
|
public void testValidateResourceContainingLoincCode(boolean theShouldSystemReturnIssuesForInvalidCode) {
|
||||||
|
addValidConcept("http://loinc.org", "1234567", theShouldSystemReturnIssuesForInvalidCode);
|
||||||
|
|
||||||
Observation input = new Observation();
|
Observation input = new Observation();
|
||||||
// input.getMeta().addProfile("http://hl7.org/fhir/StructureDefinition/devicemetricobservation");
|
// input.getMeta().addProfile("http://hl7.org/fhir/StructureDefinition/devicemetricobservation");
|
||||||
|
@ -806,7 +823,6 @@ public class FhirInstanceValidatorR5Test extends BaseValidationTestWithInlineMoc
|
||||||
|
|
||||||
ourLog.debug(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(input));
|
ourLog.debug(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(input));
|
||||||
|
|
||||||
|
|
||||||
assertEquals(ResultSeverityEnum.ERROR, errors.get(0).getSeverity());
|
assertEquals(ResultSeverityEnum.ERROR, errors.get(0).getSeverity());
|
||||||
assertEquals("Unknown code (for 'http://loinc.org#12345')", errors.get(0).getMessage());
|
assertEquals("Unknown code (for 'http://loinc.org#12345')", errors.get(0).getMessage());
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"resourceType": "OperationOutcome",
|
||||||
|
"issue": [
|
||||||
|
{
|
||||||
|
"severity": "error",
|
||||||
|
"code": "code-invalid",
|
||||||
|
"details": {
|
||||||
|
"coding": [
|
||||||
|
{
|
||||||
|
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
|
||||||
|
"code": "invalid-code"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"text": "Unknown code 'CODE' in the CodeSystem 'http://code.system/url' version '1.0.0'"
|
||||||
|
},
|
||||||
|
"location": [
|
||||||
|
"code"
|
||||||
|
],
|
||||||
|
"expression": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
{
|
||||||
|
"resourceType": "OperationOutcome",
|
||||||
|
"issue": [
|
||||||
|
{
|
||||||
|
"severity": "error",
|
||||||
|
"code": "code-invalid",
|
||||||
|
"details": {
|
||||||
|
"coding": [
|
||||||
|
{
|
||||||
|
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
|
||||||
|
"code": "not-in-vs"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"text": "The provided code 'http://code.system/url#CODE' was not found in the value set 'http://value.set/url%7C1.0.0'"
|
||||||
|
},
|
||||||
|
"location": [
|
||||||
|
"code"
|
||||||
|
],
|
||||||
|
"expression": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"severity": "error",
|
||||||
|
"code": "code-invalid",
|
||||||
|
"details": {
|
||||||
|
"coding": [
|
||||||
|
{
|
||||||
|
"system": "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type",
|
||||||
|
"code": "invalid-code"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"text": "Unknown code 'CODE' in the CodeSystem 'http://code.system/url' version '1.0.0'"
|
||||||
|
},
|
||||||
|
"location": [
|
||||||
|
"code"
|
||||||
|
],
|
||||||
|
"expression": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in New Issue