Skip to content

Commit

Permalink
Added cds hooks tests for requests with and without prefetch data (#572)
Browse files Browse the repository at this point in the history
Added cds hooks tests for requests with and without prefetch data ... cleaned up the data provider resolution
c-schuler authored Jul 25, 2022

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent f197c6e commit 6eac24f
Showing 10 changed files with 45,630 additions and 180 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
package org.opencds.cqf.ruler.cdshooks.evaluation;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;

import org.opencds.cqf.cql.engine.fhir.retrieve.Dstu3FhirQueryGenerator;
import org.opencds.cqf.cql.engine.fhir.retrieve.R4FhirQueryGenerator;
import org.opencds.cqf.cql.engine.retrieve.TerminologyAwareRetrieveProvider;
import org.opencds.cqf.cql.evaluator.engine.retrieve.PriorityRetrieveProvider;
import org.opencds.cqf.ruler.cdshooks.hooks.Hook;
import org.opencds.cqf.ruler.cdshooks.providers.PrefetchDataProviderDstu2;
import org.opencds.cqf.ruler.cdshooks.providers.PrefetchDataProviderR4;
import org.opencds.cqf.ruler.cdshooks.providers.PrefetchDataProviderStu3;
import org.opencds.cqf.ruler.cdshooks.providers.ProviderConfiguration;
import org.opencds.cqf.ruler.cdshooks.exceptions.NotImplementedException;

@@ -15,9 +22,6 @@
import org.opencds.cqf.cql.engine.model.ModelResolver;
import org.opencds.cqf.cql.engine.fhir.retrieve.RestFhirRetrieveProvider;
import org.opencds.cqf.cql.engine.fhir.searchparam.SearchParameterResolver;
import org.opencds.cqf.cql.engine.terminology.TerminologyProvider;
import org.opencds.cqf.cql.engine.fhir.terminology.Dstu3FhirTerminologyProvider;
import org.opencds.cqf.cql.engine.fhir.terminology.R4FhirTerminologyProvider;

import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
@@ -28,45 +32,37 @@
public abstract class EvaluationContext<T extends IBaseResource> {

// Provided
private Hook hook;
private FhirVersionEnum fhirVersion;
private FhirContext fhirContext;
private final Hook hook;
private final FhirVersionEnum fhirVersion;
private final FhirContext fhirContext;
private DataProvider systemProvider;
private Context context;
private T planDefinition;
private Library library;
private final Context context;
private final T planDefinition;
private final Library library;
private final IGenericClient client;
private final ProviderConfiguration providerConfiguration;
private final ModelResolver modelResolver;

// Requires resolution
private DataProvider remoteProvider;
private List<Object> contextResources;
private List<Object> prefetchResources;

private IGenericClient client;

private ProviderConfiguration providerConfiguration;

private ModelResolver modelResolver;

public EvaluationContext(Hook hook, FhirVersionEnum fhirVersion, IGenericClient fhirClient, Context context,
Library library, T planDefinition, ProviderConfiguration providerConfiguration, ModelResolver modelResolver) {

// How to determine if it's a local server?
// Local Server url?
// Need a DataRetriever for that.
// Otherwise, it's a remote data retriver.

this.hook = hook;
this.fhirVersion = fhirVersion;
this.fhirContext = FhirContext.forCached(fhirVersion);
this.context = context;
this.planDefinition = planDefinition;
this.library = library;

this.client = fhirClient;

this.providerConfiguration = providerConfiguration;
this.modelResolver = modelResolver;

context.registerDataProvider("http://hl7.org/fhir", getDataProvider());
}

@@ -102,33 +98,39 @@ public Library getLibrary() {

private DataProvider getDataProvider() {
if (remoteProvider == null) {
ModelResolver resolver = modelResolver;
TerminologyProvider terminologyProvider;
SearchParameterResolver srp = new SearchParameterResolver(fhirContext);
RestFhirRetrieveProvider remoteRetriever = new RestFhirRetrieveProvider(srp, modelResolver,
getHookFhirClient());
remoteRetriever.setTerminologyProvider(context.resolveTerminologyProvider());
remoteRetriever.setExpandValueSets(providerConfiguration.getExpandValueSets());
remoteRetriever.setMaxCodesPerQuery(providerConfiguration.getMaxCodesPerQuery());
remoteRetriever.setSearchStyle(providerConfiguration.getSearchStyle());
remoteRetriever.setModelResolver(modelResolver);
TerminologyAwareRetrieveProvider prefetchRetriever;
switch (fhirVersion) {
case DSTU2:
terminologyProvider = new Dstu3FhirTerminologyProvider(this.getSystemFhirClient());
// TODO: We need a dstu2 version remote data and terminology providers
prefetchRetriever = new PrefetchDataProviderDstu2(getPrefetchResources(), modelResolver);
break;
case DSTU3:
terminologyProvider = new Dstu3FhirTerminologyProvider(this.getSystemFhirClient());
prefetchRetriever = new PrefetchDataProviderStu3(getPrefetchResources(), modelResolver);
remoteRetriever.setFhirQueryGenerator(
new Dstu3FhirQueryGenerator(srp, context.resolveTerminologyProvider(), modelResolver));
break;
case R4:
terminologyProvider = new R4FhirTerminologyProvider(this.getSystemFhirClient());
prefetchRetriever = new PrefetchDataProviderR4(getPrefetchResources(), modelResolver);
remoteRetriever.setFhirQueryGenerator(
new R4FhirQueryGenerator(srp, context.resolveTerminologyProvider(), modelResolver));
break;
default:
throw new NotImplementedException(
"This CDS Hooks implementation is not configured for FHIR version: "
+ fhirVersion.getFhirVersionString());
}

RestFhirRetrieveProvider provider = new RestFhirRetrieveProvider(
new SearchParameterResolver(this.fhirContext), this.getHookFhirClient());
provider.setTerminologyProvider(terminologyProvider);

provider.setExpandValueSets(this.providerConfiguration.getExpandValueSets());
provider.setMaxCodesPerQuery(this.providerConfiguration.getMaxCodesPerQuery());
provider.setSearchStyle(this.providerConfiguration.getSearchStyle());

this.remoteProvider = new CompositeDataProvider(resolver, provider);
// TODO: Get the "system" terminology provider.
prefetchRetriever.setTerminologyProvider(context.resolveTerminologyProvider());
PriorityRetrieveProvider priorityRetrieveProvider = new PriorityRetrieveProvider(Arrays.asList(prefetchRetriever, remoteRetriever));
remoteProvider = new CompositeDataProvider(modelResolver, priorityRetrieveProvider);
}
return remoteProvider;
}
@@ -150,7 +152,7 @@ public List<Object> getContextResources() {
return contextResources;
}

public List<Object> getPrefetchResources() throws IOException {
public List<Object> getPrefetchResources() {
if (prefetchResources == null) {
prefetchResources = EvaluationHelper.resolvePrefetchResources(hook, fhirContext, this.getHookFhirClient(), this.providerConfiguration.getSearchStyle());
if (hook.getRequest().isApplyCql()) {
@@ -180,11 +182,9 @@ public IGenericClient getHookFhirClient() {
loggingInterceptor.setLogRequestSummary(true);
loggingInterceptor.setLogRequestHeaders(true);
loggingInterceptor.setLogRequestBody(true);

loggingInterceptor.setLogResponseSummary(true);
loggingInterceptor.setLogResponseHeaders(true);
loggingInterceptor.setLogResponseBody(true);

client.registerInterceptor(loggingInterceptor);

return client;
Original file line number Diff line number Diff line change
@@ -94,7 +94,7 @@ public static List<Object> resolveBundle(List<IBaseResource> bundles, FhirContex
return ret;
}

public static List<Object> resolvePrefetchResources(Hook hook, FhirContext fhirContext, IGenericClient client, SearchStyleEnum searchStyle) throws IOException {
public static List<Object> resolvePrefetchResources(Hook hook, FhirContext fhirContext, IGenericClient client, SearchStyleEnum searchStyle) {
List<Object> prefetchResources = new ArrayList<>();
List<String> prefetchElementsToFetch = new ArrayList<>();
Gson gson = new GsonBuilder().create();
Original file line number Diff line number Diff line change
@@ -1,28 +1,15 @@
package org.opencds.cqf.ruler.cdshooks.hooks;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;

import org.cqframework.cql.elm.execution.ListTypeSpecifier;
import org.cqframework.cql.elm.execution.ParameterDef;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.opencds.cqf.cql.engine.data.CompositeDataProvider;
import org.opencds.cqf.cql.engine.execution.Context;
import org.opencds.cqf.cql.engine.fhir.retrieve.Dstu3FhirQueryGenerator;
import org.opencds.cqf.cql.engine.fhir.retrieve.R4FhirQueryGenerator;
import org.opencds.cqf.cql.engine.fhir.retrieve.RestFhirRetrieveProvider;
import org.opencds.cqf.cql.engine.fhir.searchparam.SearchParameterResolver;
import org.opencds.cqf.cql.engine.model.ModelResolver;
import org.opencds.cqf.cql.engine.retrieve.TerminologyAwareRetrieveProvider;
import org.opencds.cqf.cql.evaluator.engine.retrieve.PriorityRetrieveProvider;
import org.opencds.cqf.ruler.cdshooks.evaluation.EvaluationContext;
import org.opencds.cqf.ruler.cdshooks.providers.PrefetchDataProviderDstu2;
import org.opencds.cqf.ruler.cdshooks.providers.PrefetchDataProviderR4;
import org.opencds.cqf.ruler.cdshooks.providers.PrefetchDataProviderStu3;
import org.opencds.cqf.ruler.cdshooks.response.CdsCard;

import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.rest.client.api.IGenericClient;


@@ -34,8 +21,7 @@ protected BaseHookEvaluator(ModelResolver modelResolver) {
this.modelResolver = modelResolver;
}

public List<CdsCard> evaluate(EvaluationContext<P> context) throws IOException {

public List<CdsCard> evaluate(EvaluationContext<P> context) {
// resolve context resources parameter
// TODO - this will need some work for libraries with multiple parameters
if (context.getLibrary().getParameters() != null && !(context.getHook() instanceof PatientViewHook)) {
@@ -45,40 +31,6 @@ public List<CdsCard> evaluate(EvaluationContext<P> context) throws IOException {
}
}
}

SearchParameterResolver srp = new SearchParameterResolver(context.getFhirContext());
RestFhirRetrieveProvider remoteRetriever = new RestFhirRetrieveProvider(srp, modelResolver,
context.getHookFhirClient());

remoteRetriever.setTerminologyProvider(context.getContext().resolveTerminologyProvider());
remoteRetriever.setExpandValueSets(context.getProviderConfiguration().getExpandValueSets());
remoteRetriever.setMaxCodesPerQuery(context.getProviderConfiguration().getMaxCodesPerQuery());
remoteRetriever.setSearchStyle(context.getProviderConfiguration().getSearchStyle());
remoteRetriever.setModelResolver(modelResolver);

TerminologyAwareRetrieveProvider prefetchRetriever;
if (context.getFhirVersion() == FhirVersionEnum.DSTU3) {
prefetchRetriever = new PrefetchDataProviderStu3(context.getPrefetchResources(), modelResolver);
remoteRetriever.setFhirQueryGenerator(
new Dstu3FhirQueryGenerator(srp, context.getContext().resolveTerminologyProvider(), modelResolver));
} else if (context.getFhirVersion() == FhirVersionEnum.DSTU2) {
prefetchRetriever = new PrefetchDataProviderDstu2(context.getPrefetchResources(), modelResolver);
// TODO: We need a dstu2 version
}
else {
prefetchRetriever = new PrefetchDataProviderR4(context.getPrefetchResources(), modelResolver);
remoteRetriever.setFhirQueryGenerator(
new R4FhirQueryGenerator(srp, context.getContext().resolveTerminologyProvider(), modelResolver));
}

// TODO: Get the "system" terminology provider.
prefetchRetriever.setTerminologyProvider(context.getContext().resolveTerminologyProvider());

PriorityRetrieveProvider priorityRetrieveProvider = new PriorityRetrieveProvider(Arrays.asList(prefetchRetriever, remoteRetriever));
context.getContext().registerDataProvider("http://hl7.org/fhir",
new CompositeDataProvider(this.modelResolver, priorityRetrieveProvider));
context.getContext().registerTerminologyProvider(prefetchRetriever.getTerminologyProvider());

return evaluateCdsHooksPlanDefinition(context.getContext(), context.getPlanDefinition(),
context.getHook().getRequest().getContext().getPatientId(), context.getSystemFhirClient());
}
Original file line number Diff line number Diff line change
@@ -71,31 +71,22 @@ public class CdsHooksServlet extends HttpServlet implements DaoRegistryUser {

@Autowired
private CqlProperties cqlProperties;

@Autowired
private DaoRegistry daoRegistry;

@Autowired
private AppProperties myAppProperties;

@Autowired
private LibraryLoaderFactory libraryLoaderFactory;

@Autowired
private JpaLibraryContentProviderFactory jpaLibraryContentProviderFactory;

@Autowired
private ProviderConfiguration providerConfiguration;

@Autowired
private JpaDataProviderFactory fhirRetrieveProviderFactory;

@Autowired
JpaTerminologyProviderFactory myJpaTerminologyProviderFactory;

@Autowired
private ModelResolver modelResolver;

@Autowired
CdsServicesCache cdsServicesCache;

@@ -196,17 +187,11 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response)
new VersionedIdentifier().withId(library.getName()).withVersion(library.getVersion()));

Context context = new Context(elm);

context.setDebugMap(this.getDebugMap());

// provider case
// No tenant information available for cds-hooks
TerminologyProvider serverTerminologyProvider = myJpaTerminologyProviderFactory.create(requestDetails);

context.registerDataProvider("http://hl7.org/fhir",
fhirRetrieveProviderFactory.create(requestDetails, serverTerminologyProvider)); // TODO make sure tooling
// handles remote

context.registerTerminologyProvider(serverTerminologyProvider);
context.registerLibraryLoader(libraryLoader);
context.setContextValue("Patient", hook.getRequest().getContext().getPatientId().replace("Patient/", ""));
@@ -218,15 +203,10 @@ protected void doPost(HttpServletRequest request, HttpServletResponse response)
planDefinition, this.getProviderConfiguration(), this.modelResolver);

this.setAccessControlHeaders(response);

response.setHeader("Content-Type", ContentType.APPLICATION_JSON.getMimeType());

R4HookEvaluator evaluator = new R4HookEvaluator(this.modelResolver);

String jsonResponse = toJsonResponse(evaluator.evaluate(evaluationContext));

logger.info(jsonResponse);

response.getWriter().println(jsonResponse);
} catch (BaseServerResponseException e) {
this.setAccessControlHeaders(response);
Loading

0 comments on commit 6eac24f

Please sign in to comment.