Ks 20230324 include prov (#4685)

* fix test

* fixed

* changelog

* add another test

* improve docs

* fix change log

* fix test

* fix test

---------

Co-authored-by: Ken Stevens <ken@smilecdr.com>
This commit is contained in:
Ken Stevens 2023-03-25 17:50:12 -04:00 committed by GitHub
parent 7d26a7a38d
commit 47262f5bd5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 91 additions and 11 deletions

View File

@ -0,0 +1,5 @@
---
type: change
issue: 4685
title: "When calling $everything on a Patient instance, the jpa server no longer traverses into other patients.
Previously it was possible to pull in data from another patient for example, via a Provenance resource."

View File

@ -125,7 +125,6 @@ import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.apache.commons.lang3.StringUtils.countMatches;
import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.defaultString;
import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank;
@ -1173,6 +1172,11 @@ public class SearchBuilder implements ISearchBuilder<JpaPid> {
} else { } else {
wantResourceType = null; wantResourceType = null;
} }
// When calling $everything on a Patient instance, we don't want to recurse into new Patient resources
// (e.g. via Provenance, List, or Group) when in an $everything operation
if (myParams != null && myParams.getEverythingMode() == SearchParameterMap.EverythingModeEnum.PATIENT_INSTANCE) {
sqlBuilder.append(" AND r.myTargetResourceType != 'Patient'");
}
String sql = sqlBuilder.toString(); String sql = sqlBuilder.toString();
List<Collection<JpaPid>> partitions = partition(nextRoundMatches, getMaximumPageSize()); List<Collection<JpaPid>> partitions = partition(nextRoundMatches, getMaximumPageSize());

View File

@ -10,6 +10,7 @@ import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpGet;
import org.hl7.fhir.dstu3.model.Bundle; import org.hl7.fhir.dstu3.model.Bundle;
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent; import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.dstu3.model.CarePlan;
import org.hl7.fhir.dstu3.model.Encounter; import org.hl7.fhir.dstu3.model.Encounter;
import org.hl7.fhir.dstu3.model.Encounter.EncounterStatus; import org.hl7.fhir.dstu3.model.Encounter.EncounterStatus;
import org.hl7.fhir.dstu3.model.Observation; import org.hl7.fhir.dstu3.model.Observation;
@ -142,16 +143,17 @@ public class PatientEverythingDstu3Test extends BaseResourceProviderDstu3Test {
@Test @Test
public void testEverythingHandlesCircularReferences() throws Exception { public void testEverythingHandlesCircularReferences() throws Exception {
Patient linkedPatient1 = new Patient(); CarePlan cp1 = new CarePlan();
linkedPatient1.addLink().setOther(new Reference(myPatientId)); cp1.setSubject(new Reference(myPatientId));
String linkedPatient1Id = myClient.create().resource(linkedPatient1).execute().getId().toUnqualifiedVersionless().getValue(); String cp1Id = myClient.create().resource(cp1).execute().getId().toUnqualifiedVersionless().getValue();
Patient linkedPatient2 = new Patient(); CarePlan cp2 = new CarePlan();
linkedPatient2.addLink().setOther(new Reference(linkedPatient1Id)); cp2.addBasedOn(new Reference(cp1Id));
String linkedPatient2Id = myClient.create().resource(linkedPatient2).execute().getId().toUnqualifiedVersionless().getValue(); String cp2Id = myClient.create().resource(cp2).execute().getId().toUnqualifiedVersionless().getValue();
myPatient.addLink().setOther(new Reference(linkedPatient2Id)); cp1.addBasedOn(new Reference(cp2Id));
myClient.update().resource(myPatient).execute(); cp1.setId(cp1Id);
myClient.update().resource(cp1).execute();
Bundle bundle = fetchBundle(myServerBase + "/" + myPatientId + "/$everything?_format=json&_count=100", EncodingEnum.JSON); Bundle bundle = fetchBundle(myServerBase + "/" + myPatientId + "/$everything?_format=json&_count=100", EncodingEnum.JSON);
@ -165,8 +167,8 @@ public class PatientEverythingDstu3Test extends BaseResourceProviderDstu3Test {
ourLog.info("Found IDs: {}", actual); ourLog.info("Found IDs: {}", actual);
assertThat(actual, hasItem(myPatientId)); assertThat(actual, hasItem(myPatientId));
assertThat(actual, hasItem(linkedPatient1Id)); assertThat(actual, hasItem(cp1Id));
assertThat(actual, hasItem(linkedPatient2Id)); assertThat(actual, hasItem(cp2Id));
assertThat(actual, hasItem(encId1)); assertThat(actual, hasItem(encId1));
assertThat(actual, hasItem(encId2)); assertThat(actual, hasItem(encId2));
assertThat(actual, hasItem(myOrgId)); assertThat(actual, hasItem(myOrgId));

View File

@ -16,6 +16,7 @@ import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.term.ZipCollectionBuilder; import ca.uhn.fhir.jpa.term.ZipCollectionBuilder;
import ca.uhn.fhir.jpa.test.config.TestR4Config; import ca.uhn.fhir.jpa.test.config.TestR4Config;
import ca.uhn.fhir.jpa.util.QueryParameterUtils; import ca.uhn.fhir.jpa.util.QueryParameterUtils;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.StorageResponseCodeEnum; import ca.uhn.fhir.model.api.StorageResponseCodeEnum;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum; import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
@ -138,6 +139,7 @@ import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.Period; import org.hl7.fhir.r4.model.Period;
import org.hl7.fhir.r4.model.Practitioner; import org.hl7.fhir.r4.model.Practitioner;
import org.hl7.fhir.r4.model.Procedure; import org.hl7.fhir.r4.model.Procedure;
import org.hl7.fhir.r4.model.Provenance;
import org.hl7.fhir.r4.model.Quantity; import org.hl7.fhir.r4.model.Quantity;
import org.hl7.fhir.r4.model.Questionnaire; import org.hl7.fhir.r4.model.Questionnaire;
import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemType; import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemType;
@ -3763,6 +3765,73 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
assertEquals(null, response.getLink("next")); assertEquals(null, response.getLink("next"));
} }
@Test
public void testEverythingDoesNotEnterSecondPatient() {
Patient goodPatient = new Patient();
goodPatient.setActive(true);
String goodPid = myPatientDao.create(goodPatient, mySrd).getId().toUnqualifiedVersionless().getValue();
Patient badPatient = new Patient();
badPatient.setActive(true);
String badPid = myPatientDao.create(badPatient, mySrd).getId().toUnqualifiedVersionless().getValue();
Observation o = new Observation();
o.getSubject().setReference(goodPid);
o.addIdentifier().setSystem("foo").setValue("1");
String oid = myObservationDao.create(o, mySrd).getId().toUnqualifiedVersionless().getValue();
Provenance prov = new Provenance();
prov.addTarget().setReference(goodPid);
prov.addTarget().setReference(badPid);
String provid = myProvenanceDao.create(prov, mySrd).getId().toUnqualifiedVersionless().getValue();
Bundle response = myClient
.operation()
.onInstance(new IdType(goodPid))
.named("everything")
.withNoParameters(Parameters.class)
.returnResourceType(Bundle.class)
.execute();
List<String> ids = toUnqualifiedVersionlessIdValues(response);
// We should not pick up other resources via the provenance
assertThat(ids, containsInAnyOrder(goodPid, oid, provid));
}
@Test
public void testIncludeRecurseFromProvenanceDoesTraverse() {
Patient goodPatient = new Patient();
goodPatient.setActive(true);
String goodPid = myPatientDao.create(goodPatient, mySrd).getId().toUnqualifiedVersionless().getValue();
Practitioner prac = new Practitioner();
prac.addName().setFamily("FAM");
String pracid = myPractitionerDao.create(prac, mySrd).getId().toUnqualifiedVersionless().getValue();
Patient otherPatient = new Patient();
otherPatient.setActive(true);
otherPatient.addGeneralPractitioner().setReference(pracid);
String otherPid = myPatientDao.create(otherPatient, mySrd).getId().toUnqualifiedVersionless().getValue();
Provenance prov = new Provenance();
prov.addTarget().setReference(goodPid);
prov.addTarget().setReference(otherPid);
String provid = myProvenanceDao.create(prov, mySrd).getId().toUnqualifiedVersionless().getValue();
Bundle response = myClient
.search()
.forResource("Provenance")
.where(Provenance.TARGET.hasId(goodPid))
.include(new Include("*", true))
.returnBundle(Bundle.class)
.execute();
List<String> ids = toUnqualifiedVersionlessIdValues(response);
// We should not pick up other resources via the provenance
assertThat(ids, containsInAnyOrder(goodPid, otherPid, pracid, provid));
}
@Test @Test
public void testParseAndEncodeExtensionWithValueWithExtension() throws IOException { public void testParseAndEncodeExtensionWithValueWithExtension() throws IOException {
String input = "<Patient xmlns=\"http://hl7.org/fhir\">\n" + String input = "<Patient xmlns=\"http://hl7.org/fhir\">\n" +