diff --git a/.travis.yml b/.travis.yml index 3e3dc22..7fc890b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,44 +2,43 @@ language: java jdk: openjdk11 os: linux dist: xenial - cache: timeout: 180 directories: - "$HOME/.m2/repository" - install: - - mvn install -U -DskipTests=true -Dmaven.javadoc.skip=true -B -V - +- ./scripts/install.sh script: - # master or PRs into master, use the release profile - - 'if [[ "$TRAVIS_BRANCH" =~ master* ]]; then mvn test -B -P release; fi' - - 'if ! [[ "$TRAVIS_BRANCH" =~ master* ]]; then mvn test -B; fi' - - -## export GPG details +- if [[ "$TRAVIS_BRANCH" =~ master* ]]; then mvn test -B -P release; fi +- if ! [[ "$TRAVIS_BRANCH" =~ master* ]]; then mvn test -B; fi before_deploy: - - 'if [[ "$TRAVIS_BRANCH" =~ master* ]]; then echo $GPG_SECRET_KEYS | base64 --decode | $GPG_EXECUTABLE --import; fi' - - 'if [[ "$TRAVIS_BRANCH" =~ master* ]]; then echo $GPG_OWNERTRUST | base64 --decode | $GPG_EXECUTABLE --import-ownertrust; fi' - -# NOTE: tests were already run as part of the script phase +- if [[ "$TRAVIS_BRANCH" =~ master* ]]; then echo $GPG_SECRET_KEYS | base64 --decode + | $GPG_EXECUTABLE --import; fi +- if [[ "$TRAVIS_BRANCH" =~ master* ]]; then echo $GPG_OWNERTRUST | base64 --decode + | $GPG_EXECUTABLE --import-ownertrust; fi deploy: - # deploy develop as a snapshot - - provider: script - script: "cp .travis.settings.xml $HOME/.m2/settings.xml && mvn deploy -DskipTests=true" - cleanup: false - skip_cleanup: true # this is the current correct option, soon to be deprecated by the above - on: - branch: develop - # deploy master to production - - provider: script - script: "cp .travis.settings.xml $HOME/.m2/settings.xml && mvn deploy -P release -DskipTests=true" - cleanup: false - skip_cleanup: true # this is the current correct option, soon to be deprecated by the above - on: - branch: master - -# Remove the binaries generated by this build so that the cache isn't invalidated. -# Probably a better way to do this. +- provider: script + script: cp .travis.settings.xml $HOME/.m2/settings.xml && mvn deploy -DskipTests=true + cleanup: false + skip_cleanup: true + on: + branch: develop +- provider: script + script: cp .travis.settings.xml $HOME/.m2/settings.xml && mvn deploy -P release + -DskipTests=true + cleanup: false + skip_cleanup: true + on: + branch: master before_cache: - - rm -rf $HOME/.m2/repository/org/opencds/cqf/cds +- rm -rf $HOME/.m2/repository/org/opencds/cqf/cds +notifications: + slack: + - rooms: + - secure: ECKEk6sCGWP0IdlKx8cW7JMuD5Z9UsrpIa+gcHFyYHTly0ExTAaGAFFbFI3h1hnLdVqlUDhP5F65VXurEJ430Z9nF9Eb5oy+2Evhur5S0jWiHwak8Yf7MAkhf3waAWPtft4I0imMmMHgBSMI9R9VLjCiyhhS90OQpN7giDK0uo6AovijN+UelkZUNMH+6mQS9c1q5BXqoFU90hCCX5VUPPGXAXW535ZadNTd3b284MFVmLxik+p+hg+FxjI5utAA5o5f9uxxAtEBkDgM9vxyDRKKi4rMvNG12YZ/e20RiFJwPcsUA4uuaWqAJP8J9OHRTfrbGbFUDCTaSUp8CZcS/ydoaZBb0BYhF33U19aqYTovdcKzoCmWs5csoEHeFqjKYKyBc6dZcj3sZ1C2HOoz+Hb7uqRAAfi0N+kxTR2j/h1eAPS/28np1Uh1nSuVNx1h3OwCms0fDBNANRcpXOhfI01aV7Zx0HSu4fO9Gek6fJ4mB6eK47cO0H8O9F6px1WtPvaEoCXPoaCCO5QLI+OM/WX+Vj+iYE+3Z/FBBwcTgVrrHc5GFW5SPttr01qKGjkee5VBUkPTL9zr2tvavZYzJ9V25gvCta5ZXDGyLjfW7HoB6dOh9sDpQTzMUnb2AiYKLztEaotQwJRX6Se8ASHpkzbkwMszzmpta2qR1uvsrhE= + on_success: always + on_failure: never + - rooms: + - secure: mtnhu2EQGqxdfEAZ0IpVEzRlFTKwNcGM88YGWmeiSVnmFZJuJfzGxM5bziJMTc+5R3nZcg2fuDyHeSIApLRXsBlQnmuh9TZZMWhDnBDmcd3ead7c8/8/6ap6pSEYhDPtLP4oQ+a7txutd51mGxL1zCPb43RAK573sO9n1e7ujMWsz7IZN6xyd7aHWyTjHTBt83b3gsc6KRdzXRr/1+sYxQQJ21HKtaIdfUCij+HkchwxMTtTZtQbwqbu07j3nyeBEwZhadThVu8bw48ORQuiUUAtUkp9cI5yjGiXhofGiRq5ZAw6qzesdGuT5YhVXd28b4Ljpb0dMNJVAMVspigIFnk1bh6V9c45ZaT6uyudLocHuOKWQKRSt6ntA4/wjU8ubuKb5OEE9s7SD5cddwyz6aoT11qenni2ZD7tJBYrZHTCsATIYwUWZI1IkNC0BqYR0VmCogRDo5ihALCBxlHPNeWIYj0H3Byf+8OuvBQnSrWRDQLRP/GgVjmq80fgUrPzJjGBTcv9xwRd6BQZ+rtpbK/ldPWM3VW/JNXfKwqHBQlEsuv8Id9H5TpfkJkDx57ae8lzB/5ryhAVDbn0JQvxz24r3drECK9vV6An9N4l5vWCJv+qwGttEniWRY8vNdoJnFz00b8WOd23Q1E4ztIXC0c24/amL++t6oZMqNdnik0= + on_success: never + on_failure: always \ No newline at end of file diff --git a/pom.xml b/pom.xml index 466a0d9..eb425d8 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ 1 - 1 + 3 0 ${version.major}.${version.minor}.${version.patch}-SNAPSHOT @@ -17,7 +17,8 @@ UTF-8 UTF-8 - 1.4.0 + 1.5.0 + 1.0.0 cds-hooks @@ -59,6 +60,12 @@ + + org.opencds.cqf.cql + evaluator.execution + ${cql-evaluator-execution.version} + + org.opencds.cqf.cql engine @@ -82,6 +89,12 @@ + + + com.google.code.gson + gson + 2.8.5 + diff --git a/scripts/install.sh b/scripts/install.sh new file mode 100755 index 0000000..117c140 --- /dev/null +++ b/scripts/install.sh @@ -0,0 +1,2 @@ +#!/bin/bash +mvn install -U -DskipTests=true -Dmaven.javadoc.skip=true -B -V \ No newline at end of file diff --git a/src/main/java/org/opencds/cqf/cds/discovery/DiscoveryResolutionR4.java b/src/main/java/org/opencds/cqf/cds/discovery/DiscoveryResolutionR4.java index 8b9deca..b2e8609 100644 --- a/src/main/java/org/opencds/cqf/cds/discovery/DiscoveryResolutionR4.java +++ b/src/main/java/org/opencds/cqf/cds/discovery/DiscoveryResolutionR4.java @@ -10,12 +10,26 @@ public class DiscoveryResolutionR4 { private final String PATIENT_ID_CONTEXT = "{{context.patientId}}"; - private final int URI_MAX_LENGTH = 8000; + private final int DEFAULT_MAX_URI_LENGTH = 8000; + private int maxUriLength; private IGenericClient client; public DiscoveryResolutionR4(IGenericClient client) { this.client = client; + this.maxUriLength = DEFAULT_MAX_URI_LENGTH; + } + + public int getMaxUriLength() { + return this.maxUriLength; + } + + public void setMaxUriLength(int maxUriLength) { + if (maxUriLength <= 0) { + throw new IllegalArgumentException("maxUriLength must be >0"); + } + + this.maxUriLength = maxUriLength; } public PlanDefinition resolvePlanDefinition(Bundle.BundleEntryComponent component) { @@ -115,10 +129,10 @@ private StringBuilder getCodesStringBuilder(List ret, StringBuilder code String codeToken = system + "|" + code; int postAppendLength = codes.length() + codeToken.length(); - if (codes.length() > 0 && postAppendLength < URI_MAX_LENGTH) { + if (codes.length() > 0 && postAppendLength < this.maxUriLength) { codes.append(","); } - else if (postAppendLength > URI_MAX_LENGTH) { + else if (postAppendLength > this.maxUriLength) { ret.add(codes.toString()); codes = new StringBuilder(); } @@ -128,7 +142,7 @@ else if (postAppendLength > URI_MAX_LENGTH) { public List createRequestUrl(DataRequirement dataRequirement) { if (!isPatientCompartment(dataRequirement.getType())) return null; - String patientRelatedResource = dataRequirement.getType() + "?" + getPatientSearchParam(dataRequirement.getType()) + "=" + PATIENT_ID_CONTEXT; + String patientRelatedResource = dataRequirement.getType() + "?" + getPatientSearchParam(dataRequirement.getType()) + "=Patient/" + PATIENT_ID_CONTEXT; List ret = new ArrayList<>(); if (dataRequirement.hasCodeFilter()) { for (DataRequirement.DataRequirementCodeFilterComponent codeFilterComponent : dataRequirement.getCodeFilter()) { diff --git a/src/main/java/org/opencds/cqf/cds/discovery/DiscoveryResolutionStu3.java b/src/main/java/org/opencds/cqf/cds/discovery/DiscoveryResolutionStu3.java index 565dfbd..1110d3c 100644 --- a/src/main/java/org/opencds/cqf/cds/discovery/DiscoveryResolutionStu3.java +++ b/src/main/java/org/opencds/cqf/cds/discovery/DiscoveryResolutionStu3.java @@ -9,12 +9,26 @@ public class DiscoveryResolutionStu3 { private final String PATIENT_ID_CONTEXT = "{{context.patientId}}"; - private final int URI_MAX_LENGTH = 8000; + private final int DEFAULT_MAX_URI_LENGTH = 8000; + private int maxUriLength; private IGenericClient client; public DiscoveryResolutionStu3(IGenericClient client) { this.client = client; + this.maxUriLength = DEFAULT_MAX_URI_LENGTH; + } + + public int getMaxUriLength() { + return this.maxUriLength; + } + + public void setMaxUriLength(int maxUriLength) { + if (maxUriLength <= 0) { + throw new IllegalArgumentException("maxUriLength must be >0"); + } + + this.maxUriLength = maxUriLength; } public PlanDefinition resolvePlanDefinition(Bundle.BundleEntryComponent component) { @@ -114,10 +128,10 @@ private StringBuilder getCodesStringBuilder(List ret, StringBuilder code String codeToken = system + "|" + code; int postAppendLength = codes.length() + codeToken.length(); - if (codes.length() > 0 && postAppendLength < URI_MAX_LENGTH) { + if (codes.length() > 0 && postAppendLength < this.maxUriLength) { codes.append(","); } - else if (postAppendLength > URI_MAX_LENGTH) { + else if (postAppendLength > this.maxUriLength) { ret.add(codes.toString()); codes = new StringBuilder(); } @@ -127,7 +141,7 @@ else if (postAppendLength > URI_MAX_LENGTH) { public List createRequestUrl(DataRequirement dataRequirement) { if (!isPatientCompartment(dataRequirement.getType())) return null; - String patientRelatedResource = dataRequirement.getType() + "?" + getPatientSearchParam(dataRequirement.getType()) + "=" + PATIENT_ID_CONTEXT; + String patientRelatedResource = dataRequirement.getType() + "?" + getPatientSearchParam(dataRequirement.getType()) + "=Patient/" + PATIENT_ID_CONTEXT; List ret = new ArrayList<>(); if (dataRequirement.hasCodeFilter()) { for (DataRequirement.DataRequirementCodeFilterComponent codeFilterComponent : dataRequirement.getCodeFilter()) { diff --git a/src/main/java/org/opencds/cqf/cds/evaluation/EvaluationContext.java b/src/main/java/org/opencds/cqf/cds/evaluation/EvaluationContext.java index 732ccee..4130bc3 100644 --- a/src/main/java/org/opencds/cqf/cds/evaluation/EvaluationContext.java +++ b/src/main/java/org/opencds/cqf/cds/evaluation/EvaluationContext.java @@ -4,6 +4,7 @@ import java.util.List; import org.opencds.cqf.cds.hooks.Hook; +import org.opencds.cqf.cds.providers.ProviderConfiguration; import org.opencds.cqf.cds.exceptions.NotImplementedException; import org.cqframework.cql.elm.execution.Library; @@ -45,8 +46,10 @@ public abstract class EvaluationContext { private IGenericClient client; + private ProviderConfiguration providerConfiguration; + public EvaluationContext(Hook hook, FhirVersionEnum fhirVersion, IGenericClient fhirClient, Context context, - Library library, T planDefinition) { + Library library, T planDefinition, ProviderConfiguration providerConfiguration) { // How to determine if it's a local server? // Local Server url? @@ -62,6 +65,8 @@ public EvaluationContext(Hook hook, FhirVersionEnum fhirVersion, IGenericClient this.client = fhirClient; + this.providerConfiguration = providerConfiguration; + context.registerDataProvider("http://hl7.org/fhir", getDataProvider()); } @@ -98,8 +103,6 @@ public Library getLibrary() { private DataProvider getDataProvider() { if (remoteProvider == null) { ModelResolver resolver; - - // TODO: Need to factor out all the SearchParamRegistry stuff. TerminologyProvider terminologyProvider; switch (fhirVersion) { case DSTU2: @@ -123,6 +126,11 @@ private DataProvider getDataProvider() { RestFhirRetrieveProvider provider = new RestFhirRetrieveProvider( new SearchParameterResolver(this.fhirContext), this.getHookFhirClient()); provider.setTerminologyProvider(terminologyProvider); + provider.setExpandValueSets(true); + + provider.setExpandValueSets(this.providerConfiguration.getExpandValueSets()); + provider.setMaxCodesPerQuery(this.providerConfiguration.getMaxCodesPerQuery()); + provider.setSearchStyle(this.providerConfiguration.getSearchStyle()); this.remoteProvider = new CompositeDataProvider(resolver, provider); } @@ -185,6 +193,10 @@ public IGenericClient getHookFhirClient() { return client; } + public ProviderConfiguration getProviderConfiguration() { + return this.providerConfiguration; + } + // NOTE: This is an operation defined in the cqf-ruler abstract List applyCqlToResources(List resources); } diff --git a/src/main/java/org/opencds/cqf/cds/evaluation/R4EvaluationContext.java b/src/main/java/org/opencds/cqf/cds/evaluation/R4EvaluationContext.java index c22d72c..ed209b3 100644 --- a/src/main/java/org/opencds/cqf/cds/evaluation/R4EvaluationContext.java +++ b/src/main/java/org/opencds/cqf/cds/evaluation/R4EvaluationContext.java @@ -4,6 +4,7 @@ import ca.uhn.fhir.rest.client.api.IGenericClient; import org.opencds.cqf.cds.hooks.Hook; +import org.opencds.cqf.cds.providers.ProviderConfiguration; import org.cqframework.cql.elm.execution.Library; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Parameters; @@ -12,18 +13,23 @@ import org.opencds.cqf.cql.engine.execution.Context; import org.opencds.cqf.cql.engine.terminology.TerminologyProvider; +import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; public class R4EvaluationContext extends EvaluationContext { public R4EvaluationContext(Hook hook, FhirVersionEnum fhirVersion, IGenericClient fhirClient, - TerminologyProvider terminologyProvider, Context context, Library library, PlanDefinition planDefinition) { - super(hook, fhirVersion, fhirClient, context, library, planDefinition); + TerminologyProvider terminologyProvider, Context context, Library library, PlanDefinition planDefinition, ProviderConfiguration providerConfiguration) { + super(hook, fhirVersion, fhirClient, context, library, planDefinition, providerConfiguration); } @Override List applyCqlToResources(List resources) { + if (resources == null || resources.isEmpty()) { + return new ArrayList<>(); + } + Bundle bundle = new Bundle(); for (Object res : resources) { bundle.addEntry(new Bundle.BundleEntryComponent().setResource((Resource) res)); diff --git a/src/main/java/org/opencds/cqf/cds/evaluation/Stu3EvaluationContext.java b/src/main/java/org/opencds/cqf/cds/evaluation/Stu3EvaluationContext.java index 2721103..7addb27 100644 --- a/src/main/java/org/opencds/cqf/cds/evaluation/Stu3EvaluationContext.java +++ b/src/main/java/org/opencds/cqf/cds/evaluation/Stu3EvaluationContext.java @@ -4,6 +4,7 @@ import ca.uhn.fhir.rest.client.api.IGenericClient; import org.opencds.cqf.cds.hooks.Hook; +import org.opencds.cqf.cds.providers.ProviderConfiguration; import org.cqframework.cql.elm.execution.Library; import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.Parameters; @@ -12,18 +13,23 @@ import org.opencds.cqf.cql.engine.execution.Context; import org.opencds.cqf.cql.engine.terminology.TerminologyProvider; +import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; public class Stu3EvaluationContext extends EvaluationContext { public Stu3EvaluationContext(Hook hook, FhirVersionEnum fhirVersion, IGenericClient fhirClient, - TerminologyProvider terminologyProvider, Context context, Library library, PlanDefinition planDefinition) { - super(hook, fhirVersion, fhirClient, context, library, planDefinition); + TerminologyProvider terminologyProvider, Context context, Library library, PlanDefinition planDefinition, ProviderConfiguration providerConfiguration) { + super(hook, fhirVersion, fhirClient, context, library, planDefinition, providerConfiguration); } @Override List applyCqlToResources(List resources) { + if (resources == null || resources.isEmpty()) { + return new ArrayList<>(); + } + Bundle bundle = new Bundle(); for (Object res : resources) { bundle.addEntry(new Bundle.BundleEntryComponent().setResource((Resource) res)); diff --git a/src/main/java/org/opencds/cqf/cds/hooks/BaseHookEvaluator.java b/src/main/java/org/opencds/cqf/cds/hooks/BaseHookEvaluator.java index c3c11a8..a2d258d 100644 --- a/src/main/java/org/opencds/cqf/cds/hooks/BaseHookEvaluator.java +++ b/src/main/java/org/opencds/cqf/cds/hooks/BaseHookEvaluator.java @@ -7,6 +7,7 @@ import org.opencds.cqf.cds.providers.PrefetchDataProviderDstu2; import org.opencds.cqf.cds.providers.PrefetchDataProviderR4; import org.opencds.cqf.cds.providers.PrefetchDataProviderStu3; +import org.opencds.cqf.cds.providers.PriorityRetrieveProvider; import org.opencds.cqf.cds.response.CdsCard; import org.cqframework.cql.elm.execution.ListTypeSpecifier; import org.cqframework.cql.elm.execution.ParameterDef; @@ -40,31 +41,36 @@ public List evaluate(EvaluationContext

context) throws IOException { } // Remote data retriever - TerminologyAwareRetrieveProvider remoteRetriever = new RestFhirRetrieveProvider( + RestFhirRetrieveProvider remoteRetriever = new RestFhirRetrieveProvider( new SearchParameterResolver(context.getFhirContext()), context.getHookFhirClient()); remoteRetriever.setTerminologyProvider(context.getContext().resolveTerminologyProvider()); + remoteRetriever.setExpandValueSets(context.getProviderConfiguration().getExpandValueSets()); + remoteRetriever.setMaxCodesPerQuery(context.getProviderConfiguration().getMaxCodesPerQuery()); + remoteRetriever.setSearchStyle(context.getProviderConfiguration().getSearchStyle()); TerminologyAwareRetrieveProvider prefetchRetriever; ModelResolver resolver; if (context.getFhirVersion() == FhirVersionEnum.DSTU3) { - prefetchRetriever = new PrefetchDataProviderStu3(context.getPrefetchResources(), remoteRetriever); + prefetchRetriever = new PrefetchDataProviderStu3(context.getPrefetchResources()); resolver = new Dstu3FhirModelResolver(); } else if (context.getFhirVersion() == FhirVersionEnum.DSTU2) { - prefetchRetriever = new PrefetchDataProviderDstu2(context.getPrefetchResources(), remoteRetriever); + prefetchRetriever = new PrefetchDataProviderDstu2(context.getPrefetchResources()); resolver = new Dstu2FhirModelResolver(); } else { - prefetchRetriever = new PrefetchDataProviderR4(context.getPrefetchResources(), remoteRetriever); + prefetchRetriever = new PrefetchDataProviderR4(context.getPrefetchResources()); resolver = new R4FhirModelResolver(); } // TODO: Get the "system" terminology provider. prefetchRetriever.setTerminologyProvider(context.getContext().resolveTerminologyProvider()); + + PriorityRetrieveProvider priorityRetrieveProvider = new PriorityRetrieveProvider(prefetchRetriever, remoteRetriever); context.getContext().registerDataProvider("http://hl7.org/fhir", - new CompositeDataProvider(resolver, prefetchRetriever)); + new CompositeDataProvider(resolver, priorityRetrieveProvider)); context.getContext().registerTerminologyProvider(prefetchRetriever.getTerminologyProvider()); return evaluateCdsHooksPlanDefinition(context.getContext(), context.getPlanDefinition(), diff --git a/src/main/java/org/opencds/cqf/cds/providers/PrefetchDataProviderDstu2.java b/src/main/java/org/opencds/cqf/cds/providers/PrefetchDataProviderDstu2.java index 7fbd888..1738266 100644 --- a/src/main/java/org/opencds/cqf/cds/providers/PrefetchDataProviderDstu2.java +++ b/src/main/java/org/opencds/cqf/cds/providers/PrefetchDataProviderDstu2.java @@ -1,8 +1,11 @@ package org.opencds.cqf.cds.providers; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; import org.opencds.cqf.cql.engine.elm.execution.InEvaluator; import org.opencds.cqf.cql.engine.elm.execution.IncludesEvaluator; import org.opencds.cqf.cql.engine.fhir.model.Dstu2FhirModelResolver; +import org.opencds.cqf.cql.engine.fhir.model.FhirModelResolver; import org.opencds.cqf.cql.engine.model.ModelResolver; import org.opencds.cqf.cql.engine.retrieve.RetrieveProvider; import org.opencds.cqf.cql.engine.retrieve.TerminologyAwareRetrieveProvider; @@ -10,25 +13,28 @@ import org.opencds.cqf.cql.engine.runtime.DateTime; import org.opencds.cqf.cql.engine.runtime.Interval; import org.opencds.cqf.cql.engine.terminology.ValueSetInfo; +import org.opencds.cqf.cql.evaluator.execution.util.CodeUtil; import java.util.*; public class PrefetchDataProviderDstu2 extends TerminologyAwareRetrieveProvider { private Map> prefetchResources; - private ModelResolver resolver; - private RetrieveProvider remoteProvider; - - public PrefetchDataProviderDstu2(List resources, RetrieveProvider remoteProvider) { + private FhirModelResolver resolver; + private CodeUtil codeUtil; + + public PrefetchDataProviderDstu2(List resources) { prefetchResources = PrefetchDataProviderHelper.populateMap(resources); this.resolver = new Dstu2FhirModelResolver(); - this.remoteProvider = remoteProvider; + this.codeUtil = new CodeUtil(this.resolver.getFhirContext()); } @Override public Iterable retrieve(String context, String contextPath, Object contextValue, String dataType, String templateId, String codePath, Iterable codes, String valueSet, String datePath, String dateLowPath, String dateHighPath, Interval dateRange) { + + if (codePath == null && (codes != null || valueSet != null)) { throw new IllegalArgumentException("A code path must be provided when filtering on codes or a valueset."); } @@ -41,8 +47,7 @@ public Iterable retrieve(String context, String contextPath, Object cont // This dataType can't be related to patient, therefore may // not be in the pre-fetch bundle, or might required a lookup by Id if (context.equals("Patient") && contextPath == null) { - return remoteProvider.retrieve(context, contextPath, contextValue, dataType, templateId, codePath, codes, - valueSet, datePath, dateLowPath, dateHighPath, dateRange); + return null; } List resourcesOfType = prefetchResources.get(dataType); @@ -116,7 +121,8 @@ else if (dateInterval != null if (codes != null) { Object codeObject = PrefetchDataProviderHelper .getDstu2Code(this.resolver.resolvePath(resource, codePath)); - includeResource = PrefetchDataProviderHelper.checkCodeMembership(codes, codeObject); + + includeResource = PrefetchDataProviderHelper.checkCodeMembership(codes, codeObject, this.codeUtil); } } diff --git a/src/main/java/org/opencds/cqf/cds/providers/PrefetchDataProviderHelper.java b/src/main/java/org/opencds/cqf/cds/providers/PrefetchDataProviderHelper.java index b66a668..c7959cf 100644 --- a/src/main/java/org/opencds/cqf/cds/providers/PrefetchDataProviderHelper.java +++ b/src/main/java/org/opencds/cqf/cds/providers/PrefetchDataProviderHelper.java @@ -1,5 +1,7 @@ package org.opencds.cqf.cds.providers; +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt; import ca.uhn.fhir.model.dstu2.composite.CodingDt; import ca.uhn.fhir.model.dstu2.composite.PeriodDt; @@ -11,6 +13,7 @@ import org.opencds.cqf.cql.engine.runtime.Code; import org.opencds.cqf.cql.engine.runtime.DateTime; import org.opencds.cqf.cql.engine.runtime.Interval; +import org.opencds.cqf.cql.evaluator.execution.util.CodeUtil; import java.util.*; @@ -18,6 +21,10 @@ public class PrefetchDataProviderHelper { public static Map> populateMap(List resources) { Map> prefetchResources = new HashMap<>(); + if (resources == null){ + return prefetchResources; + } + for (Object resource : resources) { if (resource instanceof Resource) { if (prefetchResources.containsKey(((Resource) resource).fhirType())) { @@ -182,22 +189,17 @@ public static Object getR4Code(Object codeObject) { return codeObject; } - public static boolean checkCodeMembership(Iterable codes, Object codeObject) { - // for now, just checking whether code values are equal... TODO - add - // intelligent checks for system and version - for (Code code : codes) { - if (codeObject instanceof String && code.getCode().equals(codeObject)) { - return true; - } else if (codeObject instanceof Code && code.getCode().equals(((Code) codeObject).getCode())) { + public static boolean checkCodeMembership(Iterable codes, Object codeObject, CodeUtil codeUtil) { + List qualifyingCodes = new ArrayList(); + + if (codeObject != null) { + qualifyingCodes = codeUtil.getElmCodesFromObject(codeObject); + + if (!qualifyingCodes.isEmpty()) { return true; - } else if (codeObject instanceof Iterable) { - for (Object obj : (Iterable) codeObject) { - if (code.getCode().equals(((Code) obj).getCode())) { - return true; - } - } } } + return false; } } diff --git a/src/main/java/org/opencds/cqf/cds/providers/PrefetchDataProviderR4.java b/src/main/java/org/opencds/cqf/cds/providers/PrefetchDataProviderR4.java index 0fd6cac..470aae8 100644 --- a/src/main/java/org/opencds/cqf/cds/providers/PrefetchDataProviderR4.java +++ b/src/main/java/org/opencds/cqf/cds/providers/PrefetchDataProviderR4.java @@ -1,28 +1,31 @@ package org.opencds.cqf.cds.providers; +import ca.uhn.fhir.context.FhirVersionEnum; import org.opencds.cqf.cql.engine.retrieve.RetrieveProvider; import org.opencds.cqf.cql.engine.retrieve.TerminologyAwareRetrieveProvider; import org.opencds.cqf.cql.engine.elm.execution.InEvaluator; import org.opencds.cqf.cql.engine.elm.execution.IncludesEvaluator; import org.opencds.cqf.cql.engine.model.ModelResolver; +import org.opencds.cqf.cql.engine.fhir.model.FhirModelResolver; import org.opencds.cqf.cql.engine.fhir.model.R4FhirModelResolver; import org.opencds.cqf.cql.engine.runtime.Code; import org.opencds.cqf.cql.engine.runtime.DateTime; import org.opencds.cqf.cql.engine.runtime.Interval; import org.opencds.cqf.cql.engine.terminology.ValueSetInfo; +import org.opencds.cqf.cql.evaluator.execution.util.CodeUtil; import java.util.*; public class PrefetchDataProviderR4 extends TerminologyAwareRetrieveProvider { private Map> prefetchResources; - private ModelResolver resolver; - private RetrieveProvider remoteProvider; + private FhirModelResolver resolver; + private CodeUtil codeUtil; - public PrefetchDataProviderR4(List resources, RetrieveProvider remoteProvider) { + public PrefetchDataProviderR4(List resources) { prefetchResources = PrefetchDataProviderHelper.populateMap(resources); - this.remoteProvider = remoteProvider; this.resolver = new R4FhirModelResolver(); + this.codeUtil = new CodeUtil(this.resolver.getFhirContext()); } @Override @@ -41,8 +44,7 @@ public Iterable retrieve(String context, String contextPath, Object cont // This dataType can't be related to patient, therefore may // not be in the pre-fetch bundle, or might required a lookup by Id if (context.equals("Patient") && contextPath == null) { - return remoteProvider.retrieve(context, contextPath, contextValue, dataType, templateId, codePath, codes, - valueSet, datePath, dateLowPath, dateHighPath, dateRange); + return null; } List resourcesOfType = prefetchResources.get(dataType); @@ -115,7 +117,7 @@ else if (dateInterval != null if (codes != null) { Object codeObject = PrefetchDataProviderHelper .getR4Code(this.resolver.resolvePath(resource, codePath)); - includeResource = PrefetchDataProviderHelper.checkCodeMembership(codes, codeObject); + includeResource = PrefetchDataProviderHelper.checkCodeMembership(codes, codeObject, this.codeUtil); } } diff --git a/src/main/java/org/opencds/cqf/cds/providers/PrefetchDataProviderStu3.java b/src/main/java/org/opencds/cqf/cds/providers/PrefetchDataProviderStu3.java index 500279f..1508e9e 100644 --- a/src/main/java/org/opencds/cqf/cds/providers/PrefetchDataProviderStu3.java +++ b/src/main/java/org/opencds/cqf/cds/providers/PrefetchDataProviderStu3.java @@ -1,8 +1,10 @@ package org.opencds.cqf.cds.providers; +import ca.uhn.fhir.context.FhirVersionEnum; import org.opencds.cqf.cql.engine.elm.execution.InEvaluator; import org.opencds.cqf.cql.engine.elm.execution.IncludesEvaluator; import org.opencds.cqf.cql.engine.fhir.model.Dstu3FhirModelResolver; +import org.opencds.cqf.cql.engine.fhir.model.FhirModelResolver; import org.opencds.cqf.cql.engine.model.ModelResolver; import org.opencds.cqf.cql.engine.retrieve.RetrieveProvider; import org.opencds.cqf.cql.engine.retrieve.TerminologyAwareRetrieveProvider; @@ -10,19 +12,20 @@ import org.opencds.cqf.cql.engine.runtime.DateTime; import org.opencds.cqf.cql.engine.runtime.Interval; import org.opencds.cqf.cql.engine.terminology.ValueSetInfo; +import org.opencds.cqf.cql.evaluator.execution.util.CodeUtil; import java.util.*; public class PrefetchDataProviderStu3 extends TerminologyAwareRetrieveProvider { private Map> prefetchResources; - private ModelResolver resolver; - private RetrieveProvider remoteProvider; + private FhirModelResolver resolver; + private CodeUtil codeUtil; - public PrefetchDataProviderStu3(List resources, RetrieveProvider remoteProvider) { + public PrefetchDataProviderStu3(List resources) { prefetchResources = PrefetchDataProviderHelper.populateMap(resources); this.resolver = new Dstu3FhirModelResolver(); - this.remoteProvider = remoteProvider; + this.codeUtil = new CodeUtil(this.resolver.getFhirContext()); } @Override @@ -41,8 +44,7 @@ public Iterable retrieve(String context, String contextPath, Object cont // This dataType can't be related to patient, therefore may // not be in the pre-fetch bundle, or might required a lookup by Id if (context.equals("Patient") && contextPath == null) { - return remoteProvider.retrieve(context, contextPath, contextValue, dataType, templateId, codePath, codes, - valueSet, datePath, dateLowPath, dateHighPath, dateRange); + return null; } List resourcesOfType = prefetchResources.get(dataType); @@ -115,7 +117,7 @@ else if (dateInterval != null if (codes != null) { Object codeObject = PrefetchDataProviderHelper .getStu3Code(this.resolver.resolvePath(resource, codePath)); - includeResource = PrefetchDataProviderHelper.checkCodeMembership(codes, codeObject); + includeResource = PrefetchDataProviderHelper.checkCodeMembership(codes, codeObject, this.codeUtil); } } diff --git a/src/main/java/org/opencds/cqf/cds/providers/PriorityRetrieveProvider.java b/src/main/java/org/opencds/cqf/cds/providers/PriorityRetrieveProvider.java new file mode 100644 index 0000000..dd40b82 --- /dev/null +++ b/src/main/java/org/opencds/cqf/cds/providers/PriorityRetrieveProvider.java @@ -0,0 +1,42 @@ +package org.opencds.cqf.cds.providers; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.opencds.cqf.cql.engine.retrieve.RetrieveProvider; +import org.opencds.cqf.cql.engine.runtime.Code; +import org.opencds.cqf.cql.engine.runtime.Interval; + +public class PriorityRetrieveProvider implements RetrieveProvider { + + private List providers = new ArrayList<>(); + + public PriorityRetrieveProvider (RetrieveProvider... providers) { + if (providers != null) { + for (RetrieveProvider provider : providers) { + this.providers.add(provider); + } + } + } + + @Override + public Iterable retrieve(String context, String contextPath, Object contextValue, String dataType, + String templateId, String codePath, Iterable codes, String valueSet, String datePath, + String dateLowPath, String dateHighPath, Interval dateRange) { + + Iterable result = null; + for (RetrieveProvider provider : providers) { + result = provider.retrieve(context, contextPath, contextValue, dataType, templateId, codePath, codes, valueSet, datePath, dateLowPath, dateHighPath, dateRange); + if (result != null && result instanceof Collection) { + if (!((Collection)result).isEmpty()) { + return result; + } + } + + } + + return result; + } + +} \ No newline at end of file diff --git a/src/main/java/org/opencds/cqf/cds/providers/ProviderConfiguration.java b/src/main/java/org/opencds/cqf/cds/providers/ProviderConfiguration.java new file mode 100644 index 0000000..187f0be --- /dev/null +++ b/src/main/java/org/opencds/cqf/cds/providers/ProviderConfiguration.java @@ -0,0 +1,37 @@ +package org.opencds.cqf.cds.providers; + +import ca.uhn.fhir.rest.api.SearchStyleEnum; + +public class ProviderConfiguration { + + public static final ProviderConfiguration DEFAULT_PROVIDER_CONFIGURATION = + new ProviderConfiguration(true, 64, SearchStyleEnum.GET, 8000); + + private int maxCodesPerQuery; + private SearchStyleEnum searchStyle; + private boolean expandValueSets; + private int maxUriLength; + + public ProviderConfiguration(boolean expandValueSets, int maxCodesPerQuery, SearchStyleEnum searchStyle, int maxUriLength) { + this.maxCodesPerQuery = maxCodesPerQuery; + this.searchStyle = searchStyle; + this.expandValueSets = expandValueSets; + this.maxUriLength = maxUriLength; + } + + public int getMaxCodesPerQuery() { + return this.maxCodesPerQuery; + } + + public SearchStyleEnum getSearchStyle() { + return this.searchStyle; + } + + public boolean getExpandValueSets() { + return this.expandValueSets; + } + + public int getMaxUriLength() { + return this.maxUriLength; + } +} \ No newline at end of file