Skip to content

Commit

Permalink
Add support for returning subproperties for RemoteTerminologyService …
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
codeforgreen committed May 16, 2024
1 parent 14c364d commit bff59b6
Show file tree
Hide file tree
Showing 13 changed files with 1,257 additions and 781 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -530,8 +530,13 @@ public String getPropertyName() {
public abstract String getType();
}

// The reason these cannot be declared within an enum is because a Remote Terminology Service
// can support arbitrary types. We do not restrict against the types in the spec.
// Some of the types in the spec are not yet implemented as well.
// @see https://github.com/hapifhir/hapi-fhir/issues/5700
String TYPE_STRING = "string";
String TYPE_CODING = "Coding";
String TYPE_GROUP = "group";

class StringConceptProperty extends BaseConceptProperty {
private final String myValue;
Expand Down Expand Up @@ -589,6 +594,31 @@ public String getType() {
}
}

class GroupConceptProperty extends BaseConceptProperty {
public GroupConceptProperty(String thePropertyName) {
super(thePropertyName);
}

private List<BaseConceptProperty> subProperties;

public BaseConceptProperty addSubProperty(BaseConceptProperty theProperty) {
if (subProperties == null) {
subProperties = new ArrayList<>();
}
subProperties.add(theProperty);
return this;
}

public List<BaseConceptProperty> getSubProperties() {
return subProperties != null ? subProperties : Collections.emptyList();
}

@Override
public String getType() {
return TYPE_GROUP;
}
}

class CodeValidationResult {
public static final String SOURCE_DETAILS = "sourceDetails";
public static final String RESULT = "result";
Expand Down Expand Up @@ -871,8 +901,15 @@ public void throwNotFoundIfAppropriate() {
}
}

/**
* Converts the current LookupCodeResult instance into a IBaseParameters instance which is returned
* to the client of the $lookup operation.
* @param theContext the FHIR context used for running the operation
* @param thePropertyNamesToFilter the properties which are passed as parameter to filter the result.
* @return the output for the lookup operation.
*/
public IBaseParameters toParameters(
FhirContext theContext, List<? extends IPrimitiveType<String>> thePropertyNames) {
FhirContext theContext, List<? extends IPrimitiveType<String>> thePropertyNamesToFilter) {

IBaseParameters retVal = ParametersUtil.newInstance(theContext);
if (isNotBlank(getCodeSystemDisplayName())) {
Expand All @@ -886,50 +923,29 @@ public IBaseParameters toParameters(

if (myProperties != null) {

Set<String> properties = Collections.emptySet();
if (thePropertyNames != null) {
properties = thePropertyNames.stream()
final List<BaseConceptProperty> propertiesToReturn;
if (thePropertyNamesToFilter != null && !thePropertyNamesToFilter.isEmpty()) {
// TODO MM: The logic to filter of properties could actually be moved to the lookupCode provider.
// That is where the rest of the lookupCode input parameter handling is done.
// This was left as is for now but can be done with next opportunity.
Set<String> propertyNameList = thePropertyNamesToFilter.stream()
.map(IPrimitiveType::getValueAsString)
.collect(Collectors.toSet());
propertiesToReturn = myProperties.stream()
.filter(p -> propertyNameList.contains(p.getPropertyName()))
.collect(Collectors.toList());
} else {
propertiesToReturn = myProperties;
}

for (BaseConceptProperty next : myProperties) {
String propertyName = next.getPropertyName();

if (!properties.isEmpty() && !properties.contains(propertyName)) {
continue;
}

for (BaseConceptProperty next : propertiesToReturn) {
IBase property = ParametersUtil.addParameterToParameters(theContext, retVal, "property");
ParametersUtil.addPartCode(theContext, property, "code", propertyName);

String propertyType = next.getType();
switch (propertyType) {
case TYPE_STRING:
StringConceptProperty stringConceptProperty = (StringConceptProperty) next;
ParametersUtil.addPartString(
theContext, property, "value", stringConceptProperty.getValue());
break;
case TYPE_CODING:
CodingConceptProperty codingConceptProperty = (CodingConceptProperty) next;
ParametersUtil.addPartCoding(
theContext,
property,
"value",
codingConceptProperty.getCodeSystem(),
codingConceptProperty.getCode(),
codingConceptProperty.getDisplay());
break;
default:
throw new IllegalStateException(
Msg.code(1739) + "Don't know how to handle " + next.getClass());
}
populateProperty(theContext, property, next);
}
}

if (myDesignations != null) {
for (ConceptDesignation next : myDesignations) {

IBase property = ParametersUtil.addParameterToParameters(theContext, retVal, "designation");
ParametersUtil.addPartCode(theContext, property, "language", next.getLanguage());
ParametersUtil.addPartCoding(
Expand All @@ -941,6 +957,41 @@ public IBaseParameters toParameters(
return retVal;
}

private void populateProperty(
FhirContext theContext, IBase theProperty, BaseConceptProperty theConceptProperty) {
ParametersUtil.addPartCode(theContext, theProperty, "code", theConceptProperty.getPropertyName());
String propertyType = theConceptProperty.getType();
switch (propertyType) {
case TYPE_STRING:
StringConceptProperty stringConceptProperty = (StringConceptProperty) theConceptProperty;
ParametersUtil.addPartString(theContext, theProperty, "value", stringConceptProperty.getValue());
break;
case TYPE_CODING:
CodingConceptProperty codingConceptProperty = (CodingConceptProperty) theConceptProperty;
ParametersUtil.addPartCoding(
theContext,
theProperty,
"value",
codingConceptProperty.getCodeSystem(),
codingConceptProperty.getCode(),
codingConceptProperty.getDisplay());
break;
case TYPE_GROUP:
GroupConceptProperty groupConceptProperty = (GroupConceptProperty) theConceptProperty;
if (groupConceptProperty.getSubProperties().isEmpty()) {
break;
}
groupConceptProperty.getSubProperties().forEach(p -> {
IBase subProperty = ParametersUtil.addPart(theContext, theProperty, "subproperty", null);
populateProperty(theContext, subProperty, p);
});
break;
default:
throw new IllegalStateException(
Msg.code(1739) + "Don't know how to handle " + theConceptProperty.getClass());
}
}

public void setErrorMessage(String theErrorMessage) {
myErrorMessage = theErrorMessage;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,7 @@ public static void addPartCoding(
addPart(theContext, theParameter, theName, coding);
}

public static void addPart(FhirContext theContext, IBase theParameter, String theName, IBase theValue) {
public static IBase addPart(FhirContext theContext, IBase theParameter, String theName, @Nullable IBase theValue) {
BaseRuntimeElementCompositeDefinition<?> def =
(BaseRuntimeElementCompositeDefinition<?>) theContext.getElementDefinition(theParameter.getClass());
BaseRuntimeChildDefinition partChild = def.getChildByName("part");
Expand All @@ -448,11 +448,14 @@ public static void addPart(FhirContext theContext, IBase theParameter, String th
name.setValue(theName);
partChildElem.getChildByName("name").getMutator().addValue(part, name);

if (theValue instanceof IBaseResource) {
partChildElem.getChildByName("resource").getMutator().addValue(part, theValue);
} else {
partChildElem.getChildByName("value[x]").getMutator().addValue(part, theValue);
if (theValue != null) {
if (theValue instanceof IBaseResource) {
partChildElem.getChildByName("resource").getMutator().addValue(part, theValue);
} else {
partChildElem.getChildByName("value[x]").getMutator().addValue(part, theValue);
}
}
return part;
}

public static void addPartResource(
Expand Down
Original file line number Diff line number Diff line change
@@ -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."
Loading

0 comments on commit bff59b6

Please sign in to comment.