Fixes for validation lookupCode method and improve remote terminology tests (#5535)

* Fixes for validation lookupCode method:

(1) NullPointerException when processing designation in the remote terminology implementation
(2) Inconsistency between input and output type  vs.  for property parameter across the board
(3) Fix most of the warnings in the updated classes.
(4) Code review changes

* Fix unit test failure by reverting assertion to FOO_COLUMN.

* Revert commit.

* Update tests

* Update tests - move utility class back

* Update fix and tests

* spotless fix

* Missing branches for exception cases and add tests for the same

* Spotless fix

* fix tests

* fix test

* Address code review comments. Write the remote terminology tests in a way to make them more readable.

* Revert moving utility test class

* Small changes in tests

* Small changes

* fix checkstyle

* Minor changes in exception handling and add a missing test

* Address code review comments. Decouple other tests from lookupCode tests.

* Update declaration of constants

---------

Co-authored-by: Luke deGruchy <luke.degruchy@smilecdr.com>
This commit is contained in:
Martha Mitran 2023-12-12 07:05:21 -08:00 committed by GitHub
parent b28fe3b882
commit 564ae46008
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 1306 additions and 705 deletions

View File

@ -295,8 +295,8 @@ public interface IValidationSupport {
*/
@Nullable
default CodeValidationResult validateCode(
@Nonnull ValidationSupportContext theValidationSupportContext,
@Nonnull ConceptValidationOptions theOptions,
ValidationSupportContext theValidationSupportContext,
ConceptValidationOptions theOptions,
String theCodeSystem,
String theCode,
String theDisplay,
@ -526,8 +526,13 @@ public interface IValidationSupport {
public String getPropertyName() {
return myPropertyName;
}
public abstract String getType();
}
String TYPE_STRING = "string";
String TYPE_CODING = "Coding";
class StringConceptProperty extends BaseConceptProperty {
private final String myValue;
@ -544,6 +549,10 @@ public interface IValidationSupport {
public String getValue() {
return myValue;
}
public String getType() {
return TYPE_STRING;
}
}
class CodingConceptProperty extends BaseConceptProperty {
@ -574,6 +583,10 @@ public interface IValidationSupport {
public String getDisplay() {
return myDisplay;
}
public String getType() {
return TYPE_CODING;
}
}
class CodeValidationResult {
@ -881,25 +894,35 @@ public interface IValidationSupport {
}
for (BaseConceptProperty next : myProperties) {
String propertyName = next.getPropertyName();
if (!properties.isEmpty()) {
if (!properties.contains(next.getPropertyName())) {
continue;
}
if (!properties.isEmpty() && !properties.contains(propertyName)) {
continue;
}
IBase property = ParametersUtil.addParameterToParameters(theContext, retVal, "property");
ParametersUtil.addPartCode(theContext, property, "code", next.getPropertyName());
ParametersUtil.addPartCode(theContext, property, "code", propertyName);
if (next instanceof StringConceptProperty) {
StringConceptProperty prop = (StringConceptProperty) next;
ParametersUtil.addPartString(theContext, property, "value", prop.getValue());
} else if (next instanceof CodingConceptProperty) {
CodingConceptProperty prop = (CodingConceptProperty) next;
ParametersUtil.addPartCoding(
theContext, property, "value", prop.getCodeSystem(), prop.getCode(), prop.getDisplay());
} else {
throw new IllegalStateException(Msg.code(1739) + "Don't know how to handle " + next.getClass());
String propertyType = next.getType();
switch (propertyType) {
case TYPE_STRING:
StringConceptProperty stringConceptProperty = (StringConceptProperty) next;
ParametersUtil.addPartString(
theContext, property, "value", stringConceptProperty.getValue());
break;
case TYPE_CODING:
CodingConceptProperty codingConceptProperty = (CodingConceptProperty) next;
ParametersUtil.addPartCoding(
theContext,
property,
"value",
codingConceptProperty.getCodeSystem(),
codingConceptProperty.getCode(),
codingConceptProperty.getDisplay());
break;
default:
throw new IllegalStateException(
Msg.code(1739) + "Don't know how to handle " + next.getClass());
}
}
}

View File

@ -0,0 +1,6 @@
---
type: fix
issue: 5511
title: 'Previously, CodeSystem `$lookup` with Remote Terminology Service enabled would throw NullPointerException
when the CodeSystem included designations with no language value. Also, there was an inconsistency between
input and output type `string` vs. `code` for property parameters. These issues have been fixed.'

View File

@ -4,8 +4,12 @@ import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil;
import ca.uhn.fhir.rest.gclient.IOperationUntypedWithInputAndPartialOutput;
import org.hl7.fhir.dstu3.model.CodeSystem;
import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.dstu3.model.CodeSystem.ConceptPropertyComponent;
import org.hl7.fhir.dstu3.model.CodeType;
import org.hl7.fhir.dstu3.model.Coding;
import org.hl7.fhir.dstu3.model.Parameters;
import org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent;
import org.hl7.fhir.dstu3.model.StringType;
import org.hl7.fhir.dstu3.model.UriType;
import org.junit.jupiter.params.ParameterizedTest;
@ -21,8 +25,12 @@ import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourCod
import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourCodeSystemUrl;
import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyA;
import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyB;
import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyC;
import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyValueA;
import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyValueB;
import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.propertyCode;
import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.propertyCodeSystem;
import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.propertyDisplay;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
@ -30,72 +38,70 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
public class ResourceProviderDstu3CodeSystemPropertiesTest extends BaseResourceProviderDstu3Test {
public static Stream<Arguments> parametersLookup() {
return CodeSystemLookupWithPropertiesUtil.parametersLookupWithProperties();
}
public static Stream<Arguments> parametersLookup() {
return CodeSystemLookupWithPropertiesUtil.parametersLookupWithProperties();
}
@ParameterizedTest
@MethodSource(value = "parametersLookup")
public void testLookup_withProperties_returnsCorrectParameters(List<String> theLookupProperties, List<String> theExpectedReturnedProperties) {
// setup
CodeSystem codeSystem = new CodeSystem();
codeSystem.setId(ourCodeSystemId);
codeSystem.setUrl(ourCodeSystemUrl);
CodeSystem.ConceptDefinitionComponent concept = codeSystem.addConcept().setCode(ourCode);
CodeSystem.ConceptPropertyComponent propertyComponent = new CodeSystem.ConceptPropertyComponent()
.setCode(ourPropertyA).setValue(new StringType(ourPropertyValueA));
concept.addProperty(propertyComponent);
propertyComponent = new CodeSystem.ConceptPropertyComponent()
.setCode(ourPropertyB).setValue(new StringType(ourPropertyValueB));
concept.addProperty(propertyComponent);
myCodeSystemDao.create(codeSystem, mySrd);
@ParameterizedTest
@MethodSource(value = "parametersLookup")
public void testLookup_withProperties_returnsCorrectParameters(List<String> theLookupProperties, List<String> theExpectedReturnedProperties) {
// setup
CodeSystem codeSystem = new CodeSystem();
codeSystem.setId(ourCodeSystemId);
codeSystem.setUrl(ourCodeSystemUrl);
ConceptDefinitionComponent concept = codeSystem.addConcept().setCode(ourCode)
.addProperty(new ConceptPropertyComponent().setCode(ourPropertyA).setValue(new StringType(ourPropertyValueA)))
.addProperty(new ConceptPropertyComponent().setCode(ourPropertyB).setValue(new StringType(ourPropertyValueB)))
.addProperty(new ConceptPropertyComponent().setCode(ourPropertyC).setValue(new Coding(propertyCodeSystem, propertyCode, propertyDisplay)));
myCodeSystemDao.create(codeSystem, mySrd);
// test
IOperationUntypedWithInputAndPartialOutput<Parameters> respParam = myClient
.operation()
.onType(CodeSystem.class)
.named(JpaConstants.OPERATION_LOOKUP)
.withParameter(Parameters.class, "code", new CodeType(ourCode))
.andParameter("system", new UriType(ourCodeSystemUrl));
// test
IOperationUntypedWithInputAndPartialOutput<Parameters> respParam = myClient
.operation()
.onType(CodeSystem.class)
.named(JpaConstants.OPERATION_LOOKUP)
.withParameter(Parameters.class, "code", new CodeType(ourCode))
.andParameter("system", new UriType(ourCodeSystemUrl));
theLookupProperties.forEach(p -> respParam.andParameter("property", new CodeType(p)));
Parameters parameters = respParam.execute();
theLookupProperties.forEach(p -> respParam.andParameter("property", new CodeType(p)));
Parameters parameters = respParam.execute();
Iterator<Parameters.ParametersParameterComponent> paramIterator = parameters.getParameter().iterator();
Parameters.ParametersParameterComponent parameter = null;
while (paramIterator.hasNext()) {
Parameters.ParametersParameterComponent currentParameter = paramIterator.next();
if (currentParameter.getName().equals("property")) {
parameter = currentParameter;
break;
}
}
Iterator<ParametersParameterComponent> paramIterator = parameters.getParameter().iterator();
ParametersParameterComponent parameter = null;
while (paramIterator.hasNext()) {
ParametersParameterComponent currentParameter = paramIterator.next();
if (currentParameter.getName().equals("property")) {
parameter = currentParameter;
break;
}
}
if (theExpectedReturnedProperties.isEmpty()) {
assertNull(parameter);
return;
}
// verify
if (theExpectedReturnedProperties.isEmpty()) {
assertNull(parameter);
return;
}
Iterator<CodeSystem.ConceptPropertyComponent> propertyIterator = concept.getProperty().stream()
.filter(property -> theExpectedReturnedProperties.contains(property.getCode())).iterator();
Iterator<ConceptPropertyComponent> propertyIterator = concept.getProperty().stream()
.filter(property -> theExpectedReturnedProperties.contains(property.getCode())).iterator();
while (propertyIterator.hasNext()) {
CodeSystem.ConceptPropertyComponent property = propertyIterator.next();
assertNotNull(parameter);
while (propertyIterator.hasNext()) {
ConceptPropertyComponent property = propertyIterator.next();
assertNotNull(parameter);
Iterator<Parameters.ParametersParameterComponent> parameterPartIterator = parameter.getPart().iterator();
Iterator<ParametersParameterComponent> parameterPartIterator = parameter.getPart().iterator();
parameter = parameterPartIterator.next();
assertEquals("code", parameter.getName());
assertEquals(property.getCode(), ((CodeType)parameter.getValue()).getValue());
parameter = parameterPartIterator.next();
assertEquals("code", parameter.getName());
assertEquals(property.getCode(), ((CodeType) parameter.getValue()).getValue());
parameter = parameterPartIterator.next();
assertEquals("value", parameter.getName());
assertTrue(property.getValue().equalsShallow(parameter.getValue()));
parameter = parameterPartIterator.next();
assertEquals("value", parameter.getName());
assertTrue(property.getValue().equalsShallow(parameter.getValue()));
if (paramIterator.hasNext()) {
parameter = paramIterator.next();
}
}
}
if (paramIterator.hasNext()) {
parameter = paramIterator.next();
}
}
}
}

View File

@ -5,13 +5,11 @@ import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.param.UriParam;
import ca.uhn.fhir.rest.server.IResourceProvider;
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.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.BooleanType;
@ -27,10 +25,9 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import jakarta.servlet.http.HttpServletRequest;
import java.util.List;
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;
/*
@ -45,18 +42,18 @@ public class RemoteTerminologyServiceResourceProviderR4Test {
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 MyCodeSystemProvider myCodeSystemProvider = new MyCodeSystemProvider();
private MyValueSetProvider myValueSetProvider = new MyValueSetProvider();
private static final MyCodeSystemProvider ourCodeSystemProvider = new MyCodeSystemProvider();
private static final MyValueSetProvider ourValueSetProvider = new MyValueSetProvider();
@RegisterExtension
public RestfulServerExtension myRestfulServerExtension = new RestfulServerExtension(ourCtx, myCodeSystemProvider,
myValueSetProvider);
public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx, ourCodeSystemProvider,
ourValueSetProvider);
private RemoteTerminologyServiceValidationSupport mySvc;
@BeforeEach
public void before_ConfigureService() {
String myBaseUrl = "http://localhost:" + myRestfulServerExtension.getPort();
String myBaseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx, myBaseUrl);
mySvc.addClientInterceptor(new LoggingInterceptor(false).setLogRequestSummary(true).setLogResponseSummary(true));
}
@ -64,7 +61,7 @@ public class RemoteTerminologyServiceResourceProviderR4Test {
@AfterEach
public void after_UnregisterProviders() {
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE);
myRestfulServerExtension.getRestfulServer().getInterceptorService().unregisterAllInterceptors();
ourRestfulServerExtension.getRestfulServer().getInterceptorService().unregisterAllInterceptors();
}
@Test
@ -80,12 +77,13 @@ public class RemoteTerminologyServiceResourceProviderR4Test {
IValidationSupport.CodeValidationResult outcome = mySvc
.validateCode(null, null, CODE_SYSTEM, CODE, null, null);
assertNotNull(outcome);
assertEquals(CODE, outcome.getCode());
assertEquals(null, outcome.getSeverity());
assertEquals(null, outcome.getMessage());
assertNull(outcome.getSeverity());
assertNull(outcome.getMessage());
assertEquals(CODE, myCodeSystemProvider.myLastCode.getCode());
assertEquals(CODE_SYSTEM, myCodeSystemProvider.myLastUrl.getValueAsString());
assertEquals(CODE, ourCodeSystemProvider.myLastCode.getCode());
assertEquals(CODE_SYSTEM, ourCodeSystemProvider.myLastUrl.getValueAsString());
}
@Test
@ -94,15 +92,16 @@ public class RemoteTerminologyServiceResourceProviderR4Test {
IValidationSupport.CodeValidationResult outcome = mySvc
.validateCode(null, null, CODE_SYSTEM, CODE, DISPLAY, null);
assertNotNull(outcome);
assertEquals(CODE, outcome.getCode());
assertEquals(DISPLAY, outcome.getDisplay());
assertEquals(null, outcome.getSeverity());
assertEquals(null, outcome.getMessage());
assertNull(outcome.getSeverity());
assertNull(outcome.getMessage());
assertEquals(CODE, myCodeSystemProvider.myLastCode.getCode());
assertEquals(DISPLAY, myCodeSystemProvider.myLastDisplay.getValue());
assertEquals(CODE_SYSTEM, myCodeSystemProvider.myLastUrl.getValueAsString());
assertEquals(SAMPLE_MESSAGE, myCodeSystemProvider.myNextReturnParams.getParameterValue("message").toString());
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
@ -111,65 +110,58 @@ public class RemoteTerminologyServiceResourceProviderR4Test {
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());
assertEquals(false, ((BooleanType)myCodeSystemProvider.myNextReturnParams.getParameterValue("result")).booleanValue());
assertFalse(((BooleanType) ourCodeSystemProvider.myNextReturnParams.getParameterValue("result")).booleanValue());
}
@Test
public void testValidateCodeInValueSet_ProvidingMinimalInputs_ReturnsSuccess() {
createNextValueSetReturnParameters(true, null, null);
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());
assertEquals(null, outcome.getSeverity());
assertEquals(null, outcome.getMessage());
assertNull(outcome.getSeverity());
assertNull(outcome.getMessage());
assertEquals(CODE, myValueSetProvider.myLastCode.getCode());
assertEquals(VALUE_SET_URL, myValueSetProvider.myLastUrl.getValueAsString());
assertEquals(CODE, ourValueSetProvider.myLastCode.getCode());
assertEquals(VALUE_SET_URL, ourValueSetProvider.myLastUrl.getValueAsString());
}
@Test
public void testValidateCodeInValueSet_WithMessageValue_ReturnsMessage() {
createNextValueSetReturnParameters(true, DISPLAY, SAMPLE_MESSAGE);
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());
assertEquals(null, outcome.getSeverity());
assertEquals(null, outcome.getMessage());
assertNull(outcome.getSeverity());
assertNull(outcome.getMessage());
assertEquals(CODE, myValueSetProvider.myLastCode.getCode());
assertEquals(DISPLAY, myValueSetProvider.myLastDisplay.getValue());
assertEquals(VALUE_SET_URL, myValueSetProvider.myLastUrl.getValueAsString());
assertEquals(SAMPLE_MESSAGE, myValueSetProvider.myNextReturnParams.getParameterValue("message").toString());
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());
}
private void createNextCodeSystemReturnParameters(boolean theResult, String theDisplay, String theMessage) {
myCodeSystemProvider.myNextReturnParams = new Parameters();
myCodeSystemProvider.myNextReturnParams.addParameter("result", theResult);
myCodeSystemProvider.myNextReturnParams.addParameter("display", theDisplay);
ourCodeSystemProvider.myNextReturnParams = new Parameters();
ourCodeSystemProvider.myNextReturnParams.addParameter("result", theResult);
ourCodeSystemProvider.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);
ourCodeSystemProvider.myNextReturnParams.addParameter("message", theMessage);
}
}
private static class MyCodeSystemProvider implements IResourceProvider {
private UriParam myLastUrlParam;
private List<CodeSystem> myNextReturnCodeSystems;
private int myInvocationCount;
private UriType myLastUrl;
private CodeType myLastCode;
private StringType myLastDisplay;
@ -187,7 +179,6 @@ public class RemoteTerminologyServiceResourceProviderR4Test {
@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;
@ -195,13 +186,6 @@ public class RemoteTerminologyServiceResourceProviderR4Test {
}
@Search
public List<CodeSystem> find(@RequiredParam(name = "url") UriParam theUrlParam) {
myLastUrlParam = theUrlParam;
assert myNextReturnCodeSystems != null;
return myNextReturnCodeSystems;
}
@Override
public Class<? extends IBaseResource> getResourceType() {
return CodeSystem.class;
@ -211,14 +195,9 @@ public class RemoteTerminologyServiceResourceProviderR4Test {
private static class MyValueSetProvider implements IResourceProvider {
private Parameters myNextReturnParams;
private List<ValueSet> myNextReturnValueSets;
private UriType myLastUrl;
private CodeType myLastCode;
private int myInvocationCount;
private UriType myLastSystem;
private StringType myLastDisplay;
private ValueSet myLastValueSet;
private UriParam myLastUrlParam;
@Operation(name = "validate-code", idempotent = true, returnParameters = {
@OperationParam(name = "result", type = BooleanType.class, min = 1),
@ -234,22 +213,12 @@ public class RemoteTerminologyServiceResourceProviderR4Test {
@OperationParam(name = "display", min = 0, max = 1) StringType theDisplay,
@OperationParam(name = "valueSet") ValueSet theValueSet
) {
myInvocationCount++;
myLastUrl = theValueSetUrl;
myLastCode = theCode;
myLastSystem = theSystem;
myLastDisplay = theDisplay;
myLastValueSet = theValueSet;
return myNextReturnParams;
}
@Search
public List<ValueSet> find(@RequiredParam(name = "url") UriParam theUrlParam) {
myLastUrlParam = theUrlParam;
assert myNextReturnValueSets != null;
return myNextReturnValueSets;
}
@Override
public Class<? extends IBaseResource> getResourceType() {
return ValueSet.class;

View File

@ -5,8 +5,12 @@ import ca.uhn.fhir.jpa.provider.BaseResourceProviderR4Test;
import ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil;
import ca.uhn.fhir.rest.gclient.IOperationUntypedWithInputAndPartialOutput;
import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.r4.model.CodeSystem.ConceptPropertyComponent;
import org.hl7.fhir.r4.model.CodeType;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.Parameters;
import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent;
import org.hl7.fhir.r4.model.StringType;
import org.hl7.fhir.r4.model.UriType;
import org.junit.jupiter.params.ParameterizedTest;
@ -22,70 +26,74 @@ import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourCod
import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourCodeSystemUrl;
import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyA;
import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyB;
import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyC;
import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyValueA;
import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyValueB;
import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.propertyCode;
import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.propertyCodeSystem;
import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.propertyDisplay;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class ResourceProviderR4CodeSystemPropertiesTest extends BaseResourceProviderR4Test {
public static Stream<Arguments> parametersLookup() {
return CodeSystemLookupWithPropertiesUtil.parametersLookupWithProperties();
}
public static Stream<Arguments> parametersLookup() {
return CodeSystemLookupWithPropertiesUtil.parametersLookupWithProperties();
}
@ParameterizedTest
@MethodSource(value = "parametersLookup")
public void testLookup_withProperties_returnsCorrectParameters(List<String> theLookupProperties, List<String> theExpectedReturnedProperties) {
// setup
CodeSystem codeSystem = new CodeSystem();
codeSystem.setId(ourCodeSystemId);
codeSystem.setUrl(ourCodeSystemUrl);
CodeSystem.ConceptDefinitionComponent concept = codeSystem.addConcept().setCode(ourCode);
CodeSystem.ConceptPropertyComponent propertyComponent = new CodeSystem.ConceptPropertyComponent()
.setCode(ourPropertyA).setValue(new StringType(ourPropertyValueA));
concept.addProperty(propertyComponent);
propertyComponent = new CodeSystem.ConceptPropertyComponent()
.setCode(ourPropertyB).setValue(new StringType(ourPropertyValueB));
concept.addProperty(propertyComponent);
myCodeSystemDao.create(codeSystem, mySrd);
@ParameterizedTest
@MethodSource(value = "parametersLookup")
public void testLookup_withProperties_returnsCorrectParameters(List<String> theLookupProperties, List<String> theExpectedReturnedProperties) {
// setup
CodeSystem codeSystem = new CodeSystem();
codeSystem.setId(ourCodeSystemId);
codeSystem.setUrl(ourCodeSystemUrl);
ConceptDefinitionComponent concept = codeSystem.addConcept().setCode(ourCode)
.addProperty(new ConceptPropertyComponent().setCode(ourPropertyA).setValue(new StringType(ourPropertyValueA)))
.addProperty(new ConceptPropertyComponent().setCode(ourPropertyB).setValue(new StringType(ourPropertyValueB)))
.addProperty(new ConceptPropertyComponent().setCode(ourPropertyC).setValue(new Coding(propertyCodeSystem, propertyCode, propertyDisplay)));
// test
IOperationUntypedWithInputAndPartialOutput<Parameters> respParam = myClient
.operation()
.onType(CodeSystem.class)
.named(JpaConstants.OPERATION_LOOKUP)
.withParameter(Parameters.class, "code", new CodeType(ourCode))
.andParameter("system", new UriType(ourCodeSystemUrl));
myCodeSystemDao.create(codeSystem, mySrd);
theLookupProperties.forEach(p -> respParam.andParameter("property", new CodeType(p)));
Parameters parameters = respParam.execute();
// test
IOperationUntypedWithInputAndPartialOutput<Parameters> respParam = myClient
.operation()
.onType(CodeSystem.class)
.named(JpaConstants.OPERATION_LOOKUP)
.withParameter(Parameters.class, "code", new CodeType(ourCode))
.andParameter("system", new UriType(ourCodeSystemUrl));
theLookupProperties.forEach(p -> respParam.andParameter("property", new CodeType(p)));
Parameters parameters = respParam.execute();
if (theExpectedReturnedProperties.isEmpty()) {
assertFalse(parameters.hasParameter("property"));
return;
}
// verify
if (theExpectedReturnedProperties.isEmpty()) {
assertFalse(parameters.hasParameter("property"));
return;
}
assertTrue(parameters.hasParameter("property"));
Iterator<Parameters.ParametersParameterComponent> parameterPropertyIterator = parameters.getParameters("property").iterator();
assertTrue(parameters.hasParameter("property"));
Iterator<ParametersParameterComponent> parameterPropertyIterator = parameters.getParameters("property").iterator();
Iterator<CodeSystem.ConceptPropertyComponent> propertyIterator = concept.getProperty().stream()
.filter(property -> theExpectedReturnedProperties.contains(property.getCode())).iterator();
Iterator<ConceptPropertyComponent> propertyIterator = concept.getProperty().stream()
.filter(property -> theExpectedReturnedProperties.contains(property.getCode())).iterator();
while (propertyIterator.hasNext()) {
CodeSystem.ConceptPropertyComponent property = propertyIterator.next();
while (propertyIterator.hasNext()) {
ConceptPropertyComponent property = propertyIterator.next();
assertTrue(parameterPropertyIterator.hasNext());
Parameters.ParametersParameterComponent parameter = parameterPropertyIterator.next();
Iterator<Parameters.ParametersParameterComponent> parameterPartIterator = parameter.getPart().iterator();
assertTrue(parameterPropertyIterator.hasNext());
ParametersParameterComponent parameter = parameterPropertyIterator.next();
Iterator<ParametersParameterComponent> parameterPartIterator = parameter.getPart().iterator();
parameter = parameterPartIterator.next();
assertEquals("code", parameter.getName());
assertEquals(property.getCode(), ((CodeType)parameter.getValue()).getCode());
parameter = parameterPartIterator.next();
assertEquals("code", parameter.getName());
assertEquals(property.getCode(), ((CodeType) parameter.getValue()).getValue());
parameter = parameterPartIterator.next();
assertEquals("value", parameter.getName());
assertTrue(property.getValue().equalsShallow(parameter.getValue()));
}
}
parameter = parameterPartIterator.next();
assertEquals("value", parameter.getName());
assertTrue(property.getValue().equalsShallow(parameter.getValue()));
assertFalse(parameterPartIterator.hasNext());
}
}
}

View File

@ -510,7 +510,7 @@ public class TerminologySvcDeltaR4Test extends BaseJpaR4Test {
List<Parameters.ParametersParameterComponent> properties = output.getParameter().stream().filter(t -> t.getName().equals("property")).collect(Collectors.toList());
assertEquals("code", properties.get(0).getPart().get(0).getName());
assertEquals("flavour", ((CodeType) properties.get(0).getPart().get(0).getValue()).getValueAsString());
assertEquals("flavour", ((StringType) properties.get(0).getPart().get(0).getValue()).getValueAsString());
assertEquals("value", properties.get(0).getPart().get(1).getName());
assertEquals("Hints of lime", ((StringType) properties.get(0).getPart().get(1).getValue()).getValueAsString());

View File

@ -1,11 +1,14 @@
package ca.uhn.fhir.jpa.provider.r5;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil;
import ca.uhn.fhir.rest.gclient.IOperationUntypedWithInputAndPartialOutput;
import ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil;
import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.CodeSystem.ConceptPropertyComponent;
import org.hl7.fhir.r5.model.CodeType;
import org.hl7.fhir.r5.model.Coding;
import org.hl7.fhir.r5.model.Parameters;
import org.hl7.fhir.r5.model.Parameters.ParametersParameterComponent;
import org.hl7.fhir.r5.model.StringType;
import org.hl7.fhir.r5.model.UriType;
import org.junit.jupiter.params.ParameterizedTest;
@ -21,69 +24,73 @@ import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourCod
import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourCodeSystemUrl;
import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyA;
import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyB;
import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyC;
import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyValueA;
import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.ourPropertyValueB;
import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.propertyCode;
import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.propertyCodeSystem;
import static ca.uhn.fhir.jpa.provider.CodeSystemLookupWithPropertiesUtil.propertyDisplay;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class ResourceProviderR5CodeSystemPropertiesTest extends BaseResourceProviderR5Test {
public static Stream<Arguments> parametersLookup() {
return CodeSystemLookupWithPropertiesUtil.parametersLookupWithProperties();
}
@ParameterizedTest
@MethodSource(value = "parametersLookup")
public void testLookup_withProperties_returnsCorrectParameters(List<String> theLookupProperties, List<String> theExpectedReturnedProperties) {
// setup
CodeSystem codeSystem = new CodeSystem();
codeSystem.setId(ourCodeSystemId);
codeSystem.setUrl(ourCodeSystemUrl);
CodeSystem.ConceptDefinitionComponent concept = codeSystem.addConcept().setCode(ourCode);
CodeSystem.ConceptPropertyComponent propertyComponent = new CodeSystem.ConceptPropertyComponent()
.setCode(ourPropertyA).setValue(new StringType(ourPropertyValueA));
concept.addProperty(propertyComponent);
propertyComponent = new CodeSystem.ConceptPropertyComponent()
.setCode(ourPropertyB).setValue(new StringType(ourPropertyValueB));
concept.addProperty(propertyComponent);
myCodeSystemDao.create(codeSystem, mySrd);
public static Stream<Arguments> parametersLookup() {
return CodeSystemLookupWithPropertiesUtil.parametersLookupWithProperties();
}
// test
IOperationUntypedWithInputAndPartialOutput<Parameters> respParam = myClient
.operation()
.onType(CodeSystem.class)
.named(JpaConstants.OPERATION_LOOKUP)
.withParameter(Parameters.class, "code", new CodeType(ourCode))
.andParameter("system", new UriType(ourCodeSystemUrl));
@ParameterizedTest
@MethodSource(value = "parametersLookup")
public void testLookup_withProperties_returnsCorrectParameters(List<String> theLookupProperties, List<String> theExpectedReturnedProperties) {
// setup
CodeSystem codeSystem = new CodeSystem();
codeSystem.setId(ourCodeSystemId);
codeSystem.setUrl(ourCodeSystemUrl);
CodeSystem.ConceptDefinitionComponent concept = codeSystem.addConcept().setCode(ourCode)
.addProperty(new ConceptPropertyComponent().setCode(ourPropertyA).setValue(new StringType(ourPropertyValueA)))
.addProperty(new ConceptPropertyComponent().setCode(ourPropertyB).setValue(new StringType(ourPropertyValueB)))
.addProperty(new ConceptPropertyComponent().setCode(ourPropertyC).setValue(new Coding(propertyCodeSystem, propertyCode, propertyDisplay)));
myCodeSystemDao.create(codeSystem, mySrd);
theLookupProperties.forEach(p -> respParam.andParameter("property", new CodeType(p)));
Parameters parameters = respParam.execute();
// test
IOperationUntypedWithInputAndPartialOutput<Parameters> respParam = myClient
.operation()
.onType(CodeSystem.class)
.named(JpaConstants.OPERATION_LOOKUP)
.withParameter(Parameters.class, "code", new CodeType(ourCode))
.andParameter("system", new UriType(ourCodeSystemUrl));
theLookupProperties.forEach(p -> respParam.andParameter("property", new CodeType(p)));
Parameters parameters = respParam.execute();
if (theExpectedReturnedProperties.isEmpty()) {
assertFalse(parameters.hasParameter("property"));
return;
}
// verify
if (theExpectedReturnedProperties.isEmpty()) {
assertFalse(parameters.hasParameter("property"));
return;
}
assertTrue(parameters.hasParameter("property"));
Iterator<Parameters.ParametersParameterComponent> parameterPropertyIterator = parameters.getParameters("property").iterator();
assertTrue(parameters.hasParameter("property"));
Iterator<ParametersParameterComponent> parameterPropertyIterator = parameters.getParameters("property").iterator();
Iterator<CodeSystem.ConceptPropertyComponent> propertyIterator = concept.getProperty().stream()
.filter(property -> theExpectedReturnedProperties.contains(property.getCode())).iterator();
Iterator<ConceptPropertyComponent> propertyIterator = concept.getProperty().stream()
.filter(property -> theExpectedReturnedProperties.contains(property.getCode())).iterator();
while (propertyIterator.hasNext()) {
CodeSystem.ConceptPropertyComponent property = propertyIterator.next();
while (propertyIterator.hasNext()) {
ConceptPropertyComponent property = propertyIterator.next();
assertTrue(parameterPropertyIterator.hasNext());
Parameters.ParametersParameterComponent parameter = parameterPropertyIterator.next();
Iterator<Parameters.ParametersParameterComponent> parameterPartIterator = parameter.getPart().iterator();
assertTrue(parameterPropertyIterator.hasNext());
ParametersParameterComponent parameter = parameterPropertyIterator.next();
Iterator<ParametersParameterComponent> parameterPartIterator = parameter.getPart().iterator();
parameter = parameterPartIterator.next();
assertEquals("code", parameter.getName());
assertEquals(property.getCode(), ((CodeType)parameter.getValue()).getCode());
parameter = parameterPartIterator.next();
assertEquals("code", parameter.getName());
assertEquals(property.getCode(), ((CodeType) parameter.getValue()).getValue());
parameter = parameterPartIterator.next();
assertEquals("value", parameter.getName());
assertTrue(property.getValue().equalsShallow(parameter.getValue()));
}
}
parameter = parameterPartIterator.next();
assertEquals("value", parameter.getName());
assertTrue(property.getValue().equalsShallow(parameter.getValue()));
assertFalse(parameterPartIterator.hasNext());
}
}
}

View File

@ -31,13 +31,19 @@ public class CodeSystemLookupWithPropertiesUtil {
public static final String ourCodeSystemId = "CodeSystem-Example",
ourCodeSystemUrl = "http://example/" + ourCodeSystemId;
public static final String ourCode = "Code-WithProperties";
public static final String ourPropertyA = "Property-A", ourPropertyB = "Property-B";
public static final String ourPropertyA = "Property-A", ourPropertyB = "Property-B", ourPropertyC = "Property-C";
public static final String ourPropertyValueA = "Value-A", ourPropertyValueB = "Value-B";
public static final String propertyCodeSystem = "CodeSystem-C",
propertyCode = "Code-C",
propertyDisplay = "Display-C";
public static Stream<Arguments> parametersLookupWithProperties() {
return Stream.of(
arguments(Collections.emptyList(), List.of(ourPropertyA, ourPropertyB, ourPropertyC)),
arguments(List.of(ourPropertyB), List.of(ourPropertyB)),
arguments(List.of(ourPropertyA, ourPropertyB), List.of(ourPropertyA, ourPropertyB)),
arguments(
List.of(ourPropertyA, ourPropertyB, ourPropertyC),
List.of(ourPropertyA, ourPropertyB, ourPropertyC)),
arguments(List.of(ourPropertyB, ourPropertyA), List.of(ourPropertyB, ourPropertyA)),
arguments(List.of(ourPropertyA, ourPropertyA), List.of(ourPropertyA, ourPropertyA)),
arguments(List.of(ourPropertyB, "ABC"), List.of(ourPropertyB)),

View File

@ -12,6 +12,7 @@ import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.rest.api.SummaryEnum;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.gclient.IQuery;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.util.BundleUtil;
import ca.uhn.fhir.util.ParametersUtil;
import jakarta.annotation.Nonnull;
@ -19,15 +20,25 @@ import jakarta.annotation.Nullable;
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.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.Parameters;
import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent;
import org.hl7.fhir.r4.model.Property;
import org.hl7.fhir.r4.model.StringType;
import org.hl7.fhir.r4.model.Type;
import org.hl7.fhir.r4.model.ValueSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
@ -42,7 +53,7 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
private static final Logger ourLog = LoggerFactory.getLogger(RemoteTerminologyServiceValidationSupport.class);
private String myBaseUrl;
private List<Object> myClientInterceptors = new ArrayList<>();
private final List<Object> myClientInterceptors = new ArrayList<>();
/**
* Constructor
@ -65,8 +76,8 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
@Override
public CodeValidationResult validateCode(
@Nonnull ValidationSupportContext theValidationSupportContext,
@Nonnull ConceptValidationOptions theOptions,
ValidationSupportContext theValidationSupportContext,
ConceptValidationOptions theOptions,
String theCodeSystem,
String theCode,
String theDisplay,
@ -86,7 +97,7 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
IBaseResource valueSet = theValueSet;
// some external validators require the system when the code is passed
// so let's try to get it from the VS if is is not present
// so let's try to get it from the VS if is not present
String codeSystem = theCodeSystem;
if (isNotBlank(theCode) && isBlank(codeSystem)) {
codeSystem = extractCodeSystemForCode((ValueSet) theValueSet, theCode);
@ -123,7 +134,7 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
}
// when component has more than one include, their codeSystem(s) could be different, so we need to make sure
// that we are picking up the system for the include to which the code corresponds
// that we are picking up the system for the include filter to which the code corresponds
for (ValueSet.ConceptSetComponent include : theValueSet.getCompose().getInclude()) {
if (include.hasSystem()) {
for (ValueSet.ConceptReferenceComponent concept : include.getConcept()) {
@ -222,7 +233,7 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
return generateLookupCodeResultDSTU3(
code, system, (org.hl7.fhir.dstu3.model.Parameters) outcome);
case R4:
return generateLookupCodeResultR4(code, system, (org.hl7.fhir.r4.model.Parameters) outcome);
return generateLookupCodeResultR4(code, system, (Parameters) outcome);
}
}
break;
@ -244,87 +255,133 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
result.setFound(true);
for (org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent parameterComponent :
outcomeDSTU3.getParameter()) {
String parameterTypeAsString = Objects.toString(parameterComponent.getValue(), null);
switch (parameterComponent.getName()) {
case "property":
org.hl7.fhir.dstu3.model.Property part = parameterComponent.getChildByName("part");
// The assumption here is that we may only have 2 elements in this part, and if so, these 2 will be
// saved
if (part != null && part.hasValues() && part.getValues().size() >= 2) {
String key = ((org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent)
part.getValues().get(0))
.getValue()
.toString();
String value = ((org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent)
part.getValues().get(1))
.getValue()
.toString();
if (!StringUtils.isEmpty(key) && !StringUtils.isEmpty(value)) {
result.getProperties().add(new StringConceptProperty(key, value));
}
if (part == null || part.getValues().size() < 2) {
continue;
}
BaseConceptProperty conceptProperty = createBaseConceptPropertyDstu3(part.getValues());
if (conceptProperty != null) {
result.getProperties().add(conceptProperty);
}
break;
case "designation":
ConceptDesignation conceptDesignation = new ConceptDesignation();
for (org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent designationComponent :
parameterComponent.getPart()) {
switch (designationComponent.getName()) {
case "language":
conceptDesignation.setLanguage(
designationComponent.getValue().toString());
break;
case "use":
org.hl7.fhir.dstu3.model.Coding coding =
(org.hl7.fhir.dstu3.model.Coding) designationComponent.getValue();
if (coding != null) {
conceptDesignation.setUseSystem(coding.getSystem());
conceptDesignation.setUseCode(coding.getCode());
conceptDesignation.setUseDisplay(coding.getDisplay());
}
break;
case "value":
conceptDesignation.setValue(
((designationComponent.getValue() == null)
? null
: designationComponent
.getValue()
.toString()));
break;
}
}
ConceptDesignation conceptDesignation = createConceptDesignationDstu3(parameterComponent);
result.getDesignations().add(conceptDesignation);
break;
case "name":
result.setCodeSystemDisplayName(
((parameterComponent.getValue() == null)
? null
: parameterComponent.getValue().toString()));
result.setCodeSystemDisplayName(parameterTypeAsString);
break;
case "version":
result.setCodeSystemVersion(
((parameterComponent.getValue() == null)
? null
: parameterComponent.getValue().toString()));
result.setCodeSystemVersion(parameterTypeAsString);
break;
case "display":
result.setCodeDisplay(
((parameterComponent.getValue() == null)
? null
: parameterComponent.getValue().toString()));
result.setCodeDisplay(parameterTypeAsString);
break;
case "abstract":
result.setCodeIsAbstract(
((parameterComponent.getValue() == null)
? false
: Boolean.parseBoolean(
parameterComponent.getValue().toString())));
result.setCodeIsAbstract(Boolean.parseBoolean(parameterTypeAsString));
break;
}
}
return result;
}
private LookupCodeResult generateLookupCodeResultR4(
String theCode, String theSystem, org.hl7.fhir.r4.model.Parameters outcomeR4) {
private static BaseConceptProperty createBaseConceptPropertyDstu3(List<org.hl7.fhir.dstu3.model.Base> theValues) {
org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent part1 =
(org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent) theValues.get(0);
String propertyName = ((org.hl7.fhir.dstu3.model.CodeType) part1.getValue()).getValue();
BaseConceptProperty conceptProperty = null;
org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent part2 =
(org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent) theValues.get(1);
org.hl7.fhir.dstu3.model.Type value = part2.getValue();
if (value == null) {
return conceptProperty;
}
String fhirType = value.fhirType();
switch (fhirType) {
case TYPE_STRING:
org.hl7.fhir.dstu3.model.StringType stringType = (org.hl7.fhir.dstu3.model.StringType) part2.getValue();
conceptProperty = new StringConceptProperty(propertyName, stringType.getValue());
break;
case TYPE_CODING:
org.hl7.fhir.dstu3.model.Coding coding = (org.hl7.fhir.dstu3.model.Coding) part2.getValue();
conceptProperty = new CodingConceptProperty(
propertyName, coding.getSystem(), coding.getCode(), coding.getDisplay());
break;
default:
throw new InternalErrorException(Msg.code(2450) + "Property type " + fhirType + " is not supported.");
}
return conceptProperty;
}
public static BaseConceptProperty createConceptProperty(final String theName, final IBaseDatatype theValue) {
if (theValue instanceof Type) {
return createConceptPropertyR4(theName, (Type) theValue);
}
if (theValue instanceof org.hl7.fhir.dstu3.model.Type) {
return createConceptPropertyDstu3(theName, (org.hl7.fhir.dstu3.model.Type) theValue);
}
return null;
}
private static BaseConceptProperty createConceptPropertyDstu3(
final String theName, final org.hl7.fhir.dstu3.model.Type theValue) {
if (theValue == null) {
return null;
}
BaseConceptProperty conceptProperty;
String fhirType = theValue.fhirType();
switch (fhirType) {
case IValidationSupport.TYPE_STRING:
org.hl7.fhir.dstu3.model.StringType stringType = (org.hl7.fhir.dstu3.model.StringType) theValue;
conceptProperty = new StringConceptProperty(theName, stringType.getValue());
break;
case IValidationSupport.TYPE_CODING:
org.hl7.fhir.dstu3.model.Coding coding = (org.hl7.fhir.dstu3.model.Coding) theValue;
conceptProperty =
new CodingConceptProperty(theName, coding.getSystem(), coding.getCode(), coding.getDisplay());
break;
default:
throw new InternalErrorException(Msg.code(2451) + "Property type " + fhirType + " is not supported.");
}
return conceptProperty;
}
private ConceptDesignation createConceptDesignationDstu3(
org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent theParameterComponent) {
ConceptDesignation conceptDesignation = new ConceptDesignation();
for (org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent designationComponent :
theParameterComponent.getPart()) {
org.hl7.fhir.dstu3.model.Type designationComponentValue = designationComponent.getValue();
if (designationComponentValue == null) {
continue;
}
switch (designationComponent.getName()) {
case "language":
conceptDesignation.setLanguage(designationComponentValue.toString());
break;
case "use":
org.hl7.fhir.dstu3.model.Coding coding =
(org.hl7.fhir.dstu3.model.Coding) designationComponentValue;
conceptDesignation.setUseSystem(coding.getSystem());
conceptDesignation.setUseCode(coding.getCode());
conceptDesignation.setUseDisplay(coding.getDisplay());
break;
case "value":
conceptDesignation.setValue(designationComponent.getValue().toString());
break;
}
}
return conceptDesignation;
}
private LookupCodeResult generateLookupCodeResultR4(String theCode, String theSystem, Parameters outcomeR4) {
// NOTE: I wanted to put all of this logic into the IValidationSupport Class, but it would've required adding
// several new dependencies on version-specific libraries and that is explicitly forbidden (see comment in
// POM).
@ -332,87 +389,115 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
result.setSearchedForCode(theCode);
result.setSearchedForSystem(theSystem);
result.setFound(true);
for (org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent parameterComponent :
outcomeR4.getParameter()) {
for (ParametersParameterComponent parameterComponent : outcomeR4.getParameter()) {
String parameterTypeAsString = Objects.toString(parameterComponent.getValue(), null);
switch (parameterComponent.getName()) {
case "property":
org.hl7.fhir.r4.model.Property part = parameterComponent.getChildByName("part");
Property part = parameterComponent.getChildByName("part");
// The assumption here is that we may only have 2 elements in this part, and if so, these 2 will be
// saved
if (part != null && part.hasValues() && part.getValues().size() >= 2) {
String key = ((org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent)
part.getValues().get(0))
.getValue()
.toString();
String value = ((org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent)
part.getValues().get(1))
.getValue()
.toString();
if (!StringUtils.isEmpty(key) && !StringUtils.isEmpty(value)) {
result.getProperties().add(new StringConceptProperty(key, value));
}
if (part == null || part.getValues().size() < 2) {
continue;
}
BaseConceptProperty conceptProperty = createBaseConceptPropertyR4(part.getValues());
if (conceptProperty != null) {
result.getProperties().add(conceptProperty);
}
break;
case "designation":
ConceptDesignation conceptDesignation = new ConceptDesignation();
for (org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent designationComponent :
parameterComponent.getPart()) {
switch (designationComponent.getName()) {
case "language":
conceptDesignation.setLanguage(
designationComponent.getValue().toString());
break;
case "use":
org.hl7.fhir.r4.model.Coding coding =
(org.hl7.fhir.r4.model.Coding) designationComponent.getValue();
if (coding != null) {
conceptDesignation.setUseSystem(coding.getSystem());
conceptDesignation.setUseCode(coding.getCode());
conceptDesignation.setUseDisplay(coding.getDisplay());
}
break;
case "value":
conceptDesignation.setValue(
((designationComponent.getValue() == null)
? null
: designationComponent
.getValue()
.toString()));
break;
}
}
ConceptDesignation conceptDesignation = createConceptDesignationR4(parameterComponent);
result.getDesignations().add(conceptDesignation);
break;
case "name":
result.setCodeSystemDisplayName(
((parameterComponent.getValue() == null)
? null
: parameterComponent.getValue().toString()));
result.setCodeSystemDisplayName(parameterTypeAsString);
break;
case "version":
result.setCodeSystemVersion(
((parameterComponent.getValue() == null)
? null
: parameterComponent.getValue().toString()));
result.setCodeSystemVersion(parameterTypeAsString);
break;
case "display":
result.setCodeDisplay(
((parameterComponent.getValue() == null)
? null
: parameterComponent.getValue().toString()));
result.setCodeDisplay(parameterTypeAsString);
break;
case "abstract":
result.setCodeIsAbstract(
((parameterComponent.getValue() == null)
? false
: Boolean.parseBoolean(
parameterComponent.getValue().toString())));
result.setCodeIsAbstract(Boolean.parseBoolean(parameterTypeAsString));
break;
}
}
return result;
}
private static BaseConceptProperty createBaseConceptPropertyR4(List<Base> values) {
ParametersParameterComponent part1 = (ParametersParameterComponent) values.get(0);
String propertyName = ((CodeType) part1.getValue()).getValue();
ParametersParameterComponent part2 = (ParametersParameterComponent) values.get(1);
Type value = part2.getValue();
if (value == null) {
return null;
}
BaseConceptProperty conceptProperty;
String fhirType = value.fhirType();
switch (fhirType) {
case IValidationSupport.TYPE_STRING:
StringType stringType = (StringType) part2.getValue();
conceptProperty = new StringConceptProperty(propertyName, stringType.getValue());
break;
case IValidationSupport.TYPE_CODING:
Coding coding = (Coding) part2.getValue();
conceptProperty = new CodingConceptProperty(
propertyName, coding.getSystem(), coding.getCode(), coding.getDisplay());
break;
default:
throw new InternalErrorException(Msg.code(2452) + "Property type " + fhirType + " is not supported.");
}
return conceptProperty;
}
private static BaseConceptProperty createConceptPropertyR4(final String theName, final Type theValue) {
BaseConceptProperty conceptProperty;
String fhirType = theValue.fhirType();
switch (fhirType) {
case IValidationSupport.TYPE_STRING:
StringType stringType = (StringType) theValue;
conceptProperty = new StringConceptProperty(theName, stringType.getValue());
break;
case IValidationSupport.TYPE_CODING:
Coding coding = (Coding) theValue;
conceptProperty =
new CodingConceptProperty(theName, coding.getSystem(), coding.getCode(), coding.getDisplay());
break;
default:
throw new InternalErrorException(Msg.code(2453) + "Property type " + fhirType + " is not supported.");
}
return conceptProperty;
}
private ConceptDesignation createConceptDesignationR4(ParametersParameterComponent theParameterComponent) {
ConceptDesignation conceptDesignation = new ConceptDesignation();
for (ParametersParameterComponent designationComponent : theParameterComponent.getPart()) {
Type designationComponentValue = designationComponent.getValue();
if (designationComponentValue == null) {
continue;
}
switch (designationComponent.getName()) {
case "language":
conceptDesignation.setLanguage(designationComponentValue.toString());
break;
case "use":
Coding coding = (Coding) designationComponentValue;
conceptDesignation.setUseSystem(coding.getSystem());
conceptDesignation.setUseCode(coding.getCode());
conceptDesignation.setUseDisplay(coding.getDisplay());
break;
case "value":
conceptDesignation.setValue(designationComponentValue.toString());
break;
}
}
return conceptDesignation;
}
@Override
public IBaseResource fetchValueSet(String theValueSetUrl) {
// force the remote server to send the whole resource.
@ -514,7 +599,7 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
.execute();
List<String> resultValues = ParametersUtil.getNamedParameterValuesAsString(getFhirContext(), output, "result");
if (resultValues.size() < 1 || isBlank(resultValues.get(0))) {
if (resultValues.isEmpty() || isBlank(resultValues.get(0))) {
return null;
}
Validate.isTrue(resultValues.size() == 1, "Response contained %d 'result' values", resultValues.size());
@ -527,7 +612,7 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
retVal.setCode(theCode);
List<String> displayValues =
ParametersUtil.getNamedParameterValuesAsString(getFhirContext(), output, "display");
if (displayValues.size() > 0) {
if (!displayValues.isEmpty()) {
retVal.setDisplay(displayValues.get(0));
}
@ -536,7 +621,7 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
retVal.setSeverity(IssueSeverity.ERROR);
List<String> messageValues =
ParametersUtil.getNamedParameterValuesAsString(getFhirContext(), output, "message");
if (messageValues.size() > 0) {
if (!messageValues.isEmpty()) {
retVal.setMessage(messageValues.get(0));
}
}
@ -575,7 +660,7 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
/**
* Sets the FHIR Terminology Server base URL
*
* @param theBaseUrl The base URL, e.g. "https://hapi.fhir.org/baseR4"
* @param theBaseUrl The base URL, e.g. "<a href="https://hapi.fhir.org/baseR4">...</a>"
*/
public void setBaseUrl(String theBaseUrl) {
Validate.notBlank(theBaseUrl, "theBaseUrl must be provided");

View File

@ -0,0 +1,260 @@
package org.hl7.fhir.common.hapi.validation;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.IValidationSupport.LookupCodeResult;
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.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
import org.hl7.fhir.instance.model.api.IBaseDatatype;
import org.junit.jupiter.api.Nested;
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.Collection;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
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;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
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";
interface IValidationTest {
RemoteTerminologyServiceValidationSupport getService();
IResourceProvider getCodeSystemProvider();
}
@Nested
interface ILookupCodeUnsupportedPropertyTypeTest extends IValidationTest {
String getInvalidValueErrorCode();
String getInvalidValueErrorCodeForConvert();
@Override
IMySimpleCodeSystemProvider getCodeSystemProvider();
@Test
default void testLookupCode_forCodeSystemWithPropertyInvalidValue_throwsException() {
// test and verify
try {
getService().lookupCode(null, new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, null));
fail();
} catch (InternalErrorException e) {
assertTrue(e.getMessage().contains(getInvalidValueErrorCode() + ": Property type " + getCodeSystemProvider().getPropertyValue().fhirType() + " is not supported"));
}
}
@Test
default void testCreateConceptProperty_forCodeSystemWithPropertyInvalidValue_throwsException() {
// test and verify
try {
RemoteTerminologyServiceValidationSupport.createConceptProperty("property", getCodeSystemProvider().getPropertyValue());
fail();
} catch (InternalErrorException e) {
assertTrue(e.getMessage().contains(getInvalidValueErrorCodeForConvert() + ": Property type " + getCodeSystemProvider().getPropertyValue().fhirType() + " is not supported"));
}
}
}
@Nested
interface ILookupCodeSupportedPropertyTest extends IValidationTest {
IMyCodeSystemProvider getCodeSystemProvider();
Stream<Arguments> getEmptyPropertyValues();
Stream<Arguments> getPropertyValues();
Stream<Arguments> getDesignations();
void verifyProperty(IValidationSupport.BaseConceptProperty theConceptProperty, String theExpectedPropertName, IBaseDatatype theExpectedValue);
@Test
default void testLookupCode_forCodeSystemWithBlankCode_throwsException() {
try {
getService().lookupCode(null, new LookupCodeRequest(CODE_SYSTEM, ""));
fail();
} catch (IllegalArgumentException e) {
assertEquals("theCode must be provided", e.getMessage());
}
}
@Test
default void testLookupCode_forCodeSystemWithPropertyInvalidType_throwsException() {
LookupCodeResult result = new LookupCodeResult();
result.getProperties().add(new IValidationSupport.BaseConceptProperty("someProperty") {
public String getType() {
return "someUnsupportedType";
}
});
getCodeSystemProvider().setLookupCodeResult(result);
try {
getService().lookupCode(null, new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, null));
fail();
} catch (InternalErrorException e) {
assertTrue(e.getMessage().contains("HAPI-1739: Don't know how to handle "));
}
}
@ParameterizedTest
@MethodSource(value = "getEmptyPropertyValues")
default void testLookupCode_forCodeSystemWithPropertyEmptyValue_returnsCorrectParameters(IBaseDatatype thePropertyValue) {
// setup
final String propertyName = "someProperty";
IValidationSupport.BaseConceptProperty property = createConceptProperty(propertyName, thePropertyValue);
LookupCodeResult result = new LookupCodeResult();
result.getProperties().add(property);
getCodeSystemProvider().setLookupCodeResult(result);
// test
LookupCodeResult outcome = getService().lookupCode(null, new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, List.of(propertyName)));
// verify
assertNotNull(outcome);
Optional<IValidationSupport.BaseConceptProperty> propertyOptional = outcome.getProperties().stream().findFirst().filter(a -> propertyName.equals(a.getPropertyName()));
assertFalse(propertyOptional.isPresent());
}
@Test
default void testLookupCode_forCodeSystemWithParameters_returnsCorrectParameters() {
// setup
LookupCodeResult result = new LookupCodeResult().setFound(true).setSearchedForCode(CODE).setSearchedForSystem(CODE_SYSTEM);
result.setCodeIsAbstract(false);
result.setCodeSystemVersion(CODE_SYSTEM_VERSION);
result.setCodeSystemDisplayName(CODE_SYSTEM_NAME);
result.setCodeDisplay(DISPLAY);
getCodeSystemProvider().setLookupCodeResult(result);
// test
LookupCodeResult outcome = getService().lookupCode(null, new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, null));
// verify
assertNotNull(outcome);
assertEquals(CODE, getCodeSystemProvider().getCode());
assertEquals(CODE_SYSTEM, getCodeSystemProvider().getSystem());
assertEquals(result.getCodeSystemDisplayName(), outcome.getCodeSystemDisplayName());
assertEquals(result.getCodeDisplay(), outcome.getCodeDisplay());
assertEquals(result.getCodeSystemVersion(), outcome.getCodeSystemVersion());
assertEquals(result.isCodeIsAbstract(), outcome.isCodeIsAbstract());
}
@ParameterizedTest
@MethodSource(value = "getPropertyValues")
default void testLookupCode_forCodeSystemWithProperty_returnsCorrectProperty(IBaseDatatype thePropertyValue) {
// setup
final String propertyName = "someProperty";
LookupCodeResult result = new LookupCodeResult()
.setFound(true).setSearchedForCode(CODE).setSearchedForSystem(CODE_SYSTEM);
result.setCodeIsAbstract(false);
result.setCodeSystemVersion(CODE_SYSTEM_VERSION);
result.setCodeSystemDisplayName(CODE_SYSTEM_NAME);
result.setCodeDisplay(DISPLAY);
IValidationSupport.BaseConceptProperty property = createConceptProperty(propertyName, thePropertyValue);
result.getProperties().add(property);
getCodeSystemProvider().setLookupCodeResult(result);
// test
LookupCodeResult outcome = getService().lookupCode(null, new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, List.of(propertyName)));
// verify
assertNotNull(outcome);
assertEquals(CODE, getCodeSystemProvider().getCode());
assertEquals(CODE_SYSTEM, getCodeSystemProvider().getSystem());
assertEquals(result.getCodeSystemDisplayName(), outcome.getCodeSystemDisplayName());
assertEquals(result.getCodeDisplay(), outcome.getCodeDisplay());
assertEquals(result.getCodeSystemVersion(), outcome.getCodeSystemVersion());
assertEquals(result.isCodeIsAbstract(), outcome.isCodeIsAbstract());
Optional<IValidationSupport.BaseConceptProperty> propertyOptional = outcome.getProperties().stream().findFirst().filter(a -> propertyName.equals(a.getPropertyName()));
assertTrue(propertyOptional.isPresent());
IValidationSupport.BaseConceptProperty outputProperty = propertyOptional.get();
verifyProperty(outputProperty, propertyName, thePropertyValue);
}
@ParameterizedTest
@MethodSource(value = "getDesignations")
default void testLookupCode_withCodeSystemWithDesignation_returnsCorrectDesignation(final IValidationSupport.ConceptDesignation theConceptDesignation) {
// setup
LookupCodeResult result = new LookupCodeResult();
result.getDesignations().add(theConceptDesignation);
getCodeSystemProvider().setLookupCodeResult(result);
// test
LookupCodeResult outcome = getService().lookupCode(null, new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, null));
// verify
assertNotNull(outcome);
Collection<IValidationSupport.ConceptDesignation> designations = outcome.getDesignations();
assertEquals(1, designations.size());
IValidationSupport.ConceptDesignation designation = designations.iterator().next();
assertEquals(theConceptDesignation.getValue(), designation.getValue());
assertEquals(theConceptDesignation.getLanguage(), designation.getLanguage());
assertEquals(theConceptDesignation.getUseCode(), designation.getUseCode());
assertEquals(theConceptDesignation.getUseSystem(), designation.getUseSystem());
assertEquals(theConceptDesignation.getUseDisplay(), designation.getUseDisplay());
}
@Test
default void testLookupCode_withCodeSystemWithMultipleDesignations_returnsCorrectDesignations() {
// setup
final String code1 = "code1";
final String code2 = "code2";
IValidationSupport.ConceptDesignation designation1 = new IValidationSupport.ConceptDesignation().setUseCode(code1).setUseSystem("system1").setValue("value1").setLanguage("en");
IValidationSupport.ConceptDesignation designation2 = new IValidationSupport.ConceptDesignation().setUseCode(code2).setUseSystem("system2").setValue("value2").setLanguage("es");
LookupCodeResult result = new LookupCodeResult();
result.getDesignations().add(designation1);
result.getDesignations().add(designation2);
getCodeSystemProvider().setLookupCodeResult(result);
// test
LookupCodeResult outcome = getService().lookupCode(null, new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, null));
// verify
assertNotNull(outcome);
Collection<IValidationSupport.ConceptDesignation> designations = outcome.getDesignations();
assertEquals(2, designations.size());
for (IValidationSupport.ConceptDesignation designation : designations) {
IValidationSupport.ConceptDesignation expectedDesignation = code1.equals(designation.getUseCode()) ? designation1 : designation2;
assertEquals(expectedDesignation.getValue(), designation.getValue());
assertEquals(expectedDesignation.getLanguage(), designation.getLanguage());
assertEquals(expectedDesignation.getUseCode(), designation.getUseCode());
assertEquals(expectedDesignation.getUseSystem(), designation.getUseSystem());
assertEquals(expectedDesignation.getUseDisplay(), designation.getUseDisplay());
}
}
}
interface IMyCodeSystemProvider extends IResourceProvider {
String getCode();
String getSystem();
void setLookupCodeResult(LookupCodeResult theLookupCodeResult);
}
interface IMySimpleCodeSystemProvider extends IResourceProvider {
IBaseDatatype getPropertyValue();
}
}

View File

@ -0,0 +1,257 @@
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.IValidationSupport.ConceptDesignation;
import ca.uhn.fhir.context.support.IValidationSupport.LookupCodeResult;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
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.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
import jakarta.servlet.http.HttpServletRequest;
import org.hl7.fhir.common.hapi.validation.ILookupCodeTest.ILookupCodeSupportedPropertyTest;
import org.hl7.fhir.common.hapi.validation.ILookupCodeTest.ILookupCodeUnsupportedPropertyTypeTest;
import org.hl7.fhir.common.hapi.validation.ILookupCodeTest.IMyCodeSystemProvider;
import org.hl7.fhir.common.hapi.validation.ILookupCodeTest.IMySimpleCodeSystemProvider;
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.Parameters;
import org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent;
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.instance.model.api.IBaseDatatype;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.params.provider.Arguments;
import java.util.List;
import java.util.stream.Stream;
import static ca.uhn.fhir.context.support.IValidationSupport.BaseConceptProperty;
import static ca.uhn.fhir.context.support.IValidationSupport.CodingConceptProperty;
import static ca.uhn.fhir.context.support.IValidationSupport.StringConceptProperty;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class LookupCodeDstu3Test {
private static final FhirContext ourCtx = FhirContext.forDstu3Cached();
@RegisterExtension
public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx);
private final RemoteTerminologyServiceValidationSupport mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx);
@BeforeEach
public void before() {
String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
mySvc.setBaseUrl(baseUrl);
mySvc.addClientInterceptor(new LoggingInterceptor(true));
}
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@Nested
class LookupCodeUnsupportedPropertyTypeDstu3Test implements ILookupCodeUnsupportedPropertyTypeTest {
private final MySimplePropertyCodeSystemProviderDstu3 myMySimplePropertyCodeSystemProvider = new MySimplePropertyCodeSystemProviderDstu3();
@Override
public IMySimpleCodeSystemProvider getCodeSystemProvider() {
return myMySimplePropertyCodeSystemProvider;
}
@Override
public RemoteTerminologyServiceValidationSupport getService() {
return mySvc;
}
@Override
public String getInvalidValueErrorCode() {
return "HAPI-2450";
}
@Override
public String getInvalidValueErrorCodeForConvert() {
return "HAPI-2451";
}
@BeforeEach
public void before() {
// TODO: use another type when "code" is added to the supported types
myMySimplePropertyCodeSystemProvider.myPropertyName = "somePropertyName";
myMySimplePropertyCodeSystemProvider.myPropertyValue = new CodeType("someCode");
ourRestfulServerExtension.getRestfulServer().registerProvider(myMySimplePropertyCodeSystemProvider);
}
}
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@Nested
class ILookupCodeSupportedPropertyDstu3Test implements ILookupCodeSupportedPropertyTest {
private final MyCodeSystemProviderDstu3 myCodeSystemProvider = new MyCodeSystemProviderDstu3();
@Override
public IMyCodeSystemProvider getCodeSystemProvider() {
return myCodeSystemProvider;
}
@Override
public RemoteTerminologyServiceValidationSupport getService() {
return mySvc;
}
@BeforeEach
public void before() {
ourRestfulServerExtension.getRestfulServer().registerProvider(myCodeSystemProvider);
}
public void verifyProperty(BaseConceptProperty theConceptProperty, String theExpectedPropertName, IBaseDatatype theExpectedValue) {
assertEquals(theExpectedPropertName, theConceptProperty.getPropertyName());
String type = theConceptProperty.getType();
switch (type) {
case IValidationSupport.TYPE_STRING -> {
assertTrue(theExpectedValue instanceof StringType);
StringType stringValue = (StringType) theExpectedValue;
assertTrue(theConceptProperty instanceof StringConceptProperty);
StringConceptProperty stringConceptProperty = (StringConceptProperty) theConceptProperty;
assertEquals(stringValue.getValue(), stringConceptProperty.getValue());
}
case IValidationSupport.TYPE_CODING -> {
assertTrue(theExpectedValue instanceof Coding);
Coding coding = (Coding) theExpectedValue;
assertTrue(theConceptProperty instanceof CodingConceptProperty);
CodingConceptProperty codingConceptProperty = (CodingConceptProperty) theConceptProperty;
assertEquals(coding.getCode(), codingConceptProperty.getCode());
assertEquals(coding.getSystem(), codingConceptProperty.getCodeSystem());
assertEquals(coding.getDisplay(), codingConceptProperty.getDisplay());
}
default ->
throw new InternalErrorException(Msg.code(2451) + "Property type " + type + " is not supported.");
}
}
public Stream<Arguments> getEmptyPropertyValues() {
return Stream.of(
Arguments.arguments(new StringType()),
Arguments.arguments(new StringType("")),
Arguments.arguments(new Coding()),
Arguments.arguments(new Coding("", null, null)),
Arguments.arguments(new Coding("", "", null)),
Arguments.arguments(new Coding(null, "", null))
);
}
public Stream<Arguments> getPropertyValues() {
return Stream.of(
Arguments.arguments(new StringType("value")),
Arguments.arguments(new Coding("code", "system", "display"))
);
}
public Stream<Arguments> getDesignations() {
return Stream.of(
Arguments.arguments(new ConceptDesignation().setLanguage("en").setUseCode("code1").setUseSystem("system-1").setUseDisplay("display").setValue("some value")),
Arguments.arguments(new ConceptDesignation().setUseCode("code2").setUseSystem("system1").setUseDisplay("display").setValue("someValue")),
Arguments.arguments(new ConceptDesignation().setUseCode("code2").setUseSystem("system1").setValue("someValue")),
Arguments.arguments(new ConceptDesignation().setUseCode("code2").setUseSystem("system1")),
Arguments.arguments(new ConceptDesignation().setUseCode("code2"))
);
}
}
static class MySimplePropertyCodeSystemProviderDstu3 implements IMySimpleCodeSystemProvider {
String myPropertyName;
Type myPropertyValue;
@Override
public IBaseDatatype getPropertyValue() {
return myPropertyValue;
}
@Override
public Class<? extends IBaseResource> getResourceType() {
return CodeSystem.class;
}
@Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters= {
@OperationParam(name = "name", type = StringType.class, min = 1),
@OperationParam(name = "version", type = StringType.class, min = 0),
@OperationParam(name = "display", type = StringType.class, min = 1),
@OperationParam(name = "abstract", type = BooleanType.class, min = 1),
@OperationParam(name = "property", min = 0, max = OperationParam.MAX_UNLIMITED)
})
public Parameters 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
) {
ParametersParameterComponent component = new ParametersParameterComponent();
component.setName("property");
component.addPart(new ParametersParameterComponent().setName("code").setValue(new CodeType(myPropertyName)));
component.addPart(new ParametersParameterComponent().setName("value").setValue(myPropertyValue));
return new Parameters().addParameter(component);
}
}
static class MyCodeSystemProviderDstu3 implements IMyCodeSystemProvider {
private UriType mySystemUrl;
private CodeType myCode;
private LookupCodeResult myLookupCodeResult;
@Override
public Class<? extends IBaseResource> getResourceType() {
return CodeSystem.class;
}
@Override
public String getCode() {
return myCode != null ? myCode.getValueAsString() : null;
}
@Override
public String getSystem() {
return mySystemUrl != null ? mySystemUrl.getValueAsString() : null;
}
@Override
public void setLookupCodeResult(LookupCodeResult theLookupCodeResult) {
myLookupCodeResult = theLookupCodeResult;
}
@Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters= {
@OperationParam(name = "name", type = StringType.class, min = 1),
@OperationParam(name = "version", type = StringType.class, min = 0),
@OperationParam(name = "display", type = StringType.class, min = 1),
@OperationParam(name = "abstract", type = BooleanType.class, min = 1),
@OperationParam(name = "property", min = 0, max = OperationParam.MAX_UNLIMITED)
})
public Parameters 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
) {
myCode = theCode;
mySystemUrl = theSystem;
return (Parameters)myLookupCodeResult.toParameters(theRequestDetails.getFhirContext(), thePropertyNames);
}
}
}

View File

@ -1,167 +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.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
import org.hl7.fhir.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.Parameters;
import org.hl7.fhir.dstu3.model.StringType;
import org.hl7.fhir.dstu3.model.UriType;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import jakarta.servlet.http.HttpServletRequest;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
public class RemoteTerminologyServiceValidationSupportDstu3Test {
private static final String DISPLAY = "DISPLAY";
private static final String LANGUAGE = "en";
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 FhirContext ourCtx = FhirContext.forDstu3();
@RegisterExtension
public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx);
private RemoteTerminologyServiceValidationSupport mySvc;
private MyCodeSystemProvider myCodeSystemProvider;
@BeforeEach
public void before() {
myCodeSystemProvider = new MyCodeSystemProvider();
ourRestfulServerExtension.getRestfulServer().registerProvider(myCodeSystemProvider);
String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx);
mySvc.setBaseUrl(baseUrl);
mySvc.addClientInterceptor(new LoggingInterceptor(true));
}
@Test
public void testLookupOperationWithAllParams_CodeSystem_Success() {
myCodeSystemProvider.myNextLookupCodeResult = new IValidationSupport.LookupCodeResult();
myCodeSystemProvider.myNextLookupCodeResult.setFound(true);
myCodeSystemProvider.myNextLookupCodeResult.setCodeSystemVersion(CODE_SYSTEM);
myCodeSystemProvider.myNextLookupCodeResult.setSearchedForCode(CODE);
myCodeSystemProvider.myNextLookupCodeResult.setCodeSystemDisplayName(CODE_SYSTEM_NAME);
myCodeSystemProvider.myNextLookupCodeResult.setCodeDisplay(DISPLAY);
// property
String propertyName = "birthDate";
String propertyValue = "1930-01-01";
IValidationSupport.BaseConceptProperty property = new IValidationSupport.StringConceptProperty(propertyName, propertyValue);
myCodeSystemProvider.myNextLookupCodeResult.getProperties().add(property);
// designation
IValidationSupport.ConceptDesignation designation = new IValidationSupport.ConceptDesignation();
designation.setLanguage("en");
designation.setUseCode("code");
designation.setUseSystem("system");
designation.setUseDisplay("display");
designation.setValue("some value");
myCodeSystemProvider.myNextLookupCodeResult.getDesignations().add(designation);
IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(null, new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, Set.of("birthDate")));
assertNotNull(outcome, "Call to lookupCode() should return a non-NULL result!");
assertEquals(DISPLAY, outcome.getCodeDisplay());
assertEquals(CODE_SYSTEM, outcome.getCodeSystemVersion());
assertEquals(CODE, myCodeSystemProvider.myLastCode.asStringValue());
assertEquals(CODE_SYSTEM, myCodeSystemProvider.myLastUrl.getValueAsString());
Parameters.ParametersParameterComponent propertyComponent = null;
Parameters.ParametersParameterComponent designationComponent = null;
for (Parameters.ParametersParameterComponent parameterComponent : myCodeSystemProvider.myNextReturnParams.getParameter()) {
if ("property".equals(parameterComponent.getName())) {
propertyComponent = parameterComponent;
}
if ("designation".equals(parameterComponent.getName())) {
designationComponent = parameterComponent;
}
}
assertNotNull(propertyComponent);
Iterator<Parameters.ParametersParameterComponent> propertyComponentIterator = propertyComponent.getPart().iterator();
propertyComponent = propertyComponentIterator.next();
assertEquals("code", propertyComponent.getName());
assertEquals(propertyName, ((StringType)propertyComponent.getValue()).getValue());
propertyComponent = propertyComponentIterator.next();
assertEquals("value", propertyComponent.getName());
assertEquals(propertyValue, ((StringType)propertyComponent.getValue()).getValue());
assertNotNull(designationComponent);
Iterator<Parameters.ParametersParameterComponent> partParameter = designationComponent.getPart().iterator();
designationComponent = partParameter.next();
assertEquals("language", designationComponent.getName());
assertEquals(LANGUAGE, designationComponent.getValue().toString());
designationComponent = partParameter.next();
assertEquals("use", designationComponent.getName());
Coding coding = (Coding)designationComponent.getValue();
assertNotNull(coding, "Coding value returned via designation use should NOT be NULL!");
assertEquals("code", coding.getCode());
assertEquals("system", coding.getSystem());
assertEquals("display", coding.getDisplay());
designationComponent = partParameter.next();
assertEquals("value", designationComponent.getName());
assertEquals("some value", designationComponent.getValue().toString());
}
private static class MyCodeSystemProvider implements IResourceProvider {
private UriType myLastUrl;
private CodeType myLastCode;
private Parameters myNextReturnParams;
private IValidationSupport.LookupCodeResult myNextLookupCodeResult;
@Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters= {
@OperationParam(name="name", type=StringType.class, min=1),
@OperationParam(name="version", type=StringType.class, min=0),
@OperationParam(name="display", type=StringType.class, min=1),
@OperationParam(name="abstract", type=BooleanType.class, min=1),
@OperationParam(name="property", min = 0, max = OperationParam.MAX_UNLIMITED)
})
public Parameters lookup(
HttpServletRequest theServletRequest,
@OperationParam(name="code", min=0, max=1) CodeType theCode,
@OperationParam(name="system", min=0, max=1) UriType theSystem,
@OperationParam(name="coding", min=0, max=1) Coding theCoding,
@OperationParam(name="version", min=0, max=1) StringType theVersion,
@OperationParam(name="displayLanguage", min=0, max=1) CodeType theDisplayLanguage,
@OperationParam(name="property", min = 0, max = OperationParam.MAX_UNLIMITED) List<CodeType> thePropertyNames,
RequestDetails theRequestDetails
) {
myLastCode = theCode;
myLastUrl = theSystem;
myNextReturnParams = (Parameters)myNextLookupCodeResult.toParameters(theRequestDetails.getFhirContext(), thePropertyNames);
return myNextReturnParams;
}
@Override
public Class<? extends IBaseResource> getResourceType() {
return CodeSystem.class;
}
}
}

View File

@ -0,0 +1,252 @@
package org.hl7.fhir.r4.validation;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
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.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
import jakarta.servlet.http.HttpServletRequest;
import org.hl7.fhir.common.hapi.validation.ILookupCodeTest.ILookupCodeSupportedPropertyTest;
import org.hl7.fhir.common.hapi.validation.ILookupCodeTest.ILookupCodeUnsupportedPropertyTypeTest;
import org.hl7.fhir.common.hapi.validation.ILookupCodeTest.IMyCodeSystemProvider;
import org.hl7.fhir.common.hapi.validation.ILookupCodeTest.IMySimpleCodeSystemProvider;
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;
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.Parameters;
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.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.params.provider.Arguments;
import java.util.List;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class LookupCodeR4Test {
private static final FhirContext ourCtx = FhirContext.forR4Cached();
@RegisterExtension
public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx);
private final RemoteTerminologyServiceValidationSupport mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx);
@BeforeEach
public void before() {
String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
mySvc.setBaseUrl(baseUrl);
mySvc.addClientInterceptor(new LoggingInterceptor(true));
}
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@Nested
class LookupCodeUnsupportedPropertyTypeR4Test implements ILookupCodeUnsupportedPropertyTypeTest {
private final MySimplePropertyCodeSystemProviderR4 myMySimplePropertyCodeSystemProvider = new MySimplePropertyCodeSystemProviderR4();
@Override
public IMySimpleCodeSystemProvider getCodeSystemProvider() {
return myMySimplePropertyCodeSystemProvider;
}
@Override
public RemoteTerminologyServiceValidationSupport getService() {
return mySvc;
}
@Override
public String getInvalidValueErrorCode() {
return "HAPI-2452";
}
@Override
public String getInvalidValueErrorCodeForConvert() {
return "HAPI-2453";
}
@BeforeEach
public void before() {
// TODO: use another type when "code" is added to the supported types
myMySimplePropertyCodeSystemProvider.myPropertyName = "somePropertyName";
myMySimplePropertyCodeSystemProvider.myPropertyValue = new CodeType("someCode");
ourRestfulServerExtension.getRestfulServer().registerProvider(myMySimplePropertyCodeSystemProvider);
}
}
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@Nested
class ILookupCodeSupportedPropertyR4Test implements ILookupCodeSupportedPropertyTest {
private final MyCodeSystemProviderR4 myCodeSystemProvider = new MyCodeSystemProviderR4();
@Override
public IMyCodeSystemProvider getCodeSystemProvider() {
return myCodeSystemProvider;
}
@Override
public RemoteTerminologyServiceValidationSupport getService() {
return mySvc;
}
@BeforeEach
public void before() {
ourRestfulServerExtension.getRestfulServer().registerProvider(myCodeSystemProvider);
}
@Override
public void verifyProperty(IValidationSupport.BaseConceptProperty theConceptProperty, String theExpectedPropertName, IBaseDatatype theExpectedValue) {
assertEquals(theExpectedPropertName, theConceptProperty.getPropertyName());
String type = theConceptProperty.getType();
switch (type) {
case IValidationSupport.TYPE_STRING -> {
assertTrue(theExpectedValue instanceof StringType);
StringType stringValue = (StringType) theExpectedValue;
assertTrue(theConceptProperty instanceof IValidationSupport.StringConceptProperty);
IValidationSupport.StringConceptProperty stringConceptProperty = (IValidationSupport.StringConceptProperty) theConceptProperty;
assertEquals(stringValue.getValue(), stringConceptProperty.getValue());
}
case IValidationSupport.TYPE_CODING -> {
assertTrue(theExpectedValue instanceof Coding);
Coding coding = (Coding) theExpectedValue;
assertTrue(theConceptProperty instanceof IValidationSupport.CodingConceptProperty);
IValidationSupport.CodingConceptProperty codingConceptProperty = (IValidationSupport.CodingConceptProperty) theConceptProperty;
assertEquals(coding.getCode(), codingConceptProperty.getCode());
assertEquals(coding.getSystem(), codingConceptProperty.getCodeSystem());
assertEquals(coding.getDisplay(), codingConceptProperty.getDisplay());
}
default ->
throw new InternalErrorException(Msg.code(2451) + "Property type " + type + " is not supported.");
}
}
public Stream<Arguments> getEmptyPropertyValues() {
return Stream.of(
Arguments.arguments(new StringType()),
Arguments.arguments(new StringType("")),
Arguments.arguments(new Coding()),
Arguments.arguments(new Coding("", null, null)),
Arguments.arguments(new Coding("", "", null)),
Arguments.arguments(new Coding(null, "", null))
);
}
public Stream<Arguments> getPropertyValues() {
return Stream.of(
Arguments.arguments(new StringType("value")),
Arguments.arguments(new Coding("code", "system", "display"))
);
}
public Stream<Arguments> getDesignations() {
return Stream.of(
Arguments.arguments(new IValidationSupport.ConceptDesignation().setLanguage("en").setUseCode("code1").setUseSystem("system-1").setUseDisplay("display").setValue("some value")),
Arguments.arguments(new IValidationSupport.ConceptDesignation().setUseCode("code2").setUseSystem("system1").setUseDisplay("display").setValue("someValue")),
Arguments.arguments(new IValidationSupport.ConceptDesignation().setUseCode("code2").setUseSystem("system1").setValue("someValue")),
Arguments.arguments(new IValidationSupport.ConceptDesignation().setUseCode("code2").setUseSystem("system1")),
Arguments.arguments(new IValidationSupport.ConceptDesignation().setUseCode("code2"))
);
}
}
static class MySimplePropertyCodeSystemProviderR4 implements IMySimpleCodeSystemProvider {
String myPropertyName;
Type myPropertyValue;
@Override
public IBaseDatatype getPropertyValue() {
return myPropertyValue;
}
@Override
public Class<? extends IBaseResource> getResourceType() {
return CodeSystem.class;
}
@Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters= {
@OperationParam(name = "name", type = StringType.class, min = 1),
@OperationParam(name = "version", type = StringType.class, min = 0),
@OperationParam(name = "display", type = StringType.class, min = 1),
@OperationParam(name = "abstract", type = BooleanType.class, min = 1),
@OperationParam(name = "property", min = 0, max = OperationParam.MAX_UNLIMITED)
})
public Parameters 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
) {
Parameters.ParametersParameterComponent component = new Parameters.ParametersParameterComponent();
component.setName("property");
component.addPart(new Parameters.ParametersParameterComponent().setName("code").setValue(new CodeType(myPropertyName)));
component.addPart(new Parameters.ParametersParameterComponent().setName("value").setValue(myPropertyValue));
return new Parameters().addParameter(component);
}
}
static class MyCodeSystemProviderR4 implements IMyCodeSystemProvider {
private UriType mySystemUrl;
private CodeType myCode;
private IValidationSupport.LookupCodeResult myLookupCodeResult;
@Override
public void setLookupCodeResult(IValidationSupport.LookupCodeResult theLookupCodeResult) {
myLookupCodeResult = theLookupCodeResult;
}
@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<StringType> thePropertyNames,
RequestDetails theRequestDetails
) {
myCode = theCode;
mySystemUrl = theSystem;
return myLookupCodeResult.toParameters(theRequestDetails.getFhirContext(), thePropertyNames);
}
@Override
public Class<? extends IBaseResource> getResourceType() {
return CodeSystem.class;
}
@Override
public String getCode() {
return myCode != null ? myCode.getValueAsString() : null;
}
@Override
public String getSystem() {
return mySystemUrl != null ? mySystemUrl.getValueAsString() : null;
}
}
}

View File

@ -1,11 +1,10 @@
package org.hl7.fhir.common.hapi.validation.support;
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.context.support.LookupCodeRequest;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.parser.IJsonLikeParser;
import ca.uhn.fhir.rest.annotation.IdParam;
@ -24,6 +23,8 @@ import ca.uhn.fhir.rest.server.IResourceProvider;
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;
@ -39,19 +40,15 @@ 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.Assertions;
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 jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.lessThan;
@ -61,9 +58,8 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class RemoteTerminologyServiceValidationSupportTest {
public class RemoteTerminologyServiceValidationSupportR4Test {
private static final String DISPLAY = "DISPLAY";
private static final String LANGUAGE = "en";
private static final String CODE_SYSTEM = "CODE_SYS";
private static final String CODE_SYSTEM_NAME = "Code System";
private static final String CODE = "CODE";
@ -84,27 +80,20 @@ public class RemoteTerminologyServiceValidationSupportTest {
private static final FhirContext ourCtx = FhirContext.forR4Cached();
@RegisterExtension
public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx);
public static RestfulServerExtension myRestfulServerExtension = new RestfulServerExtension(ourCtx);
private MyValueSetProvider myValueSetProvider;
private RemoteTerminologyServiceValidationSupport mySvc;
private MyCodeSystemProvider myCodeSystemProvider;
private MyConceptMapProvider myConceptMapProvider;
private final MyValueSetProvider myValueSetProvider = new MyValueSetProvider();
private final MyCodeSystemProvider myCodeSystemProvider = new MyCodeSystemProvider();
private final MyConceptMapProvider myConceptMapProvider = new MyConceptMapProvider();
private final RemoteTerminologyServiceValidationSupport mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx);
@BeforeEach
public void before() {
myValueSetProvider = new MyValueSetProvider();
ourRestfulServerExtension.getRestfulServer().registerProvider(myValueSetProvider);
myRestfulServerExtension.getRestfulServer().registerProvider(myValueSetProvider);
myRestfulServerExtension.getRestfulServer().registerProvider(myCodeSystemProvider);
myRestfulServerExtension.getRestfulServer().registerProvider(myConceptMapProvider);
myCodeSystemProvider = new MyCodeSystemProvider();
ourRestfulServerExtension.getRestfulServer().registerProvider(myCodeSystemProvider);
myConceptMapProvider = new MyConceptMapProvider();
ourRestfulServerExtension.getRestfulServer().registerProvider(myConceptMapProvider);
String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx);
String baseUrl = "http://localhost:" + myRestfulServerExtension.getPort();
mySvc.setBaseUrl(baseUrl);
mySvc.addClientInterceptor(new LoggingInterceptor(true));
}
@ -117,78 +106,7 @@ public class RemoteTerminologyServiceValidationSupportTest {
@Test
public void testValidateCode_withBlankCode_returnsNull() {
IValidationSupport.CodeValidationResult outcome = mySvc.validateCode(null, null, CODE_SYSTEM, "", DISPLAY, VALUE_SET_URL);
assertNull(outcome);
}
@Test
public void testLookupCode_forCodeSystemWithAllParams_returnsCorrectParameters() {
myCodeSystemProvider.myNextLookupCodeResult = new IValidationSupport.LookupCodeResult();
myCodeSystemProvider.myNextLookupCodeResult.setFound(true);
myCodeSystemProvider.myNextLookupCodeResult.setCodeSystemVersion(CODE_SYSTEM);
myCodeSystemProvider.myNextLookupCodeResult.setSearchedForCode(CODE);
myCodeSystemProvider.myNextLookupCodeResult.setCodeSystemDisplayName(CODE_SYSTEM_NAME);
myCodeSystemProvider.myNextLookupCodeResult.setCodeDisplay(DISPLAY);
// property
String propertyName = "birthDate";
String propertyValue = "1930-01-01";
IValidationSupport.BaseConceptProperty property = new IValidationSupport.StringConceptProperty(propertyName, propertyValue);
myCodeSystemProvider.myNextLookupCodeResult.getProperties().add(property);
// designation
IValidationSupport.ConceptDesignation designation = new IValidationSupport.ConceptDesignation();
designation.setLanguage("en");
designation.setUseCode("code");
designation.setUseSystem("system");
designation.setUseDisplay("display");
designation.setValue("some value");
myCodeSystemProvider.myNextLookupCodeResult.getDesignations().add(designation);
IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(null, new LookupCodeRequest(CODE_SYSTEM, CODE, null, Set.of("birthDate")));
assertNotNull(outcome, "Call to lookupCode() should return a non-NULL result!");
assertEquals(DISPLAY, outcome.getCodeDisplay());
assertEquals(CODE_SYSTEM, outcome.getCodeSystemVersion());
assertEquals(CODE_SYSTEM_NAME, myCodeSystemProvider.myNextReturnParams.getParameterValue("name").toString());
assertEquals(CODE, myCodeSystemProvider.myLastCode.getCode());
assertEquals(CODE_SYSTEM, myCodeSystemProvider.myLastUrl.getValueAsString());
Parameters.ParametersParameterComponent propertyComponent = myCodeSystemProvider.myNextReturnParams.getParameter("property");
assertNotNull(propertyComponent);
Iterator<Parameters.ParametersParameterComponent> propertyComponentIterator = propertyComponent.getPart().iterator();
propertyComponent = propertyComponentIterator.next();
assertEquals("code", propertyComponent.getName());
assertEquals(propertyName, ((StringType)propertyComponent.getValue()).getValue());
propertyComponent = propertyComponentIterator.next();
assertEquals("value", propertyComponent.getName());
assertEquals(propertyValue, ((StringType)propertyComponent.getValue()).getValue());
Parameters.ParametersParameterComponent designationComponent = myCodeSystemProvider.myNextReturnParams.getParameter("designation");
Iterator<Parameters.ParametersParameterComponent> partParameter = designationComponent.getPart().iterator();
designationComponent = partParameter.next();
assertEquals("language", designationComponent.getName());
assertEquals(LANGUAGE, designationComponent.getValue().toString());
designationComponent = partParameter.next();
assertEquals("use", designationComponent.getName());
Coding coding = (Coding)designationComponent.getValue();
assertNotNull(coding, "Coding value returned via designation use should NOT be NULL!");
assertEquals("code", coding.getCode());
assertEquals("system", coding.getSystem());
assertEquals("display", coding.getDisplay());
designationComponent = partParameter.next();
assertEquals("value", designationComponent.getName());
assertEquals("some value", designationComponent.getValue().toString());
}
@Test
public void testLookupCode_forCodeSystemWithBlankCode_throwsException() {
Assertions.assertThrows(IllegalArgumentException.class,
() -> mySvc.lookupCode(null, new LookupCodeRequest(CODE_SYSTEM, "")));
assertNull(outcome);
}
@Test
@ -199,26 +117,26 @@ public class RemoteTerminologyServiceValidationSupportTest {
assertNotNull(outcome);
assertEquals(CODE, outcome.getCode());
assertEquals(DISPLAY, outcome.getDisplay());
assertNull(outcome.getSeverity());
assertNull(outcome.getMessage());
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);
assertNull(myValueSetProvider.myLastValueSet);
}
@Test
void testFetchValueSet_forcesSummaryFalse() {
// given
// given
myValueSetProvider.myNextReturnValueSets = new ArrayList<>();
// when
mySvc.fetchValueSet(VALUE_SET_URL);
// then
assertEquals(SummaryEnum.FALSE, myValueSetProvider.myLastSummaryParam);
// then
assertEquals(SummaryEnum.FALSE, myValueSetProvider.myLastSummaryParam);
}
@Test
@ -227,8 +145,8 @@ public class RemoteTerminologyServiceValidationSupportTest {
IValidationSupport.CodeValidationResult outcome = mySvc.validateCode(null, null, CODE_SYSTEM, CODE, DISPLAY, VALUE_SET_URL);
assertNotNull(outcome);
assertNull(outcome.getCode());
assertNull(outcome.getDisplay());
assertNull(outcome.getCode());
assertNull(outcome.getDisplay());
assertEquals(IValidationSupport.IssueSeverity.ERROR, outcome.getSeverity());
assertEquals(ERROR_MESSAGE, outcome.getMessage());
@ -236,7 +154,7 @@ public class RemoteTerminologyServiceValidationSupportTest {
assertEquals(DISPLAY, myValueSetProvider.myLastDisplay.getValue());
assertEquals(CODE_SYSTEM, myValueSetProvider.myLastSystem.getValue());
assertEquals(VALUE_SET_URL, myValueSetProvider.myLastUrl.getValue());
assertNull(myValueSetProvider.myLastValueSet);
assertNull(myValueSetProvider.myLastValueSet);
}
@Test
@ -252,11 +170,11 @@ public class RemoteTerminologyServiceValidationSupportTest {
assertNotNull(outcome);
assertEquals(CODE, outcome.getCode());
assertEquals(DISPLAY, outcome.getDisplay());
assertNull(outcome.getSeverity());
assertNull(outcome.getMessage());
assertNull(outcome.getSeverity());
assertNull(outcome.getMessage());
assertEquals(CODE, myCodeSystemProvider.myLastCode.getCode());
assertEquals(CODE_SYSTEM, myCodeSystemProvider.myLastUrl.getValueAsString());
assertEquals(CODE, myCodeSystemProvider.myCode.getCode());
assertEquals(CODE_SYSTEM, myCodeSystemProvider.mySystemUrl.getValueAsString());
}
@ -271,14 +189,14 @@ public class RemoteTerminologyServiceValidationSupportTest {
assertNotNull(outcome);
assertEquals(CODE, outcome.getCode());
assertEquals(DISPLAY, outcome.getDisplay());
assertNull(outcome.getSeverity());
assertNull(outcome.getMessage());
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);
assertNull(myValueSetProvider.myLastValueSet);
}
/**
@ -292,7 +210,7 @@ public class RemoteTerminologyServiceValidationSupportTest {
valueSet.setUrl(VALUE_SET_URL);
IValidationSupport.CodeValidationResult outcome = mySvc.validateCodeInValueSet(null, new ConceptValidationOptions().setInferSystem(true), null, CODE, DISPLAY, valueSet);
assertNull(outcome);
assertNull(outcome);
}
@Test
@ -336,7 +254,7 @@ public class RemoteTerminologyServiceValidationSupportTest {
TranslateConceptResults results = mySvc.translateConcept(request);
assertNotNull(results);
assertTrue(results.getResult());
assertTrue(results.getResult());
assertEquals(results.getResults().size(), 2);
for(TranslateConceptResult result : results.getResults()) {
assertEquals(singleResult, result);
@ -361,7 +279,7 @@ public class RemoteTerminologyServiceValidationSupportTest {
TranslateConceptResults results = mySvc.translateConcept(request);
assertNotNull(results);
assertFalse(results.getResult());
assertFalse(results.getResult());
assertEquals(results.getResults().size(), 0);
assertNull(myConceptMapProvider.myLastCodeableConcept);
@ -556,7 +474,7 @@ public class RemoteTerminologyServiceValidationSupportTest {
myValueSetProvider.myNextReturnValueSets = new ArrayList<>();
boolean outcome = mySvc.isValueSetSupported(null, "http://loinc.org/VS");
assertFalse(outcome);
assertFalse(outcome);
assertEquals("http://loinc.org/VS", myValueSetProvider.myLastUrlParam.getValue());
}
@ -566,7 +484,7 @@ public class RemoteTerminologyServiceValidationSupportTest {
myValueSetProvider.myNextReturnValueSets.add((ValueSet) new ValueSet().setId("ValueSet/123"));
boolean outcome = mySvc.isValueSetSupported(null, "http://loinc.org/VS");
assertTrue(outcome);
assertTrue(outcome);
assertEquals("http://loinc.org/VS", myValueSetProvider.myLastUrlParam.getValue());
}
@ -575,7 +493,7 @@ public class RemoteTerminologyServiceValidationSupportTest {
myCodeSystemProvider.myNextReturnCodeSystems = new ArrayList<>();
boolean outcome = mySvc.isCodeSystemSupported(null, "http://loinc.org");
assertFalse(outcome);
assertFalse(outcome);
assertEquals("http://loinc.org", myCodeSystemProvider.myLastUrlParam.getValue());
}
@ -585,30 +503,30 @@ public class RemoteTerminologyServiceValidationSupportTest {
myCodeSystemProvider.myNextReturnCodeSystems.add((CodeSystem) new CodeSystem().setId("CodeSystem/123"));
boolean outcome = mySvc.isCodeSystemSupported(null, "http://loinc.org");
assertTrue(outcome);
assertTrue(outcome);
assertEquals("http://loinc.org", myCodeSystemProvider.myLastUrlParam.getValue());
}
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);
}
myValueSetProvider.myNextReturnParams = new Parameters()
.addParameter("result", theResult)
.addParameter("display", theDisplay)
.addParameter("message", theMessage);
}
private static class MyCodeSystemProvider implements IResourceProvider {
static private class MyCodeSystemProvider implements IResourceProvider {
private SummaryEnum myLastSummaryParam;
private UriParam myLastUrlParam;
private List<CodeSystem> myNextReturnCodeSystems;
private UriType myLastUrl;
private CodeType myLastCode;
private Parameters myNextReturnParams;
private IValidationSupport.LookupCodeResult myNextLookupCodeResult;
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),
@ -617,37 +535,13 @@ public class RemoteTerminologyServiceValidationSupportTest {
public IBaseParameters validateCode(
HttpServletRequest theServletRequest,
@IdParam(optional = true) IdType theId,
@OperationParam(name = "url", min = 0, max = 1) UriType theCodeSystemUrl,
@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
) {
myLastUrl = theCodeSystemUrl;
myLastCode = theCode;
myNextReturnParams = (Parameters)myNextValidationResult.toParameters(ourCtx);
return myNextReturnParams;
}
@Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters= {
@OperationParam(name="name", type=StringType.class, min=1),
@OperationParam(name="version", type=StringType.class, min=0),
@OperationParam(name="display", type=StringType.class, min=1),
@OperationParam(name="abstract", type=BooleanType.class, min=1),
@OperationParam(name="property", min = 0, max = OperationParam.MAX_UNLIMITED)
})
public IBaseParameters lookup(
HttpServletRequest theServletRequest,
@OperationParam(name="code", min=0, max=1) CodeType theCode,
@OperationParam(name="system", min=0, max=1) UriType theSystem,
@OperationParam(name="coding", min=0, max=1) Coding theCoding,
@OperationParam(name="version", min=0, max=1) StringType theVersion,
@OperationParam(name="displayLanguage", min=0, max=1) CodeType theDisplayLanguage,
@OperationParam(name="property", min = 0, max = OperationParam.MAX_UNLIMITED) List<CodeType> thePropertyNames,
RequestDetails theRequestDetails
) {
myLastCode = theCode;
myLastUrl = theSystem;
myNextReturnParams = (Parameters)myNextLookupCodeResult.toParameters(theRequestDetails.getFhirContext(), thePropertyNames);
return myNextReturnParams;
myCode = theCode;
mySystemUrl = theSystem;
return myNextValidationResult.toParameters(ourCtx);
}
@Search
@ -657,11 +551,6 @@ public class RemoteTerminologyServiceValidationSupportTest {
assert myNextReturnCodeSystems != null;
return myNextReturnCodeSystems;
}
@Override
public Class<? extends IBaseResource> getResourceType() {
return CodeSystem.class;
}
}