Compare commits
13 Commits
97f1ea8753
...
066f3717aa
Author | SHA1 | Date |
---|---|---|
Brenin Rhodes | 066f3717aa | |
Thomas Papke | 3f6d1eb29b | |
Tadgh | 377e44b6ca | |
Martha Mitran | 20d3e6bb25 | |
Brenin Rhodes | cdce4f4806 | |
Brenin Rhodes | e81827fe9d | |
Brenin Rhodes | 3cb57324be | |
Brenin Rhodes | e8d49fedaf | |
Brenin Rhodes | 4f48bce959 | |
Brenin Rhodes | a881ce620c | |
Brenin Rhodes | a49b133a39 | |
Brenin Rhodes | 3e762cced8 | |
Brenin Rhodes | 09eb56e2db |
|
@ -0,0 +1,7 @@
|
||||||
|
---
|
||||||
|
type: add
|
||||||
|
issue: 5873
|
||||||
|
title: "Added support for CDS on FHIR PlanDefinitions that use the CRMI Effective Data Requirements extension when
|
||||||
|
creating the JSON for the Service Discovery. Also added support for use of the FHIR Query Pattern extension when
|
||||||
|
generating Prefetch Templates for Service Discovery. If a DataRequirement has FHIR Query Pattern extensions they
|
||||||
|
will be used, otherwise Prefetch Templates will be generated."
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
type: fix
|
||||||
|
issue: 6290
|
||||||
|
title: "Previously, a specific migration task was using the `TRIM()` function, which does not exist in MSSQL 2012. This was causing migrations targeting MSSQL 2012 to fail.
|
||||||
|
This has been corrected and replaced with usage of a combination of LTRIM() and RTRIM(). Thanks to Primož Delopst at Better for the contribution!"
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
type: fix
|
||||||
|
issue: 6305
|
||||||
|
title: "Previously, when having StorageSettings#getIndexMissingFields() == IndexEnabledEnum.DISABLED (default value)
|
||||||
|
and attempting to search with the missing qualifier against a resource type with multiple search parameters of type reference,
|
||||||
|
the returned results would be incorrect. This has been fixed."
|
|
@ -136,6 +136,8 @@ To create CDS Services from PlanDefinitions the dependencies for a FHIR Storage
|
||||||
|
|
||||||
Any PlanDefinition resource with an action that has a trigger of type [named-event](http://hl7.org/fhir/R4/codesystem-trigger-type.html#trigger-type-named-event) will have a CDS Service created using the PlanDefinition.id as the service id and the name of the trigger as the hook that the service is created for per the [CDS on FHIR Specification](https://hl7.org/fhir/clinicalreasoning-cds-on-fhir.html#surfacing-clinical-decision-support).
|
Any PlanDefinition resource with an action that has a trigger of type [named-event](http://hl7.org/fhir/R4/codesystem-trigger-type.html#trigger-type-named-event) will have a CDS Service created using the PlanDefinition.id as the service id and the name of the trigger as the hook that the service is created for per the [CDS on FHIR Specification](https://hl7.org/fhir/clinicalreasoning-cds-on-fhir.html#surfacing-clinical-decision-support).
|
||||||
|
|
||||||
|
The [CRMI Effective Data Requirements](https://hl7.org/fhir/uv/crmi/1.0.0-snapshot/StructureDefinition-crmi-effectiveDataRequirements.html) extension, if present, will be used to determine the library to use when creating the prefetch templates. Otherwise, the primary library of the PlanDefinition will be used.
|
||||||
|
|
||||||
CDS Services created this way will show up as registered services and can be called just as other services are called. The CDS Service request will be converted into parameters for the [$apply operation](/docs/clinical_reasoning/plan_definitions.html#apply), the results of which are then converted into a CDS Response per the [CDS on FHIR Specification](https://hl7.org/fhir/clinicalreasoning-cds-on-fhir.html#consuming-decision-support).
|
CDS Services created this way will show up as registered services and can be called just as other services are called. The CDS Service request will be converted into parameters for the [$apply operation](/docs/clinical_reasoning/plan_definitions.html#apply), the results of which are then converted into a CDS Response per the [CDS on FHIR Specification](https://hl7.org/fhir/clinicalreasoning-cds-on-fhir.html#consuming-decision-support).
|
||||||
|
|
||||||
These CDS Services will take advantage of the [Auto Prefetch](/docs/cds_hooks/#auto-prefetch) feature. Prefetch data is included as a Bundle in the `data` parameter of the $apply call.
|
These CDS Services will take advantage of the [Auto Prefetch](/docs/cds_hooks/#auto-prefetch) feature. Prefetch data is included as a Bundle in the `data` parameter of the $apply call.
|
||||||
|
|
|
@ -800,14 +800,19 @@ public class ResourceLinkPredicateBuilder extends BaseJoiningPredicateBuilder im
|
||||||
subquery.addCustomColumns(1);
|
subquery.addCustomColumns(1);
|
||||||
subquery.addFromTable(getTable());
|
subquery.addFromTable(getTable());
|
||||||
|
|
||||||
|
String resourceType = theParams.getResourceTablePredicateBuilder().getResourceType();
|
||||||
|
RuntimeSearchParam paramDefinition =
|
||||||
|
mySearchParamRegistry.getRuntimeSearchParam(resourceType, theParams.getParamName());
|
||||||
|
List<String> pathList = paramDefinition.getPathsSplitForResourceType(resourceType);
|
||||||
|
|
||||||
Condition subQueryCondition = ComboCondition.and(
|
Condition subQueryCondition = ComboCondition.and(
|
||||||
BinaryCondition.equalTo(
|
BinaryCondition.equalTo(
|
||||||
getResourceIdColumn(),
|
getResourceIdColumn(),
|
||||||
theParams.getResourceTablePredicateBuilder().getResourceIdColumn()),
|
theParams.getResourceTablePredicateBuilder().getResourceIdColumn()),
|
||||||
BinaryCondition.equalTo(
|
BinaryCondition.equalTo(getResourceTypeColumn(), generatePlaceholder(resourceType)),
|
||||||
getResourceTypeColumn(),
|
ComboCondition.or(pathList.stream()
|
||||||
generatePlaceholder(
|
.map(path -> BinaryCondition.equalTo(getColumnSourcePath(), generatePlaceholder(path)))
|
||||||
theParams.getResourceTablePredicateBuilder().getResourceType())));
|
.toArray(BinaryCondition[]::new)));
|
||||||
|
|
||||||
subquery.addCondition(subQueryCondition);
|
subquery.addCondition(subQueryCondition);
|
||||||
|
|
||||||
|
|
|
@ -70,6 +70,11 @@
|
||||||
<artifactId>jakarta.servlet-api</artifactId>
|
<artifactId>jakarta.servlet-api</artifactId>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>jakarta.mail</groupId>
|
||||||
|
<artifactId>jakarta.mail-api</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- test dependencies -->
|
<!-- test dependencies -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|
|
@ -28,8 +28,8 @@ import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
import javax.mail.internet.InternetAddress;
|
import jakarta.mail.internet.InternetAddress;
|
||||||
import javax.mail.internet.MimeMessage;
|
import jakarta.mail.internet.MimeMessage;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
|
@ -17,8 +17,8 @@ import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.mail.internet.InternetAddress;
|
import jakarta.mail.internet.InternetAddress;
|
||||||
import javax.mail.internet.MimeMessage;
|
import jakarta.mail.internet.MimeMessage;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
|
@ -26,8 +26,8 @@ import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
import javax.mail.internet.InternetAddress;
|
import jakarta.mail.internet.InternetAddress;
|
||||||
import javax.mail.internet.MimeMessage;
|
import jakarta.mail.internet.MimeMessage;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
package ca.uhn.fhir.jpa.dao.r4;
|
package ca.uhn.fhir.jpa.dao.r4;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
import ca.uhn.fhir.i18n.Msg;
|
|
||||||
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
|
|
||||||
import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel;
|
import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel;
|
||||||
|
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamCoords;
|
||||||
|
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity;
|
||||||
|
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantityNormalized;
|
||||||
|
import ca.uhn.fhir.jpa.model.entity.StorageSettings;
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
import ca.uhn.fhir.jpa.test.BaseJpaR4Test;
|
import ca.uhn.fhir.jpa.test.BaseJpaR4Test;
|
||||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
|
@ -12,7 +13,6 @@ import ca.uhn.fhir.rest.param.QuantityParam;
|
||||||
import ca.uhn.fhir.rest.param.ReferenceParam;
|
import ca.uhn.fhir.rest.param.ReferenceParam;
|
||||||
import ca.uhn.fhir.rest.param.StringParam;
|
import ca.uhn.fhir.rest.param.StringParam;
|
||||||
import ca.uhn.fhir.rest.param.TokenParam;
|
import ca.uhn.fhir.rest.param.TokenParam;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;
|
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.hl7.fhir.r4.model.DateType;
|
import org.hl7.fhir.r4.model.DateType;
|
||||||
import org.hl7.fhir.r4.model.DecimalType;
|
import org.hl7.fhir.r4.model.DecimalType;
|
||||||
|
@ -26,57 +26,25 @@ import org.hl7.fhir.r4.model.Reference;
|
||||||
import org.hl7.fhir.r4.model.Task;
|
import org.hl7.fhir.r4.model.Task;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Nested;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
|
public class FhirResourceDaoR4SearchMissingTest {
|
||||||
private static final Logger ourLog = LoggerFactory.getLogger(FhirResourceDaoR4SearchMissingTest.class);
|
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
class IndexMissingDisabledTests extends MissingTests {
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void beforeResetMissing() {
|
public void before() {
|
||||||
myStorageSettings.setIndexMissingFields(JpaStorageSettings.IndexEnabledEnum.ENABLED);
|
myStorageSettings.setIndexMissingFields(StorageSettings.IndexEnabledEnum.DISABLED);
|
||||||
}
|
|
||||||
|
|
||||||
@AfterEach
|
|
||||||
public void afterResetSearch() {
|
|
||||||
myStorageSettings.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testIndexMissingFieldsDisabledDontAllowInSearch_NonReference() {
|
|
||||||
myStorageSettings.setIndexMissingFields(JpaStorageSettings.IndexEnabledEnum.DISABLED);
|
|
||||||
|
|
||||||
SearchParameterMap params = new SearchParameterMap();
|
|
||||||
params.add(Patient.SP_ACTIVE, new StringParam().setMissing(true));
|
|
||||||
try {
|
|
||||||
myPatientDao.search(params);
|
|
||||||
} catch (MethodNotAllowedException e) {
|
|
||||||
assertEquals(Msg.code(985) + ":missing modifier is disabled on this server", e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testIndexMissingFieldsDisabledDontAllowInSearch_Reference() {
|
|
||||||
myStorageSettings.setIndexMissingFields(JpaStorageSettings.IndexEnabledEnum.DISABLED);
|
|
||||||
|
|
||||||
SearchParameterMap params = new SearchParameterMap();
|
|
||||||
params.add(Patient.SP_ORGANIZATION, new StringParam().setMissing(true));
|
|
||||||
try {
|
|
||||||
myPatientDao.search(params);
|
|
||||||
} catch (MethodNotAllowedException e) {
|
|
||||||
assertEquals(Msg.code(985) + ":missing modifier is disabled on this server", e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIndexMissingFieldsDisabledDontCreateIndexes() {
|
public void testIndexMissingFieldsDisabledDontCreateIndexes() {
|
||||||
myStorageSettings.setIndexMissingFields(JpaStorageSettings.IndexEnabledEnum.DISABLED);
|
|
||||||
Organization org = new Organization();
|
Organization org = new Organization();
|
||||||
org.setActive(true);
|
org.setActive(true);
|
||||||
myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless();
|
myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless();
|
||||||
|
@ -93,8 +61,6 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIndexMissingFieldsDisabledDontCreateIndexesWithNormalizedQuantitySearchSupported() {
|
public void testIndexMissingFieldsDisabledDontCreateIndexesWithNormalizedQuantitySearchSupported() {
|
||||||
|
|
||||||
myStorageSettings.setIndexMissingFields(JpaStorageSettings.IndexEnabledEnum.DISABLED);
|
|
||||||
myStorageSettings.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
|
myStorageSettings.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
|
||||||
Organization org = new Organization();
|
Organization org = new Organization();
|
||||||
org.setActive(true);
|
org.setActive(true);
|
||||||
|
@ -112,8 +78,6 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIndexMissingFieldsDisabledDontCreateIndexesWithNormalizedQuantityStorageSupported() {
|
public void testIndexMissingFieldsDisabledDontCreateIndexesWithNormalizedQuantityStorageSupported() {
|
||||||
|
|
||||||
myStorageSettings.setIndexMissingFields(JpaStorageSettings.IndexEnabledEnum.DISABLED);
|
|
||||||
myStorageSettings.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_STORAGE_SUPPORTED);
|
myStorageSettings.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_STORAGE_SUPPORTED);
|
||||||
Organization org = new Organization();
|
Organization org = new Organization();
|
||||||
org.setActive(true);
|
org.setActive(true);
|
||||||
|
@ -128,6 +92,15 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nested
|
||||||
|
class IndexMissingEnabledTests extends MissingTests {
|
||||||
|
@BeforeEach
|
||||||
|
public void before() {
|
||||||
|
myStorageSettings.setIndexMissingFields(StorageSettings.IndexEnabledEnum.ENABLED);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@Test
|
@Test
|
||||||
|
@ -144,11 +117,10 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
|
||||||
task.setRequester(new Reference(oid1));
|
task.setRequester(new Reference(oid1));
|
||||||
tid1 = myTaskDao.create(task, mySrd).getId().toUnqualifiedVersionless();
|
tid1 = myTaskDao.create(task, mySrd).getId().toUnqualifiedVersionless();
|
||||||
}
|
}
|
||||||
IIdType tid2;
|
|
||||||
{
|
{
|
||||||
Task task = new Task();
|
Task task = new Task();
|
||||||
task.setOwner(new Reference(oid1));
|
task.setOwner(new Reference(oid1));
|
||||||
tid2 = myTaskDao.create(task, mySrd).getId().toUnqualifiedVersionless();
|
myTaskDao.create(task, mySrd).getId().toUnqualifiedVersionless();
|
||||||
}
|
}
|
||||||
|
|
||||||
IIdType oid2;
|
IIdType oid2;
|
||||||
|
@ -170,27 +142,36 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
|
||||||
|
|
||||||
map = new SearchParameterMap();
|
map = new SearchParameterMap();
|
||||||
map.add(Organization.SP_NAME, new StringParam().setMissing(true));
|
map.add(Organization.SP_NAME, new StringParam().setMissing(true));
|
||||||
ids = toUnqualifiedVersionlessIds(myOrganizationDao.search(map));
|
ids = toUnqualifiedVersionlessIds(myOrganizationDao.search(map, mySrd));
|
||||||
assertThat(ids).containsExactly(oid1);
|
assertThat(ids).containsExactly(oid1);
|
||||||
|
|
||||||
ourLog.info("Starting Search 2");
|
ourLog.info("Starting Search 2");
|
||||||
|
|
||||||
map = new SearchParameterMap();
|
map = new SearchParameterMap();
|
||||||
map.add(Task.SP_REQUESTER, new ReferenceParam("Organization", "name:missing", "true"));
|
map.add(Task.SP_REQUESTER, new ReferenceParam("Organization", "name:missing", "true"));
|
||||||
ids = toUnqualifiedVersionlessIds(myTaskDao.search(map));
|
ids = toUnqualifiedVersionlessIds(myTaskDao.search(map, mySrd));
|
||||||
assertThat(ids).containsExactly(tid1); // NOT tid2
|
assertThat(ids).containsExactly(tid1); // NOT tid2
|
||||||
|
|
||||||
map = new SearchParameterMap();
|
map = new SearchParameterMap();
|
||||||
map.add(Task.SP_REQUESTER, new ReferenceParam("Organization", "name:missing", "false"));
|
map.add(Task.SP_REQUESTER, new ReferenceParam("Organization", "name:missing", "false"));
|
||||||
ids = toUnqualifiedVersionlessIds(myTaskDao.search(map));
|
ids = toUnqualifiedVersionlessIds(myTaskDao.search(map, mySrd));
|
||||||
assertThat(ids).containsExactly(tid3);
|
assertThat(ids).containsExactly(tid3);
|
||||||
|
|
||||||
map = new SearchParameterMap();
|
map = new SearchParameterMap();
|
||||||
map.add(Patient.SP_ORGANIZATION, new ReferenceParam("Organization", "name:missing", "true"));
|
map.add(Patient.SP_ORGANIZATION, new ReferenceParam("Organization", "name:missing", "true"));
|
||||||
ids = toUnqualifiedVersionlessIds(myPatientDao.search(map));
|
ids = toUnqualifiedVersionlessIds(myPatientDao.search(map, mySrd));
|
||||||
assertThat(ids).isEmpty();
|
assertThat(ids).isEmpty();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class MissingTests extends BaseJpaR4Test {
|
||||||
|
@AfterEach
|
||||||
|
public void after() {
|
||||||
|
myStorageSettings.setIndexMissingFields(new StorageSettings().getIndexMissingFields());
|
||||||
|
myStorageSettings.setNormalizedQuantitySearchLevel(new StorageSettings().getNormalizedQuantitySearchLevel());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSearchWithMissingDate() {
|
public void testSearchWithMissingDate() {
|
||||||
|
@ -217,7 +198,7 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
|
||||||
DateParam param = new DateParam();
|
DateParam param = new DateParam();
|
||||||
param.setMissing(false);
|
param.setMissing(false);
|
||||||
params.add(Patient.SP_BIRTHDATE, param);
|
params.add(Patient.SP_BIRTHDATE, param);
|
||||||
List<IIdType> patients = toUnqualifiedVersionlessIds(myPatientDao.search(params));
|
List<IIdType> patients = toUnqualifiedVersionlessIds(myPatientDao.search(params, mySrd));
|
||||||
assertThat(patients).containsSubsequence(notMissing);
|
assertThat(patients).containsSubsequence(notMissing);
|
||||||
assertThat(patients).doesNotContainSubsequence(missing);
|
assertThat(patients).doesNotContainSubsequence(missing);
|
||||||
}
|
}
|
||||||
|
@ -227,7 +208,7 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
|
||||||
DateParam param = new DateParam();
|
DateParam param = new DateParam();
|
||||||
param.setMissing(true);
|
param.setMissing(true);
|
||||||
params.add(Patient.SP_BIRTHDATE, param);
|
params.add(Patient.SP_BIRTHDATE, param);
|
||||||
List<IIdType> patients = toUnqualifiedVersionlessIds(myPatientDao.search(params));
|
List<IIdType> patients = toUnqualifiedVersionlessIds(myPatientDao.search(params, mySrd));
|
||||||
assertThat(patients).containsSubsequence(missing);
|
assertThat(patients).containsSubsequence(missing);
|
||||||
assertThat(patients).doesNotContainSubsequence(notMissing);
|
assertThat(patients).doesNotContainSubsequence(notMissing);
|
||||||
}
|
}
|
||||||
|
@ -238,9 +219,11 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
|
||||||
String locId = myLocationDao.create(new Location(), mySrd).getId().toUnqualifiedVersionless().getValue();
|
String locId = myLocationDao.create(new Location(), mySrd).getId().toUnqualifiedVersionless().getValue();
|
||||||
String locId2 = myLocationDao.create(new Location().setPosition(new Location.LocationPositionComponent(new DecimalType(10), new DecimalType(10))), mySrd).getId().toUnqualifiedVersionless().getValue();
|
String locId2 = myLocationDao.create(new Location().setPosition(new Location.LocationPositionComponent(new DecimalType(10), new DecimalType(10))), mySrd).getId().toUnqualifiedVersionless().getValue();
|
||||||
|
|
||||||
runInTransaction(() -> {
|
runInTransaction(() -> ourLog.info("Coords:\n * {}",
|
||||||
ourLog.info("Coords:\n * {}", myResourceIndexedSearchParamCoordsDao.findAll().stream().map(t -> t.toString()).collect(Collectors.joining("\n * ")));
|
myResourceIndexedSearchParamCoordsDao.findAll().stream()
|
||||||
});
|
.map(ResourceIndexedSearchParamCoords::toString).collect(Collectors.joining("\n * "))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
{
|
{
|
||||||
SearchParameterMap params = new SearchParameterMap();
|
SearchParameterMap params = new SearchParameterMap();
|
||||||
|
@ -249,7 +232,7 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
|
||||||
param.setMissing(true);
|
param.setMissing(true);
|
||||||
params.add(Location.SP_NEAR, param);
|
params.add(Location.SP_NEAR, param);
|
||||||
myCaptureQueriesListener.clear();
|
myCaptureQueriesListener.clear();
|
||||||
List<String> patients = toUnqualifiedVersionlessIdValues(myLocationDao.search(params));
|
List<String> patients = toUnqualifiedVersionlessIdValues(myLocationDao.search(params, mySrd));
|
||||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread(0);
|
myCaptureQueriesListener.logSelectQueriesForCurrentThread(0);
|
||||||
assertThat(patients).containsSubsequence(locId);
|
assertThat(patients).containsSubsequence(locId);
|
||||||
assertThat(patients).doesNotContainSubsequence(locId2);
|
assertThat(patients).doesNotContainSubsequence(locId2);
|
||||||
|
@ -260,7 +243,7 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
|
||||||
TokenParam param = new TokenParam();
|
TokenParam param = new TokenParam();
|
||||||
param.setMissing(false);
|
param.setMissing(false);
|
||||||
params.add(Location.SP_NEAR, param);
|
params.add(Location.SP_NEAR, param);
|
||||||
List<String> patients = toUnqualifiedVersionlessIdValues(myLocationDao.search(params));
|
List<String> patients = toUnqualifiedVersionlessIdValues(myLocationDao.search(params, mySrd));
|
||||||
assertThat(patients).containsSubsequence(locId2);
|
assertThat(patients).containsSubsequence(locId2);
|
||||||
assertThat(patients).doesNotContainSubsequence(locId);
|
assertThat(patients).doesNotContainSubsequence(locId);
|
||||||
}
|
}
|
||||||
|
@ -271,15 +254,15 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
|
||||||
MedicationRequest mr1 = new MedicationRequest();
|
MedicationRequest mr1 = new MedicationRequest();
|
||||||
mr1.addCategory().addCoding().setSystem("urn:medicationroute").setCode("oral");
|
mr1.addCategory().addCoding().setSystem("urn:medicationroute").setCode("oral");
|
||||||
mr1.addDosageInstruction().getTiming().addEventElement().setValueAsString("2017-01-01");
|
mr1.addDosageInstruction().getTiming().addEventElement().setValueAsString("2017-01-01");
|
||||||
IIdType id1 = myMedicationRequestDao.create(mr1).getId().toUnqualifiedVersionless();
|
myMedicationRequestDao.create(mr1, mySrd).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
MedicationRequest mr2 = new MedicationRequest();
|
MedicationRequest mr2 = new MedicationRequest();
|
||||||
mr2.addCategory().addCoding().setSystem("urn:medicationroute").setCode("oral");
|
mr2.addCategory().addCoding().setSystem("urn:medicationroute").setCode("oral");
|
||||||
IIdType id2 = myMedicationRequestDao.create(mr2).getId().toUnqualifiedVersionless();
|
IIdType id2 = myMedicationRequestDao.create(mr2, mySrd).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
SearchParameterMap map = new SearchParameterMap();
|
SearchParameterMap map = new SearchParameterMap();
|
||||||
map.add(MedicationRequest.SP_DATE, new DateParam().setMissing(true));
|
map.add(MedicationRequest.SP_DATE, new DateParam().setMissing(true));
|
||||||
IBundleProvider results = myMedicationRequestDao.search(map);
|
IBundleProvider results = myMedicationRequestDao.search(map, mySrd);
|
||||||
List<String> ids = toUnqualifiedVersionlessIdValues(results);
|
List<String> ids = toUnqualifiedVersionlessIdValues(results);
|
||||||
|
|
||||||
assertThat(ids).containsExactly(id2.getValue());
|
assertThat(ids).containsExactly(id2.getValue());
|
||||||
|
@ -308,7 +291,7 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
|
||||||
QuantityParam param = new QuantityParam();
|
QuantityParam param = new QuantityParam();
|
||||||
param.setMissing(false);
|
param.setMissing(false);
|
||||||
params.add(Observation.SP_VALUE_QUANTITY, param);
|
params.add(Observation.SP_VALUE_QUANTITY, param);
|
||||||
List<IIdType> patients = toUnqualifiedVersionlessIds(myObservationDao.search(params));
|
List<IIdType> patients = toUnqualifiedVersionlessIds(myObservationDao.search(params, mySrd));
|
||||||
assertThat(patients).doesNotContainSubsequence(missing);
|
assertThat(patients).doesNotContainSubsequence(missing);
|
||||||
assertThat(patients).containsSubsequence(notMissing);
|
assertThat(patients).containsSubsequence(notMissing);
|
||||||
}
|
}
|
||||||
|
@ -318,7 +301,7 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
|
||||||
QuantityParam param = new QuantityParam();
|
QuantityParam param = new QuantityParam();
|
||||||
param.setMissing(true);
|
param.setMissing(true);
|
||||||
params.add(Observation.SP_VALUE_QUANTITY, param);
|
params.add(Observation.SP_VALUE_QUANTITY, param);
|
||||||
List<IIdType> patients = toUnqualifiedVersionlessIds(myObservationDao.search(params));
|
List<IIdType> patients = toUnqualifiedVersionlessIds(myObservationDao.search(params, mySrd));
|
||||||
assertThat(patients).containsSubsequence(missing);
|
assertThat(patients).containsSubsequence(missing);
|
||||||
assertThat(patients).doesNotContainSubsequence(notMissing);
|
assertThat(patients).doesNotContainSubsequence(notMissing);
|
||||||
}
|
}
|
||||||
|
@ -343,8 +326,16 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
|
||||||
}
|
}
|
||||||
|
|
||||||
runInTransaction(() -> {
|
runInTransaction(() -> {
|
||||||
ourLog.info("Quantity Indexes:\n * {}", myResourceIndexedSearchParamQuantityDao.findAll().stream().filter(t -> t.getParamName().equals("value-quantity")).map(t -> t.toString()).collect(Collectors.joining("\n * ")));
|
ourLog.info("Quantity Indexes:\n * {}",
|
||||||
ourLog.info("Normalized Quantity Indexes:\n * {}", myResourceIndexedSearchParamQuantityNormalizedDao.findAll().stream().filter(t -> t.getParamName().equals("value-quantity")).map(t -> t.toString()).collect(Collectors.joining("\n * ")));
|
myResourceIndexedSearchParamQuantityDao.findAll().stream()
|
||||||
|
.filter(t -> t.getParamName().equals("value-quantity")).map(ResourceIndexedSearchParamQuantity::toString).collect(Collectors.joining("\n * ")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
ourLog.info("Normalized Quantity Indexes:\n * {}",
|
||||||
|
myResourceIndexedSearchParamQuantityNormalizedDao.findAll().stream().
|
||||||
|
filter(t -> t.getParamName().equals("value-quantity")).map(ResourceIndexedSearchParamQuantityNormalized::toString).collect(Collectors.joining("\n * ")
|
||||||
|
)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Quantity Param
|
// Quantity Param
|
||||||
|
@ -354,7 +345,7 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
|
||||||
QuantityParam param = new QuantityParam();
|
QuantityParam param = new QuantityParam();
|
||||||
param.setMissing(false);
|
param.setMissing(false);
|
||||||
params.add(Observation.SP_VALUE_QUANTITY, param);
|
params.add(Observation.SP_VALUE_QUANTITY, param);
|
||||||
List<IIdType> patients = toUnqualifiedVersionlessIds(myObservationDao.search(params));
|
List<IIdType> patients = toUnqualifiedVersionlessIds(myObservationDao.search(params, mySrd));
|
||||||
assertThat(patients).doesNotContainSubsequence(missing);
|
assertThat(patients).doesNotContainSubsequence(missing);
|
||||||
assertThat(patients).containsSubsequence(notMissing);
|
assertThat(patients).containsSubsequence(notMissing);
|
||||||
}
|
}
|
||||||
|
@ -365,7 +356,7 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
|
||||||
param.setMissing(true);
|
param.setMissing(true);
|
||||||
params.add(Observation.SP_VALUE_QUANTITY, param);
|
params.add(Observation.SP_VALUE_QUANTITY, param);
|
||||||
myCaptureQueriesListener.clear();
|
myCaptureQueriesListener.clear();
|
||||||
List<IIdType> patients = toUnqualifiedVersionlessIds(myObservationDao.search(params));
|
List<IIdType> patients = toUnqualifiedVersionlessIds(myObservationDao.search(params, mySrd));
|
||||||
myCaptureQueriesListener.logSelectQueries();
|
myCaptureQueriesListener.logSelectQueries();
|
||||||
assertThat(patients).containsSubsequence(missing);
|
assertThat(patients).containsSubsequence(missing);
|
||||||
assertThat(patients).doesNotContainSubsequence(notMissing);
|
assertThat(patients).doesNotContainSubsequence(notMissing);
|
||||||
|
@ -397,7 +388,7 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
|
||||||
QuantityParam param = new QuantityParam();
|
QuantityParam param = new QuantityParam();
|
||||||
param.setMissing(false);
|
param.setMissing(false);
|
||||||
params.add(Observation.SP_VALUE_QUANTITY, param);
|
params.add(Observation.SP_VALUE_QUANTITY, param);
|
||||||
List<IIdType> patients = toUnqualifiedVersionlessIds(myObservationDao.search(params));
|
List<IIdType> patients = toUnqualifiedVersionlessIds(myObservationDao.search(params, mySrd));
|
||||||
assertThat(patients).doesNotContainSubsequence(missing);
|
assertThat(patients).doesNotContainSubsequence(missing);
|
||||||
assertThat(patients).containsSubsequence(notMissing);
|
assertThat(patients).containsSubsequence(notMissing);
|
||||||
}
|
}
|
||||||
|
@ -407,7 +398,7 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
|
||||||
QuantityParam param = new QuantityParam();
|
QuantityParam param = new QuantityParam();
|
||||||
param.setMissing(true);
|
param.setMissing(true);
|
||||||
params.add(Observation.SP_VALUE_QUANTITY, param);
|
params.add(Observation.SP_VALUE_QUANTITY, param);
|
||||||
List<IIdType> patients = toUnqualifiedVersionlessIds(myObservationDao.search(params));
|
List<IIdType> patients = toUnqualifiedVersionlessIds(myObservationDao.search(params, mySrd));
|
||||||
assertThat(patients).containsSubsequence(missing);
|
assertThat(patients).containsSubsequence(missing);
|
||||||
assertThat(patients).doesNotContainSubsequence(notMissing);
|
assertThat(patients).doesNotContainSubsequence(notMissing);
|
||||||
}
|
}
|
||||||
|
@ -440,7 +431,7 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
|
||||||
ReferenceParam param = new ReferenceParam();
|
ReferenceParam param = new ReferenceParam();
|
||||||
param.setMissing(false);
|
param.setMissing(false);
|
||||||
params.add(Patient.SP_ORGANIZATION, param);
|
params.add(Patient.SP_ORGANIZATION, param);
|
||||||
List<IIdType> patients = toUnqualifiedVersionlessIds(myPatientDao.search(params));
|
List<IIdType> patients = toUnqualifiedVersionlessIds(myPatientDao.search(params, mySrd));
|
||||||
assertThat(patients).doesNotContainSubsequence(missing);
|
assertThat(patients).doesNotContainSubsequence(missing);
|
||||||
assertThat(patients).containsSubsequence(notMissing);
|
assertThat(patients).containsSubsequence(notMissing);
|
||||||
}
|
}
|
||||||
|
@ -450,13 +441,36 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
|
||||||
ReferenceParam param = new ReferenceParam();
|
ReferenceParam param = new ReferenceParam();
|
||||||
param.setMissing(true);
|
param.setMissing(true);
|
||||||
params.add(Patient.SP_ORGANIZATION, param);
|
params.add(Patient.SP_ORGANIZATION, param);
|
||||||
List<IIdType> patients = toUnqualifiedVersionlessIds(myPatientDao.search(params));
|
List<IIdType> patients = toUnqualifiedVersionlessIds(myPatientDao.search(params, mySrd));
|
||||||
assertThat(patients).containsSubsequence(missing);
|
assertThat(patients).containsSubsequence(missing);
|
||||||
assertThat(patients).doesNotContainSubsequence(notMissing);
|
assertThat(patients).doesNotContainSubsequence(notMissing);
|
||||||
assertThat(patients).doesNotContainSubsequence(orgId);
|
assertThat(patients).doesNotContainSubsequence(orgId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearchWithMissingReference_resourceTypeWithMultipleReferences() {
|
||||||
|
IIdType patientId = createPatient();
|
||||||
|
IIdType observationId = createObservation(withSubject(patientId));
|
||||||
|
|
||||||
|
SearchParameterMap params = new SearchParameterMap();
|
||||||
|
params.add(Observation.SP_PERFORMER, new ReferenceParam().setMissing(true));
|
||||||
|
IBundleProvider bundleProvider = myObservationDao.search(params, mySrd);
|
||||||
|
assertThat(bundleProvider.getAllResourceIds()).containsExactly(observationId.getIdPart());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearchWithMissingReference_searchParamMultiplePaths() {
|
||||||
|
IIdType encounterId = createEncounter();
|
||||||
|
createObservation(withEncounter(encounterId.getValue()));
|
||||||
|
|
||||||
|
SearchParameterMap params = new SearchParameterMap();
|
||||||
|
params.add(Observation.SP_ENCOUNTER, new ReferenceParam().setMissing(true));
|
||||||
|
IBundleProvider bundleProvider = myObservationDao.search(params, mySrd);
|
||||||
|
assertThat(bundleProvider.getAllResourceIds()).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSearchWithMissingString() {
|
public void testSearchWithMissingString() {
|
||||||
IIdType orgId = myOrganizationDao.create(new Organization(), mySrd).getId();
|
IIdType orgId = myOrganizationDao.create(new Organization(), mySrd).getId();
|
||||||
|
@ -482,7 +496,7 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
|
||||||
StringParam param = new StringParam();
|
StringParam param = new StringParam();
|
||||||
param.setMissing(false);
|
param.setMissing(false);
|
||||||
params.add(Patient.SP_FAMILY, param);
|
params.add(Patient.SP_FAMILY, param);
|
||||||
List<IIdType> patients = toUnqualifiedVersionlessIds(myPatientDao.search(params));
|
List<IIdType> patients = toUnqualifiedVersionlessIds(myPatientDao.search(params, mySrd));
|
||||||
assertThat(patients).doesNotContainSubsequence(missing);
|
assertThat(patients).doesNotContainSubsequence(missing);
|
||||||
assertThat(patients).containsSubsequence(notMissing);
|
assertThat(patients).containsSubsequence(notMissing);
|
||||||
}
|
}
|
||||||
|
@ -492,7 +506,7 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
|
||||||
StringParam param = new StringParam();
|
StringParam param = new StringParam();
|
||||||
param.setMissing(true);
|
param.setMissing(true);
|
||||||
params.add(Patient.SP_FAMILY, param);
|
params.add(Patient.SP_FAMILY, param);
|
||||||
List<IIdType> patients = toUnqualifiedVersionlessIds(myPatientDao.search(params));
|
List<IIdType> patients = toUnqualifiedVersionlessIds(myPatientDao.search(params, mySrd));
|
||||||
assertThat(patients).containsSubsequence(missing);
|
assertThat(patients).containsSubsequence(missing);
|
||||||
assertThat(patients).doesNotContainSubsequence(notMissing);
|
assertThat(patients).doesNotContainSubsequence(notMissing);
|
||||||
}
|
}
|
||||||
|
@ -520,7 +534,7 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
|
||||||
TokenParam param = new TokenParam();
|
TokenParam param = new TokenParam();
|
||||||
param.setMissing(false);
|
param.setMissing(false);
|
||||||
params.add(Observation.SP_CODE, param);
|
params.add(Observation.SP_CODE, param);
|
||||||
List<IIdType> patients = toUnqualifiedVersionlessIds(myObservationDao.search(params));
|
List<IIdType> patients = toUnqualifiedVersionlessIds(myObservationDao.search(params, mySrd));
|
||||||
assertThat(patients).doesNotContainSubsequence(missing);
|
assertThat(patients).doesNotContainSubsequence(missing);
|
||||||
assertThat(patients).containsSubsequence(notMissing);
|
assertThat(patients).containsSubsequence(notMissing);
|
||||||
}
|
}
|
||||||
|
@ -530,10 +544,11 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
|
||||||
TokenParam param = new TokenParam();
|
TokenParam param = new TokenParam();
|
||||||
param.setMissing(true);
|
param.setMissing(true);
|
||||||
params.add(Observation.SP_CODE, param);
|
params.add(Observation.SP_CODE, param);
|
||||||
List<IIdType> patients = toUnqualifiedVersionlessIds(myObservationDao.search(params));
|
List<IIdType> patients = toUnqualifiedVersionlessIds(myObservationDao.search(params, mySrd));
|
||||||
assertThat(patients).containsSubsequence(missing);
|
assertThat(patients).containsSubsequence(missing);
|
||||||
assertThat(patients).doesNotContainSubsequence(notMissing);
|
assertThat(patients).doesNotContainSubsequence(notMissing);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -217,7 +217,7 @@ import static org.mockito.Mockito.when;
|
||||||
public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderR4Test.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderR4Test.class);
|
||||||
private SearchCoordinatorSvcImpl mySearchCoordinatorSvcRaw;
|
private SearchCoordinatorSvcImpl mySearchCoordinatorSvcRaw;
|
||||||
private CapturingInterceptor myCapturingInterceptor = new CapturingInterceptor();
|
private final CapturingInterceptor myCapturingInterceptor = new CapturingInterceptor();
|
||||||
@Autowired
|
@Autowired
|
||||||
private ISearchDao mySearchEntityDao;
|
private ISearchDao mySearchEntityDao;
|
||||||
|
|
||||||
|
@ -413,15 +413,15 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
Patient pt1 = new Patient();
|
Patient pt1 = new Patient();
|
||||||
pt1.addName().setFamily("Elizabeth");
|
pt1.addName().setFamily("Elizabeth");
|
||||||
String pt1id = myPatientDao.create(pt1).getId().toUnqualifiedVersionless().getValue();
|
String pt1id = myPatientDao.create(pt1, mySrd).getId().toUnqualifiedVersionless().getValue();
|
||||||
|
|
||||||
Patient pt2 = new Patient();
|
Patient pt2 = new Patient();
|
||||||
pt2.addName().setFamily("fghijk");
|
pt2.addName().setFamily("fghijk");
|
||||||
String pt2id = myPatientDao.create(pt2).getId().toUnqualifiedVersionless().getValue();
|
String pt2id = myPatientDao.create(pt2, mySrd).getId().toUnqualifiedVersionless().getValue();
|
||||||
|
|
||||||
Patient pt3 = new Patient();
|
Patient pt3 = new Patient();
|
||||||
pt3.addName().setFamily("zzzzz");
|
pt3.addName().setFamily("zzzzz");
|
||||||
myPatientDao.create(pt3).getId().toUnqualifiedVersionless().getValue();
|
myPatientDao.create(pt3, mySrd).getId().toUnqualifiedVersionless().getValue();
|
||||||
|
|
||||||
|
|
||||||
Bundle output = myClient
|
Bundle output = myClient
|
||||||
|
@ -450,7 +450,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
Patient pt1 = new Patient();
|
Patient pt1 = new Patient();
|
||||||
pt1.addName().setFamily("Smith%");
|
pt1.addName().setFamily("Smith%");
|
||||||
String pt1id = myPatientDao.create(pt1).getId().toUnqualifiedVersionless().getValue();
|
String pt1id = myPatientDao.create(pt1, mySrd).getId().toUnqualifiedVersionless().getValue();
|
||||||
|
|
||||||
Bundle output = myClient
|
Bundle output = myClient
|
||||||
.search()
|
.search()
|
||||||
|
@ -463,7 +463,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
Patient pt2 = new Patient();
|
Patient pt2 = new Patient();
|
||||||
pt2.addName().setFamily("Sm%ith");
|
pt2.addName().setFamily("Sm%ith");
|
||||||
String pt2id = myPatientDao.create(pt2).getId().toUnqualifiedVersionless().getValue();
|
String pt2id = myPatientDao.create(pt2, mySrd).getId().toUnqualifiedVersionless().getValue();
|
||||||
|
|
||||||
output = myClient
|
output = myClient
|
||||||
.search()
|
.search()
|
||||||
|
@ -740,7 +740,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
Patient p = new Patient();
|
Patient p = new Patient();
|
||||||
p.addName().setFamily("FAM").addGiven("GIV");
|
p.addName().setFamily("FAM").addGiven("GIV");
|
||||||
IIdType id = myPatientDao.create(p).getId();
|
IIdType id = myPatientDao.create(p, mySrd).getId();
|
||||||
|
|
||||||
myClient.read().resource("Patient").withId(id.toUnqualifiedVersionless()).execute();
|
myClient.read().resource("Patient").withId(id.toUnqualifiedVersionless()).execute();
|
||||||
|
|
||||||
|
@ -763,7 +763,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
Patient p = new Patient();
|
Patient p = new Patient();
|
||||||
p.addName().setFamily("FAM").addGiven("GIV");
|
p.addName().setFamily("FAM").addGiven("GIV");
|
||||||
IIdType id = myPatientDao.create(p).getId().toUnqualifiedVersionless();
|
IIdType id = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
myClient
|
myClient
|
||||||
.delete()
|
.delete()
|
||||||
|
@ -1025,57 +1025,58 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
public void testCreateAndReadBackResourceWithContainedReferenceToContainer() {
|
public void testCreateAndReadBackResourceWithContainedReferenceToContainer() {
|
||||||
myFhirContext.setParserErrorHandler(new StrictErrorHandler());
|
myFhirContext.setParserErrorHandler(new StrictErrorHandler());
|
||||||
|
|
||||||
String input = "{\n" +
|
String input = """
|
||||||
" \"resourceType\": \"Organization\",\n" +
|
{
|
||||||
" \"id\": \"1\",\n" +
|
"resourceType": "Organization",
|
||||||
" \"meta\": {\n" +
|
"id": "1",
|
||||||
" \"tag\": [\n" +
|
"meta": {
|
||||||
" {\n" +
|
"tag": [
|
||||||
" \"system\": \"https://blah.org/deployment\",\n" +
|
{
|
||||||
" \"code\": \"e69414dd-b5c2-462d-bcfd-9d04d6b16596\",\n" +
|
"system": "https://blah.org/deployment",
|
||||||
" \"display\": \"DEPLOYMENT\"\n" +
|
"code": "e69414dd-b5c2-462d-bcfd-9d04d6b16596",
|
||||||
" },\n" +
|
"display": "DEPLOYMENT"
|
||||||
" {\n" +
|
},
|
||||||
" \"system\": \"https://blah.org/region\",\n" +
|
{
|
||||||
" \"code\": \"b47d7a5b-b159-4bed-a8f8-3258e6603adb\",\n" +
|
"system": "https://blah.org/region",
|
||||||
" \"display\": \"REGION\"\n" +
|
"code": "b47d7a5b-b159-4bed-a8f8-3258e6603adb",
|
||||||
" },\n" +
|
"display": "REGION"
|
||||||
" {\n" +
|
},
|
||||||
" \"system\": \"https://blah.org/provider\",\n" +
|
{
|
||||||
" \"code\": \"28c30004-0333-40cf-9e7f-3f9e080930bd\",\n" +
|
"system": "https://blah.org/provider",
|
||||||
" \"display\": \"PROVIDER\"\n" +
|
"code": "28c30004-0333-40cf-9e7f-3f9e080930bd",
|
||||||
" }\n" +
|
"display": "PROVIDER"
|
||||||
" ]\n" +
|
}
|
||||||
" },\n" +
|
]
|
||||||
" \"contained\": [\n" +
|
},
|
||||||
" {\n" +
|
"contained": [
|
||||||
" \"resourceType\": \"Location\",\n" +
|
{
|
||||||
" \"id\": \"2\",\n" +
|
"resourceType": "Location",
|
||||||
" \"position\": {\n" +
|
"id": "2",
|
||||||
" \"longitude\": 51.443238301454289,\n" +
|
"position": {
|
||||||
" \"latitude\": 7.34196905697293\n" +
|
"longitude": 51.443238301454289,
|
||||||
" },\n" +
|
"latitude": 7.34196905697293
|
||||||
" \"managingOrganization\": {\n" +
|
},
|
||||||
" \"reference\": \"#\"\n" +
|
"managingOrganization": {
|
||||||
" }\n" +
|
"reference": "#"
|
||||||
" }\n" +
|
}
|
||||||
" ],\n" +
|
}
|
||||||
" \"type\": [\n" +
|
],
|
||||||
" {\n" +
|
"type": [
|
||||||
" \"coding\": [\n" +
|
{
|
||||||
" {\n" +
|
"coding": [
|
||||||
" \"system\": \"https://blah.org/fmc/OrganizationType\",\n" +
|
{
|
||||||
" \"code\": \"CLINIC\",\n" +
|
"system": "https://blah.org/fmc/OrganizationType",
|
||||||
" \"display\": \"Clinic\"\n" +
|
"code": "CLINIC",
|
||||||
" }\n" +
|
"display": "Clinic"
|
||||||
" ]\n" +
|
}
|
||||||
" }\n" +
|
]
|
||||||
" ],\n" +
|
}
|
||||||
" \"name\": \"testOrg\"\n" +
|
],
|
||||||
"}";
|
"name": "testOrg"
|
||||||
|
}""";
|
||||||
|
|
||||||
Organization org = myFhirContext.newJsonParser().parseResource(Organization.class, input);
|
Organization org = myFhirContext.newJsonParser().parseResource(Organization.class, input);
|
||||||
IIdType id = myOrganizationDao.create(org).getId();
|
IIdType id = myOrganizationDao.create(org, mySrd).getId();
|
||||||
org = myOrganizationDao.read(id);
|
org = myOrganizationDao.read(id);
|
||||||
|
|
||||||
String output = myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(org);
|
String output = myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(org);
|
||||||
|
@ -1095,9 +1096,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
}
|
}
|
||||||
List<IBaseResource> outcome = myClient.transaction().withResources(resources).prettyPrint().encodedXml().execute();
|
List<IBaseResource> outcome = myClient.transaction().withResources(resources).prettyPrint().encodedXml().execute();
|
||||||
|
|
||||||
runInTransaction(() -> {
|
runInTransaction(() -> assertEquals(100, myResourceTableDao.count()));
|
||||||
assertEquals(100, myResourceTableDao.count());
|
|
||||||
});
|
|
||||||
|
|
||||||
Bundle found = myClient
|
Bundle found = myClient
|
||||||
.search()
|
.search()
|
||||||
|
@ -1306,7 +1305,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreateQuestionnaireResponseWithValidation() throws IOException {
|
public void testCreateQuestionnaireResponseWithValidation() {
|
||||||
CodeSystem cs = new CodeSystem();
|
CodeSystem cs = new CodeSystem();
|
||||||
cs.setUrl("http://cs");
|
cs.setUrl("http://cs");
|
||||||
cs.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
cs.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||||
|
@ -1906,8 +1905,8 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
* Try it with a raw socket call. The Apache client won't let us use the unescaped "|" in the URL but we want to make sure that works too..
|
* Try it with a raw socket call. The Apache client won't let us use the unescaped "|" in the URL but we want to make sure that works too..
|
||||||
*/
|
*/
|
||||||
Socket sock = new Socket();
|
Socket sock = new Socket();
|
||||||
|
try (sock) {
|
||||||
sock.setSoTimeout(3000);
|
sock.setSoTimeout(3000);
|
||||||
try {
|
|
||||||
sock.connect(new InetSocketAddress("localhost", myPort));
|
sock.connect(new InetSocketAddress("localhost", myPort));
|
||||||
sock.getOutputStream().write(("DELETE /fhir/context/Patient?identifier=http://ghh.org/patient|" + methodName + " HTTP/1.1\n").getBytes(StandardCharsets.UTF_8));
|
sock.getOutputStream().write(("DELETE /fhir/context/Patient?identifier=http://ghh.org/patient|" + methodName + " HTTP/1.1\n").getBytes(StandardCharsets.UTF_8));
|
||||||
sock.getOutputStream().write("Host: localhost\n".getBytes(StandardCharsets.UTF_8));
|
sock.getOutputStream().write("Host: localhost\n".getBytes(StandardCharsets.UTF_8));
|
||||||
|
@ -1915,7 +1914,6 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
BufferedReader socketInput = new BufferedReader(new InputStreamReader(sock.getInputStream()));
|
BufferedReader socketInput = new BufferedReader(new InputStreamReader(sock.getInputStream()));
|
||||||
|
|
||||||
// String response = "";
|
|
||||||
StringBuilder b = new StringBuilder();
|
StringBuilder b = new StringBuilder();
|
||||||
char[] buf = new char[1000];
|
char[] buf = new char[1000];
|
||||||
while (socketInput.read(buf) != -1) {
|
while (socketInput.read(buf) != -1) {
|
||||||
|
@ -1925,9 +1923,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
ourLog.debug("Resp: {}", resp);
|
ourLog.debug("Resp: {}", resp);
|
||||||
} catch (SocketTimeoutException e) {
|
} catch (SocketTimeoutException e) {
|
||||||
e.printStackTrace();
|
ourLog.debug(e.getMessage(), e);
|
||||||
} finally {
|
|
||||||
sock.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread.sleep(1000);
|
Thread.sleep(1000);
|
||||||
|
@ -2398,7 +2394,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
assertThat(idValues).as(idValues.toString()).hasSize(10);
|
assertThat(idValues).as(idValues.toString()).hasSize(10);
|
||||||
|
|
||||||
idValues = searchAndReturnUnqualifiedIdValues(myServerBase + "/_history?_at=gt" + InstantDt.withCurrentTime().getYear());
|
idValues = searchAndReturnUnqualifiedIdValues(myServerBase + "/_history?_at=gt" + InstantDt.withCurrentTime().getYear());
|
||||||
assertThat(idValues).hasSize(0);
|
assertThat(idValues).isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2427,7 +2423,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
myMemoryCacheService.invalidateCaches(MemoryCacheService.CacheEnum.PID_TO_FORCED_ID);
|
myMemoryCacheService.invalidateCaches(MemoryCacheService.CacheEnum.PID_TO_FORCED_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
Bundle history = myClient.history().onInstance(id.getValue()).andReturnBundle(Bundle.class).execute();
|
Bundle history = myClient.history().onInstance(id.getValue()).returnBundle(Bundle.class).execute();
|
||||||
assertEquals(1, history.getEntry().size());
|
assertEquals(1, history.getEntry().size());
|
||||||
BundleEntryComponent historyEntry0 = history.getEntry().get(0);
|
BundleEntryComponent historyEntry0 = history.getEntry().get(0);
|
||||||
// validate entry.fullUrl
|
// validate entry.fullUrl
|
||||||
|
@ -2476,7 +2472,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
myMemoryCacheService.invalidateCaches(MemoryCacheService.CacheEnum.PID_TO_FORCED_ID);
|
myMemoryCacheService.invalidateCaches(MemoryCacheService.CacheEnum.PID_TO_FORCED_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
Bundle history = myClient.history().onInstance(id.getValue()).andReturnBundle(Bundle.class).execute();
|
Bundle history = myClient.history().onInstance(id.getValue()).returnBundle(Bundle.class).execute();
|
||||||
assertEquals(1, history.getEntry().size());
|
assertEquals(1, history.getEntry().size());
|
||||||
BundleEntryComponent historyEntry0 = history.getEntry().get(0);
|
BundleEntryComponent historyEntry0 = history.getEntry().get(0);
|
||||||
// validate entry.fullUrl
|
// validate entry.fullUrl
|
||||||
|
@ -2508,7 +2504,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
ourLog.info("Res ID: {}", id);
|
ourLog.info("Res ID: {}", id);
|
||||||
|
|
||||||
Bundle history = myClient.history().onInstance(id.getValue()).andReturnBundle(Bundle.class).prettyPrint().summaryMode(SummaryEnum.DATA).execute();
|
Bundle history = myClient.history().onInstance(id.getValue()).returnBundle(Bundle.class).prettyPrint().summaryMode(SummaryEnum.DATA).execute();
|
||||||
assertThat(history.getEntry()).hasSize(3);
|
assertThat(history.getEntry()).hasSize(3);
|
||||||
assertEquals(id.withVersion("3").getValue(), history.getEntry().get(0).getResource().getId());
|
assertEquals(id.withVersion("3").getValue(), history.getEntry().get(0).getResource().getId());
|
||||||
assertThat(((Patient) history.getEntry().get(0).getResource()).getName()).hasSize(1);
|
assertThat(((Patient) history.getEntry().get(0).getResource()).getName()).hasSize(1);
|
||||||
|
@ -2746,7 +2742,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
int total = 20;
|
int total = 20;
|
||||||
Organization org = new Organization();
|
Organization org = new Organization();
|
||||||
org.setName("ORG");
|
org.setName("ORG");
|
||||||
IIdType orgId = myOrganizationDao.create(org).getId().toUnqualifiedVersionless();
|
IIdType orgId = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
Coding tagCode = new Coding();
|
Coding tagCode = new Coding();
|
||||||
tagCode.setCode("test");
|
tagCode.setCode("test");
|
||||||
|
@ -2757,7 +2753,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
.addTag(tagCode);
|
.addTag(tagCode);
|
||||||
t.setStatus(Task.TaskStatus.REQUESTED);
|
t.setStatus(Task.TaskStatus.REQUESTED);
|
||||||
t.getOwner().setReference(orgId.getValue());
|
t.getOwner().setReference(orgId.getValue());
|
||||||
myTaskDao.create(t);
|
myTaskDao.create(t, mySrd);
|
||||||
}
|
}
|
||||||
HashSet<String> ids = new HashSet<>();
|
HashSet<String> ids = new HashSet<>();
|
||||||
|
|
||||||
|
@ -2835,12 +2831,12 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
if (orgCount > 0) {
|
if (orgCount > 0) {
|
||||||
Organization org = new Organization();
|
Organization org = new Organization();
|
||||||
org.setName("ORG");
|
org.setName("ORG");
|
||||||
IIdType orgId = myOrganizationDao.create(org).getId().toUnqualifiedVersionless();
|
IIdType orgId = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
orgCount--;
|
orgCount--;
|
||||||
t.getOwner().setReference(orgId.getValue());
|
t.getOwner().setReference(orgId.getValue());
|
||||||
}
|
}
|
||||||
myTaskDao.create(t);
|
myTaskDao.create(t, mySrd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2909,12 +2905,12 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
if (orgCount > 0) {
|
if (orgCount > 0) {
|
||||||
Organization org = new Organization();
|
Organization org = new Organization();
|
||||||
org.setName("ORG");
|
org.setName("ORG");
|
||||||
IIdType orgId = myOrganizationDao.create(org).getId().toUnqualifiedVersionless();
|
IIdType orgId = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
orgCount--;
|
orgCount--;
|
||||||
t.getOwner().setReference(orgId.getValue());
|
t.getOwner().setReference(orgId.getValue());
|
||||||
}
|
}
|
||||||
myTaskDao.create(t);
|
myTaskDao.create(t, mySrd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2961,13 +2957,13 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
public void testIncludeCountDoesntIncludeIncludes() {
|
public void testIncludeCountDoesntIncludeIncludes() {
|
||||||
Organization org = new Organization();
|
Organization org = new Organization();
|
||||||
org.setName("ORG");
|
org.setName("ORG");
|
||||||
IIdType orgId = myOrganizationDao.create(org).getId().toUnqualifiedVersionless();
|
IIdType orgId = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
for (int i = 0; i < 10; i++) {
|
for (int i = 0; i < 10; i++) {
|
||||||
Patient pt = new Patient();
|
Patient pt = new Patient();
|
||||||
pt.getManagingOrganization().setReference(orgId.getValue());
|
pt.getManagingOrganization().setReference(orgId.getValue());
|
||||||
pt.addName().setFamily("FAM" + i);
|
pt.addName().setFamily("FAM" + i);
|
||||||
myPatientDao.create(pt);
|
myPatientDao.create(pt, mySrd);
|
||||||
}
|
}
|
||||||
|
|
||||||
Bundle bundle = myClient
|
Bundle bundle = myClient
|
||||||
|
@ -3168,7 +3164,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
Patient newPt = myClient.read().resource(Patient.class).withId(pid1.getIdPart()).execute();
|
Patient newPt = myClient.read().resource(Patient.class).withId(pid1.getIdPart()).execute();
|
||||||
assertEquals("2", newPt.getIdElement().getVersionIdPart());
|
assertEquals("2", newPt.getIdElement().getVersionIdPart());
|
||||||
assertEquals(false, newPt.getActive());
|
assertFalse(newPt.getActive());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -3196,7 +3192,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
Patient newPt = myClient.read().resource(Patient.class).withId(pid1.getIdPart()).execute();
|
Patient newPt = myClient.read().resource(Patient.class).withId(pid1.getIdPart()).execute();
|
||||||
assertEquals("1", newPt.getIdElement().getVersionIdPart());
|
assertEquals("1", newPt.getIdElement().getVersionIdPart());
|
||||||
assertEquals(true, newPt.getActive());
|
assertTrue(newPt.getActive());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -3226,7 +3222,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
Patient newPt = myClient.read().resource(Patient.class).withId(pid1.getIdPart()).execute();
|
Patient newPt = myClient.read().resource(Patient.class).withId(pid1.getIdPart()).execute();
|
||||||
assertEquals("2", newPt.getIdElement().getVersionIdPart());
|
assertEquals("2", newPt.getIdElement().getVersionIdPart());
|
||||||
assertEquals(false, newPt.getActive());
|
assertFalse(newPt.getActive());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -3255,7 +3251,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
Patient newPt = myClient.read().resource(Patient.class).withId(pid1.getIdPart()).execute();
|
Patient newPt = myClient.read().resource(Patient.class).withId(pid1.getIdPart()).execute();
|
||||||
assertEquals("2", newPt.getIdElement().getVersionIdPart());
|
assertEquals("2", newPt.getIdElement().getVersionIdPart());
|
||||||
assertEquals(false, newPt.getActive());
|
assertFalse(newPt.getActive());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -3323,12 +3319,12 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
{
|
{
|
||||||
Bundle returned = myClient.search().forResource(Patient.class).encodedXml().returnBundle(Bundle.class).execute();
|
Bundle returned = myClient.search().forResource(Patient.class).encodedXml().returnBundle(Bundle.class).execute();
|
||||||
assertThat(returned.getEntry().size()).isGreaterThan(1);
|
assertThat(returned.getEntry()).hasSizeGreaterThan(1);
|
||||||
assertEquals(BundleType.SEARCHSET, returned.getType());
|
assertEquals(BundleType.SEARCHSET, returned.getType());
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
Bundle returned = myClient.search().forResource(Patient.class).encodedJson().returnBundle(Bundle.class).execute();
|
Bundle returned = myClient.search().forResource(Patient.class).encodedJson().returnBundle(Bundle.class).execute();
|
||||||
assertThat(returned.getEntry().size()).isGreaterThan(1);
|
assertThat(returned.getEntry()).hasSizeGreaterThan(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3350,7 +3346,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Bundle bundle = myClient.history().onServer().andReturnBundle(Bundle.class).execute();
|
Bundle bundle = myClient.history().onServer().returnBundle(Bundle.class).execute();
|
||||||
assertEquals(1, bundle.getTotal());
|
assertEquals(1, bundle.getTotal());
|
||||||
assertThat(bundle.getEntry()).hasSize(1);
|
assertThat(bundle.getEntry()).hasSize(1);
|
||||||
assertEquals(id2.getIdPart(), bundle.getEntry().get(0).getResource().getIdElement().getIdPart());
|
assertEquals(id2.getIdPart(), bundle.getEntry().get(0).getResource().getIdElement().getIdPart());
|
||||||
|
@ -3507,15 +3503,6 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
assertThat(text).doesNotContain("\"B\"");
|
assertThat(text).doesNotContain("\"B\"");
|
||||||
assertThat(text).doesNotContain("\"B1\"");
|
assertThat(text).doesNotContain("\"B1\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// HttpGet read = new HttpGet(ourServerBase + "/Observation?patient=P5000000302&_sort:desc=code&code:in=http://fkcfhir.org/fhir/vs/ccdacapddialysisorder");
|
|
||||||
// try (CloseableHttpResponse response = ourHttpClient.execute(read)) {
|
|
||||||
// String text = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
|
||||||
// ourLog.info(text);
|
|
||||||
// assertEquals(Constants.STATUS_HTTP_200_OK, response.getStatusLine().getStatusCode());
|
|
||||||
// assertThat(text).doesNotContain("\"text\",\"type\"");
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -3873,9 +3860,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
p.addName().setFamily(methodName + "1");
|
p.addName().setFamily(methodName + "1");
|
||||||
IIdType pid1 = myClient.create().resource(p).execute().getId().toUnqualifiedVersionless();
|
IIdType pid1 = myClient.create().resource(p).execute().getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
Thread.sleep(10);
|
|
||||||
long time1 = System.currentTimeMillis();
|
long time1 = System.currentTimeMillis();
|
||||||
Thread.sleep(10);
|
|
||||||
|
|
||||||
Patient p2 = new Patient();
|
Patient p2 = new Patient();
|
||||||
p2.addName().setFamily(methodName + "2");
|
p2.addName().setFamily(methodName + "2");
|
||||||
|
@ -4064,9 +4049,6 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
public void testSearchLastUpdatedParamRp() throws InterruptedException {
|
public void testSearchLastUpdatedParamRp() throws InterruptedException {
|
||||||
String methodName = "testSearchLastUpdatedParamRp";
|
String methodName = "testSearchLastUpdatedParamRp";
|
||||||
|
|
||||||
int sleep = 100;
|
|
||||||
Thread.sleep(sleep);
|
|
||||||
|
|
||||||
DateTimeType beforeAny = new DateTimeType(new Date(), TemporalPrecisionEnum.MILLI);
|
DateTimeType beforeAny = new DateTimeType(new Date(), TemporalPrecisionEnum.MILLI);
|
||||||
IIdType id1a;
|
IIdType id1a;
|
||||||
{
|
{
|
||||||
|
@ -4083,9 +4065,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
id1b = myClient.create().resource(patient).execute().getId().toUnqualifiedVersionless();
|
id1b = myClient.create().resource(patient).execute().getId().toUnqualifiedVersionless();
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread.sleep(1100);
|
|
||||||
DateTimeType beforeR2 = new DateTimeType(new Date(), TemporalPrecisionEnum.MILLI);
|
DateTimeType beforeR2 = new DateTimeType(new Date(), TemporalPrecisionEnum.MILLI);
|
||||||
Thread.sleep(1100);
|
|
||||||
|
|
||||||
IIdType id2;
|
IIdType id2;
|
||||||
{
|
{
|
||||||
|
@ -4249,13 +4229,12 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
Bundle bundle = myFhirContext.newXmlParser().parseResource(Bundle.class, resp);
|
Bundle bundle = myFhirContext.newXmlParser().parseResource(Bundle.class, resp);
|
||||||
matches = bundle.getEntry().size();
|
matches = bundle.getEntry().size();
|
||||||
|
|
||||||
assertThat(matches).isGreaterThan(0);
|
assertThat(matches).isPositive();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSearchReturnsSearchDate() throws Exception {
|
public void testSearchReturnsSearchDate() throws Exception {
|
||||||
Date before = new Date();
|
Date before = new Date();
|
||||||
Thread.sleep(1);
|
|
||||||
|
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
Bundle found = myClient
|
Bundle found = myClient
|
||||||
|
@ -4266,7 +4245,6 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
.execute();
|
.execute();
|
||||||
//@formatter:on
|
//@formatter:on
|
||||||
|
|
||||||
Thread.sleep(1);
|
|
||||||
Date after = new Date();
|
Date after = new Date();
|
||||||
|
|
||||||
InstantType updated = found.getMeta().getLastUpdatedElement();
|
InstantType updated = found.getMeta().getLastUpdatedElement();
|
||||||
|
@ -4300,7 +4278,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
myObservationDao.create(obs, mySrd);
|
myObservationDao.create(obs, mySrd);
|
||||||
|
|
||||||
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -4313,7 +4291,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
myObservationDao.create(obs, mySrd);
|
myObservationDao.create(obs, mySrd);
|
||||||
|
|
||||||
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -4326,7 +4304,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
myObservationDao.create(obs, mySrd);
|
myObservationDao.create(obs, mySrd);
|
||||||
|
|
||||||
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -4339,24 +4317,24 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
myObservationDao.create(obs, mySrd);
|
myObservationDao.create(obs, mySrd);
|
||||||
|
|
||||||
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
||||||
}
|
}
|
||||||
|
|
||||||
// > 1m
|
// > 1m
|
||||||
String uri = myServerBase + "/Observation?code-value-quantity=http://" + UrlUtil.escapeUrlParam("loinc.org|2345-7$gt1|http://unitsofmeasure.org|m");
|
String uri = myServerBase + "/Observation?code-value-quantity=http://" + UrlUtil.escapeUrlParam("loinc.org|2345-7$gt1|http://unitsofmeasure.org|m");
|
||||||
ourLog.info("uri = " + uri);
|
ourLog.info("uri = {}", uri);
|
||||||
List<String> ids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
|
List<String> ids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
|
||||||
assertThat(ids).hasSize(3);
|
assertThat(ids).hasSize(3);
|
||||||
|
|
||||||
//>= 100cm
|
//>= 100cm
|
||||||
uri = myServerBase + "/Observation?code-value-quantity=http://" + UrlUtil.escapeUrlParam("loinc.org|2345-7$gt100|http://unitsofmeasure.org|cm");
|
uri = myServerBase + "/Observation?code-value-quantity=http://" + UrlUtil.escapeUrlParam("loinc.org|2345-7$gt100|http://unitsofmeasure.org|cm");
|
||||||
ourLog.info("uri = " + uri);
|
ourLog.info("uri = {}", uri);
|
||||||
ids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
|
ids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
|
||||||
assertThat(ids).hasSize(3);
|
assertThat(ids).hasSize(3);
|
||||||
|
|
||||||
//>= 10dm
|
//>= 10dm
|
||||||
uri = myServerBase + "/Observation?code-value-quantity=http://" + UrlUtil.escapeUrlParam("loinc.org|2345-7$gt10|http://unitsofmeasure.org|dm");
|
uri = myServerBase + "/Observation?code-value-quantity=http://" + UrlUtil.escapeUrlParam("loinc.org|2345-7$gt10|http://unitsofmeasure.org|dm");
|
||||||
ourLog.info("uri = " + uri);
|
ourLog.info("uri = {}", uri);
|
||||||
ids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
|
ids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
|
||||||
assertThat(ids).hasSize(3);
|
assertThat(ids).hasSize(3);
|
||||||
}
|
}
|
||||||
|
@ -4381,7 +4359,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
myObservationDao.create(obs, mySrd);
|
myObservationDao.create(obs, mySrd);
|
||||||
|
|
||||||
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -4392,7 +4370,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
myObservationDao.create(obs, mySrd);
|
myObservationDao.create(obs, mySrd);
|
||||||
|
|
||||||
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -4403,7 +4381,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
myObservationDao.create(obs, mySrd);
|
myObservationDao.create(obs, mySrd);
|
||||||
|
|
||||||
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -4414,7 +4392,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
myObservationDao.create(obs, mySrd);
|
myObservationDao.create(obs, mySrd);
|
||||||
|
|
||||||
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
||||||
}
|
}
|
||||||
|
|
||||||
String uri;
|
String uri;
|
||||||
|
@ -4451,7 +4429,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
myObservationDao.create(obs, mySrd);
|
myObservationDao.create(obs, mySrd);
|
||||||
|
|
||||||
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -4462,7 +4440,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
myObservationDao.create(obs, mySrd);
|
myObservationDao.create(obs, mySrd);
|
||||||
|
|
||||||
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -4474,7 +4452,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
myObservationDao.create(obs, mySrd);
|
myObservationDao.create(obs, mySrd);
|
||||||
|
|
||||||
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
||||||
}
|
}
|
||||||
|
|
||||||
myCaptureQueriesListener.clear();
|
myCaptureQueriesListener.clear();
|
||||||
|
@ -4490,8 +4468,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
//-- check use normalized quantity table to search
|
//-- check use normalized quantity table to search
|
||||||
String searchSql = myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, true);
|
String searchSql = myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, true);
|
||||||
assertThat(searchSql).doesNotContain("HFJ_SPIDX_QUANTITY t0");
|
assertThat(searchSql).doesNotContain("HFJ_SPIDX_QUANTITY t0").contains("HFJ_SPIDX_QUANTITY_NRML");
|
||||||
assertThat(searchSql).contains("HFJ_SPIDX_QUANTITY_NRML");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -5044,7 +5021,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
oid1 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
|
oid1 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -5056,7 +5033,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
oid2 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
|
oid2 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -5068,7 +5045,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
oid3 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
|
oid3 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -5080,7 +5057,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
oid4 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
|
oid4 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
||||||
}
|
}
|
||||||
|
|
||||||
String uri = myServerBase + "/Observation?_sort=code-value-quantity";
|
String uri = myServerBase + "/Observation?_sort=code-value-quantity";
|
||||||
|
@ -5092,7 +5069,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
found = myFhirContext.newXmlParser().parseResource(Bundle.class, output);
|
found = myFhirContext.newXmlParser().parseResource(Bundle.class, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
ourLog.debug("Bundle: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(found));
|
ourLog.debug("Bundle: {}\n", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(found));
|
||||||
|
|
||||||
List<IIdType> list = toUnqualifiedVersionlessIds(found);
|
List<IIdType> list = toUnqualifiedVersionlessIds(found);
|
||||||
assertThat(found.getEntry()).hasSize(4);
|
assertThat(found.getEntry()).hasSize(4);
|
||||||
|
@ -5129,7 +5106,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
oid1 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
|
oid1 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -5145,7 +5122,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
oid2 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
|
oid2 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -5161,7 +5138,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
oid3 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
|
oid3 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -5176,7 +5153,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
comp.setValue(new Quantity().setValue(250));
|
comp.setValue(new Quantity().setValue(250));
|
||||||
oid4 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
|
oid4 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
||||||
}
|
}
|
||||||
|
|
||||||
String uri = myServerBase + "/Observation?_sort=combo-code-value-quantity";
|
String uri = myServerBase + "/Observation?_sort=combo-code-value-quantity";
|
||||||
|
@ -5188,7 +5165,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
found = myFhirContext.newXmlParser().parseResource(Bundle.class, output);
|
found = myFhirContext.newXmlParser().parseResource(Bundle.class, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
ourLog.debug("Bundle: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(found));
|
ourLog.debug("Bundle: {}\n", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(found));
|
||||||
|
|
||||||
List<IIdType> list = toUnqualifiedVersionlessIds(found);
|
List<IIdType> list = toUnqualifiedVersionlessIds(found);
|
||||||
assertThat(found.getEntry()).hasSize(4);
|
assertThat(found.getEntry()).hasSize(4);
|
||||||
|
@ -5264,9 +5241,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
List<IIdType> list = toUnqualifiedVersionlessIds(found);
|
List<IIdType> list = toUnqualifiedVersionlessIds(found);
|
||||||
ourLog.info(methodName + " found: " + list.toString() + " - Wanted " + orgMissing + " but not " + orgNotMissing);
|
ourLog.info(methodName + " found: " + list.toString() + " - Wanted " + orgMissing + " but not " + orgNotMissing);
|
||||||
assertThat(list).doesNotContain(orgNotMissing);
|
assertThat(list).doesNotContain(orgNotMissing).doesNotContain(deletedIdMissingTrue).contains(orgMissing);
|
||||||
assertThat(list).doesNotContain(deletedIdMissingTrue);
|
|
||||||
assertThat(list).contains(orgMissing);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -5927,7 +5902,6 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
}
|
}
|
||||||
|
|
||||||
Date before = new Date();
|
Date before = new Date();
|
||||||
Thread.sleep(100);
|
|
||||||
|
|
||||||
pt = new Patient();
|
pt = new Patient();
|
||||||
pt.setId(id.getIdPart());
|
pt.setId(id.getIdPart());
|
||||||
|
@ -6450,7 +6424,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
cc.addCoding().setCode("2345-7").setSystem("http://loinc.org");
|
cc.addCoding().setCode("2345-7").setSystem("http://loinc.org");
|
||||||
obs.setValue(new Quantity().setValueElement(new DecimalType(125.12)).setUnit("CM").setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL).setCode("cm"));
|
obs.setValue(new Quantity().setValueElement(new DecimalType(125.12)).setUnit("CM").setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL).setCode("cm"));
|
||||||
|
|
||||||
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
||||||
|
|
||||||
IIdType opid1 = myObservationDao.create(obs, mySrd).getId();
|
IIdType opid1 = myObservationDao.create(obs, mySrd).getId();
|
||||||
|
|
||||||
|
@ -6463,7 +6437,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
cc.addCoding().setCode("2345-7").setSystem("http://loinc.org");
|
cc.addCoding().setCode("2345-7").setSystem("http://loinc.org");
|
||||||
obs.setValue(new Quantity().setValueElement(new DecimalType(24.12)).setUnit("CM").setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL).setCode("cm"));
|
obs.setValue(new Quantity().setValueElement(new DecimalType(24.12)).setUnit("CM").setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL).setCode("cm"));
|
||||||
|
|
||||||
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
||||||
|
|
||||||
myObservationDao.update(obs, mySrd);
|
myObservationDao.update(obs, mySrd);
|
||||||
}
|
}
|
||||||
|
@ -6479,7 +6453,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
myObservationDao.create(obs, mySrd);
|
myObservationDao.create(obs, mySrd);
|
||||||
|
|
||||||
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -6492,7 +6466,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
myObservationDao.create(obs, mySrd);
|
myObservationDao.create(obs, mySrd);
|
||||||
|
|
||||||
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -6505,25 +6479,25 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
myObservationDao.create(obs, mySrd);
|
myObservationDao.create(obs, mySrd);
|
||||||
|
|
||||||
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
|
||||||
}
|
}
|
||||||
|
|
||||||
// > 1m
|
// > 1m
|
||||||
String uri = myServerBase + "/Observation?code-value-quantity=http://" + UrlUtil.escapeUrlParam("loinc.org|2345-7$gt1|http://unitsofmeasure.org|m");
|
String uri = myServerBase + "/Observation?code-value-quantity=http://" + UrlUtil.escapeUrlParam("loinc.org|2345-7$gt1|http://unitsofmeasure.org|m");
|
||||||
ourLog.info("uri = " + uri);
|
ourLog.info("uri = {}", uri);
|
||||||
List<String> ids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
|
List<String> ids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
|
||||||
assertThat(ids).hasSize(2);
|
assertThat(ids).hasSize(2);
|
||||||
|
|
||||||
|
|
||||||
//>= 100cm
|
//>= 100cm
|
||||||
uri = myServerBase + "/Observation?code-value-quantity=http://" + UrlUtil.escapeUrlParam("loinc.org|2345-7$gt100|http://unitsofmeasure.org|cm");
|
uri = myServerBase + "/Observation?code-value-quantity=http://" + UrlUtil.escapeUrlParam("loinc.org|2345-7$gt100|http://unitsofmeasure.org|cm");
|
||||||
ourLog.info("uri = " + uri);
|
ourLog.info("uri = {}", uri);
|
||||||
ids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
|
ids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
|
||||||
assertThat(ids).hasSize(2);
|
assertThat(ids).hasSize(2);
|
||||||
|
|
||||||
//>= 10dm
|
//>= 10dm
|
||||||
uri = myServerBase + "/Observation?code-value-quantity=http://" + UrlUtil.escapeUrlParam("loinc.org|2345-7$gt10|http://unitsofmeasure.org|dm");
|
uri = myServerBase + "/Observation?code-value-quantity=http://" + UrlUtil.escapeUrlParam("loinc.org|2345-7$gt10|http://unitsofmeasure.org|dm");
|
||||||
ourLog.info("uri = " + uri);
|
ourLog.info("uri = {}", uri);
|
||||||
ids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
|
ids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
|
||||||
assertThat(ids).hasSize(2);
|
assertThat(ids).hasSize(2);
|
||||||
}
|
}
|
||||||
|
@ -6540,7 +6514,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
patient.setBirthDateElement(new DateType("2073"));
|
patient.setBirthDateElement(new DateType("2073"));
|
||||||
pid0 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
|
pid0 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
ourLog.debug("Patient: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient));
|
ourLog.debug("Patient: {}\n", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient));
|
||||||
|
|
||||||
ourLog.info("pid0 " + pid0);
|
ourLog.info("pid0 " + pid0);
|
||||||
}
|
}
|
||||||
|
@ -6553,7 +6527,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||||
ourLog.info(resp);
|
ourLog.info(resp);
|
||||||
Bundle bundle = myFhirContext.newXmlParser().parseResource(Bundle.class, resp);
|
Bundle bundle = myFhirContext.newXmlParser().parseResource(Bundle.class, resp);
|
||||||
ourLog.debug("Patient: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle));
|
ourLog.debug("Patient: {}\n", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle));
|
||||||
}
|
}
|
||||||
|
|
||||||
uri = myServerBase + "/Patient?_total=accurate&birthdate=gt2072-01-01";
|
uri = myServerBase + "/Patient?_total=accurate&birthdate=gt2072-01-01";
|
||||||
|
@ -6564,7 +6538,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||||
ourLog.info(resp);
|
ourLog.info(resp);
|
||||||
Bundle bundle = myFhirContext.newXmlParser().parseResource(Bundle.class, resp);
|
Bundle bundle = myFhirContext.newXmlParser().parseResource(Bundle.class, resp);
|
||||||
ourLog.debug("Patient: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle));
|
ourLog.debug("Patient: {}\n", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -6995,9 +6969,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
&& theInput.IsEnforceRefOnType
|
&& theInput.IsEnforceRefOnType
|
||||||
&& theInput.IsEnforceRefOnWrite).isFalse();
|
&& theInput.IsEnforceRefOnWrite).isFalse();
|
||||||
} catch (InvalidRequestException ex) {
|
} catch (InvalidRequestException ex) {
|
||||||
assertThat(ex.getMessage().contains(
|
assertThat(ex.getMessage()).as(ex.getMessage()).contains("Invalid resource reference");
|
||||||
"Invalid resource reference"
|
|
||||||
)).as(ex.getMessage()).isTrue();
|
|
||||||
} finally {
|
} finally {
|
||||||
myStorageSettings.setEnforceReferentialIntegrityOnWrite(isEnforceRefOnWrite);
|
myStorageSettings.setEnforceReferentialIntegrityOnWrite(isEnforceRefOnWrite);
|
||||||
myStorageSettings.setEnforceReferenceTargetTypes(isEnforceRefTargetTypes);
|
myStorageSettings.setEnforceReferenceTargetTypes(isEnforceRefTargetTypes);
|
||||||
|
@ -7331,9 +7303,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
patient.setBirthDate(cal.getTime());
|
patient.setBirthDate(cal.getTime());
|
||||||
}
|
}
|
||||||
return patient;
|
return patient;
|
||||||
}, (isMissing) -> {
|
}, (isMissing) -> doSearch(Patient.class, Patient.BIRTHDATE.isMissing(isMissing)));
|
||||||
return doSearch(Patient.class, Patient.BIRTHDATE.isMissing(isMissing));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
|
@ -7346,9 +7316,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
patient.setGender(AdministrativeGender.FEMALE);
|
patient.setGender(AdministrativeGender.FEMALE);
|
||||||
}
|
}
|
||||||
return patient;
|
return patient;
|
||||||
}, isMissing -> {
|
}, isMissing -> doSearch(Patient.class, Patient.GENDER.isMissing(isMissing)));
|
||||||
return doSearch(Patient.class, Patient.GENDER.isMissing(isMissing));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
|
@ -7364,9 +7332,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
patient.setGeneralPractitioner(Collections.singletonList(new Reference(practitionerId)));
|
patient.setGeneralPractitioner(Collections.singletonList(new Reference(practitionerId)));
|
||||||
}
|
}
|
||||||
return patient;
|
return patient;
|
||||||
}, isMissing -> {
|
}, isMissing -> doSearch(Patient.class, Patient.GENERAL_PRACTITIONER.isMissing(isMissing)));
|
||||||
return doSearch(Patient.class, Patient.GENERAL_PRACTITIONER.isMissing(isMissing));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
|
@ -7409,9 +7375,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
sp.setUrl("http://example.com");
|
sp.setUrl("http://example.com");
|
||||||
}
|
}
|
||||||
return sp;
|
return sp;
|
||||||
}, isMissing -> {
|
}, isMissing -> doSearch(SearchParameter.class, SearchParameter.URL.isMissing(isMissing)));
|
||||||
return doSearch(SearchParameter.class, SearchParameter.URL.isMissing(isMissing));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
|
@ -7424,9 +7388,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
obs.setValue(new Quantity(3));
|
obs.setValue(new Quantity(3));
|
||||||
}
|
}
|
||||||
return obs;
|
return obs;
|
||||||
}, isMissing -> {
|
}, isMissing -> doSearch(Observation.class, Observation.VALUE_QUANTITY.isMissing(isMissing)));
|
||||||
return doSearch(Observation.class, Observation.VALUE_QUANTITY.isMissing(isMissing));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
|
@ -7457,7 +7419,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
Y doTask(X theInput);
|
Y doTask(X theInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class MissingSearchTestParameters {
|
public static class MissingSearchTestParameters {
|
||||||
/**
|
/**
|
||||||
* The setting for IndexMissingFields
|
* The setting for IndexMissingFields
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -79,25 +79,11 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.simplejavamail</groupId>
|
<groupId>org.simplejavamail</groupId>
|
||||||
<artifactId>simple-java-mail</artifactId>
|
<artifactId>simple-java-mail</artifactId>
|
||||||
<!-- Excluded in favor of jakarta.activation:jakarta.activation-api -->
|
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>com.sun.activation</groupId>
|
|
||||||
<artifactId>jakarta.activation</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.icegreen</groupId>
|
<groupId>com.icegreen</groupId>
|
||||||
<artifactId>greenmail</artifactId>
|
<artifactId>greenmail</artifactId>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
<!-- Excluded in favor of jakarta.activation:jakarta.activation-api -->
|
|
||||||
<exclusions>
|
|
||||||
<exclusion>
|
|
||||||
<groupId>com.sun.activation</groupId>
|
|
||||||
<artifactId>jakarta.activation</artifactId>
|
|
||||||
</exclusion>
|
|
||||||
</exclusions>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|
|
@ -20,10 +20,13 @@
|
||||||
package ca.uhn.hapi.fhir.cdshooks.svc.cr;
|
package ca.uhn.hapi.fhir.cdshooks.svc.cr;
|
||||||
|
|
||||||
public class CdsCrConstants {
|
public class CdsCrConstants {
|
||||||
private CdsCrConstants() {}
|
|
||||||
|
|
||||||
public static final String CDS_CR_MODULE_ID = "CR";
|
public static final String CDS_CR_MODULE_ID = "CR";
|
||||||
|
|
||||||
|
public static final String CRMI_EFFECTIVE_DATA_REQUIREMENTS =
|
||||||
|
"http://hl7.org/fhir/uv/crmi/StructureDefinition/crmi-effectiveDataRequirements";
|
||||||
|
|
||||||
|
public static final String CQF_FHIR_QUERY_PATTERN = "http://hl7.org/fhir/StructureDefinition/cqf-fhirQueryPattern";
|
||||||
|
|
||||||
// CDS Hook field names
|
// CDS Hook field names
|
||||||
public static final String CDS_PARAMETER_USER_ID = "userId";
|
public static final String CDS_PARAMETER_USER_ID = "userId";
|
||||||
public static final String CDS_PARAMETER_PATIENT_ID = "patientId";
|
public static final String CDS_PARAMETER_PATIENT_ID = "patientId";
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - CDS Hooks
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2024 Smile CDR, Inc.
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
package ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery;
|
||||||
|
|
||||||
|
import org.opencds.cqf.fhir.api.Repository;
|
||||||
|
|
||||||
|
public abstract class BasePrefetchTemplateBuilder {
|
||||||
|
protected final String PATIENT_ID_CONTEXT = "{{context.patientId}}";
|
||||||
|
protected final int DEFAULT_MAX_URI_LENGTH = 8000;
|
||||||
|
protected int myMaxUriLength;
|
||||||
|
|
||||||
|
protected final Repository myRepository;
|
||||||
|
|
||||||
|
public BasePrefetchTemplateBuilder(Repository theRepository) {
|
||||||
|
myRepository = theRepository;
|
||||||
|
myMaxUriLength = DEFAULT_MAX_URI_LENGTH;
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,32 +23,24 @@ import ca.uhn.fhir.context.FhirVersionEnum;
|
||||||
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson;
|
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson;
|
||||||
import ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrUtils;
|
import ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrUtils;
|
||||||
import org.hl7.fhir.dstu3.model.Coding;
|
import org.hl7.fhir.dstu3.model.Coding;
|
||||||
import org.hl7.fhir.dstu3.model.DataRequirement;
|
|
||||||
import org.hl7.fhir.dstu3.model.Library;
|
|
||||||
import org.hl7.fhir.dstu3.model.PlanDefinition;
|
import org.hl7.fhir.dstu3.model.PlanDefinition;
|
||||||
import org.hl7.fhir.dstu3.model.StringType;
|
import org.hl7.fhir.dstu3.model.TriggerDefinition;
|
||||||
import org.hl7.fhir.dstu3.model.ValueSet;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.opencds.cqf.fhir.api.Repository;
|
import org.opencds.cqf.fhir.api.Repository;
|
||||||
import org.opencds.cqf.fhir.utility.dstu3.SearchHelper;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.stream.Collectors;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class CrDiscoveryServiceDstu3 implements ICrDiscoveryService {
|
public class CrDiscoveryServiceDstu3 implements ICrDiscoveryService {
|
||||||
|
|
||||||
protected final String PATIENT_ID_CONTEXT = "{{context.patientId}}";
|
protected final Repository myRepository;
|
||||||
protected final int DEFAULT_MAX_URI_LENGTH = 8000;
|
|
||||||
protected int myMaxUriLength;
|
|
||||||
|
|
||||||
protected Repository myRepository;
|
|
||||||
protected final IIdType myPlanDefinitionId;
|
protected final IIdType myPlanDefinitionId;
|
||||||
|
protected final PrefetchTemplateBuilderDstu3 myPrefetchTemplateBuilder;
|
||||||
|
|
||||||
public CrDiscoveryServiceDstu3(IIdType thePlanDefinitionId, Repository theRepository) {
|
public CrDiscoveryServiceDstu3(IIdType thePlanDefinitionId, Repository theRepository) {
|
||||||
myPlanDefinitionId = thePlanDefinitionId;
|
myPlanDefinitionId = thePlanDefinitionId;
|
||||||
myRepository = theRepository;
|
myRepository = theRepository;
|
||||||
myMaxUriLength = DEFAULT_MAX_URI_LENGTH;
|
myPrefetchTemplateBuilder = new PrefetchTemplateBuilderDstu3(myRepository);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CdsServiceJson resolveService() {
|
public CdsServiceJson resolveService() {
|
||||||
|
@ -59,12 +51,36 @@ public class CrDiscoveryServiceDstu3 implements ICrDiscoveryService {
|
||||||
protected CdsServiceJson resolveService(IBaseResource thePlanDefinition) {
|
protected CdsServiceJson resolveService(IBaseResource thePlanDefinition) {
|
||||||
if (thePlanDefinition instanceof PlanDefinition) {
|
if (thePlanDefinition instanceof PlanDefinition) {
|
||||||
PlanDefinition planDef = (PlanDefinition) thePlanDefinition;
|
PlanDefinition planDef = (PlanDefinition) thePlanDefinition;
|
||||||
return new CrDiscoveryElementDstu3(planDef, getPrefetchUrlList(planDef)).getCdsServiceJson();
|
String triggerEvent = getTriggerEvent(planDef);
|
||||||
|
if (triggerEvent != null) {
|
||||||
|
PrefetchUrlList prefetchUrlList =
|
||||||
|
isEca(planDef) ? myPrefetchTemplateBuilder.getPrefetchUrlList(planDef) : new PrefetchUrlList();
|
||||||
|
return new CrDiscoveryElementDstu3(planDef, prefetchUrlList).getCdsServiceJson();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEca(PlanDefinition thePlanDefinition) {
|
protected String getTriggerEvent(PlanDefinition thePlanDefinition) {
|
||||||
|
if (thePlanDefinition == null
|
||||||
|
|| !thePlanDefinition.hasAction()
|
||||||
|
|| thePlanDefinition.getAction().stream().noneMatch(a -> a.hasTriggerDefinition())) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var triggerDefs = thePlanDefinition.getAction().stream()
|
||||||
|
.filter(a -> a.hasTriggerDefinition())
|
||||||
|
.flatMap(a -> a.getTriggerDefinition().stream())
|
||||||
|
.filter(t -> t.getType().equals(TriggerDefinition.TriggerType.NAMEDEVENT))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (triggerDefs == null || triggerDefs.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return triggerDefs.get(0).getEventName();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isEca(PlanDefinition thePlanDefinition) {
|
||||||
if (thePlanDefinition.hasType() && thePlanDefinition.getType().hasCoding()) {
|
if (thePlanDefinition.hasType() && thePlanDefinition.getType().hasCoding()) {
|
||||||
for (Coding coding : thePlanDefinition.getType().getCoding()) {
|
for (Coding coding : thePlanDefinition.getType().getCoding()) {
|
||||||
if (coding.getCode().equals("eca-rule")) {
|
if (coding.getCode().equals("eca-rule")) {
|
||||||
|
@ -74,372 +90,4 @@ public class CrDiscoveryServiceDstu3 implements ICrDiscoveryService {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Library resolvePrimaryLibrary(PlanDefinition thePlanDefinition) {
|
|
||||||
// Assuming 1 library
|
|
||||||
// TODO: enhance to handle multiple libraries - need a way to identify primary
|
|
||||||
// library
|
|
||||||
Library library = null;
|
|
||||||
if (thePlanDefinition.hasLibrary()
|
|
||||||
&& thePlanDefinition.getLibraryFirstRep().hasReference()) {
|
|
||||||
library = myRepository.read(
|
|
||||||
Library.class, thePlanDefinition.getLibraryFirstRep().getReferenceElement());
|
|
||||||
}
|
|
||||||
return library;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> resolveValueCodingCodes(List<Coding> theValueCodings) {
|
|
||||||
List<String> result = new ArrayList<>();
|
|
||||||
|
|
||||||
StringBuilder codes = new StringBuilder();
|
|
||||||
for (Coding coding : theValueCodings) {
|
|
||||||
if (coding.hasCode()) {
|
|
||||||
String system = coding.getSystem();
|
|
||||||
String code = coding.getCode();
|
|
||||||
|
|
||||||
codes = getCodesStringBuilder(result, codes, system, code);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result.add(codes.toString());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> resolveValueSetCodes(StringType theValueSetId) {
|
|
||||||
ValueSet valueSet = (ValueSet) SearchHelper.searchRepositoryByCanonical(myRepository, theValueSetId);
|
|
||||||
List<String> result = new ArrayList<>();
|
|
||||||
StringBuilder codes = new StringBuilder();
|
|
||||||
if (valueSet.hasExpansion() && valueSet.getExpansion().hasContains()) {
|
|
||||||
for (ValueSet.ValueSetExpansionContainsComponent contains :
|
|
||||||
valueSet.getExpansion().getContains()) {
|
|
||||||
String system = contains.getSystem();
|
|
||||||
String code = contains.getCode();
|
|
||||||
|
|
||||||
codes = getCodesStringBuilder(result, codes, system, code);
|
|
||||||
}
|
|
||||||
} else if (valueSet.hasCompose() && valueSet.getCompose().hasInclude()) {
|
|
||||||
for (ValueSet.ConceptSetComponent concepts : valueSet.getCompose().getInclude()) {
|
|
||||||
String system = concepts.getSystem();
|
|
||||||
if (concepts.hasConcept()) {
|
|
||||||
for (ValueSet.ConceptReferenceComponent concept : concepts.getConcept()) {
|
|
||||||
String code = concept.getCode();
|
|
||||||
|
|
||||||
codes = getCodesStringBuilder(result, codes, system, code);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result.add(codes.toString());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected StringBuilder getCodesStringBuilder(
|
|
||||||
List<String> theList, StringBuilder theCodes, String theSystem, String theCode) {
|
|
||||||
String codeToken = theSystem + "|" + theCode;
|
|
||||||
int postAppendLength = theCodes.length() + codeToken.length();
|
|
||||||
|
|
||||||
if (theCodes.length() > 0 && postAppendLength < myMaxUriLength) {
|
|
||||||
theCodes.append(",");
|
|
||||||
} else if (postAppendLength > myMaxUriLength) {
|
|
||||||
theList.add(theCodes.toString());
|
|
||||||
theCodes = new StringBuilder();
|
|
||||||
}
|
|
||||||
theCodes.append(codeToken);
|
|
||||||
return theCodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> createRequestUrl(DataRequirement theDataRequirement) {
|
|
||||||
if (!isPatientCompartment(theDataRequirement.getType())) return null;
|
|
||||||
String patientRelatedResource = theDataRequirement.getType() + "?"
|
|
||||||
+ getPatientSearchParam(theDataRequirement.getType())
|
|
||||||
+ "=Patient/" + PATIENT_ID_CONTEXT;
|
|
||||||
List<String> ret = new ArrayList<>();
|
|
||||||
if (theDataRequirement.hasCodeFilter()) {
|
|
||||||
for (DataRequirement.DataRequirementCodeFilterComponent codeFilterComponent :
|
|
||||||
theDataRequirement.getCodeFilter()) {
|
|
||||||
if (!codeFilterComponent.hasPath()) continue;
|
|
||||||
String path = mapCodePathToSearchParam(theDataRequirement.getType(), codeFilterComponent.getPath());
|
|
||||||
|
|
||||||
StringType codeFilterComponentString = null;
|
|
||||||
if (codeFilterComponent.hasValueSetStringType()) {
|
|
||||||
codeFilterComponentString = codeFilterComponent.getValueSetStringType();
|
|
||||||
} else if (codeFilterComponent.hasValueSetReference()) {
|
|
||||||
codeFilterComponentString = new StringType(
|
|
||||||
codeFilterComponent.getValueSetReference().getReference());
|
|
||||||
} else if (codeFilterComponent.hasValueCoding()) {
|
|
||||||
List<Coding> codeFilterValueCodings = codeFilterComponent.getValueCoding();
|
|
||||||
boolean isFirstCodingInFilter = true;
|
|
||||||
for (String code : resolveValueCodingCodes(codeFilterValueCodings)) {
|
|
||||||
if (isFirstCodingInFilter) {
|
|
||||||
ret.add(patientRelatedResource + "&" + path + "=" + code);
|
|
||||||
} else {
|
|
||||||
ret.add("," + code);
|
|
||||||
}
|
|
||||||
|
|
||||||
isFirstCodingInFilter = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (codeFilterComponentString != null) {
|
|
||||||
for (String codes : resolveValueSetCodes(codeFilterComponentString)) {
|
|
||||||
ret.add(patientRelatedResource + "&" + path + "=" + codes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
} else {
|
|
||||||
ret.add(patientRelatedResource);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public PrefetchUrlList getPrefetchUrlList(PlanDefinition thePlanDefinition) {
|
|
||||||
PrefetchUrlList prefetchList = new PrefetchUrlList();
|
|
||||||
if (thePlanDefinition == null) return null;
|
|
||||||
if (!isEca(thePlanDefinition)) return null;
|
|
||||||
Library library = resolvePrimaryLibrary(thePlanDefinition);
|
|
||||||
// TODO: resolve data requirements
|
|
||||||
if (!library.hasDataRequirement()) return null;
|
|
||||||
for (DataRequirement dataRequirement : library.getDataRequirement()) {
|
|
||||||
List<String> requestUrls = createRequestUrl(dataRequirement);
|
|
||||||
if (requestUrls != null) {
|
|
||||||
prefetchList.addAll(requestUrls);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return prefetchList;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String mapCodePathToSearchParam(String theDataType, String thePath) {
|
|
||||||
switch (theDataType) {
|
|
||||||
case "MedicationAdministration":
|
|
||||||
if (thePath.equals("medication")) return "code";
|
|
||||||
break;
|
|
||||||
case "MedicationDispense":
|
|
||||||
if (thePath.equals("medication")) return "code";
|
|
||||||
break;
|
|
||||||
case "MedicationRequest":
|
|
||||||
if (thePath.equals("medication")) return "code";
|
|
||||||
break;
|
|
||||||
case "MedicationStatement":
|
|
||||||
if (thePath.equals("medication")) return "code";
|
|
||||||
break;
|
|
||||||
case "ProcedureRequest":
|
|
||||||
if (thePath.equals("bodySite")) return "body-site";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (thePath.equals("vaccineCode")) return "vaccine-code";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return thePath.replace('.', '-').toLowerCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isPatientCompartment(String theDataType) {
|
|
||||||
if (theDataType == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
switch (theDataType) {
|
|
||||||
case "Account":
|
|
||||||
case "AdverseEvent":
|
|
||||||
case "AllergyIntolerance":
|
|
||||||
case "Appointment":
|
|
||||||
case "AppointmentResponse":
|
|
||||||
case "AuditEvent":
|
|
||||||
case "Basic":
|
|
||||||
case "BodySite":
|
|
||||||
case "CarePlan":
|
|
||||||
case "CareTeam":
|
|
||||||
case "ChargeItem":
|
|
||||||
case "Claim":
|
|
||||||
case "ClaimResponse":
|
|
||||||
case "ClinicalImpression":
|
|
||||||
case "Communication":
|
|
||||||
case "CommunicationRequest":
|
|
||||||
case "Composition":
|
|
||||||
case "Condition":
|
|
||||||
case "Consent":
|
|
||||||
case "Coverage":
|
|
||||||
case "DetectedIssue":
|
|
||||||
case "DeviceRequest":
|
|
||||||
case "DeviceUseStatement":
|
|
||||||
case "DiagnosticReport":
|
|
||||||
case "DocumentManifest":
|
|
||||||
case "EligibilityRequest":
|
|
||||||
case "Encounter":
|
|
||||||
case "EnrollmentRequest":
|
|
||||||
case "EpisodeOfCare":
|
|
||||||
case "ExplanationOfBenefit":
|
|
||||||
case "FamilyMemberHistory":
|
|
||||||
case "Flag":
|
|
||||||
case "Goal":
|
|
||||||
case "Group":
|
|
||||||
case "ImagingManifest":
|
|
||||||
case "ImagingStudy":
|
|
||||||
case "Immunization":
|
|
||||||
case "ImmunizationRecommendation":
|
|
||||||
case "List":
|
|
||||||
case "MeasureReport":
|
|
||||||
case "Media":
|
|
||||||
case "MedicationAdministration":
|
|
||||||
case "MedicationDispense":
|
|
||||||
case "MedicationRequest":
|
|
||||||
case "MedicationStatement":
|
|
||||||
case "NutritionOrder":
|
|
||||||
case "Observation":
|
|
||||||
case "Patient":
|
|
||||||
case "Person":
|
|
||||||
case "Procedure":
|
|
||||||
case "ProcedureRequest":
|
|
||||||
case "Provenance":
|
|
||||||
case "QuestionnaireResponse":
|
|
||||||
case "ReferralRequest":
|
|
||||||
case "RelatedPerson":
|
|
||||||
case "RequestGroup":
|
|
||||||
case "ResearchSubject":
|
|
||||||
case "RiskAssessment":
|
|
||||||
case "Schedule":
|
|
||||||
case "Specimen":
|
|
||||||
case "SupplyDelivery":
|
|
||||||
case "SupplyRequest":
|
|
||||||
case "VisionPrescription":
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPatientSearchParam(String theDataType) {
|
|
||||||
switch (theDataType) {
|
|
||||||
case "Account":
|
|
||||||
return "subject";
|
|
||||||
case "AdverseEvent":
|
|
||||||
return "subject";
|
|
||||||
case "AllergyIntolerance":
|
|
||||||
return "patient";
|
|
||||||
case "Appointment":
|
|
||||||
return "actor";
|
|
||||||
case "AppointmentResponse":
|
|
||||||
return "actor";
|
|
||||||
case "AuditEvent":
|
|
||||||
return "patient";
|
|
||||||
case "Basic":
|
|
||||||
return "patient";
|
|
||||||
case "BodySite":
|
|
||||||
return "patient";
|
|
||||||
case "CarePlan":
|
|
||||||
return "patient";
|
|
||||||
case "CareTeam":
|
|
||||||
return "patient";
|
|
||||||
case "ChargeItem":
|
|
||||||
return "subject";
|
|
||||||
case "Claim":
|
|
||||||
return "patient";
|
|
||||||
case "ClaimResponse":
|
|
||||||
return "patient";
|
|
||||||
case "ClinicalImpression":
|
|
||||||
return "subject";
|
|
||||||
case "Communication":
|
|
||||||
return "subject";
|
|
||||||
case "CommunicationRequest":
|
|
||||||
return "subject";
|
|
||||||
case "Composition":
|
|
||||||
return "subject";
|
|
||||||
case "Condition":
|
|
||||||
return "patient";
|
|
||||||
case "Consent":
|
|
||||||
return "patient";
|
|
||||||
case "Coverage":
|
|
||||||
return "patient";
|
|
||||||
case "DetectedIssue":
|
|
||||||
return "patient";
|
|
||||||
case "DeviceRequest":
|
|
||||||
return "subject";
|
|
||||||
case "DeviceUseStatement":
|
|
||||||
return "subject";
|
|
||||||
case "DiagnosticReport":
|
|
||||||
return "subject";
|
|
||||||
case "DocumentManifest":
|
|
||||||
return "subject";
|
|
||||||
case "DocumentReference":
|
|
||||||
return "subject";
|
|
||||||
case "EligibilityRequest":
|
|
||||||
return "patient";
|
|
||||||
case "Encounter":
|
|
||||||
return "patient";
|
|
||||||
case "EnrollmentRequest":
|
|
||||||
return "subject";
|
|
||||||
case "EpisodeOfCare":
|
|
||||||
return "patient";
|
|
||||||
case "ExplanationOfBenefit":
|
|
||||||
return "patient";
|
|
||||||
case "FamilyMemberHistory":
|
|
||||||
return "patient";
|
|
||||||
case "Flag":
|
|
||||||
return "patient";
|
|
||||||
case "Goal":
|
|
||||||
return "patient";
|
|
||||||
case "Group":
|
|
||||||
return "member";
|
|
||||||
case "ImagingManifest":
|
|
||||||
return "patient";
|
|
||||||
case "ImagingStudy":
|
|
||||||
return "patient";
|
|
||||||
case "Immunization":
|
|
||||||
return "patient";
|
|
||||||
case "ImmunizationRecommendation":
|
|
||||||
return "patient";
|
|
||||||
case "List":
|
|
||||||
return "subject";
|
|
||||||
case "MeasureReport":
|
|
||||||
return "patient";
|
|
||||||
case "Media":
|
|
||||||
return "subject";
|
|
||||||
case "MedicationAdministration":
|
|
||||||
return "patient";
|
|
||||||
case "MedicationDispense":
|
|
||||||
return "patient";
|
|
||||||
case "MedicationRequest":
|
|
||||||
return "subject";
|
|
||||||
case "MedicationStatement":
|
|
||||||
return "subject";
|
|
||||||
case "NutritionOrder":
|
|
||||||
return "patient";
|
|
||||||
case "Observation":
|
|
||||||
return "subject";
|
|
||||||
case "Patient":
|
|
||||||
return "_id";
|
|
||||||
case "Person":
|
|
||||||
return "patient";
|
|
||||||
case "Procedure":
|
|
||||||
return "patient";
|
|
||||||
case "ProcedureRequest":
|
|
||||||
return "patient";
|
|
||||||
case "Provenance":
|
|
||||||
return "patient";
|
|
||||||
case "QuestionnaireResponse":
|
|
||||||
return "subject";
|
|
||||||
case "ReferralRequest":
|
|
||||||
return "patient";
|
|
||||||
case "RelatedPerson":
|
|
||||||
return "patient";
|
|
||||||
case "RequestGroup":
|
|
||||||
return "subject";
|
|
||||||
case "ResearchSubject":
|
|
||||||
return "individual";
|
|
||||||
case "RiskAssessment":
|
|
||||||
return "subject";
|
|
||||||
case "Schedule":
|
|
||||||
return "actor";
|
|
||||||
case "Specimen":
|
|
||||||
return "subject";
|
|
||||||
case "SupplyDelivery":
|
|
||||||
return "patient";
|
|
||||||
case "SupplyRequest":
|
|
||||||
return "subject";
|
|
||||||
case "VisionPrescription":
|
|
||||||
return "patient";
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,31 +24,23 @@ import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson;
|
||||||
import ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrUtils;
|
import ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrUtils;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
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.DataRequirement;
|
|
||||||
import org.hl7.fhir.r4.model.Library;
|
|
||||||
import org.hl7.fhir.r4.model.PlanDefinition;
|
import org.hl7.fhir.r4.model.PlanDefinition;
|
||||||
import org.hl7.fhir.r4.model.ValueSet;
|
import org.hl7.fhir.r4.model.TriggerDefinition;
|
||||||
import org.opencds.cqf.fhir.api.Repository;
|
import org.opencds.cqf.fhir.api.Repository;
|
||||||
import org.opencds.cqf.fhir.utility.r4.SearchHelper;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.stream.Collectors;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class CrDiscoveryServiceR4 implements ICrDiscoveryService {
|
public class CrDiscoveryServiceR4 implements ICrDiscoveryService {
|
||||||
|
|
||||||
protected final String PATIENT_ID_CONTEXT = "{{context.patientId}}";
|
|
||||||
protected final int DEFAULT_MAX_URI_LENGTH = 8000;
|
|
||||||
protected int myMaxUriLength;
|
|
||||||
|
|
||||||
protected final Repository myRepository;
|
protected final Repository myRepository;
|
||||||
protected final IIdType myPlanDefinitionId;
|
protected final IIdType myPlanDefinitionId;
|
||||||
|
protected final PrefetchTemplateBuilderR4 myPrefetchTemplateBuilder;
|
||||||
|
|
||||||
public CrDiscoveryServiceR4(IIdType thePlanDefinitionId, Repository theRepository) {
|
public CrDiscoveryServiceR4(IIdType thePlanDefinitionId, Repository theRepository) {
|
||||||
myPlanDefinitionId = thePlanDefinitionId;
|
myPlanDefinitionId = thePlanDefinitionId;
|
||||||
myRepository = theRepository;
|
myRepository = theRepository;
|
||||||
myMaxUriLength = DEFAULT_MAX_URI_LENGTH;
|
myPrefetchTemplateBuilder = new PrefetchTemplateBuilderR4(myRepository);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CdsServiceJson resolveService() {
|
public CdsServiceJson resolveService() {
|
||||||
|
@ -59,12 +51,36 @@ public class CrDiscoveryServiceR4 implements ICrDiscoveryService {
|
||||||
protected CdsServiceJson resolveService(IBaseResource thePlanDefinition) {
|
protected CdsServiceJson resolveService(IBaseResource thePlanDefinition) {
|
||||||
if (thePlanDefinition instanceof PlanDefinition) {
|
if (thePlanDefinition instanceof PlanDefinition) {
|
||||||
PlanDefinition planDef = (PlanDefinition) thePlanDefinition;
|
PlanDefinition planDef = (PlanDefinition) thePlanDefinition;
|
||||||
return new CrDiscoveryElementR4(planDef, getPrefetchUrlList(planDef)).getCdsServiceJson();
|
String triggerEvent = getTriggerEvent(planDef);
|
||||||
|
if (triggerEvent != null) {
|
||||||
|
PrefetchUrlList prefetchUrlList =
|
||||||
|
isEca(planDef) ? myPrefetchTemplateBuilder.getPrefetchUrlList(planDef) : new PrefetchUrlList();
|
||||||
|
return new CrDiscoveryElementR4(planDef, prefetchUrlList).getCdsServiceJson();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEca(PlanDefinition planDefinition) {
|
protected String getTriggerEvent(PlanDefinition thePlanDefinition) {
|
||||||
|
if (thePlanDefinition == null
|
||||||
|
|| !thePlanDefinition.hasAction()
|
||||||
|
|| thePlanDefinition.getAction().stream().noneMatch(a -> a.hasTrigger())) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var triggerDefs = thePlanDefinition.getAction().stream()
|
||||||
|
.filter(a -> a.hasTrigger())
|
||||||
|
.flatMap(a -> a.getTrigger().stream())
|
||||||
|
.filter(t -> t.getType().equals(TriggerDefinition.TriggerType.NAMEDEVENT))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (triggerDefs == null || triggerDefs.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return triggerDefs.get(0).getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isEca(PlanDefinition planDefinition) {
|
||||||
if (planDefinition.hasType() && planDefinition.getType().hasCoding()) {
|
if (planDefinition.hasType() && planDefinition.getType().hasCoding()) {
|
||||||
for (Coding coding : planDefinition.getType().getCoding()) {
|
for (Coding coding : planDefinition.getType().getCoding()) {
|
||||||
if (coding.getCode().equals("eca-rule")) {
|
if (coding.getCode().equals("eca-rule")) {
|
||||||
|
@ -74,356 +90,4 @@ public class CrDiscoveryServiceR4 implements ICrDiscoveryService {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Library resolvePrimaryLibrary(PlanDefinition thePlanDefinition) {
|
|
||||||
// The CPGComputablePlanDefinition profile limits the cardinality of library to 1
|
|
||||||
Library library = null;
|
|
||||||
if (thePlanDefinition.hasLibrary() && !thePlanDefinition.getLibrary().isEmpty()) {
|
|
||||||
library = (Library) SearchHelper.searchRepositoryByCanonical(
|
|
||||||
myRepository, thePlanDefinition.getLibrary().get(0));
|
|
||||||
}
|
|
||||||
return library;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> resolveValueCodingCodes(List<Coding> valueCodings) {
|
|
||||||
List<String> result = new ArrayList<>();
|
|
||||||
|
|
||||||
StringBuilder codes = new StringBuilder();
|
|
||||||
for (Coding coding : valueCodings) {
|
|
||||||
if (coding.hasCode()) {
|
|
||||||
String system = coding.getSystem();
|
|
||||||
String code = coding.getCode();
|
|
||||||
|
|
||||||
codes = getCodesStringBuilder(result, codes, system, code);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result.add(codes.toString());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> resolveValueSetCodes(CanonicalType valueSetId) {
|
|
||||||
ValueSet valueSet = (ValueSet) SearchHelper.searchRepositoryByCanonical(myRepository, valueSetId);
|
|
||||||
List<String> result = new ArrayList<>();
|
|
||||||
StringBuilder codes = new StringBuilder();
|
|
||||||
if (valueSet.hasExpansion() && valueSet.getExpansion().hasContains()) {
|
|
||||||
for (ValueSet.ValueSetExpansionContainsComponent contains :
|
|
||||||
valueSet.getExpansion().getContains()) {
|
|
||||||
String system = contains.getSystem();
|
|
||||||
String code = contains.getCode();
|
|
||||||
|
|
||||||
codes = getCodesStringBuilder(result, codes, system, code);
|
|
||||||
}
|
|
||||||
} else if (valueSet.hasCompose() && valueSet.getCompose().hasInclude()) {
|
|
||||||
for (ValueSet.ConceptSetComponent concepts : valueSet.getCompose().getInclude()) {
|
|
||||||
String system = concepts.getSystem();
|
|
||||||
if (concepts.hasConcept()) {
|
|
||||||
for (ValueSet.ConceptReferenceComponent concept : concepts.getConcept()) {
|
|
||||||
String code = concept.getCode();
|
|
||||||
|
|
||||||
codes = getCodesStringBuilder(result, codes, system, code);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result.add(codes.toString());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected StringBuilder getCodesStringBuilder(List<String> ret, StringBuilder codes, String system, String code) {
|
|
||||||
String codeToken = system + "|" + code;
|
|
||||||
int postAppendLength = codes.length() + codeToken.length();
|
|
||||||
|
|
||||||
if (codes.length() > 0 && postAppendLength < myMaxUriLength) {
|
|
||||||
codes.append(",");
|
|
||||||
} else if (postAppendLength > myMaxUriLength) {
|
|
||||||
ret.add(codes.toString());
|
|
||||||
codes = new StringBuilder();
|
|
||||||
}
|
|
||||||
codes.append(codeToken);
|
|
||||||
return codes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> createRequestUrl(DataRequirement theDataRequirement) {
|
|
||||||
if (!isPatientCompartment(theDataRequirement.getType())) return null;
|
|
||||||
String patientRelatedResource = theDataRequirement.getType() + "?"
|
|
||||||
+ getPatientSearchParam(theDataRequirement.getType())
|
|
||||||
+ "=Patient/" + PATIENT_ID_CONTEXT;
|
|
||||||
List<String> ret = new ArrayList<>();
|
|
||||||
if (theDataRequirement.hasCodeFilter()) {
|
|
||||||
for (DataRequirement.DataRequirementCodeFilterComponent codeFilterComponent :
|
|
||||||
theDataRequirement.getCodeFilter()) {
|
|
||||||
if (!codeFilterComponent.hasPath()) continue;
|
|
||||||
String path = mapCodePathToSearchParam(theDataRequirement.getType(), codeFilterComponent.getPath());
|
|
||||||
if (codeFilterComponent.hasValueSetElement()) {
|
|
||||||
for (String codes : resolveValueSetCodes(codeFilterComponent.getValueSetElement())) {
|
|
||||||
ret.add(patientRelatedResource + "&" + path + "=" + codes);
|
|
||||||
}
|
|
||||||
} else if (codeFilterComponent.hasCode()) {
|
|
||||||
List<Coding> codeFilterValueCodings = codeFilterComponent.getCode();
|
|
||||||
boolean isFirstCodingInFilter = true;
|
|
||||||
for (String code : resolveValueCodingCodes(codeFilterValueCodings)) {
|
|
||||||
if (isFirstCodingInFilter) {
|
|
||||||
ret.add(patientRelatedResource + "&" + path + "=" + code);
|
|
||||||
} else {
|
|
||||||
ret.add("," + code);
|
|
||||||
}
|
|
||||||
|
|
||||||
isFirstCodingInFilter = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
} else {
|
|
||||||
ret.add(patientRelatedResource);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public PrefetchUrlList getPrefetchUrlList(PlanDefinition thePlanDefinition) {
|
|
||||||
PrefetchUrlList prefetchList = new PrefetchUrlList();
|
|
||||||
if (thePlanDefinition == null) return null;
|
|
||||||
if (!isEca(thePlanDefinition)) return null;
|
|
||||||
Library library = resolvePrimaryLibrary(thePlanDefinition);
|
|
||||||
// TODO: resolve data requirements
|
|
||||||
if (library == null || !library.hasDataRequirement()) return null;
|
|
||||||
for (DataRequirement dataRequirement : library.getDataRequirement()) {
|
|
||||||
List<String> requestUrls = createRequestUrl(dataRequirement);
|
|
||||||
if (requestUrls != null) {
|
|
||||||
prefetchList.addAll(requestUrls);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return prefetchList;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String mapCodePathToSearchParam(String theDataType, String thePath) {
|
|
||||||
switch (theDataType) {
|
|
||||||
case "MedicationAdministration":
|
|
||||||
if (thePath.equals("medication")) return "code";
|
|
||||||
break;
|
|
||||||
case "MedicationDispense":
|
|
||||||
if (thePath.equals("medication")) return "code";
|
|
||||||
break;
|
|
||||||
case "MedicationRequest":
|
|
||||||
if (thePath.equals("medication")) return "code";
|
|
||||||
break;
|
|
||||||
case "MedicationStatement":
|
|
||||||
if (thePath.equals("medication")) return "code";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (thePath.equals("vaccineCode")) return "vaccine-code";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return thePath.replace('.', '-').toLowerCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isPatientCompartment(String theDataType) {
|
|
||||||
if (theDataType == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
switch (theDataType) {
|
|
||||||
case "Account":
|
|
||||||
case "AdverseEvent":
|
|
||||||
case "AllergyIntolerance":
|
|
||||||
case "Appointment":
|
|
||||||
case "AppointmentResponse":
|
|
||||||
case "AuditEvent":
|
|
||||||
case "Basic":
|
|
||||||
case "BodyStructure":
|
|
||||||
case "CarePlan":
|
|
||||||
case "CareTeam":
|
|
||||||
case "ChargeItem":
|
|
||||||
case "Claim":
|
|
||||||
case "ClaimResponse":
|
|
||||||
case "ClinicalImpression":
|
|
||||||
case "Communication":
|
|
||||||
case "CommunicationRequest":
|
|
||||||
case "Composition":
|
|
||||||
case "Condition":
|
|
||||||
case "Consent":
|
|
||||||
case "Coverage":
|
|
||||||
case "CoverageEligibilityRequest":
|
|
||||||
case "CoverageEligibilityResponse":
|
|
||||||
case "DetectedIssue":
|
|
||||||
case "DeviceRequest":
|
|
||||||
case "DeviceUseStatement":
|
|
||||||
case "DiagnosticReport":
|
|
||||||
case "DocumentManifest":
|
|
||||||
case "DocumentReference":
|
|
||||||
case "Encounter":
|
|
||||||
case "EnrollmentRequest":
|
|
||||||
case "EpisodeOfCare":
|
|
||||||
case "ExplanationOfBenefit":
|
|
||||||
case "FamilyMemberHistory":
|
|
||||||
case "Flag":
|
|
||||||
case "Goal":
|
|
||||||
case "Group":
|
|
||||||
case "ImagingStudy":
|
|
||||||
case "Immunization":
|
|
||||||
case "ImmunizationEvaluation":
|
|
||||||
case "ImmunizationRecommendation":
|
|
||||||
case "Invoice":
|
|
||||||
case "List":
|
|
||||||
case "MeasureReport":
|
|
||||||
case "Media":
|
|
||||||
case "MedicationAdministration":
|
|
||||||
case "MedicationDispense":
|
|
||||||
case "MedicationRequest":
|
|
||||||
case "MedicationStatement":
|
|
||||||
case "MolecularSequence":
|
|
||||||
case "NutritionOrder":
|
|
||||||
case "Observation":
|
|
||||||
case "Patient":
|
|
||||||
case "Person":
|
|
||||||
case "Procedure":
|
|
||||||
case "Provenance":
|
|
||||||
case "QuestionnaireResponse":
|
|
||||||
case "RelatedPerson":
|
|
||||||
case "RequestGroup":
|
|
||||||
case "ResearchSubject":
|
|
||||||
case "RiskAssessment":
|
|
||||||
case "Schedule":
|
|
||||||
case "ServiceRequest":
|
|
||||||
case "Specimen":
|
|
||||||
case "SupplyDelivery":
|
|
||||||
case "SupplyRequest":
|
|
||||||
case "VisionPrescription":
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPatientSearchParam(String theDataType) {
|
|
||||||
switch (theDataType) {
|
|
||||||
case "Account":
|
|
||||||
return "subject";
|
|
||||||
case "AdverseEvent":
|
|
||||||
return "subject";
|
|
||||||
case "AllergyIntolerance":
|
|
||||||
return "patient";
|
|
||||||
case "Appointment":
|
|
||||||
return "actor";
|
|
||||||
case "AppointmentResponse":
|
|
||||||
return "actor";
|
|
||||||
case "AuditEvent":
|
|
||||||
return "patient";
|
|
||||||
case "Basic":
|
|
||||||
return "patient";
|
|
||||||
case "BodyStructure":
|
|
||||||
return "patient";
|
|
||||||
case "CarePlan":
|
|
||||||
return "patient";
|
|
||||||
case "CareTeam":
|
|
||||||
return "patient";
|
|
||||||
case "ChargeItem":
|
|
||||||
return "subject";
|
|
||||||
case "Claim":
|
|
||||||
return "patient";
|
|
||||||
case "ClaimResponse":
|
|
||||||
return "patient";
|
|
||||||
case "ClinicalImpression":
|
|
||||||
return "subject";
|
|
||||||
case "Communication":
|
|
||||||
return "subject";
|
|
||||||
case "CommunicationRequest":
|
|
||||||
return "subject";
|
|
||||||
case "Composition":
|
|
||||||
return "subject";
|
|
||||||
case "Condition":
|
|
||||||
return "patient";
|
|
||||||
case "Consent":
|
|
||||||
return "patient";
|
|
||||||
case "Coverage":
|
|
||||||
return "policy-holder";
|
|
||||||
case "DetectedIssue":
|
|
||||||
return "patient";
|
|
||||||
case "DeviceRequest":
|
|
||||||
return "subject";
|
|
||||||
case "DeviceUseStatement":
|
|
||||||
return "subject";
|
|
||||||
case "DiagnosticReport":
|
|
||||||
return "subject";
|
|
||||||
case "DocumentManifest":
|
|
||||||
return "subject";
|
|
||||||
case "DocumentReference":
|
|
||||||
return "subject";
|
|
||||||
case "Encounter":
|
|
||||||
return "patient";
|
|
||||||
case "EnrollmentRequest":
|
|
||||||
return "subject";
|
|
||||||
case "EpisodeOfCare":
|
|
||||||
return "patient";
|
|
||||||
case "ExplanationOfBenefit":
|
|
||||||
return "patient";
|
|
||||||
case "FamilyMemberHistory":
|
|
||||||
return "patient";
|
|
||||||
case "Flag":
|
|
||||||
return "patient";
|
|
||||||
case "Goal":
|
|
||||||
return "patient";
|
|
||||||
case "Group":
|
|
||||||
return "member";
|
|
||||||
case "ImagingStudy":
|
|
||||||
return "patient";
|
|
||||||
case "Immunization":
|
|
||||||
return "patient";
|
|
||||||
case "ImmunizationRecommendation":
|
|
||||||
return "patient";
|
|
||||||
case "Invoice":
|
|
||||||
return "subject";
|
|
||||||
case "List":
|
|
||||||
return "subject";
|
|
||||||
case "MeasureReport":
|
|
||||||
return "patient";
|
|
||||||
case "Media":
|
|
||||||
return "subject";
|
|
||||||
case "MedicationAdministration":
|
|
||||||
return "patient";
|
|
||||||
case "MedicationDispense":
|
|
||||||
return "patient";
|
|
||||||
case "MedicationRequest":
|
|
||||||
return "subject";
|
|
||||||
case "MedicationStatement":
|
|
||||||
return "subject";
|
|
||||||
case "MolecularSequence":
|
|
||||||
return "patient";
|
|
||||||
case "NutritionOrder":
|
|
||||||
return "patient";
|
|
||||||
case "Observation":
|
|
||||||
return "subject";
|
|
||||||
case "Patient":
|
|
||||||
return "_id";
|
|
||||||
case "Person":
|
|
||||||
return "patient";
|
|
||||||
case "Procedure":
|
|
||||||
return "patient";
|
|
||||||
case "Provenance":
|
|
||||||
return "patient";
|
|
||||||
case "QuestionnaireResponse":
|
|
||||||
return "subject";
|
|
||||||
case "RelatedPerson":
|
|
||||||
return "patient";
|
|
||||||
case "RequestGroup":
|
|
||||||
return "subject";
|
|
||||||
case "ResearchSubject":
|
|
||||||
return "individual";
|
|
||||||
case "RiskAssessment":
|
|
||||||
return "subject";
|
|
||||||
case "Schedule":
|
|
||||||
return "actor";
|
|
||||||
case "ServiceRequest":
|
|
||||||
return "patient";
|
|
||||||
case "Specimen":
|
|
||||||
return "subject";
|
|
||||||
case "SupplyDelivery":
|
|
||||||
return "patient";
|
|
||||||
case "SupplyRequest":
|
|
||||||
return "subject";
|
|
||||||
case "VisionPrescription":
|
|
||||||
return "patient";
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,31 +24,23 @@ import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson;
|
||||||
import ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrUtils;
|
import ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrUtils;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.hl7.fhir.r5.model.CanonicalType;
|
|
||||||
import org.hl7.fhir.r5.model.Coding;
|
import org.hl7.fhir.r5.model.Coding;
|
||||||
import org.hl7.fhir.r5.model.DataRequirement;
|
|
||||||
import org.hl7.fhir.r5.model.Library;
|
|
||||||
import org.hl7.fhir.r5.model.PlanDefinition;
|
import org.hl7.fhir.r5.model.PlanDefinition;
|
||||||
import org.hl7.fhir.r5.model.ValueSet;
|
import org.hl7.fhir.r5.model.TriggerDefinition;
|
||||||
import org.opencds.cqf.fhir.api.Repository;
|
import org.opencds.cqf.fhir.api.Repository;
|
||||||
import org.opencds.cqf.fhir.utility.r5.SearchHelper;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.stream.Collectors;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class CrDiscoveryServiceR5 implements ICrDiscoveryService {
|
public class CrDiscoveryServiceR5 implements ICrDiscoveryService {
|
||||||
|
|
||||||
protected final String PATIENT_ID_CONTEXT = "{{context.patientId}}";
|
|
||||||
protected final int DEFAULT_MAX_URI_LENGTH = 8000;
|
|
||||||
protected int myMaxUriLength;
|
|
||||||
|
|
||||||
protected final Repository myRepository;
|
protected final Repository myRepository;
|
||||||
protected final IIdType myPlanDefinitionId;
|
protected final IIdType myPlanDefinitionId;
|
||||||
|
protected final PrefetchTemplateBuilderR5 myPrefetchTemplateBuilder;
|
||||||
|
|
||||||
public CrDiscoveryServiceR5(IIdType thePlanDefinitionId, Repository theRepository) {
|
public CrDiscoveryServiceR5(IIdType thePlanDefinitionId, Repository theRepository) {
|
||||||
myPlanDefinitionId = thePlanDefinitionId;
|
myPlanDefinitionId = thePlanDefinitionId;
|
||||||
myRepository = theRepository;
|
myRepository = theRepository;
|
||||||
myMaxUriLength = DEFAULT_MAX_URI_LENGTH;
|
myPrefetchTemplateBuilder = new PrefetchTemplateBuilderR5(myRepository);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CdsServiceJson resolveService() {
|
public CdsServiceJson resolveService() {
|
||||||
|
@ -59,12 +51,36 @@ public class CrDiscoveryServiceR5 implements ICrDiscoveryService {
|
||||||
protected CdsServiceJson resolveService(IBaseResource thePlanDefinition) {
|
protected CdsServiceJson resolveService(IBaseResource thePlanDefinition) {
|
||||||
if (thePlanDefinition instanceof PlanDefinition) {
|
if (thePlanDefinition instanceof PlanDefinition) {
|
||||||
PlanDefinition planDef = (PlanDefinition) thePlanDefinition;
|
PlanDefinition planDef = (PlanDefinition) thePlanDefinition;
|
||||||
return new CrDiscoveryElementR5(planDef, getPrefetchUrlList(planDef)).getCdsServiceJson();
|
String triggerEvent = getTriggerEvent(planDef);
|
||||||
|
if (triggerEvent != null) {
|
||||||
|
PrefetchUrlList prefetchUrlList =
|
||||||
|
isEca(planDef) ? myPrefetchTemplateBuilder.getPrefetchUrlList(planDef) : new PrefetchUrlList();
|
||||||
|
return new CrDiscoveryElementR5(planDef, prefetchUrlList).getCdsServiceJson();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEca(PlanDefinition thePlanDefinition) {
|
protected String getTriggerEvent(PlanDefinition thePlanDefinition) {
|
||||||
|
if (thePlanDefinition == null
|
||||||
|
|| !thePlanDefinition.hasAction()
|
||||||
|
|| thePlanDefinition.getAction().stream().noneMatch(a -> a.hasTrigger())) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var triggerDefs = thePlanDefinition.getAction().stream()
|
||||||
|
.filter(a -> a.hasTrigger())
|
||||||
|
.flatMap(a -> a.getTrigger().stream())
|
||||||
|
.filter(t -> t.getType().equals(TriggerDefinition.TriggerType.NAMEDEVENT))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (triggerDefs == null || triggerDefs.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return triggerDefs.get(0).getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isEca(PlanDefinition thePlanDefinition) {
|
||||||
if (thePlanDefinition.hasType() && thePlanDefinition.getType().hasCoding()) {
|
if (thePlanDefinition.hasType() && thePlanDefinition.getType().hasCoding()) {
|
||||||
for (Coding coding : thePlanDefinition.getType().getCoding()) {
|
for (Coding coding : thePlanDefinition.getType().getCoding()) {
|
||||||
if (coding.getCode().equals("eca-rule")) {
|
if (coding.getCode().equals("eca-rule")) {
|
||||||
|
@ -74,358 +90,4 @@ public class CrDiscoveryServiceR5 implements ICrDiscoveryService {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Library resolvePrimaryLibrary(PlanDefinition thePlanDefinition) {
|
|
||||||
// The CPGComputablePlanDefinition profile limits the cardinality of library to 1
|
|
||||||
Library library = null;
|
|
||||||
if (thePlanDefinition.hasLibrary() && !thePlanDefinition.getLibrary().isEmpty()) {
|
|
||||||
library = (Library) SearchHelper.searchRepositoryByCanonical(
|
|
||||||
myRepository, thePlanDefinition.getLibrary().get(0));
|
|
||||||
}
|
|
||||||
return library;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> resolveValueCodingCodes(List<Coding> theValueCodings) {
|
|
||||||
List<String> result = new ArrayList<>();
|
|
||||||
|
|
||||||
StringBuilder codes = new StringBuilder();
|
|
||||||
for (Coding coding : theValueCodings) {
|
|
||||||
if (coding.hasCode()) {
|
|
||||||
String system = coding.getSystem();
|
|
||||||
String code = coding.getCode();
|
|
||||||
|
|
||||||
codes = getCodesStringBuilder(result, codes, system, code);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result.add(codes.toString());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> resolveValueSetCodes(CanonicalType theValueSetId) {
|
|
||||||
ValueSet valueSet = (ValueSet) SearchHelper.searchRepositoryByCanonical(myRepository, theValueSetId);
|
|
||||||
List<String> result = new ArrayList<>();
|
|
||||||
StringBuilder codes = new StringBuilder();
|
|
||||||
if (valueSet.hasExpansion() && valueSet.getExpansion().hasContains()) {
|
|
||||||
for (ValueSet.ValueSetExpansionContainsComponent contains :
|
|
||||||
valueSet.getExpansion().getContains()) {
|
|
||||||
String system = contains.getSystem();
|
|
||||||
String code = contains.getCode();
|
|
||||||
|
|
||||||
codes = getCodesStringBuilder(result, codes, system, code);
|
|
||||||
}
|
|
||||||
} else if (valueSet.hasCompose() && valueSet.getCompose().hasInclude()) {
|
|
||||||
for (ValueSet.ConceptSetComponent concepts : valueSet.getCompose().getInclude()) {
|
|
||||||
String system = concepts.getSystem();
|
|
||||||
if (concepts.hasConcept()) {
|
|
||||||
for (ValueSet.ConceptReferenceComponent concept : concepts.getConcept()) {
|
|
||||||
String code = concept.getCode();
|
|
||||||
|
|
||||||
codes = getCodesStringBuilder(result, codes, system, code);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result.add(codes.toString());
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected StringBuilder getCodesStringBuilder(
|
|
||||||
List<String> theList, StringBuilder theCodes, String theSystem, String theCode) {
|
|
||||||
String codeToken = theSystem + "|" + theCode;
|
|
||||||
int postAppendLength = theCodes.length() + codeToken.length();
|
|
||||||
|
|
||||||
if (theCodes.length() > 0 && postAppendLength < myMaxUriLength) {
|
|
||||||
theCodes.append(",");
|
|
||||||
} else if (postAppendLength > myMaxUriLength) {
|
|
||||||
theList.add(theCodes.toString());
|
|
||||||
theCodes = new StringBuilder();
|
|
||||||
}
|
|
||||||
theCodes.append(codeToken);
|
|
||||||
return theCodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> createRequestUrl(DataRequirement theDataRequirement) {
|
|
||||||
if (!isPatientCompartment(theDataRequirement.getType().toCode())) return null;
|
|
||||||
String patientRelatedResource = theDataRequirement.getType() + "?"
|
|
||||||
+ getPatientSearchParam(theDataRequirement.getType().toCode())
|
|
||||||
+ "=Patient/" + PATIENT_ID_CONTEXT;
|
|
||||||
List<String> ret = new ArrayList<>();
|
|
||||||
if (theDataRequirement.hasCodeFilter()) {
|
|
||||||
for (DataRequirement.DataRequirementCodeFilterComponent codeFilterComponent :
|
|
||||||
theDataRequirement.getCodeFilter()) {
|
|
||||||
if (!codeFilterComponent.hasPath()) continue;
|
|
||||||
String path =
|
|
||||||
mapCodePathToSearchParam(theDataRequirement.getType().toCode(), codeFilterComponent.getPath());
|
|
||||||
if (codeFilterComponent.hasValueSetElement()) {
|
|
||||||
for (String codes : resolveValueSetCodes(codeFilterComponent.getValueSetElement())) {
|
|
||||||
ret.add(patientRelatedResource + "&" + path + "=" + codes);
|
|
||||||
}
|
|
||||||
} else if (codeFilterComponent.hasCode()) {
|
|
||||||
List<Coding> codeFilterValueCodings = codeFilterComponent.getCode();
|
|
||||||
boolean isFirstCodingInFilter = true;
|
|
||||||
for (String code : resolveValueCodingCodes(codeFilterValueCodings)) {
|
|
||||||
if (isFirstCodingInFilter) {
|
|
||||||
ret.add(patientRelatedResource + "&" + path + "=" + code);
|
|
||||||
} else {
|
|
||||||
ret.add("," + code);
|
|
||||||
}
|
|
||||||
|
|
||||||
isFirstCodingInFilter = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
} else {
|
|
||||||
ret.add(patientRelatedResource);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public PrefetchUrlList getPrefetchUrlList(PlanDefinition thePlanDefinition) {
|
|
||||||
PrefetchUrlList prefetchList = new PrefetchUrlList();
|
|
||||||
if (thePlanDefinition == null) return null;
|
|
||||||
if (!isEca(thePlanDefinition)) return null;
|
|
||||||
Library library = resolvePrimaryLibrary(thePlanDefinition);
|
|
||||||
// TODO: resolve data requirements
|
|
||||||
if (library == null || !library.hasDataRequirement()) return null;
|
|
||||||
for (DataRequirement dataRequirement : library.getDataRequirement()) {
|
|
||||||
List<String> requestUrls = createRequestUrl(dataRequirement);
|
|
||||||
if (requestUrls != null) {
|
|
||||||
prefetchList.addAll(requestUrls);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return prefetchList;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String mapCodePathToSearchParam(String theDataType, String thePath) {
|
|
||||||
switch (theDataType) {
|
|
||||||
case "MedicationAdministration":
|
|
||||||
if (thePath.equals("medication")) return "code";
|
|
||||||
break;
|
|
||||||
case "MedicationDispense":
|
|
||||||
if (thePath.equals("medication")) return "code";
|
|
||||||
break;
|
|
||||||
case "MedicationRequest":
|
|
||||||
if (thePath.equals("medication")) return "code";
|
|
||||||
break;
|
|
||||||
case "MedicationStatement":
|
|
||||||
if (thePath.equals("medication")) return "code";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (thePath.equals("vaccineCode")) return "vaccine-code";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return thePath.replace('.', '-').toLowerCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isPatientCompartment(String theDataType) {
|
|
||||||
if (theDataType == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
switch (theDataType) {
|
|
||||||
case "Account":
|
|
||||||
case "AdverseEvent":
|
|
||||||
case "AllergyIntolerance":
|
|
||||||
case "Appointment":
|
|
||||||
case "AppointmentResponse":
|
|
||||||
case "AuditEvent":
|
|
||||||
case "Basic":
|
|
||||||
case "BodyStructure":
|
|
||||||
case "CarePlan":
|
|
||||||
case "CareTeam":
|
|
||||||
case "ChargeItem":
|
|
||||||
case "Claim":
|
|
||||||
case "ClaimResponse":
|
|
||||||
case "ClinicalImpression":
|
|
||||||
case "Communication":
|
|
||||||
case "CommunicationRequest":
|
|
||||||
case "Composition":
|
|
||||||
case "Condition":
|
|
||||||
case "Consent":
|
|
||||||
case "Coverage":
|
|
||||||
case "CoverageEligibilityRequest":
|
|
||||||
case "CoverageEligibilityResponse":
|
|
||||||
case "DetectedIssue":
|
|
||||||
case "DeviceRequest":
|
|
||||||
case "DeviceUseStatement":
|
|
||||||
case "DiagnosticReport":
|
|
||||||
case "DocumentManifest":
|
|
||||||
case "DocumentReference":
|
|
||||||
case "Encounter":
|
|
||||||
case "EnrollmentRequest":
|
|
||||||
case "EpisodeOfCare":
|
|
||||||
case "ExplanationOfBenefit":
|
|
||||||
case "FamilyMemberHistory":
|
|
||||||
case "Flag":
|
|
||||||
case "Goal":
|
|
||||||
case "Group":
|
|
||||||
case "ImagingStudy":
|
|
||||||
case "Immunization":
|
|
||||||
case "ImmunizationEvaluation":
|
|
||||||
case "ImmunizationRecommendation":
|
|
||||||
case "Invoice":
|
|
||||||
case "List":
|
|
||||||
case "MeasureReport":
|
|
||||||
case "Media":
|
|
||||||
case "MedicationAdministration":
|
|
||||||
case "MedicationDispense":
|
|
||||||
case "MedicationRequest":
|
|
||||||
case "MedicationStatement":
|
|
||||||
case "MolecularSequence":
|
|
||||||
case "NutritionOrder":
|
|
||||||
case "Observation":
|
|
||||||
case "Patient":
|
|
||||||
case "Person":
|
|
||||||
case "Procedure":
|
|
||||||
case "Provenance":
|
|
||||||
case "QuestionnaireResponse":
|
|
||||||
case "RelatedPerson":
|
|
||||||
case "RequestGroup":
|
|
||||||
case "ResearchSubject":
|
|
||||||
case "RiskAssessment":
|
|
||||||
case "Schedule":
|
|
||||||
case "ServiceRequest":
|
|
||||||
case "Specimen":
|
|
||||||
case "SupplyDelivery":
|
|
||||||
case "SupplyRequest":
|
|
||||||
case "VisionPrescription":
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getPatientSearchParam(String theDataType) {
|
|
||||||
switch (theDataType) {
|
|
||||||
case "Account":
|
|
||||||
return "subject";
|
|
||||||
case "AdverseEvent":
|
|
||||||
return "subject";
|
|
||||||
case "AllergyIntolerance":
|
|
||||||
return "patient";
|
|
||||||
case "Appointment":
|
|
||||||
return "actor";
|
|
||||||
case "AppointmentResponse":
|
|
||||||
return "actor";
|
|
||||||
case "AuditEvent":
|
|
||||||
return "patient";
|
|
||||||
case "Basic":
|
|
||||||
return "patient";
|
|
||||||
case "BodyStructure":
|
|
||||||
return "patient";
|
|
||||||
case "CarePlan":
|
|
||||||
return "patient";
|
|
||||||
case "CareTeam":
|
|
||||||
return "patient";
|
|
||||||
case "ChargeItem":
|
|
||||||
return "subject";
|
|
||||||
case "Claim":
|
|
||||||
return "patient";
|
|
||||||
case "ClaimResponse":
|
|
||||||
return "patient";
|
|
||||||
case "ClinicalImpression":
|
|
||||||
return "subject";
|
|
||||||
case "Communication":
|
|
||||||
return "subject";
|
|
||||||
case "CommunicationRequest":
|
|
||||||
return "subject";
|
|
||||||
case "Composition":
|
|
||||||
return "subject";
|
|
||||||
case "Condition":
|
|
||||||
return "patient";
|
|
||||||
case "Consent":
|
|
||||||
return "patient";
|
|
||||||
case "Coverage":
|
|
||||||
return "policy-holder";
|
|
||||||
case "DetectedIssue":
|
|
||||||
return "patient";
|
|
||||||
case "DeviceRequest":
|
|
||||||
return "subject";
|
|
||||||
case "DeviceUseStatement":
|
|
||||||
return "subject";
|
|
||||||
case "DiagnosticReport":
|
|
||||||
return "subject";
|
|
||||||
case "DocumentManifest":
|
|
||||||
return "subject";
|
|
||||||
case "DocumentReference":
|
|
||||||
return "subject";
|
|
||||||
case "Encounter":
|
|
||||||
return "patient";
|
|
||||||
case "EnrollmentRequest":
|
|
||||||
return "subject";
|
|
||||||
case "EpisodeOfCare":
|
|
||||||
return "patient";
|
|
||||||
case "ExplanationOfBenefit":
|
|
||||||
return "patient";
|
|
||||||
case "FamilyMemberHistory":
|
|
||||||
return "patient";
|
|
||||||
case "Flag":
|
|
||||||
return "patient";
|
|
||||||
case "Goal":
|
|
||||||
return "patient";
|
|
||||||
case "Group":
|
|
||||||
return "member";
|
|
||||||
case "ImagingStudy":
|
|
||||||
return "patient";
|
|
||||||
case "Immunization":
|
|
||||||
return "patient";
|
|
||||||
case "ImmunizationRecommendation":
|
|
||||||
return "patient";
|
|
||||||
case "Invoice":
|
|
||||||
return "subject";
|
|
||||||
case "List":
|
|
||||||
return "subject";
|
|
||||||
case "MeasureReport":
|
|
||||||
return "patient";
|
|
||||||
case "Media":
|
|
||||||
return "subject";
|
|
||||||
case "MedicationAdministration":
|
|
||||||
return "patient";
|
|
||||||
case "MedicationDispense":
|
|
||||||
return "patient";
|
|
||||||
case "MedicationRequest":
|
|
||||||
return "subject";
|
|
||||||
case "MedicationStatement":
|
|
||||||
return "subject";
|
|
||||||
case "MolecularSequence":
|
|
||||||
return "patient";
|
|
||||||
case "NutritionOrder":
|
|
||||||
return "patient";
|
|
||||||
case "Observation":
|
|
||||||
return "subject";
|
|
||||||
case "Patient":
|
|
||||||
return "_id";
|
|
||||||
case "Person":
|
|
||||||
return "patient";
|
|
||||||
case "Procedure":
|
|
||||||
return "patient";
|
|
||||||
case "Provenance":
|
|
||||||
return "patient";
|
|
||||||
case "QuestionnaireResponse":
|
|
||||||
return "subject";
|
|
||||||
case "RelatedPerson":
|
|
||||||
return "patient";
|
|
||||||
case "RequestGroup":
|
|
||||||
return "subject";
|
|
||||||
case "ResearchSubject":
|
|
||||||
return "individual";
|
|
||||||
case "RiskAssessment":
|
|
||||||
return "subject";
|
|
||||||
case "Schedule":
|
|
||||||
return "actor";
|
|
||||||
case "ServiceRequest":
|
|
||||||
return "patient";
|
|
||||||
case "Specimen":
|
|
||||||
return "subject";
|
|
||||||
case "SupplyDelivery":
|
|
||||||
return "patient";
|
|
||||||
case "SupplyRequest":
|
|
||||||
return "subject";
|
|
||||||
case "VisionPrescription":
|
|
||||||
return "patient";
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,434 @@
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - CDS Hooks
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2024 Smile CDR, Inc.
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
package ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery;
|
||||||
|
|
||||||
|
import org.hl7.fhir.dstu3.model.Coding;
|
||||||
|
import org.hl7.fhir.dstu3.model.DataRequirement;
|
||||||
|
import org.hl7.fhir.dstu3.model.Extension;
|
||||||
|
import org.hl7.fhir.dstu3.model.Library;
|
||||||
|
import org.hl7.fhir.dstu3.model.PlanDefinition;
|
||||||
|
import org.hl7.fhir.dstu3.model.StringType;
|
||||||
|
import org.hl7.fhir.dstu3.model.ValueSet;
|
||||||
|
import org.opencds.cqf.fhir.api.Repository;
|
||||||
|
import org.opencds.cqf.fhir.utility.dstu3.SearchHelper;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.CQF_FHIR_QUERY_PATTERN;
|
||||||
|
import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.CRMI_EFFECTIVE_DATA_REQUIREMENTS;
|
||||||
|
|
||||||
|
public class PrefetchTemplateBuilderDstu3 extends BasePrefetchTemplateBuilder {
|
||||||
|
|
||||||
|
public PrefetchTemplateBuilderDstu3(Repository theRepository) {
|
||||||
|
super(theRepository);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PrefetchUrlList getPrefetchUrlList(PlanDefinition thePlanDefinition) {
|
||||||
|
PrefetchUrlList prefetchList = new PrefetchUrlList();
|
||||||
|
if (thePlanDefinition == null) return null;
|
||||||
|
Library library = resolvePrimaryLibrary(thePlanDefinition);
|
||||||
|
if (!library.hasDataRequirement()) return null;
|
||||||
|
for (DataRequirement dataRequirement : library.getDataRequirement()) {
|
||||||
|
List<String> requestUrls = createRequestUrl(dataRequirement);
|
||||||
|
if (requestUrls != null) {
|
||||||
|
prefetchList.addAll(requestUrls);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return prefetchList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ReassignedVariable")
|
||||||
|
protected Library resolvePrimaryLibrary(PlanDefinition thePlanDefinition) {
|
||||||
|
Library library = null;
|
||||||
|
Extension dataReqExt = thePlanDefinition.getExtensionByUrl(CRMI_EFFECTIVE_DATA_REQUIREMENTS);
|
||||||
|
// Use a Module Definition Library with Effective Data Requirements for the Plan Definition if it exists
|
||||||
|
if (dataReqExt != null && dataReqExt.hasValue()) {
|
||||||
|
StringType moduleDefCanonical = (StringType) dataReqExt.getValue();
|
||||||
|
library = (Library) SearchHelper.searchRepositoryByCanonical(myRepository, moduleDefCanonical);
|
||||||
|
}
|
||||||
|
// Otherwise use the primary Library
|
||||||
|
if (library == null && thePlanDefinition.hasLibrary()) {
|
||||||
|
// The CPGComputablePlanDefinition profile limits the cardinality of library to 1
|
||||||
|
StringType canonical =
|
||||||
|
new StringType(thePlanDefinition.getLibrary().get(0).getReference());
|
||||||
|
library = (Library) SearchHelper.searchRepositoryByCanonical(myRepository, canonical);
|
||||||
|
}
|
||||||
|
return library;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<String> createRequestUrl(DataRequirement theDataRequirement) {
|
||||||
|
List<String> urlList = new ArrayList<>();
|
||||||
|
// if we have a fhirQueryPattern extensions, use them
|
||||||
|
List<Extension> fhirQueryExtList = theDataRequirement.getExtension().stream()
|
||||||
|
.filter(e -> e.getUrl().equals(CQF_FHIR_QUERY_PATTERN) && e.hasValue())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (!fhirQueryExtList.isEmpty()) {
|
||||||
|
for (Extension fhirQueryExt : fhirQueryExtList) {
|
||||||
|
urlList.add(fhirQueryExt.getValueAsPrimitive().getValueAsString());
|
||||||
|
}
|
||||||
|
return urlList;
|
||||||
|
}
|
||||||
|
|
||||||
|
// else build the query
|
||||||
|
if (!isPatientCompartment(theDataRequirement.getType())) return null;
|
||||||
|
String baseQuery = theDataRequirement.getType() + "?"
|
||||||
|
+ getPatientSearchParam(theDataRequirement.getType())
|
||||||
|
+ "=Patient/" + PATIENT_ID_CONTEXT;
|
||||||
|
|
||||||
|
// TODO: Add valueFilter extension resolution
|
||||||
|
|
||||||
|
if (theDataRequirement.hasCodeFilter()) {
|
||||||
|
resolveCodeFilter(theDataRequirement, urlList, baseQuery);
|
||||||
|
} else {
|
||||||
|
urlList.add(baseQuery);
|
||||||
|
}
|
||||||
|
return urlList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ReassignedVariable")
|
||||||
|
protected void resolveCodeFilter(DataRequirement theDataRequirement, List<String> theUrlList, String theBaseQuery) {
|
||||||
|
for (DataRequirement.DataRequirementCodeFilterComponent codeFilterComponent :
|
||||||
|
theDataRequirement.getCodeFilter()) {
|
||||||
|
if (!codeFilterComponent.hasPath()) continue;
|
||||||
|
String path = mapCodePathToSearchParam(theDataRequirement.getType(), codeFilterComponent.getPath());
|
||||||
|
StringType codeFilterComponentString = null;
|
||||||
|
if (codeFilterComponent.hasValueSetStringType()) {
|
||||||
|
codeFilterComponentString = codeFilterComponent.getValueSetStringType();
|
||||||
|
} else if (codeFilterComponent.hasValueSetReference()) {
|
||||||
|
codeFilterComponentString = new StringType(
|
||||||
|
codeFilterComponent.getValueSetReference().getReference());
|
||||||
|
} else if (codeFilterComponent.hasValueCoding()) {
|
||||||
|
List<Coding> codeFilterValueCodings = codeFilterComponent.getValueCoding();
|
||||||
|
boolean isFirstCodingInFilter = true;
|
||||||
|
for (String code : resolveValueCodingCodes(codeFilterValueCodings)) {
|
||||||
|
if (isFirstCodingInFilter) {
|
||||||
|
theUrlList.add(theBaseQuery + "&" + path + "=" + code);
|
||||||
|
} else {
|
||||||
|
theUrlList.add("," + code);
|
||||||
|
}
|
||||||
|
isFirstCodingInFilter = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (codeFilterComponentString != null) {
|
||||||
|
for (String codes : resolveValueSetCodes(codeFilterComponentString)) {
|
||||||
|
theUrlList.add(theBaseQuery + "&" + path + "=" + codes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ReassignedVariable")
|
||||||
|
protected List<String> resolveValueCodingCodes(List<Coding> theValueCodings) {
|
||||||
|
List<String> result = new ArrayList<>();
|
||||||
|
StringBuilder codes = new StringBuilder();
|
||||||
|
for (Coding coding : theValueCodings) {
|
||||||
|
if (coding.hasCode()) {
|
||||||
|
String system = coding.getSystem();
|
||||||
|
String code = coding.getCode();
|
||||||
|
codes = getCodesStringBuilder(result, codes, system, code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.add(codes.toString());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ReassignedVariable")
|
||||||
|
protected List<String> resolveValueSetCodes(StringType theValueSetId) {
|
||||||
|
ValueSet valueSet = (ValueSet) SearchHelper.searchRepositoryByCanonical(myRepository, theValueSetId);
|
||||||
|
List<String> result = new ArrayList<>();
|
||||||
|
StringBuilder codes = new StringBuilder();
|
||||||
|
if (valueSet.hasExpansion() && valueSet.getExpansion().hasContains()) {
|
||||||
|
for (ValueSet.ValueSetExpansionContainsComponent contains :
|
||||||
|
valueSet.getExpansion().getContains()) {
|
||||||
|
String system = contains.getSystem();
|
||||||
|
String code = contains.getCode();
|
||||||
|
|
||||||
|
codes = getCodesStringBuilder(result, codes, system, code);
|
||||||
|
}
|
||||||
|
} else if (valueSet.hasCompose() && valueSet.getCompose().hasInclude()) {
|
||||||
|
for (ValueSet.ConceptSetComponent concepts : valueSet.getCompose().getInclude()) {
|
||||||
|
String system = concepts.getSystem();
|
||||||
|
if (concepts.hasConcept()) {
|
||||||
|
for (ValueSet.ConceptReferenceComponent concept : concepts.getConcept()) {
|
||||||
|
String code = concept.getCode();
|
||||||
|
|
||||||
|
codes = getCodesStringBuilder(result, codes, system, code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.add(codes.toString());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ReassignedVariable")
|
||||||
|
protected StringBuilder getCodesStringBuilder(
|
||||||
|
List<String> theStrings, StringBuilder theCodes, String theSystem, String theCode) {
|
||||||
|
StringBuilder codes = theCodes;
|
||||||
|
String codeToken = theSystem + "|" + theCode;
|
||||||
|
int postAppendLength = codes.length() + codeToken.length();
|
||||||
|
if (codes.length() > 0 && postAppendLength < myMaxUriLength) {
|
||||||
|
codes.append(",");
|
||||||
|
} else if (postAppendLength > myMaxUriLength) {
|
||||||
|
theStrings.add(codes.toString());
|
||||||
|
codes = new StringBuilder();
|
||||||
|
}
|
||||||
|
codes.append(codeToken);
|
||||||
|
return codes;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String mapCodePathToSearchParam(String theDataType, String thePath) {
|
||||||
|
switch (theDataType) {
|
||||||
|
case "MedicationAdministration":
|
||||||
|
if (thePath.equals("medication")) return "code";
|
||||||
|
break;
|
||||||
|
case "MedicationDispense":
|
||||||
|
if (thePath.equals("medication")) return "code";
|
||||||
|
break;
|
||||||
|
case "MedicationRequest":
|
||||||
|
if (thePath.equals("medication")) return "code";
|
||||||
|
break;
|
||||||
|
case "MedicationStatement":
|
||||||
|
if (thePath.equals("medication")) return "code";
|
||||||
|
break;
|
||||||
|
case "ProcedureRequest":
|
||||||
|
if (thePath.equals("bodySite")) return "body-site";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (thePath.equals("vaccineCode")) return "vaccine-code";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return thePath.replace('.', '-').toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static boolean isPatientCompartment(String theDataType) {
|
||||||
|
if (theDataType == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
switch (theDataType) {
|
||||||
|
case "Account":
|
||||||
|
case "AdverseEvent":
|
||||||
|
case "AllergyIntolerance":
|
||||||
|
case "Appointment":
|
||||||
|
case "AppointmentResponse":
|
||||||
|
case "AuditEvent":
|
||||||
|
case "Basic":
|
||||||
|
case "BodySite":
|
||||||
|
case "CarePlan":
|
||||||
|
case "CareTeam":
|
||||||
|
case "ChargeItem":
|
||||||
|
case "Claim":
|
||||||
|
case "ClaimResponse":
|
||||||
|
case "ClinicalImpression":
|
||||||
|
case "Communication":
|
||||||
|
case "CommunicationRequest":
|
||||||
|
case "Composition":
|
||||||
|
case "Condition":
|
||||||
|
case "Consent":
|
||||||
|
case "Coverage":
|
||||||
|
case "DetectedIssue":
|
||||||
|
case "DeviceRequest":
|
||||||
|
case "DeviceUseStatement":
|
||||||
|
case "DiagnosticReport":
|
||||||
|
case "DocumentManifest":
|
||||||
|
case "EligibilityRequest":
|
||||||
|
case "Encounter":
|
||||||
|
case "EnrollmentRequest":
|
||||||
|
case "EpisodeOfCare":
|
||||||
|
case "ExplanationOfBenefit":
|
||||||
|
case "FamilyMemberHistory":
|
||||||
|
case "Flag":
|
||||||
|
case "Goal":
|
||||||
|
case "Group":
|
||||||
|
case "ImagingManifest":
|
||||||
|
case "ImagingStudy":
|
||||||
|
case "Immunization":
|
||||||
|
case "ImmunizationRecommendation":
|
||||||
|
case "List":
|
||||||
|
case "MeasureReport":
|
||||||
|
case "Media":
|
||||||
|
case "MedicationAdministration":
|
||||||
|
case "MedicationDispense":
|
||||||
|
case "MedicationRequest":
|
||||||
|
case "MedicationStatement":
|
||||||
|
case "NutritionOrder":
|
||||||
|
case "Observation":
|
||||||
|
case "Patient":
|
||||||
|
case "Person":
|
||||||
|
case "Procedure":
|
||||||
|
case "ProcedureRequest":
|
||||||
|
case "Provenance":
|
||||||
|
case "QuestionnaireResponse":
|
||||||
|
case "ReferralRequest":
|
||||||
|
case "RelatedPerson":
|
||||||
|
case "RequestGroup":
|
||||||
|
case "ResearchSubject":
|
||||||
|
case "RiskAssessment":
|
||||||
|
case "Schedule":
|
||||||
|
case "Specimen":
|
||||||
|
case "SupplyDelivery":
|
||||||
|
case "SupplyRequest":
|
||||||
|
case "VisionPrescription":
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getPatientSearchParam(String theDataType) {
|
||||||
|
switch (theDataType) {
|
||||||
|
case "Account":
|
||||||
|
return "subject";
|
||||||
|
case "AdverseEvent":
|
||||||
|
return "subject";
|
||||||
|
case "AllergyIntolerance":
|
||||||
|
return "patient";
|
||||||
|
case "Appointment":
|
||||||
|
return "actor";
|
||||||
|
case "AppointmentResponse":
|
||||||
|
return "actor";
|
||||||
|
case "AuditEvent":
|
||||||
|
return "patient";
|
||||||
|
case "Basic":
|
||||||
|
return "patient";
|
||||||
|
case "BodySite":
|
||||||
|
return "patient";
|
||||||
|
case "CarePlan":
|
||||||
|
return "patient";
|
||||||
|
case "CareTeam":
|
||||||
|
return "patient";
|
||||||
|
case "ChargeItem":
|
||||||
|
return "subject";
|
||||||
|
case "Claim":
|
||||||
|
return "patient";
|
||||||
|
case "ClaimResponse":
|
||||||
|
return "patient";
|
||||||
|
case "ClinicalImpression":
|
||||||
|
return "subject";
|
||||||
|
case "Communication":
|
||||||
|
return "subject";
|
||||||
|
case "CommunicationRequest":
|
||||||
|
return "subject";
|
||||||
|
case "Composition":
|
||||||
|
return "subject";
|
||||||
|
case "Condition":
|
||||||
|
return "patient";
|
||||||
|
case "Consent":
|
||||||
|
return "patient";
|
||||||
|
case "Coverage":
|
||||||
|
return "beneficiary";
|
||||||
|
case "DetectedIssue":
|
||||||
|
return "patient";
|
||||||
|
case "DeviceRequest":
|
||||||
|
return "subject";
|
||||||
|
case "DeviceUseStatement":
|
||||||
|
return "subject";
|
||||||
|
case "DiagnosticReport":
|
||||||
|
return "subject";
|
||||||
|
case "DocumentManifest":
|
||||||
|
return "subject";
|
||||||
|
case "DocumentReference":
|
||||||
|
return "subject";
|
||||||
|
case "EligibilityRequest":
|
||||||
|
return "patient";
|
||||||
|
case "Encounter":
|
||||||
|
return "patient";
|
||||||
|
case "EnrollmentRequest":
|
||||||
|
return "subject";
|
||||||
|
case "EpisodeOfCare":
|
||||||
|
return "patient";
|
||||||
|
case "ExplanationOfBenefit":
|
||||||
|
return "patient";
|
||||||
|
case "FamilyMemberHistory":
|
||||||
|
return "patient";
|
||||||
|
case "Flag":
|
||||||
|
return "patient";
|
||||||
|
case "Goal":
|
||||||
|
return "patient";
|
||||||
|
case "Group":
|
||||||
|
return "member";
|
||||||
|
case "ImagingManifest":
|
||||||
|
return "patient";
|
||||||
|
case "ImagingStudy":
|
||||||
|
return "patient";
|
||||||
|
case "Immunization":
|
||||||
|
return "patient";
|
||||||
|
case "ImmunizationRecommendation":
|
||||||
|
return "patient";
|
||||||
|
case "List":
|
||||||
|
return "subject";
|
||||||
|
case "MeasureReport":
|
||||||
|
return "patient";
|
||||||
|
case "Media":
|
||||||
|
return "subject";
|
||||||
|
case "MedicationAdministration":
|
||||||
|
return "patient";
|
||||||
|
case "MedicationDispense":
|
||||||
|
return "patient";
|
||||||
|
case "MedicationRequest":
|
||||||
|
return "subject";
|
||||||
|
case "MedicationStatement":
|
||||||
|
return "subject";
|
||||||
|
case "NutritionOrder":
|
||||||
|
return "patient";
|
||||||
|
case "Observation":
|
||||||
|
return "subject";
|
||||||
|
case "Patient":
|
||||||
|
return "_id";
|
||||||
|
case "Person":
|
||||||
|
return "patient";
|
||||||
|
case "Procedure":
|
||||||
|
return "patient";
|
||||||
|
case "ProcedureRequest":
|
||||||
|
return "patient";
|
||||||
|
case "Provenance":
|
||||||
|
return "patient";
|
||||||
|
case "QuestionnaireResponse":
|
||||||
|
return "subject";
|
||||||
|
case "ReferralRequest":
|
||||||
|
return "patient";
|
||||||
|
case "RelatedPerson":
|
||||||
|
return "patient";
|
||||||
|
case "RequestGroup":
|
||||||
|
return "subject";
|
||||||
|
case "ResearchSubject":
|
||||||
|
return "individual";
|
||||||
|
case "RiskAssessment":
|
||||||
|
return "subject";
|
||||||
|
case "Schedule":
|
||||||
|
return "actor";
|
||||||
|
case "Specimen":
|
||||||
|
return "subject";
|
||||||
|
case "SupplyDelivery":
|
||||||
|
return "patient";
|
||||||
|
case "SupplyRequest":
|
||||||
|
return "subject";
|
||||||
|
case "VisionPrescription":
|
||||||
|
return "patient";
|
||||||
|
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,414 @@
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - CDS Hooks
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2024 Smile CDR, Inc.
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
package ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery;
|
||||||
|
|
||||||
|
import org.hl7.fhir.r4.model.CanonicalType;
|
||||||
|
import org.hl7.fhir.r4.model.Coding;
|
||||||
|
import org.hl7.fhir.r4.model.DataRequirement;
|
||||||
|
import org.hl7.fhir.r4.model.Extension;
|
||||||
|
import org.hl7.fhir.r4.model.Library;
|
||||||
|
import org.hl7.fhir.r4.model.PlanDefinition;
|
||||||
|
import org.hl7.fhir.r4.model.ValueSet;
|
||||||
|
import org.opencds.cqf.fhir.api.Repository;
|
||||||
|
import org.opencds.cqf.fhir.utility.SearchHelper;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.CQF_FHIR_QUERY_PATTERN;
|
||||||
|
import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.CRMI_EFFECTIVE_DATA_REQUIREMENTS;
|
||||||
|
|
||||||
|
public class PrefetchTemplateBuilderR4 extends BasePrefetchTemplateBuilder {
|
||||||
|
|
||||||
|
public PrefetchTemplateBuilderR4(Repository theRepository) {
|
||||||
|
super(theRepository);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PrefetchUrlList getPrefetchUrlList(PlanDefinition thePlanDefinition) {
|
||||||
|
if (thePlanDefinition == null) return null;
|
||||||
|
PrefetchUrlList prefetchList = new PrefetchUrlList();
|
||||||
|
Library library = resolvePrimaryLibrary(thePlanDefinition);
|
||||||
|
if (library == null || !library.hasDataRequirement()) return null;
|
||||||
|
for (DataRequirement dataRequirement : library.getDataRequirement()) {
|
||||||
|
List<String> requestUrls = createRequestUrl(dataRequirement);
|
||||||
|
if (requestUrls != null) {
|
||||||
|
prefetchList.addAll(requestUrls);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return prefetchList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ReassignedVariable")
|
||||||
|
protected Library resolvePrimaryLibrary(PlanDefinition thePlanDefinition) {
|
||||||
|
Library library = null;
|
||||||
|
Extension dataReqExt = thePlanDefinition.getExtensionByUrl(CRMI_EFFECTIVE_DATA_REQUIREMENTS);
|
||||||
|
// Use a Module Definition Library with Effective Data Requirements for the Plan Definition if it exists
|
||||||
|
if (dataReqExt != null && dataReqExt.hasValue()) {
|
||||||
|
CanonicalType moduleDefCanonical = (CanonicalType) dataReqExt.getValue();
|
||||||
|
library = (Library) SearchHelper.searchRepositoryByCanonical(myRepository, moduleDefCanonical);
|
||||||
|
}
|
||||||
|
// Otherwise use the primary Library
|
||||||
|
if (library == null && thePlanDefinition.hasLibrary()) {
|
||||||
|
// The CPGComputablePlanDefinition profile limits the cardinality of library to 1
|
||||||
|
library = (Library) SearchHelper.searchRepositoryByCanonical(
|
||||||
|
myRepository, thePlanDefinition.getLibrary().get(0));
|
||||||
|
}
|
||||||
|
return library;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<String> createRequestUrl(DataRequirement theDataRequirement) {
|
||||||
|
List<String> urlList = new ArrayList<>();
|
||||||
|
// if we have a fhirQueryPattern extensions, use them
|
||||||
|
List<Extension> fhirQueryExtList = theDataRequirement.getExtension().stream()
|
||||||
|
.filter(e -> e.getUrl().equals(CQF_FHIR_QUERY_PATTERN) && e.hasValue())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (!fhirQueryExtList.isEmpty()) {
|
||||||
|
for (Extension fhirQueryExt : fhirQueryExtList) {
|
||||||
|
urlList.add(fhirQueryExt.getValueAsPrimitive().getValueAsString());
|
||||||
|
}
|
||||||
|
return urlList;
|
||||||
|
}
|
||||||
|
|
||||||
|
// else build the query
|
||||||
|
if (!isPatientCompartment(theDataRequirement.getType())) return null;
|
||||||
|
String baseQuery = theDataRequirement.getType() + "?"
|
||||||
|
+ getPatientSearchParam(theDataRequirement.getType())
|
||||||
|
+ "=Patient/" + PATIENT_ID_CONTEXT;
|
||||||
|
|
||||||
|
// TODO: Add valueFilter extension resolution
|
||||||
|
|
||||||
|
if (theDataRequirement.hasCodeFilter()) {
|
||||||
|
resolveCodeFilter(theDataRequirement, urlList, baseQuery);
|
||||||
|
} else {
|
||||||
|
urlList.add(baseQuery);
|
||||||
|
}
|
||||||
|
return urlList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ReassignedVariable")
|
||||||
|
protected void resolveCodeFilter(DataRequirement theDataRequirement, List<String> theUrlList, String theBaseQuery) {
|
||||||
|
for (DataRequirement.DataRequirementCodeFilterComponent codeFilterComponent :
|
||||||
|
theDataRequirement.getCodeFilter()) {
|
||||||
|
if (!codeFilterComponent.hasPath()) continue;
|
||||||
|
String path = mapCodePathToSearchParam(theDataRequirement.getType(), codeFilterComponent.getPath());
|
||||||
|
if (codeFilterComponent.hasValueSetElement()) {
|
||||||
|
for (String codes : resolveValueSetCodes(codeFilterComponent.getValueSetElement())) {
|
||||||
|
theUrlList.add(theBaseQuery + "&" + path + "=" + codes);
|
||||||
|
}
|
||||||
|
} else if (codeFilterComponent.hasCode()) {
|
||||||
|
List<Coding> codeFilterValueCodings = codeFilterComponent.getCode();
|
||||||
|
boolean isFirstCodingInFilter = true;
|
||||||
|
for (String code : resolveValueCodingCodes(codeFilterValueCodings)) {
|
||||||
|
if (isFirstCodingInFilter) {
|
||||||
|
theUrlList.add(theBaseQuery + "&" + path + "=" + code);
|
||||||
|
} else {
|
||||||
|
theUrlList.add("," + code);
|
||||||
|
}
|
||||||
|
isFirstCodingInFilter = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ReassignedVariable")
|
||||||
|
protected List<String> resolveValueCodingCodes(List<Coding> theValueCodings) {
|
||||||
|
List<String> result = new ArrayList<>();
|
||||||
|
StringBuilder codes = new StringBuilder();
|
||||||
|
for (Coding coding : theValueCodings) {
|
||||||
|
if (coding.hasCode()) {
|
||||||
|
String system = coding.getSystem();
|
||||||
|
String code = coding.getCode();
|
||||||
|
codes = getCodesStringBuilder(result, codes, system, code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.add(codes.toString());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ReassignedVariable")
|
||||||
|
protected List<String> resolveValueSetCodes(CanonicalType theValueSetId) {
|
||||||
|
ValueSet valueSet = (ValueSet) SearchHelper.searchRepositoryByCanonical(myRepository, theValueSetId);
|
||||||
|
List<String> result = new ArrayList<>();
|
||||||
|
StringBuilder codes = new StringBuilder();
|
||||||
|
if (valueSet.hasExpansion() && valueSet.getExpansion().hasContains()) {
|
||||||
|
for (ValueSet.ValueSetExpansionContainsComponent contains :
|
||||||
|
valueSet.getExpansion().getContains()) {
|
||||||
|
String system = contains.getSystem();
|
||||||
|
String code = contains.getCode();
|
||||||
|
codes = getCodesStringBuilder(result, codes, system, code);
|
||||||
|
}
|
||||||
|
} else if (valueSet.hasCompose() && valueSet.getCompose().hasInclude()) {
|
||||||
|
for (ValueSet.ConceptSetComponent concepts : valueSet.getCompose().getInclude()) {
|
||||||
|
String system = concepts.getSystem();
|
||||||
|
if (concepts.hasConcept()) {
|
||||||
|
for (ValueSet.ConceptReferenceComponent concept : concepts.getConcept()) {
|
||||||
|
String code = concept.getCode();
|
||||||
|
codes = getCodesStringBuilder(result, codes, system, code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.add(codes.toString());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ReassignedVariable")
|
||||||
|
protected StringBuilder getCodesStringBuilder(
|
||||||
|
List<String> theStrings, StringBuilder theCodes, String theSystem, String theCode) {
|
||||||
|
StringBuilder codes = theCodes;
|
||||||
|
String codeToken = theSystem + "|" + theCode;
|
||||||
|
int postAppendLength = codes.length() + codeToken.length();
|
||||||
|
if (codes.length() > 0 && postAppendLength < myMaxUriLength) {
|
||||||
|
codes.append(",");
|
||||||
|
} else if (postAppendLength > myMaxUriLength) {
|
||||||
|
theStrings.add(codes.toString());
|
||||||
|
codes = new StringBuilder();
|
||||||
|
}
|
||||||
|
codes.append(codeToken);
|
||||||
|
return codes;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String mapCodePathToSearchParam(String theDataType, String thePath) {
|
||||||
|
switch (theDataType) {
|
||||||
|
case "MedicationAdministration":
|
||||||
|
case "MedicationDispense":
|
||||||
|
case "MedicationRequest":
|
||||||
|
case "MedicationStatement":
|
||||||
|
if (thePath.equals("medication")) return "code";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (thePath.equals("vaccineCode")) return "vaccine-code";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return thePath.replace('.', '-').toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static boolean isPatientCompartment(String theDataType) {
|
||||||
|
if (theDataType == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
switch (theDataType) {
|
||||||
|
case "Account":
|
||||||
|
case "AdverseEvent":
|
||||||
|
case "AllergyIntolerance":
|
||||||
|
case "Appointment":
|
||||||
|
case "AppointmentResponse":
|
||||||
|
case "AuditEvent":
|
||||||
|
case "Basic":
|
||||||
|
case "BodyStructure":
|
||||||
|
case "CarePlan":
|
||||||
|
case "CareTeam":
|
||||||
|
case "ChargeItem":
|
||||||
|
case "Claim":
|
||||||
|
case "ClaimResponse":
|
||||||
|
case "ClinicalImpression":
|
||||||
|
case "Communication":
|
||||||
|
case "CommunicationRequest":
|
||||||
|
case "Composition":
|
||||||
|
case "Condition":
|
||||||
|
case "Consent":
|
||||||
|
case "Coverage":
|
||||||
|
case "CoverageEligibilityRequest":
|
||||||
|
case "CoverageEligibilityResponse":
|
||||||
|
case "DetectedIssue":
|
||||||
|
case "DeviceRequest":
|
||||||
|
case "DeviceUseStatement":
|
||||||
|
case "DiagnosticReport":
|
||||||
|
case "DocumentManifest":
|
||||||
|
case "DocumentReference":
|
||||||
|
case "Encounter":
|
||||||
|
case "EnrollmentRequest":
|
||||||
|
case "EpisodeOfCare":
|
||||||
|
case "ExplanationOfBenefit":
|
||||||
|
case "FamilyMemberHistory":
|
||||||
|
case "Flag":
|
||||||
|
case "Goal":
|
||||||
|
case "Group":
|
||||||
|
case "ImagingStudy":
|
||||||
|
case "Immunization":
|
||||||
|
case "ImmunizationEvaluation":
|
||||||
|
case "ImmunizationRecommendation":
|
||||||
|
case "Invoice":
|
||||||
|
case "List":
|
||||||
|
case "MeasureReport":
|
||||||
|
case "Media":
|
||||||
|
case "MedicationAdministration":
|
||||||
|
case "MedicationDispense":
|
||||||
|
case "MedicationRequest":
|
||||||
|
case "MedicationStatement":
|
||||||
|
case "MolecularSequence":
|
||||||
|
case "NutritionOrder":
|
||||||
|
case "Observation":
|
||||||
|
case "Patient":
|
||||||
|
case "Person":
|
||||||
|
case "Procedure":
|
||||||
|
case "Provenance":
|
||||||
|
case "QuestionnaireResponse":
|
||||||
|
case "RelatedPerson":
|
||||||
|
case "RequestGroup":
|
||||||
|
case "ResearchSubject":
|
||||||
|
case "RiskAssessment":
|
||||||
|
case "Schedule":
|
||||||
|
case "ServiceRequest":
|
||||||
|
case "Specimen":
|
||||||
|
case "SupplyDelivery":
|
||||||
|
case "SupplyRequest":
|
||||||
|
case "VisionPrescription":
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getPatientSearchParam(String theDataType) {
|
||||||
|
switch (theDataType) {
|
||||||
|
case "Account":
|
||||||
|
return "subject";
|
||||||
|
case "AdverseEvent":
|
||||||
|
return "subject";
|
||||||
|
case "AllergyIntolerance":
|
||||||
|
return "patient";
|
||||||
|
case "Appointment":
|
||||||
|
return "actor";
|
||||||
|
case "AppointmentResponse":
|
||||||
|
return "actor";
|
||||||
|
case "AuditEvent":
|
||||||
|
return "patient";
|
||||||
|
case "Basic":
|
||||||
|
return "patient";
|
||||||
|
case "BodyStructure":
|
||||||
|
return "patient";
|
||||||
|
case "CarePlan":
|
||||||
|
return "patient";
|
||||||
|
case "CareTeam":
|
||||||
|
return "patient";
|
||||||
|
case "ChargeItem":
|
||||||
|
return "subject";
|
||||||
|
case "Claim":
|
||||||
|
return "patient";
|
||||||
|
case "ClaimResponse":
|
||||||
|
return "patient";
|
||||||
|
case "ClinicalImpression":
|
||||||
|
return "subject";
|
||||||
|
case "Communication":
|
||||||
|
return "subject";
|
||||||
|
case "CommunicationRequest":
|
||||||
|
return "subject";
|
||||||
|
case "Composition":
|
||||||
|
return "subject";
|
||||||
|
case "Condition":
|
||||||
|
return "patient";
|
||||||
|
case "Consent":
|
||||||
|
return "patient";
|
||||||
|
case "Coverage":
|
||||||
|
return "beneficiary";
|
||||||
|
case "DetectedIssue":
|
||||||
|
return "patient";
|
||||||
|
case "DeviceRequest":
|
||||||
|
return "subject";
|
||||||
|
case "DeviceUseStatement":
|
||||||
|
return "subject";
|
||||||
|
case "DiagnosticReport":
|
||||||
|
return "subject";
|
||||||
|
case "DocumentManifest":
|
||||||
|
return "subject";
|
||||||
|
case "DocumentReference":
|
||||||
|
return "subject";
|
||||||
|
case "Encounter":
|
||||||
|
return "patient";
|
||||||
|
case "EnrollmentRequest":
|
||||||
|
return "subject";
|
||||||
|
case "EpisodeOfCare":
|
||||||
|
return "patient";
|
||||||
|
case "ExplanationOfBenefit":
|
||||||
|
return "patient";
|
||||||
|
case "FamilyMemberHistory":
|
||||||
|
return "patient";
|
||||||
|
case "Flag":
|
||||||
|
return "patient";
|
||||||
|
case "Goal":
|
||||||
|
return "patient";
|
||||||
|
case "Group":
|
||||||
|
return "member";
|
||||||
|
case "ImagingStudy":
|
||||||
|
return "patient";
|
||||||
|
case "Immunization":
|
||||||
|
return "patient";
|
||||||
|
case "ImmunizationRecommendation":
|
||||||
|
return "patient";
|
||||||
|
case "Invoice":
|
||||||
|
return "subject";
|
||||||
|
case "List":
|
||||||
|
return "subject";
|
||||||
|
case "MeasureReport":
|
||||||
|
return "patient";
|
||||||
|
case "Media":
|
||||||
|
return "subject";
|
||||||
|
case "MedicationAdministration":
|
||||||
|
return "patient";
|
||||||
|
case "MedicationDispense":
|
||||||
|
return "patient";
|
||||||
|
case "MedicationRequest":
|
||||||
|
return "subject";
|
||||||
|
case "MedicationStatement":
|
||||||
|
return "subject";
|
||||||
|
case "MolecularSequence":
|
||||||
|
return "patient";
|
||||||
|
case "NutritionOrder":
|
||||||
|
return "patient";
|
||||||
|
case "Observation":
|
||||||
|
return "subject";
|
||||||
|
case "Patient":
|
||||||
|
return "_id";
|
||||||
|
case "Person":
|
||||||
|
return "patient";
|
||||||
|
case "Procedure":
|
||||||
|
return "patient";
|
||||||
|
case "Provenance":
|
||||||
|
return "patient";
|
||||||
|
case "QuestionnaireResponse":
|
||||||
|
return "subject";
|
||||||
|
case "RelatedPerson":
|
||||||
|
return "patient";
|
||||||
|
case "RequestGroup":
|
||||||
|
return "subject";
|
||||||
|
case "ResearchSubject":
|
||||||
|
return "individual";
|
||||||
|
case "RiskAssessment":
|
||||||
|
return "subject";
|
||||||
|
case "Schedule":
|
||||||
|
return "actor";
|
||||||
|
case "ServiceRequest":
|
||||||
|
return "patient";
|
||||||
|
case "Specimen":
|
||||||
|
return "subject";
|
||||||
|
case "SupplyDelivery":
|
||||||
|
return "patient";
|
||||||
|
case "SupplyRequest":
|
||||||
|
return "subject";
|
||||||
|
case "VisionPrescription":
|
||||||
|
return "patient";
|
||||||
|
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,422 @@
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - CDS Hooks
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2024 Smile CDR, Inc.
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
|
package ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery;
|
||||||
|
|
||||||
|
import org.hl7.fhir.r5.model.CanonicalType;
|
||||||
|
import org.hl7.fhir.r5.model.Coding;
|
||||||
|
import org.hl7.fhir.r5.model.DataRequirement;
|
||||||
|
import org.hl7.fhir.r5.model.Extension;
|
||||||
|
import org.hl7.fhir.r5.model.Library;
|
||||||
|
import org.hl7.fhir.r5.model.PlanDefinition;
|
||||||
|
import org.hl7.fhir.r5.model.ValueSet;
|
||||||
|
import org.opencds.cqf.fhir.api.Repository;
|
||||||
|
import org.opencds.cqf.fhir.utility.SearchHelper;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.CQF_FHIR_QUERY_PATTERN;
|
||||||
|
import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.CRMI_EFFECTIVE_DATA_REQUIREMENTS;
|
||||||
|
|
||||||
|
public class PrefetchTemplateBuilderR5 extends BasePrefetchTemplateBuilder {
|
||||||
|
|
||||||
|
public PrefetchTemplateBuilderR5(Repository theRepository) {
|
||||||
|
super(theRepository);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PrefetchUrlList getPrefetchUrlList(PlanDefinition thePlanDefinition) {
|
||||||
|
if (thePlanDefinition == null) return null;
|
||||||
|
PrefetchUrlList prefetchList = new PrefetchUrlList();
|
||||||
|
Library library = resolvePrimaryLibrary(thePlanDefinition);
|
||||||
|
if (library == null || !library.hasDataRequirement()) return null;
|
||||||
|
for (DataRequirement dataRequirement : library.getDataRequirement()) {
|
||||||
|
List<String> requestUrls = createRequestUrl(dataRequirement);
|
||||||
|
if (requestUrls != null) {
|
||||||
|
prefetchList.addAll(requestUrls);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return prefetchList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ReassignedVariable")
|
||||||
|
protected Library resolvePrimaryLibrary(PlanDefinition thePlanDefinition) {
|
||||||
|
Library library = null;
|
||||||
|
Extension dataReqExt = thePlanDefinition.getExtensionByUrl(CRMI_EFFECTIVE_DATA_REQUIREMENTS);
|
||||||
|
// Use a Module Definition Library with Effective Data Requirements for the Plan Definition if it exists
|
||||||
|
if (dataReqExt != null && dataReqExt.hasValue()) {
|
||||||
|
CanonicalType moduleDefCanonical = (CanonicalType) dataReqExt.getValue();
|
||||||
|
library = (Library) SearchHelper.searchRepositoryByCanonical(myRepository, moduleDefCanonical);
|
||||||
|
}
|
||||||
|
// Otherwise use the primary Library
|
||||||
|
if (library == null && thePlanDefinition.hasLibrary()) {
|
||||||
|
// The CPGComputablePlanDefinition profile limits the cardinality of library to 1
|
||||||
|
library = (Library) SearchHelper.searchRepositoryByCanonical(
|
||||||
|
myRepository, thePlanDefinition.getLibrary().get(0));
|
||||||
|
}
|
||||||
|
return library;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<String> createRequestUrl(DataRequirement theDataRequirement) {
|
||||||
|
List<String> urlList = new ArrayList<>();
|
||||||
|
// if we have a fhirQueryPattern extensions, use them
|
||||||
|
List<Extension> fhirQueryExtList = theDataRequirement.getExtension().stream()
|
||||||
|
.filter(e -> e.getUrl().equals(CQF_FHIR_QUERY_PATTERN) && e.hasValue())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (!fhirQueryExtList.isEmpty()) {
|
||||||
|
for (Extension fhirQueryExt : fhirQueryExtList) {
|
||||||
|
urlList.add(fhirQueryExt.getValueAsPrimitive().getValueAsString());
|
||||||
|
}
|
||||||
|
return urlList;
|
||||||
|
}
|
||||||
|
|
||||||
|
// else build the query
|
||||||
|
if (!isPatientCompartment(theDataRequirement.getType().toCode())) return null;
|
||||||
|
String patientRelatedResource = theDataRequirement.getType() + "?"
|
||||||
|
+ getPatientSearchParam(theDataRequirement.getType().toCode())
|
||||||
|
+ "=Patient/" + PATIENT_ID_CONTEXT;
|
||||||
|
|
||||||
|
// TODO: Add valueFilter extension resolution
|
||||||
|
|
||||||
|
if (theDataRequirement.hasCodeFilter()) {
|
||||||
|
resolveCodeFilter(theDataRequirement, urlList, patientRelatedResource);
|
||||||
|
} else {
|
||||||
|
urlList.add(patientRelatedResource);
|
||||||
|
}
|
||||||
|
return urlList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ReassignedVariable")
|
||||||
|
protected void resolveCodeFilter(DataRequirement theDataRequirement, List<String> theUrlList, String theBaseQuery) {
|
||||||
|
for (DataRequirement.DataRequirementCodeFilterComponent codeFilterComponent :
|
||||||
|
theDataRequirement.getCodeFilter()) {
|
||||||
|
if (!codeFilterComponent.hasPath()) continue;
|
||||||
|
String path =
|
||||||
|
mapCodePathToSearchParam(theDataRequirement.getType().toCode(), codeFilterComponent.getPath());
|
||||||
|
if (codeFilterComponent.hasValueSetElement()) {
|
||||||
|
for (String codes : resolveValueSetCodes(codeFilterComponent.getValueSetElement())) {
|
||||||
|
theUrlList.add(theBaseQuery + "&" + path + "=" + codes);
|
||||||
|
}
|
||||||
|
} else if (codeFilterComponent.hasCode()) {
|
||||||
|
List<Coding> codeFilterValueCodings = codeFilterComponent.getCode();
|
||||||
|
boolean isFirstCodingInFilter = true;
|
||||||
|
for (String code : resolveValueCodingCodes(codeFilterValueCodings)) {
|
||||||
|
if (isFirstCodingInFilter) {
|
||||||
|
theUrlList.add(theBaseQuery + "&" + path + "=" + code);
|
||||||
|
} else {
|
||||||
|
theUrlList.add("," + code);
|
||||||
|
}
|
||||||
|
isFirstCodingInFilter = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ReassignedVariable")
|
||||||
|
protected List<String> resolveValueCodingCodes(List<Coding> theValueCodings) {
|
||||||
|
List<String> result = new ArrayList<>();
|
||||||
|
StringBuilder codes = new StringBuilder();
|
||||||
|
for (Coding coding : theValueCodings) {
|
||||||
|
if (coding.hasCode()) {
|
||||||
|
String system = coding.getSystem();
|
||||||
|
String code = coding.getCode();
|
||||||
|
codes = getCodesStringBuilder(result, codes, system, code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.add(codes.toString());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<String> resolveValueSetCodes(CanonicalType theValueSetId) {
|
||||||
|
ValueSet valueSet = (ValueSet) SearchHelper.searchRepositoryByCanonical(myRepository, theValueSetId);
|
||||||
|
List<String> result = new ArrayList<>();
|
||||||
|
StringBuilder codes = new StringBuilder();
|
||||||
|
if (valueSet.hasExpansion() && valueSet.getExpansion().hasContains()) {
|
||||||
|
for (ValueSet.ValueSetExpansionContainsComponent contains :
|
||||||
|
valueSet.getExpansion().getContains()) {
|
||||||
|
String system = contains.getSystem();
|
||||||
|
String code = contains.getCode();
|
||||||
|
|
||||||
|
codes = getCodesStringBuilder(result, codes, system, code);
|
||||||
|
}
|
||||||
|
} else if (valueSet.hasCompose() && valueSet.getCompose().hasInclude()) {
|
||||||
|
for (ValueSet.ConceptSetComponent concepts : valueSet.getCompose().getInclude()) {
|
||||||
|
String system = concepts.getSystem();
|
||||||
|
if (concepts.hasConcept()) {
|
||||||
|
for (ValueSet.ConceptReferenceComponent concept : concepts.getConcept()) {
|
||||||
|
String code = concept.getCode();
|
||||||
|
|
||||||
|
codes = getCodesStringBuilder(result, codes, system, code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result.add(codes.toString());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ReassignedVariable")
|
||||||
|
protected StringBuilder getCodesStringBuilder(
|
||||||
|
List<String> theStrings, StringBuilder theCodes, String theSystem, String theCode) {
|
||||||
|
StringBuilder codes = theCodes;
|
||||||
|
String codeToken = theSystem + "|" + theCode;
|
||||||
|
int postAppendLength = codes.length() + codeToken.length();
|
||||||
|
if (codes.length() > 0 && postAppendLength < myMaxUriLength) {
|
||||||
|
codes.append(",");
|
||||||
|
} else if (postAppendLength > myMaxUriLength) {
|
||||||
|
theStrings.add(codes.toString());
|
||||||
|
codes = new StringBuilder();
|
||||||
|
}
|
||||||
|
codes.append(codeToken);
|
||||||
|
return codes;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String mapCodePathToSearchParam(String theDataType, String thePath) {
|
||||||
|
switch (theDataType) {
|
||||||
|
case "MedicationAdministration":
|
||||||
|
if (thePath.equals("medication")) return "code";
|
||||||
|
break;
|
||||||
|
case "MedicationDispense":
|
||||||
|
if (thePath.equals("medication")) return "code";
|
||||||
|
break;
|
||||||
|
case "MedicationRequest":
|
||||||
|
if (thePath.equals("medication")) return "code";
|
||||||
|
break;
|
||||||
|
case "MedicationStatement":
|
||||||
|
if (thePath.equals("medication")) return "code";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (thePath.equals("vaccineCode")) return "vaccine-code";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return thePath.replace('.', '-').toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isPatientCompartment(String theDataType) {
|
||||||
|
if (theDataType == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
switch (theDataType) {
|
||||||
|
case "Account":
|
||||||
|
case "AdverseEvent":
|
||||||
|
case "AllergyIntolerance":
|
||||||
|
case "Appointment":
|
||||||
|
case "AppointmentResponse":
|
||||||
|
case "AuditEvent":
|
||||||
|
case "Basic":
|
||||||
|
case "BodyStructure":
|
||||||
|
case "CarePlan":
|
||||||
|
case "CareTeam":
|
||||||
|
case "ChargeItem":
|
||||||
|
case "Claim":
|
||||||
|
case "ClaimResponse":
|
||||||
|
case "ClinicalImpression":
|
||||||
|
case "Communication":
|
||||||
|
case "CommunicationRequest":
|
||||||
|
case "Composition":
|
||||||
|
case "Condition":
|
||||||
|
case "Consent":
|
||||||
|
case "Coverage":
|
||||||
|
case "CoverageEligibilityRequest":
|
||||||
|
case "CoverageEligibilityResponse":
|
||||||
|
case "DetectedIssue":
|
||||||
|
case "DeviceRequest":
|
||||||
|
case "DeviceUseStatement":
|
||||||
|
case "DiagnosticReport":
|
||||||
|
case "DocumentManifest":
|
||||||
|
case "DocumentReference":
|
||||||
|
case "Encounter":
|
||||||
|
case "EnrollmentRequest":
|
||||||
|
case "EpisodeOfCare":
|
||||||
|
case "ExplanationOfBenefit":
|
||||||
|
case "FamilyMemberHistory":
|
||||||
|
case "Flag":
|
||||||
|
case "Goal":
|
||||||
|
case "Group":
|
||||||
|
case "ImagingStudy":
|
||||||
|
case "Immunization":
|
||||||
|
case "ImmunizationEvaluation":
|
||||||
|
case "ImmunizationRecommendation":
|
||||||
|
case "Invoice":
|
||||||
|
case "List":
|
||||||
|
case "MeasureReport":
|
||||||
|
case "Media":
|
||||||
|
case "MedicationAdministration":
|
||||||
|
case "MedicationDispense":
|
||||||
|
case "MedicationRequest":
|
||||||
|
case "MedicationStatement":
|
||||||
|
case "MolecularSequence":
|
||||||
|
case "NutritionOrder":
|
||||||
|
case "Observation":
|
||||||
|
case "Patient":
|
||||||
|
case "Person":
|
||||||
|
case "Procedure":
|
||||||
|
case "Provenance":
|
||||||
|
case "QuestionnaireResponse":
|
||||||
|
case "RelatedPerson":
|
||||||
|
case "RequestGroup":
|
||||||
|
case "ResearchSubject":
|
||||||
|
case "RiskAssessment":
|
||||||
|
case "Schedule":
|
||||||
|
case "ServiceRequest":
|
||||||
|
case "Specimen":
|
||||||
|
case "SupplyDelivery":
|
||||||
|
case "SupplyRequest":
|
||||||
|
case "VisionPrescription":
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPatientSearchParam(String theDataType) {
|
||||||
|
switch (theDataType) {
|
||||||
|
case "Account":
|
||||||
|
return "subject";
|
||||||
|
case "AdverseEvent":
|
||||||
|
return "subject";
|
||||||
|
case "AllergyIntolerance":
|
||||||
|
return "patient";
|
||||||
|
case "Appointment":
|
||||||
|
return "actor";
|
||||||
|
case "AppointmentResponse":
|
||||||
|
return "actor";
|
||||||
|
case "AuditEvent":
|
||||||
|
return "patient";
|
||||||
|
case "Basic":
|
||||||
|
return "patient";
|
||||||
|
case "BodyStructure":
|
||||||
|
return "patient";
|
||||||
|
case "CarePlan":
|
||||||
|
return "patient";
|
||||||
|
case "CareTeam":
|
||||||
|
return "patient";
|
||||||
|
case "ChargeItem":
|
||||||
|
return "subject";
|
||||||
|
case "Claim":
|
||||||
|
return "patient";
|
||||||
|
case "ClaimResponse":
|
||||||
|
return "patient";
|
||||||
|
case "ClinicalImpression":
|
||||||
|
return "subject";
|
||||||
|
case "Communication":
|
||||||
|
return "subject";
|
||||||
|
case "CommunicationRequest":
|
||||||
|
return "subject";
|
||||||
|
case "Composition":
|
||||||
|
return "subject";
|
||||||
|
case "Condition":
|
||||||
|
return "patient";
|
||||||
|
case "Consent":
|
||||||
|
return "patient";
|
||||||
|
case "Coverage":
|
||||||
|
return "beneficiary";
|
||||||
|
case "DetectedIssue":
|
||||||
|
return "patient";
|
||||||
|
case "DeviceRequest":
|
||||||
|
return "subject";
|
||||||
|
case "DeviceUseStatement":
|
||||||
|
return "subject";
|
||||||
|
case "DiagnosticReport":
|
||||||
|
return "subject";
|
||||||
|
case "DocumentManifest":
|
||||||
|
return "subject";
|
||||||
|
case "DocumentReference":
|
||||||
|
return "subject";
|
||||||
|
case "Encounter":
|
||||||
|
return "patient";
|
||||||
|
case "EnrollmentRequest":
|
||||||
|
return "subject";
|
||||||
|
case "EpisodeOfCare":
|
||||||
|
return "patient";
|
||||||
|
case "ExplanationOfBenefit":
|
||||||
|
return "patient";
|
||||||
|
case "FamilyMemberHistory":
|
||||||
|
return "patient";
|
||||||
|
case "Flag":
|
||||||
|
return "patient";
|
||||||
|
case "Goal":
|
||||||
|
return "patient";
|
||||||
|
case "Group":
|
||||||
|
return "member";
|
||||||
|
case "ImagingStudy":
|
||||||
|
return "patient";
|
||||||
|
case "Immunization":
|
||||||
|
return "patient";
|
||||||
|
case "ImmunizationRecommendation":
|
||||||
|
return "patient";
|
||||||
|
case "Invoice":
|
||||||
|
return "subject";
|
||||||
|
case "List":
|
||||||
|
return "subject";
|
||||||
|
case "MeasureReport":
|
||||||
|
return "patient";
|
||||||
|
case "Media":
|
||||||
|
return "subject";
|
||||||
|
case "MedicationAdministration":
|
||||||
|
return "patient";
|
||||||
|
case "MedicationDispense":
|
||||||
|
return "patient";
|
||||||
|
case "MedicationRequest":
|
||||||
|
return "subject";
|
||||||
|
case "MedicationStatement":
|
||||||
|
return "subject";
|
||||||
|
case "MolecularSequence":
|
||||||
|
return "patient";
|
||||||
|
case "NutritionOrder":
|
||||||
|
return "patient";
|
||||||
|
case "Observation":
|
||||||
|
return "subject";
|
||||||
|
case "Patient":
|
||||||
|
return "_id";
|
||||||
|
case "Person":
|
||||||
|
return "patient";
|
||||||
|
case "Procedure":
|
||||||
|
return "patient";
|
||||||
|
case "Provenance":
|
||||||
|
return "patient";
|
||||||
|
case "QuestionnaireResponse":
|
||||||
|
return "subject";
|
||||||
|
case "RelatedPerson":
|
||||||
|
return "patient";
|
||||||
|
case "RequestGroup":
|
||||||
|
return "subject";
|
||||||
|
case "ResearchSubject":
|
||||||
|
return "individual";
|
||||||
|
case "RiskAssessment":
|
||||||
|
return "subject";
|
||||||
|
case "Schedule":
|
||||||
|
return "actor";
|
||||||
|
case "ServiceRequest":
|
||||||
|
return "patient";
|
||||||
|
case "Specimen":
|
||||||
|
return "subject";
|
||||||
|
case "SupplyDelivery":
|
||||||
|
return "patient";
|
||||||
|
case "SupplyRequest":
|
||||||
|
return "subject";
|
||||||
|
case "VisionPrescription":
|
||||||
|
return "patient";
|
||||||
|
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,5 +40,4 @@ public class CrDiscoveryServiceR4Test extends BaseCrTest {
|
||||||
"}";
|
"}";
|
||||||
assertEquals(expected, actual);
|
assertEquals(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
package ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.util.ClasspathUtil;
|
||||||
|
import ca.uhn.hapi.fhir.cdshooks.svc.cr.BaseCrTest;
|
||||||
|
import org.hl7.fhir.r4.model.CanonicalType;
|
||||||
|
import org.hl7.fhir.r4.model.Library;
|
||||||
|
import org.hl7.fhir.r4.model.PlanDefinition;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Spy;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import org.opencds.cqf.fhir.api.Repository;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import static ca.uhn.hapi.fhir.cdshooks.svc.cr.CdsCrConstants.CRMI_EFFECTIVE_DATA_REQUIREMENTS;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.mockito.Mockito.doReturn;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class PrefetchTemplateBuilderR4Test extends BaseCrTest {
|
||||||
|
@Mock
|
||||||
|
Repository myRepository;
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
@Spy
|
||||||
|
PrefetchTemplateBuilderR4 myFixture;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testR4DiscoveryServiceWithEffectiveDataRequirements() {
|
||||||
|
PlanDefinition planDefinition = new PlanDefinition();
|
||||||
|
planDefinition.addExtension(CRMI_EFFECTIVE_DATA_REQUIREMENTS,
|
||||||
|
new CanonicalType("http://hl7.org/fhir/uv/crmi/Library/moduledefinition-example"));
|
||||||
|
planDefinition.setId("ModuleDefinitionTest");
|
||||||
|
Library library = ClasspathUtil.loadResource(myFhirContext, Library.class, "ModuleDefinitionExample.json");
|
||||||
|
doReturn(library).when(myFixture).resolvePrimaryLibrary(planDefinition);
|
||||||
|
PrefetchUrlList actual = myFixture.getPrefetchUrlList(planDefinition);
|
||||||
|
assertNotNull(actual);
|
||||||
|
PrefetchUrlList expected = new PrefetchUrlList();
|
||||||
|
expected.addAll(Arrays.asList("Patient?_id={{context.patientId}}",
|
||||||
|
"Encounter?status=finished&subject=Patient/{{context.patientId}}&type:in=http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.117.1.7.1.292",
|
||||||
|
"Coverage?policy-holder=Patient/{{context.patientId}}&type:in=http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.3591"));
|
||||||
|
assertEquals(expected, actual);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,298 @@
|
||||||
|
{
|
||||||
|
"resourceType": "Library",
|
||||||
|
"id": "moduledefinition-example",
|
||||||
|
"meta": {
|
||||||
|
"profile": [
|
||||||
|
"http://hl7.org/fhir/uv/crmi/StructureDefinition/crmi-moduledefinitionlibrary"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"url": "http://hl7.org/fhir/uv/crmi/Library/moduledefinition-example",
|
||||||
|
"identifier": [
|
||||||
|
{
|
||||||
|
"use": "official",
|
||||||
|
"system": "http://example.org/fhir/cqi/ecqm/Library/Identifier",
|
||||||
|
"value": "EXMLogic"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"system": "urn:ietf:rfc:3986",
|
||||||
|
"value": "urn:oid:2.16.840.1.113883.4.642.40.38.28.7"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version": "1.0.0-snapshot",
|
||||||
|
"name": "EXMLogicModuleDefinition",
|
||||||
|
"title": "Example Logic Library - Module Definition",
|
||||||
|
"status": "active",
|
||||||
|
"experimental": true,
|
||||||
|
"type": {
|
||||||
|
"coding": [
|
||||||
|
{
|
||||||
|
"system": "http://terminology.hl7.org/CodeSystem/library-type",
|
||||||
|
"code": "module-definition"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"date": "2019-09-03",
|
||||||
|
"publisher": "HL7 International / Clinical Decision Support",
|
||||||
|
"contact": [
|
||||||
|
{
|
||||||
|
"telecom": [
|
||||||
|
{
|
||||||
|
"system": "url",
|
||||||
|
"value": "http://www.hl7.org/Special/committees/dss"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "This library is used as an example module definition in the FHIR Quality Measure Implementation Guide",
|
||||||
|
"jurisdiction": [
|
||||||
|
{
|
||||||
|
"coding": [
|
||||||
|
{
|
||||||
|
"system": "http://unstats.un.org/unsd/methods/m49/m49.htm",
|
||||||
|
"code": "001",
|
||||||
|
"display": "World"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"relatedArtifact": [
|
||||||
|
{
|
||||||
|
"type": "depends-on",
|
||||||
|
"display": "FHIR model information",
|
||||||
|
"resource": "http://fhir.org/guides/cqf/common/Library/FHIR-ModelInfo|4.0.1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "depends-on",
|
||||||
|
"display": "Library FHIRHelpers",
|
||||||
|
"resource": "http://fhir.org/guides/cqf/common/Library/FHIRHelpers|4.0.1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "depends-on",
|
||||||
|
"display": "Code system Diagnosis Role",
|
||||||
|
"resource": "http://terminology.hl7.org/CodeSystem/diagnosis-role"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "depends-on",
|
||||||
|
"display": "Value set Emergency Department Visit",
|
||||||
|
"resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.117.1.7.1.292"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "depends-on",
|
||||||
|
"display": "Value set Psychiatric/Mental Health Patient",
|
||||||
|
"resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.117.1.7.1.299"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "depends-on",
|
||||||
|
"display": "Value set Hospital Settings",
|
||||||
|
"resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1111.126"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "depends-on",
|
||||||
|
"display": "Value set ONC Administrative Sex",
|
||||||
|
"resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "depends-on",
|
||||||
|
"display": "Value set Race",
|
||||||
|
"resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.836"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "depends-on",
|
||||||
|
"display": "Value set Ethnicity",
|
||||||
|
"resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.837"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "depends-on",
|
||||||
|
"display": "Value set Payer",
|
||||||
|
"resource": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.3591"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameter": [
|
||||||
|
{
|
||||||
|
"name": "Measurement Period",
|
||||||
|
"use": "in",
|
||||||
|
"min": 0,
|
||||||
|
"max": "1",
|
||||||
|
"type": "Period"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Patient",
|
||||||
|
"use": "out",
|
||||||
|
"min": 0,
|
||||||
|
"max": "1",
|
||||||
|
"type": "Patient"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Inpatient Encounter",
|
||||||
|
"use": "out",
|
||||||
|
"min": 0,
|
||||||
|
"max": "*",
|
||||||
|
"type": "Encounter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Initial Population",
|
||||||
|
"use": "out",
|
||||||
|
"min": 0,
|
||||||
|
"max": "*",
|
||||||
|
"type": "Encounter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Measure Population",
|
||||||
|
"use": "out",
|
||||||
|
"min": 0,
|
||||||
|
"max": "*",
|
||||||
|
"type": "Encounter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Stratifier 1",
|
||||||
|
"use": "out",
|
||||||
|
"min": 0,
|
||||||
|
"max": "*",
|
||||||
|
"type": "Encounter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Stratifier 2",
|
||||||
|
"use": "out",
|
||||||
|
"min": 0,
|
||||||
|
"max": "*",
|
||||||
|
"type": "Encounter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Stratifier 3",
|
||||||
|
"use": "out",
|
||||||
|
"min": 0,
|
||||||
|
"max": "*",
|
||||||
|
"type": "Encounter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Stratifier 4",
|
||||||
|
"use": "out",
|
||||||
|
"min": 0,
|
||||||
|
"max": "*",
|
||||||
|
"type": "Encounter"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "SDE Ethnicity",
|
||||||
|
"use": "out",
|
||||||
|
"min": 0,
|
||||||
|
"max": "*",
|
||||||
|
"type": "Coding"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "SDE Payer",
|
||||||
|
"use": "out",
|
||||||
|
"min": 0,
|
||||||
|
"max": "*",
|
||||||
|
"type": "Resource"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "SDE Race",
|
||||||
|
"use": "out",
|
||||||
|
"min": 0,
|
||||||
|
"max": "*",
|
||||||
|
"type": "Coding"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "SDE Sex",
|
||||||
|
"use": "out",
|
||||||
|
"min": 0,
|
||||||
|
"max": "1",
|
||||||
|
"type": "Coding"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dataRequirement": [
|
||||||
|
{
|
||||||
|
"extension": [
|
||||||
|
{
|
||||||
|
"url": "http://hl7.org/fhir/StructureDefinition/cqf-fhirQueryPattern",
|
||||||
|
"valueString": "Patient?_id={{context.patientId}}"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "Patient",
|
||||||
|
"profile": [
|
||||||
|
"http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-patient"
|
||||||
|
],
|
||||||
|
"mustSupport": [
|
||||||
|
"extension('http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity')"
|
||||||
|
],
|
||||||
|
"_mustSupport": [
|
||||||
|
{
|
||||||
|
"extension": [
|
||||||
|
{
|
||||||
|
"url": "http://hl7.org/fhir/StructureDefinition/rendered-value",
|
||||||
|
"valueString": "ethnicity"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"extension": [
|
||||||
|
{
|
||||||
|
"url": "http://hl7.org/fhir/StructureDefinition/cqf-fhirQueryPattern",
|
||||||
|
"valueString": "Encounter?status=finished&subject=Patient/{{context.patientId}}&type:in=http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.117.1.7.1.292"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "http://hl7.org/fhir/StructureDefinition/cqf-isSelective",
|
||||||
|
"valueBoolean": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"extension": [
|
||||||
|
{
|
||||||
|
"url": "path",
|
||||||
|
"valueString": "status"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "comparator",
|
||||||
|
"valueCode": "eq"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "value",
|
||||||
|
"valueString": "finished"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"url": "http://hl7.org/fhir/StructureDefinition/cqf-valueFilter"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "Encounter",
|
||||||
|
"profile": [
|
||||||
|
"http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-encounter"
|
||||||
|
],
|
||||||
|
"codeFilter": [
|
||||||
|
{
|
||||||
|
"path": "type",
|
||||||
|
"valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.117.1.7.1.292"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "Condition",
|
||||||
|
"profile": [
|
||||||
|
"http://hl7.org/fhir/StructureDefinition/Condition"
|
||||||
|
],
|
||||||
|
"codeFilter": [
|
||||||
|
{
|
||||||
|
"path": "id"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"extension": [
|
||||||
|
{
|
||||||
|
"url": "http://hl7.org/fhir/StructureDefinition/cqf-fhirQueryPattern",
|
||||||
|
"valueString": "Coverage?policy-holder=Patient/{{context.patientId}}&type:in=http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.3591"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": "Coverage",
|
||||||
|
"profile": [
|
||||||
|
"http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-coverage"
|
||||||
|
],
|
||||||
|
"codeFilter": [
|
||||||
|
{
|
||||||
|
"path": "type",
|
||||||
|
"valueSet": "http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.114222.4.11.3591"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -21,9 +21,9 @@ package ca.uhn.fhir.rest.server.mail;
|
||||||
|
|
||||||
import jakarta.annotation.Nonnull;
|
import jakarta.annotation.Nonnull;
|
||||||
import org.simplejavamail.api.email.Email;
|
import org.simplejavamail.api.email.Email;
|
||||||
import org.simplejavamail.api.mailer.AsyncResponse;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public interface IMailSvc {
|
public interface IMailSvc {
|
||||||
void sendMail(@Nonnull List<Email> theEmails);
|
void sendMail(@Nonnull List<Email> theEmails);
|
||||||
|
@ -31,7 +31,5 @@ public interface IMailSvc {
|
||||||
void sendMail(@Nonnull Email theEmail);
|
void sendMail(@Nonnull Email theEmail);
|
||||||
|
|
||||||
void sendMail(
|
void sendMail(
|
||||||
@Nonnull Email theEmail,
|
@Nonnull Email theEmail, @Nonnull Runnable theOnSuccess, @Nonnull Consumer<Throwable> theErrorHandler);
|
||||||
@Nonnull Runnable theOnSuccess,
|
|
||||||
@Nonnull AsyncResponse.ExceptionConsumer theErrorHandler);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,12 +20,9 @@
|
||||||
package ca.uhn.fhir.rest.server.mail;
|
package ca.uhn.fhir.rest.server.mail;
|
||||||
|
|
||||||
import jakarta.annotation.Nonnull;
|
import jakarta.annotation.Nonnull;
|
||||||
import org.apache.commons.lang3.Validate;
|
|
||||||
import org.simplejavamail.MailException;
|
import org.simplejavamail.MailException;
|
||||||
import org.simplejavamail.api.email.Email;
|
import org.simplejavamail.api.email.Email;
|
||||||
import org.simplejavamail.api.email.Recipient;
|
import org.simplejavamail.api.email.Recipient;
|
||||||
import org.simplejavamail.api.mailer.AsyncResponse;
|
|
||||||
import org.simplejavamail.api.mailer.AsyncResponse.ExceptionConsumer;
|
|
||||||
import org.simplejavamail.api.mailer.Mailer;
|
import org.simplejavamail.api.mailer.Mailer;
|
||||||
import org.simplejavamail.api.mailer.config.TransportStrategy;
|
import org.simplejavamail.api.mailer.config.TransportStrategy;
|
||||||
import org.simplejavamail.mailer.MailerBuilder;
|
import org.simplejavamail.mailer.MailerBuilder;
|
||||||
|
@ -33,6 +30,8 @@ import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class MailSvc implements IMailSvc {
|
public class MailSvc implements IMailSvc {
|
||||||
|
@ -42,14 +41,14 @@ public class MailSvc implements IMailSvc {
|
||||||
private final Mailer myMailer;
|
private final Mailer myMailer;
|
||||||
|
|
||||||
public MailSvc(@Nonnull MailConfig theMailConfig) {
|
public MailSvc(@Nonnull MailConfig theMailConfig) {
|
||||||
Validate.notNull(theMailConfig);
|
Objects.requireNonNull(theMailConfig);
|
||||||
myMailConfig = theMailConfig;
|
myMailConfig = theMailConfig;
|
||||||
myMailer = makeMailer(myMailConfig);
|
myMailer = makeMailer(myMailConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendMail(@Nonnull List<Email> theEmails) {
|
public void sendMail(@Nonnull List<Email> theEmails) {
|
||||||
Validate.notNull(theEmails);
|
Objects.requireNonNull(theEmails);
|
||||||
theEmails.forEach(theEmail -> send(theEmail, new OnSuccess(theEmail), new ErrorHandler(theEmail)));
|
theEmails.forEach(theEmail -> send(theEmail, new OnSuccess(theEmail), new ErrorHandler(theEmail)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,21 +59,23 @@ public class MailSvc implements IMailSvc {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendMail(
|
public void sendMail(
|
||||||
@Nonnull Email theEmail, @Nonnull Runnable theOnSuccess, @Nonnull ExceptionConsumer theErrorHandler) {
|
@Nonnull Email theEmail, @Nonnull Runnable theOnSuccess, @Nonnull Consumer<Throwable> theErrorHandler) {
|
||||||
send(theEmail, theOnSuccess, theErrorHandler);
|
send(theEmail, theOnSuccess, theErrorHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void send(
|
private void send(
|
||||||
@Nonnull Email theEmail, @Nonnull Runnable theOnSuccess, @Nonnull ExceptionConsumer theErrorHandler) {
|
@Nonnull Email theEmail, @Nonnull Runnable theOnSuccess, @Nonnull Consumer<Throwable> theErrorHandler) {
|
||||||
Validate.notNull(theEmail);
|
Objects.requireNonNull(theEmail);
|
||||||
Validate.notNull(theOnSuccess);
|
Objects.requireNonNull(theOnSuccess);
|
||||||
Validate.notNull(theErrorHandler);
|
Objects.requireNonNull(theErrorHandler);
|
||||||
try {
|
try {
|
||||||
final AsyncResponse asyncResponse = myMailer.sendMail(theEmail, true);
|
myMailer.sendMail(theEmail, true).whenComplete((result, ex) -> {
|
||||||
if (asyncResponse != null) {
|
if (ex != null) {
|
||||||
asyncResponse.onSuccess(theOnSuccess);
|
theErrorHandler.accept(ex);
|
||||||
asyncResponse.onException(theErrorHandler);
|
} else {
|
||||||
|
theOnSuccess.run();
|
||||||
}
|
}
|
||||||
|
});
|
||||||
} catch (MailException e) {
|
} catch (MailException e) {
|
||||||
theErrorHandler.accept(e);
|
theErrorHandler.accept(e);
|
||||||
}
|
}
|
||||||
|
@ -117,7 +118,7 @@ public class MailSvc implements IMailSvc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ErrorHandler implements ExceptionConsumer {
|
private class ErrorHandler implements Consumer<Throwable> {
|
||||||
private final Email myEmail;
|
private final Email myEmail;
|
||||||
|
|
||||||
private ErrorHandler(@Nonnull Email theEmail) {
|
private ErrorHandler(@Nonnull Email theEmail) {
|
||||||
|
@ -125,7 +126,7 @@ public class MailSvc implements IMailSvc {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void accept(Exception t) {
|
public void accept(Throwable t) {
|
||||||
ourLog.error("Email not sent" + makeMessage(myEmail), t);
|
ourLog.error("Email not sent" + makeMessage(myEmail), t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import com.icegreen.greenmail.junit5.GreenMailExtension;
|
||||||
import com.icegreen.greenmail.util.GreenMailUtil;
|
import com.icegreen.greenmail.util.GreenMailUtil;
|
||||||
import com.icegreen.greenmail.util.ServerSetupTest;
|
import com.icegreen.greenmail.util.ServerSetupTest;
|
||||||
import jakarta.annotation.Nonnull;
|
import jakarta.annotation.Nonnull;
|
||||||
|
import jakarta.mail.internet.MimeMessage;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
|
@ -11,7 +12,6 @@ import org.simplejavamail.MailException;
|
||||||
import org.simplejavamail.api.email.Email;
|
import org.simplejavamail.api.email.Email;
|
||||||
import org.simplejavamail.email.EmailBuilder;
|
import org.simplejavamail.email.EmailBuilder;
|
||||||
|
|
||||||
import javax.mail.internet.MimeMessage;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -86,13 +86,14 @@ public class MailSvcIT {
|
||||||
@Test
|
@Test
|
||||||
public void testSendMailWithInvalidToAddressExpectErrorHandler() {
|
public void testSendMailWithInvalidToAddressExpectErrorHandler() {
|
||||||
// setup
|
// setup
|
||||||
final Email email = withEmail("xyz");
|
String invalidEmailAdress = "xyz";
|
||||||
|
final Email email = withEmail(invalidEmailAdress);
|
||||||
// execute
|
// execute
|
||||||
fixture.sendMail(email,
|
fixture.sendMail(email,
|
||||||
() -> fail("Should not execute on Success"),
|
() -> fail("Should not execute on Success"),
|
||||||
(e) -> {
|
(e) -> {
|
||||||
assertTrue(e instanceof MailException);
|
assertTrue(e instanceof MailException);
|
||||||
assertEquals("Invalid TO address: " + email, e.getMessage());
|
assertEquals("Invalid TO address: " + invalidEmailAdress, e.getMessage());
|
||||||
});
|
});
|
||||||
// validate
|
// validate
|
||||||
assertTrue(ourGreenMail.waitForIncomingEmail(1000, 0));
|
assertTrue(ourGreenMail.waitForIncomingEmail(1000, 0));
|
||||||
|
|
34
pom.xml
34
pom.xml
|
@ -869,6 +869,7 @@
|
||||||
<developer>
|
<developer>
|
||||||
<id>delopst</id>
|
<id>delopst</id>
|
||||||
<name>Primož Delopst</name>
|
<name>Primož Delopst</name>
|
||||||
|
<organization>Better</organization>
|
||||||
</developer>
|
</developer>
|
||||||
<developer>
|
<developer>
|
||||||
<id>Zach Smith</id>
|
<id>Zach Smith</id>
|
||||||
|
@ -1160,27 +1161,38 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.simplejavamail</groupId>
|
<groupId>org.simplejavamail</groupId>
|
||||||
<artifactId>simple-java-mail</artifactId>
|
<artifactId>simple-java-mail</artifactId>
|
||||||
<version>6.6.1</version>
|
<version>8.11.2</version>
|
||||||
<exclusions>
|
<exclusions>
|
||||||
<exclusion>
|
<exclusion>
|
||||||
<groupId>com.sun.activation</groupId>
|
<groupId>com.github.bbottema</groupId>
|
||||||
<artifactId>jakarta.activation-api</artifactId>
|
<artifactId>jetbrains-runtime-annotations</artifactId>
|
||||||
</exclusion>
|
</exclusion>
|
||||||
<exclusion>
|
<exclusion>
|
||||||
<groupId>com.sun.activation</groupId>
|
<groupId>jakarta.mail</groupId>
|
||||||
<artifactId>jakarta.activation</artifactId>
|
<artifactId>jakarta.mail-api</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>jakarta.mail</groupId>
|
||||||
|
<artifactId>jakarta.mail-api</artifactId>
|
||||||
|
<version>2.1.3</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.icegreen</groupId>
|
||||||
|
<artifactId>greenmail</artifactId>
|
||||||
|
<version>2.1.0-rc-1</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>jakarta.mail</groupId>
|
||||||
|
<artifactId>jakarta.mail-api</artifactId>
|
||||||
</exclusion>
|
</exclusion>
|
||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>com.icegreen</groupId>
|
|
||||||
<artifactId>greenmail</artifactId>
|
|
||||||
<version>1.6.4</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.icegreen</groupId>
|
<groupId>com.icegreen</groupId>
|
||||||
<artifactId>greenmail-junit5</artifactId>
|
<artifactId>greenmail-junit5</artifactId>
|
||||||
<version>1.6.4</version>
|
<version>2.1.0-rc-1</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- mail end -->
|
<!-- mail end -->
|
||||||
|
|
Loading…
Reference in New Issue