Merge branch 'master' into 2667_terserutil_merge_empty_fields

This commit is contained in:
Nick Goupinets 2021-05-25 11:48:56 -04:00
commit 0c50d51e2c
28 changed files with 10458 additions and 238 deletions

View File

@ -0,0 +1,5 @@
---
type: fix
issue: 2672
title: "A concurrency error was fixed when using client assigned IDs on a highly concurrent server
with resource deletion disabled."

View File

@ -0,0 +1,5 @@
---
type: add
issue: 2672
title: "Support has been added to the JPA server for `_include` and `_revinclude` where the
value is a qualified star, e.g. `_include=Observation:*`."

View File

@ -144,6 +144,13 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<executions>
<execution><id>validate</id><phase>none</phase></execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -34,6 +34,7 @@ import javax.annotation.Nonnull;
import javax.persistence.EntityManager;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
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);
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

View File

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

View File

@ -42,6 +42,8 @@ import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@ -82,7 +84,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
@Service
public class IdHelperService {
private static final String RESOURCE_PID = "RESOURCE_PID";
private static final Logger ourLog = LoggerFactory.getLogger(IdHelperService.class);
@Autowired
protected IForcedIdDao myForcedIdDao;
@Autowired
@ -142,7 +144,7 @@ public class IdHelperService {
retVal = new ResourcePersistentId(resolveResourceIdentity(theRequestPartitionId, theResourceType, theId).getResourceId());
} else {
String key = toForcedIdToPidKey(theRequestPartitionId, theResourceType, theId);
retVal = myMemoryCacheService.get(MemoryCacheService.CacheEnum.FORCED_ID_TO_PID, key, t -> new ResourcePersistentId(resolveResourceIdentity(theRequestPartitionId, theResourceType, theId).getResourceId()));
retVal = myMemoryCacheService.getThenPutAfterCommit(MemoryCacheService.CacheEnum.FORCED_ID_TO_PID, key, t -> new ResourcePersistentId(resolveResourceIdentity(theRequestPartitionId, theResourceType, theId).getResourceId()));
}
} else {
@ -252,7 +254,6 @@ public class IdHelperService {
return retVal;
}
public Optional<String> translatePidIdToForcedIdWithCache(ResourcePersistentId theId) {
return myMemoryCacheService.get(MemoryCacheService.CacheEnum.PID_TO_FORCED_ID, theId.getIdAsLong(), pid -> myForcedIdDao.findByResourcePid(pid).map(t -> t.getForcedId()));
}
@ -334,7 +335,7 @@ public class IdHelperService {
if (!myDaoConfig.isDeleteEnabled()) {
String key = resourceType + "/" + forcedId;
myMemoryCacheService.put(MemoryCacheService.CacheEnum.RESOURCE_LOOKUP, key, lookup);
myMemoryCacheService.putAfterCommit(MemoryCacheService.CacheEnum.RESOURCE_LOOKUP, key, lookup);
}
}
}
@ -378,7 +379,7 @@ public class IdHelperService {
theTarget.add(t);
if (!myDaoConfig.isDeleteEnabled()) {
String nextKey = Long.toString(t.getResourceId());
myMemoryCacheService.put(MemoryCacheService.CacheEnum.RESOURCE_LOOKUP, nextKey, t);
myMemoryCacheService.putAfterCommit(MemoryCacheService.CacheEnum.RESOURCE_LOOKUP, nextKey, t);
}
});
@ -386,12 +387,11 @@ public class IdHelperService {
}
/**
*
* Given a set of PIDs, return a set of public FHIR Resource IDs.
* This function will resolve a forced ID if it resolves, and if it fails to resolve to a forced it, will just return the pid
* Example:
* Let's say we have Patient/1(pid == 1), Patient/pat1 (pid == 2), Patient/3 (pid == 3), their pids would resolve as follows:
*
* <p>
* [1,2,3] -> ["1","pat1","3"]
*
* @param thePids The Set of pids you would like to resolve to external FHIR Resource IDs.
@ -408,6 +408,7 @@ public class IdHelperService {
return resolvedResourceIds;
}
public Map<Long, Optional<String>> translatePidsToForcedIds(Set<Long> thePids) {
Map<Long, Optional<String>> retVal = new HashMap<>(myMemoryCacheService.getAllPresent(MemoryCacheService.CacheEnum.PID_TO_FORCED_ID, thePids));
@ -423,7 +424,7 @@ public class IdHelperService {
Long nextResourcePid = forcedId.getResourceId();
Optional<String> nextForcedId = Optional.of(forcedId.getForcedId());
retVal.put(nextResourcePid, nextForcedId);
myMemoryCacheService.put(MemoryCacheService.CacheEnum.PID_TO_FORCED_ID, nextResourcePid, nextForcedId);
myMemoryCacheService.putAfterCommit(MemoryCacheService.CacheEnum.PID_TO_FORCED_ID, nextResourcePid, nextForcedId);
}
});
@ -433,7 +434,7 @@ public class IdHelperService {
.collect(Collectors.toList());
for (Long nextResourcePid : remainingPids) {
retVal.put(nextResourcePid, Optional.empty());
myMemoryCacheService.put(MemoryCacheService.CacheEnum.PID_TO_FORCED_ID, nextResourcePid, Optional.empty());
myMemoryCacheService.putAfterCommit(MemoryCacheService.CacheEnum.PID_TO_FORCED_ID, nextResourcePid, Optional.empty());
}
return retVal;
@ -491,11 +492,11 @@ public class IdHelperService {
*/
public void addResolvedPidToForcedId(ResourcePersistentId theResourcePersistentId, @Nonnull RequestPartitionId theRequestPartitionId, String theResourceType, @Nullable String theForcedId) {
if (theForcedId != null) {
myMemoryCacheService.put(MemoryCacheService.CacheEnum.PID_TO_FORCED_ID, theResourcePersistentId.getIdAsLong(), Optional.of(theForcedId));
myMemoryCacheService.putAfterCommit(MemoryCacheService.CacheEnum.PID_TO_FORCED_ID, theResourcePersistentId.getIdAsLong(), Optional.of(theForcedId));
String key = toForcedIdToPidKey(theRequestPartitionId, theResourceType, theForcedId);
myMemoryCacheService.put(MemoryCacheService.CacheEnum.FORCED_ID_TO_PID, key, theResourcePersistentId);
}else {
myMemoryCacheService.put(MemoryCacheService.CacheEnum.PID_TO_FORCED_ID, theResourcePersistentId.getIdAsLong(), Optional.empty());
myMemoryCacheService.putAfterCommit(MemoryCacheService.CacheEnum.FORCED_ID_TO_PID, key, theResourcePersistentId);
} else {
myMemoryCacheService.putAfterCommit(MemoryCacheService.CacheEnum.PID_TO_FORCED_ID, theResourcePersistentId.getIdAsLong(), Optional.empty());
}
}

View File

@ -416,6 +416,7 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
}
List<ResourcePersistentId> includedPidList = new ArrayList<>(includedPids);
thePids.addAll(includedPidList);
// Execute the query and make sure we return distinct results
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
*/
final Set<ResourcePersistentId> includedPids = new HashSet<>();
includedPids.addAll(theSb.loadIncludes(myContext, myEntityManager, pids, theParams.getRevIncludes(), true, theParams.getLastUpdated(), "(synchronous)", theRequestDetails));
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.getRevIncludes(), true, theParams.getLastUpdated(), "(synchronous)", theRequestDetails));
List<ResourcePersistentId> includedPidsList = new ArrayList<>(includedPids);
pids.addAll(includedPidsList);
List<IBaseResource> resources = new ArrayList<>();
theSb.loadResourcesByPid(pids, includedPidsList, resources, false, theRequestDetails);
@ -1250,9 +1252,12 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
theSearch.setStatus(SearchStatusEnum.LOADING);
theSearch.setSearchQueryString(theQueryString, theRequestPartitionId);
for (Include next : theParams.getIncludes()) {
theSearch.addInclude(new SearchInclude(theSearch, next.getValue(), false, next.isRecurse()));
if (theParams.hasIncludes()) {
for (Include next : theParams.getIncludes()) {
theSearch.addInclude(new SearchInclude(theSearch, next.getValue(), false, next.isRecurse()));
}
}
for (Include next : theParams.getRevIncludes()) {
theSearch.addInclude(new SearchInclude(theSearch, next.getValue(), true, next.isRecurse()));
}

View File

@ -115,6 +115,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@ -752,12 +753,12 @@ public class SearchBuilder implements ISearchBuilder {
* so it can't be Collections.emptySet() or some such thing
*/
@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) {
if (theMatches.size() == 0) {
return new HashSet<>();
}
if (theRevIncludes == null || theRevIncludes.isEmpty()) {
if (theIncludes == null || theIncludes.isEmpty()) {
return new HashSet<>();
}
String searchPidFieldName = theReverseMode ? "myTargetResourcePid" : "mySourceResourcePid";
@ -770,7 +771,7 @@ public class SearchBuilder implements ISearchBuilder {
List<ResourcePersistentId> nextRoundMatches = new ArrayList<>(theMatches);
HashSet<ResourcePersistentId> allAdded = new HashSet<>();
HashSet<ResourcePersistentId> original = new HashSet<>(theMatches);
ArrayList<Include> includes = new ArrayList<>(theRevIncludes);
ArrayList<Include> includes = new ArrayList<>(theIncludes);
int roundCounts = 0;
StopWatch w = new StopWatch();
@ -787,7 +788,18 @@ public class SearchBuilder implements ISearchBuilder {
iter.remove();
}
// Account for _include=*
boolean matchAll = "*".equals(nextInclude.getValue());
// Account for _include=[resourceType]:*
String wantResourceType = null;
if (!matchAll) {
if (nextInclude.getParamName().equals("*")) {
wantResourceType = nextInclude.getParamType();
matchAll = true;
}
}
if (matchAll) {
StringBuilder sqlBuilder = new StringBuilder();
sqlBuilder.append("SELECT r.").append(findPidFieldName);
@ -797,11 +809,27 @@ public class SearchBuilder implements ISearchBuilder {
sqlBuilder.append(" FROM ResourceLink r WHERE r.");
sqlBuilder.append(searchPidFieldName);
sqlBuilder.append(" IN (:target_pids)");
// Technically if the request is a qualified star (e.g. _include=Observation:*) we
// should always be checking the source resource type on the resource link. We don't
// actually index that column though by default, so in order to try and be efficient
// we don't actually include it for includes (but we do for revincludes). This is
// because for an include it doesn't really make sense to include a different
// resource type than the one you are searching on.
if (wantResourceType != null && theReverseMode) {
sqlBuilder.append(" AND r.mySourceResourceType = :want_resource_type");
} else {
wantResourceType = null;
}
String sql = sqlBuilder.toString();
List<Collection<ResourcePersistentId>> partitions = partition(nextRoundMatches, getMaximumPageSize());
for (Collection<ResourcePersistentId> nextPartition : partitions) {
TypedQuery<?> q = theEntityManager.createQuery(sql, Object[].class);
q.setParameter("target_pids", ResourcePersistentId.toLongList(nextPartition));
if (wantResourceType != null) {
q.setParameter("want_resource_type", wantResourceType);
}
List<?> results = q.getResultList();
for (Object nextRow : results) {
if (nextRow == null) {
@ -905,7 +933,6 @@ public class SearchBuilder implements ISearchBuilder {
nextRoundMatches.clear();
for (ResourcePersistentId next : pidsToInclude) {
if (original.contains(next) == false && allAdded.contains(next) == false) {
theMatches.add(next);
nextRoundMatches.add(next);
}
}
@ -921,24 +948,25 @@ public class SearchBuilder implements ISearchBuilder {
// 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
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 (accessDetails.isDontReturnResourceAtIndex(i)) {
ResourcePersistentId value = includedPidList.remove(i);
if (value != null) {
theMatches.remove(value);
if (JpaInterceptorBroadcaster.hasHooks(Pointcut.STORAGE_PREACCESS_RESOURCES, myInterceptorBroadcaster, theRequest)) {
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 (accessDetails.isDontReturnResourceAtIndex(i)) {
ResourcePersistentId value = includedPidList.remove(i);
if (value != null) {
allAdded.remove(value);
}
}
}
}
allAdded = new HashSet<>(includedPidList);
}
return allAdded;

View File

@ -28,6 +28,7 @@ import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.hl7.fhir.instance.model.api.IIdType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.support.TransactionSynchronization;
@ -94,6 +95,24 @@ public class MemoryCacheService {
return cache.get(theKey, theSupplier);
}
/**
* Fetch an item from the cache if it exists, and use the loading function to
* obtain it otherwise.
*
* This method will put the value into the cache using {@link #putAfterCommit(CacheEnum, Object, Object)}.
*/
public <K, T> T getThenPutAfterCommit(CacheEnum theCache, K theKey, Function<K, T> theSupplier) {
assert theCache.myKeyType.isAssignableFrom(theKey.getClass());
Cache<K, T> cache = getCache(theCache);
T retVal = cache.getIfPresent(theKey);
if (retVal == null) {
retVal = theSupplier.apply(theKey);
putAfterCommit(theCache, theKey, retVal);
}
return retVal;
}
public <K, V> V getIfPresent(CacheEnum theCache, K theKey) {
assert theCache.myKeyType.isAssignableFrom(theKey.getClass());
return (V) getCache(theCache).getIfPresent(theKey);

View File

@ -312,7 +312,7 @@ public class FhirResourceDaoDstu2SearchCustomSearchParamTest extends BaseJpaDstu
map.addInclude(new Include("Patient:attending", true));
results = myAppointmentDao.search(map);
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());
List<IIdType> resources = toUnqualifiedVersionlessIds(myOrganizationDao.search(params));
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.addInclude(new Include("*").asRecursive());
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));
results = myAppointmentDao.search(map);
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());
List<IIdType> resources = toUnqualifiedVersionlessIds(myOrganizationDao.search(params));
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.addInclude(new Include("*").asRecursive());
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);
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.setActive(true);

View File

@ -25,6 +25,7 @@ import org.hl7.fhir.r4.model.Observation;
import org.hl7.fhir.r4.model.Parameters;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.Practitioner;
import org.hl7.fhir.r4.model.Reference;
import org.hl7.fhir.r4.model.SearchParameter;
import org.hl7.fhir.r4.model.StringType;
import org.junit.jupiter.api.AfterEach;
@ -147,12 +148,12 @@ public class FhirResourceDaoR4ConcurrentWriteTest extends BaseJpaR4Test {
creator.run();
}
runInTransaction(()->{
runInTransaction(() -> {
Map<String, Integer> counts = new TreeMap<>();
myResourceTableDao
.findAll()
.stream()
.forEach(t->{
.forEach(t -> {
counts.putIfAbsent(t.getResourceType(), 0);
int value = counts.get(t.getResourceType());
value++;
@ -166,7 +167,6 @@ public class FhirResourceDaoR4ConcurrentWriteTest extends BaseJpaR4Test {
}
@Test
public void testCreateWithClientAssignedId() {
myInterceptorRegistry.registerInterceptor(myRetryInterceptor);
@ -603,4 +603,63 @@ public class FhirResourceDaoR4ConcurrentWriteTest extends BaseJpaR4Test {
}
@Test
public void testTransactionWithCreateClientAssignedIdAndReferenceToThatId() {
myInterceptorRegistry.registerInterceptor(myRetryInterceptor);
myDaoConfig.setDeleteEnabled(false);
ServletRequestDetails srd = mock(ServletRequestDetails.class);
String value = UserRequestRetryVersionConflictsInterceptor.RETRY + "; " + UserRequestRetryVersionConflictsInterceptor.MAX_RETRIES + "=10";
when(srd.getHeaders(eq(UserRequestRetryVersionConflictsInterceptor.HEADER_NAME))).thenReturn(Collections.singletonList(value));
when(srd.getUserData()).thenReturn(new HashMap<>());
when(srd.getServer()).thenReturn(new RestfulServer(myFhirCtx));
when(srd.getInterceptorBroadcaster()).thenReturn(new InterceptorService());
List<Future<?>> futures = new ArrayList<>();
int repetitionCount = 3;
for (int i = 0; i < repetitionCount; i++) {
String patientId = "PATIENT" + i;
Runnable task = () -> {
BundleBuilder bb = new BundleBuilder(myFhirCtx);
Patient p = new Patient();
p.setId(patientId);
p.setActive(true);
bb.addTransactionUpdateEntry(p);
Observation obs = new Observation();
obs.setSubject(new Reference("Patient/" + patientId));
bb.addTransactionCreateEntry(obs);
ourLog.info("Submitting transaction");
mySystemDao.transaction(srd, (Bundle) bb.getBundle());
};
for (int j = 0; j < 5; j++) {
Future<?> future = myExecutor.submit(task);
futures.add(future);
}
}
// Look for failures
for (Future<?> next : futures) {
try {
next.get();
ourLog.info("Future produced success");
} catch (Exception e) {
ourLog.info("Future produced exception: {}", e.toString());
throw new AssertionError("Failed with message: " + e.toString(), e);
}
}
// Make sure we saved the object
for (int i = 0; i < repetitionCount; i++) {
Patient patient = myPatientDao.read(new IdType("Patient/PATIENT0"));
assertEquals(true, patient.getActive());
}
}
}

View File

@ -4421,7 +4421,7 @@ public class FhirResourceDaoR4LegacySearchBuilderTest extends BaseJpaR4Test {
params.addInclude(Organization.INCLUDE_PARTOF.asRecursive());
List<IIdType> resources = toUnqualifiedVersionlessIds(myOrganizationDao.search(params));
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.addInclude(new Include("*").asRecursive());
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));
results = myAppointmentDao.search(map);
foundResources = toUnqualifiedVersionlessIdValues(results);
assertThat(foundResources, contains(appId.getValue(), p2id.getValue(), p1id.getValue()));
assertThat(foundResources, containsInAnyOrder(appId.getValue(), p2id.getValue(), p1id.getValue()));
}

View File

@ -1,40 +1,60 @@
package ca.uhn.fhir.jpa.dao.r4;
import static ca.uhn.fhir.rest.api.Constants.PARAM_TYPE;
import static org.apache.commons.lang3.StringUtils.countMatches;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.not;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.interceptor.api.IAnonymousInterceptor;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.entity.Search;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamNumber;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantityNormalized;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamUri;
import ca.uhn.fhir.jpa.model.entity.ResourceLink;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage;
import ca.uhn.fhir.jpa.model.util.UcumServiceUtil;
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap.EverythingModeEnum;
import ca.uhn.fhir.jpa.util.SqlQuery;
import ca.uhn.fhir.jpa.util.TestUtil;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
import ca.uhn.fhir.parser.StrictErrorHandler;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.CompositeParam;
import ca.uhn.fhir.rest.param.DateParam;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.param.HasAndListParam;
import ca.uhn.fhir.rest.param.HasOrListParam;
import ca.uhn.fhir.rest.param.HasParam;
import ca.uhn.fhir.rest.param.NumberParam;
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
import ca.uhn.fhir.rest.param.QuantityParam;
import ca.uhn.fhir.rest.param.ReferenceAndListParam;
import ca.uhn.fhir.rest.param.ReferenceOrListParam;
import ca.uhn.fhir.rest.param.ReferenceParam;
import ca.uhn.fhir.rest.param.StringAndListParam;
import ca.uhn.fhir.rest.param.StringOrListParam;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.param.TokenAndListParam;
import ca.uhn.fhir.rest.param.TokenOrListParam;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.param.TokenParamModifier;
import ca.uhn.fhir.rest.param.UriParam;
import ca.uhn.fhir.rest.param.UriParamQualifierEnum;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;
import ca.uhn.fhir.util.HapiExtensions;
import com.google.common.collect.Lists;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
@ -116,61 +136,38 @@ import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import com.google.common.collect.Lists;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.interceptor.api.IAnonymousInterceptor;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.entity.Search;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamDate;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamNumber;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantityNormalized;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamUri;
import ca.uhn.fhir.jpa.model.entity.ResourceLink;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.model.search.StorageProcessingMessage;
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap.EverythingModeEnum;
import ca.uhn.fhir.jpa.util.SqlQuery;
import ca.uhn.fhir.jpa.util.TestUtil;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
import ca.uhn.fhir.parser.StrictErrorHandler;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.CompositeParam;
import ca.uhn.fhir.rest.param.DateParam;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.param.HasAndListParam;
import ca.uhn.fhir.rest.param.HasOrListParam;
import ca.uhn.fhir.rest.param.HasParam;
import ca.uhn.fhir.rest.param.NumberParam;
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
import ca.uhn.fhir.rest.param.QuantityParam;
import ca.uhn.fhir.rest.param.ReferenceAndListParam;
import ca.uhn.fhir.rest.param.ReferenceOrListParam;
import ca.uhn.fhir.rest.param.ReferenceParam;
import ca.uhn.fhir.rest.param.StringAndListParam;
import ca.uhn.fhir.rest.param.StringOrListParam;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.param.TokenAndListParam;
import ca.uhn.fhir.rest.param.TokenOrListParam;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.param.TokenParamModifier;
import ca.uhn.fhir.rest.param.UriParam;
import ca.uhn.fhir.rest.param.UriParamQualifierEnum;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;
import ca.uhn.fhir.util.HapiExtensions;
import ca.uhn.fhir.jpa.model.util.UcumServiceUtil;
import static ca.uhn.fhir.rest.api.Constants.PARAM_TYPE;
import static org.apache.commons.lang3.StringUtils.countMatches;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.not;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@SuppressWarnings({"unchecked", "Duplicates"})
public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
@ -186,8 +183,8 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
myDaoConfig.setAllowContainsSearches(new DaoConfig().isAllowContainsSearches());
myDaoConfig.setSearchPreFetchThresholds(new DaoConfig().getSearchPreFetchThresholds());
myDaoConfig.setIndexMissingFields(new DaoConfig().getIndexMissingFields());
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED);
}
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED);
}
@BeforeEach
public void beforeDisableCacheReuse() {
@ -573,7 +570,7 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
ourLog.info("Token indexes:\n * {}", myResourceIndexedSearchParamTokenDao.findAll().stream().map(t -> t.toString()).collect(Collectors.joining("\n * ")));
});
SearchParameterMap map = SearchParameterMap.newSynchronous();
SearchParameterMap map = SearchParameterMap.newSynchronous();
map.add(MedicationAdministration.SP_MEDICATION, new ReferenceAndListParam().addAnd(new ReferenceOrListParam().add(new ReferenceParam("code", "04823543"))));
myCaptureQueriesListener.clear();
@ -607,8 +604,8 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
String yesterday = new DateType(DateUtils.addDays(new Date(), -1)).getValueAsString();
String tomorrow = new DateType(DateUtils.addDays(new Date(), 1)).getValueAsString();
runInTransaction(()->{
ourLog.info("Resources:\n * {}", myResourceTableDao.findAll().stream().map(t->t.toString()).collect(Collectors.joining("\n * ")));
runInTransaction(() -> {
ourLog.info("Resources:\n * {}", myResourceTableDao.findAll().stream().map(t -> t.toString()).collect(Collectors.joining("\n * ")));
});
RuntimeResourceDefinition resDef = myFhirCtx.getResourceDefinition("DiagnosticReport");
@ -768,8 +765,8 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
pat2.getManagingOrganization().setReferenceElement(orgId);
IIdType patId2 = myPatientDao.create(pat2, mySrd).getId().toUnqualifiedVersionless();
runInTransaction(()->{
ourLog.info("Links:\n * {}", myResourceLinkDao.findAll().stream().map(t->t.toString()).collect(Collectors.joining("\n * ")));
runInTransaction(() -> {
ourLog.info("Links:\n * {}", myResourceLinkDao.findAll().stream().map(t -> t.toString()).collect(Collectors.joining("\n * ")));
});
// All patient IDs
@ -1229,7 +1226,7 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
ourLog.info(toStringMultiline(results));
assertEquals(0, results.size());
});
List<IIdType> actual = toUnqualifiedVersionlessIds(
mySubstanceDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Substance.SP_QUANTITY, new QuantityParam(null, 123, "http://foo", "UNIT"))));
assertThat(actual, contains(id));
@ -1244,45 +1241,45 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
res.addInstance().getQuantity().setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL).setCode("m").setValue(123);
res.addInstance().getQuantity().setSystem("http://foo2").setCode("UNIT2").setValue(1232);
res.addInstance().getQuantity().setSystem("http://foo2").setCode("UNIT2").setValue(1232);
IIdType id = mySubstanceDao.create(res, mySrd).getId().toUnqualifiedVersionless();
List<IIdType> actual = toUnqualifiedVersionlessIds(
mySubstanceDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Substance.SP_QUANTITY, new QuantityParam(null, 12300, UcumServiceUtil.UCUM_CODESYSTEM_URL, "cm"))));
assertThat(actual, contains(id));
}
@Test
public void testQuantityWithNormalizedQuantitySearchSupported_InvalidUCUMCode() {
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Substance res = new Substance();
res.addInstance().getQuantity().setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL).setCode("FOO").setValue(123);
IIdType id = mySubstanceDao.create(res, mySrd).getId().toUnqualifiedVersionless();
List<IIdType> actual = toUnqualifiedVersionlessIds(
mySubstanceDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Substance.SP_QUANTITY, new QuantityParam(null, 123, UcumServiceUtil.UCUM_CODESYSTEM_URL, "FOO"))));
assertThat(actual, contains(id));
}
@Test
public void testQuantityWithNormalizedQuantitySearchSupported_NotUCUM() {
myModelConfig.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Substance res = new Substance();
res.addInstance().getQuantity().setSystem("http://bar").setCode("FOO").setValue(123);
IIdType id = mySubstanceDao.create(res, mySrd).getId().toUnqualifiedVersionless();
List<IIdType> actual = toUnqualifiedVersionlessIds(
mySubstanceDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Substance.SP_QUANTITY, new QuantityParam(null, 123, "http://bar", "FOO"))));
assertThat(actual, contains(id));
}
@Test
public void testIndexNoDuplicatesQuantityWithNormalizedQuantityStorageSupported() {
@ -1297,9 +1294,9 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
List<IIdType> actual = toUnqualifiedVersionlessIds(
mySubstanceDao.search(new SearchParameterMap().setLoadSynchronous(true).add(Substance.SP_QUANTITY, new QuantityParam(null, 123, UcumServiceUtil.UCUM_CODESYSTEM_URL, "m"))));
assertThat(actual, contains(id));
assertThat(actual, contains(id));
}
@Test
public void testIndexNoDuplicatesReference() {
ServiceRequest pr = new ServiceRequest();
@ -1509,7 +1506,7 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
// Should not crash
myServiceRequestDao.create(serviceRequest);
runInTransaction(()->{
runInTransaction(() -> {
assertEquals(1, myResourceIndexedSearchParamDateDao.findAll().size());
});
}
@ -1517,7 +1514,7 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
@Test
public void testPeriodWithNoEnd() {
ServiceRequest serviceRequest = new ServiceRequest();
Period period = new Period();
period.setStart(new Date());
Timing timing = new Timing();
@ -1527,11 +1524,11 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
// Should not crash
myServiceRequestDao.create(serviceRequest);
runInTransaction(()->{
runInTransaction(() -> {
assertEquals(1, myResourceIndexedSearchParamDateDao.findAll().size());
});
}
@Test
public void testSearchByIdParamInverse() {
@ -1840,6 +1837,72 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
}
@Test
public void testSearchWithIncludeStarQualified() {
Patient pt = new Patient();
pt.setActive(true);
IIdType ptId = myPatientDao.create(pt, mySrd).getId().toUnqualifiedVersionless();
Encounter enc = new Encounter();
enc.setStatus(Encounter.EncounterStatus.ARRIVED);
IIdType encId = myEncounterDao.create(enc, mySrd).getId().toUnqualifiedVersionless();
Observation obs = new Observation();
obs.getSubject().setReference(ptId.getValue());
obs.getEncounter().setReference(encId.getValue());
IIdType obsId = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
// Async Search
SearchParameterMap map = new SearchParameterMap();
map.addInclude(new Include("Observation:*"));
List<IIdType> ids = toUnqualifiedVersionlessIds(myObservationDao.search(map));
assertThat(ids, containsInAnyOrder(obsId, ptId, encId));
// Sync Search
map = new SearchParameterMap();
map.setLoadSynchronous(true);
map.addInclude(new Include("Observation:*"));
ids = toUnqualifiedVersionlessIds(myObservationDao.search(map));
assertThat(ids, containsInAnyOrder(obsId, ptId, encId));
}
@Test
public void testSearchWithRevIncludeStarQualified() {
Patient pt = new Patient();
pt.setActive(true);
IIdType ptId = myPatientDao.create(pt, mySrd).getId().toUnqualifiedVersionless();
Encounter enc = new Encounter();
enc.setStatus(Encounter.EncounterStatus.ARRIVED);
IIdType encId = myEncounterDao.create(enc, mySrd).getId().toUnqualifiedVersionless();
Observation obs = new Observation();
obs.getSubject().setReference(ptId.getValue());
obs.getEncounter().setReference(encId.getValue());
IIdType obsId = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
MedicationRequest mr = new MedicationRequest();
mr.getEncounter().setReference(encId.getValue());
myMedicationRequestDao.create(mr, mySrd);
// Async Search
SearchParameterMap map = new SearchParameterMap();
map.addRevInclude(new Include("Observation:*"));
List<IIdType> ids = toUnqualifiedVersionlessIds(myEncounterDao.search(map));
assertThat(ids, containsInAnyOrder(obsId, encId));
// Sync Search
map = new SearchParameterMap();
map.setLoadSynchronous(true);
map.addRevInclude(new Include("Observation:*"));
ids = toUnqualifiedVersionlessIds(myEncounterDao.search(map));
assertThat(ids, containsInAnyOrder(obsId, encId));
}
@Test
public void testComponentQuantity() {
Observation o1 = new Observation();
@ -2758,7 +2821,7 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
assertEquals(1, myChargeItemDao.search(map).size().intValue());
}
@Test
public void testSearchNameParam() {
IIdType id1;
@ -3122,7 +3185,7 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
assertEquals(1, found.size().intValue());
}
}
@Test
public void testSearchResourceLinkOnCanonical() {
@ -3902,8 +3965,8 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
patient.addName().setFamily("Tester").addGiven("testSearchTokenParam2");
myPatientDao.create(patient, mySrd);
runInTransaction(()->{
ourLog.info("Token indexes:\n * {}", myResourceIndexedSearchParamTokenDao.findAll().stream().filter(t->t.getParamName().equals("identifier")).map(t->t.toString()).collect(Collectors.joining("\n * ")));
runInTransaction(() -> {
ourLog.info("Token indexes:\n * {}", myResourceIndexedSearchParamTokenDao.findAll().stream().filter(t -> t.getParamName().equals("identifier")).map(t -> t.toString()).collect(Collectors.joining("\n * ")));
});
{
@ -3945,8 +4008,8 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
female = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless().getValue();
}
runInTransaction(()->{
ourLog.info("Tokens:\n * {}", myResourceIndexedSearchParamTokenDao.findAll().stream().map(t->t.toString()).collect(Collectors.joining("\n * ")));
runInTransaction(() -> {
ourLog.info("Tokens:\n * {}", myResourceIndexedSearchParamTokenDao.findAll().stream().map(t -> t.toString()).collect(Collectors.joining("\n * ")));
});
List<String> patients;
@ -4656,7 +4719,7 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
params.addInclude(Organization.INCLUDE_PARTOF.asRecursive());
List<IIdType> resources = toUnqualifiedVersionlessIds(myOrganizationDao.search(params));
ourLog.info(resources.toString());
assertThat(resources, contains(orgId, parentOrgId, parentParentOrgId));
assertThat(resources, containsInAnyOrder(orgId, parentOrgId, parentParentOrgId));
}
}
@ -4738,7 +4801,7 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
params.add(Patient.SP_FAMILY, new StringParam("Tester_" + methodName + "_P1"));
params.addInclude(new Include("*").asRecursive());
List<IIdType> resources = toUnqualifiedVersionlessIds(myPatientDao.search(params));
assertThat(resources, contains(patientId, orgId, parentOrgId, parentParentOrgId));
assertThat(resources, containsInAnyOrder(patientId, orgId, parentOrgId, parentParentOrgId));
}
}
@ -5446,9 +5509,9 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
c3.getEncounter().setReference(e3Id);
myCommunicationDao.create(c3);
runInTransaction(()->{
ourLog.info("Links:\n * {}", myResourceLinkDao.findAll().stream().map(t->t.toString()).collect(Collectors.joining("\n * ")));
ourLog.info("Dates:\n * {}", myResourceIndexedSearchParamDateDao.findAll().stream().map(t->t.toString()).collect(Collectors.joining("\n * ")));
runInTransaction(() -> {
ourLog.info("Links:\n * {}", myResourceLinkDao.findAll().stream().map(t -> t.toString()).collect(Collectors.joining("\n * ")));
ourLog.info("Dates:\n * {}", myResourceIndexedSearchParamDateDao.findAll().stream().map(t -> t.toString()).collect(Collectors.joining("\n * ")));
});
SearchParameterMap map;

View File

@ -2966,7 +2966,7 @@ public class FhirResourceDaoR4SearchNoHashesTest extends BaseJpaR4Test {
params.addInclude(Organization.INCLUDE_PARTOF.asRecursive());
List<IIdType> resources = toUnqualifiedVersionlessIds(myOrganizationDao.search(params));
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.addInclude(new Include("*").asRecursive());
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.provider.r4.ResourceProviderR4Test;
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.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.UriDt;
import ca.uhn.fhir.parser.IParser;
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.MethodOutcome;
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.IGenericClient;
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.StringOrListParam;
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.InvalidRequestException;
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.Enumerations;
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.IdType;
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.ValueSet;
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.IIdType;
import org.junit.jupiter.api.AfterEach;
@ -145,6 +151,7 @@ import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.UUID;
import java.util.stream.Collectors;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.hamcrest.MatcherAssert.assertThat;
@ -665,6 +672,58 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
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
public void testCreateConditional() {
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"
}
}
]
}

View File

@ -58,13 +58,10 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
*/
public class SearchParameterMap implements Serializable {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchParameterMap.class);
public static final Integer INTEGER_0 = 0;
private final HashMap<String, List<List<IQueryParameterType>>> mySearchParameterMap = new LinkedHashMap<>();
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchParameterMap.class);
private static final long serialVersionUID = 1L;
private final HashMap<String, List<List<IQueryParameterType>>> mySearchParameterMap = new LinkedHashMap<>();
private Integer myCount;
private Integer myOffset;
private EverythingModeEnum myEverythingMode = null;
@ -81,7 +78,7 @@ public class SearchParameterMap implements Serializable {
private Integer myLastNMax;
private boolean myDeleteExpunge;
private SearchContainedModeEnum mySearchContainedMode = SearchContainedModeEnum.FALSE;
/**
* Constructor
*/
@ -467,7 +464,9 @@ public class SearchParameterMap implements Serializable {
sort = sort.getChain();
}
addUrlIncludeParams(b, Constants.PARAM_INCLUDE, getIncludes());
if (hasIncludes()) {
addUrlIncludeParams(b, Constants.PARAM_INCLUDE, getIncludes());
}
addUrlIncludeParams(b, Constants.PARAM_REVINCLUDE, getRevIncludes());
if (getLastUpdated() != null) {
@ -514,6 +513,13 @@ public class SearchParameterMap implements Serializable {
return b.toString();
}
/**
* @since 5.5.0
*/
public boolean hasIncludes() {
return myIncludes != null && !myIncludes.isEmpty();
}
@Override
public String toString() {
ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
@ -555,14 +561,14 @@ public class SearchParameterMap implements Serializable {
theAndOrParams.removeIf(List::isEmpty);
}
public void setNearDistanceParam(QuantityParam theQuantityParam) {
myNearDistanceParam = theQuantityParam;
}
public QuantityParam getNearDistanceParam() {
return myNearDistanceParam;
}
public void setNearDistanceParam(QuantityParam theQuantityParam) {
myNearDistanceParam = theQuantityParam;
}
public boolean isWantOnlyCount() {
return SummaryEnum.COUNT.equals(getSummaryMode()) || INTEGER_0.equals(getCount());
}
@ -576,6 +582,52 @@ public class SearchParameterMap implements Serializable {
return this;
}
public List<List<IQueryParameterType>> get(String theName) {
return mySearchParameterMap.get(theName);
}
public void put(String theName, List<List<IQueryParameterType>> theParams) {
mySearchParameterMap.put(theName, theParams);
}
public boolean containsKey(String theName) {
return mySearchParameterMap.containsKey(theName);
}
public Set<String> keySet() {
return mySearchParameterMap.keySet();
}
public boolean isEmpty() {
return mySearchParameterMap.isEmpty();
}
// Wrapper methods
public Set<Map.Entry<String, List<List<IQueryParameterType>>>> entrySet() {
return mySearchParameterMap.entrySet();
}
public List<List<IQueryParameterType>> remove(String theName) {
return mySearchParameterMap.remove(theName);
}
public int size() {
return mySearchParameterMap.size();
}
public SearchContainedModeEnum getSearchContainedMode() {
return mySearchContainedMode;
}
public void setSearchContainedMode(SearchContainedModeEnum theSearchContainedMode) {
if (theSearchContainedMode == null) {
mySearchContainedMode = SearchContainedModeEnum.FALSE;
} else {
this.mySearchContainedMode = theSearchContainedMode;
}
}
public enum EverythingModeEnum {
/*
* Don't reorder! We rely on the ordinals
@ -689,40 +741,6 @@ public class SearchParameterMap implements Serializable {
return retVal;
}
// Wrapper methods
public List<List<IQueryParameterType>> get(String theName) {
return mySearchParameterMap.get(theName);
}
public void put(String theName, List<List<IQueryParameterType>> theParams) {
mySearchParameterMap.put(theName, theParams);
}
public boolean containsKey(String theName) {
return mySearchParameterMap.containsKey(theName);
}
public Set<String> keySet() {
return mySearchParameterMap.keySet();
}
public boolean isEmpty() {
return mySearchParameterMap.isEmpty();
}
public Set<Map.Entry<String, List<List<IQueryParameterType>>>> entrySet() {
return mySearchParameterMap.entrySet();
}
public List<List<IQueryParameterType>> remove(String theName) {
return mySearchParameterMap.remove(theName);
}
public int size() {
return mySearchParameterMap.size();
}
public static SearchParameterMap newSynchronous() {
SearchParameterMap retVal = new SearchParameterMap();
retVal.setLoadSynchronous(true);
@ -736,17 +754,5 @@ public class SearchParameterMap implements Serializable {
return retVal;
}
public SearchContainedModeEnum getSearchContainedMode() {
return mySearchContainedMode;
}
public void setSearchContainedMode(SearchContainedModeEnum theSearchContainedMode) {
if (theSearchContainedMode == null) {
mySearchContainedMode = SearchContainedModeEnum.FALSE;
} else {
this.mySearchContainedMode = theSearchContainedMode;
}
}
}

View File

@ -2204,7 +2204,7 @@
<dependency>
<groupId>com.puppycrawl.tools</groupId>
<artifactId>checkstyle</artifactId>
<version>8.41.1</version>
<version>8.42</version>
</dependency>
</dependencies>
<configuration>