Add support for returning subproperties for RemoteTerminologyService lookup (#5936)
* Add support for returning subproperties in the RemoteTerminologyService * Add clarifying comments to code. Simplify logic to filter properties. * Update sub-property iteration logic in the conversion code to make it more readable * Fix spotless errors * Add a few more test cases to test property filtering for the lookup
This commit is contained in:
parent
14c364dffd
commit
bff59b6c50
|
@ -530,8 +530,13 @@ public interface IValidationSupport {
|
|||
public abstract String getType();
|
||||
}
|
||||
|
||||
// The reason these cannot be declared within an enum is because a Remote Terminology Service
|
||||
// can support arbitrary types. We do not restrict against the types in the spec.
|
||||
// Some of the types in the spec are not yet implemented as well.
|
||||
// @see https://github.com/hapifhir/hapi-fhir/issues/5700
|
||||
String TYPE_STRING = "string";
|
||||
String TYPE_CODING = "Coding";
|
||||
String TYPE_GROUP = "group";
|
||||
|
||||
class StringConceptProperty extends BaseConceptProperty {
|
||||
private final String myValue;
|
||||
|
@ -589,6 +594,31 @@ public interface IValidationSupport {
|
|||
}
|
||||
}
|
||||
|
||||
class GroupConceptProperty extends BaseConceptProperty {
|
||||
public GroupConceptProperty(String thePropertyName) {
|
||||
super(thePropertyName);
|
||||
}
|
||||
|
||||
private List<BaseConceptProperty> subProperties;
|
||||
|
||||
public BaseConceptProperty addSubProperty(BaseConceptProperty theProperty) {
|
||||
if (subProperties == null) {
|
||||
subProperties = new ArrayList<>();
|
||||
}
|
||||
subProperties.add(theProperty);
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<BaseConceptProperty> getSubProperties() {
|
||||
return subProperties != null ? subProperties : Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return TYPE_GROUP;
|
||||
}
|
||||
}
|
||||
|
||||
class CodeValidationResult {
|
||||
public static final String SOURCE_DETAILS = "sourceDetails";
|
||||
public static final String RESULT = "result";
|
||||
|
@ -871,8 +901,15 @@ public interface IValidationSupport {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the current LookupCodeResult instance into a IBaseParameters instance which is returned
|
||||
* to the client of the $lookup operation.
|
||||
* @param theContext the FHIR context used for running the operation
|
||||
* @param thePropertyNamesToFilter the properties which are passed as parameter to filter the result.
|
||||
* @return the output for the lookup operation.
|
||||
*/
|
||||
public IBaseParameters toParameters(
|
||||
FhirContext theContext, List<? extends IPrimitiveType<String>> thePropertyNames) {
|
||||
FhirContext theContext, List<? extends IPrimitiveType<String>> thePropertyNamesToFilter) {
|
||||
|
||||
IBaseParameters retVal = ParametersUtil.newInstance(theContext);
|
||||
if (isNotBlank(getCodeSystemDisplayName())) {
|
||||
|
@ -886,50 +923,29 @@ public interface IValidationSupport {
|
|||
|
||||
if (myProperties != null) {
|
||||
|
||||
Set<String> properties = Collections.emptySet();
|
||||
if (thePropertyNames != null) {
|
||||
properties = thePropertyNames.stream()
|
||||
final List<BaseConceptProperty> propertiesToReturn;
|
||||
if (thePropertyNamesToFilter != null && !thePropertyNamesToFilter.isEmpty()) {
|
||||
// TODO MM: The logic to filter of properties could actually be moved to the lookupCode provider.
|
||||
// That is where the rest of the lookupCode input parameter handling is done.
|
||||
// This was left as is for now but can be done with next opportunity.
|
||||
Set<String> propertyNameList = thePropertyNamesToFilter.stream()
|
||||
.map(IPrimitiveType::getValueAsString)
|
||||
.collect(Collectors.toSet());
|
||||
propertiesToReturn = myProperties.stream()
|
||||
.filter(p -> propertyNameList.contains(p.getPropertyName()))
|
||||
.collect(Collectors.toList());
|
||||
} else {
|
||||
propertiesToReturn = myProperties;
|
||||
}
|
||||
|
||||
for (BaseConceptProperty next : myProperties) {
|
||||
String propertyName = next.getPropertyName();
|
||||
|
||||
if (!properties.isEmpty() && !properties.contains(propertyName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (BaseConceptProperty next : propertiesToReturn) {
|
||||
IBase property = ParametersUtil.addParameterToParameters(theContext, retVal, "property");
|
||||
ParametersUtil.addPartCode(theContext, property, "code", propertyName);
|
||||
|
||||
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());
|
||||
}
|
||||
populateProperty(theContext, property, next);
|
||||
}
|
||||
}
|
||||
|
||||
if (myDesignations != null) {
|
||||
for (ConceptDesignation next : myDesignations) {
|
||||
|
||||
IBase property = ParametersUtil.addParameterToParameters(theContext, retVal, "designation");
|
||||
ParametersUtil.addPartCode(theContext, property, "language", next.getLanguage());
|
||||
ParametersUtil.addPartCoding(
|
||||
|
@ -941,6 +957,41 @@ public interface IValidationSupport {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
private void populateProperty(
|
||||
FhirContext theContext, IBase theProperty, BaseConceptProperty theConceptProperty) {
|
||||
ParametersUtil.addPartCode(theContext, theProperty, "code", theConceptProperty.getPropertyName());
|
||||
String propertyType = theConceptProperty.getType();
|
||||
switch (propertyType) {
|
||||
case TYPE_STRING:
|
||||
StringConceptProperty stringConceptProperty = (StringConceptProperty) theConceptProperty;
|
||||
ParametersUtil.addPartString(theContext, theProperty, "value", stringConceptProperty.getValue());
|
||||
break;
|
||||
case TYPE_CODING:
|
||||
CodingConceptProperty codingConceptProperty = (CodingConceptProperty) theConceptProperty;
|
||||
ParametersUtil.addPartCoding(
|
||||
theContext,
|
||||
theProperty,
|
||||
"value",
|
||||
codingConceptProperty.getCodeSystem(),
|
||||
codingConceptProperty.getCode(),
|
||||
codingConceptProperty.getDisplay());
|
||||
break;
|
||||
case TYPE_GROUP:
|
||||
GroupConceptProperty groupConceptProperty = (GroupConceptProperty) theConceptProperty;
|
||||
if (groupConceptProperty.getSubProperties().isEmpty()) {
|
||||
break;
|
||||
}
|
||||
groupConceptProperty.getSubProperties().forEach(p -> {
|
||||
IBase subProperty = ParametersUtil.addPart(theContext, theProperty, "subproperty", null);
|
||||
populateProperty(theContext, subProperty, p);
|
||||
});
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException(
|
||||
Msg.code(1739) + "Don't know how to handle " + theConceptProperty.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
public void setErrorMessage(String theErrorMessage) {
|
||||
myErrorMessage = theErrorMessage;
|
||||
}
|
||||
|
|
|
@ -433,7 +433,7 @@ public class ParametersUtil {
|
|||
addPart(theContext, theParameter, theName, coding);
|
||||
}
|
||||
|
||||
public static void addPart(FhirContext theContext, IBase theParameter, String theName, IBase theValue) {
|
||||
public static IBase addPart(FhirContext theContext, IBase theParameter, String theName, @Nullable IBase theValue) {
|
||||
BaseRuntimeElementCompositeDefinition<?> def =
|
||||
(BaseRuntimeElementCompositeDefinition<?>) theContext.getElementDefinition(theParameter.getClass());
|
||||
BaseRuntimeChildDefinition partChild = def.getChildByName("part");
|
||||
|
@ -448,12 +448,15 @@ public class ParametersUtil {
|
|||
name.setValue(theName);
|
||||
partChildElem.getChildByName("name").getMutator().addValue(part, name);
|
||||
|
||||
if (theValue != null) {
|
||||
if (theValue instanceof IBaseResource) {
|
||||
partChildElem.getChildByName("resource").getMutator().addValue(part, theValue);
|
||||
} else {
|
||||
partChildElem.getChildByName("value[x]").getMutator().addValue(part, theValue);
|
||||
}
|
||||
}
|
||||
return part;
|
||||
}
|
||||
|
||||
public static void addPartResource(
|
||||
FhirContext theContext, IBase theParameter, String theName, IBaseResource theValue) {
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
type: add
|
||||
issue: 5935
|
||||
title: "Remote Terminology Service can now return subproperty fields for a CodeSystem lookup operation.
|
||||
This can be done in DSTU3 and R4. R5 is not yet implemented."
|
|
@ -216,7 +216,7 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
|
|||
ParametersUtil.addParameterToParametersString(fhirContext, params, "language", displayLanguage);
|
||||
}
|
||||
for (String propertyName : theLookupCodeRequest.getPropertyNames()) {
|
||||
ParametersUtil.addParameterToParametersString(fhirContext, params, "property", propertyName);
|
||||
ParametersUtil.addParameterToParametersCode(fhirContext, params, "property", propertyName);
|
||||
}
|
||||
Class<? extends IBaseResource> codeSystemClass =
|
||||
myCtx.getResourceDefinition("CodeSystem").getImplementingClass();
|
||||
|
@ -229,7 +229,7 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
|
|||
if (outcome != null && !outcome.isEmpty()) {
|
||||
switch (fhirVersion) {
|
||||
case DSTU3:
|
||||
return generateLookupCodeResultDSTU3(
|
||||
return generateLookupCodeResultDstu3(
|
||||
code, system, (org.hl7.fhir.dstu3.model.Parameters) outcome);
|
||||
case R4:
|
||||
return generateLookupCodeResultR4(code, system, (Parameters) outcome);
|
||||
|
@ -243,7 +243,7 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
|
|||
return null;
|
||||
}
|
||||
|
||||
private LookupCodeResult generateLookupCodeResultDSTU3(
|
||||
private LookupCodeResult generateLookupCodeResultDstu3(
|
||||
String theCode, String theSystem, org.hl7.fhir.dstu3.model.Parameters outcomeDSTU3) {
|
||||
// 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
|
||||
|
@ -257,13 +257,7 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
|
|||
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.getValues().size() < 2) {
|
||||
continue;
|
||||
}
|
||||
BaseConceptProperty conceptProperty = createBaseConceptPropertyDstu3(part.getValues());
|
||||
BaseConceptProperty conceptProperty = createConceptPropertyDstu3(parameterComponent);
|
||||
if (conceptProperty != null) {
|
||||
result.getProperties().add(conceptProperty);
|
||||
}
|
||||
|
@ -289,36 +283,47 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
|
|||
return result;
|
||||
}
|
||||
|
||||
private static BaseConceptProperty createBaseConceptPropertyDstu3(List<org.hl7.fhir.dstu3.model.Base> theValues) {
|
||||
org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent part1 =
|
||||
(org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent) theValues.get(0);
|
||||
String propertyName = ((org.hl7.fhir.dstu3.model.CodeType) part1.getValue()).getValue();
|
||||
private static BaseConceptProperty createConceptPropertyDstu3(
|
||||
org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent theParameterComponent) {
|
||||
org.hl7.fhir.dstu3.model.Property property = theParameterComponent.getChildByName("part");
|
||||
|
||||
BaseConceptProperty conceptProperty = null;
|
||||
org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent part2 =
|
||||
(org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent) theValues.get(1);
|
||||
// The assumption here is that we may at east 2 elements in this part
|
||||
if (property == null || property.getValues().size() < 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
org.hl7.fhir.dstu3.model.Type value = part2.getValue();
|
||||
if (value == null) {
|
||||
return conceptProperty;
|
||||
List<org.hl7.fhir.dstu3.model.Base> values = property.getValues();
|
||||
org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent firstPart =
|
||||
(org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent) values.get(0);
|
||||
String propertyName = ((org.hl7.fhir.dstu3.model.CodeType) firstPart.getValue()).getValue();
|
||||
|
||||
org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent secondPart =
|
||||
(org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent) values.get(1);
|
||||
org.hl7.fhir.dstu3.model.Type value = secondPart.getValue();
|
||||
|
||||
if (value != null) {
|
||||
return createConceptPropertyDstu3(propertyName, value);
|
||||
}
|
||||
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;
|
||||
// TODO: add other property types as per FHIR spec https://github.com/hapifhir/hapi-fhir/issues/5699
|
||||
default:
|
||||
// other types will not fail for Remote Terminology
|
||||
conceptProperty = new StringConceptProperty(propertyName, value.toString());
|
||||
|
||||
String groupName = secondPart.getName();
|
||||
if (!"subproperty".equals(groupName)) {
|
||||
return null;
|
||||
}
|
||||
return conceptProperty;
|
||||
|
||||
// handle property group (a property containing sub-properties)
|
||||
GroupConceptProperty groupConceptProperty = new GroupConceptProperty(propertyName);
|
||||
|
||||
// we already retrieved the property name (group name) as first element, next will be the sub-properties.
|
||||
// there is no dedicated value for a property group as it is an aggregate
|
||||
for (int i = 1; i < values.size(); i++) {
|
||||
org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent nextPart =
|
||||
(org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent) values.get(i);
|
||||
BaseConceptProperty subProperty = createConceptPropertyDstu3(nextPart);
|
||||
if (subProperty != null) {
|
||||
groupConceptProperty.addSubProperty(subProperty);
|
||||
}
|
||||
}
|
||||
return groupConceptProperty;
|
||||
}
|
||||
|
||||
public static BaseConceptProperty createConceptProperty(final String theName, final IBaseDatatype theValue) {
|
||||
|
@ -396,13 +401,7 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
|
|||
String parameterTypeAsString = Objects.toString(parameterComponent.getValue(), null);
|
||||
switch (parameterComponent.getName()) {
|
||||
case "property":
|
||||
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.getValues().size() < 2) {
|
||||
continue;
|
||||
}
|
||||
BaseConceptProperty conceptProperty = createBaseConceptPropertyR4(part.getValues());
|
||||
BaseConceptProperty conceptProperty = createConceptPropertyR4(parameterComponent);
|
||||
if (conceptProperty != null) {
|
||||
result.getProperties().add(conceptProperty);
|
||||
}
|
||||
|
@ -428,34 +427,43 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
|
|||
return result;
|
||||
}
|
||||
|
||||
private static BaseConceptProperty createBaseConceptPropertyR4(List<Base> values) {
|
||||
ParametersParameterComponent part1 = (ParametersParameterComponent) values.get(0);
|
||||
String propertyName = ((CodeType) part1.getValue()).getValue();
|
||||
private static BaseConceptProperty createConceptPropertyR4(ParametersParameterComponent thePropertyComponent) {
|
||||
Property property = thePropertyComponent.getChildByName("part");
|
||||
|
||||
ParametersParameterComponent part2 = (ParametersParameterComponent) values.get(1);
|
||||
|
||||
Type value = part2.getValue();
|
||||
if (value == null) {
|
||||
// The assumption here is that we may at east 2 elements in this part
|
||||
if (property == null || property.getValues().size() < 2) {
|
||||
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;
|
||||
// TODO: add other property types as per FHIR spec https://github.com/hapifhir/hapi-fhir/issues/5699
|
||||
default:
|
||||
// other types will not fail for Remote Terminology
|
||||
conceptProperty = new StringConceptProperty(propertyName, value.toString());
|
||||
|
||||
List<Base> values = property.getValues();
|
||||
ParametersParameterComponent firstPart = (ParametersParameterComponent) values.get(0);
|
||||
String propertyName = ((CodeType) firstPart.getValue()).getValue();
|
||||
|
||||
ParametersParameterComponent secondPart = (ParametersParameterComponent) values.get(1);
|
||||
Type value = secondPart.getValue();
|
||||
|
||||
if (value != null) {
|
||||
return createConceptPropertyR4(propertyName, value);
|
||||
}
|
||||
return conceptProperty;
|
||||
|
||||
String groupName = secondPart.getName();
|
||||
if (!"subproperty".equals(groupName)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// handle property group (a property containing sub-properties)
|
||||
GroupConceptProperty groupConceptProperty = new GroupConceptProperty(propertyName);
|
||||
|
||||
// we already retrieved the property name (group name) as first element, next will be the sub-properties.
|
||||
// there is no dedicated value for a property group as it is an aggregate
|
||||
for (int i = 1; i < values.size(); i++) {
|
||||
ParametersParameterComponent nextPart = (ParametersParameterComponent) values.get(i);
|
||||
BaseConceptProperty subProperty = createConceptPropertyR4(nextPart);
|
||||
if (subProperty != null) {
|
||||
groupConceptProperty.addSubProperty(subProperty);
|
||||
}
|
||||
}
|
||||
return groupConceptProperty;
|
||||
}
|
||||
|
||||
private static BaseConceptProperty createConceptPropertyR4(final String theName, final Type theValue) {
|
||||
|
|
|
@ -1,23 +1,26 @@
|
|||
package org.hl7.fhir.common.hapi.validation;
|
||||
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport.BaseConceptProperty;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport.CodingConceptProperty;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport.ConceptDesignation;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport.GroupConceptProperty;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport.LookupCodeResult;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport.StringConceptProperty;
|
||||
import ca.uhn.fhir.context.support.LookupCodeRequest;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import org.hl7.fhir.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.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static ca.uhn.fhir.context.support.IValidationSupport.TYPE_CODING;
|
||||
import static ca.uhn.fhir.context.support.IValidationSupport.TYPE_GROUP;
|
||||
import static ca.uhn.fhir.context.support.IValidationSupport.TYPE_STRING;
|
||||
import static java.util.stream.IntStream.range;
|
||||
import static org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport.createConceptProperty;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
|
@ -25,6 +28,17 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
/**
|
||||
* Contains basic test setup for the $lookup operation against a {@link org.hl7.fhir.dstu3.model.CodeSystem}.
|
||||
* The provider for CodeSystem and the validation service needs to be configured by the implementing class.
|
||||
* This test interface contains the following:
|
||||
* (1) basic tests which are version independent, without any parameters, declared as default with @Test annotation
|
||||
* e.g. lookupCode_forCodeSystemWithBlankCode_throwsException
|
||||
* (2) test template methods for running version dependent tests with parameters, declared as default, without annotations
|
||||
* e.g. @see #verifyLookupCodeResult
|
||||
* (3) methods which help to assert part of the output (designation, property), declared as private
|
||||
* e.g. assertEqualConceptProperty
|
||||
*/
|
||||
public interface ILookupCodeTest {
|
||||
String DISPLAY = "DISPLAY";
|
||||
String LANGUAGE = "en";
|
||||
|
@ -33,26 +47,11 @@ public interface ILookupCodeTest {
|
|||
String CODE_SYSTEM_NAME = "Code System";
|
||||
String CODE = "CODE";
|
||||
|
||||
interface IValidationTest {
|
||||
|
||||
RemoteTerminologyServiceValidationSupport getService();
|
||||
IResourceProvider getCodeSystemProvider();
|
||||
}
|
||||
|
||||
@Nested
|
||||
interface ILookupCodeSupportedPropertyTest extends IValidationTest {
|
||||
IValidationSupport getService();
|
||||
IMyCodeSystemProvider getCodeSystemProvider();
|
||||
|
||||
Stream<Arguments> getEmptyPropertyValues();
|
||||
|
||||
Stream<Arguments> getPropertyValues();
|
||||
|
||||
Stream<Arguments> getDesignations();
|
||||
|
||||
void verifyProperty(IValidationSupport.BaseConceptProperty theConceptProperty, String theExpectedPropertName, IBaseDatatype theExpectedValue);
|
||||
|
||||
@Test
|
||||
default void testLookupCode_forCodeSystemWithBlankCode_throwsException() {
|
||||
default void lookupCode_forCodeSystemWithBlankCode_throwsException() {
|
||||
try {
|
||||
getService().lookupCode(null, new LookupCodeRequest(CODE_SYSTEM, ""));
|
||||
fail();
|
||||
|
@ -62,45 +61,28 @@ public interface ILookupCodeTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
default void testLookupCode_forCodeSystemWithPropertyInvalidType_throwsException() {
|
||||
default void lookupCode_forCodeSystemWithPropertyInvalidType_throwsException() {
|
||||
// test
|
||||
LookupCodeResult result = new LookupCodeResult();
|
||||
result.getProperties().add(new IValidationSupport.BaseConceptProperty("someProperty") {
|
||||
result.getProperties().add(new BaseConceptProperty("someProperty") {
|
||||
public String getType() {
|
||||
return "someUnsupportedType";
|
||||
}
|
||||
});
|
||||
getCodeSystemProvider().setLookupCodeResult(result);
|
||||
|
||||
// test and verify
|
||||
try {
|
||||
getService().lookupCode(null, new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, null));
|
||||
LookupCodeRequest request = new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, null);
|
||||
getService().lookupCode(null, request);
|
||||
fail();
|
||||
} catch (InternalErrorException e) {
|
||||
assertTrue(e.getMessage().contains("HAPI-1739: Don't know how to handle "));
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource(value = "getEmptyPropertyValues")
|
||||
default void testLookupCode_forCodeSystemWithPropertyEmptyValue_returnsCorrectParameters(IBaseDatatype thePropertyValue) {
|
||||
// setup
|
||||
final String propertyName = "someProperty";
|
||||
IValidationSupport.BaseConceptProperty property = createConceptProperty(propertyName, thePropertyValue);
|
||||
LookupCodeResult result = new LookupCodeResult();
|
||||
result.getProperties().add(property);
|
||||
getCodeSystemProvider().setLookupCodeResult(result);
|
||||
|
||||
// test
|
||||
LookupCodeResult outcome = getService().lookupCode(null, new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, List.of(propertyName)));
|
||||
|
||||
// verify
|
||||
assertNotNull(outcome);
|
||||
Optional<IValidationSupport.BaseConceptProperty> propertyOptional = outcome.getProperties().stream().findFirst().filter(a -> propertyName.equals(a.getPropertyName()));
|
||||
assertFalse(propertyOptional.isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
default void testLookupCode_forCodeSystemWithParameters_returnsCorrectParameters() {
|
||||
// setup
|
||||
default void lookupCode_forCodeSystem_returnsCorrectResult() {
|
||||
LookupCodeResult result = new LookupCodeResult().setFound(true).setSearchedForCode(CODE).setSearchedForSystem(CODE_SYSTEM);
|
||||
result.setCodeIsAbstract(false);
|
||||
result.setCodeSystemVersion(CODE_SYSTEM_VERSION);
|
||||
|
@ -108,111 +90,159 @@ public interface ILookupCodeTest {
|
|||
result.setCodeDisplay(DISPLAY);
|
||||
getCodeSystemProvider().setLookupCodeResult(result);
|
||||
|
||||
// test
|
||||
LookupCodeResult outcome = getService().lookupCode(null, new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, null));
|
||||
|
||||
// verify
|
||||
assertNotNull(outcome);
|
||||
assertEquals(CODE, getCodeSystemProvider().getCode());
|
||||
assertEquals(CODE_SYSTEM, getCodeSystemProvider().getSystem());
|
||||
assertEquals(result.getCodeSystemDisplayName(), outcome.getCodeSystemDisplayName());
|
||||
assertEquals(result.getCodeDisplay(), outcome.getCodeDisplay());
|
||||
assertEquals(result.getCodeSystemVersion(), outcome.getCodeSystemVersion());
|
||||
assertEquals(result.isCodeIsAbstract(), outcome.isCodeIsAbstract());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource(value = "getPropertyValues")
|
||||
default void testLookupCode_forCodeSystemWithProperty_returnsCorrectProperty(IBaseDatatype thePropertyValue) {
|
||||
// setup
|
||||
final String propertyName = "someProperty";
|
||||
LookupCodeResult result = new LookupCodeResult()
|
||||
.setFound(true).setSearchedForCode(CODE).setSearchedForSystem(CODE_SYSTEM);
|
||||
result.setCodeIsAbstract(false);
|
||||
result.setCodeSystemVersion(CODE_SYSTEM_VERSION);
|
||||
result.setCodeSystemDisplayName(CODE_SYSTEM_NAME);
|
||||
result.setCodeDisplay(DISPLAY);
|
||||
IValidationSupport.BaseConceptProperty property = createConceptProperty(propertyName, thePropertyValue);
|
||||
result.getProperties().add(property);
|
||||
getCodeSystemProvider().setLookupCodeResult(result);
|
||||
|
||||
// test
|
||||
LookupCodeResult outcome = getService().lookupCode(null, new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, List.of(propertyName)));
|
||||
|
||||
// verify
|
||||
assertNotNull(outcome);
|
||||
assertEquals(CODE, getCodeSystemProvider().getCode());
|
||||
assertEquals(CODE_SYSTEM, getCodeSystemProvider().getSystem());
|
||||
assertEquals(result.getCodeSystemDisplayName(), outcome.getCodeSystemDisplayName());
|
||||
assertEquals(result.getCodeDisplay(), outcome.getCodeDisplay());
|
||||
assertEquals(result.getCodeSystemVersion(), outcome.getCodeSystemVersion());
|
||||
assertEquals(result.isCodeIsAbstract(), outcome.isCodeIsAbstract());
|
||||
|
||||
Optional<IValidationSupport.BaseConceptProperty> propertyOptional = outcome.getProperties().stream().findFirst().filter(a -> propertyName.equals(a.getPropertyName()));
|
||||
assertTrue(propertyOptional.isPresent());
|
||||
IValidationSupport.BaseConceptProperty outputProperty = propertyOptional.get();
|
||||
|
||||
verifyProperty(outputProperty, propertyName, thePropertyValue);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource(value = "getDesignations")
|
||||
default void testLookupCode_withCodeSystemWithDesignation_returnsCorrectDesignation(final IValidationSupport.ConceptDesignation theConceptDesignation) {
|
||||
// setup
|
||||
LookupCodeResult result = new LookupCodeResult();
|
||||
result.getDesignations().add(theConceptDesignation);
|
||||
getCodeSystemProvider().setLookupCodeResult(result);
|
||||
|
||||
// test
|
||||
LookupCodeResult outcome = getService().lookupCode(null, new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, null));
|
||||
|
||||
// verify
|
||||
assertNotNull(outcome);
|
||||
|
||||
Collection<IValidationSupport.ConceptDesignation> designations = outcome.getDesignations();
|
||||
assertEquals(1, designations.size());
|
||||
|
||||
IValidationSupport.ConceptDesignation designation = designations.iterator().next();
|
||||
assertEquals(theConceptDesignation.getValue(), designation.getValue());
|
||||
assertEquals(theConceptDesignation.getLanguage(), designation.getLanguage());
|
||||
assertEquals(theConceptDesignation.getUseCode(), designation.getUseCode());
|
||||
assertEquals(theConceptDesignation.getUseSystem(), designation.getUseSystem());
|
||||
assertEquals(theConceptDesignation.getUseDisplay(), designation.getUseDisplay());
|
||||
// test and verify
|
||||
LookupCodeRequest request = new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, null);
|
||||
verifyLookupCodeResult(request, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
default void testLookupCode_withCodeSystemWithMultipleDesignations_returnsCorrectDesignations() {
|
||||
default void lookupCode_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");
|
||||
ConceptDesignation designation1 = new ConceptDesignation().setUseCode(code1).setUseSystem("system1").setValue("value1").setLanguage("en");
|
||||
ConceptDesignation designation2 = new 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 and verify
|
||||
LookupCodeRequest request = new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, null);
|
||||
verifyLookupCodeResult(request, result);
|
||||
}
|
||||
|
||||
default void verifyLookupWithEmptyPropertyValue(IBaseDatatype thePropertyValue) {
|
||||
// setup
|
||||
final String propertyName = "someProperty";
|
||||
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, null));
|
||||
LookupCodeRequest request = new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, List.of(propertyName));
|
||||
LookupCodeResult outcome = getService().lookupCode(null, request);
|
||||
|
||||
// verify
|
||||
assertNotNull(outcome);
|
||||
|
||||
Collection<IValidationSupport.ConceptDesignation> designations = outcome.getDesignations();
|
||||
assertEquals(2, designations.size());
|
||||
|
||||
for (IValidationSupport.ConceptDesignation designation : designations) {
|
||||
IValidationSupport.ConceptDesignation expectedDesignation = code1.equals(designation.getUseCode()) ? designation1 : designation2;
|
||||
assertEquals(expectedDesignation.getValue(), designation.getValue());
|
||||
assertEquals(expectedDesignation.getLanguage(), designation.getLanguage());
|
||||
assertEquals(expectedDesignation.getUseCode(), designation.getUseCode());
|
||||
assertEquals(expectedDesignation.getUseSystem(), designation.getUseSystem());
|
||||
assertEquals(expectedDesignation.getUseDisplay(), designation.getUseDisplay());
|
||||
Optional<BaseConceptProperty> propertyOptional = outcome.getProperties().stream().findFirst().filter(a -> propertyName.equals(a.getPropertyName()));
|
||||
assertFalse(propertyOptional.isPresent());
|
||||
}
|
||||
|
||||
default void verifyLookupWithProperty(List<IBaseDatatype> thePropertyValues, List<Integer> thePropertyIndexesToFilter) {
|
||||
// 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);
|
||||
List<String> propertyNamesToFilter = new ArrayList<>();
|
||||
for (int i = 0; i < thePropertyValues.size(); i++) {
|
||||
String currentPropertyName = propertyName + i;
|
||||
result.getProperties().add(createConceptProperty(currentPropertyName, thePropertyValues.get(i)));
|
||||
if (thePropertyIndexesToFilter.contains(i)) {
|
||||
propertyNamesToFilter.add(currentPropertyName);
|
||||
}
|
||||
}
|
||||
getCodeSystemProvider().setLookupCodeResult(result);
|
||||
|
||||
// test
|
||||
LookupCodeRequest request = new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, propertyNamesToFilter);
|
||||
|
||||
// verify
|
||||
result.getProperties().removeIf(p -> !propertyNamesToFilter.contains(p.getPropertyName()));
|
||||
verifyLookupCodeResult(request, result);
|
||||
}
|
||||
|
||||
default void verifyLookupWithSubProperties(List<IBaseDatatype> thePropertyValues) {
|
||||
// setup
|
||||
final String groupName = "group";
|
||||
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);
|
||||
final String subPropertyName = "someSubProperty";
|
||||
GroupConceptProperty group = new GroupConceptProperty(groupName);
|
||||
for (int i = 0; i < thePropertyValues.size(); i++) {
|
||||
group.addSubProperty(createConceptProperty(subPropertyName + i, thePropertyValues.get(i)));
|
||||
}
|
||||
result.getProperties().add(group);
|
||||
getCodeSystemProvider().setLookupCodeResult(result);
|
||||
|
||||
// test and verify
|
||||
LookupCodeRequest request = new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, List.of(groupName));
|
||||
verifyLookupCodeResult(request, result);
|
||||
}
|
||||
|
||||
default void verifyLookupCodeResult(LookupCodeRequest theRequest, LookupCodeResult theExpectedResult) {
|
||||
// test
|
||||
LookupCodeResult outcome = getService().lookupCode(null, theRequest);
|
||||
assertNotNull(outcome);
|
||||
|
||||
// verify
|
||||
assertNotNull(outcome);
|
||||
assertEquals(theRequest.getCode(), getCodeSystemProvider().getCode());
|
||||
assertEquals(theRequest.getSystem(), getCodeSystemProvider().getSystem());
|
||||
assertEquals(theExpectedResult.getCodeSystemDisplayName(), outcome.getCodeSystemDisplayName());
|
||||
assertEquals(theExpectedResult.getCodeDisplay(), outcome.getCodeDisplay());
|
||||
assertEquals(theExpectedResult.getCodeSystemVersion(), outcome.getCodeSystemVersion());
|
||||
assertEquals(theExpectedResult.isCodeIsAbstract(), outcome.isCodeIsAbstract());
|
||||
|
||||
assertEquals(theExpectedResult.getProperties().size(), outcome.getProperties().size());
|
||||
range(0, outcome.getProperties().size()).forEach(i -> assertEqualConceptProperty(theExpectedResult.getProperties().get(i), outcome.getProperties().get(i)));
|
||||
|
||||
assertEquals(theExpectedResult.getDesignations().size(), outcome.getDesignations().size());
|
||||
range(0, outcome.getDesignations().size()).forEach(i -> assertEqualConceptDesignation(theExpectedResult.getDesignations().get(i), outcome.getDesignations().get(i)));
|
||||
}
|
||||
|
||||
default void verifyLookupWithConceptDesignation(final ConceptDesignation theConceptDesignation) {
|
||||
// setup
|
||||
LookupCodeResult result = new LookupCodeResult();
|
||||
result.getDesignations().add(theConceptDesignation);
|
||||
getCodeSystemProvider().setLookupCodeResult(result);
|
||||
|
||||
// test and verify
|
||||
LookupCodeRequest request = new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, null);
|
||||
verifyLookupCodeResult(request, result);
|
||||
}
|
||||
|
||||
private void assertEqualConceptProperty(BaseConceptProperty theProperty, BaseConceptProperty theExpectedProperty) {
|
||||
assertEquals(theExpectedProperty.getPropertyName(), theProperty.getPropertyName());
|
||||
assertEquals(theExpectedProperty.getType(), theProperty.getType());
|
||||
switch (theProperty.getType()) {
|
||||
case TYPE_STRING -> {
|
||||
StringConceptProperty expected = (StringConceptProperty) theExpectedProperty;
|
||||
StringConceptProperty actual = (StringConceptProperty) theProperty;
|
||||
assertEquals(expected.getValue(), actual.getValue());
|
||||
}
|
||||
case TYPE_CODING -> {
|
||||
CodingConceptProperty expected = (CodingConceptProperty) theExpectedProperty;
|
||||
CodingConceptProperty actual = (CodingConceptProperty) theProperty;
|
||||
assertEquals(expected.getCode(), actual.getCode());
|
||||
assertEquals(expected.getCodeSystem(), actual.getCodeSystem());
|
||||
assertEquals(expected.getDisplay(), actual.getDisplay());
|
||||
}
|
||||
case TYPE_GROUP -> {
|
||||
GroupConceptProperty expected = (GroupConceptProperty) theExpectedProperty;
|
||||
GroupConceptProperty actual = (GroupConceptProperty) theProperty;
|
||||
assertEquals(expected.getSubProperties().size(), actual.getSubProperties().size());
|
||||
range(0, actual.getSubProperties().size()).forEach(i -> assertEqualConceptProperty(expected.getSubProperties().get(i), actual.getSubProperties().get(i)));
|
||||
}
|
||||
default -> fail();
|
||||
}
|
||||
}
|
||||
|
||||
private void assertEqualConceptDesignation(final ConceptDesignation theActualDesignation, final ConceptDesignation theExpectedDesignation) {
|
||||
assertEquals(theActualDesignation.getValue(), theExpectedDesignation.getValue());
|
||||
assertEquals(theActualDesignation.getLanguage(), theExpectedDesignation.getLanguage());
|
||||
assertEquals(theActualDesignation.getUseCode(), theExpectedDesignation.getUseCode());
|
||||
assertEquals(theActualDesignation.getUseSystem(), theExpectedDesignation.getUseSystem());
|
||||
assertEquals(theActualDesignation.getUseDisplay(), theExpectedDesignation.getUseDisplay());
|
||||
}
|
||||
|
||||
interface IMyCodeSystemProvider extends IResourceProvider {
|
||||
String getCode();
|
||||
|
@ -220,8 +250,4 @@ public interface ILookupCodeTest {
|
|||
|
||||
void setLookupCodeResult(LookupCodeResult theLookupCodeResult);
|
||||
}
|
||||
|
||||
interface IMySimpleCodeSystemProvider extends IResourceProvider {
|
||||
IBaseDatatype getPropertyValue();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,251 +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.IValidationSupport.ConceptDesignation;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport.LookupCodeResult;
|
||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
||||
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.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.hl7.fhir.r4.model.DateTimeType;
|
||||
import org.hl7.fhir.r4.model.DecimalType;
|
||||
import org.hl7.fhir.r4.model.InstantType;
|
||||
import org.hl7.fhir.r4.model.IntegerType;
|
||||
import org.junit.jupiter.api.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.Calendar;
|
||||
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 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 -> {
|
||||
if (!(theExpectedValue instanceof StringType stringValue)) {
|
||||
// TODO: remove this branch to test other property types as per FHIR spec https://github.com/hapifhir/hapi-fhir/issues/5699
|
||||
IValidationSupport.StringConceptProperty stringConceptProperty = (IValidationSupport.StringConceptProperty) theConceptProperty;
|
||||
assertEquals(theExpectedValue.toString(), stringConceptProperty.getValue());
|
||||
break;
|
||||
}
|
||||
// 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 -> {
|
||||
IValidationSupport.StringConceptProperty stringConceptProperty = (IValidationSupport.StringConceptProperty) theConceptProperty;
|
||||
assertEquals(theExpectedValue.toString(), stringConceptProperty.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Stream<Arguments> getEmptyPropertyValues() {
|
||||
return Stream.of(
|
||||
Arguments.arguments(new StringType()),
|
||||
Arguments.arguments(new StringType("")),
|
||||
Arguments.arguments(new Coding()),
|
||||
Arguments.arguments(new Coding("", null, null)),
|
||||
Arguments.arguments(new Coding("", "", null)),
|
||||
Arguments.arguments(new Coding(null, "", null))
|
||||
);
|
||||
}
|
||||
|
||||
public Stream<Arguments> getPropertyValues() {
|
||||
return Stream.of(
|
||||
// FHIR DSTU3 spec types
|
||||
Arguments.arguments(new StringType("value")),
|
||||
Arguments.arguments(new Coding("code", "system", "display")),
|
||||
Arguments.arguments(new CodeType("code")),
|
||||
Arguments.arguments(new BooleanType(true)),
|
||||
Arguments.arguments(new IntegerType(1)),
|
||||
Arguments.arguments(new DateTimeType(Calendar.getInstance())),
|
||||
// other types will also not fail for Remote Terminology
|
||||
Arguments.arguments(new DecimalType(1.1)),
|
||||
Arguments.arguments(new InstantType(Calendar.getInstance())),
|
||||
Arguments.arguments(new Type() {
|
||||
@Override
|
||||
protected Type typedCopy() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "randomType";
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
public Stream<Arguments> getDesignations() {
|
||||
return Stream.of(
|
||||
Arguments.arguments(new ConceptDesignation().setLanguage("en").setUseCode("code1").setUseSystem("system-1").setUseDisplay("display").setValue("some value")),
|
||||
Arguments.arguments(new ConceptDesignation().setUseCode("code2").setUseSystem("system1").setUseDisplay("display").setValue("someValue")),
|
||||
Arguments.arguments(new ConceptDesignation().setUseCode("code2").setUseSystem("system1").setValue("someValue")),
|
||||
Arguments.arguments(new ConceptDesignation().setUseCode("code2").setUseSystem("system1")),
|
||||
Arguments.arguments(new ConceptDesignation().setUseCode("code2"))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static class MySimplePropertyCodeSystemProviderDstu3 implements IMySimpleCodeSystemProvider {
|
||||
|
||||
String myPropertyName;
|
||||
Type myPropertyValue;
|
||||
|
||||
@Override
|
||||
public IBaseDatatype getPropertyValue() {
|
||||
return myPropertyValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return CodeSystem.class;
|
||||
}
|
||||
|
||||
@Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters= {
|
||||
@OperationParam(name = "name", type = StringType.class, min = 1),
|
||||
@OperationParam(name = "version", type = StringType.class, min = 0),
|
||||
@OperationParam(name = "display", type = StringType.class, min = 1),
|
||||
@OperationParam(name = "abstract", type = BooleanType.class, min = 1),
|
||||
@OperationParam(name = "property", min = 0, max = OperationParam.MAX_UNLIMITED)
|
||||
})
|
||||
public Parameters lookup(
|
||||
HttpServletRequest theServletRequest,
|
||||
@OperationParam(name = "code", max = 1) CodeType theCode,
|
||||
@OperationParam(name = "system", max = 1) UriType theSystem,
|
||||
@OperationParam(name = "coding", max = 1) Coding theCoding,
|
||||
@OperationParam(name = "version", max = 1) StringType theVersion,
|
||||
@OperationParam(name = "displayLanguage", max = 1) CodeType theDisplayLanguage,
|
||||
@OperationParam(name= " property", max = OperationParam.MAX_UNLIMITED) List<CodeType> thePropertyNames,
|
||||
RequestDetails theRequestDetails
|
||||
) {
|
||||
ParametersParameterComponent component = new ParametersParameterComponent();
|
||||
component.setName("property");
|
||||
component.addPart(new ParametersParameterComponent().setName("code").setValue(new CodeType(myPropertyName)));
|
||||
component.addPart(new ParametersParameterComponent().setName("value").setValue(myPropertyValue));
|
||||
return new Parameters().addParameter(component);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class MyCodeSystemProviderDstu3 implements IMyCodeSystemProvider {
|
||||
private UriType mySystemUrl;
|
||||
private CodeType myCode;
|
||||
private LookupCodeResult myLookupCodeResult;
|
||||
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return CodeSystem.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCode() {
|
||||
return myCode != null ? myCode.getValueAsString() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSystem() {
|
||||
return mySystemUrl != null ? mySystemUrl.getValueAsString() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLookupCodeResult(LookupCodeResult theLookupCodeResult) {
|
||||
myLookupCodeResult = theLookupCodeResult;
|
||||
}
|
||||
|
||||
@Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters= {
|
||||
@OperationParam(name = "name", type = StringType.class, min = 1),
|
||||
@OperationParam(name = "version", type = StringType.class, min = 0),
|
||||
@OperationParam(name = "display", type = StringType.class, min = 1),
|
||||
@OperationParam(name = "abstract", type = BooleanType.class, min = 1),
|
||||
@OperationParam(name = "property", min = 0, max = OperationParam.MAX_UNLIMITED)
|
||||
})
|
||||
public Parameters lookup(
|
||||
HttpServletRequest theServletRequest,
|
||||
@OperationParam(name = "code", max = 1) CodeType theCode,
|
||||
@OperationParam(name = "system", max = 1) UriType theSystem,
|
||||
@OperationParam(name = "coding", max = 1) Coding theCoding,
|
||||
@OperationParam(name = "version", max = 1) StringType theVersion,
|
||||
@OperationParam(name = "displayLanguage", max = 1) CodeType theDisplayLanguage,
|
||||
@OperationParam(name= " property", max = OperationParam.MAX_UNLIMITED) List<CodeType> thePropertyNames,
|
||||
RequestDetails theRequestDetails
|
||||
) {
|
||||
myCode = theCode;
|
||||
mySystemUrl = theSystem;
|
||||
|
||||
return (Parameters)myLookupCodeResult.toParameters(theRequestDetails.getFhirContext(), thePropertyNames);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,202 @@
|
|||
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.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.test.utilities.server.RestfulServerExtension;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.hl7.fhir.common.hapi.validation.ILookupCodeTest;
|
||||
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.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.IBaseParameters;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r4.model.DateTimeType;
|
||||
import org.hl7.fhir.r4.model.DecimalType;
|
||||
import org.hl7.fhir.r4.model.InstantType;
|
||||
import org.hl7.fhir.r4.model.IntegerType;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Version specific tests for CodeSystem $lookup against RemoteTerminologyValidationSupport.
|
||||
* @see RemoteTerminologyServiceValidationSupport
|
||||
*/
|
||||
public class RemoteTerminologyLookupCodeDstu3Test implements ILookupCodeTest {
|
||||
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));
|
||||
ourRestfulServerExtension.getRestfulServer().registerProvider(myCodeSystemProvider);
|
||||
}
|
||||
|
||||
private final MyCodeSystemProviderDstu3 myCodeSystemProvider = new MyCodeSystemProviderDstu3();
|
||||
|
||||
@Override
|
||||
public IMyCodeSystemProvider getCodeSystemProvider() {
|
||||
return myCodeSystemProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemoteTerminologyServiceValidationSupport getService() {
|
||||
return mySvc;
|
||||
}
|
||||
|
||||
public static Stream<Arguments> getEmptyPropertyValues() {
|
||||
return Stream.of(
|
||||
Arguments.of(new StringType()),
|
||||
Arguments.of(new StringType("")),
|
||||
Arguments.of(new Coding()),
|
||||
Arguments.of(new Coding("", null, null)),
|
||||
Arguments.of(new Coding("", "", null)),
|
||||
Arguments.of(new Coding(null, "", null))
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource(value = "getEmptyPropertyValues")
|
||||
public void lookupCode_forCodeSystemWithPropertyEmptyValue_returnsCorrectParameters(IBaseDatatype thePropertyValue) {
|
||||
verifyLookupWithEmptyPropertyValue(thePropertyValue);
|
||||
}
|
||||
|
||||
public static Stream<Arguments> getPropertyValueArguments() {
|
||||
return Stream.of(
|
||||
// FHIR DSTU3 spec types
|
||||
Arguments.of(new StringType("value")),
|
||||
Arguments.of(new Coding("code", "system", "display")),
|
||||
Arguments.of(new CodeType("code")),
|
||||
Arguments.of(new BooleanType(true)),
|
||||
Arguments.of(new IntegerType(1)),
|
||||
Arguments.of(new DateTimeType(Calendar.getInstance())),
|
||||
// other types will also not fail for Remote Terminology
|
||||
Arguments.of(new DecimalType(1.1)),
|
||||
Arguments.of(new InstantType(Calendar.getInstance())),
|
||||
Arguments.of(new Type() {
|
||||
@Override
|
||||
protected Type typedCopy() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "randomType";
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
public static Stream<Arguments> getPropertyValueListArguments() {
|
||||
return Stream.of(
|
||||
Arguments.of(List.of(new StringType("value1")), new StringType("value2")),
|
||||
Arguments.of(List.of(new StringType("value1")), new Coding("code", "system", "display"))
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource(value = "getPropertyValueArguments")
|
||||
public void lookupCode_forCodeSystemWithProperty_returnsCorrectProperty(IBaseDatatype thePropertyValue) {
|
||||
verifyLookupWithProperty(List.of(thePropertyValue), List.of());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource(value = "getPropertyValueListArguments")
|
||||
public void lookupCode_forCodeSystemWithPropertyFilter_returnsCorrectProperty(List<IBaseDatatype> thePropertyValues) {
|
||||
verifyLookupWithProperty(thePropertyValues, List.of());
|
||||
verifyLookupWithProperty(thePropertyValues, List.of(thePropertyValues.size() - 1));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource(value = "getPropertyValueListArguments")
|
||||
public void lookupCode_forCodeSystemWithPropertyGroup_returnsCorrectProperty(List<IBaseDatatype> thePropertyValues) {
|
||||
verifyLookupWithSubProperties(thePropertyValues);
|
||||
}
|
||||
|
||||
public static Stream<Arguments> getDesignations() {
|
||||
return Stream.of(
|
||||
Arguments.of(new ConceptDesignation().setLanguage("en").setUseCode("code1").setUseSystem("system-1").setUseDisplay("display").setValue("some value")),
|
||||
Arguments.of(new ConceptDesignation().setUseCode("code2").setUseSystem("system1").setUseDisplay("display").setValue("someValue")),
|
||||
Arguments.of(new ConceptDesignation().setUseCode("code2").setUseSystem("system1").setValue("someValue")),
|
||||
Arguments.of(new ConceptDesignation().setUseCode("code2").setUseSystem("system1")),
|
||||
Arguments.of(new ConceptDesignation().setUseCode("code2"))
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource(value = "getDesignations")
|
||||
void lookupCode_withCodeSystemWithDesignation_returnsCorrectDesignation(final IValidationSupport.ConceptDesignation theConceptDesignation) {
|
||||
verifyLookupWithConceptDesignation(theConceptDesignation);
|
||||
}
|
||||
|
||||
static class MyCodeSystemProviderDstu3 implements IMyCodeSystemProvider {
|
||||
private UriType mySystemUrl;
|
||||
private CodeType myCode;
|
||||
private LookupCodeResult myLookupCodeResult;
|
||||
|
||||
@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 IBaseParameters lookup(
|
||||
HttpServletRequest theServletRequest,
|
||||
@OperationParam(name = "code", max = 1) CodeType theCode,
|
||||
@OperationParam(name = "system", max = 1) UriType theSystem,
|
||||
@OperationParam(name = "coding", max = 1) Coding theCoding,
|
||||
@OperationParam(name = "version", max = 1) StringType theVersion,
|
||||
@OperationParam(name = "displayLanguage", max = 1) CodeType theDisplayLanguage,
|
||||
@OperationParam(name= " property", max = OperationParam.MAX_UNLIMITED) List<StringType> thePropertyNames,
|
||||
RequestDetails theRequestDetails
|
||||
) {
|
||||
myCode = theCode;
|
||||
mySystemUrl = theSystem;
|
||||
return myLookupCodeResult.toParameters(theRequestDetails.getFhirContext(), thePropertyNames);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return CodeSystem.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCode() {
|
||||
return myCode != null ? myCode.getValueAsString() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSystem() {
|
||||
return mySystemUrl != null ? mySystemUrl.getValueAsString() : null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,294 @@
|
|||
package org.hl7.fhir.dstu3.hapi.validation;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.context.support.LookupCodeRequest;
|
||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
|
||||
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
||||
import ca.uhn.fhir.util.ClasspathUtil;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
|
||||
import org.hl7.fhir.dstu3.model.BooleanType;
|
||||
import org.hl7.fhir.dstu3.model.CodeSystem;
|
||||
import org.hl7.fhir.dstu3.model.CodeType;
|
||||
import org.hl7.fhir.dstu3.model.Coding;
|
||||
import org.hl7.fhir.dstu3.model.IdType;
|
||||
import org.hl7.fhir.dstu3.model.Parameters;
|
||||
import org.hl7.fhir.dstu3.model.StringType;
|
||||
import org.hl7.fhir.dstu3.model.Type;
|
||||
import org.hl7.fhir.dstu3.model.UriType;
|
||||
import org.hl7.fhir.dstu3.model.ValueSet;
|
||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* Version specific tests for validation using RemoteTerminologyValidationSupport.
|
||||
* The tests in this class simulate the call to a remote server and therefore, only tests the code in
|
||||
* the RemoteTerminologyServiceValidationSupport itself. The remote client call is simulated using the test providers.
|
||||
* @see RemoteTerminologyServiceValidationSupport
|
||||
*
|
||||
* Other operations are tested separately.
|
||||
* @see RemoteTerminologyLookupCodeDstu3Test
|
||||
*/
|
||||
public class RemoteTerminologyServiceResourceProviderDstu3Test {
|
||||
private static final String DISPLAY = "DISPLAY";
|
||||
private static final String CODE_SYSTEM = "CODE_SYS";
|
||||
private static final String CODE = "CODE";
|
||||
private static final String VALUE_SET_URL = "http://value.set/url";
|
||||
private static final String SAMPLE_MESSAGE = "This is a sample message";
|
||||
private static final FhirContext ourCtx = FhirContext.forDstu3Cached();
|
||||
private static final MyCodeSystemProvider ourCodeSystemProvider = new MyCodeSystemProvider();
|
||||
private static final MyValueSetProvider ourValueSetProvider = new MyValueSetProvider();
|
||||
|
||||
@RegisterExtension
|
||||
public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx, ourCodeSystemProvider,
|
||||
ourValueSetProvider);
|
||||
|
||||
private RemoteTerminologyServiceValidationSupport mySvc;
|
||||
|
||||
@BeforeEach
|
||||
public void before_ConfigureService() {
|
||||
String myBaseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
|
||||
mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx, myBaseUrl);
|
||||
mySvc.addClientInterceptor(new LoggingInterceptor(false).setLogRequestSummary(true).setLogResponseSummary(true));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void after_UnregisterProviders() {
|
||||
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE);
|
||||
ourRestfulServerExtension.getRestfulServer().getInterceptorService().unregisterAllInterceptors();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateCodeInCodeSystem_BlankCode_ReturnsNull() {
|
||||
IValidationSupport.CodeValidationResult outcome = mySvc
|
||||
.validateCode(null, null, CODE_SYSTEM, null, DISPLAY, null);
|
||||
assertNull(outcome);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateCodeInCodeSystem_ProvidingMinimalInputs_ReturnsSuccess() {
|
||||
createNextCodeSystemReturnParameters(true, null, null);
|
||||
|
||||
IValidationSupport.CodeValidationResult outcome = mySvc
|
||||
.validateCode(null, null, CODE_SYSTEM, CODE, null, null);
|
||||
assertNotNull(outcome);
|
||||
assertEquals(CODE, outcome.getCode());
|
||||
assertNull(outcome.getSeverity());
|
||||
assertNull(outcome.getMessage());
|
||||
|
||||
assertEquals(CODE, ourCodeSystemProvider.myLastCode.getValue());
|
||||
assertEquals(CODE_SYSTEM, ourCodeSystemProvider.myLastUrl.getValueAsString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateCodeInCodeSystem_WithMessageValue_ReturnsMessage() {
|
||||
createNextCodeSystemReturnParameters(true, DISPLAY, SAMPLE_MESSAGE);
|
||||
|
||||
IValidationSupport.CodeValidationResult outcome = mySvc
|
||||
.validateCode(null, null, CODE_SYSTEM, CODE, DISPLAY, null);
|
||||
assertNotNull(outcome);
|
||||
assertEquals(CODE, outcome.getCode());
|
||||
assertEquals(DISPLAY, outcome.getDisplay());
|
||||
assertNull(outcome.getSeverity());
|
||||
assertNull(outcome.getMessage());
|
||||
|
||||
assertEquals(CODE, ourCodeSystemProvider.myLastCode.getValue());
|
||||
assertEquals(DISPLAY, ourCodeSystemProvider.myLastDisplay.getValue());
|
||||
assertEquals(CODE_SYSTEM, ourCodeSystemProvider.myLastUrl.getValueAsString());
|
||||
assertEquals(SAMPLE_MESSAGE, getParameterValue(ourCodeSystemProvider.myNextReturnParams, "message").toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateCodeInCodeSystem_AssumeFailure_ReturnsFailureCodeAndFailureMessage() {
|
||||
createNextCodeSystemReturnParameters(false, null, SAMPLE_MESSAGE);
|
||||
|
||||
IValidationSupport.CodeValidationResult outcome = mySvc
|
||||
.validateCode(null, null, CODE_SYSTEM, CODE, null, null);
|
||||
assertNotNull(outcome);
|
||||
assertEquals(IValidationSupport.IssueSeverity.ERROR, outcome.getSeverity());
|
||||
assertEquals(SAMPLE_MESSAGE, outcome.getMessage());
|
||||
|
||||
assertFalse(((BooleanType) getParameterValue(ourCodeSystemProvider.myNextReturnParams, "result")).booleanValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateCodeInValueSet_ProvidingMinimalInputs_ReturnsSuccess() {
|
||||
ourValueSetProvider.myNextReturnParams = new Parameters();
|
||||
ourValueSetProvider.myNextReturnParams.addParameter().setName("result").setValue(new BooleanType(true));
|
||||
|
||||
IValidationSupport.CodeValidationResult outcome = mySvc
|
||||
.validateCode(null, null, CODE_SYSTEM, CODE, null, VALUE_SET_URL);
|
||||
assertNotNull(outcome);
|
||||
assertEquals(CODE, outcome.getCode());
|
||||
assertNull(outcome.getSeverity());
|
||||
assertNull(outcome.getMessage());
|
||||
|
||||
assertEquals(CODE, ourValueSetProvider.myLastCode.getValue());
|
||||
assertEquals(VALUE_SET_URL, ourValueSetProvider.myLastUrl.getValueAsString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidateCodeInValueSet_WithMessageValue_ReturnsMessage() {
|
||||
ourValueSetProvider.myNextReturnParams = new Parameters();
|
||||
ourValueSetProvider.myNextReturnParams.addParameter().setName("result").setValue(new BooleanType(true));
|
||||
ourValueSetProvider.myNextReturnParams.addParameter().setName("display").setValue(new StringType(DISPLAY));
|
||||
ourValueSetProvider.myNextReturnParams.addParameter().setName("message").setValue(new StringType(SAMPLE_MESSAGE));
|
||||
|
||||
IValidationSupport.CodeValidationResult outcome = mySvc
|
||||
.validateCode(null, null, CODE_SYSTEM, CODE, DISPLAY, VALUE_SET_URL);
|
||||
assertNotNull(outcome);
|
||||
assertEquals(CODE, outcome.getCode());
|
||||
assertEquals(DISPLAY, outcome.getDisplay());
|
||||
assertNull(outcome.getSeverity());
|
||||
assertNull(outcome.getMessage());
|
||||
|
||||
assertEquals(CODE, ourValueSetProvider.myLastCode.getValue());
|
||||
assertEquals(DISPLAY, ourValueSetProvider.myLastDisplay.getValue());
|
||||
assertEquals(VALUE_SET_URL, ourValueSetProvider.myLastUrl.getValueAsString());
|
||||
assertEquals(SAMPLE_MESSAGE, getParameterValue(ourValueSetProvider.myNextReturnParams, "message").toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lookupCode_withParametersOutput_convertsCorrectly() {
|
||||
String paramsAsString = ClasspathUtil.loadResource("/r4/CodeSystem-lookup-output-with-subproperties.json");
|
||||
IBaseResource baseResource = ourCtx.newJsonParser().parseResource(paramsAsString);
|
||||
assertTrue(baseResource instanceof Parameters);
|
||||
Parameters resultParameters = (Parameters) baseResource;
|
||||
ourCodeSystemProvider.myNextReturnParams = resultParameters;
|
||||
|
||||
LookupCodeRequest request = new LookupCodeRequest(CODE_SYSTEM, CODE, null, List.of("interfaces"));
|
||||
|
||||
// test
|
||||
IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(null, request);
|
||||
assertNotNull(outcome);
|
||||
|
||||
IBaseParameters theActualParameters = outcome.toParameters(ourCtx, request.getPropertyNames().stream().map(StringType::new).toList());
|
||||
String actual = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(theActualParameters);
|
||||
String expected = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(resultParameters);
|
||||
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
private void createNextCodeSystemReturnParameters(boolean theResult, String theDisplay, String theMessage) {
|
||||
ourCodeSystemProvider.myNextReturnParams = new Parameters();
|
||||
ourCodeSystemProvider.myNextReturnParams.addParameter().setName("result").setValue(new BooleanType(theResult));
|
||||
ourCodeSystemProvider.myNextReturnParams.addParameter().setName("display").setValue(new StringType(theDisplay));
|
||||
if (theMessage != null) {
|
||||
ourCodeSystemProvider.myNextReturnParams.addParameter().setName("message").setValue(new StringType(theMessage));
|
||||
}
|
||||
}
|
||||
|
||||
private Type getParameterValue(Parameters theParameters, String theParameterName) {
|
||||
Optional<Parameters.ParametersParameterComponent> paramOpt = theParameters.getParameter()
|
||||
.stream().filter(param -> param.getName().equals(theParameterName)).findFirst();
|
||||
assertTrue(paramOpt.isPresent());
|
||||
return paramOpt.get().getValue();
|
||||
}
|
||||
|
||||
private static class MyCodeSystemProvider implements IResourceProvider {
|
||||
private UriType myLastUrl;
|
||||
private CodeType myLastCode;
|
||||
private StringType myLastDisplay;
|
||||
private Parameters myNextReturnParams;
|
||||
|
||||
@Operation(name = "validate-code", idempotent = true, returnParameters = {
|
||||
@OperationParam(name = "result", type = BooleanType.class, min = 1),
|
||||
@OperationParam(name = "message", type = StringType.class),
|
||||
@OperationParam(name = "display", type = StringType.class)
|
||||
})
|
||||
public Parameters validateCode(
|
||||
HttpServletRequest theServletRequest,
|
||||
@IdParam(optional = true) IdType theId,
|
||||
@OperationParam(name = "url", min = 0, max = 1) UriType theCodeSystemUrl,
|
||||
@OperationParam(name = "code", min = 0, max = 1) CodeType theCode,
|
||||
@OperationParam(name = "display", min = 0, max = 1) StringType theDisplay
|
||||
) {
|
||||
myLastUrl = theCodeSystemUrl;
|
||||
myLastCode = theCode;
|
||||
myLastDisplay = theDisplay;
|
||||
return myNextReturnParams;
|
||||
|
||||
}
|
||||
|
||||
@Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters= {
|
||||
@OperationParam(name = "name", type = StringType.class, min = 1),
|
||||
@OperationParam(name = "version", type = StringType.class),
|
||||
@OperationParam(name = "display", type = StringType.class, min = 1),
|
||||
@OperationParam(name = "abstract", type = BooleanType.class, min = 1),
|
||||
@OperationParam(name = "property", type = StringType.class, min = 0, max = OperationParam.MAX_UNLIMITED)
|
||||
})
|
||||
public IBaseParameters lookup(
|
||||
HttpServletRequest theServletRequest,
|
||||
@OperationParam(name = "code", max = 1) CodeType theCode,
|
||||
@OperationParam(name = "system",max = 1) UriType theSystem,
|
||||
@OperationParam(name = "coding", max = 1) Coding theCoding,
|
||||
@OperationParam(name = "version", max = 1) StringType theVersion,
|
||||
@OperationParam(name = "displayLanguage", max = 1) CodeType theDisplayLanguage,
|
||||
@OperationParam(name = "property", max = OperationParam.MAX_UNLIMITED) List<CodeType> thePropertyNames,
|
||||
RequestDetails theRequestDetails
|
||||
) {
|
||||
myLastCode = theCode;
|
||||
return myNextReturnParams;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return CodeSystem.class;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class MyValueSetProvider implements IResourceProvider {
|
||||
private Parameters myNextReturnParams;
|
||||
private UriType myLastUrl;
|
||||
private CodeType myLastCode;
|
||||
private StringType myLastDisplay;
|
||||
|
||||
@Operation(name = "validate-code", idempotent = true, returnParameters = {
|
||||
@OperationParam(name = "result", type = BooleanType.class, min = 1),
|
||||
@OperationParam(name = "message", type = StringType.class),
|
||||
@OperationParam(name = "display", type = StringType.class)
|
||||
})
|
||||
public Parameters validateCode(
|
||||
HttpServletRequest theServletRequest,
|
||||
@IdParam(optional = true) IdType theId,
|
||||
@OperationParam(name = "url", min = 0, max = 1) UriType theValueSetUrl,
|
||||
@OperationParam(name = "code", min = 0, max = 1) CodeType theCode,
|
||||
@OperationParam(name = "system", min = 0, max = 1) UriType theSystem,
|
||||
@OperationParam(name = "display", min = 0, max = 1) StringType theDisplay,
|
||||
@OperationParam(name = "valueSet") ValueSet theValueSet
|
||||
) {
|
||||
myLastUrl = theValueSetUrl;
|
||||
myLastCode = theCode;
|
||||
myLastDisplay = theDisplay;
|
||||
return myNextReturnParams;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return ValueSet.class;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -1,242 +0,0 @@
|
|||
package org.hl7.fhir.r4.validation;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
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.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.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.DateTimeType;
|
||||
import org.hl7.fhir.r4.model.DecimalType;
|
||||
import org.hl7.fhir.r4.model.InstantType;
|
||||
import org.hl7.fhir.r4.model.IntegerType;
|
||||
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.Calendar;
|
||||
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 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 -> {
|
||||
if (!(theExpectedValue instanceof StringType stringValue)) {
|
||||
// TODO: remove this branch to test other property types as per FHIR spec https://github.com/hapifhir/hapi-fhir/issues/5699
|
||||
IValidationSupport.StringConceptProperty stringConceptProperty = (IValidationSupport.StringConceptProperty) theConceptProperty;
|
||||
assertEquals(theExpectedValue.toString(), stringConceptProperty.getValue());
|
||||
break;
|
||||
}
|
||||
// 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 -> {
|
||||
IValidationSupport.StringConceptProperty stringConceptProperty = (IValidationSupport.StringConceptProperty) theConceptProperty;
|
||||
assertEquals(theExpectedValue.toString(), stringConceptProperty.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Stream<Arguments> getEmptyPropertyValues() {
|
||||
return Stream.of(
|
||||
Arguments.arguments(new StringType()),
|
||||
Arguments.arguments(new StringType("")),
|
||||
Arguments.arguments(new Coding()),
|
||||
Arguments.arguments(new Coding("", null, null)),
|
||||
Arguments.arguments(new Coding("", "", null)),
|
||||
Arguments.arguments(new Coding(null, "", null))
|
||||
);
|
||||
}
|
||||
|
||||
public Stream<Arguments> getPropertyValues() {
|
||||
return Stream.of(
|
||||
// FHIR R4 spec types
|
||||
Arguments.arguments(new StringType("value")),
|
||||
Arguments.arguments(new Coding("code", "system", "display")),
|
||||
Arguments.arguments(new CodeType("code")),
|
||||
Arguments.arguments(new BooleanType(true)),
|
||||
Arguments.arguments(new IntegerType(1)),
|
||||
Arguments.arguments(new DateTimeType(Calendar.getInstance())),
|
||||
Arguments.arguments(new DecimalType(1.1)),
|
||||
// other types will also not fail for Remote Terminology
|
||||
Arguments.arguments(new InstantType(Calendar.getInstance())),
|
||||
Arguments.arguments(new Type() {
|
||||
@Override
|
||||
protected Type typedCopy() {
|
||||
return this;
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return "randomType";
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
public Stream<Arguments> getDesignations() {
|
||||
return Stream.of(
|
||||
Arguments.arguments(new IValidationSupport.ConceptDesignation().setLanguage("en").setUseCode("code1").setUseSystem("system-1").setUseDisplay("display").setValue("some value")),
|
||||
Arguments.arguments(new IValidationSupport.ConceptDesignation().setUseCode("code2").setUseSystem("system1").setUseDisplay("display").setValue("someValue")),
|
||||
Arguments.arguments(new IValidationSupport.ConceptDesignation().setUseCode("code2").setUseSystem("system1").setValue("someValue")),
|
||||
Arguments.arguments(new IValidationSupport.ConceptDesignation().setUseCode("code2").setUseSystem("system1")),
|
||||
Arguments.arguments(new IValidationSupport.ConceptDesignation().setUseCode("code2"))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static class MySimplePropertyCodeSystemProviderR4 implements IMySimpleCodeSystemProvider {
|
||||
String myPropertyName;
|
||||
Type myPropertyValue;
|
||||
|
||||
@Override
|
||||
public IBaseDatatype getPropertyValue() {
|
||||
return myPropertyValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return CodeSystem.class;
|
||||
}
|
||||
|
||||
@Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters= {
|
||||
@OperationParam(name = "name", type = StringType.class, min = 1),
|
||||
@OperationParam(name = "version", type = StringType.class, min = 0),
|
||||
@OperationParam(name = "display", type = StringType.class, min = 1),
|
||||
@OperationParam(name = "abstract", type = BooleanType.class, min = 1),
|
||||
@OperationParam(name = "property", min = 0, max = OperationParam.MAX_UNLIMITED)
|
||||
})
|
||||
public Parameters lookup(
|
||||
HttpServletRequest theServletRequest,
|
||||
@OperationParam(name = "code", max = 1) CodeType theCode,
|
||||
@OperationParam(name = "system", max = 1) UriType theSystem,
|
||||
@OperationParam(name = "coding", max = 1) Coding theCoding,
|
||||
@OperationParam(name = "version", max = 1) StringType theVersion,
|
||||
@OperationParam(name = "displayLanguage", max = 1) CodeType theDisplayLanguage,
|
||||
@OperationParam(name= " property", max = OperationParam.MAX_UNLIMITED) List<CodeType> thePropertyNames,
|
||||
RequestDetails theRequestDetails
|
||||
) {
|
||||
Parameters.ParametersParameterComponent component = new Parameters.ParametersParameterComponent();
|
||||
component.setName("property");
|
||||
component.addPart(new Parameters.ParametersParameterComponent().setName("code").setValue(new CodeType(myPropertyName)));
|
||||
component.addPart(new Parameters.ParametersParameterComponent().setName("value").setValue(myPropertyValue));
|
||||
return new Parameters().addParameter(component);
|
||||
}
|
||||
}
|
||||
|
||||
static class MyCodeSystemProviderR4 implements IMyCodeSystemProvider {
|
||||
private UriType mySystemUrl;
|
||||
private CodeType myCode;
|
||||
private IValidationSupport.LookupCodeResult myLookupCodeResult;
|
||||
|
||||
@Override
|
||||
public void setLookupCodeResult(IValidationSupport.LookupCodeResult theLookupCodeResult) {
|
||||
myLookupCodeResult = theLookupCodeResult;
|
||||
}
|
||||
|
||||
@Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters= {
|
||||
@OperationParam(name = "name", type = StringType.class, min = 1),
|
||||
@OperationParam(name = "version", type = StringType.class),
|
||||
@OperationParam(name = "display", type = StringType.class, min = 1),
|
||||
@OperationParam(name = "abstract", type = BooleanType.class, min = 1),
|
||||
@OperationParam(name = "property", type = StringType.class, min = 0, max = OperationParam.MAX_UNLIMITED)
|
||||
})
|
||||
public IBaseParameters lookup(
|
||||
HttpServletRequest theServletRequest,
|
||||
@OperationParam(name = "code", max = 1) CodeType theCode,
|
||||
@OperationParam(name = "system",max = 1) UriType theSystem,
|
||||
@OperationParam(name = "coding", max = 1) Coding theCoding,
|
||||
@OperationParam(name = "version", max = 1) StringType theVersion,
|
||||
@OperationParam(name = "displayLanguage", max = 1) CodeType theDisplayLanguage,
|
||||
@OperationParam(name = "property", max = OperationParam.MAX_UNLIMITED) List<StringType> thePropertyNames,
|
||||
RequestDetails theRequestDetails
|
||||
) {
|
||||
myCode = theCode;
|
||||
mySystemUrl = theSystem;
|
||||
return myLookupCodeResult.toParameters(theRequestDetails.getFhirContext(), thePropertyNames);
|
||||
}
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return CodeSystem.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCode() {
|
||||
return myCode != null ? myCode.getValueAsString() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSystem() {
|
||||
return mySystemUrl != null ? mySystemUrl.getValueAsString() : null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,201 @@
|
|||
package org.hl7.fhir.r4.validation;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
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.test.utilities.server.RestfulServerExtension;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.hl7.fhir.common.hapi.validation.ILookupCodeTest;
|
||||
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.DateTimeType;
|
||||
import org.hl7.fhir.r4.model.DecimalType;
|
||||
import org.hl7.fhir.r4.model.InstantType;
|
||||
import org.hl7.fhir.r4.model.IntegerType;
|
||||
import org.hl7.fhir.r4.model.StringType;
|
||||
import org.hl7.fhir.r4.model.Type;
|
||||
import org.hl7.fhir.r4.model.UriType;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static ca.uhn.fhir.context.support.IValidationSupport.ConceptDesignation;
|
||||
import static ca.uhn.fhir.context.support.IValidationSupport.LookupCodeResult;
|
||||
|
||||
/**
|
||||
* Version specific tests for CodeSystem $lookup against RemoteTerminologyValidationSupport.
|
||||
* @see RemoteTerminologyServiceValidationSupport
|
||||
*/
|
||||
public class RemoteTerminologyLookupCodeR4Test implements ILookupCodeTest {
|
||||
private static final FhirContext ourCtx = FhirContext.forR4Cached();
|
||||
@RegisterExtension
|
||||
public static RestfulServerExtension ourRestfulServerExtension = new RestfulServerExtension(ourCtx);
|
||||
private final RemoteTerminologyServiceValidationSupport mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx);
|
||||
private final MyCodeSystemProviderR4 myCodeSystemProvider = new MyCodeSystemProviderR4();
|
||||
|
||||
@BeforeEach
|
||||
public void before() {
|
||||
String baseUrl = "http://localhost:" + ourRestfulServerExtension.getPort();
|
||||
mySvc.setBaseUrl(baseUrl);
|
||||
mySvc.addClientInterceptor(new LoggingInterceptor(true));
|
||||
ourRestfulServerExtension.getRestfulServer().registerProvider(myCodeSystemProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IMyCodeSystemProvider getCodeSystemProvider() {
|
||||
return myCodeSystemProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemoteTerminologyServiceValidationSupport getService() {
|
||||
return mySvc;
|
||||
}
|
||||
|
||||
public static Stream<Arguments> getEmptyPropertyValues() {
|
||||
return Stream.of(
|
||||
Arguments.of(new StringType()),
|
||||
Arguments.of(new StringType("")),
|
||||
Arguments.of(new Coding()),
|
||||
Arguments.of(new Coding("", null, null)),
|
||||
Arguments.of(new Coding("", "", null)),
|
||||
Arguments.of(new Coding(null, "", null))
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource(value = "getEmptyPropertyValues")
|
||||
public void lookupCode_forCodeSystemWithPropertyEmptyValue_returnsCorrectParameters(IBaseDatatype thePropertyValue) {
|
||||
verifyLookupWithEmptyPropertyValue(thePropertyValue);
|
||||
}
|
||||
|
||||
|
||||
public static Stream<Arguments> getPropertyValueArguments() {
|
||||
return Stream.of(
|
||||
// FHIR R4 spec types
|
||||
Arguments.of(new StringType("test-value")),
|
||||
Arguments.of(new Coding("test-system", "test-code", "test-display")),
|
||||
Arguments.of(new CodeType("test-code")),
|
||||
Arguments.of(new BooleanType(true)),
|
||||
Arguments.of(new IntegerType(1)),
|
||||
Arguments.of(new DateTimeType(Calendar.getInstance())),
|
||||
Arguments.of(new DecimalType(1.1)),
|
||||
// other types will also not fail for Remote Terminology
|
||||
Arguments.of(new InstantType(Calendar.getInstance())),
|
||||
Arguments.of(new Type() {
|
||||
@Override
|
||||
protected Type typedCopy() {
|
||||
return this;
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return "randomType";
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
public static Stream<Arguments> getPropertyValueListArguments() {
|
||||
return Stream.of(
|
||||
Arguments.of(List.of(new StringType("value1")), new StringType("value2")),
|
||||
Arguments.of(List.of(new StringType("value1")), new Coding("code", "system", "display"))
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource(value = "getPropertyValueArguments")
|
||||
public void lookupCode_forCodeSystemWithProperty_returnsCorrectProperty(IBaseDatatype thePropertyValue) {
|
||||
verifyLookupWithProperty(List.of(thePropertyValue), List.of());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource(value = "getPropertyValueListArguments")
|
||||
public void lookupCode_forCodeSystemWithPropertyFilter_returnsCorrectProperty(List<IBaseDatatype> thePropertyValues) {
|
||||
verifyLookupWithProperty(thePropertyValues, List.of());
|
||||
verifyLookupWithProperty(thePropertyValues, List.of(thePropertyValues.size() - 1));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource(value = "getPropertyValueListArguments")
|
||||
public void lookupCode_forCodeSystemWithPropertyGroup_returnsCorrectProperty(List<IBaseDatatype> thePropertyValues) {
|
||||
verifyLookupWithSubProperties(thePropertyValues);
|
||||
}
|
||||
|
||||
public static Stream<Arguments> getDesignations() {
|
||||
return Stream.of(
|
||||
Arguments.of(new ConceptDesignation().setLanguage("en").setUseCode("code1").setUseSystem("system-1").setUseDisplay("display").setValue("some value")),
|
||||
Arguments.of(new ConceptDesignation().setUseCode("code2").setUseSystem("system1").setUseDisplay("display").setValue("someValue")),
|
||||
Arguments.of(new ConceptDesignation().setUseCode("code2").setUseSystem("system1").setValue("someValue")),
|
||||
Arguments.of(new ConceptDesignation().setUseCode("code2").setUseSystem("system1")),
|
||||
Arguments.of(new ConceptDesignation().setUseCode("code2"))
|
||||
);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource(value = "getDesignations")
|
||||
void lookupCode_withCodeSystemWithDesignation_returnsCorrectDesignation(final IValidationSupport.ConceptDesignation theConceptDesignation) {
|
||||
verifyLookupWithConceptDesignation(theConceptDesignation);
|
||||
}
|
||||
|
||||
static class MyCodeSystemProviderR4 implements IMyCodeSystemProvider {
|
||||
private UriType mySystemUrl;
|
||||
private CodeType myCode;
|
||||
private LookupCodeResult myLookupCodeResult;
|
||||
|
||||
@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),
|
||||
@OperationParam(name = "display", type = StringType.class, min = 1),
|
||||
@OperationParam(name = "abstract", type = BooleanType.class, min = 1),
|
||||
@OperationParam(name = "property", type = StringType.class, min = 0, max = OperationParam.MAX_UNLIMITED)
|
||||
})
|
||||
public IBaseParameters lookup(
|
||||
HttpServletRequest theServletRequest,
|
||||
@OperationParam(name = "code", max = 1) CodeType theCode,
|
||||
@OperationParam(name = "system",max = 1) UriType theSystem,
|
||||
@OperationParam(name = "coding", max = 1) Coding theCoding,
|
||||
@OperationParam(name = "version", max = 1) StringType theVersion,
|
||||
@OperationParam(name = "displayLanguage", max = 1) CodeType theDisplayLanguage,
|
||||
@OperationParam(name = "property", max = OperationParam.MAX_UNLIMITED) List<CodeType> thePropertyNames,
|
||||
RequestDetails theRequestDetails
|
||||
) {
|
||||
myCode = theCode;
|
||||
mySystemUrl = theSystem;
|
||||
return myLookupCodeResult.toParameters(theRequestDetails.getFhirContext(), thePropertyNames);
|
||||
}
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return CodeSystem.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCode() {
|
||||
return myCode != null ? myCode.getValueAsString() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSystem() {
|
||||
return mySystemUrl != null ? mySystemUrl.getValueAsString() : null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,20 +1,26 @@
|
|||
package ca.uhn.fhir.jpa.provider.r4;
|
||||
package org.hl7.fhir.r4.validation;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.context.support.LookupCodeRequest;
|
||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
|
||||
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
||||
import ca.uhn.fhir.util.ClasspathUtil;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
|
||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r4.model.BooleanType;
|
||||
import org.hl7.fhir.r4.model.CodeSystem;
|
||||
import org.hl7.fhir.r4.model.CodeType;
|
||||
import org.hl7.fhir.r4.model.Coding;
|
||||
import org.hl7.fhir.r4.model.IdType;
|
||||
import org.hl7.fhir.r4.model.Parameters;
|
||||
import org.hl7.fhir.r4.model.StringType;
|
||||
|
@ -25,15 +31,23 @@ import org.junit.jupiter.api.BeforeEach;
|
|||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/*
|
||||
* This set of Unit Tests simulates the call to a remote server and therefore, only tests the code in the
|
||||
* {@link org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport#invokeRemoteValidateCode}
|
||||
* method, before and after it makes that remote client call.
|
||||
/**
|
||||
* Version specific tests for validation using RemoteTerminologyValidationSupport.
|
||||
* The tests in this class simulate the call to a remote server and therefore, only tests the code in
|
||||
* the RemoteTerminologyServiceValidationSupport itself. The remote client call is simulated using the test providers.
|
||||
* @see RemoteTerminologyServiceValidationSupport
|
||||
*
|
||||
* Other operations are tested separately.
|
||||
* @see RemoteTerminologyLookupCodeR4Test
|
||||
* @see RemoteTerminologyServiceValidationSupportR4Test
|
||||
*/
|
||||
public class RemoteTerminologyServiceResourceProviderR4Test {
|
||||
private static final String DISPLAY = "DISPLAY";
|
||||
|
@ -152,6 +166,27 @@ public class RemoteTerminologyServiceResourceProviderR4Test {
|
|||
assertEquals(SAMPLE_MESSAGE, ourValueSetProvider.myNextReturnParams.getParameterValue("message").toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lookupCode_withParametersOutput_convertsCorrectly() {
|
||||
String paramsAsString = ClasspathUtil.loadResource("/r4/CodeSystem-lookup-output-with-subproperties.json");
|
||||
IBaseResource baseResource = ourCtx.newJsonParser().parseResource(paramsAsString);
|
||||
assertTrue(baseResource instanceof Parameters);
|
||||
Parameters resultParameters = (Parameters) baseResource;
|
||||
ourCodeSystemProvider.myNextReturnParams = resultParameters;
|
||||
|
||||
LookupCodeRequest request = new LookupCodeRequest(CODE_SYSTEM, CODE, null, List.of("interfaces"));
|
||||
|
||||
// test
|
||||
IValidationSupport.LookupCodeResult outcome = mySvc.lookupCode(null, request);
|
||||
assertNotNull(outcome);
|
||||
|
||||
IBaseParameters theActualParameters = outcome.toParameters(ourCtx, request.getPropertyNames().stream().map(StringType::new).toList());
|
||||
String actual = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(theActualParameters);
|
||||
String expected = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(resultParameters);
|
||||
|
||||
assertEquals(expected, actual);
|
||||
}
|
||||
|
||||
private void createNextCodeSystemReturnParameters(boolean theResult, String theDisplay, String theMessage) {
|
||||
ourCodeSystemProvider.myNextReturnParams = new Parameters();
|
||||
ourCodeSystemProvider.myNextReturnParams.addParameter("result", theResult);
|
||||
|
@ -186,6 +221,27 @@ public class RemoteTerminologyServiceResourceProviderR4Test {
|
|||
|
||||
}
|
||||
|
||||
@Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters= {
|
||||
@OperationParam(name = "name", type = StringType.class, min = 1),
|
||||
@OperationParam(name = "version", type = StringType.class),
|
||||
@OperationParam(name = "display", type = StringType.class, min = 1),
|
||||
@OperationParam(name = "abstract", type = BooleanType.class, min = 1),
|
||||
@OperationParam(name = "property", type = StringType.class, min = 0, max = OperationParam.MAX_UNLIMITED)
|
||||
})
|
||||
public IBaseParameters lookup(
|
||||
HttpServletRequest theServletRequest,
|
||||
@OperationParam(name = "code", max = 1) CodeType theCode,
|
||||
@OperationParam(name = "system",max = 1) UriType theSystem,
|
||||
@OperationParam(name = "coding", max = 1) Coding theCoding,
|
||||
@OperationParam(name = "version", max = 1) StringType theVersion,
|
||||
@OperationParam(name = "displayLanguage", max = 1) CodeType theDisplayLanguage,
|
||||
@OperationParam(name = "property", max = OperationParam.MAX_UNLIMITED) List<CodeType> thePropertyNames,
|
||||
RequestDetails theRequestDetails
|
||||
) {
|
||||
myLastCode = theCode;
|
||||
return myNextReturnParams;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return CodeSystem.class;
|
|
@ -59,6 +59,16 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* Version specific tests for validation using RemoteTerminologyValidationSupport.
|
||||
* The tests in this class simulate the call to a remote server and therefore, only tests the code in
|
||||
* the RemoteTerminologyServiceValidationSupport itself. The remote client call is simulated using the test providers.
|
||||
* @see RemoteTerminologyServiceValidationSupport
|
||||
*
|
||||
* Other operations are tested separately.
|
||||
* @see RemoteTerminologyLookupCodeR4Test
|
||||
* @see RemoteTerminologyServiceResourceProviderR4Test
|
||||
*/
|
||||
public class RemoteTerminologyServiceValidationSupportR4Test extends BaseValidationTestWithInlineMocks {
|
||||
private static final String DISPLAY = "DISPLAY";
|
||||
private static final String CODE_SYSTEM = "CODE_SYS";
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
{
|
||||
"resourceType": "Parameters",
|
||||
"parameter": [
|
||||
{
|
||||
"name": "name",
|
||||
"valueString": "Client_Config_CodeSystem"
|
||||
},
|
||||
{
|
||||
"name": "display",
|
||||
"valueString": "Value to display"
|
||||
},
|
||||
{
|
||||
"name": "abstract",
|
||||
"valueBoolean": false
|
||||
},
|
||||
{
|
||||
"name": "property",
|
||||
"part": [
|
||||
{
|
||||
"name": "code",
|
||||
"valueCode": "interfaces"
|
||||
},
|
||||
{
|
||||
"name": "subproperty",
|
||||
"part": [
|
||||
{
|
||||
"name": "code",
|
||||
"valueCode": "dataType"
|
||||
},
|
||||
{
|
||||
"name": "value",
|
||||
"valueString": "drugs"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "subproperty",
|
||||
"part": [
|
||||
{
|
||||
"name": "code",
|
||||
"valueCode": "apiVersion"
|
||||
},
|
||||
{
|
||||
"name": "value",
|
||||
"valueString": "4.5.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "subproperty",
|
||||
"part": [
|
||||
{
|
||||
"name": "code",
|
||||
"valueCode": "onboardStatus"
|
||||
},
|
||||
{
|
||||
"name": "value",
|
||||
"valueString": "unauthorized"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "property",
|
||||
"part": [
|
||||
{
|
||||
"name": "code",
|
||||
"valueCode": "interfaces"
|
||||
},
|
||||
{
|
||||
"name": "subproperty",
|
||||
"part": [
|
||||
{
|
||||
"name": "code",
|
||||
"valueCode": "dataType"
|
||||
},
|
||||
{
|
||||
"name": "value",
|
||||
"valueString": "ps"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "subproperty",
|
||||
"part": [
|
||||
{
|
||||
"name": "code",
|
||||
"valueCode": "apiVersion"
|
||||
},
|
||||
{
|
||||
"name": "value",
|
||||
"valueString": "4.0.0"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "subproperty",
|
||||
"part": [
|
||||
{
|
||||
"name": "code",
|
||||
"valueCode": "onboardStatus"
|
||||
},
|
||||
{
|
||||
"name": "value",
|
||||
"valueString": "contributor-testing"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
Loading…
Reference in New Issue