2669 - Cache-Control no-store returns excessive resources (#2671)

* First attempt at a Unit Test.

* Removed sample test data.

* Added failing Unit Test - now to find out why.

* Added a _count param to more exactly match the provided customer scenario.

* Added a possible fix for this sceanrio.

* Work on this

* Have a working fix, not yet fully tested

* Test fix

Co-authored-by: jamesagnew <jamesagnew@gmail.com>
This commit is contained in:
Kevin Dougan SmileCDR 2021-05-25 08:49:25 -04:00 committed by GitHub
parent 2c722e64c2
commit 6167fb2415
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 10066 additions and 41 deletions

View File

@ -34,6 +34,7 @@ import javax.annotation.Nonnull;
import javax.persistence.EntityManager; import javax.persistence.EntityManager;
import java.util.Collection; import java.util.Collection;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
@ -48,7 +49,7 @@ public interface ISearchBuilder {
void loadResourcesByPid(Collection<ResourcePersistentId> thePids, Collection<ResourcePersistentId> theIncludedPids, List<IBaseResource> theResourceListToPopulate, boolean theForHistoryOperation, RequestDetails theDetails); void loadResourcesByPid(Collection<ResourcePersistentId> thePids, Collection<ResourcePersistentId> theIncludedPids, List<IBaseResource> theResourceListToPopulate, boolean theForHistoryOperation, RequestDetails theDetails);
Set<ResourcePersistentId> loadIncludes(FhirContext theContext, EntityManager theEntityManager, Collection<ResourcePersistentId> theMatches, Set<Include> theRevIncludes, boolean theReverseMode, Set<ResourcePersistentId> loadIncludes(FhirContext theContext, EntityManager theEntityManager, Collection<ResourcePersistentId> theMatches, Set<Include> theRevIncludes, boolean theReverseMode,
DateRangeParam theLastUpdated, String theSearchIdOrDescription, RequestDetails theRequest); DateRangeParam theLastUpdated, String theSearchIdOrDescription, RequestDetails theRequest);
/** /**
* How many results may be fetched at once * How many results may be fetched at once

View File

@ -804,11 +804,6 @@ public class LegacySearchBuilder implements ISearchBuilder {
pidsToInclude = new HashSet<>(filterResourceIdsByLastUpdated(theEntityManager, theLastUpdated, pidsToInclude)); pidsToInclude = new HashSet<>(filterResourceIdsByLastUpdated(theEntityManager, theLastUpdated, pidsToInclude));
} }
} }
for (ResourcePersistentId next : pidsToInclude) {
if (original.contains(next) == false && allAdded.contains(next) == false) {
theMatches.add(next);
}
}
addedSomeThisRound = allAdded.addAll(pidsToInclude); addedSomeThisRound = allAdded.addAll(pidsToInclude);
nextRoundMatches = pidsToInclude; nextRoundMatches = pidsToInclude;
@ -830,16 +825,16 @@ public class LegacySearchBuilder implements ISearchBuilder {
.addIfMatchesType(ServletRequestDetails.class, theRequest); .addIfMatchesType(ServletRequestDetails.class, theRequest);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PREACCESS_RESOURCES, params); JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PREACCESS_RESOURCES, params);
allAdded = new HashSet<>(includedPidList);
for (int i = includedPidList.size() - 1; i >= 0; i--) { for (int i = includedPidList.size() - 1; i >= 0; i--) {
if (accessDetails.isDontReturnResourceAtIndex(i)) { if (accessDetails.isDontReturnResourceAtIndex(i)) {
ResourcePersistentId value = includedPidList.remove(i); ResourcePersistentId value = includedPidList.remove(i);
if (value != null) { if (value != null) {
theMatches.remove(value); allAdded.remove(value);
} }
} }
} }
allAdded = new HashSet<>(includedPidList);
} }
return allAdded; return allAdded;
@ -1047,6 +1042,7 @@ public class LegacySearchBuilder implements ISearchBuilder {
myNext = NO_MORE; myNext = NO_MORE;
break; break;
} }
myCurrentPids.addAll(newPids);
myCurrentIterator = newPids.iterator(); myCurrentIterator = newPids.iterator();
} }
} }

View File

@ -416,6 +416,7 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
} }
List<ResourcePersistentId> includedPidList = new ArrayList<>(includedPids); List<ResourcePersistentId> includedPidList = new ArrayList<>(includedPids);
thePids.addAll(includedPidList);
// Execute the query and make sure we return distinct results // Execute the query and make sure we return distinct results
List<IBaseResource> resources = new ArrayList<>(); List<IBaseResource> resources = new ArrayList<>();

View File

@ -533,12 +533,14 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
* individually for pages as we return them to clients * individually for pages as we return them to clients
*/ */
final Set<ResourcePersistentId> includedPids = new HashSet<>(); final Set<ResourcePersistentId> includedPids = new HashSet<>();
includedPids.addAll(theSb.loadIncludes(myContext, myEntityManager, pids, theParams.getRevIncludes(), true, theParams.getLastUpdated(), "(synchronous)", theRequestDetails));
if (theParams.getEverythingMode() == null) { if (theParams.getEverythingMode() == null) {
includedPids.addAll(theSb.loadIncludes(myContext, myEntityManager, pids, theParams.getIncludes(), false, theParams.getLastUpdated(), "(synchronous)", theRequestDetails)); includedPids.addAll(theSb.loadIncludes(myContext, myEntityManager, pids, theParams.getIncludes(), false, theParams.getLastUpdated(), "(synchronous)", theRequestDetails));
} }
includedPids.addAll(theSb.loadIncludes(myContext, myEntityManager, pids, theParams.getRevIncludes(), true, theParams.getLastUpdated(), "(synchronous)", theRequestDetails));
List<ResourcePersistentId> includedPidsList = new ArrayList<>(includedPids); List<ResourcePersistentId> includedPidsList = new ArrayList<>(includedPids);
pids.addAll(includedPidsList);
List<IBaseResource> resources = new ArrayList<>(); List<IBaseResource> resources = new ArrayList<>();
theSb.loadResourcesByPid(pids, includedPidsList, resources, false, theRequestDetails); theSb.loadResourcesByPid(pids, includedPidsList, resources, false, theRequestDetails);

View File

@ -115,6 +115,7 @@ import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
@ -752,12 +753,12 @@ public class SearchBuilder implements ISearchBuilder {
* so it can't be Collections.emptySet() or some such thing * so it can't be Collections.emptySet() or some such thing
*/ */
@Override @Override
public HashSet<ResourcePersistentId> loadIncludes(FhirContext theContext, EntityManager theEntityManager, Collection<ResourcePersistentId> theMatches, Set<Include> theRevIncludes, public Set<ResourcePersistentId> loadIncludes(FhirContext theContext, EntityManager theEntityManager, Collection<ResourcePersistentId> theMatches, Set<Include> theIncludes,
boolean theReverseMode, DateRangeParam theLastUpdated, String theSearchIdOrDescription, RequestDetails theRequest) { boolean theReverseMode, DateRangeParam theLastUpdated, String theSearchIdOrDescription, RequestDetails theRequest) {
if (theMatches.size() == 0) { if (theMatches.size() == 0) {
return new HashSet<>(); return new HashSet<>();
} }
if (theRevIncludes == null || theRevIncludes.isEmpty()) { if (theIncludes == null || theIncludes.isEmpty()) {
return new HashSet<>(); return new HashSet<>();
} }
String searchPidFieldName = theReverseMode ? "myTargetResourcePid" : "mySourceResourcePid"; String searchPidFieldName = theReverseMode ? "myTargetResourcePid" : "mySourceResourcePid";
@ -770,7 +771,7 @@ public class SearchBuilder implements ISearchBuilder {
List<ResourcePersistentId> nextRoundMatches = new ArrayList<>(theMatches); List<ResourcePersistentId> nextRoundMatches = new ArrayList<>(theMatches);
HashSet<ResourcePersistentId> allAdded = new HashSet<>(); HashSet<ResourcePersistentId> allAdded = new HashSet<>();
HashSet<ResourcePersistentId> original = new HashSet<>(theMatches); HashSet<ResourcePersistentId> original = new HashSet<>(theMatches);
ArrayList<Include> includes = new ArrayList<>(theRevIncludes); ArrayList<Include> includes = new ArrayList<>(theIncludes);
int roundCounts = 0; int roundCounts = 0;
StopWatch w = new StopWatch(); StopWatch w = new StopWatch();
@ -932,7 +933,6 @@ public class SearchBuilder implements ISearchBuilder {
nextRoundMatches.clear(); nextRoundMatches.clear();
for (ResourcePersistentId next : pidsToInclude) { for (ResourcePersistentId next : pidsToInclude) {
if (original.contains(next) == false && allAdded.contains(next) == false) { if (original.contains(next) == false && allAdded.contains(next) == false) {
theMatches.add(next);
nextRoundMatches.add(next); nextRoundMatches.add(next);
} }
} }
@ -948,24 +948,25 @@ public class SearchBuilder implements ISearchBuilder {
// This can be used to remove results from the search result details before // This can be used to remove results from the search result details before
// the user has a chance to know that they were in the results // the user has a chance to know that they were in the results
if (allAdded.size() > 0) { if (allAdded.size() > 0) {
List<ResourcePersistentId> includedPidList = new ArrayList<>(allAdded);
JpaPreResourceAccessDetails accessDetails = new JpaPreResourceAccessDetails(includedPidList, () -> this);
HookParams params = new HookParams()
.add(IPreResourceAccessDetails.class, accessDetails)
.add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PREACCESS_RESOURCES, params);
for (int i = includedPidList.size() - 1; i >= 0; i--) { if (JpaInterceptorBroadcaster.hasHooks(Pointcut.STORAGE_PREACCESS_RESOURCES, myInterceptorBroadcaster, theRequest)) {
if (accessDetails.isDontReturnResourceAtIndex(i)) { List<ResourcePersistentId> includedPidList = new ArrayList<>(allAdded);
ResourcePersistentId value = includedPidList.remove(i); JpaPreResourceAccessDetails accessDetails = new JpaPreResourceAccessDetails(includedPidList, () -> this);
if (value != null) { HookParams params = new HookParams()
theMatches.remove(value); .add(IPreResourceAccessDetails.class, accessDetails)
.add(RequestDetails.class, theRequest)
.addIfMatchesType(ServletRequestDetails.class, theRequest);
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PREACCESS_RESOURCES, params);
for (int i = includedPidList.size() - 1; i >= 0; i--) {
if (accessDetails.isDontReturnResourceAtIndex(i)) {
ResourcePersistentId value = includedPidList.remove(i);
if (value != null) {
allAdded.remove(value);
}
} }
} }
} }
allAdded = new HashSet<>(includedPidList);
} }
return allAdded; return allAdded;

View File

@ -312,7 +312,7 @@ public class FhirResourceDaoDstu2SearchCustomSearchParamTest extends BaseJpaDstu
map.addInclude(new Include("Patient:attending", true)); map.addInclude(new Include("Patient:attending", true));
results = myAppointmentDao.search(map); results = myAppointmentDao.search(map);
foundResources = toUnqualifiedVersionlessIdValues(results); foundResources = toUnqualifiedVersionlessIdValues(results);
assertThat(foundResources, contains(appId.getValue(), p2id.getValue(), p1id.getValue())); assertThat(foundResources, containsInAnyOrder(appId.getValue(), p2id.getValue(), p1id.getValue()));
} }

View File

@ -1953,7 +1953,7 @@ public class FhirResourceDaoDstu2SearchNoFtTest extends BaseJpaDstu2Test {
params.addInclude(Organization.INCLUDE_PARTOF.asRecursive()); params.addInclude(Organization.INCLUDE_PARTOF.asRecursive());
List<IIdType> resources = toUnqualifiedVersionlessIds(myOrganizationDao.search(params)); List<IIdType> resources = toUnqualifiedVersionlessIds(myOrganizationDao.search(params));
ourLog.info(resources.toString()); ourLog.info(resources.toString());
assertThat(resources, contains(orgId, parentOrgId, parentParentOrgId)); assertThat(resources, containsInAnyOrder(orgId, parentOrgId, parentParentOrgId));
} }
} }
@ -2035,7 +2035,7 @@ public class FhirResourceDaoDstu2SearchNoFtTest extends BaseJpaDstu2Test {
params.add(Patient.SP_FAMILY, new StringDt("Tester_" + methodName + "_P1")); params.add(Patient.SP_FAMILY, new StringDt("Tester_" + methodName + "_P1"));
params.addInclude(new Include("*").asRecursive()); params.addInclude(new Include("*").asRecursive());
List<IIdType> resources = toUnqualifiedVersionlessIds(myPatientDao.search(params)); List<IIdType> resources = toUnqualifiedVersionlessIds(myPatientDao.search(params));
assertThat(resources, contains(patientId, orgId, parentOrgId, parentParentOrgId)); assertThat(resources, containsInAnyOrder(patientId, orgId, parentOrgId, parentParentOrgId));
} }
} }

View File

@ -226,7 +226,7 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu
map.addInclude(new Include("Patient:attending", true)); map.addInclude(new Include("Patient:attending", true));
results = myAppointmentDao.search(map); results = myAppointmentDao.search(map);
foundResources = toUnqualifiedVersionlessIdValues(results); foundResources = toUnqualifiedVersionlessIdValues(results);
assertThat(foundResources, contains(appId.getValue(), p2id.getValue(), p1id.getValue())); assertThat(foundResources, containsInAnyOrder(appId.getValue(), p2id.getValue(), p1id.getValue()));
} }

View File

@ -2740,7 +2740,7 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
params.addInclude(Organization.INCLUDE_PARTOF.asRecursive()); params.addInclude(Organization.INCLUDE_PARTOF.asRecursive());
List<IIdType> resources = toUnqualifiedVersionlessIds(myOrganizationDao.search(params)); List<IIdType> resources = toUnqualifiedVersionlessIds(myOrganizationDao.search(params));
ourLog.info(resources.toString()); ourLog.info(resources.toString());
assertThat(resources, contains(orgId, parentOrgId, parentParentOrgId)); assertThat(resources, containsInAnyOrder(orgId, parentOrgId, parentParentOrgId));
} }
} }
@ -2822,7 +2822,7 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
params.add(Patient.SP_FAMILY, new StringParam("Tester_" + methodName + "_P1")); params.add(Patient.SP_FAMILY, new StringParam("Tester_" + methodName + "_P1"));
params.addInclude(new Include("*").asRecursive()); params.addInclude(new Include("*").asRecursive());
List<IIdType> resources = toUnqualifiedVersionlessIds(myPatientDao.search(params)); List<IIdType> resources = toUnqualifiedVersionlessIds(myPatientDao.search(params));
assertThat(resources, contains(patientId, orgId, parentOrgId, parentParentOrgId)); assertThat(resources, containsInAnyOrder(patientId, orgId, parentOrgId, parentParentOrgId));
} }
} }

View File

@ -474,6 +474,8 @@ public class FhirResourceDaoDstu3ValidateTest extends BaseJpaDstu3Test {
myStructureDefinitionDao.create(sd); myStructureDefinitionDao.create(sd);
Patient p = new Patient(); Patient p = new Patient();
p.getText().getDiv().setValue("<div>hello</div>");
p.getText().setStatus(Narrative.NarrativeStatus.GENERATED);
p.getMeta().addProfile("http://hl7.org/fhir/StructureDefinition/MyPatient421"); p.getMeta().addProfile("http://hl7.org/fhir/StructureDefinition/MyPatient421");
p.setActive(true); p.setActive(true);

View File

@ -4421,7 +4421,7 @@ public class FhirResourceDaoR4LegacySearchBuilderTest extends BaseJpaR4Test {
params.addInclude(Organization.INCLUDE_PARTOF.asRecursive()); params.addInclude(Organization.INCLUDE_PARTOF.asRecursive());
List<IIdType> resources = toUnqualifiedVersionlessIds(myOrganizationDao.search(params)); List<IIdType> resources = toUnqualifiedVersionlessIds(myOrganizationDao.search(params));
ourLog.info(resources.toString()); ourLog.info(resources.toString());
assertThat(resources, contains(orgId, parentOrgId, parentParentOrgId)); assertThat(resources, containsInAnyOrder(orgId, parentOrgId, parentParentOrgId));
} }
} }
@ -4503,7 +4503,7 @@ public class FhirResourceDaoR4LegacySearchBuilderTest extends BaseJpaR4Test {
params.add(Patient.SP_FAMILY, new StringParam("Tester_" + methodName + "_P1")); params.add(Patient.SP_FAMILY, new StringParam("Tester_" + methodName + "_P1"));
params.addInclude(new Include("*").asRecursive()); params.addInclude(new Include("*").asRecursive());
List<IIdType> resources = toUnqualifiedVersionlessIds(myPatientDao.search(params)); List<IIdType> resources = toUnqualifiedVersionlessIds(myPatientDao.search(params));
assertThat(resources, contains(patientId, orgId, parentOrgId, parentParentOrgId)); assertThat(resources, containsInAnyOrder(patientId, orgId, parentOrgId, parentParentOrgId));
} }
} }

View File

@ -502,7 +502,7 @@ public class FhirResourceDaoR4SearchCustomSearchParamTest extends BaseJpaR4Test
map.addInclude(new Include("Patient:attending", true)); map.addInclude(new Include("Patient:attending", true));
results = myAppointmentDao.search(map); results = myAppointmentDao.search(map);
foundResources = toUnqualifiedVersionlessIdValues(results); foundResources = toUnqualifiedVersionlessIdValues(results);
assertThat(foundResources, contains(appId.getValue(), p2id.getValue(), p1id.getValue())); assertThat(foundResources, containsInAnyOrder(appId.getValue(), p2id.getValue(), p1id.getValue()));
} }

View File

@ -4719,7 +4719,7 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
params.addInclude(Organization.INCLUDE_PARTOF.asRecursive()); params.addInclude(Organization.INCLUDE_PARTOF.asRecursive());
List<IIdType> resources = toUnqualifiedVersionlessIds(myOrganizationDao.search(params)); List<IIdType> resources = toUnqualifiedVersionlessIds(myOrganizationDao.search(params));
ourLog.info(resources.toString()); ourLog.info(resources.toString());
assertThat(resources, contains(orgId, parentOrgId, parentParentOrgId)); assertThat(resources, containsInAnyOrder(orgId, parentOrgId, parentParentOrgId));
} }
} }
@ -4801,7 +4801,7 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
params.add(Patient.SP_FAMILY, new StringParam("Tester_" + methodName + "_P1")); params.add(Patient.SP_FAMILY, new StringParam("Tester_" + methodName + "_P1"));
params.addInclude(new Include("*").asRecursive()); params.addInclude(new Include("*").asRecursive());
List<IIdType> resources = toUnqualifiedVersionlessIds(myPatientDao.search(params)); List<IIdType> resources = toUnqualifiedVersionlessIds(myPatientDao.search(params));
assertThat(resources, contains(patientId, orgId, parentOrgId, parentParentOrgId)); assertThat(resources, containsInAnyOrder(patientId, orgId, parentOrgId, parentParentOrgId));
} }
} }

View File

@ -2966,7 +2966,7 @@ public class FhirResourceDaoR4SearchNoHashesTest extends BaseJpaR4Test {
params.addInclude(Organization.INCLUDE_PARTOF.asRecursive()); params.addInclude(Organization.INCLUDE_PARTOF.asRecursive());
List<IIdType> resources = toUnqualifiedVersionlessIds(myOrganizationDao.search(params)); List<IIdType> resources = toUnqualifiedVersionlessIds(myOrganizationDao.search(params));
ourLog.info(resources.toString()); ourLog.info(resources.toString());
assertThat(resources, contains(orgId, parentOrgId, parentParentOrgId)); assertThat(resources, containsInAnyOrder(orgId, parentOrgId, parentParentOrgId));
} }
} }
@ -3048,7 +3048,7 @@ public class FhirResourceDaoR4SearchNoHashesTest extends BaseJpaR4Test {
params.add(Patient.SP_FAMILY, new StringParam("Tester_" + methodName + "_P1")); params.add(Patient.SP_FAMILY, new StringParam("Tester_" + methodName + "_P1"));
params.addInclude(new Include("*").asRecursive()); params.addInclude(new Include("*").asRecursive());
List<IIdType> resources = toUnqualifiedVersionlessIds(myPatientDao.search(params)); List<IIdType> resources = toUnqualifiedVersionlessIds(myPatientDao.search(params));
assertThat(resources, contains(patientId, orgId, parentOrgId, parentParentOrgId)); assertThat(resources, containsInAnyOrder(patientId, orgId, parentOrgId, parentParentOrgId));
} }
} }

View File

@ -5,14 +5,17 @@ import ca.uhn.fhir.jpa.dao.data.ISearchDao;
import ca.uhn.fhir.jpa.entity.Search; import ca.uhn.fhir.jpa.entity.Search;
import ca.uhn.fhir.jpa.provider.r4.ResourceProviderR4Test; import ca.uhn.fhir.jpa.provider.r4.ResourceProviderR4Test;
import ca.uhn.fhir.jpa.search.SearchCoordinatorSvcImpl; import ca.uhn.fhir.jpa.search.SearchCoordinatorSvcImpl;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum; import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.UriDt; import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.parser.StrictErrorHandler; import ca.uhn.fhir.parser.StrictErrorHandler;
import ca.uhn.fhir.rest.api.CacheControlDirective;
import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.SummaryEnum; import ca.uhn.fhir.rest.api.SummaryEnum;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.client.api.IClientInterceptor; import ca.uhn.fhir.rest.client.api.IClientInterceptor;
import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.api.IHttpRequest; import ca.uhn.fhir.rest.client.api.IHttpRequest;
@ -25,6 +28,7 @@ import ca.uhn.fhir.rest.param.ParamPrefixEnum;
import ca.uhn.fhir.rest.param.StringAndListParam; import ca.uhn.fhir.rest.param.StringAndListParam;
import ca.uhn.fhir.rest.param.StringOrListParam; import ca.uhn.fhir.rest.param.StringOrListParam;
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.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
@ -79,6 +83,7 @@ import org.hl7.fhir.dstu3.model.Encounter.EncounterLocationComponent;
import org.hl7.fhir.dstu3.model.Encounter.EncounterStatus; import org.hl7.fhir.dstu3.model.Encounter.EncounterStatus;
import org.hl7.fhir.dstu3.model.Enumerations; import org.hl7.fhir.dstu3.model.Enumerations;
import org.hl7.fhir.dstu3.model.Enumerations.AdministrativeGender; import org.hl7.fhir.dstu3.model.Enumerations.AdministrativeGender;
import org.hl7.fhir.dstu3.model.EpisodeOfCare;
import org.hl7.fhir.dstu3.model.Extension; import org.hl7.fhir.dstu3.model.Extension;
import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.dstu3.model.Identifier; import org.hl7.fhir.dstu3.model.Identifier;
@ -117,6 +122,7 @@ import org.hl7.fhir.dstu3.model.Task;
import org.hl7.fhir.dstu3.model.UnsignedIntType; import org.hl7.fhir.dstu3.model.UnsignedIntType;
import org.hl7.fhir.dstu3.model.ValueSet; import org.hl7.fhir.dstu3.model.ValueSet;
import org.hl7.fhir.dstu3.model.codesystems.DeviceStatus; import org.hl7.fhir.dstu3.model.codesystems.DeviceStatus;
import org.hl7.fhir.instance.model.api.IAnyResource;
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.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
@ -145,6 +151,7 @@ import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Collectors;
import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
@ -665,6 +672,58 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
ourClient.create().resource(input).execute().getResource(); ourClient.create().resource(input).execute().getResource();
} }
@Test
public void testUrlSearchWithNoStoreHeader() throws IOException {
submitBundle("/dstu3/no-store-header/patient-bundle.json");
submitBundle("/dstu3/no-store-header/practitioner-bundle.json");
submitBundle("/dstu3/no-store-header/episodeofcare-bundle.json");
Bundle responseBundle;
responseBundle = ourClient
.search()
.forResource(EpisodeOfCare.class)
.where(new StringClientParam("_id").matches().value("ECC19005O3"))
.where(new StringClientParam("_include").matches().value("*"))
.where(new StringClientParam("_revinclude").matches().value("*"))
.where(new StringClientParam("_count").matches().value("300"))
.returnBundle(Bundle.class)
.encodedJson()
.execute();
String bundleContents = "\n * " + responseBundle.getEntry().stream().map(t->t.getResource().getIdElement().toUnqualifiedVersionless().getValue()).collect(Collectors.joining("\n * "));
assertEquals(3, responseBundle.getEntry().size(), bundleContents);
runInTransaction(()->{
ourLog.info("Resources:\n * {}", myResourceTableDao
.findAll()
.stream()
.sorted(((o1, o2) -> (int) (o1.getId() - o2.getId())))
.map(t->t.getId() + " - " + t.getIdDt().toUnqualifiedVersionless().getValue())
.collect(Collectors.joining("\n * ")));
});
// Now try the exact same search again but using the Cache-Control no-store Header
responseBundle = ourClient
.search()
.forResource(EpisodeOfCare.class)
.where(new StringClientParam("_id").matches().value("ECC19005O3"))
.where(new StringClientParam("_include").matches().value("*"))
.where(new StringClientParam("_revinclude").matches().value("*"))
.where(new StringClientParam("_count").matches().value("300"))
.cacheControl(new CacheControlDirective().setNoStore(true))
.returnBundle(Bundle.class)
.encodedJson()
.execute();
bundleContents = "\n * " + responseBundle.getEntry().stream().map(t->t.getResource().getIdElement().toUnqualifiedVersionless().getValue()).collect(Collectors.joining("\n * "));
assertEquals(3, responseBundle.getEntry().size(), bundleContents);
}
private void submitBundle(String bundleName) throws IOException {
String input = IOUtils.toString(getClass().getResourceAsStream(bundleName), StandardCharsets.UTF_8);
Validate.notNull(input);
ourClient.transaction().withBundle(input).execute();
}
@Test @Test
public void testCreateConditional() { public void testCreateConditional() {
Patient patient = new Patient(); Patient patient = new Patient();

View File

@ -0,0 +1,211 @@
{
"resourceType": "Bundle",
"id": "PROD-DEMO-DATA",
"type": "batch",
"entry": [
{
"resource": {
"resourceType": "Location",
"id": "L439101",
"meta": {
"versionId": "17",
"lastUpdated": "2019-03-24T19:52:02.888-04:00"
},
"identifier": [
{
"type": {
"coding": [
{
"system": "http://hl7.org/fhir/v2/0203",
"code": "FI",
"display": "Facility ID"
}
]
},
"system": "urn:oid:2.16.840.1.113883.3.7418.5.1",
"value": "439101"
}
],
"status": "active",
"name": "DemoClinicA",
"type": {
"coding": [
{
"system": "http://mydomain.org/fhir/cs/OrgHierarchy",
"code": "DL",
"display": "Dialysis Location"
}
],
"text": "Dialysis Location"
},
"telecom": [
{
"system": "phone",
"value": "800-243-1139"
},
{
"system": "fax",
"value": "800-243-1488"
}
],
"address": {
"type": "physical",
"line": [
"2345 W. Van Buren Road"
],
"city": "Chicago",
"district": "test district",
"state": "IL",
"postalCode": "60654"
},
"position": {
"longitude": -17.6723,
"latitude": 11.89018
},
"managingOrganization": {
"reference": "Organization/O182901"
}
},
"request": {
"method": "PUT",
"url": "Location/L439101"
}
},
{
"resource": {
"resourceType": "Location",
"id": "L439102",
"meta": {
"versionId": "1",
"lastUpdated": "2019-03-24T16:42:27.858-04:00"
},
"identifier": [
{
"type": {
"coding": [
{
"system": "http://hl7.org/fhir/v2/0203",
"code": "FI",
"display": "Facility ID"
}
]
},
"system": "urn:oid:2.16.840.1.113883.3.7418.5.1",
"value": "439102"
}
],
"status": "active",
"name": "DemoClinicB",
"type": {
"coding": [
{
"system": "http://mydomain.org/fhir/cs/OrgHierarchy",
"code": "DL",
"display": "Dialysis Location"
}
],
"text": "Dialysis Location"
},
"telecom": [
{
"system": "phone",
"value": "800-100-1000"
},
{
"system": "fax",
"value": "800-100-1000"
}
],
"address": {
"type": "physical",
"line": [
"1234 West Hubbard Street"
],
"city": "Chicago",
"district": "test district",
"state": "IL",
"postalCode": "60654"
},
"position": {
"longitude": -17.9596,
"latitude": 313.83922
},
"managingOrganization": {
"reference": "Organization/O182902"
}
},
"request": {
"method": "PUT",
"url": "Location/L439102"
}
},
{
"resource": {
"resourceType": "Location",
"id": "L439103",
"meta": {
"versionId": "1",
"lastUpdated": "2019-03-24T16:42:42.794-04:00"
},
"identifier": [
{
"type": {
"coding": [
{
"system": "http://hl7.org/fhir/v2/0203",
"code": "FI",
"display": "Facility ID"
}
]
},
"system": "urn:oid:2.16.840.1.113883.3.7418.5.1",
"value": "439103"
}
],
"status": "active",
"name": "DemoClinicC",
"type": {
"coding": [
{
"system": "http://mydomain.org/fhir/cs/OrgHierarchy",
"code": "DL",
"display": "Dialysis Location"
}
],
"text": "Dialysis Location"
},
"telecom": [
{
"system": "phone",
"value": "800-243-1139"
},
{
"system": "fax",
"value": "800-243-1488"
}
],
"address": {
"type": "physical",
"line": [
"345 Katella Road"
],
"city": "Anaheim",
"district": "test district",
"state": "CA",
"postalCode": "92802"
},
"position": {
"longitude": -17.6723,
"latitude": 11.89018
},
"managingOrganization": {
"reference": "Organization/O182903"
}
},
"request": {
"method": "PUT",
"url": "Location/L439103"
}
}
]
}

View File

@ -0,0 +1,469 @@
{
"resourceType": "Bundle",
"id": "PROD-DEMO-DATA",
"type": "batch",
"entry": [
{
"resource": {
"resourceType": "Organization",
"id": "O182901",
"meta": {
"versionId": "1",
"lastUpdated": "2019-03-23T11:16:08.324-04:00"
},
"extension": [
{
"url": "Organization#effectiveDate",
"valueDate": "2006-03-31"
},
{
"url": "Organization#timeZoneId",
"valueString": "America/New_York"
}
],
"identifier": [
{
"type": {
"coding": [
{
"system": "http://hl7.org/fhir/v2/0203",
"code": "XX",
"display": "Organization identifier"
}
]
},
"system": "urn:oid:2.16.840.1.113883.3.7418.1.1",
"value": "O182901"
},
{
"type": {
"coding": [
{
"system": "http://hl7.org/fhir/v2/0203",
"code": "NPI",
"display": "National provider identifier"
}
]
},
"system": "http://hl7.org/fhir/sid/us-npi",
"value": "112323111"
}
],
"active": true,
"type": [
{
"coding": [
{
"system": "http://mydomain.org/fhir/cs/ECCInstance",
"code": "C",
"display": "Instance C"
}
],
"text": "Instance C"
},
{
"coding": [
{
"system": "http://mydomain.org/fhir/cs/OrgHierarchy",
"code": "DL",
"display": "Dialysis Location"
}
],
"text": "Dialysis Location"
},
{
"coding": [
{
"system": "http://mydomain.org/fhir/cs/OrgOwnershipType",
"code": "JOINT VENTURE",
"display": "JOINT VENTURE"
}
],
"text": "JOINT VENTURE"
}
],
"name": "Demo Clinic A",
"telecom": [
{
"system": "phone",
"value": "800-000-0001"
},
{
"system": "fax",
"value": "800-000-0001"
}
],
"address": [
{
"type": "physical",
"line": [
"test st3"
],
"city": "Cleveland",
"district": "test district1",
"state": "OH",
"postalCode": "02130"
},
{
"type": "postal",
"line": [
"test st3"
],
"city": "CLEVELAND",
"district": "test district1",
"state": "OH",
"postalCode": "02130"
}
],
"contact": [
{
"purpose": {
"coding": [
{
"system": "http://hl7.org/fhir/contactentity-type",
"code": "ADMIN",
"display": "Administrative"
}
]
},
"name": {
"family": "OrganizationAdminthree",
"given": [
"Demo Clinic A"
]
}
},
{
"purpose": {
"coding": [
{
"system": "http://hl7.org/fhir/contactentity-type",
"code": "MEDICAL_DIR_INCENTER",
"display": "MEDICAL DIRECTOR INCENTER"
}
]
},
"name": {
"family": "OrganizationOne",
"given": [
"test1"
]
}
}
]
},
"request": {
"method": "PUT",
"url": "Organization/O182901"
}
},
{
"resource": {
"resourceType": "Organization",
"id": "O182902",
"meta": {
"versionId": "1",
"lastUpdated": "2019-03-23T11:16:09.599-04:00"
},
"extension": [
{
"url": "Organization#effectiveDate",
"valueDate": "2006-03-31"
},
{
"url": "Organization#timeZoneId",
"valueString": "America/New_York"
}
],
"identifier": [
{
"type": {
"coding": [
{
"system": "http://hl7.org/fhir/v2/0203",
"code": "XX",
"display": "Organization identifier"
}
]
},
"system": "urn:oid:2.16.840.1.113883.3.7418.1.1",
"value": "O182902"
},
{
"type": {
"coding": [
{
"system": "http://hl7.org/fhir/v2/0203",
"code": "NPI",
"display": "National provider identifier"
}
]
},
"system": "http://hl7.org/fhir/sid/us-npi",
"value": "112323222"
}
],
"active": true,
"type": [
{
"coding": [
{
"system": "http://mydomain.org/fhir/cs/ECCInstance",
"code": "C",
"display": "Instance C"
}
],
"text": "Instance C"
},
{
"coding": [
{
"system": "http://mydomain.org/fhir/cs/OrgHierarchy",
"code": "DL",
"display": "Dialysis Location"
}
],
"text": "Dialysis Location"
},
{
"coding": [
{
"system": "http://mydomain.org/fhir/cs/OrgOwnershipType",
"code": "JOINT VENTURE",
"display": "JOINT VENTURE"
}
],
"text": "JOINT VENTURE"
}
],
"name": "Demo Clinic B",
"telecom": [
{
"system": "phone",
"value": "800-000-0002"
},
{
"system": "fax",
"value": "800-000-0002"
}
],
"address": [
{
"type": "physical",
"line": [
"test st2"
],
"city": "Cleveland",
"district": "test district2",
"state": "OH",
"postalCode": "02130"
},
{
"type": "postal",
"line": [
"test st2"
],
"city": "CLEVELAND",
"district": "test district2",
"state": "OH",
"postalCode": "02130"
}
],
"contact": [
{
"purpose": {
"coding": [
{
"system": "http://hl7.org/fhir/contactentity-type",
"code": "ADMIN",
"display": "Administrative"
}
]
},
"name": {
"family": "OrganizationAdminthree",
"given": [
"Demo Clinic B"
]
}
},
{
"purpose": {
"coding": [
{
"system": "http://hl7.org/fhir/contactentity-type",
"code": "MEDICAL_DIR_INCENTER",
"display": "MEDICAL DIRECTOR INCENTER"
}
]
},
"name": {
"family": "OrganizationTwo",
"given": [
"test2"
]
}
}
]
},
"request": {
"method": "PUT",
"url": "Organization/O182902"
}
},
{
"resource": {
"resourceType": "Organization",
"id": "O182903",
"meta": {
"versionId": "1",
"lastUpdated": "2019-03-23T11:16:09.643-04:00"
},
"extension": [
{
"url": "Organization#effectiveDate",
"valueDate": "2006-03-31"
},
{
"url": "Organization#timeZoneId",
"valueString": "America/New_York"
}
],
"identifier": [
{
"type": {
"coding": [
{
"system": "http://hl7.org/fhir/v2/0203",
"code": "XX",
"display": "Organization identifier"
}
]
},
"system": "urn:oid:2.16.840.1.113883.3.7418.1.1",
"value": "O182903"
},
{
"type": {
"coding": [
{
"system": "http://hl7.org/fhir/v2/0203",
"code": "NPI",
"display": "National provider identifier"
}
]
},
"system": "http://hl7.org/fhir/sid/us-npi",
"value": "112323333"
}
],
"active": true,
"type": [
{
"coding": [
{
"system": "http://mydomain.org/fhir/cs/ECCInstance",
"code": "C",
"display": "Instance C"
}
],
"text": "Instance C"
},
{
"coding": [
{
"system": "http://mydomain.org/fhir/cs/OrgHierarchy",
"code": "DL",
"display": "Dialysis Location"
}
],
"text": "Dialysis Location"
},
{
"coding": [
{
"system": "http://mydomain.org/fhir/cs/OrgOwnershipType",
"code": "JOINT VENTURE",
"display": "JOINT VENTURE"
}
],
"text": "JOINT VENTURE"
}
],
"name": "Demo Clinic C",
"telecom": [
{
"system": "phone",
"value": "800-000-0003"
},
{
"system": "fax",
"value": "800-000-0003"
}
],
"address": [
{
"type": "physical",
"line": [
"test st3"
],
"city": "Cleveland",
"district": "test district3",
"state": "OH",
"postalCode": "02130"
},
{
"type": "postal",
"line": [
"test st3"
],
"city": "CLEVELAND",
"district": "test district3",
"state": "OH",
"postalCode": "02130"
}
],
"contact": [
{
"purpose": {
"coding": [
{
"system": "http://hl7.org/fhir/contactentity-type",
"code": "ADMIN",
"display": "Administrative"
}
]
},
"name": {
"family": "OrganizationAdminthree",
"given": [
"Demo Clinic C"
]
}
},
{
"purpose": {
"coding": [
{
"system": "http://hl7.org/fhir/contactentity-type",
"code": "MEDICAL_DIR_INCENTER",
"display": "MEDICAL DIRECTOR INCENTER"
}
]
},
"name": {
"family": "OrganizationThree",
"given": [
"test3"
]
}
}
]
},
"request": {
"method": "PUT",
"url": "Organization/O182903"
}
}
]
}

View File

@ -0,0 +1,247 @@
{
"resourceType": "Bundle",
"id": "PROD-DEMO-DATA",
"type": "batch",
"entry": [
{
"resource": {
"resourceType": "Practitioner",
"id": "PRProviderThree",
"meta": {
"versionId": "1",
"lastUpdated": "2019-03-23T11:17:01.907-04:00"
},
"identifier": [
{
"type": {
"coding": [
{
"system": "http://hl7.org/fhir/v2/0203",
"code": "EI",
"display": "Employee number"
}
]
},
"system": "urn:oid:2.16.840.1.113883.3.7418.3.1",
"value": "3456789012"
}
],
"active": true,
"name": [
{
"use": "official",
"family": "DemoProvider",
"given": [
"C"
]
}
],
"gender": "female"
},
"request": {
"method": "PUT",
"url": "Practitioner/PRProviderThree"
}
},
{
"resource": {
"resourceType": "Practitioner",
"id": "PRProviderFour",
"meta": {
"versionId": "1",
"lastUpdated": "2019-03-23T11:17:01.923-04:00"
},
"identifier": [
{
"type": {
"coding": [
{
"system": "http://hl7.org/fhir/v2/0203",
"code": "EI",
"display": "Employee number"
}
]
},
"system": "urn:oid:2.16.840.1.113883.3.7418.3.1",
"value": "4567890123"
}
],
"active": true,
"name": [
{
"use": "official",
"family": "DemoProvider",
"given": [
"D"
]
}
],
"gender": "female"
},
"request": {
"method": "PUT",
"url": "Practitioner/PRProviderFour"
}
},
{
"resource": {
"resourceType": "Practitioner",
"id": "PRProviderFive",
"meta": {
"versionId": "1",
"lastUpdated": "2019-03-23T11:17:01.937-04:00"
},
"identifier": [
{
"type": {
"coding": [
{
"system": "http://hl7.org/fhir/v2/0203",
"code": "EI",
"display": "Employee number"
}
]
},
"system": "urn:oid:2.16.840.1.113883.3.7418.3.1",
"value": "5678901234"
}
],
"active": true,
"name": [
{
"use": "official",
"family": "DemoProvider",
"given": [
"E"
]
}
],
"gender": "female"
},
"request": {
"method": "PUT",
"url": "Practitioner/PRProviderFive"
}
},
{
"resource": {
"resourceType": "Practitioner",
"id": "PRMonicaProviderOne",
"meta": {
"versionId": "1",
"lastUpdated": "2019-03-23T11:17:01.949-04:00"
},
"identifier": [
{
"type": {
"coding": [
{
"system": "http://hl7.org/fhir/v2/0203",
"code": "EI",
"display": "Employee number"
}
]
},
"system": "urn:oid:2.16.840.1.113883.3.7418.3.1",
"value": "1234567890"
}
],
"active": true,
"name": [
{
"use": "official",
"family": "DemoProvider",
"given": [
"A"
]
}
],
"gender": "female"
},
"request": {
"method": "PUT",
"url": "Practitioner/PRMonicaProviderOne"
}
},
{
"resource": {
"resourceType": "Practitioner",
"id": "PRProviderTwo",
"meta": {
"versionId": "1",
"lastUpdated": "2019-03-23T11:17:01.963-04:00"
},
"identifier": [
{
"type": {
"coding": [
{
"system": "http://hl7.org/fhir/v2/0203",
"code": "EI",
"display": "Employee number"
}
]
},
"system": "urn:oid:2.16.840.1.113883.3.7418.3.1",
"value": "2345678901"
}
],
"active": true,
"name": [
{
"use": "official",
"family": "DemoProvider",
"given": [
"B"
]
}
],
"gender": "male"
},
"request": {
"method": "PUT",
"url": "Practitioner/PRProviderTwo"
}
},
{
"resource": {
"resourceType": "Practitioner",
"id": "PRClinicalImpression",
"meta": {
"versionId": "1",
"lastUpdated": "2019-03-23T11:17:01.963-04:00"
},
"identifier": [
{
"type": {
"coding": [
{
"system": "http://hl7.org/fhir/v2/0203",
"code": "EI",
"display": "Employee number"
}
]
},
"system": "urn:oid:2.16.840.1.113883.3.7418.3.1",
"value": "00000000"
}
],
"active": true,
"name": [
{
"use": "official",
"family": "NurseOne",
"given": [
"AClinic"
]
}
],
"gender": "male"
},
"request": {
"method": "PUT",
"url": "Practitioner/PRClinicalImpression"
}
}
]
}