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();
|
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_STRING = "string";
|
||||||
String TYPE_CODING = "Coding";
|
String TYPE_CODING = "Coding";
|
||||||
|
String TYPE_GROUP = "group";
|
||||||
|
|
||||||
class StringConceptProperty extends BaseConceptProperty {
|
class StringConceptProperty extends BaseConceptProperty {
|
||||||
private final String myValue;
|
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 {
|
class CodeValidationResult {
|
||||||
public static final String SOURCE_DETAILS = "sourceDetails";
|
public static final String SOURCE_DETAILS = "sourceDetails";
|
||||||
public static final String RESULT = "result";
|
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(
|
public IBaseParameters toParameters(
|
||||||
FhirContext theContext, List<? extends IPrimitiveType<String>> thePropertyNames) {
|
FhirContext theContext, List<? extends IPrimitiveType<String>> thePropertyNamesToFilter) {
|
||||||
|
|
||||||
IBaseParameters retVal = ParametersUtil.newInstance(theContext);
|
IBaseParameters retVal = ParametersUtil.newInstance(theContext);
|
||||||
if (isNotBlank(getCodeSystemDisplayName())) {
|
if (isNotBlank(getCodeSystemDisplayName())) {
|
||||||
|
@ -886,50 +923,29 @@ public interface IValidationSupport {
|
||||||
|
|
||||||
if (myProperties != null) {
|
if (myProperties != null) {
|
||||||
|
|
||||||
Set<String> properties = Collections.emptySet();
|
final List<BaseConceptProperty> propertiesToReturn;
|
||||||
if (thePropertyNames != null) {
|
if (thePropertyNamesToFilter != null && !thePropertyNamesToFilter.isEmpty()) {
|
||||||
properties = thePropertyNames.stream()
|
// 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)
|
.map(IPrimitiveType::getValueAsString)
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
|
propertiesToReturn = myProperties.stream()
|
||||||
|
.filter(p -> propertyNameList.contains(p.getPropertyName()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
} else {
|
||||||
|
propertiesToReturn = myProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (BaseConceptProperty next : myProperties) {
|
for (BaseConceptProperty next : propertiesToReturn) {
|
||||||
String propertyName = next.getPropertyName();
|
|
||||||
|
|
||||||
if (!properties.isEmpty() && !properties.contains(propertyName)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
IBase property = ParametersUtil.addParameterToParameters(theContext, retVal, "property");
|
IBase property = ParametersUtil.addParameterToParameters(theContext, retVal, "property");
|
||||||
ParametersUtil.addPartCode(theContext, property, "code", propertyName);
|
populateProperty(theContext, property, next);
|
||||||
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (myDesignations != null) {
|
if (myDesignations != null) {
|
||||||
for (ConceptDesignation next : myDesignations) {
|
for (ConceptDesignation next : myDesignations) {
|
||||||
|
|
||||||
IBase property = ParametersUtil.addParameterToParameters(theContext, retVal, "designation");
|
IBase property = ParametersUtil.addParameterToParameters(theContext, retVal, "designation");
|
||||||
ParametersUtil.addPartCode(theContext, property, "language", next.getLanguage());
|
ParametersUtil.addPartCode(theContext, property, "language", next.getLanguage());
|
||||||
ParametersUtil.addPartCoding(
|
ParametersUtil.addPartCoding(
|
||||||
|
@ -941,6 +957,41 @@ public interface IValidationSupport {
|
||||||
return retVal;
|
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) {
|
public void setErrorMessage(String theErrorMessage) {
|
||||||
myErrorMessage = theErrorMessage;
|
myErrorMessage = theErrorMessage;
|
||||||
}
|
}
|
||||||
|
|
|
@ -433,7 +433,7 @@ public class ParametersUtil {
|
||||||
addPart(theContext, theParameter, theName, coding);
|
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<?> def =
|
||||||
(BaseRuntimeElementCompositeDefinition<?>) theContext.getElementDefinition(theParameter.getClass());
|
(BaseRuntimeElementCompositeDefinition<?>) theContext.getElementDefinition(theParameter.getClass());
|
||||||
BaseRuntimeChildDefinition partChild = def.getChildByName("part");
|
BaseRuntimeChildDefinition partChild = def.getChildByName("part");
|
||||||
|
@ -448,12 +448,15 @@ public class ParametersUtil {
|
||||||
name.setValue(theName);
|
name.setValue(theName);
|
||||||
partChildElem.getChildByName("name").getMutator().addValue(part, name);
|
partChildElem.getChildByName("name").getMutator().addValue(part, name);
|
||||||
|
|
||||||
|
if (theValue != null) {
|
||||||
if (theValue instanceof IBaseResource) {
|
if (theValue instanceof IBaseResource) {
|
||||||
partChildElem.getChildByName("resource").getMutator().addValue(part, theValue);
|
partChildElem.getChildByName("resource").getMutator().addValue(part, theValue);
|
||||||
} else {
|
} else {
|
||||||
partChildElem.getChildByName("value[x]").getMutator().addValue(part, theValue);
|
partChildElem.getChildByName("value[x]").getMutator().addValue(part, theValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return part;
|
||||||
|
}
|
||||||
|
|
||||||
public static void addPartResource(
|
public static void addPartResource(
|
||||||
FhirContext theContext, IBase theParameter, String theName, IBaseResource theValue) {
|
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);
|
ParametersUtil.addParameterToParametersString(fhirContext, params, "language", displayLanguage);
|
||||||
}
|
}
|
||||||
for (String propertyName : theLookupCodeRequest.getPropertyNames()) {
|
for (String propertyName : theLookupCodeRequest.getPropertyNames()) {
|
||||||
ParametersUtil.addParameterToParametersString(fhirContext, params, "property", propertyName);
|
ParametersUtil.addParameterToParametersCode(fhirContext, params, "property", propertyName);
|
||||||
}
|
}
|
||||||
Class<? extends IBaseResource> codeSystemClass =
|
Class<? extends IBaseResource> codeSystemClass =
|
||||||
myCtx.getResourceDefinition("CodeSystem").getImplementingClass();
|
myCtx.getResourceDefinition("CodeSystem").getImplementingClass();
|
||||||
|
@ -229,7 +229,7 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
|
||||||
if (outcome != null && !outcome.isEmpty()) {
|
if (outcome != null && !outcome.isEmpty()) {
|
||||||
switch (fhirVersion) {
|
switch (fhirVersion) {
|
||||||
case DSTU3:
|
case DSTU3:
|
||||||
return generateLookupCodeResultDSTU3(
|
return generateLookupCodeResultDstu3(
|
||||||
code, system, (org.hl7.fhir.dstu3.model.Parameters) outcome);
|
code, system, (org.hl7.fhir.dstu3.model.Parameters) outcome);
|
||||||
case R4:
|
case R4:
|
||||||
return generateLookupCodeResultR4(code, system, (Parameters) outcome);
|
return generateLookupCodeResultR4(code, system, (Parameters) outcome);
|
||||||
|
@ -243,7 +243,7 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private LookupCodeResult generateLookupCodeResultDSTU3(
|
private LookupCodeResult generateLookupCodeResultDstu3(
|
||||||
String theCode, String theSystem, org.hl7.fhir.dstu3.model.Parameters outcomeDSTU3) {
|
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
|
// 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
|
// 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);
|
String parameterTypeAsString = Objects.toString(parameterComponent.getValue(), null);
|
||||||
switch (parameterComponent.getName()) {
|
switch (parameterComponent.getName()) {
|
||||||
case "property":
|
case "property":
|
||||||
org.hl7.fhir.dstu3.model.Property part = parameterComponent.getChildByName("part");
|
BaseConceptProperty conceptProperty = createConceptPropertyDstu3(parameterComponent);
|
||||||
// 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());
|
|
||||||
if (conceptProperty != null) {
|
if (conceptProperty != null) {
|
||||||
result.getProperties().add(conceptProperty);
|
result.getProperties().add(conceptProperty);
|
||||||
}
|
}
|
||||||
|
@ -289,36 +283,47 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BaseConceptProperty createBaseConceptPropertyDstu3(List<org.hl7.fhir.dstu3.model.Base> theValues) {
|
private static BaseConceptProperty createConceptPropertyDstu3(
|
||||||
org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent part1 =
|
org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent theParameterComponent) {
|
||||||
(org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent) theValues.get(0);
|
org.hl7.fhir.dstu3.model.Property property = theParameterComponent.getChildByName("part");
|
||||||
String propertyName = ((org.hl7.fhir.dstu3.model.CodeType) part1.getValue()).getValue();
|
|
||||||
|
|
||||||
BaseConceptProperty conceptProperty = null;
|
// The assumption here is that we may at east 2 elements in this part
|
||||||
org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent part2 =
|
if (property == null || property.getValues().size() < 2) {
|
||||||
(org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent) theValues.get(1);
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
org.hl7.fhir.dstu3.model.Type value = part2.getValue();
|
List<org.hl7.fhir.dstu3.model.Base> values = property.getValues();
|
||||||
if (value == null) {
|
org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent firstPart =
|
||||||
return conceptProperty;
|
(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) {
|
String groupName = secondPart.getName();
|
||||||
case TYPE_STRING:
|
if (!"subproperty".equals(groupName)) {
|
||||||
org.hl7.fhir.dstu3.model.StringType stringType = (org.hl7.fhir.dstu3.model.StringType) part2.getValue();
|
return null;
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
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) {
|
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);
|
String parameterTypeAsString = Objects.toString(parameterComponent.getValue(), null);
|
||||||
switch (parameterComponent.getName()) {
|
switch (parameterComponent.getName()) {
|
||||||
case "property":
|
case "property":
|
||||||
Property part = parameterComponent.getChildByName("part");
|
BaseConceptProperty conceptProperty = createConceptPropertyR4(parameterComponent);
|
||||||
// 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());
|
|
||||||
if (conceptProperty != null) {
|
if (conceptProperty != null) {
|
||||||
result.getProperties().add(conceptProperty);
|
result.getProperties().add(conceptProperty);
|
||||||
}
|
}
|
||||||
|
@ -428,34 +427,43 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BaseConceptProperty createBaseConceptPropertyR4(List<Base> values) {
|
private static BaseConceptProperty createConceptPropertyR4(ParametersParameterComponent thePropertyComponent) {
|
||||||
ParametersParameterComponent part1 = (ParametersParameterComponent) values.get(0);
|
Property property = thePropertyComponent.getChildByName("part");
|
||||||
String propertyName = ((CodeType) part1.getValue()).getValue();
|
|
||||||
|
|
||||||
ParametersParameterComponent part2 = (ParametersParameterComponent) values.get(1);
|
// The assumption here is that we may at east 2 elements in this part
|
||||||
|
if (property == null || property.getValues().size() < 2) {
|
||||||
Type value = part2.getValue();
|
|
||||||
if (value == null) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
BaseConceptProperty conceptProperty;
|
|
||||||
String fhirType = value.fhirType();
|
List<Base> values = property.getValues();
|
||||||
switch (fhirType) {
|
ParametersParameterComponent firstPart = (ParametersParameterComponent) values.get(0);
|
||||||
case IValidationSupport.TYPE_STRING:
|
String propertyName = ((CodeType) firstPart.getValue()).getValue();
|
||||||
StringType stringType = (StringType) part2.getValue();
|
|
||||||
conceptProperty = new StringConceptProperty(propertyName, stringType.getValue());
|
ParametersParameterComponent secondPart = (ParametersParameterComponent) values.get(1);
|
||||||
break;
|
Type value = secondPart.getValue();
|
||||||
case IValidationSupport.TYPE_CODING:
|
|
||||||
Coding coding = (Coding) part2.getValue();
|
if (value != null) {
|
||||||
conceptProperty = new CodingConceptProperty(
|
return createConceptPropertyR4(propertyName, value);
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
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) {
|
private static BaseConceptProperty createConceptPropertyR4(final String theName, final Type theValue) {
|
||||||
|
|
|
@ -1,23 +1,26 @@
|
||||||
package org.hl7.fhir.common.hapi.validation;
|
package org.hl7.fhir.common.hapi.validation;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
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.LookupCodeResult;
|
||||||
|
import ca.uhn.fhir.context.support.IValidationSupport.StringConceptProperty;
|
||||||
import ca.uhn.fhir.context.support.LookupCodeRequest;
|
import ca.uhn.fhir.context.support.LookupCodeRequest;
|
||||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
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.hl7.fhir.instance.model.api.IBaseDatatype;
|
||||||
import org.junit.jupiter.api.Nested;
|
|
||||||
import org.junit.jupiter.api.Test;
|
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.List;
|
||||||
import java.util.Optional;
|
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.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport.createConceptProperty;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
@ -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.assertTrue;
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
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 {
|
public interface ILookupCodeTest {
|
||||||
String DISPLAY = "DISPLAY";
|
String DISPLAY = "DISPLAY";
|
||||||
String LANGUAGE = "en";
|
String LANGUAGE = "en";
|
||||||
|
@ -33,26 +47,11 @@ public interface ILookupCodeTest {
|
||||||
String CODE_SYSTEM_NAME = "Code System";
|
String CODE_SYSTEM_NAME = "Code System";
|
||||||
String CODE = "CODE";
|
String CODE = "CODE";
|
||||||
|
|
||||||
interface IValidationTest {
|
IValidationSupport getService();
|
||||||
|
|
||||||
RemoteTerminologyServiceValidationSupport getService();
|
|
||||||
IResourceProvider getCodeSystemProvider();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nested
|
|
||||||
interface ILookupCodeSupportedPropertyTest extends IValidationTest {
|
|
||||||
IMyCodeSystemProvider getCodeSystemProvider();
|
IMyCodeSystemProvider getCodeSystemProvider();
|
||||||
|
|
||||||
Stream<Arguments> getEmptyPropertyValues();
|
|
||||||
|
|
||||||
Stream<Arguments> getPropertyValues();
|
|
||||||
|
|
||||||
Stream<Arguments> getDesignations();
|
|
||||||
|
|
||||||
void verifyProperty(IValidationSupport.BaseConceptProperty theConceptProperty, String theExpectedPropertName, IBaseDatatype theExpectedValue);
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
default void testLookupCode_forCodeSystemWithBlankCode_throwsException() {
|
default void lookupCode_forCodeSystemWithBlankCode_throwsException() {
|
||||||
try {
|
try {
|
||||||
getService().lookupCode(null, new LookupCodeRequest(CODE_SYSTEM, ""));
|
getService().lookupCode(null, new LookupCodeRequest(CODE_SYSTEM, ""));
|
||||||
fail();
|
fail();
|
||||||
|
@ -62,45 +61,28 @@ public interface ILookupCodeTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
default void testLookupCode_forCodeSystemWithPropertyInvalidType_throwsException() {
|
default void lookupCode_forCodeSystemWithPropertyInvalidType_throwsException() {
|
||||||
|
// test
|
||||||
LookupCodeResult result = new LookupCodeResult();
|
LookupCodeResult result = new LookupCodeResult();
|
||||||
result.getProperties().add(new IValidationSupport.BaseConceptProperty("someProperty") {
|
result.getProperties().add(new BaseConceptProperty("someProperty") {
|
||||||
public String getType() {
|
public String getType() {
|
||||||
return "someUnsupportedType";
|
return "someUnsupportedType";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
getCodeSystemProvider().setLookupCodeResult(result);
|
getCodeSystemProvider().setLookupCodeResult(result);
|
||||||
|
|
||||||
|
// test and verify
|
||||||
try {
|
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();
|
fail();
|
||||||
} catch (InternalErrorException e) {
|
} catch (InternalErrorException e) {
|
||||||
assertTrue(e.getMessage().contains("HAPI-1739: Don't know how to handle "));
|
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
|
@Test
|
||||||
default void testLookupCode_forCodeSystemWithParameters_returnsCorrectParameters() {
|
default void lookupCode_forCodeSystem_returnsCorrectResult() {
|
||||||
// setup
|
|
||||||
LookupCodeResult result = new LookupCodeResult().setFound(true).setSearchedForCode(CODE).setSearchedForSystem(CODE_SYSTEM);
|
LookupCodeResult result = new LookupCodeResult().setFound(true).setSearchedForCode(CODE).setSearchedForSystem(CODE_SYSTEM);
|
||||||
result.setCodeIsAbstract(false);
|
result.setCodeIsAbstract(false);
|
||||||
result.setCodeSystemVersion(CODE_SYSTEM_VERSION);
|
result.setCodeSystemVersion(CODE_SYSTEM_VERSION);
|
||||||
|
@ -108,111 +90,159 @@ public interface ILookupCodeTest {
|
||||||
result.setCodeDisplay(DISPLAY);
|
result.setCodeDisplay(DISPLAY);
|
||||||
getCodeSystemProvider().setLookupCodeResult(result);
|
getCodeSystemProvider().setLookupCodeResult(result);
|
||||||
|
|
||||||
// test
|
// test and verify
|
||||||
LookupCodeResult outcome = getService().lookupCode(null, new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, null));
|
LookupCodeRequest request = new LookupCodeRequest(CODE_SYSTEM, CODE, LANGUAGE, null);
|
||||||
|
verifyLookupCodeResult(request, result);
|
||||||
// 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
|
@Test
|
||||||
default void testLookupCode_withCodeSystemWithMultipleDesignations_returnsCorrectDesignations() {
|
default void lookupCode_withCodeSystemWithMultipleDesignations_returnsCorrectDesignations() {
|
||||||
// setup
|
// setup
|
||||||
final String code1 = "code1";
|
final String code1 = "code1";
|
||||||
final String code2 = "code2";
|
final String code2 = "code2";
|
||||||
|
|
||||||
IValidationSupport.ConceptDesignation designation1 = new IValidationSupport.ConceptDesignation().setUseCode(code1).setUseSystem("system1").setValue("value1").setLanguage("en");
|
ConceptDesignation designation1 = new ConceptDesignation().setUseCode(code1).setUseSystem("system1").setValue("value1").setLanguage("en");
|
||||||
IValidationSupport.ConceptDesignation designation2 = new IValidationSupport.ConceptDesignation().setUseCode(code2).setUseSystem("system2").setValue("value2").setLanguage("es");
|
ConceptDesignation designation2 = new ConceptDesignation().setUseCode(code2).setUseSystem("system2").setValue("value2").setLanguage("es");
|
||||||
LookupCodeResult result = new LookupCodeResult();
|
LookupCodeResult result = new LookupCodeResult();
|
||||||
result.getDesignations().add(designation1);
|
result.getDesignations().add(designation1);
|
||||||
result.getDesignations().add(designation2);
|
result.getDesignations().add(designation2);
|
||||||
getCodeSystemProvider().setLookupCodeResult(result);
|
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
|
// 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
|
// verify
|
||||||
assertNotNull(outcome);
|
assertNotNull(outcome);
|
||||||
|
Optional<BaseConceptProperty> propertyOptional = outcome.getProperties().stream().findFirst().filter(a -> propertyName.equals(a.getPropertyName()));
|
||||||
Collection<IValidationSupport.ConceptDesignation> designations = outcome.getDesignations();
|
assertFalse(propertyOptional.isPresent());
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
interface IMyCodeSystemProvider extends IResourceProvider {
|
||||||
String getCode();
|
String getCode();
|
||||||
|
@ -220,8 +250,4 @@ public interface ILookupCodeTest {
|
||||||
|
|
||||||
void setLookupCodeResult(LookupCodeResult theLookupCodeResult);
|
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.FhirContext;
|
||||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
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.IdParam;
|
||||||
import ca.uhn.fhir.rest.annotation.Operation;
|
import ca.uhn.fhir.rest.annotation.Operation;
|
||||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||||
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
|
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
|
||||||
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
||||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
||||||
|
import ca.uhn.fhir.util.ClasspathUtil;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
|
import org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.r4.model.BooleanType;
|
import org.hl7.fhir.r4.model.BooleanType;
|
||||||
import org.hl7.fhir.r4.model.CodeSystem;
|
import org.hl7.fhir.r4.model.CodeSystem;
|
||||||
import org.hl7.fhir.r4.model.CodeType;
|
import org.hl7.fhir.r4.model.CodeType;
|
||||||
|
import org.hl7.fhir.r4.model.Coding;
|
||||||
import org.hl7.fhir.r4.model.IdType;
|
import org.hl7.fhir.r4.model.IdType;
|
||||||
import org.hl7.fhir.r4.model.Parameters;
|
import org.hl7.fhir.r4.model.Parameters;
|
||||||
import org.hl7.fhir.r4.model.StringType;
|
import org.hl7.fhir.r4.model.StringType;
|
||||||
|
@ -25,15 +31,23 @@ import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
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
|
* Version specific tests for validation using RemoteTerminologyValidationSupport.
|
||||||
* {@link org.hl7.fhir.common.hapi.validation.support.RemoteTerminologyServiceValidationSupport#invokeRemoteValidateCode}
|
* The tests in this class simulate the call to a remote server and therefore, only tests the code in
|
||||||
* method, before and after it makes that remote client call.
|
* 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 {
|
public class RemoteTerminologyServiceResourceProviderR4Test {
|
||||||
private static final String DISPLAY = "DISPLAY";
|
private static final String DISPLAY = "DISPLAY";
|
||||||
|
@ -152,6 +166,27 @@ public class RemoteTerminologyServiceResourceProviderR4Test {
|
||||||
assertEquals(SAMPLE_MESSAGE, ourValueSetProvider.myNextReturnParams.getParameterValue("message").toString());
|
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) {
|
private void createNextCodeSystemReturnParameters(boolean theResult, String theDisplay, String theMessage) {
|
||||||
ourCodeSystemProvider.myNextReturnParams = new Parameters();
|
ourCodeSystemProvider.myNextReturnParams = new Parameters();
|
||||||
ourCodeSystemProvider.myNextReturnParams.addParameter("result", theResult);
|
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
|
@Override
|
||||||
public Class<? extends IBaseResource> getResourceType() {
|
public Class<? extends IBaseResource> getResourceType() {
|
||||||
return CodeSystem.class;
|
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.assertNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
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 {
|
public class RemoteTerminologyServiceValidationSupportR4Test extends BaseValidationTestWithInlineMocks {
|
||||||
private static final String DISPLAY = "DISPLAY";
|
private static final String DISPLAY = "DISPLAY";
|
||||||
private static final String CODE_SYSTEM = "CODE_SYS";
|
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