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;
|
||||
}
|
||||
|
||||
String getCodeSystemName() {
|
||||
public String getCodeSystemName() {
|
||||
return myCodeSystemName;
|
||||
}
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@ import java.util.ArrayList;
|
|||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -58,20 +59,20 @@ public class ParametersUtil {
|
|||
public static Optional<String> getNamedParameterValueAsString(
|
||||
FhirContext theCtx, IBaseParameters theParameters, String theParameterName) {
|
||||
Function<IPrimitiveType<?>, String> mapper = t -> defaultIfBlank(t.getValueAsString(), null);
|
||||
return extractNamedParameters(theCtx, theParameters, theParameterName, mapper).stream()
|
||||
return extractNamedParameterValues(theCtx, theParameters, theParameterName, mapper).stream()
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
public static List<String> getNamedParameterValuesAsString(
|
||||
FhirContext theCtx, IBaseParameters theParameters, String theParameterName) {
|
||||
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(
|
||||
FhirContext theCtx, IBaseParameters theParameters, String theParameterName) {
|
||||
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(
|
||||
|
@ -80,6 +81,19 @@ public class ParametersUtil {
|
|||
.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(
|
||||
FhirContext theCtx, IBaseResource theParameters, String theParameterName) {
|
||||
return getNamedParameters(theCtx, theParameters, theParameterName).stream()
|
||||
|
@ -153,7 +167,7 @@ public class ParametersUtil {
|
|||
.map(t -> (Integer) t);
|
||||
}
|
||||
|
||||
private static <T> List<T> extractNamedParameters(
|
||||
private static <T> List<T> extractNamedParameterValues(
|
||||
FhirContext theCtx,
|
||||
IBaseParameters theParameters,
|
||||
String theParameterName,
|
||||
|
@ -170,7 +184,25 @@ public class ParametersUtil {
|
|||
.filter(t -> t instanceof IPrimitiveType<?>)
|
||||
.map(t -> ((IPrimitiveType<?>) t))
|
||||
.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);
|
||||
}
|
||||
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;
|
||||
|
||||
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.jpa.config.JpaConfig;
|
||||
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.exceptions.InvalidRequestException;
|
||||
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.ValidationSupportChain;
|
||||
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.Qualifier;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
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.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}
|
||||
* into the ValidationSupportChain, which tests the logic of dynamically selecting the correct Remote Terminology
|
||||
* implementation. It also exercises the code found in
|
||||
* {@link org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport#invokeRemoteValidateCode}
|
||||
*/
|
||||
public class ResourceProviderR4RemoteTerminologyTest extends BaseResourceProviderR4Test {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderR4RemoteTerminologyTest.class);
|
||||
public class ValidateCodeOperationWithRemoteTerminologyR4Test extends BaseResourceProviderR4Test {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ValidateCodeOperationWithRemoteTerminologyR4Test.class);
|
||||
private static final String DISPLAY = "DISPLAY";
|
||||
private static final String DISPLAY_BODY_MASS_INDEX = "Body mass index (BMI) [Ratio]";
|
||||
private static final String CODE_BODY_MASS_INDEX = "39156-5";
|
||||
private static 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 UNKNOWN_VALUE_SYSTEM_URI = "http://hl7.org/fhir/ValueSet/unknown-value-set";
|
||||
private static FhirContext ourCtx = FhirContext.forR4();
|
||||
private MyCodeSystemProvider myCodeSystemProvider = new MyCodeSystemProvider();
|
||||
private MyValueSetProvider myValueSetProvider = new MyValueSetProvider();
|
||||
private static final FhirContext ourCtx = FhirContext.forR4();
|
||||
|
||||
@RegisterExtension
|
||||
public RestfulServerExtension myRestfulServerExtension = new RestfulServerExtension(ourCtx, myCodeSystemProvider,
|
||||
myValueSetProvider);
|
||||
protected static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx);
|
||||
|
||||
private RemoteTerminologyServiceValidationSupport mySvc;
|
||||
private MyCodeSystemProvider myCodeSystemProvider;
|
||||
private MyValueSetProvider myValueSetProvider;
|
||||
|
||||
@Autowired
|
||||
@Qualifier(JpaConfig.JPA_VALIDATION_SUPPORT_CHAIN)
|
||||
private ValidationSupportChain myValidationSupportChain;
|
||||
|
||||
@BeforeEach
|
||||
public void before_addRemoteTerminologySupport() throws Exception {
|
||||
String baseUrl = "http://localhost:" + myRestfulServerExtension.getPort();
|
||||
public void before() throws Exception {
|
||||
String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
|
||||
mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx, baseUrl);
|
||||
myValidationSupportChain.addValidationSupport(0, mySvc);
|
||||
myCodeSystemProvider = new MyCodeSystemProvider();
|
||||
myValueSetProvider = new MyValueSetProvider();
|
||||
ourRestfulServerExtension.registerProvider(myCodeSystemProvider);
|
||||
ourRestfulServerExtension.registerProvider(myValueSetProvider);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void after_removeRemoteTerminologySupport() {
|
||||
public void after() {
|
||||
myValidationSupportChain.removeValidationSupport(mySvc);
|
||||
myRestfulServerExtension.getRestfulServer().getInterceptorService().unregisterAllInterceptors();
|
||||
ourRestfulServerExtension.getRestfulServer().getInterceptorService().unregisterAllInterceptors();
|
||||
ourRestfulServerExtension.unregisterProvider(myCodeSystemProvider);
|
||||
ourRestfulServerExtension.unregisterProvider(myValueSetProvider);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateCodeOperationOnCodeSystem_byCodingAndUrlWhereSystemIsDifferent_throwsException() {
|
||||
assertThatExceptionOfType(InvalidRequestException.class).isThrownBy(() -> {
|
||||
Parameters respParam = myClient
|
||||
public void validateCodeOperationOnCodeSystem_byCodingAndUrlWhereSystemIsDifferent_throwsException() {
|
||||
assertThatExceptionOfType(InvalidRequestException.class).isThrownBy(() -> myClient
|
||||
.operation()
|
||||
.onType(CodeSystem.class)
|
||||
.named(JpaConstants.OPERATION_VALIDATE_CODE)
|
||||
.withParameter(Parameters.class, "coding", new Coding().setSystem(CODE_SYSTEM_V2_0247_URI).setCode("P"))
|
||||
.andParameter("url", new UriType(INVALID_CODE_SYSTEM_URI))
|
||||
.execute();
|
||||
});
|
||||
.execute());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateCodeOperationOnCodeSystem_byCodingAndUrl_usingBuiltInCodeSystems() {
|
||||
myCodeSystemProvider.myNextReturnCodeSystems = new ArrayList<>();
|
||||
myCodeSystemProvider.myNextReturnCodeSystems.add((CodeSystem) new CodeSystem().setId("CodeSystem/v2-0247"));
|
||||
createNextCodeSystemReturnParameters(true, DISPLAY, null);
|
||||
public void validateCodeOperationOnCodeSystem_byCodingAndUrl_usingBuiltInCodeSystems() {
|
||||
myCodeSystemProvider.myReturnCodeSystems = new ArrayList<>();
|
||||
myCodeSystemProvider.myReturnCodeSystems.add((CodeSystem) new CodeSystem().setId("CodeSystem/v2-0247"));
|
||||
myCodeSystemProvider.myReturnParams = new Parameters();
|
||||
myCodeSystemProvider.myReturnParams.addParameter("result", true);
|
||||
myCodeSystemProvider.myReturnParams.addParameter("display", DISPLAY);
|
||||
|
||||
logAllConcepts();
|
||||
|
||||
|
@ -116,13 +122,13 @@ public class ResourceProviderR4RemoteTerminologyTest extends BaseResourceProvide
|
|||
String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertEquals(true, ((BooleanType) respParam.getParameterValue("result")).booleanValue());
|
||||
assertTrue(((BooleanType) respParam.getParameterValue("result")).booleanValue());
|
||||
assertEquals(DISPLAY, respParam.getParameterValue("display").toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateCodeOperationOnCodeSystem_byCodingAndUrlWhereCodeSystemIsUnknown_returnsFalse() {
|
||||
myCodeSystemProvider.myNextReturnCodeSystems = new ArrayList<>();
|
||||
public void validateCodeOperationOnCodeSystem_byCodingAndUrlWhereCodeSystemIsUnknown_returnsFalse() {
|
||||
myCodeSystemProvider.myReturnCodeSystems = new ArrayList<>();
|
||||
|
||||
Parameters respParam = myClient
|
||||
.operation()
|
||||
|
@ -142,7 +148,7 @@ public class ResourceProviderR4RemoteTerminologyTest extends BaseResourceProvide
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testValidateCodeOperationOnValueSet_byCodingAndUrlWhereSystemIsDifferent_throwsException() {
|
||||
public void validateCodeOperationOnValueSet_byCodingAndUrlWhereSystemIsDifferent_throwsException() {
|
||||
try {
|
||||
myClient.operation()
|
||||
.onType(ValueSet.class)
|
||||
|
@ -159,12 +165,14 @@ public class ResourceProviderR4RemoteTerminologyTest extends BaseResourceProvide
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testValidateCodeOperationOnValueSet_byUrlAndSystem_usingBuiltInCodeSystems() {
|
||||
myCodeSystemProvider.myNextReturnCodeSystems = new ArrayList<>();
|
||||
myCodeSystemProvider.myNextReturnCodeSystems.add((CodeSystem) new CodeSystem().setId("CodeSystem/list-example-use-codes"));
|
||||
myValueSetProvider.myNextReturnValueSets = new ArrayList<>();
|
||||
myValueSetProvider.myNextReturnValueSets.add((ValueSet) new ValueSet().setId("ValueSet/list-example-codes"));
|
||||
createNextValueSetReturnParameters(true, DISPLAY, null);
|
||||
public void validateCodeOperationOnValueSet_byUrlAndSystem_usingBuiltInCodeSystems() {
|
||||
myCodeSystemProvider.myReturnCodeSystems = new ArrayList<>();
|
||||
myCodeSystemProvider.myReturnCodeSystems.add((CodeSystem) new CodeSystem().setId("CodeSystem/list-example-use-codes"));
|
||||
myValueSetProvider.myReturnValueSets = new ArrayList<>();
|
||||
myValueSetProvider.myReturnValueSets.add((ValueSet) new ValueSet().setId("ValueSet/list-example-codes"));
|
||||
myValueSetProvider.myReturnParams = new Parameters();
|
||||
myValueSetProvider.myReturnParams.addParameter("result", true);
|
||||
myValueSetProvider.myReturnParams.addParameter("display", DISPLAY);
|
||||
|
||||
Parameters respParam = myClient
|
||||
.operation()
|
||||
|
@ -179,17 +187,19 @@ public class ResourceProviderR4RemoteTerminologyTest extends BaseResourceProvide
|
|||
String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
ourLog.info(resp);
|
||||
|
||||
assertEquals(true, ((BooleanType) respParam.getParameterValue("result")).booleanValue());
|
||||
assertTrue(((BooleanType) respParam.getParameterValue("result")).booleanValue());
|
||||
assertEquals(DISPLAY, respParam.getParameterValue("display").toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateCodeOperationOnValueSet_byUrlSystemAndCode() {
|
||||
myCodeSystemProvider.myNextReturnCodeSystems = new ArrayList<>();
|
||||
myCodeSystemProvider.myNextReturnCodeSystems.add((CodeSystem) new CodeSystem().setId("CodeSystem/list-example-use-codes"));
|
||||
myValueSetProvider.myNextReturnValueSets = new ArrayList<>();
|
||||
myValueSetProvider.myNextReturnValueSets.add((ValueSet) new ValueSet().setId("ValueSet/list-example-codes"));
|
||||
createNextValueSetReturnParameters(true, DISPLAY_BODY_MASS_INDEX, null);
|
||||
public void validateCodeOperationOnValueSet_byUrlSystemAndCode() {
|
||||
myCodeSystemProvider.myReturnCodeSystems = new ArrayList<>();
|
||||
myCodeSystemProvider.myReturnCodeSystems.add((CodeSystem) new CodeSystem().setId("CodeSystem/list-example-use-codes"));
|
||||
myValueSetProvider.myReturnValueSets = new ArrayList<>();
|
||||
myValueSetProvider.myReturnValueSets.add((ValueSet) new ValueSet().setId("ValueSet/list-example-codes"));
|
||||
myValueSetProvider.myReturnParams = new Parameters();
|
||||
myValueSetProvider.myReturnParams.addParameter("result", true);
|
||||
myValueSetProvider.myReturnParams.addParameter("display", DISPLAY_BODY_MASS_INDEX);
|
||||
|
||||
Parameters respParam = myClient
|
||||
.operation()
|
||||
|
@ -203,13 +213,13 @@ public class ResourceProviderR4RemoteTerminologyTest extends BaseResourceProvide
|
|||
String resp = myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||
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());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateCodeOperationOnValueSet_byCodingAndUrlWhereValueSetIsUnknown_returnsFalse() {
|
||||
myValueSetProvider.myNextReturnValueSets = new ArrayList<>();
|
||||
public void validateCodeOperationOnValueSet_byCodingAndUrlWhereValueSetIsUnknown_returnsFalse() {
|
||||
myValueSetProvider.myReturnValueSets = new ArrayList<>();
|
||||
|
||||
Parameters respParam = myClient
|
||||
.operation()
|
||||
|
@ -228,33 +238,10 @@ public class ResourceProviderR4RemoteTerminologyTest extends BaseResourceProvide
|
|||
" - Unknown or unusable ValueSet[" + UNKNOWN_VALUE_SYSTEM_URI + "]");
|
||||
}
|
||||
|
||||
private void createNextCodeSystemReturnParameters(boolean theResult, String theDisplay, String theMessage) {
|
||||
myCodeSystemProvider.myNextReturnParams = new Parameters();
|
||||
myCodeSystemProvider.myNextReturnParams.addParameter("result", theResult);
|
||||
myCodeSystemProvider.myNextReturnParams.addParameter("display", theDisplay);
|
||||
if (theMessage != null) {
|
||||
myCodeSystemProvider.myNextReturnParams.addParameter("message", theMessage);
|
||||
}
|
||||
}
|
||||
|
||||
private void createNextValueSetReturnParameters(boolean theResult, String theDisplay, String theMessage) {
|
||||
myValueSetProvider.myNextReturnParams = new Parameters();
|
||||
myValueSetProvider.myNextReturnParams.addParameter("result", theResult);
|
||||
myValueSetProvider.myNextReturnParams.addParameter("display", theDisplay);
|
||||
if (theMessage != null) {
|
||||
myValueSetProvider.myNextReturnParams.addParameter("message", theMessage);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class MyCodeSystemProvider implements IResourceProvider {
|
||||
|
||||
private UriParam myLastUrlParam;
|
||||
private List<CodeSystem> myNextReturnCodeSystems;
|
||||
private int myInvocationCount;
|
||||
private UriType myLastUrl;
|
||||
private CodeType myLastCode;
|
||||
private StringType myLastDisplay;
|
||||
private Parameters myNextReturnParams;
|
||||
private List<CodeSystem> myReturnCodeSystems;
|
||||
private Parameters myReturnParams;
|
||||
|
||||
@Operation(name = "validate-code", idempotent = true, returnParameters = {
|
||||
@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 = "display", min = 0, max = 1) StringType theDisplay
|
||||
) {
|
||||
myInvocationCount++;
|
||||
myLastUrl = theCodeSystemUrl;
|
||||
myLastCode = theCode;
|
||||
myLastDisplay = theDisplay;
|
||||
return myNextReturnParams;
|
||||
return myReturnParams;
|
||||
}
|
||||
|
||||
@Search
|
||||
public List<CodeSystem> find(@RequiredParam(name = "url") UriParam theUrlParam) {
|
||||
myLastUrlParam = theUrlParam;
|
||||
assert myNextReturnCodeSystems != null;
|
||||
return myNextReturnCodeSystems;
|
||||
assert myReturnCodeSystems != null;
|
||||
return myReturnCodeSystems;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -288,16 +270,10 @@ public class ResourceProviderR4RemoteTerminologyTest extends BaseResourceProvide
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class MyValueSetProvider implements IResourceProvider {
|
||||
private Parameters myNextReturnParams;
|
||||
private List<ValueSet> myNextReturnValueSets;
|
||||
private UriType myLastUrl;
|
||||
private CodeType myLastCode;
|
||||
private int myInvocationCount;
|
||||
private UriType myLastSystem;
|
||||
private StringType myLastDisplay;
|
||||
private ValueSet myLastValueSet;
|
||||
private UriParam myLastUrlParam;
|
||||
private Parameters myReturnParams;
|
||||
private List<ValueSet> myReturnValueSets;
|
||||
|
||||
@Operation(name = "validate-code", idempotent = true, returnParameters = {
|
||||
@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 = "valueSet") ValueSet theValueSet
|
||||
) {
|
||||
myInvocationCount++;
|
||||
myLastUrl = theValueSetUrl;
|
||||
myLastCode = theCode;
|
||||
myLastSystem = theSystem;
|
||||
myLastDisplay = theDisplay;
|
||||
myLastValueSet = theValueSet;
|
||||
return myNextReturnParams;
|
||||
return myReturnParams;
|
||||
}
|
||||
|
||||
@Search
|
||||
public List<ValueSet> find(@RequiredParam(name = "url") UriParam theUrlParam) {
|
||||
myLastUrlParam = theUrlParam;
|
||||
assert myNextReturnValueSets != null;
|
||||
return myNextReturnValueSets;
|
||||
assert myReturnValueSets != null;
|
||||
return myReturnValueSets;
|
||||
}
|
||||
|
||||
@Override
|
|
@ -3,7 +3,9 @@ package ca.uhn.fhir.util;
|
|||
import ca.uhn.fhir.context.FhirContext;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
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.OperationOutcome;
|
||||
import org.hl7.fhir.r4.model.Parameters;
|
||||
import org.hl7.fhir.r4.model.StringType;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
@ -17,7 +19,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
|||
|
||||
public class ParametersUtilR4Test {
|
||||
private static final String TEST_PERSON_ID = "Person/32768";
|
||||
private static FhirContext ourFhirContext = FhirContext.forR4();
|
||||
private static final FhirContext ourFhirContext = FhirContext.forR4Cached();
|
||||
|
||||
@Test
|
||||
public void testCreateParameters() {
|
||||
|
@ -47,7 +49,7 @@ public class ParametersUtilR4Test {
|
|||
p.addParameter()
|
||||
.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");
|
||||
}
|
||||
|
||||
|
@ -58,7 +60,7 @@ public class ParametersUtilR4Test {
|
|||
.setName("foo")
|
||||
.setValue(new IntegerType(123));
|
||||
|
||||
Optional<Integer> value = ParametersUtil.getNamedParameterValueAsInteger(FhirContext.forR4(), p, "foo");
|
||||
Optional<Integer> value = ParametersUtil.getNamedParameterValueAsInteger(ourFhirContext, p, "foo");
|
||||
assertThat(value).isPresent();
|
||||
assertEquals(123, value.get().intValue());
|
||||
}
|
||||
|
@ -80,7 +82,7 @@ public class ParametersUtilR4Test {
|
|||
@Test
|
||||
public void testAddPartDecimalNoScientificNotation() {
|
||||
// setup
|
||||
Double decimalValue = Double.valueOf("10000000");
|
||||
double decimalValue = 10000000;
|
||||
IBaseParameters parameters = ParametersUtil.newInstance(ourFhirContext);
|
||||
IBase resultPart = ParametersUtil.addParameterToParameters(ourFhirContext, parameters, "link");
|
||||
|
||||
|
@ -92,4 +94,20 @@ public class ParametersUtilR4Test {
|
|||
List<String> results = ParametersUtil.getNamedParameterPartAsString(ourFhirContext, parameters, "link", "linkCreated");
|
||||
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.hl7.fhir.instance.model.api.IBaseBundle;
|
||||
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.IBaseResource;
|
||||
import org.hl7.fhir.r4.model.Base;
|
||||
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.OperationOutcome;
|
||||
import org.hl7.fhir.r4.model.Parameters;
|
||||
import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent;
|
||||
import org.hl7.fhir.r4.model.Property;
|
||||
|
@ -37,9 +39,14 @@ import org.slf4j.Logger;
|
|||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
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.isNotBlank;
|
||||
|
||||
|
@ -52,6 +59,9 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|||
public class RemoteTerminologyServiceValidationSupport extends BaseValidationSupport implements IValidationSupport {
|
||||
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 final List<Object> myClientInterceptors = new ArrayList<>();
|
||||
|
||||
|
@ -192,8 +202,8 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
|
|||
// e.g. ClientResponseInterceptorModificationTemplate
|
||||
ourLog.error(e.getMessage(), e);
|
||||
LookupCodeResult result = LookupCodeResult.notFound(system, code);
|
||||
result.setErrorMessage(
|
||||
getErrorMessage("unknownCodeInSystem", system, code, client.getServerBase(), e.getMessage()));
|
||||
result.setErrorMessage(getErrorMessage(
|
||||
ERROR_CODE_UNKNOWN_CODE_IN_CODE_SYSTEM, system, code, getBaseUrl(), e.getMessage()));
|
||||
return result;
|
||||
}
|
||||
if (outcome != null && !outcome.isEmpty()) {
|
||||
|
@ -575,6 +585,21 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
|
|||
|
||||
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 =
|
||||
buildValidateCodeInputParameters(theCodeSystem, theCode, theDisplay, theValueSetUrl, theValueSet);
|
||||
|
||||
|
@ -583,93 +608,167 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
|
|||
resourceType = "CodeSystem";
|
||||
}
|
||||
|
||||
IBaseParameters output;
|
||||
try {
|
||||
output = client.operation()
|
||||
IBaseParameters output = client.operation()
|
||||
.onType(resourceType)
|
||||
.named("validate-code")
|
||||
.withParameters(input)
|
||||
.execute();
|
||||
return createCodeValidationResult(output, errorMessageBuilder, theCode);
|
||||
} catch (ResourceNotFoundException | InvalidRequestException ex) {
|
||||
ourLog.error(ex.getMessage(), ex);
|
||||
CodeValidationResult result = new CodeValidationResult();
|
||||
result.setSeverity(IssueSeverity.ERROR);
|
||||
String errorMessage = buildErrorMessage(
|
||||
theCodeSystem, theCode, theValueSetUrl, theValueSet, client.getServerBase(), ex.getMessage());
|
||||
result.setMessage(errorMessage);
|
||||
String errorMessage = errorMessageBuilder.buildErrorMessage(ex.getMessage());
|
||||
CodeValidationIssueCode issueCode = ex instanceof ResourceNotFoundException
|
||||
? CodeValidationIssueCode.NOT_FOUND
|
||||
: CodeValidationIssueCode.CODE_INVALID;
|
||||
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;
|
||||
}
|
||||
|
||||
List<String> resultValues = ParametersUtil.getNamedParameterValuesAsString(getFhirContext(), output, "result");
|
||||
if (resultValues.isEmpty() || isBlank(resultValues.get(0))) {
|
||||
return null;
|
||||
}
|
||||
Validate.isTrue(resultValues.size() == 1, "Response contained %d 'result' values", resultValues.size());
|
||||
// for now assume severity ERROR, we may need to process the following for success cases as well
|
||||
result.setSeverity(IssueSeverity.ERROR);
|
||||
|
||||
boolean success = "true".equalsIgnoreCase(resultValues.get(0));
|
||||
|
||||
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<String> messageValue = getNamedParameterValueAsString(fhirContext, theOutput, "message");
|
||||
messageValue.ifPresent(value -> result.setMessage(theMessageBuilder.buildErrorMessage(value)));
|
||||
|
||||
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 {
|
||||
|
||||
retVal.setSeverity(IssueSeverity.ERROR);
|
||||
List<String> messageValues =
|
||||
ParametersUtil.getNamedParameterValuesAsString(getFhirContext(), output, "message");
|
||||
if (!messageValues.isEmpty()) {
|
||||
retVal.setMessage(messageValues.get(0));
|
||||
}
|
||||
// create a validation issue out of the message
|
||||
// this is a workaround to overcome an issue in the FHIR Validator library
|
||||
// where ValueSet bindings are only reading issues but not messages
|
||||
// @see https://github.com/hapifhir/org.hl7.fhir.core/issues/1766
|
||||
result.addCodeValidationIssue(createCodeValidationIssue(result.getMessage()));
|
||||
}
|
||||
return retVal;
|
||||
return result;
|
||||
}
|
||||
|
||||
private String buildErrorMessage(
|
||||
String theCodeSystem,
|
||||
String theCode,
|
||||
String theValueSetUrl,
|
||||
IBaseResource theValueSet,
|
||||
String theServerUrl,
|
||||
String theServerMessage) {
|
||||
if (theValueSetUrl == null && theValueSet == null) {
|
||||
return getErrorMessage("unknownCodeInSystem", theCodeSystem, theCode, theServerUrl, theServerMessage);
|
||||
} else {
|
||||
return getErrorMessage(
|
||||
"unknownCodeInValueSet", theCodeSystem, theCode, theValueSetUrl, theServerUrl, theServerMessage);
|
||||
/**
|
||||
* Creates a list of {@link ca.uhn.fhir.context.support.IValidationSupport.CodeValidationIssue} from the issues
|
||||
* returned by the $validate-code operation.
|
||||
* Please note that this method should only be used for Remote Terminology for now as it only translates
|
||||
* issues text/message and assumes all other fields.
|
||||
* When issues will be supported across all validators in hapi-fhir, a proper generic conversion method should
|
||||
* be available and this method will be deleted.
|
||||
*
|
||||
* @param theOperationOutcome the outcome of the $validate-code operation
|
||||
* @param theFhirVersion the FHIR version
|
||||
* @return the list of {@link ca.uhn.fhir.context.support.IValidationSupport.CodeValidationIssue}
|
||||
*/
|
||||
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(
|
||||
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) {
|
||||
ParametersUtil.addParameterToParametersUri(getFhirContext(), params, "url", theCodeSystem);
|
||||
ParametersUtil.addParameterToParametersString(getFhirContext(), params, "code", theCode);
|
||||
ParametersUtil.addParameterToParametersUri(fhirContext, params, "url", theCodeSystem);
|
||||
ParametersUtil.addParameterToParametersString(fhirContext, params, "code", theCode);
|
||||
if (isNotBlank(theDisplay)) {
|
||||
ParametersUtil.addParameterToParametersString(getFhirContext(), params, "display", theDisplay);
|
||||
ParametersUtil.addParameterToParametersString(fhirContext, params, "display", theDisplay);
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
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)) {
|
||||
ParametersUtil.addParameterToParametersUri(getFhirContext(), params, "system", theCodeSystem);
|
||||
ParametersUtil.addParameterToParametersUri(fhirContext, params, "system", theCodeSystem);
|
||||
}
|
||||
if (isNotBlank(theDisplay)) {
|
||||
ParametersUtil.addParameterToParametersString(getFhirContext(), params, "display", theDisplay);
|
||||
ParametersUtil.addParameterToParametersString(fhirContext, params, "display", theDisplay);
|
||||
}
|
||||
if (theValueSet != null) {
|
||||
ParametersUtil.addParameterToParameters(getFhirContext(), params, "valueSet", theValueSet);
|
||||
ParametersUtil.addParameterToParameters(fhirContext, params, "valueSet", theValueSet);
|
||||
}
|
||||
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.StringConceptProperty;
|
||||
import ca.uhn.fhir.context.support.LookupCodeRequest;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import org.hl7.fhir.instance.model.api.IBaseDatatype;
|
||||
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 java.util.stream.IntStream.range;
|
||||
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.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
|
@ -40,20 +45,15 @@ import static org.junit.jupiter.api.Assertions.fail;
|
|||
* e.g. assertEqualConceptProperty
|
||||
*/
|
||||
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();
|
||||
IMyCodeSystemProvider getCodeSystemProvider();
|
||||
IValidationProviders.IMyLookupCodeProvider getLookupCodeProvider();
|
||||
|
||||
@Test
|
||||
default void lookupCode_forCodeSystemWithBlankCode_throwsException() {
|
||||
IValidationSupport service = getService();
|
||||
LookupCodeRequest request = new LookupCodeRequest(CODE_SYSTEM, "");
|
||||
try {
|
||||
getService().lookupCode(null, new LookupCodeRequest(CODE_SYSTEM, ""));
|
||||
service.lookupCode(null, request);
|
||||
fail();
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertEquals("theCode must be provided", e.getMessage());
|
||||
|
@ -70,11 +70,14 @@ public interface ILookupCodeTest {
|
|||
return "someUnsupportedType";
|
||||
}
|
||||
});
|
||||
getCodeSystemProvider().setLookupCodeResult(result);
|
||||
getLookupCodeProvider().setLookupCodeResult(result);
|
||||
|
||||
IValidationSupport service = getService();
|
||||
LookupCodeRequest request = new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, null);
|
||||
|
||||
// test and verify
|
||||
try {
|
||||
getService().lookupCode(null, new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, null));
|
||||
service.lookupCode(null, request);
|
||||
fail();
|
||||
} catch (InternalErrorException e) {
|
||||
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.setCodeSystemDisplayName(CODE_SYSTEM_NAME);
|
||||
result.setCodeDisplay(DISPLAY);
|
||||
getCodeSystemProvider().setLookupCodeResult(result);
|
||||
getLookupCodeProvider().setLookupCodeResult(result);
|
||||
|
||||
// test and verify
|
||||
LookupCodeRequest request = new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, null);
|
||||
|
@ -107,7 +110,7 @@ public interface ILookupCodeTest {
|
|||
result.setFound(true);
|
||||
result.getDesignations().add(designation1);
|
||||
result.getDesignations().add(designation2);
|
||||
getCodeSystemProvider().setLookupCodeResult(result);
|
||||
getLookupCodeProvider().setLookupCodeResult(result);
|
||||
|
||||
// test and verify
|
||||
LookupCodeRequest request = new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, null);
|
||||
|
@ -120,7 +123,7 @@ public interface ILookupCodeTest {
|
|||
BaseConceptProperty property = createConceptProperty(propertyName, thePropertyValue);
|
||||
LookupCodeResult result = new LookupCodeResult();
|
||||
result.getProperties().add(property);
|
||||
getCodeSystemProvider().setLookupCodeResult(result);
|
||||
getLookupCodeProvider().setLookupCodeResult(result);
|
||||
|
||||
// test
|
||||
LookupCodeRequest request = new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, List.of(propertyName));
|
||||
|
@ -148,7 +151,7 @@ public interface ILookupCodeTest {
|
|||
propertyNamesToFilter.add(currentPropertyName);
|
||||
}
|
||||
}
|
||||
getCodeSystemProvider().setLookupCodeResult(result);
|
||||
getLookupCodeProvider().setLookupCodeResult(result);
|
||||
|
||||
// test
|
||||
LookupCodeRequest request = new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, propertyNamesToFilter);
|
||||
|
@ -172,7 +175,7 @@ public interface ILookupCodeTest {
|
|||
group.addSubProperty(createConceptProperty(subPropertyName + i, thePropertyValues.get(i)));
|
||||
}
|
||||
result.getProperties().add(group);
|
||||
getCodeSystemProvider().setLookupCodeResult(result);
|
||||
getLookupCodeProvider().setLookupCodeResult(result);
|
||||
|
||||
// test and verify
|
||||
LookupCodeRequest request = new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, List.of(groupName));
|
||||
|
@ -186,8 +189,8 @@ public interface ILookupCodeTest {
|
|||
|
||||
// verify
|
||||
assertNotNull(outcome);
|
||||
assertEquals(theRequest.getCode(), getCodeSystemProvider().getCode());
|
||||
assertEquals(theRequest.getSystem(), getCodeSystemProvider().getSystem());
|
||||
assertEquals(theRequest.getCode(), getLookupCodeProvider().getCode());
|
||||
assertEquals(theRequest.getSystem(), getLookupCodeProvider().getSystem());
|
||||
assertEquals(theExpectedResult.isFound(), outcome.isFound());
|
||||
assertEquals(theExpectedResult.getErrorMessage(), outcome.getErrorMessage());
|
||||
assertEquals(theExpectedResult.getCodeSystemDisplayName(), outcome.getCodeSystemDisplayName());
|
||||
|
@ -207,7 +210,7 @@ public interface ILookupCodeTest {
|
|||
LookupCodeResult result = new LookupCodeResult();
|
||||
result.setFound(true);
|
||||
result.getDesignations().add(theConceptDesignation);
|
||||
getCodeSystemProvider().setLookupCodeResult(result);
|
||||
getLookupCodeProvider().setLookupCodeResult(result);
|
||||
|
||||
// test and verify
|
||||
LookupCodeRequest request = new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, null);
|
||||
|
@ -247,11 +250,4 @@ public interface ILookupCodeTest {
|
|||
assertEquals(theActualDesignation.getUseSystem(), theExpectedDesignation.getUseSystem());
|
||||
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() {
|
||||
String baseUrl = getService().getBaseUrl();
|
||||
final String codeNotFound = "a";
|
||||
final String system = CODE_SYSTEM;
|
||||
final String system = IValidationProviders.CODE_SYSTEM;
|
||||
final String codeAndSystem = system + "#" + codeNotFound;
|
||||
final String exceptionMessage = MessageFormat.format(MESSAGE_RESPONSE_NOT_FOUND, codeNotFound);
|
||||
LookupCodeResult result = new LookupCodeResult()
|
||||
|
@ -31,7 +31,7 @@ public interface IRemoteTerminologyLookupCodeTest extends ILookupCodeTest {
|
|||
.setSearchedForCode(codeNotFound)
|
||||
.setSearchedForSystem(system)
|
||||
.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);
|
||||
verifyLookupCodeResult(request, result);
|
||||
|
@ -49,9 +49,11 @@ public interface IRemoteTerminologyLookupCodeTest extends ILookupCodeTest {
|
|||
.setSearchedForCode(codeNotFound)
|
||||
.setSearchedForSystem(system)
|
||||
.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);
|
||||
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.ValidationSupportChain;
|
||||
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.hapi.ctx.HapiWorkerContext;
|
||||
import org.hl7.fhir.dstu3.model.Base;
|
||||
import org.hl7.fhir.dstu3.model.BooleanType;
|
||||
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.ValueSetExpansionComponent;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import org.hl7.fhir.r5.utils.validation.IValidationPolicyAdvisor;
|
||||
import org.hl7.fhir.r5.utils.validation.IValidatorResourceFetcher;
|
||||
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.Test;
|
||||
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.quality.Strictness;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
@ -110,6 +111,7 @@ public class FhirInstanceValidatorDstu3Test extends BaseValidationTestWithInline
|
|||
private FhirValidator myVal;
|
||||
private ArrayList<String> myValidConcepts;
|
||||
private Set<String> myValidSystems = new HashSet<>();
|
||||
private Set<String> myValidSystemsNotReturningIssues = new HashSet<>();
|
||||
private HashMap<String, StructureDefinition> myStructureDefinitions;
|
||||
private HashMap<String, CodeSystem> myCodeSystems;
|
||||
private HashMap<String, ValueSet> myValueSets;
|
||||
|
@ -117,7 +119,15 @@ public class FhirInstanceValidatorDstu3Test extends BaseValidationTestWithInline
|
|||
private CachingValidationSupport myValidationSupport;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -219,9 +229,10 @@ public class FhirInstanceValidatorDstu3Test extends BaseValidationTestWithInline
|
|||
retVal = new IValidationSupport.CodeValidationResult().setCode(code);
|
||||
} else if (myValidSystems.contains(system)) {
|
||||
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 if (myCodeSystems.containsKey(system)) {
|
||||
CodeSystem cs = myCodeSystems.get(system);
|
||||
Optional<ConceptDefinitionComponent> found = cs.getConcept().stream().filter(t -> t.getCode().equals(code)).findFirst();
|
||||
|
@ -1161,9 +1172,11 @@ public class FhirInstanceValidatorDstu3Test extends BaseValidationTestWithInline
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateResourceContainingLoincCode() {
|
||||
addValidConcept("http://loinc.org", "1234567");
|
||||
// TODO: uncomment value false when https://github.com/hapifhir/org.hl7.fhir.core/issues/1766 is fixed
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {true, /*false*/})
|
||||
public void testValidateResourceContainingLoincCode(boolean theShouldSystemReturnIssuesForInvalidCode) {
|
||||
addValidConcept("http://loinc.org", "1234567", theShouldSystemReturnIssuesForInvalidCode);
|
||||
|
||||
Observation input = new Observation();
|
||||
// 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 jakarta.servlet.http.HttpServletRequest;
|
||||
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.dstu3.model.BooleanType;
|
||||
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.InstantType;
|
||||
import org.hl7.fhir.r4.model.IntegerType;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
|
@ -49,20 +51,25 @@ public class RemoteTerminologyLookupCodeDstu3Test implements IRemoteTerminologyL
|
|||
@RegisterExtension
|
||||
public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx);
|
||||
private final RemoteTerminologyServiceValidationSupport mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx);
|
||||
private IValidationProviders.IMyLookupCodeProvider myLookupCodeProvider;
|
||||
|
||||
@BeforeEach
|
||||
public void before() {
|
||||
String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
|
||||
mySvc.setBaseUrl(baseUrl);
|
||||
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
|
||||
public IMyCodeSystemProvider getCodeSystemProvider() {
|
||||
return myCodeSystemProvider;
|
||||
public IValidationProviders.IMyLookupCodeProvider getLookupCodeProvider() {
|
||||
return myLookupCodeProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -83,7 +90,7 @@ public class RemoteTerminologyLookupCodeDstu3Test implements IRemoteTerminologyL
|
|||
|
||||
@ParameterizedTest
|
||||
@MethodSource(value = "getEmptyPropertyValues")
|
||||
public void lookupCode_forCodeSystemWithPropertyEmptyValue_returnsCorrectParameters(IBaseDatatype thePropertyValue) {
|
||||
void lookupCode_forCodeSystemWithPropertyEmptyValue_returnsCorrectParameters(IBaseDatatype thePropertyValue) {
|
||||
verifyLookupWithEmptyPropertyValue(thePropertyValue);
|
||||
}
|
||||
|
||||
|
@ -122,20 +129,20 @@ public class RemoteTerminologyLookupCodeDstu3Test implements IRemoteTerminologyL
|
|||
|
||||
@ParameterizedTest
|
||||
@MethodSource(value = "getPropertyValueArguments")
|
||||
public void lookupCode_forCodeSystemWithProperty_returnsCorrectProperty(IBaseDatatype thePropertyValue) {
|
||||
void lookupCode_forCodeSystemWithProperty_returnsCorrectProperty(IBaseDatatype thePropertyValue) {
|
||||
verifyLookupWithProperty(List.of(thePropertyValue), List.of());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@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(thePropertyValues.size() - 1));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource(value = "getPropertyValueListArguments")
|
||||
public void lookupCode_forCodeSystemWithPropertyGroup_returnsCorrectProperty(List<IBaseDatatype> thePropertyValues) {
|
||||
void lookupCode_forCodeSystemWithPropertyGroup_returnsCorrectProperty(List<IBaseDatatype> thePropertyValues) {
|
||||
verifyLookupWithSubProperties(thePropertyValues);
|
||||
}
|
||||
|
||||
|
@ -155,7 +162,8 @@ public class RemoteTerminologyLookupCodeDstu3Test implements IRemoteTerminologyL
|
|||
verifyLookupWithConceptDesignation(theConceptDesignation);
|
||||
}
|
||||
|
||||
static class MyCodeSystemProviderDstu3 implements IMyCodeSystemProvider {
|
||||
@SuppressWarnings("unused")
|
||||
static class MyLookupCodeProviderDstu3 implements IValidationProviders.IMyLookupCodeProvider {
|
||||
private UriType mySystemUrl;
|
||||
private CodeType myCode;
|
||||
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.r4.conformance.ProfileUtilities;
|
||||
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.model.AllergyIntolerance;
|
||||
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.ValueSetExpansionComponent;
|
||||
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.test.utils.ClassesLoadedFlags;
|
||||
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.Test;
|
||||
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.stubbing.Answer;
|
||||
|
||||
|
@ -128,13 +130,22 @@ public class FhirInstanceValidatorR4Test extends BaseValidationTestWithInlineMoc
|
|||
private FhirValidator myFhirValidator;
|
||||
private ArrayList<String> myValidConcepts;
|
||||
private Set<String> myValidSystems = new HashSet<>();
|
||||
private Set<String> myValidSystemsNotReturningIssues = new HashSet<>();
|
||||
private Set<String> myValidValueSets = new HashSet<>();
|
||||
private Map<String, StructureDefinition> myStructureDefinitionMap = new HashMap<>();
|
||||
private CachingValidationSupport myValidationSupport;
|
||||
private IValidationSupport myMockSupport;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -296,7 +307,10 @@ public class FhirInstanceValidatorR4Test extends BaseValidationTestWithInlineMoc
|
|||
retVal = new IValidationSupport.CodeValidationResult().setCode(code);
|
||||
} else if (myValidSystems.contains(system)) {
|
||||
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 {
|
||||
retVal = myDefaultValidationSupport.validateCode(new ValidationSupportContext(myDefaultValidationSupport), options, system, code, display, valueSetUrl);
|
||||
}
|
||||
|
@ -1226,14 +1240,15 @@ public class FhirInstanceValidatorR4Test extends BaseValidationTestWithInlineMoc
|
|||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testValidateResourceContainingLoincCode() {
|
||||
addValidConcept("http://loinc.org", "1234567");
|
||||
// TODO: uncomment value false when https://github.com/hapifhir/org.hl7.fhir.core/issues/1766 is fixed
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {true, /*false*/})
|
||||
public void testValidateResourceContainingLoincCode(boolean theShouldSystemReturnIssuesForInvalidCode) {
|
||||
addValidConcept("http://loinc.org", "1234567", theShouldSystemReturnIssuesForInvalidCode);
|
||||
|
||||
Observation input = createObservationWithDefaultSubjectPerfomerEffective();
|
||||
input.getText().setDiv(new XhtmlNode().setValue("<div>AA</div>")).setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||
|
||||
|
||||
input.addIdentifier().setSystem("http://acme").setValue("12345");
|
||||
input.getEncounter().setReference("http://foo.com/Encounter/9");
|
||||
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.support.IValidationSupport;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport.LookupCodeResult;
|
||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
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 jakarta.servlet.http.HttpServletRequest;
|
||||
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.instance.model.api.IBaseDatatype;
|
||||
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.Type;
|
||||
import org.hl7.fhir.r4.model.UriType;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
|
@ -39,7 +42,6 @@ import java.util.List;
|
|||
import java.util.stream.Stream;
|
||||
|
||||
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.
|
||||
|
@ -50,19 +52,27 @@ public class RemoteTerminologyLookupCodeR4Test implements IRemoteTerminologyLook
|
|||
@RegisterExtension
|
||||
public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx);
|
||||
private final RemoteTerminologyServiceValidationSupport mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx);
|
||||
private final MyCodeSystemProviderR4 myCodeSystemProvider = new MyCodeSystemProviderR4();
|
||||
private IValidateCodeProvidersR4.MyCodeSystemProviderR4 myCodeSystemProvider;
|
||||
private MyLookupCodeProviderR4 myLookupCodeProviderR4;
|
||||
|
||||
@BeforeEach
|
||||
public void before() {
|
||||
String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
|
||||
mySvc.setBaseUrl(baseUrl);
|
||||
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
|
||||
public IMyCodeSystemProvider getCodeSystemProvider() {
|
||||
return myCodeSystemProvider;
|
||||
public IValidationProviders.IMyLookupCodeProvider getLookupCodeProvider() {
|
||||
return myLookupCodeProviderR4;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -87,7 +97,6 @@ public class RemoteTerminologyLookupCodeR4Test implements IRemoteTerminologyLook
|
|||
verifyLookupWithEmptyPropertyValue(thePropertyValue);
|
||||
}
|
||||
|
||||
|
||||
public static Stream<Arguments> getPropertyValueArguments() {
|
||||
return Stream.of(
|
||||
// FHIR R4 spec types
|
||||
|
@ -155,7 +164,8 @@ public class RemoteTerminologyLookupCodeR4Test implements IRemoteTerminologyLook
|
|||
verifyLookupWithConceptDesignation(theConceptDesignation);
|
||||
}
|
||||
|
||||
static class MyCodeSystemProviderR4 implements IMyCodeSystemProvider {
|
||||
@SuppressWarnings("unused")
|
||||
static class MyLookupCodeProviderR4 implements IValidationProviders.IMyLookupCodeProvider {
|
||||
private UriType mySystemUrl;
|
||||
private CodeType myCode;
|
||||
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;
|
||||
|
||||
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.TranslateConceptResult;
|
||||
import ca.uhn.fhir.context.support.TranslateConceptResults;
|
||||
import ca.uhn.fhir.fhirpath.BaseValidationTestWithInlineMocks;
|
||||
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.Operation;
|
||||
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.api.SummaryEnum;
|
||||
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.param.UriParam;
|
||||
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.util.ParametersUtil;
|
||||
import com.google.common.collect.Lists;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
|
||||
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.r4.model.BooleanType;
|
||||
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.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.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.ArrayList;
|
||||
import java.util.Collections;
|
||||
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.assertFalse;
|
||||
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.
|
||||
* @see RemoteTerminologyLookupCodeR4Test
|
||||
* @see RemoteTerminologyServiceResourceProviderR4Test
|
||||
* @see RemoteTerminologyValidateCodeR4Test
|
||||
*/
|
||||
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_NAME = "Code System";
|
||||
private static final String CODE = "CODE";
|
||||
private static final String VALUE_SET_URL = "http://value.set/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 boolean REVERSE = true;
|
||||
private static final String EQUIVALENCE_CODE = "equivalent";
|
||||
|
||||
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();
|
||||
|
||||
|
@ -114,83 +92,8 @@ public class RemoteTerminologyServiceValidationSupportR4Test extends BaseValidat
|
|||
mySvc.addClientInterceptor(new LoggingInterceptor(true));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void after() {
|
||||
assertThat(myValueSetProvider.myInvocationCount).isLessThan(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateCode_withBlankCode_returnsNull() {
|
||||
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() {
|
||||
void fetchValueSet_forcesSummaryFalse() {
|
||||
// given
|
||||
myValueSetProvider.myNextReturnValueSets = new ArrayList<>();
|
||||
|
||||
|
@ -202,67 +105,7 @@ public class RemoteTerminologyServiceValidationSupportR4Test extends BaseValidat
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testValidateCode_forSystemCodeWithError_returnsCorrectly() {
|
||||
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() {
|
||||
void translateCode_AllInParams_AllOutParams() {
|
||||
myConceptMapProvider.myNextReturnParams = new Parameters();
|
||||
myConceptMapProvider.myNextReturnParams.addParameter("result", true);
|
||||
myConceptMapProvider.myNextReturnParams.addParameter("message", ERROR_MESSAGE);
|
||||
|
@ -303,7 +146,7 @@ public class RemoteTerminologyServiceValidationSupportR4Test extends BaseValidat
|
|||
|
||||
assertNotNull(results);
|
||||
assertTrue(results.getResult());
|
||||
assertEquals(results.getResults().size(), 2);
|
||||
assertEquals(2, results.getResults().size());
|
||||
for(TranslateConceptResult result : results.getResults()) {
|
||||
assertEquals(singleResult, result);
|
||||
}
|
||||
|
@ -318,7 +161,7 @@ public class RemoteTerminologyServiceValidationSupportR4Test extends BaseValidat
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testTranslateCode_NoInParams_NoOutParams() {
|
||||
void translateCode_NoInParams_NoOutParams() {
|
||||
myConceptMapProvider.myNextReturnParams = new Parameters();
|
||||
|
||||
List<IBaseCoding> codings = new ArrayList<>();
|
||||
|
@ -328,7 +171,7 @@ public class RemoteTerminologyServiceValidationSupportR4Test extends BaseValidat
|
|||
TranslateConceptResults results = mySvc.translateConcept(request);
|
||||
assertNotNull(results);
|
||||
assertFalse(results.getResult());
|
||||
assertEquals(results.getResults().size(), 0);
|
||||
assertEquals(0, results.getResults().size());
|
||||
|
||||
assertNull(myConceptMapProvider.myLastCodeableConcept);
|
||||
assertNull(myConceptMapProvider.myLastTargetCodeSystem);
|
||||
|
@ -340,7 +183,7 @@ public class RemoteTerminologyServiceValidationSupportR4Test extends BaseValidat
|
|||
}
|
||||
|
||||
@Test
|
||||
void testFetchCodeSystem_forcesSummaryFalse() {
|
||||
void fetchCodeSystem_forcesSummaryFalse() {
|
||||
// given
|
||||
myCodeSystemProvider.myNextReturnCodeSystems = new ArrayList<>();
|
||||
|
||||
|
@ -360,180 +203,8 @@ public class RemoteTerminologyServiceValidationSupportR4Test extends BaseValidat
|
|||
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
|
||||
public void testIsValueSetSupported_False() {
|
||||
void isValueSetSupported_False() {
|
||||
myValueSetProvider.myNextReturnValueSets = new ArrayList<>();
|
||||
|
||||
boolean outcome = mySvc.isValueSetSupported(null, "http://loinc.org/VS");
|
||||
|
@ -542,7 +213,7 @@ public class RemoteTerminologyServiceValidationSupportR4Test extends BaseValidat
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testIsValueSetSupported_True() {
|
||||
void isValueSetSupported_True() {
|
||||
myValueSetProvider.myNextReturnValueSets = new ArrayList<>();
|
||||
myValueSetProvider.myNextReturnValueSets.add((ValueSet) new ValueSet().setId("ValueSet/123"));
|
||||
|
||||
|
@ -552,7 +223,7 @@ public class RemoteTerminologyServiceValidationSupportR4Test extends BaseValidat
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testIsCodeSystemSupported_False() {
|
||||
void isCodeSystemSupported_False() {
|
||||
myCodeSystemProvider.myNextReturnCodeSystems = new ArrayList<>();
|
||||
|
||||
boolean outcome = mySvc.isCodeSystemSupported(null, "http://loinc.org");
|
||||
|
@ -561,7 +232,7 @@ public class RemoteTerminologyServiceValidationSupportR4Test extends BaseValidat
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testIsCodeSystemSupported_True() {
|
||||
void isCodeSystemSupported_True() {
|
||||
myCodeSystemProvider.myNextReturnCodeSystems = new ArrayList<>();
|
||||
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());
|
||||
}
|
||||
|
||||
private void createNextValueSetReturnParameters(boolean theResult, String theDisplay, String theMessage) {
|
||||
myValueSetProvider.myNextReturnParams = new Parameters()
|
||||
.addParameter("result", theResult)
|
||||
.addParameter("display", theDisplay)
|
||||
.addParameter("message", theMessage);
|
||||
}
|
||||
|
||||
static private class MyCodeSystemProvider implements IResourceProvider {
|
||||
@SuppressWarnings("unused")
|
||||
private static class MyCodeSystemProvider implements IResourceProvider {
|
||||
private SummaryEnum myLastSummaryParam;
|
||||
private Exception myNextValidateCodeException;
|
||||
private UriParam myLastUrlParam;
|
||||
private List<CodeSystem> myNextReturnCodeSystems;
|
||||
private UriType mySystemUrl;
|
||||
private CodeType myCode;
|
||||
private IValidationSupport.CodeValidationResult myNextValidationResult;
|
||||
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
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
|
||||
public List<CodeSystem> find(@RequiredParam(name = "url") UriParam theUrlParam, SummaryEnum theSummaryParam) {
|
||||
myLastUrlParam = theUrlParam;
|
||||
|
@ -620,46 +261,12 @@ public class RemoteTerminologyServiceValidationSupportR4Test extends BaseValidat
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class MyValueSetProvider implements IResourceProvider {
|
||||
private Parameters myNextReturnParams;
|
||||
private Exception myNextValidateCodeException;
|
||||
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 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
|
||||
public List<ValueSet> find(@RequiredParam(name = "url") UriParam theUrlParam, SummaryEnum theSummaryParam) {
|
||||
myLastUrlParam = theUrlParam;
|
||||
|
@ -675,6 +282,7 @@ public class RemoteTerminologyServiceValidationSupportR4Test extends BaseValidat
|
|||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class MyConceptMapProvider implements IResourceProvider {
|
||||
private UriType myLastConceptMapUrl;
|
||||
private StringType myLastConceptMapVersion;
|
||||
|
@ -715,7 +323,5 @@ public class RemoteTerminologyServiceValidationSupportR4Test extends BaseValidat
|
|||
public Class<? extends IBaseResource> getResourceType() {
|
||||
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.constants.BestPracticeWarningLevel;
|
||||
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.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
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.ValueSource;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
|
@ -97,10 +100,19 @@ public class FhirInstanceValidatorR5Test extends BaseValidationTestWithInlineMoc
|
|||
|
||||
private Set<String> mySupportedValueSets = new HashSet<>();
|
||||
private Set<String> myValidSystems = new HashSet<>();
|
||||
private Set<String> myValidSystemsNotReturningIssues = new HashSet<>();
|
||||
private CachingValidationSupport myValidationSupport;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -188,7 +200,10 @@ public class FhirInstanceValidatorR5Test extends BaseValidationTestWithInlineMoc
|
|||
retVal = new IValidationSupport.CodeValidationResult().setCode(code);
|
||||
} else if (myValidSystems.contains(system)) {
|
||||
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 {
|
||||
retVal = myDefaultValidationSupport.validateCode(new ValidationSupportContext(myDefaultValidationSupport), options, system, code, display, valueSetUrl);
|
||||
}
|
||||
|
@ -788,9 +803,11 @@ public class FhirInstanceValidatorR5Test extends BaseValidationTestWithInlineMoc
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateResourceContainingLoincCode() {
|
||||
addValidConcept("http://loinc.org", "1234567");
|
||||
// TODO: uncomment value false when https://github.com/hapifhir/org.hl7.fhir.core/issues/1766 is fixed
|
||||
@ParameterizedTest
|
||||
@ValueSource(booleans = {true, /*false*/})
|
||||
public void testValidateResourceContainingLoincCode(boolean theShouldSystemReturnIssuesForInvalidCode) {
|
||||
addValidConcept("http://loinc.org", "1234567", theShouldSystemReturnIssuesForInvalidCode);
|
||||
|
||||
Observation input = new Observation();
|
||||
// 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));
|
||||
|
||||
|
||||
assertEquals(ResultSeverityEnum.ERROR, errors.get(0).getSeverity());
|
||||
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