Merge branch 'master' into 2566_update_s13n_and_validation_handling
This commit is contained in:
commit
8cde076f1e
|
@ -70,6 +70,9 @@ page.server_jpa_mdm.mdm_operations=MDM Operations
|
||||||
page.server_jpa_mdm.mdm_details=MDM Technical Details
|
page.server_jpa_mdm.mdm_details=MDM Technical Details
|
||||||
page.server_jpa_mdm.mdm_expansion=MDM Search Expansion
|
page.server_jpa_mdm.mdm_expansion=MDM Search Expansion
|
||||||
|
|
||||||
|
section.server_jpa_cql.title=JPA Server: CQL
|
||||||
|
page.server_jpa_cql.cql=CQL Getting Started
|
||||||
|
|
||||||
section.server_jpa_partitioning.title=JPA Server: Partitioning and Multitenancy
|
section.server_jpa_partitioning.title=JPA Server: Partitioning and Multitenancy
|
||||||
page.server_jpa_partitioning.partitioning=Partitioning and Multitenancy
|
page.server_jpa_partitioning.partitioning=Partitioning and Multitenancy
|
||||||
page.server_jpa_partitioning.partition_interceptor_examples=Partition Interceptor Examples
|
page.server_jpa_partitioning.partition_interceptor_examples=Partition Interceptor Examples
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
# CQL Getting Started
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
Clinical Quality Language (CQL) is a high-level, domain-specific language focused on clinical quality and targeted at measure and decision support artifact authors. HAPI embeds a [CQL engine](https://github.com/DBCG/cql_engine) allowing the evaluation of clinical knowledge artifacts that use CQL to describe their logic.
|
||||||
|
|
||||||
|
A more detailed description of CQL is available at the [CQL Specification Implementation Guide](https://cql.hl7.org/)
|
||||||
|
|
||||||
|
The FHIR [Clinical Reasoning module](http://www.hl7.org/fhir/clinicalreasoning-module.html) defines a set of resources, profiles, operations, etc. that can be used to work with clinical knowledge within FHIR. HAPI provides implementation for some of those operations, described in more detail below.
|
||||||
|
|
||||||
|
## Working Example
|
||||||
|
|
||||||
|
A complete working example of HAPI CQL can be found in the [JPA Server Starter](/hapi-fhir/docs/server_jpa/get_started.html) project. You may wish to browse its source to see how it is set up.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
To get up and running with HAPI CQL, you can enable it using the `hapi.properties` file in the JPA Server Starter by setting `hapi.fhir.enable_cql` key to `true`. If you are running your own server follow the instructions below to [enable it in HAPI FHIR directly](#cql-settings).
|
||||||
|
|
||||||
|
Once you've enabled CQL processing, the next step is to load the appropriate knowledge artifact resources into your server.
|
||||||
|
|
||||||
|
## CQL Settings
|
||||||
|
|
||||||
|
There are two Spring beans available that add CQL processing to HAPI. You can enable CQL processing by importing the appropriate version for your server configuration.
|
||||||
|
|
||||||
|
* `ca.uhn.fhir.cql.config.CqlDstu3Config`
|
||||||
|
* `ca.uhn.fhir.cql.config.CqlR4Config`
|
||||||
|
|
||||||
|
## Operations
|
||||||
|
|
||||||
|
HAPI provides implementations for some Measure operations for DSTU3 and R4
|
||||||
|
|
||||||
|
### $evaluate-measure
|
||||||
|
|
||||||
|
The [$evaluate-measure](http://hl7.org/fhir/measure-operation-evaluate-measure.html) operation allows the evaluation of a clinical quality measure. This operation is invoked on an instance of a Measure resource:
|
||||||
|
|
||||||
|
`http://base/Measure/measureId/$evaluate-measure?subject=124&periodStart=2014-01&periodend=2014-03`
|
||||||
|
|
||||||
|
The Measure will be evaluated, including any CQL that is referenced. The CQL evaluation requires that all the supporting knowledge artifacts for a given Measure be loaded on the HAPI server, including `Libaries` and `ValueSets`.
|
|
@ -437,25 +437,25 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
||||||
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
|
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||||
obs.getCode().getCodingFirstRep().setSystem("http://loinc.org").setCode("non-existing-code").setDisplay("Display 3");
|
obs.getCode().getCodingFirstRep().setSystem("http://loinc.org").setCode("non-existing-code").setDisplay("Display 3");
|
||||||
oo = validateAndReturnOutcome(obs);
|
oo = validateAndReturnOutcome(obs);
|
||||||
assertEquals("None of the codes provided are in the value set http://example.com/fhir/ValueSet/observation-vitalsignresult (http://example.com/fhir/ValueSet/observation-vitalsignresult), and a code from this value set is required) (codes = http://loinc.org#non-existing-code)", oo.getIssueFirstRep().getDiagnostics(), encode(oo));
|
assertEquals("None of the codings provided are in the value set http://example.com/fhir/ValueSet/observation-vitalsignresult (http://example.com/fhir/ValueSet/observation-vitalsignresult), and a coding from this value set is required) (codes = http://loinc.org#non-existing-code)", oo.getIssueFirstRep().getDiagnostics(), encode(oo));
|
||||||
|
|
||||||
// Valid code with no system
|
// Valid code with no system
|
||||||
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
|
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||||
obs.getCode().getCodingFirstRep().setSystem(null).setCode("CODE3").setDisplay("Display 3");
|
obs.getCode().getCodingFirstRep().setSystem(null).setCode("CODE3").setDisplay("Display 3");
|
||||||
oo = validateAndReturnOutcome(obs);
|
oo = validateAndReturnOutcome(obs);
|
||||||
assertEquals("None of the codes provided are in the value set http://example.com/fhir/ValueSet/observation-vitalsignresult (http://example.com/fhir/ValueSet/observation-vitalsignresult), and a code from this value set is required) (codes = null#CODE3)", oo.getIssueFirstRep().getDiagnostics(), encode(oo));
|
assertEquals("None of the codings provided are in the value set http://example.com/fhir/ValueSet/observation-vitalsignresult (http://example.com/fhir/ValueSet/observation-vitalsignresult), and a coding from this value set is required) (codes = null#CODE3)", oo.getIssueFirstRep().getDiagnostics(), encode(oo));
|
||||||
|
|
||||||
// Valid code with wrong system
|
// Valid code with wrong system
|
||||||
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
|
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||||
obs.getCode().getCodingFirstRep().setSystem("http://foo").setCode("CODE3").setDisplay("Display 3");
|
obs.getCode().getCodingFirstRep().setSystem("http://foo").setCode("CODE3").setDisplay("Display 3");
|
||||||
oo = validateAndReturnOutcome(obs);
|
oo = validateAndReturnOutcome(obs);
|
||||||
assertEquals("None of the codes provided are in the value set http://example.com/fhir/ValueSet/observation-vitalsignresult (http://example.com/fhir/ValueSet/observation-vitalsignresult), and a code from this value set is required) (codes = http://foo#CODE3)", oo.getIssueFirstRep().getDiagnostics(), encode(oo));
|
assertEquals("None of the codings provided are in the value set http://example.com/fhir/ValueSet/observation-vitalsignresult (http://example.com/fhir/ValueSet/observation-vitalsignresult), and a coding from this value set is required) (codes = http://foo#CODE3)", oo.getIssueFirstRep().getDiagnostics(), encode(oo));
|
||||||
|
|
||||||
// Code that exists but isn't in the valueset
|
// Code that exists but isn't in the valueset
|
||||||
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
|
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||||
obs.getCode().getCodingFirstRep().setSystem("http://terminology.hl7.org/CodeSystem/observation-category").setCode("vital-signs").setDisplay("Display 3");
|
obs.getCode().getCodingFirstRep().setSystem("http://terminology.hl7.org/CodeSystem/observation-category").setCode("vital-signs").setDisplay("Display 3");
|
||||||
oo = validateAndReturnOutcome(obs);
|
oo = validateAndReturnOutcome(obs);
|
||||||
assertEquals("None of the codes provided are in the value set http://example.com/fhir/ValueSet/observation-vitalsignresult (http://example.com/fhir/ValueSet/observation-vitalsignresult), and a code from this value set is required) (codes = http://terminology.hl7.org/CodeSystem/observation-category#vital-signs)", oo.getIssueFirstRep().getDiagnostics(), encode(oo));
|
assertEquals("None of the codings provided are in the value set http://example.com/fhir/ValueSet/observation-vitalsignresult (http://example.com/fhir/ValueSet/observation-vitalsignresult), and a coding from this value set is required) (codes = http://terminology.hl7.org/CodeSystem/observation-category#vital-signs)", oo.getIssueFirstRep().getDiagnostics(), encode(oo));
|
||||||
|
|
||||||
// Invalid code in built-in VS/CS
|
// Invalid code in built-in VS/CS
|
||||||
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
|
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||||
|
@ -813,7 +813,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
||||||
} catch (PreconditionFailedException e) {
|
} catch (PreconditionFailedException e) {
|
||||||
outcome = (OperationOutcome) e.getOperationOutcome();
|
outcome = (OperationOutcome) e.getOperationOutcome();
|
||||||
ourLog.info("Outcome: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome));
|
ourLog.info("Outcome: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(outcome));
|
||||||
assertEquals("None of the codes provided are in the value set http://example.com/valueset (http://example.com/valueset), and a code from this value set is required) (codes = http://example.com/foo-foo#some-code)", outcome.getIssueFirstRep().getDiagnostics());
|
assertEquals("None of the codings provided are in the value set http://example.com/valueset (http://example.com/valueset), and a coding from this value set is required) (codes = http://example.com/foo-foo#some-code)", outcome.getIssueFirstRep().getDiagnostics());
|
||||||
assertEquals(OperationOutcome.IssueSeverity.ERROR, outcome.getIssueFirstRep().getSeverity());
|
assertEquals(OperationOutcome.IssueSeverity.ERROR, outcome.getIssueFirstRep().getSeverity());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -872,25 +872,25 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
||||||
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
|
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||||
obs.getCode().getCodingFirstRep().setSystem("http://loinc.org").setCode("non-existing-code").setDisplay("Display 3");
|
obs.getCode().getCodingFirstRep().setSystem("http://loinc.org").setCode("non-existing-code").setDisplay("Display 3");
|
||||||
oo = validateAndReturnOutcome(obs);
|
oo = validateAndReturnOutcome(obs);
|
||||||
assertEquals("None of the codes provided are in the value set http://example.com/fhir/ValueSet/observation-vitalsignresult (http://example.com/fhir/ValueSet/observation-vitalsignresult), and a code from this value set is required) (codes = http://loinc.org#non-existing-code)", oo.getIssueFirstRep().getDiagnostics(), encode(oo));
|
assertEquals("None of the codings provided are in the value set http://example.com/fhir/ValueSet/observation-vitalsignresult (http://example.com/fhir/ValueSet/observation-vitalsignresult), and a coding from this value set is required) (codes = http://loinc.org#non-existing-code)", oo.getIssueFirstRep().getDiagnostics(), encode(oo));
|
||||||
|
|
||||||
// Valid code with no system
|
// Valid code with no system
|
||||||
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
|
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||||
obs.getCode().getCodingFirstRep().setSystem(null).setCode("CODE3").setDisplay("Display 3");
|
obs.getCode().getCodingFirstRep().setSystem(null).setCode("CODE3").setDisplay("Display 3");
|
||||||
oo = validateAndReturnOutcome(obs);
|
oo = validateAndReturnOutcome(obs);
|
||||||
assertEquals("None of the codes provided are in the value set http://example.com/fhir/ValueSet/observation-vitalsignresult (http://example.com/fhir/ValueSet/observation-vitalsignresult), and a code from this value set is required) (codes = null#CODE3)", oo.getIssueFirstRep().getDiagnostics(), encode(oo));
|
assertEquals("None of the codings provided are in the value set http://example.com/fhir/ValueSet/observation-vitalsignresult (http://example.com/fhir/ValueSet/observation-vitalsignresult), and a coding from this value set is required) (codes = null#CODE3)", oo.getIssueFirstRep().getDiagnostics(), encode(oo));
|
||||||
|
|
||||||
// Valid code with wrong system
|
// Valid code with wrong system
|
||||||
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
|
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||||
obs.getCode().getCodingFirstRep().setSystem("http://foo").setCode("CODE3").setDisplay("Display 3");
|
obs.getCode().getCodingFirstRep().setSystem("http://foo").setCode("CODE3").setDisplay("Display 3");
|
||||||
oo = validateAndReturnOutcome(obs);
|
oo = validateAndReturnOutcome(obs);
|
||||||
assertEquals("None of the codes provided are in the value set http://example.com/fhir/ValueSet/observation-vitalsignresult (http://example.com/fhir/ValueSet/observation-vitalsignresult), and a code from this value set is required) (codes = http://foo#CODE3)", oo.getIssueFirstRep().getDiagnostics(), encode(oo));
|
assertEquals("None of the codings provided are in the value set http://example.com/fhir/ValueSet/observation-vitalsignresult (http://example.com/fhir/ValueSet/observation-vitalsignresult), and a coding from this value set is required) (codes = http://foo#CODE3)", oo.getIssueFirstRep().getDiagnostics(), encode(oo));
|
||||||
|
|
||||||
// Code that exists but isn't in the valueset
|
// Code that exists but isn't in the valueset
|
||||||
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
|
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||||
obs.getCode().getCodingFirstRep().setSystem("http://terminology.hl7.org/CodeSystem/observation-category").setCode("vital-signs").setDisplay("Display 3");
|
obs.getCode().getCodingFirstRep().setSystem("http://terminology.hl7.org/CodeSystem/observation-category").setCode("vital-signs").setDisplay("Display 3");
|
||||||
oo = validateAndReturnOutcome(obs);
|
oo = validateAndReturnOutcome(obs);
|
||||||
assertEquals("None of the codes provided are in the value set http://example.com/fhir/ValueSet/observation-vitalsignresult (http://example.com/fhir/ValueSet/observation-vitalsignresult), and a code from this value set is required) (codes = http://terminology.hl7.org/CodeSystem/observation-category#vital-signs)", oo.getIssueFirstRep().getDiagnostics(), encode(oo));
|
assertEquals("None of the codings provided are in the value set http://example.com/fhir/ValueSet/observation-vitalsignresult (http://example.com/fhir/ValueSet/observation-vitalsignresult), and a coding from this value set is required) (codes = http://terminology.hl7.org/CodeSystem/observation-category#vital-signs)", oo.getIssueFirstRep().getDiagnostics(), encode(oo));
|
||||||
|
|
||||||
// Invalid code in built-in VS/CS
|
// Invalid code in built-in VS/CS
|
||||||
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
|
obs.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||||
|
@ -1028,7 +1028,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
||||||
ourLog.info(myFhirCtx.newJsonParser().encodeResourceToString(allergy));
|
ourLog.info(myFhirCtx.newJsonParser().encodeResourceToString(allergy));
|
||||||
|
|
||||||
OperationOutcome oo = validateAndReturnOutcome(allergy);
|
OperationOutcome oo = validateAndReturnOutcome(allergy);
|
||||||
assertThat(encode(oo), containsString("None of the codes provided are in the value set http://hl7.org/fhir/ValueSet/allergyintolerance-clinical"));
|
assertThat(encode(oo), containsString("None of the codings provided are in the value set http://hl7.org/fhir/ValueSet/allergyintolerance-clinical"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
@ -1138,7 +1138,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
||||||
|
|
||||||
// It would be ok for this to produce 0 issues, or just an information message too
|
// It would be ok for this to produce 0 issues, or just an information message too
|
||||||
assertEquals(1, OperationOutcomeUtil.getIssueCount(myFhirCtx, oo));
|
assertEquals(1, OperationOutcomeUtil.getIssueCount(myFhirCtx, oo));
|
||||||
assertEquals("None of the codes provided are in the value set http://hl7.org/fhir/ValueSet/identifier-type (http://hl7.org/fhir/ValueSet/identifier-type), and a code should come from this value set unless it has no suitable code and the validator cannot judge what is suitable) (codes = http://foo#bar)", OperationOutcomeUtil.getFirstIssueDetails(myFhirCtx, oo));
|
assertEquals("None of the codings provided are in the value set http://hl7.org/fhir/ValueSet/identifier-type (http://hl7.org/fhir/ValueSet/identifier-type), and a coding should come from this value set unless it has no suitable code (note that the validator cannot judge what is suitable) (codes = http://foo#bar)", OperationOutcomeUtil.getFirstIssueDetails(myFhirCtx, oo));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1630,7 +1630,7 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
||||||
} catch (PreconditionFailedException e) {
|
} catch (PreconditionFailedException e) {
|
||||||
OperationOutcome oo = (OperationOutcome) e.getOperationOutcome();
|
OperationOutcome oo = (OperationOutcome) e.getOperationOutcome();
|
||||||
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo));
|
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo));
|
||||||
assertThat(oo.getIssueFirstRep().getDiagnostics(), containsString("None of the codes provided are in the value set http://hl7.org/fhir/ValueSet/condition-clinical"));
|
assertThat(oo.getIssueFirstRep().getDiagnostics(), containsString("None of the codings provided are in the value set http://hl7.org/fhir/ValueSet/condition-clinical"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,6 @@ public class ResourceProviderR5ConceptMapTest extends BaseResourceProviderR5Test
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTranslateWithConceptMapUrlAndVersion() {
|
public void testTranslateWithConceptMapUrlAndVersion() {
|
||||||
|
|
||||||
//- conceptMap1 v1
|
//- conceptMap1 v1
|
||||||
ConceptMap conceptMap1 = new ConceptMap();
|
ConceptMap conceptMap1 = new ConceptMap();
|
||||||
conceptMap1.setUrl(CM_URL).setVersion("v1").setSource(new UriType(VS_URL)).setTarget(new UriType(VS_URL_2));
|
conceptMap1.setUrl(CM_URL).setVersion("v1").setSource(new UriType(VS_URL)).setTarget(new UriType(VS_URL_2));
|
||||||
|
@ -63,7 +62,6 @@ public class ResourceProviderR5ConceptMapTest extends BaseResourceProviderR5Test
|
||||||
|
|
||||||
ourLog.info("ConceptMap: 2 \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap2));
|
ourLog.info("ConceptMap: 2 \n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap2));
|
||||||
|
|
||||||
|
|
||||||
Parameters inParams = new Parameters();
|
Parameters inParams = new Parameters();
|
||||||
inParams.addParameter().setName("url").setValue(new UriType(CM_URL));
|
inParams.addParameter().setName("url").setValue(new UriType(CM_URL));
|
||||||
inParams.addParameter().setName("conceptMapVersion").setValue(new StringType("v2"));
|
inParams.addParameter().setName("conceptMapVersion").setValue(new StringType("v2"));
|
||||||
|
@ -73,7 +71,6 @@ public class ResourceProviderR5ConceptMapTest extends BaseResourceProviderR5Test
|
||||||
|
|
||||||
ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams));
|
ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams));
|
||||||
|
|
||||||
|
|
||||||
Parameters respParams = myClient
|
Parameters respParams = myClient
|
||||||
.operation()
|
.operation()
|
||||||
.onType(ConceptMap.class)
|
.onType(ConceptMap.class)
|
||||||
|
@ -82,8 +79,7 @@ public class ResourceProviderR5ConceptMapTest extends BaseResourceProviderR5Test
|
||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
ourLog.info("Response Parameters\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(respParams));
|
ourLog.info("Response Parameters\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(respParams));
|
||||||
|
|
||||||
|
|
||||||
ParametersParameterComponent param = getParameterByName(respParams, "result");
|
ParametersParameterComponent param = getParameterByName(respParams, "result");
|
||||||
assertTrue(((BooleanType) param.getValue()).booleanValue());
|
assertTrue(((BooleanType) param.getValue()).booleanValue());
|
||||||
|
|
||||||
|
@ -92,7 +88,8 @@ public class ResourceProviderR5ConceptMapTest extends BaseResourceProviderR5Test
|
||||||
|
|
||||||
assertEquals(1, getNumberOfParametersByName(respParams, "match"));
|
assertEquals(1, getNumberOfParametersByName(respParams, "match"));
|
||||||
param = getParametersByName(respParams, "match").get(0);
|
param = getParametersByName(respParams, "match").get(0);
|
||||||
assertEquals(2, param.getPart().size());
|
|
||||||
|
assertEquals(3, param.getPart().size());
|
||||||
|
|
||||||
ParametersParameterComponent part = getPartByName(param, "concept");
|
ParametersParameterComponent part = getPartByName(param, "concept");
|
||||||
Coding coding = (Coding) part.getValue();
|
Coding coding = (Coding) part.getValue();
|
||||||
|
@ -104,7 +101,9 @@ public class ResourceProviderR5ConceptMapTest extends BaseResourceProviderR5Test
|
||||||
|
|
||||||
part = getPartByName(param, "source");
|
part = getPartByName(param, "source");
|
||||||
assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString());
|
assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString());
|
||||||
|
|
||||||
|
part = getPartByName(param, "equivalence");
|
||||||
|
assertEquals("relatedto", part.getValue().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -22,14 +22,15 @@ package ca.uhn.fhir.cql.common.provider;
|
||||||
|
|
||||||
import org.cqframework.cql.cql2elm.FhirLibrarySourceProvider;
|
import org.cqframework.cql.cql2elm.FhirLibrarySourceProvider;
|
||||||
import org.hl7.elm.r1.VersionedIdentifier;
|
import org.hl7.elm.r1.VersionedIdentifier;
|
||||||
|
import org.opencds.cqf.cql.evaluator.cql2elm.content.LibraryContentType;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
public class LibrarySourceProvider<LibraryType, AttachmentType>
|
public class LibraryContentProvider<LibraryType, AttachmentType>
|
||||||
implements org.cqframework.cql.cql2elm.LibrarySourceProvider {
|
implements org.opencds.cqf.cql.evaluator.cql2elm.content.LibraryContentProvider {
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(LibrarySourceProvider.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(LibraryContentProvider.class);
|
||||||
|
|
||||||
private FhirLibrarySourceProvider innerProvider;
|
private FhirLibrarySourceProvider innerProvider;
|
||||||
private LibraryResolutionProvider<LibraryType> provider;
|
private LibraryResolutionProvider<LibraryType> provider;
|
||||||
|
@ -37,7 +38,7 @@ public class LibrarySourceProvider<LibraryType, AttachmentType>
|
||||||
private Function<AttachmentType, String> getContentType;
|
private Function<AttachmentType, String> getContentType;
|
||||||
private Function<AttachmentType, byte[]> getContent;
|
private Function<AttachmentType, byte[]> getContent;
|
||||||
|
|
||||||
public LibrarySourceProvider(LibraryResolutionProvider<LibraryType> provider,
|
public LibraryContentProvider(LibraryResolutionProvider<LibraryType> provider,
|
||||||
Function<LibraryType, Iterable<AttachmentType>> getAttachments,
|
Function<LibraryType, Iterable<AttachmentType>> getAttachments,
|
||||||
Function<AttachmentType, String> getContentType, Function<AttachmentType, byte[]> getContent) {
|
Function<AttachmentType, String> getContentType, Function<AttachmentType, byte[]> getContent) {
|
||||||
|
|
||||||
|
@ -50,7 +51,13 @@ public class LibrarySourceProvider<LibraryType, AttachmentType>
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InputStream getLibrarySource(VersionedIdentifier versionedIdentifier) {
|
public InputStream getLibraryContent(VersionedIdentifier versionedIdentifier, LibraryContentType libraryContentType){
|
||||||
|
|
||||||
|
// TODO: Support loading ELM
|
||||||
|
if (libraryContentType != LibraryContentType.CQL) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
LibraryType lib = this.provider.resolveLibraryByName(versionedIdentifier.getId(),
|
LibraryType lib = this.provider.resolveLibraryByName(versionedIdentifier.getId(),
|
||||||
versionedIdentifier.getVersion());
|
versionedIdentifier.getVersion());
|
|
@ -27,6 +27,7 @@ import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import org.cqframework.cql.cql2elm.model.Model;
|
import org.cqframework.cql.cql2elm.model.Model;
|
||||||
|
import org.cqframework.cql.elm.execution.Library;
|
||||||
import org.hl7.elm.r1.VersionedIdentifier;
|
import org.hl7.elm.r1.VersionedIdentifier;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
|
|
||||||
|
@ -46,4 +47,9 @@ public abstract class BaseCqlConfig {
|
||||||
Map<VersionedIdentifier, Model> globalModelCache() {
|
Map<VersionedIdentifier, Model> globalModelCache() {
|
||||||
return new ConcurrentHashMap<VersionedIdentifier, Model>();
|
return new ConcurrentHashMap<VersionedIdentifier, Model>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean(name="globalLibraryCache")
|
||||||
|
Map<org.cqframework.cql.elm.execution.VersionedIdentifier, Library> globalLibraryCache() {
|
||||||
|
return new ConcurrentHashMap<org.cqframework.cql.elm.execution.VersionedIdentifier, Library>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,12 +26,20 @@ import ca.uhn.fhir.cql.common.provider.EvaluationProviderFactory;
|
||||||
import ca.uhn.fhir.cql.common.provider.LibraryResolutionProvider;
|
import ca.uhn.fhir.cql.common.provider.LibraryResolutionProvider;
|
||||||
import ca.uhn.fhir.cql.dstu3.evaluation.ProviderFactory;
|
import ca.uhn.fhir.cql.dstu3.evaluation.ProviderFactory;
|
||||||
import ca.uhn.fhir.cql.dstu3.helper.LibraryHelper;
|
import ca.uhn.fhir.cql.dstu3.helper.LibraryHelper;
|
||||||
|
import ca.uhn.fhir.cql.dstu3.listener.ElmCacheResourceChangeListener;
|
||||||
import ca.uhn.fhir.cql.dstu3.provider.JpaTerminologyProvider;
|
import ca.uhn.fhir.cql.dstu3.provider.JpaTerminologyProvider;
|
||||||
import ca.uhn.fhir.cql.dstu3.provider.LibraryResolutionProviderImpl;
|
import ca.uhn.fhir.cql.dstu3.provider.LibraryResolutionProviderImpl;
|
||||||
import ca.uhn.fhir.cql.dstu3.provider.MeasureOperationsProvider;
|
import ca.uhn.fhir.cql.dstu3.provider.MeasureOperationsProvider;
|
||||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||||
|
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||||
|
import ca.uhn.fhir.jpa.cache.IResourceChangeListenerRegistry;
|
||||||
import ca.uhn.fhir.jpa.term.api.ITermReadSvcDstu3;
|
import ca.uhn.fhir.jpa.term.api.ITermReadSvcDstu3;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
|
|
||||||
|
import org.cqframework.cql.cql2elm.CqlTranslatorOptions;
|
||||||
import org.cqframework.cql.cql2elm.model.Model;
|
import org.cqframework.cql.cql2elm.model.Model;
|
||||||
|
import org.cqframework.cql.elm.execution.Library;
|
||||||
import org.hl7.elm.r1.VersionedIdentifier;
|
import org.hl7.elm.r1.VersionedIdentifier;
|
||||||
import org.opencds.cqf.cql.engine.fhir.model.Dstu3FhirModelResolver;
|
import org.opencds.cqf.cql.engine.fhir.model.Dstu3FhirModelResolver;
|
||||||
import org.opencds.cqf.cql.engine.model.ModelResolver;
|
import org.opencds.cqf.cql.engine.model.ModelResolver;
|
||||||
|
@ -60,7 +68,7 @@ public class CqlDstu3Config extends BaseCqlConfig {
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
@Bean
|
@Bean
|
||||||
LibraryResolutionProvider libraryResolutionProvider() {
|
LibraryResolutionProvider<org.hl7.fhir.dstu3.model.Library> libraryResolutionProvider() {
|
||||||
return new LibraryResolutionProviderImpl();
|
return new LibraryResolutionProviderImpl();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,13 +78,28 @@ public class CqlDstu3Config extends BaseCqlConfig {
|
||||||
return new MeasureOperationsProvider();
|
return new MeasureOperationsProvider();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Lazy
|
||||||
@Bean
|
@Bean
|
||||||
public ModelResolver fhirModelResolver() {
|
public ModelResolver fhirModelResolver() {
|
||||||
return new CachingModelResolverDecorator(new Dstu3FhirModelResolver());
|
return new CachingModelResolverDecorator(new Dstu3FhirModelResolver());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Lazy
|
||||||
@Bean
|
@Bean
|
||||||
public LibraryHelper libraryHelper(Map<VersionedIdentifier, Model> globalModelCache) {
|
public LibraryHelper libraryHelper(Map<VersionedIdentifier, Model> globalModelCache, Map<org.cqframework.cql.elm.execution.VersionedIdentifier, Library> globalLibraryCache, CqlTranslatorOptions cqlTranslatorOptions) {
|
||||||
return new LibraryHelper(globalModelCache);
|
return new LibraryHelper(globalModelCache, globalLibraryCache, cqlTranslatorOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public CqlTranslatorOptions cqlTranslatorOptions() {
|
||||||
|
return CqlTranslatorOptions.defaultOptions().withCompatibilityLevel("1.3");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ElmCacheResourceChangeListener elmCacheResourceChangeListener(IResourceChangeListenerRegistry resourceChangeListenerRegistry, IFhirResourceDao<org.hl7.fhir.dstu3.model.Library> libraryDao, Map<org.cqframework.cql.elm.execution.VersionedIdentifier, Library> globalLibraryCache) {
|
||||||
|
ElmCacheResourceChangeListener listener = new ElmCacheResourceChangeListener(libraryDao, globalLibraryCache);
|
||||||
|
resourceChangeListenerRegistry.registerResourceResourceChangeListener("Library", new SearchParameterMap(), listener, 1000);
|
||||||
|
return listener;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,12 +27,20 @@ import ca.uhn.fhir.cql.common.provider.EvaluationProviderFactory;
|
||||||
import ca.uhn.fhir.cql.common.provider.LibraryResolutionProvider;
|
import ca.uhn.fhir.cql.common.provider.LibraryResolutionProvider;
|
||||||
import ca.uhn.fhir.cql.r4.evaluation.ProviderFactory;
|
import ca.uhn.fhir.cql.r4.evaluation.ProviderFactory;
|
||||||
import ca.uhn.fhir.cql.r4.helper.LibraryHelper;
|
import ca.uhn.fhir.cql.r4.helper.LibraryHelper;
|
||||||
|
import ca.uhn.fhir.cql.r4.listener.ElmCacheResourceChangeListener;
|
||||||
import ca.uhn.fhir.cql.r4.provider.JpaTerminologyProvider;
|
import ca.uhn.fhir.cql.r4.provider.JpaTerminologyProvider;
|
||||||
import ca.uhn.fhir.cql.r4.provider.LibraryResolutionProviderImpl;
|
import ca.uhn.fhir.cql.r4.provider.LibraryResolutionProviderImpl;
|
||||||
import ca.uhn.fhir.cql.r4.provider.MeasureOperationsProvider;
|
import ca.uhn.fhir.cql.r4.provider.MeasureOperationsProvider;
|
||||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||||
|
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||||
|
import ca.uhn.fhir.jpa.cache.IResourceChangeListenerRegistry;
|
||||||
import ca.uhn.fhir.jpa.term.api.ITermReadSvcR4;
|
import ca.uhn.fhir.jpa.term.api.ITermReadSvcR4;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
|
|
||||||
|
import org.cqframework.cql.cql2elm.CqlTranslatorOptions;
|
||||||
import org.cqframework.cql.cql2elm.model.Model;
|
import org.cqframework.cql.cql2elm.model.Model;
|
||||||
|
import org.cqframework.cql.elm.execution.Library;
|
||||||
import org.hl7.elm.r1.VersionedIdentifier;
|
import org.hl7.elm.r1.VersionedIdentifier;
|
||||||
import org.opencds.cqf.cql.engine.fhir.model.R4FhirModelResolver;
|
import org.opencds.cqf.cql.engine.fhir.model.R4FhirModelResolver;
|
||||||
import org.opencds.cqf.cql.engine.model.ModelResolver;
|
import org.opencds.cqf.cql.engine.model.ModelResolver;
|
||||||
|
@ -55,19 +63,21 @@ public class CqlR4Config extends BaseCqlConfig {
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
@Bean
|
@Bean
|
||||||
TerminologyProvider terminologyProvider(ITermReadSvcR4 theITermReadSvc, DaoRegistry theDaoRegistry, IValidationSupport theValidationSupport) {
|
TerminologyProvider terminologyProvider(ITermReadSvcR4 theITermReadSvc, DaoRegistry theDaoRegistry,
|
||||||
|
IValidationSupport theValidationSupport) {
|
||||||
return new JpaTerminologyProvider(theITermReadSvc, theDaoRegistry, theValidationSupport);
|
return new JpaTerminologyProvider(theITermReadSvc, theDaoRegistry, theValidationSupport);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
@Bean
|
@Bean
|
||||||
EvaluationProviderFactory evaluationProviderFactory(FhirContext theFhirContext, DaoRegistry theDaoRegistry, TerminologyProvider theLocalSystemTerminologyProvider, ModelResolver modelResolver) {
|
EvaluationProviderFactory evaluationProviderFactory(FhirContext theFhirContext, DaoRegistry theDaoRegistry,
|
||||||
|
TerminologyProvider theLocalSystemTerminologyProvider, ModelResolver modelResolver) {
|
||||||
return new ProviderFactory(theFhirContext, theDaoRegistry, theLocalSystemTerminologyProvider, modelResolver);
|
return new ProviderFactory(theFhirContext, theDaoRegistry, theLocalSystemTerminologyProvider, modelResolver);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Lazy
|
@Lazy
|
||||||
@Bean
|
@Bean
|
||||||
LibraryResolutionProvider libraryResolutionProvider() {
|
LibraryResolutionProvider<org.hl7.fhir.r4.model.Library> libraryResolutionProvider() {
|
||||||
return new LibraryResolutionProviderImpl();
|
return new LibraryResolutionProviderImpl();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,13 +87,30 @@ public class CqlR4Config extends BaseCqlConfig {
|
||||||
return new MeasureOperationsProvider();
|
return new MeasureOperationsProvider();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Lazy
|
||||||
@Bean
|
@Bean
|
||||||
public ModelResolver fhirModelResolver() {
|
public ModelResolver fhirModelResolver() {
|
||||||
return new CachingModelResolverDecorator(new R4FhirModelResolver());
|
return new CachingModelResolverDecorator(new R4FhirModelResolver());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Lazy
|
||||||
@Bean
|
@Bean
|
||||||
public LibraryHelper libraryHelper(Map<VersionedIdentifier, Model> globalModelCache) {
|
public LibraryHelper libraryHelper(Map<VersionedIdentifier, Model> globalModelCache,
|
||||||
return new LibraryHelper(globalModelCache);
|
Map<org.cqframework.cql.elm.execution.VersionedIdentifier, Library> globalLibraryCache,
|
||||||
|
CqlTranslatorOptions cqlTranslatorOptions) {
|
||||||
|
return new LibraryHelper(globalModelCache, globalLibraryCache, cqlTranslatorOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
@Bean
|
||||||
|
public CqlTranslatorOptions cqlTranslatorOptions() {
|
||||||
|
return CqlTranslatorOptions.defaultOptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ElmCacheResourceChangeListener elmCacheResourceChangeListener(IResourceChangeListenerRegistry resourceChangeListenerRegistry, IFhirResourceDao<org.hl7.fhir.r4.model.Library> libraryDao, Map<org.cqframework.cql.elm.execution.VersionedIdentifier, Library> globalLibraryCache) {
|
||||||
|
ElmCacheResourceChangeListener listener = new ElmCacheResourceChangeListener(libraryDao, globalLibraryCache);
|
||||||
|
resourceChangeListenerRegistry.registerResourceResourceChangeListener("Library", new SearchParameterMap(), listener, 1000);
|
||||||
|
return listener;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@ import org.hl7.fhir.dstu3.model.Measure;
|
||||||
import org.hl7.fhir.dstu3.model.MeasureReport;
|
import org.hl7.fhir.dstu3.model.MeasureReport;
|
||||||
import org.hl7.fhir.dstu3.model.Observation;
|
import org.hl7.fhir.dstu3.model.Observation;
|
||||||
import org.hl7.fhir.dstu3.model.Patient;
|
import org.hl7.fhir.dstu3.model.Patient;
|
||||||
|
import org.hl7.fhir.dstu3.model.Quantity;
|
||||||
import org.hl7.fhir.dstu3.model.Reference;
|
import org.hl7.fhir.dstu3.model.Reference;
|
||||||
import org.hl7.fhir.dstu3.model.Resource;
|
import org.hl7.fhir.dstu3.model.Resource;
|
||||||
import org.hl7.fhir.dstu3.model.StringType;
|
import org.hl7.fhir.dstu3.model.StringType;
|
||||||
|
@ -677,7 +678,7 @@ public class MeasureEvaluation {
|
||||||
.setValue(new StringType(sdeKey));
|
.setValue(new StringType(sdeKey));
|
||||||
obsExtension.addExtension(extExtPop);
|
obsExtension.addExtension(extExtPop);
|
||||||
obs.addExtension(obsExtension);
|
obs.addExtension(obsExtension);
|
||||||
obs.setValue(new IntegerType(sdeAccumulatorValue));
|
obs.setValue(new Quantity(sdeAccumulatorValue));
|
||||||
if(!isSingle) {
|
if(!isSingle) {
|
||||||
valueCoding.setCode(sdeAccumulatorKey);
|
valueCoding.setCode(sdeAccumulatorKey);
|
||||||
obsCodeableConcept.setCoding(Collections.singletonList(valueCoding));
|
obsCodeableConcept.setCoding(Collections.singletonList(valueCoding));
|
||||||
|
|
|
@ -20,11 +20,12 @@ package ca.uhn.fhir.cql.dstu3.helper;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import ca.uhn.fhir.cql.common.evaluation.LibraryLoader;
|
|
||||||
import ca.uhn.fhir.cql.common.provider.LibraryResolutionProvider;
|
import ca.uhn.fhir.cql.common.provider.LibraryResolutionProvider;
|
||||||
import ca.uhn.fhir.cql.common.provider.LibrarySourceProvider;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.cqframework.cql.cql2elm.LibraryManager;
|
import org.cqframework.cql.cql2elm.LibraryManager;
|
||||||
|
import ca.uhn.fhir.cql.common.provider.LibraryContentProvider;
|
||||||
|
|
||||||
|
import org.cqframework.cql.cql2elm.CqlTranslatorOptions;
|
||||||
import org.cqframework.cql.cql2elm.ModelManager;
|
import org.cqframework.cql.cql2elm.ModelManager;
|
||||||
import org.cqframework.cql.cql2elm.model.Model;
|
import org.cqframework.cql.cql2elm.model.Model;
|
||||||
import org.cqframework.cql.elm.execution.Library;
|
import org.cqframework.cql.elm.execution.Library;
|
||||||
|
@ -36,11 +37,13 @@ import org.hl7.fhir.dstu3.model.Reference;
|
||||||
import org.hl7.fhir.dstu3.model.RelatedArtifact;
|
import org.hl7.fhir.dstu3.model.RelatedArtifact;
|
||||||
import org.hl7.fhir.dstu3.model.Resource;
|
import org.hl7.fhir.dstu3.model.Resource;
|
||||||
import org.opencds.cqf.cql.evaluator.cql2elm.model.CacheAwareModelManager;
|
import org.opencds.cqf.cql.evaluator.cql2elm.model.CacheAwareModelManager;
|
||||||
import org.opencds.cqf.cql.evaluator.engine.execution.PrivateCachingLibraryLoaderDecorator;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.opencds.cqf.cql.evaluator.engine.execution.CacheAwareLibraryLoaderDecorator;
|
||||||
|
import org.opencds.cqf.cql.evaluator.engine.execution.TranslatingLibraryLoader;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -48,22 +51,24 @@ public class LibraryHelper {
|
||||||
private static final Logger ourLog = LoggerFactory.getLogger(LibraryHelper.class);
|
private static final Logger ourLog = LoggerFactory.getLogger(LibraryHelper.class);
|
||||||
|
|
||||||
private final Map<org.hl7.elm.r1.VersionedIdentifier, Model> modelCache;
|
private final Map<org.hl7.elm.r1.VersionedIdentifier, Model> modelCache;
|
||||||
|
private Map<VersionedIdentifier, Library> libraryCache;
|
||||||
|
private CqlTranslatorOptions translatorOptions;
|
||||||
|
|
||||||
public LibraryHelper(Map<org.hl7.elm.r1.VersionedIdentifier, Model> modelCache) {
|
|
||||||
|
public LibraryHelper(Map<org.hl7.elm.r1.VersionedIdentifier, Model> modelCache, Map<VersionedIdentifier, Library> libraryCache, CqlTranslatorOptions translatorOptions) {
|
||||||
this.modelCache = modelCache;
|
this.modelCache = modelCache;
|
||||||
|
this.libraryCache = libraryCache;
|
||||||
|
this.translatorOptions = translatorOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public org.opencds.cqf.cql.engine.execution.LibraryLoader createLibraryLoader(
|
public org.opencds.cqf.cql.engine.execution.LibraryLoader createLibraryLoader(
|
||||||
LibraryResolutionProvider<org.hl7.fhir.dstu3.model.Library> provider) {
|
LibraryResolutionProvider<org.hl7.fhir.dstu3.model.Library> provider) {
|
||||||
ModelManager modelManager = new CacheAwareModelManager(this.modelCache);
|
ModelManager modelManager = new CacheAwareModelManager(this.modelCache);
|
||||||
LibraryManager libraryManager = new LibraryManager(modelManager);
|
|
||||||
libraryManager.getLibrarySourceLoader().clearProviders();
|
|
||||||
|
|
||||||
libraryManager.getLibrarySourceLoader().registerProvider(
|
List<org.opencds.cqf.cql.evaluator.cql2elm.content.LibraryContentProvider> contentProviders = Collections.singletonList(new LibraryContentProvider<org.hl7.fhir.dstu3.model.Library, Attachment>(
|
||||||
new LibrarySourceProvider<org.hl7.fhir.dstu3.model.Library, Attachment>(
|
provider, x -> x.getContent(), x -> x.getContentType(), x -> x.getData()));
|
||||||
provider, x -> x.getContent(), x -> x.getContentType(), x -> x.getData()));
|
|
||||||
|
|
||||||
return new PrivateCachingLibraryLoaderDecorator(new LibraryLoader(libraryManager, modelManager));
|
return new CacheAwareLibraryLoaderDecorator(new TranslatingLibraryLoader(modelManager, contentProviders, translatorOptions), libraryCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Library> loadLibraries(Measure measure,
|
public List<Library> loadLibraries(Measure measure,
|
||||||
|
@ -115,11 +120,13 @@ public class LibraryHelper {
|
||||||
if (artifact.hasType() && artifact.getType().equals(RelatedArtifact.RelatedArtifactType.DEPENDSON) && artifact.hasResource() && artifact.getResource().hasReference()) {
|
if (artifact.hasType() && artifact.getType().equals(RelatedArtifact.RelatedArtifactType.DEPENDSON) && artifact.hasResource() && artifact.getResource().hasReference()) {
|
||||||
if (artifact.getResource().getReferenceElement().getResourceType().equals("Library")) {
|
if (artifact.getResource().getReferenceElement().getResourceType().equals("Library")) {
|
||||||
org.hl7.fhir.dstu3.model.Library library = libraryResourceProvider.resolveLibraryById(artifact.getResource().getReferenceElement().getIdPart());
|
org.hl7.fhir.dstu3.model.Library library = libraryResourceProvider.resolveLibraryById(artifact.getResource().getReferenceElement().getIdPart());
|
||||||
|
if (library != null) {
|
||||||
if (library != null && isLogicLibrary(library)) {
|
if (isLogicLibrary(library)) {
|
||||||
libraries.add(
|
libraries.add(libraryLoader
|
||||||
libraryLoader.load(new VersionedIdentifier().withId(library.getName()).withVersion(library.getVersion()))
|
.load(new VersionedIdentifier().withId(library.getName()).withVersion(library.getVersion())));
|
||||||
);
|
} else {
|
||||||
|
ourLog.warn("Library {} not included as part of evaluation context. Only Libraries with the 'logic-library' type are included.", library.getId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
package ca.uhn.fhir.cql.dstu3.listener;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.cqframework.cql.elm.execution.Library;
|
||||||
|
import org.cqframework.cql.elm.execution.VersionedIdentifier;
|
||||||
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||||
|
import ca.uhn.fhir.jpa.cache.IResourceChangeEvent;
|
||||||
|
import ca.uhn.fhir.jpa.cache.IResourceChangeListener;
|
||||||
|
|
||||||
|
public class ElmCacheResourceChangeListener implements IResourceChangeListener {
|
||||||
|
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ElmCacheResourceChangeListener.class);
|
||||||
|
|
||||||
|
private IFhirResourceDao<org.hl7.fhir.dstu3.model.Library> libraryDao;
|
||||||
|
private Map<VersionedIdentifier, Library> globalLibraryCache;
|
||||||
|
|
||||||
|
public ElmCacheResourceChangeListener(IFhirResourceDao<org.hl7.fhir.dstu3.model.Library> libraryDao, Map<VersionedIdentifier, Library> globalLibraryCache) {
|
||||||
|
this.libraryDao = libraryDao;
|
||||||
|
this.globalLibraryCache = globalLibraryCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleInit(Collection<IIdType> theResourceIds) {
|
||||||
|
// Intentionally empty. Only cache ELM on eval request
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleChange(IResourceChangeEvent theResourceChangeEvent) {
|
||||||
|
if (theResourceChangeEvent == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.invalidateCacheByIds(theResourceChangeEvent.getDeletedResourceIds());
|
||||||
|
this.invalidateCacheByIds(theResourceChangeEvent.getUpdatedResourceIds());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void invalidateCacheByIds(List<IIdType> theIds) {
|
||||||
|
if (theIds == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (IIdType id : theIds) {
|
||||||
|
this.invalidateCacheById(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void invalidateCacheById(IIdType theId) {
|
||||||
|
if (!theId.getResourceType().equals("Library")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
org.hl7.fhir.dstu3.model.Library library = this.libraryDao.read(theId);
|
||||||
|
|
||||||
|
this.globalLibraryCache.remove(new VersionedIdentifier().withId(library.getName()).withVersion(library.getVersion()));
|
||||||
|
}
|
||||||
|
// This happens when a Library is deleted entirely so it's impossible to look up name and version.
|
||||||
|
catch (Exception e) {
|
||||||
|
// TODO: This needs to be smarter... the issue is that ELM is cached with library name and version as the key since
|
||||||
|
// that's the access path the CQL engine uses, but change notifications occur with the resource Id, which is not
|
||||||
|
// necessarily tied to the resource name. In any event, if a unknown resource is deleted, clear all libraries as a workaround.
|
||||||
|
// One option is to maintain a cache with multiple indices.
|
||||||
|
ourLog.debug("Failed to locate resource {} to look up name and version. Clearing all libraries from cache.", theId.getValueAsString());
|
||||||
|
this.globalLibraryCache.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,8 +40,6 @@ import org.hl7.fhir.dstu3.model.MeasureReport;
|
||||||
import org.hl7.fhir.dstu3.model.StringType;
|
import org.hl7.fhir.dstu3.model.StringType;
|
||||||
import org.hl7.fhir.exceptions.FHIRException;
|
import org.hl7.fhir.exceptions.FHIRException;
|
||||||
import org.opencds.cqf.cql.engine.execution.LibraryLoader;
|
import org.opencds.cqf.cql.engine.execution.LibraryLoader;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@ -54,8 +52,6 @@ import org.springframework.stereotype.Component;
|
||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
public class MeasureOperationsProvider {
|
public class MeasureOperationsProvider {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(MeasureOperationsProvider.class);
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private LibraryResolutionProvider<Library> libraryResolutionProvider;
|
private LibraryResolutionProvider<Library> libraryResolutionProvider;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -20,10 +20,11 @@ package ca.uhn.fhir.cql.r4.helper;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import ca.uhn.fhir.cql.common.evaluation.LibraryLoader;
|
|
||||||
import ca.uhn.fhir.cql.common.provider.LibraryResolutionProvider;
|
import ca.uhn.fhir.cql.common.provider.LibraryResolutionProvider;
|
||||||
import ca.uhn.fhir.cql.common.provider.LibrarySourceProvider;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import ca.uhn.fhir.cql.common.provider.LibraryContentProvider;
|
||||||
|
|
||||||
|
import org.cqframework.cql.cql2elm.CqlTranslatorOptions;
|
||||||
import org.cqframework.cql.cql2elm.LibraryManager;
|
import org.cqframework.cql.cql2elm.LibraryManager;
|
||||||
import org.cqframework.cql.cql2elm.ModelManager;
|
import org.cqframework.cql.cql2elm.ModelManager;
|
||||||
import org.cqframework.cql.cql2elm.model.Model;
|
import org.cqframework.cql.cql2elm.model.Model;
|
||||||
|
@ -33,14 +34,16 @@ import org.hl7.fhir.r4.model.Attachment;
|
||||||
import org.hl7.fhir.r4.model.CanonicalType;
|
import org.hl7.fhir.r4.model.CanonicalType;
|
||||||
import org.hl7.fhir.r4.model.Coding;
|
import org.hl7.fhir.r4.model.Coding;
|
||||||
import org.hl7.fhir.r4.model.Measure;
|
import org.hl7.fhir.r4.model.Measure;
|
||||||
|
import org.hl7.fhir.r4.model.PlanDefinition;
|
||||||
import org.hl7.fhir.r4.model.RelatedArtifact;
|
import org.hl7.fhir.r4.model.RelatedArtifact;
|
||||||
import org.hl7.fhir.r4.model.Resource;
|
import org.hl7.fhir.r4.model.Resource;
|
||||||
import org.opencds.cqf.cql.evaluator.cql2elm.model.CacheAwareModelManager;
|
import org.opencds.cqf.cql.evaluator.cql2elm.model.CacheAwareModelManager;
|
||||||
import org.opencds.cqf.cql.evaluator.engine.execution.PrivateCachingLibraryLoaderDecorator;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.opencds.cqf.cql.evaluator.engine.execution.CacheAwareLibraryLoaderDecorator;
|
||||||
|
import org.opencds.cqf.cql.evaluator.engine.execution.TranslatingLibraryLoader;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -51,34 +54,43 @@ public class LibraryHelper {
|
||||||
private static final Logger ourLog = LoggerFactory.getLogger(LibraryHelper.class);
|
private static final Logger ourLog = LoggerFactory.getLogger(LibraryHelper.class);
|
||||||
|
|
||||||
private final Map<org.hl7.elm.r1.VersionedIdentifier, Model> modelCache;
|
private final Map<org.hl7.elm.r1.VersionedIdentifier, Model> modelCache;
|
||||||
|
private Map<VersionedIdentifier, Library> libraryCache;
|
||||||
|
private CqlTranslatorOptions translatorOptions;
|
||||||
|
|
||||||
public LibraryHelper(Map<org.hl7.elm.r1.VersionedIdentifier, Model> modelCache) {
|
public LibraryHelper(Map<org.hl7.elm.r1.VersionedIdentifier, Model> modelCache,
|
||||||
|
Map<VersionedIdentifier, Library> libraryCache, CqlTranslatorOptions translatorOptions) {
|
||||||
this.modelCache = modelCache;
|
this.modelCache = modelCache;
|
||||||
|
this.libraryCache = libraryCache;
|
||||||
|
this.translatorOptions = translatorOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public org.opencds.cqf.cql.engine.execution.LibraryLoader createLibraryLoader(LibraryResolutionProvider<org.hl7.fhir.r4.model.Library> provider) {
|
public org.opencds.cqf.cql.engine.execution.LibraryLoader createLibraryLoader(
|
||||||
|
LibraryResolutionProvider<org.hl7.fhir.r4.model.Library> provider) {
|
||||||
ModelManager modelManager = new CacheAwareModelManager(this.modelCache);
|
ModelManager modelManager = new CacheAwareModelManager(this.modelCache);
|
||||||
LibraryManager libraryManager = new LibraryManager(modelManager);
|
LibraryManager libraryManager = new LibraryManager(modelManager);
|
||||||
libraryManager.getLibrarySourceLoader().clearProviders();
|
libraryManager.getLibrarySourceLoader().clearProviders();
|
||||||
|
List<org.opencds.cqf.cql.evaluator.cql2elm.content.LibraryContentProvider> contentProviders = Collections
|
||||||
|
.singletonList(new LibraryContentProvider<org.hl7.fhir.r4.model.Library, Attachment>(provider,
|
||||||
|
x -> x.getContent(), x -> x.getContentType(), x -> x.getData()));
|
||||||
|
|
||||||
libraryManager.getLibrarySourceLoader().registerProvider(
|
return new CacheAwareLibraryLoaderDecorator(
|
||||||
new LibrarySourceProvider<org.hl7.fhir.r4.model.Library, Attachment>(provider,
|
new TranslatingLibraryLoader(modelManager, contentProviders, translatorOptions), libraryCache);
|
||||||
x -> x.getContent(), x -> x.getContentType(), x -> x.getData()));
|
|
||||||
|
|
||||||
return new PrivateCachingLibraryLoaderDecorator(new LibraryLoader(libraryManager, modelManager));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public org.opencds.cqf.cql.engine.execution.LibraryLoader createLibraryLoader(org.cqframework.cql.cql2elm.LibrarySourceProvider provider) {
|
public org.opencds.cqf.cql.engine.execution.LibraryLoader createLibraryLoader(
|
||||||
|
org.cqframework.cql.cql2elm.LibrarySourceProvider provider) {
|
||||||
ModelManager modelManager = new CacheAwareModelManager(this.modelCache);
|
ModelManager modelManager = new CacheAwareModelManager(this.modelCache);
|
||||||
LibraryManager libraryManager = new LibraryManager(modelManager);
|
LibraryManager libraryManager = new LibraryManager(modelManager);
|
||||||
libraryManager.getLibrarySourceLoader().clearProviders();
|
libraryManager.getLibrarySourceLoader().clearProviders();
|
||||||
|
|
||||||
libraryManager.getLibrarySourceLoader().registerProvider(provider);
|
libraryManager.getLibrarySourceLoader().registerProvider(provider);
|
||||||
|
|
||||||
return new PrivateCachingLibraryLoaderDecorator(new LibraryLoader(libraryManager, modelManager));
|
return new CacheAwareLibraryLoaderDecorator(new TranslatingLibraryLoader(modelManager, null, translatorOptions),
|
||||||
|
libraryCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
public org.hl7.fhir.r4.model.Library resolveLibraryReference(LibraryResolutionProvider<org.hl7.fhir.r4.model.Library> libraryResourceProvider, String reference) {
|
public org.hl7.fhir.r4.model.Library resolveLibraryReference(
|
||||||
|
LibraryResolutionProvider<org.hl7.fhir.r4.model.Library> libraryResourceProvider, String reference) {
|
||||||
// Raw references to Library/libraryId or libraryId
|
// Raw references to Library/libraryId or libraryId
|
||||||
if (reference.startsWith("Library/") || !reference.contains("/")) {
|
if (reference.startsWith("Library/") || !reference.contains("/")) {
|
||||||
return libraryResourceProvider.resolveLibraryById(reference.replace("Library/", ""));
|
return libraryResourceProvider.resolveLibraryById(reference.replace("Library/", ""));
|
||||||
|
@ -92,30 +104,21 @@ public class LibraryHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<org.cqframework.cql.elm.execution.Library> loadLibraries(Measure measure,
|
public List<org.cqframework.cql.elm.execution.Library> loadLibraries(Measure measure,
|
||||||
org.opencds.cqf.cql.engine.execution.LibraryLoader libraryLoader,
|
org.opencds.cqf.cql.engine.execution.LibraryLoader libraryLoader,
|
||||||
LibraryResolutionProvider<org.hl7.fhir.r4.model.Library> libraryResourceProvider) {
|
LibraryResolutionProvider<org.hl7.fhir.r4.model.Library> libraryResourceProvider) {
|
||||||
List<org.cqframework.cql.elm.execution.Library> libraries = new ArrayList<org.cqframework.cql.elm.execution.Library>();
|
List<org.cqframework.cql.elm.execution.Library> libraries = new ArrayList<org.cqframework.cql.elm.execution.Library>();
|
||||||
|
|
||||||
List<String> messages = new ArrayList<>();
|
|
||||||
|
|
||||||
// load libraries
|
// load libraries
|
||||||
//TODO: if there's a bad measure argument, this blows up for an obscure error
|
// TODO: if there's a bad measure argument, this blows up for an obscure error
|
||||||
org.hl7.fhir.r4.model.Library primaryLibrary = null;
|
org.hl7.fhir.r4.model.Library primaryLibrary = null;
|
||||||
|
for (CanonicalType ref : measure.getLibrary()) {
|
||||||
List<CanonicalType> measureLibraries = measure.getLibrary();
|
|
||||||
if (measureLibraries.isEmpty()) {
|
|
||||||
String message = "No libraries found on " + measure.getId() + ". Did you perhaps load a DSTU3 Measure onto an R4 server?";
|
|
||||||
messages.add(message);
|
|
||||||
ourLog.warn(message);
|
|
||||||
}
|
|
||||||
for (CanonicalType ref : measureLibraries) {
|
|
||||||
// if library is contained in measure, load it into server
|
// if library is contained in measure, load it into server
|
||||||
String id = ref.getValue(); //CanonicalHelper.getId(ref);
|
String id = ref.getValue(); // CanonicalHelper.getId(ref);
|
||||||
if (id.startsWith("#")) {
|
if (id.startsWith("#")) {
|
||||||
id = id.substring(1);
|
id = id.substring(1);
|
||||||
for (Resource resource : measure.getContained()) {
|
for (Resource resource : measure.getContained()) {
|
||||||
if (resource instanceof org.hl7.fhir.r4.model.Library
|
if (resource instanceof org.hl7.fhir.r4.model.Library
|
||||||
&& resource.getIdElement().getIdPart().equals(id)) {
|
&& resource.getIdElement().getIdPart().equals(id)) {
|
||||||
libraryResourceProvider.update((org.hl7.fhir.r4.model.Library) resource);
|
libraryResourceProvider.update((org.hl7.fhir.r4.model.Library) resource);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -127,34 +130,30 @@ public class LibraryHelper {
|
||||||
primaryLibrary = library;
|
primaryLibrary = library;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (library != null && isLogicLibrary(library)) {
|
||||||
if (library != null) {
|
libraries.add(libraryLoader
|
||||||
if (isLogicLibrary(library)) {
|
.load(new VersionedIdentifier().withId(library.getName()).withVersion(library.getVersion())));
|
||||||
libraries.add(
|
|
||||||
libraryLoader.load(new VersionedIdentifier().withId(library.getName()).withVersion(library.getVersion()))
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
String message = "Skipping library " + library.getId() + " is not a logic library. Probably missing type.coding.system=\"http://terminology.hl7.org/CodeSystem/library-type\"";
|
|
||||||
messages.add(message);
|
|
||||||
ourLog.warn(message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (libraries.isEmpty()) {
|
if (libraries.isEmpty()) {
|
||||||
throw new IllegalArgumentException(String
|
throw new IllegalArgumentException(
|
||||||
.format("Could not load library source for libraries referenced in %s:\n%s", measure.getId(), StringUtils.join("\n", messages)));
|
String.format("Could not load library source for libraries referenced in %s.", measure.getId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (RelatedArtifact artifact : primaryLibrary.getRelatedArtifact()) {
|
for (RelatedArtifact artifact : primaryLibrary.getRelatedArtifact()) {
|
||||||
if (artifact.hasType() && artifact.getType().equals(RelatedArtifact.RelatedArtifactType.DEPENDSON) && artifact.hasResource()) {
|
if (artifact.hasType() && artifact.getType().equals(RelatedArtifact.RelatedArtifactType.DEPENDSON)
|
||||||
|
&& artifact.hasResource()) {
|
||||||
org.hl7.fhir.r4.model.Library library = null;
|
org.hl7.fhir.r4.model.Library library = null;
|
||||||
library = resolveLibraryReference(libraryResourceProvider, artifact.getResource());
|
library = resolveLibraryReference(libraryResourceProvider, artifact.getResource());
|
||||||
|
|
||||||
if (library != null && isLogicLibrary(library)) {
|
if (library != null) {
|
||||||
libraries.add(
|
if (isLogicLibrary(library)) {
|
||||||
libraryLoader.load(new VersionedIdentifier().withId(library.getName()).withVersion(library.getVersion()))
|
libraries.add(libraryLoader
|
||||||
);
|
.load(new VersionedIdentifier().withId(library.getName()).withVersion(library.getVersion())));
|
||||||
|
} else {
|
||||||
|
ourLog.warn("Library {} not included as part of evaluation context. Only Libraries with the 'logic-library' type are included.", library.getId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -168,12 +167,13 @@ public class LibraryHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!library.hasType()) {
|
if (!library.hasType()) {
|
||||||
// If no type is specified, assume it is a logic library based on whether there is a CQL content element.
|
// If no type is specified, assume it is a logic library based on whether there
|
||||||
|
// is a CQL content element.
|
||||||
if (library.hasContent()) {
|
if (library.hasContent()) {
|
||||||
for (Attachment a : library.getContent()) {
|
for (Attachment a : library.getContent()) {
|
||||||
if (a.hasContentType() && (a.getContentType().equals("text/cql")
|
if (a.hasContentType()
|
||||||
|| a.getContentType().equals("application/elm+xml")
|
&& (a.getContentType().equals("text/cql") || a.getContentType().equals("application/elm+xml")
|
||||||
|| a.getContentType().equals("application/elm+json"))) {
|
|| a.getContentType().equals("application/elm+json"))) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -186,8 +186,8 @@ public class LibraryHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Coding c : library.getType().getCoding()) {
|
for (Coding c : library.getType().getCoding()) {
|
||||||
if (c.hasSystem() && c.getSystem().equals("http://terminology.hl7.org/CodeSystem/library-type")
|
if (c.hasSystem() && c.getSystem().equals("http://terminology.hl7.org/CodeSystem/library-type") && c.hasCode()
|
||||||
&& c.hasCode() && c.getCode().equals("logic-library")) {
|
&& c.getCode().equals("logic-library")) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -195,27 +195,40 @@ public class LibraryHelper {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Library resolveLibraryById(String libraryId,
|
public Library resolveLibraryById(String libraryId, org.opencds.cqf.cql.engine.execution.LibraryLoader libraryLoader,
|
||||||
org.opencds.cqf.cql.engine.execution.LibraryLoader libraryLoader,
|
LibraryResolutionProvider<org.hl7.fhir.r4.model.Library> libraryResourceProvider) {
|
||||||
LibraryResolutionProvider<org.hl7.fhir.r4.model.Library> libraryResourceProvider) {
|
|
||||||
// Library library = null;
|
|
||||||
|
|
||||||
org.hl7.fhir.r4.model.Library fhirLibrary = libraryResourceProvider.resolveLibraryById(libraryId);
|
org.hl7.fhir.r4.model.Library fhirLibrary = libraryResourceProvider.resolveLibraryById(libraryId);
|
||||||
return libraryLoader
|
return libraryLoader
|
||||||
.load(new VersionedIdentifier().withId(fhirLibrary.getName()).withVersion(fhirLibrary.getVersion()));
|
.load(new VersionedIdentifier().withId(fhirLibrary.getName()).withVersion(fhirLibrary.getVersion()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Library resolvePrimaryLibrary(Measure measure,
|
public Library resolvePrimaryLibrary(Measure measure,
|
||||||
org.opencds.cqf.cql.engine.execution.LibraryLoader libraryLoader,
|
org.opencds.cqf.cql.engine.execution.LibraryLoader libraryLoader,
|
||||||
LibraryResolutionProvider<org.hl7.fhir.r4.model.Library> libraryResourceProvider) {
|
LibraryResolutionProvider<org.hl7.fhir.r4.model.Library> libraryResourceProvider) {
|
||||||
// default is the first library reference
|
// default is the first library reference
|
||||||
String id = CanonicalHelper.getId(measure.getLibrary().get(0));
|
String id = CanonicalHelper.getId(measure.getLibrary().get(0));
|
||||||
|
|
||||||
Library library = resolveLibraryById(id, libraryLoader, libraryResourceProvider);
|
Library library = resolveLibraryById(id, libraryLoader, libraryResourceProvider);
|
||||||
|
|
||||||
if (library == null) {
|
if (library == null) {
|
||||||
throw new IllegalArgumentException(String.format("Could not resolve primary library for Measure/%s.",
|
throw new IllegalArgumentException(
|
||||||
measure.getIdElement().getIdPart()));
|
String.format("Could not resolve primary library for Measure/%s.", measure.getIdElement().getIdPart()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return library;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Library resolvePrimaryLibrary(PlanDefinition planDefinition,
|
||||||
|
org.opencds.cqf.cql.engine.execution.LibraryLoader libraryLoader,
|
||||||
|
LibraryResolutionProvider<org.hl7.fhir.r4.model.Library> libraryResourceProvider) {
|
||||||
|
String id = CanonicalHelper.getId(planDefinition.getLibrary().get(0));
|
||||||
|
|
||||||
|
Library library = resolveLibraryById(id, libraryLoader, libraryResourceProvider);
|
||||||
|
|
||||||
|
if (library == null) {
|
||||||
|
throw new IllegalArgumentException(String.format("Could not resolve primary library for PlanDefinition/%s",
|
||||||
|
planDefinition.getIdElement().getIdPart()));
|
||||||
}
|
}
|
||||||
|
|
||||||
return library;
|
return library;
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
package ca.uhn.fhir.cql.r4.listener;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.cqframework.cql.elm.execution.Library;
|
||||||
|
import org.cqframework.cql.elm.execution.VersionedIdentifier;
|
||||||
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||||
|
import ca.uhn.fhir.jpa.cache.IResourceChangeEvent;
|
||||||
|
import ca.uhn.fhir.jpa.cache.IResourceChangeListener;
|
||||||
|
|
||||||
|
public class ElmCacheResourceChangeListener implements IResourceChangeListener {
|
||||||
|
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ElmCacheResourceChangeListener.class);
|
||||||
|
|
||||||
|
private IFhirResourceDao<org.hl7.fhir.r4.model.Library> libraryDao;
|
||||||
|
private Map<VersionedIdentifier, Library> globalLibraryCache;
|
||||||
|
|
||||||
|
public ElmCacheResourceChangeListener(IFhirResourceDao<org.hl7.fhir.r4.model.Library> libraryDao, Map<VersionedIdentifier, Library> globalLibraryCache) {
|
||||||
|
this.libraryDao = libraryDao;
|
||||||
|
this.globalLibraryCache = globalLibraryCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleInit(Collection<IIdType> theResourceIds) {
|
||||||
|
// Intentionally empty. Only cache ELM on eval request
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleChange(IResourceChangeEvent theResourceChangeEvent) {
|
||||||
|
if (theResourceChangeEvent == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.invalidateCacheByIds(theResourceChangeEvent.getDeletedResourceIds());
|
||||||
|
this.invalidateCacheByIds(theResourceChangeEvent.getUpdatedResourceIds());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void invalidateCacheByIds(List<IIdType> theIds) {
|
||||||
|
if (theIds == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (IIdType id : theIds) {
|
||||||
|
this.invalidateCacheById(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void invalidateCacheById(IIdType theId) {
|
||||||
|
if (!theId.getResourceType().equals("Library")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
org.hl7.fhir.r4.model.Library library = this.libraryDao.read(theId);
|
||||||
|
|
||||||
|
this.globalLibraryCache.remove(new VersionedIdentifier().withId(library.getName()).withVersion(library.getVersion()));
|
||||||
|
}
|
||||||
|
// This happens when a Library is deleted entirely so it's impossible to look up name and version.
|
||||||
|
catch (Exception e) {
|
||||||
|
// TODO: This needs to be smarter... the issue is that ELM is cached with library name and version as the key since
|
||||||
|
// that's the access path the CQL engine uses, but change notifications occur with the resource Id, which is not
|
||||||
|
// necessarily tied to the resource name. In any event, if a unknown resource is deleted, clear all libraries as a workaround.
|
||||||
|
// One option is to maintain a cache with multiple indices.
|
||||||
|
ourLog.debug("Failed to locate resource {} to look up name and version. Clearing all libraries from cache.", theId.getValueAsString());
|
||||||
|
this.globalLibraryCache.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,15 +1,23 @@
|
||||||
package ca.uhn.fhir.cql.config;
|
package ca.uhn.fhir.cql.config;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.subscription.channel.config.SubscriptionChannelConfig;
|
import ca.uhn.fhir.jpa.subscription.channel.config.SubscriptionChannelConfig;
|
||||||
import ca.uhn.fhir.jpa.subscription.submit.config.SubscriptionSubmitterConfig;
|
import ca.uhn.fhir.jpa.subscription.submit.config.SubscriptionSubmitterConfig;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Primary;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@Import({SubscriptionSubmitterConfig.class, SubscriptionChannelConfig.class})
|
@Import({SubscriptionSubmitterConfig.class, SubscriptionChannelConfig.class})
|
||||||
public class TestCqlConfig {
|
public class TestCqlConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public DaoConfig daoConfig() {
|
||||||
|
DaoConfig daoConfig = new DaoConfig();
|
||||||
|
daoConfig.setAllowExternalReferences(true);
|
||||||
|
daoConfig.setEnforceReferentialIntegrityOnWrite(false);
|
||||||
|
daoConfig.setEnforceReferenceTargetTypes(false);
|
||||||
|
|
||||||
|
return daoConfig;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,7 +75,7 @@ public class CqlMeasureEvaluationDstu3Test extends BaseCqlDstu3Test {
|
||||||
assertNotNull("expected MeasureReport can not be null", expected);
|
assertNotNull("expected MeasureReport can not be null", expected);
|
||||||
assertNotNull("actual MeasureReport can not be null", actual);
|
assertNotNull("actual MeasureReport can not be null", actual);
|
||||||
|
|
||||||
String errorLocator = String.format("Measure: %s, Subject: %s", expected.getMeasure(),
|
String errorLocator = String.format("Measure: %s, Subject: %s", expected.getMeasure().getReference(),
|
||||||
expected.getPatient().getReference());
|
expected.getPatient().getReference());
|
||||||
|
|
||||||
assertEquals(expected.hasGroup(), actual.hasGroup(), errorLocator);
|
assertEquals(expected.hasGroup(), actual.hasGroup(), errorLocator);
|
||||||
|
@ -83,10 +83,10 @@ public class CqlMeasureEvaluationDstu3Test extends BaseCqlDstu3Test {
|
||||||
|
|
||||||
for (MeasureReportGroupComponent mrgcExpected : expected.getGroup()) {
|
for (MeasureReportGroupComponent mrgcExpected : expected.getGroup()) {
|
||||||
Optional<MeasureReportGroupComponent> mrgcActualOptional = actual.getGroup().stream()
|
Optional<MeasureReportGroupComponent> mrgcActualOptional = actual.getGroup().stream()
|
||||||
.filter(x -> x.getId().equals(mrgcExpected.getId())).findFirst();
|
.filter(x -> x.getIdentifier() != null && x.getIdentifier().getValue().equals(mrgcExpected.getIdentifier().getValue())).findFirst();
|
||||||
|
|
||||||
errorLocator = String.format("Measure: %s, Subject: %s, Group: %s", expected.getMeasure(),
|
errorLocator = String.format("Measure: %s, Subject: %s, Group: %s", expected.getMeasure().getReference(),
|
||||||
expected.getPatient().getReference(), mrgcExpected.getId());
|
expected.getPatient().getReference(), mrgcExpected.getIdentifier().getValue());
|
||||||
assertTrue(errorLocator, mrgcActualOptional.isPresent());
|
assertTrue(errorLocator, mrgcActualOptional.isPresent());
|
||||||
|
|
||||||
MeasureReportGroupComponent mrgcActual = mrgcActualOptional.get();
|
MeasureReportGroupComponent mrgcActual = mrgcActualOptional.get();
|
||||||
|
@ -94,7 +94,7 @@ public class CqlMeasureEvaluationDstu3Test extends BaseCqlDstu3Test {
|
||||||
if (mrgcExpected.getMeasureScore() == null) {
|
if (mrgcExpected.getMeasureScore() == null) {
|
||||||
assertNull(mrgcActual.getMeasureScore(), errorLocator);
|
assertNull(mrgcActual.getMeasureScore(), errorLocator);
|
||||||
} else {
|
} else {
|
||||||
assertNotNull(mrgcActual.getMeasureScore());
|
assertNotNull(errorLocator, mrgcActual.getMeasureScore());
|
||||||
BigDecimal decimalExpected = mrgcExpected.getMeasureScore();
|
BigDecimal decimalExpected = mrgcExpected.getMeasureScore();
|
||||||
BigDecimal decimalActual = mrgcActual.getMeasureScore();
|
BigDecimal decimalActual = mrgcActual.getMeasureScore();
|
||||||
|
|
||||||
|
@ -135,10 +135,13 @@ public class CqlMeasureEvaluationDstu3Test extends BaseCqlDstu3Test {
|
||||||
return new DateTimeType(date).getValueAsString();
|
return new DateTimeType(date).getValueAsString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// As of 2/11/2021, all the DSTU3 bundles in the Connectathon IG are out of date
|
@Test
|
||||||
// and can't be posted
|
public void test_EXM124_FHIR3_72000() throws IOException {
|
||||||
// @Test
|
this.testMeasureBundle("dstu3/connectathon/EXM124-FHIR3-7.2.000-bundle.json");
|
||||||
// public void test_EXM117_83000() throws IOException {
|
}
|
||||||
// this.testMeasureBundle("dstu3/connectathon/EXM117_FHIR3-8.3.000-bundle.json");
|
|
||||||
// }
|
@Test
|
||||||
|
public void test_EXM104_FHIR3_81000() throws IOException {
|
||||||
|
this.testMeasureBundle("dstu3/connectathon/EXM104-FHIR3-8.1.000-bundle.json");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,7 +83,7 @@ public class CqlMeasureEvaluationR4Test extends BaseCqlR4Test {
|
||||||
|
|
||||||
for (MeasureReportGroupComponent mrgcExpected : expected.getGroup()) {
|
for (MeasureReportGroupComponent mrgcExpected : expected.getGroup()) {
|
||||||
Optional<MeasureReportGroupComponent> mrgcActualOptional = actual.getGroup().stream()
|
Optional<MeasureReportGroupComponent> mrgcActualOptional = actual.getGroup().stream()
|
||||||
.filter(x -> x.getId().equals(mrgcExpected.getId())).findFirst();
|
.filter(x -> x.getId() != null && x.getId().equals(mrgcExpected.getId())).findFirst();
|
||||||
|
|
||||||
errorLocator = String.format("Measure: %s, Subject: %s, Group: %s", expected.getMeasure(),
|
errorLocator = String.format("Measure: %s, Subject: %s, Group: %s", expected.getMeasure(),
|
||||||
expected.getSubject().getReference(), mrgcExpected.getId());
|
expected.getSubject().getReference(), mrgcExpected.getId());
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -63,6 +63,10 @@
|
||||||
<groupId>junit</groupId>
|
<groupId>junit</groupId>
|
||||||
<artifactId>junit</artifactId>
|
<artifactId>junit</artifactId>
|
||||||
</exclusion>
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-collections4</artifactId>
|
||||||
|
</exclusion>
|
||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|
|
@ -596,7 +596,7 @@ public class FhirInstanceValidatorDstu3Test {
|
||||||
ValidationResult output = myVal.validateWithResult(input);
|
ValidationResult output = myVal.validateWithResult(input);
|
||||||
List<SingleValidationMessage> issues = logResultsAndReturnNonInformationalOnes(output);
|
List<SingleValidationMessage> issues = logResultsAndReturnNonInformationalOnes(output);
|
||||||
|
|
||||||
assertThat(issues.toString(), containsString("None of the codes provided are in the value set http://phr.kanta.fi/ValueSet/fiphr-vs-medicationcontext"));
|
assertThat(issues.toString(), containsString("None of the codings provided are in the value set http://phr.kanta.fi/ValueSet/fiphr-vs-medicationcontext"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -1278,7 +1278,7 @@ public class FhirInstanceValidatorDstu3Test {
|
||||||
assertEquals(1, all.size());
|
assertEquals(1, all.size());
|
||||||
assertEquals("Patient.identifier[0].type", all.get(0).getLocationString());
|
assertEquals("Patient.identifier[0].type", all.get(0).getLocationString());
|
||||||
assertEquals(
|
assertEquals(
|
||||||
"None of the codes provided are in the value set http://hl7.org/fhir/ValueSet/identifier-type (http://hl7.org/fhir/ValueSet/identifier-type), and a code should come from this value set unless it has no suitable code and the validator cannot judge what is suitable) (codes = http://example.com/foo/bar#bar)",
|
"None of the codings provided are in the value set http://hl7.org/fhir/ValueSet/identifier-type (http://hl7.org/fhir/ValueSet/identifier-type), and a coding should come from this value set unless it has no suitable code (note that the validator cannot judge what is suitable) (codes = http://example.com/foo/bar#bar)",
|
||||||
all.get(0).getMessage());
|
all.get(0).getMessage());
|
||||||
assertEquals(ResultSeverityEnum.WARNING, all.get(0).getSeverity());
|
assertEquals(ResultSeverityEnum.WARNING, all.get(0).getSeverity());
|
||||||
|
|
||||||
|
|
|
@ -118,7 +118,7 @@ public class QuestionnaireValidatorDstu3Test {
|
||||||
ValidationResult errors = myVal.validateWithResult(q);
|
ValidationResult errors = myVal.validateWithResult(q);
|
||||||
ourLog.info(errors.toString());
|
ourLog.info(errors.toString());
|
||||||
assertThat(errors.isSuccessful(), Matchers.is(true));
|
assertThat(errors.isSuccessful(), Matchers.is(true));
|
||||||
assertThat(errors.getMessages().get(0).getMessage(), containsString("and a code should come from this value set unless it has no suitable code and the validator cannot judge what is suitable) (codes = null#text-box)"));
|
assertThat(errors.getMessages().get(0).getMessage(), containsString("and a coding should come from this value set unless it has no suitable code (note that the validator cannot judge what is suitable) (codes = null#text-box)"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -165,7 +165,7 @@ public class ResourceValidatorDstu3Test {
|
||||||
ValidationResult output = val.validateWithResult(p);
|
ValidationResult output = val.validateWithResult(p);
|
||||||
List<SingleValidationMessage> all = logResultsAndReturnNonInformationalOnes(output);
|
List<SingleValidationMessage> all = logResultsAndReturnNonInformationalOnes(output);
|
||||||
|
|
||||||
assertEquals("None of the codes provided are in the value set http://hl7.org/fhir/ValueSet/marital-status (http://hl7.org/fhir/ValueSet/marital-status), and a code should come from this value set unless it has no suitable code and the validator cannot judge what is suitable) (codes = http://hl7.org/fhir/v3/MaritalStatus#FOO)", output.getMessages().get(0).getMessage());
|
assertEquals("None of the codings provided are in the value set http://hl7.org/fhir/ValueSet/marital-status (http://hl7.org/fhir/ValueSet/marital-status), and a coding should come from this value set unless it has no suitable code (note that the validator cannot judge what is suitable) (codes = http://hl7.org/fhir/v3/MaritalStatus#FOO)", output.getMessages().get(0).getMessage());
|
||||||
assertEquals(ResultSeverityEnum.WARNING, output.getMessages().get(0).getSeverity());
|
assertEquals(ResultSeverityEnum.WARNING, output.getMessages().get(0).getSeverity());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -628,7 +628,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
|
||||||
ValidationResult output = myVal.validateWithResult(input);
|
ValidationResult output = myVal.validateWithResult(input);
|
||||||
List<SingleValidationMessage> errors = logResultsAndReturnAll(output);
|
List<SingleValidationMessage> errors = logResultsAndReturnAll(output);
|
||||||
assertEquals(1, errors.size());
|
assertEquals(1, errors.size());
|
||||||
assertEquals("None of the codes provided are in the value set http://hl7.org/fhir/ValueSet/report-codes (http://hl7.org/fhir/ValueSet/report-codes), and a code is recommended to come from this value set) (codes = http://loinc.org#1-8)", errors.get(0).getMessage());
|
assertEquals("None of the codings provided are in the value set http://hl7.org/fhir/ValueSet/report-codes (http://hl7.org/fhir/ValueSet/report-codes), and a coding is recommended to come from this value set) (codes = http://loinc.org#1-8)", errors.get(0).getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -1333,7 +1333,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
|
||||||
List<SingleValidationMessage> all = logResultsAndReturnAll(output);
|
List<SingleValidationMessage> all = logResultsAndReturnAll(output);
|
||||||
assertEquals(1, all.size());
|
assertEquals(1, all.size());
|
||||||
assertEquals("Patient.identifier[0].type", all.get(0).getLocationString());
|
assertEquals("Patient.identifier[0].type", all.get(0).getLocationString());
|
||||||
assertThat(all.get(0).getMessage(), containsString("None of the codes provided are in the value set http://hl7.org/fhir/ValueSet/identifier-type"));
|
assertThat(all.get(0).getMessage(), containsString("None of the codings provided are in the value set http://hl7.org/fhir/ValueSet/identifier-type"));
|
||||||
assertEquals(ResultSeverityEnum.WARNING, all.get(0).getSeverity());
|
assertEquals(ResultSeverityEnum.WARNING, all.get(0).getSeverity());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -945,7 +945,7 @@ public class FhirInstanceValidatorR5Test {
|
||||||
assertEquals(1, all.size());
|
assertEquals(1, all.size());
|
||||||
assertEquals("Patient.identifier[0].type", all.get(0).getLocationString());
|
assertEquals("Patient.identifier[0].type", all.get(0).getLocationString());
|
||||||
assertEquals(
|
assertEquals(
|
||||||
"None of the codes provided are in the value set http://hl7.org/fhir/ValueSet/identifier-type (http://hl7.org/fhir/ValueSet/identifier-type), and a code should come from this value set unless it has no suitable code and the validator cannot judge what is suitable) (codes = http://example.com/foo/bar#bar)",
|
"None of the codings provided are in the value set http://hl7.org/fhir/ValueSet/identifier-type (http://hl7.org/fhir/ValueSet/identifier-type), and a coding should come from this value set unless it has no suitable code (note that the validator cannot judge what is suitable) (codes = http://example.com/foo/bar#bar)",
|
||||||
all.get(0).getMessage());
|
all.get(0).getMessage());
|
||||||
assertEquals(ResultSeverityEnum.WARNING, all.get(0).getSeverity());
|
assertEquals(ResultSeverityEnum.WARNING, all.get(0).getSeverity());
|
||||||
|
|
||||||
|
|
|
@ -142,7 +142,7 @@ public class ResourceValidatorDstu3FeatureTest {
|
||||||
ValidationResult output = val.validateWithResult(p);
|
ValidationResult output = val.validateWithResult(p);
|
||||||
List<SingleValidationMessage> all = logResultsAndReturnNonInformationalOnes(output);
|
List<SingleValidationMessage> all = logResultsAndReturnNonInformationalOnes(output);
|
||||||
|
|
||||||
assertEquals("None of the codes provided are in the value set http://hl7.org/fhir/ValueSet/marital-status (http://hl7.org/fhir/ValueSet/marital-status, and a code should come from this value set unless it has no suitable code) (codes = http://hl7.org/fhir/v3/MaritalStatus#FOO)", output.getMessages().get(0).getMessage());
|
assertEquals("None of the codings provided are in the value set http://hl7.org/fhir/ValueSet/marital-status (http://hl7.org/fhir/ValueSet/marital-status, and a coding should come from this value set unless it has no suitable code) (codes = http://hl7.org/fhir/v3/MaritalStatus#FOO)", output.getMessages().get(0).getMessage());
|
||||||
assertEquals(ResultSeverityEnum.WARNING, output.getMessages().get(0).getSeverity());
|
assertEquals(ResultSeverityEnum.WARNING, output.getMessages().get(0).getSeverity());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
6
pom.xml
6
pom.xml
|
@ -743,7 +743,7 @@
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
|
|
||||||
<fhir_core_version>5.3.0</fhir_core_version>
|
<fhir_core_version>5.3.10</fhir_core_version>
|
||||||
<ucum_version>1.0.3</ucum_version>
|
<ucum_version>1.0.3</ucum_version>
|
||||||
|
|
||||||
<surefire_jvm_args>-Dfile.encoding=UTF-8 -Xmx2048m</surefire_jvm_args>
|
<surefire_jvm_args>-Dfile.encoding=UTF-8 -Xmx2048m</surefire_jvm_args>
|
||||||
|
@ -829,8 +829,8 @@
|
||||||
<elastic_apm_version>1.13.0</elastic_apm_version>
|
<elastic_apm_version>1.13.0</elastic_apm_version>
|
||||||
<!-- CQL Support -->
|
<!-- CQL Support -->
|
||||||
<cql-engine.version>1.5.1</cql-engine.version>
|
<cql-engine.version>1.5.1</cql-engine.version>
|
||||||
<cql-evaluator.version>1.1.0</cql-evaluator.version>
|
<cql-evaluator.version>1.2.0</cql-evaluator.version>
|
||||||
<cqframework.version>1.5.1</cqframework.version>
|
<cqframework.version>1.5.2</cqframework.version>
|
||||||
|
|
||||||
<!-- Site properties -->
|
<!-- Site properties -->
|
||||||
<fontawesomeVersion>5.4.1</fontawesomeVersion>
|
<fontawesomeVersion>5.4.1</fontawesomeVersion>
|
||||||
|
|
Loading…
Reference in New Issue