diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java index 9528ee1d8ba..967aeb3a549 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IValidationSupport.java @@ -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()); } } } diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5512-codesystem-lookup-with-designation-no-language-nullpointerexception.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5512-codesystem-lookup-with-designation-no-language-nullpointerexception.yaml new file mode 100644 index 00000000000..b55b4044b51 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5512-codesystem-lookup-with-designation-no-language-nullpointerexception.yaml @@ -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.' diff --git a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemPropertiesTest.java b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemPropertiesTest.java index 66eaa679850..5ea44c64476 100644 --- a/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemPropertiesTest.java +++ b/hapi-fhir-jpaserver-test-dstu3/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemPropertiesTest.java @@ -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 parametersLookup() { - return CodeSystemLookupWithPropertiesUtil.parametersLookupWithProperties(); - } + public static Stream parametersLookup() { + return CodeSystemLookupWithPropertiesUtil.parametersLookupWithProperties(); + } - @ParameterizedTest - @MethodSource(value = "parametersLookup") - public void testLookup_withProperties_returnsCorrectParameters(List theLookupProperties, List 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 theLookupProperties, List 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 respParam = myClient - .operation() - .onType(CodeSystem.class) - .named(JpaConstants.OPERATION_LOOKUP) - .withParameter(Parameters.class, "code", new CodeType(ourCode)) - .andParameter("system", new UriType(ourCodeSystemUrl)); + // test + IOperationUntypedWithInputAndPartialOutput 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 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 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 propertyIterator = concept.getProperty().stream() - .filter(property -> theExpectedReturnedProperties.contains(property.getCode())).iterator(); + Iterator 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 parameterPartIterator = parameter.getPart().iterator(); + Iterator 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(); + } + } + } } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/RemoteTerminologyServiceResourceProviderR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/RemoteTerminologyServiceResourceProviderR4Test.java index 4e543edd508..823438e450e 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/RemoteTerminologyServiceResourceProviderR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/RemoteTerminologyServiceResourceProviderR4Test.java @@ -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 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 find(@RequiredParam(name = "url") UriParam theUrlParam) { - myLastUrlParam = theUrlParam; - assert myNextReturnCodeSystems != null; - return myNextReturnCodeSystems; - } - @Override public Class getResourceType() { return CodeSystem.class; @@ -211,14 +195,9 @@ public class RemoteTerminologyServiceResourceProviderR4Test { private static class MyValueSetProvider implements IResourceProvider { private Parameters myNextReturnParams; - private List 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 find(@RequiredParam(name = "url") UriParam theUrlParam) { - myLastUrlParam = theUrlParam; - assert myNextReturnValueSets != null; - return myNextReturnValueSets; - } - @Override public Class getResourceType() { return ValueSet.class; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemPropertiesTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemPropertiesTest.java index 7338a0b4174..f96556ddc36 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemPropertiesTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4CodeSystemPropertiesTest.java @@ -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 parametersLookup() { - return CodeSystemLookupWithPropertiesUtil.parametersLookupWithProperties(); - } + public static Stream parametersLookup() { + return CodeSystemLookupWithPropertiesUtil.parametersLookupWithProperties(); + } - @ParameterizedTest - @MethodSource(value = "parametersLookup") - public void testLookup_withProperties_returnsCorrectParameters(List theLookupProperties, List 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 theLookupProperties, List 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 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 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 parameterPropertyIterator = parameters.getParameters("property").iterator(); + assertTrue(parameters.hasParameter("property")); + Iterator parameterPropertyIterator = parameters.getParameters("property").iterator(); - Iterator propertyIterator = concept.getProperty().stream() - .filter(property -> theExpectedReturnedProperties.contains(property.getCode())).iterator(); + Iterator 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 parameterPartIterator = parameter.getPart().iterator(); + assertTrue(parameterPropertyIterator.hasNext()); + ParametersParameterComponent parameter = parameterPropertyIterator.next(); + Iterator 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()); + } + } } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcDeltaR4Test.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcDeltaR4Test.java index 7ab96037c26..24b0673fd16 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcDeltaR4Test.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcDeltaR4Test.java @@ -510,7 +510,7 @@ public class TerminologySvcDeltaR4Test extends BaseJpaR4Test { List 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()); diff --git a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5CodeSystemPropertiesTest.java b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5CodeSystemPropertiesTest.java index e8b5be13fb4..5a9f26f8c1c 100644 --- a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5CodeSystemPropertiesTest.java +++ b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/provider/r5/ResourceProviderR5CodeSystemPropertiesTest.java @@ -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 parametersLookup() { - return CodeSystemLookupWithPropertiesUtil.parametersLookupWithProperties(); - } - @ParameterizedTest - @MethodSource(value = "parametersLookup") - public void testLookup_withProperties_returnsCorrectParameters(List theLookupProperties, List 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 parametersLookup() { + return CodeSystemLookupWithPropertiesUtil.parametersLookupWithProperties(); + } - // test - IOperationUntypedWithInputAndPartialOutput 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 theLookupProperties, List 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 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 parameterPropertyIterator = parameters.getParameters("property").iterator(); + assertTrue(parameters.hasParameter("property")); + Iterator parameterPropertyIterator = parameters.getParameters("property").iterator(); - Iterator propertyIterator = concept.getProperty().stream() - .filter(property -> theExpectedReturnedProperties.contains(property.getCode())).iterator(); + Iterator 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 parameterPartIterator = parameter.getPart().iterator(); + assertTrue(parameterPropertyIterator.hasNext()); + ParametersParameterComponent parameter = parameterPropertyIterator.next(); + Iterator 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()); + } + } } diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/provider/CodeSystemLookupWithPropertiesUtil.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/provider/CodeSystemLookupWithPropertiesUtil.java index baf2b8c623f..d68fcee92ba 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/provider/CodeSystemLookupWithPropertiesUtil.java +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/provider/CodeSystemLookupWithPropertiesUtil.java @@ -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 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)), diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupport.java index a44cce1ee7b..08f94902a66 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupport.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupport.java @@ -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 myClientInterceptors = new ArrayList<>(); + private final List 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 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 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 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 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 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. "..." */ public void setBaseUrl(String theBaseUrl) { Validate.notBlank(theBaseUrl, "theBaseUrl must be provided"); diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/ILookupCodeTest.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/ILookupCodeTest.java new file mode 100644 index 00000000000..966a795eef3 --- /dev/null +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/ILookupCodeTest.java @@ -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 getEmptyPropertyValues(); + + Stream getPropertyValues(); + + Stream 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 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 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 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 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(); + } +} diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/LookupCodeDstu3Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/LookupCodeDstu3Test.java new file mode 100644 index 00000000000..6bf78229f1e --- /dev/null +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/LookupCodeDstu3Test.java @@ -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 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 getPropertyValues() { + return Stream.of( + Arguments.arguments(new StringType("value")), + Arguments.arguments(new Coding("code", "system", "display")) + ); + } + + public Stream 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 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 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 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 thePropertyNames, + RequestDetails theRequestDetails + ) { + myCode = theCode; + mySystemUrl = theSystem; + + return (Parameters)myLookupCodeResult.toParameters(theRequestDetails.getFhirContext(), thePropertyNames); + } + } +} diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/RemoteTerminologyServiceValidationSupportDstu3Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/RemoteTerminologyServiceValidationSupportDstu3Test.java deleted file mode 100644 index af961377fda..00000000000 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/dstu3/hapi/validation/RemoteTerminologyServiceValidationSupportDstu3Test.java +++ /dev/null @@ -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 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 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 thePropertyNames, - RequestDetails theRequestDetails - ) { - myLastCode = theCode; - myLastUrl = theSystem; - - myNextReturnParams = (Parameters)myNextLookupCodeResult.toParameters(theRequestDetails.getFhirContext(), thePropertyNames); - return myNextReturnParams; - } - - @Override - public Class getResourceType() { - return CodeSystem.class; - } - } -} diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/LookupCodeR4Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/LookupCodeR4Test.java new file mode 100644 index 00000000000..9fb662a8a3b --- /dev/null +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/LookupCodeR4Test.java @@ -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 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 getPropertyValues() { + return Stream.of( + Arguments.arguments(new StringType("value")), + Arguments.arguments(new Coding("code", "system", "display")) + ); + } + + + + public Stream 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 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 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 thePropertyNames, + RequestDetails theRequestDetails + ) { + myCode = theCode; + mySystemUrl = theSystem; + return myLookupCodeResult.toParameters(theRequestDetails.getFhirContext(), thePropertyNames); + } + @Override + public Class getResourceType() { + return CodeSystem.class; + } + + @Override + public String getCode() { + return myCode != null ? myCode.getValueAsString() : null; + } + + @Override + public String getSystem() { + return mySystemUrl != null ? mySystemUrl.getValueAsString() : null; + } + } +} diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupportTest.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/RemoteTerminologyServiceValidationSupportR4Test.java similarity index 74% rename from hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupportTest.java rename to hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/RemoteTerminologyServiceValidationSupportR4Test.java index d98785bb2aa..eca05ae6d05 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/RemoteTerminologyServiceValidationSupportTest.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/RemoteTerminologyServiceValidationSupportR4Test.java @@ -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 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 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 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 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 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 getResourceType() { - return CodeSystem.class; - } }