Skip to content

Commit

Permalink
Add support for Questionnaire Launch Context extension in $populate
Browse files Browse the repository at this point in the history
  • Loading branch information
barhodes committed Sep 3, 2024
1 parent 775af19 commit 3ebf962
Show file tree
Hide file tree
Showing 22 changed files with 1,289 additions and 77 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package org.opencds.cqf.fhir.cr.inputparameters;

import ca.uhn.fhir.context.FhirContext;
import java.util.List;
import org.hl7.fhir.instance.model.api.IBaseBackboneElement;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseExtension;
import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
Expand Down Expand Up @@ -56,5 +59,6 @@ protected <R extends IBaseResource> R readRepository(Class<R> resourceType, IIdT
}
}

protected abstract IBaseParameters resolveParameters(IBaseParameters parameters);
protected abstract IBaseParameters resolveParameters(
IBaseParameters parameters, List<IBaseBackboneElement> context, List<IBaseExtension<?, ?>> launchContext);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
import static com.google.common.base.Preconditions.checkNotNull;

import java.util.List;
import org.hl7.fhir.instance.model.api.IBaseBackboneElement;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseExtension;
import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.ICompositeType;
import org.hl7.fhir.instance.model.api.IIdType;
Expand All @@ -14,7 +16,6 @@ public interface IInputParameterResolver {

public <T extends ICompositeType> IBaseParameters resolveInputParameters(List<T> dataRequirement);

@SuppressWarnings("unchecked")
public static <T extends IInputParameterResolver> T createResolver(
Repository repository,
IIdType subjectId,
Expand All @@ -23,18 +24,57 @@ public static <T extends IInputParameterResolver> T createResolver(
IBaseParameters parameters,
boolean useServerData,
IBaseBundle data) {
return createResolver(
repository, subjectId, encounterId, practitionerId, parameters, useServerData, data, null, null);
}

@SuppressWarnings("unchecked")
public static <T extends IInputParameterResolver> T createResolver(
Repository repository,
IIdType subjectId,
IIdType encounterId,
IIdType practitionerId,
IBaseParameters parameters,
boolean useServerData,
IBaseBundle data,
List<IBaseBackboneElement> context,
List<IBaseExtension<?, ?>> launchContext) {
checkNotNull(repository, "expected non-null value for repository");
var fhirVersion = repository.fhirContext().getVersion().getVersion();
switch (fhirVersion) {
case DSTU3:
return (T) new org.opencds.cqf.fhir.cr.inputparameters.dstu3.InputParameterResolver(
repository, subjectId, encounterId, practitionerId, parameters, useServerData, data);
repository,
subjectId,
encounterId,
practitionerId,
parameters,
useServerData,
data,
context,
launchContext);
case R4:
return (T) new org.opencds.cqf.fhir.cr.inputparameters.r4.InputParameterResolver(
repository, subjectId, encounterId, practitionerId, parameters, useServerData, data);
repository,
subjectId,
encounterId,
practitionerId,
parameters,
useServerData,
data,
context,
launchContext);
case R5:
return (T) new org.opencds.cqf.fhir.cr.inputparameters.r5.InputParameterResolver(
repository, subjectId, encounterId, practitionerId, parameters, useServerData, data);
repository,
subjectId,
encounterId,
practitionerId,
parameters,
useServerData,
data,
context,
launchContext);
default:
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
import org.hl7.fhir.dstu3.model.Practitioner;
import org.hl7.fhir.dstu3.model.Resource;
import org.hl7.fhir.dstu3.model.ValueSet;
import org.hl7.fhir.instance.model.api.IBaseBackboneElement;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseExtension;
import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.ICompositeType;
import org.hl7.fhir.instance.model.api.IIdType;
Expand All @@ -40,13 +42,18 @@ public InputParameterResolver(
IIdType practitionerId,
IBaseParameters parameters,
boolean useServerData,
IBaseBundle data) {
IBaseBundle data,
List<IBaseBackboneElement> context,
List<IBaseExtension<?, ?>> launchContext) {
super(repository, subjectId, encounterId, practitionerId, parameters, useServerData, data);
this.parameters = resolveParameters(parameters);
this.parameters = resolveParameters(parameters, context, launchContext);
}

@Override
protected final Parameters resolveParameters(IBaseParameters baseParameters) {
protected final Parameters resolveParameters(
IBaseParameters baseParameters,
List<IBaseBackboneElement> context,
List<IBaseExtension<?, ?>> launchContext) {
var params = parameters();
if (baseParameters != null) {
params.getParameter().addAll(((Parameters) baseParameters).getParameter());
Expand All @@ -69,6 +76,7 @@ protected final Parameters resolveParameters(IBaseParameters baseParameters) {
params.addParameter(part("%practitioner", practitioner));
}
}
// Launch Context is not supported in Dstu3 due to the lack of an Expression type
return params;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,34 @@
import static org.opencds.cqf.fhir.utility.r4.Parameters.parameters;
import static org.opencds.cqf.fhir.utility.r4.Parameters.part;

import ca.uhn.fhir.context.FhirVersionEnum;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.hl7.fhir.instance.model.api.IBaseBackboneElement;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseExtension;
import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.ICompositeType;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.DataRequirement;
import org.hl7.fhir.r4.model.Encounter;
import org.hl7.fhir.r4.model.Extension;
import org.hl7.fhir.r4.model.Parameters;
import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent;
import org.hl7.fhir.r4.model.Practitioner;
import org.hl7.fhir.r4.model.Reference;
import org.hl7.fhir.r4.model.Resource;
import org.hl7.fhir.r4.model.ResourceType;
import org.hl7.fhir.r4.model.StringType;
import org.hl7.fhir.r4.model.ValueSet;
import org.opencds.cqf.fhir.api.Repository;
import org.opencds.cqf.fhir.cr.inputparameters.BaseInputParameterResolver;
import org.opencds.cqf.fhir.utility.BundleHelper;
import org.opencds.cqf.fhir.utility.search.Searches;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -40,13 +51,18 @@ public InputParameterResolver(
IIdType practitionerId,
IBaseParameters parameters,
boolean useServerData,
IBaseBundle data) {
IBaseBundle data,
List<IBaseBackboneElement> context,
List<IBaseExtension<?, ?>> launchContext) {
super(repository, subjectId, encounterId, practitionerId, parameters, useServerData, data);
this.parameters = resolveParameters(parameters);
this.parameters = resolveParameters(parameters, context, launchContext);
}

@Override
protected final Parameters resolveParameters(IBaseParameters baseParameters) {
protected final Parameters resolveParameters(
IBaseParameters baseParameters,
List<IBaseBackboneElement> context,
List<IBaseExtension<?, ?>> launchContext) {
var params = parameters();
if (baseParameters != null) {
params.getParameter().addAll(((Parameters) baseParameters).getParameter());
Expand All @@ -69,9 +85,81 @@ protected final Parameters resolveParameters(IBaseParameters baseParameters) {
params.addParameter(part("%practitioner", practitioner));
}
}
resolveLaunchContext(params, context, launchContext);
return params;
}

protected boolean validateContext(String name, String type) {
switch (name) {
case "patient":
return type.equals(ResourceType.Patient.name());
case "encounter":
return type.equals(ResourceType.Encounter.name());
case "location":
return type.equals(ResourceType.Location.name());
case "user":
return type.equals(ResourceType.Patient.name())
|| type.equals(ResourceType.Practitioner.name())
|| type.equals(ResourceType.PractitionerRole.name())
|| type.equals(ResourceType.RelatedPerson.name());
case "study":
return type.equals(ResourceType.ResearchStudy.name());

default:
return false;
}
}

protected void resolveLaunchContext(
Parameters params, List<IBaseBackboneElement> contexts, List<IBaseExtension<?, ?>> launchContexts) {
final List<String> validContexts = Arrays.asList("patient", "encounter", "location", "user", "study");
if (launchContexts != null && !launchContexts.isEmpty()) {
launchContexts.stream().map(e -> (Extension) e).forEach(launchContext -> {
var name = ((Coding) launchContext.getExtensionByUrl("name").getValue()).getCode();
if (!validContexts.contains(name)) {
throw new IllegalArgumentException(String.format("Unsupported launch context: %s", name));
}
var type = launchContext
.getExtensionByUrl("type")
.getValueAsPrimitive()
.getValueAsString();
if (!validateContext(name, type)) {
throw new IllegalArgumentException(
String.format("Unsupported launch context for %s: %s", name, type));
}
var content = contexts == null
? null
: contexts.stream()
.map(c -> (ParametersParameterComponent) c)
.filter(c -> c.getPart().stream()
.filter(p -> p.getName().equals("name"))
.anyMatch(p -> ((StringType) p.getValue())
.getValueAsString()
.equals(name)))
.flatMap(c -> c.getPart().stream()
.filter(p -> p.getName().equals("content")))
.collect(Collectors.toList());
if (content == null || content.isEmpty()) {
throw new IllegalArgumentException(String.format("Missing content for context: %s", name));
}
var value = content.stream()
.map(p -> readRepository(
fhirContext().getResourceDefinition(type).getImplementingClass(),
((Reference) p.getValue()).getReferenceElement()))
.collect(Collectors.toList());
if (!value.isEmpty()) {
var resource =
(Resource) (value.size() == 1 ? value.get(0) : BundleHelper.newBundle(FhirVersionEnum.R4));
if (value.size() > 1) {
value.forEach(v ->
((Bundle) resource).addEntry(new BundleEntryComponent().setResource((Resource) v)));
}
params.addParameter(part("%" + name, resource));
}
});
}
}

@Override
public Parameters getParameters() {
return parameters;
Expand Down
Loading

0 comments on commit 3ebf962

Please sign in to comment.