Improve Subscription Retriggering Efficiency (#4742)
* Improve subscription efficiency * Reduce number of queries * Add changelog * Fix ITs * Review comments * Test fix
This commit is contained in:
parent
83f216bcee
commit
7bdbda9ef0
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
type: add
|
||||||
|
issue: 4742
|
||||||
|
title: "The in-memory matcher used by the JPA server subscription processor has been
|
||||||
|
optimized to reduce the number of FHIRPath expressions executed while processing
|
||||||
|
in-memory matching."
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
type: perf
|
||||||
|
issue: 4742
|
||||||
|
title: "The SQL generated for the JPA server `$trigger-subscription` operation has been
|
||||||
|
optimized in order to drastically reduce the number of database round trips for large
|
||||||
|
triggering jobs."
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
type: add
|
||||||
|
issue: 4742
|
||||||
|
title: "The JPA server in-memory resource matcher, which is used to improve the efficiency of
|
||||||
|
subscription processing on eligible criteria, now has support for the `_tag`,
|
||||||
|
`_tag:not`, `_security`, `_security:not` and `_profile` parameters."
|
|
@ -97,7 +97,6 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||||
import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster;
|
import ca.uhn.fhir.rest.server.util.CompositeInterceptorBroadcaster;
|
||||||
import ca.uhn.fhir.rest.server.util.ResourceSearchParams;
|
|
||||||
import ca.uhn.fhir.util.CoverageIgnore;
|
import ca.uhn.fhir.util.CoverageIgnore;
|
||||||
import ca.uhn.fhir.util.HapiExtensions;
|
import ca.uhn.fhir.util.HapiExtensions;
|
||||||
import ca.uhn.fhir.util.MetaUtil;
|
import ca.uhn.fhir.util.MetaUtil;
|
||||||
|
@ -160,7 +159,6 @@ import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.IdentityHashMap;
|
import java.util.IdentityHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
@ -914,9 +912,9 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
||||||
myDaoSearchParamSynchronizer = theDaoSearchParamSynchronizer;
|
myDaoSearchParamSynchronizer = theDaoSearchParamSynchronizer;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifyMatchUrlForConditionalCreate(IBaseResource theResource, String theIfNoneExist, ResourceTable entity, ResourceIndexedSearchParams theParams) {
|
private void verifyMatchUrlForConditionalCreate(IBaseResource theResource, String theIfNoneExist, ResourceIndexedSearchParams theParams, RequestDetails theRequestDetails) {
|
||||||
// Make sure that the match URL was actually appropriate for the supplied resource
|
// Make sure that the match URL was actually appropriate for the supplied resource
|
||||||
InMemoryMatchResult outcome = myInMemoryResourceMatcher.match(theIfNoneExist, theResource, theParams);
|
InMemoryMatchResult outcome = myInMemoryResourceMatcher.match(theIfNoneExist, theResource, theParams, theRequestDetails);
|
||||||
if (outcome.supported() && !outcome.matched()) {
|
if (outcome.supported() && !outcome.matched()) {
|
||||||
throw new InvalidRequestException(Msg.code(929) + "Failed to process conditional create. The supplied resource did not satisfy the conditional URL.");
|
throw new InvalidRequestException(Msg.code(929) + "Failed to process conditional create. The supplied resource did not satisfy the conditional URL.");
|
||||||
}
|
}
|
||||||
|
@ -1032,7 +1030,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
||||||
// matches. We could certainly make this configurable though in the
|
// matches. We could certainly make this configurable though in the
|
||||||
// future.
|
// future.
|
||||||
if (entity.getVersion() <= 1L && entity.getCreatedByMatchUrl() != null && thePerformIndexing) {
|
if (entity.getVersion() <= 1L && entity.getCreatedByMatchUrl() != null && thePerformIndexing) {
|
||||||
verifyMatchUrlForConditionalCreate(theResource, entity.getCreatedByMatchUrl(), entity, newParams);
|
verifyMatchUrlForConditionalCreate(theResource, entity.getCreatedByMatchUrl(), newParams, theRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
entity.setUpdated(theTransactionDetails.getTransactionDate());
|
entity.setUpdated(theTransactionDetails.getTransactionDate());
|
||||||
|
|
|
@ -60,6 +60,7 @@ import java.util.stream.Collectors;
|
||||||
import static ca.uhn.fhir.rest.param.ParamPrefixEnum.GREATERTHAN_OR_EQUALS;
|
import static ca.uhn.fhir.rest.param.ParamPrefixEnum.GREATERTHAN_OR_EQUALS;
|
||||||
import static ca.uhn.fhir.rest.param.ParamPrefixEnum.LESSTHAN_OR_EQUALS;
|
import static ca.uhn.fhir.rest.param.ParamPrefixEnum.LESSTHAN_OR_EQUALS;
|
||||||
import static ca.uhn.fhir.rest.param.ParamPrefixEnum.NOT_EQUAL;
|
import static ca.uhn.fhir.rest.param.ParamPrefixEnum.NOT_EQUAL;
|
||||||
|
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
|
||||||
|
@ -487,6 +488,7 @@ public class SearchParameterMap implements Serializable {
|
||||||
b.append(',');
|
b.append(',');
|
||||||
}
|
}
|
||||||
String valueAsQueryToken = nextValueOr.getValueAsQueryToken(theCtx);
|
String valueAsQueryToken = nextValueOr.getValueAsQueryToken(theCtx);
|
||||||
|
valueAsQueryToken = defaultString(valueAsQueryToken);
|
||||||
b.append(UrlUtil.escapeUrlParam(valueAsQueryToken));
|
b.append(UrlUtil.escapeUrlParam(valueAsQueryToken));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,21 +27,25 @@ import ca.uhn.fhir.context.support.ConceptValidationOptions;
|
||||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||||
import ca.uhn.fhir.context.support.ValidationSupportContext;
|
import ca.uhn.fhir.context.support.ValidationSupportContext;
|
||||||
import ca.uhn.fhir.i18n.Msg;
|
import ca.uhn.fhir.i18n.Msg;
|
||||||
import ca.uhn.fhir.jpa.model.entity.StorageSettings;
|
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken;
|
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken;
|
||||||
|
import ca.uhn.fhir.jpa.model.entity.StorageSettings;
|
||||||
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
|
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
|
import ca.uhn.fhir.jpa.searchparam.extractor.ISearchParamExtractor;
|
||||||
import ca.uhn.fhir.jpa.searchparam.extractor.ResourceIndexedSearchParams;
|
import ca.uhn.fhir.jpa.searchparam.extractor.ResourceIndexedSearchParams;
|
||||||
|
import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorService;
|
||||||
import ca.uhn.fhir.jpa.searchparam.util.SourceParam;
|
import ca.uhn.fhir.jpa.searchparam.util.SourceParam;
|
||||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||||
import ca.uhn.fhir.rest.api.Constants;
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
|
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
|
||||||
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
import ca.uhn.fhir.rest.param.BaseParamWithPrefix;
|
import ca.uhn.fhir.rest.param.BaseParamWithPrefix;
|
||||||
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
|
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
|
||||||
import ca.uhn.fhir.rest.param.ReferenceParam;
|
import ca.uhn.fhir.rest.param.ReferenceParam;
|
||||||
import ca.uhn.fhir.rest.param.StringParam;
|
import ca.uhn.fhir.rest.param.StringParam;
|
||||||
import ca.uhn.fhir.rest.param.TokenParam;
|
import ca.uhn.fhir.rest.param.TokenParam;
|
||||||
import ca.uhn.fhir.rest.param.TokenParamModifier;
|
import ca.uhn.fhir.rest.param.TokenParamModifier;
|
||||||
|
import ca.uhn.fhir.rest.param.UriParam;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
|
import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
|
||||||
import ca.uhn.fhir.util.MetaUtil;
|
import ca.uhn.fhir.util.MetaUtil;
|
||||||
|
@ -50,39 +54,47 @@ import com.google.common.collect.Sets;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.hl7.fhir.dstu3.model.Location;
|
import org.hl7.fhir.dstu3.model.Location;
|
||||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseCoding;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.BeansException;
|
import org.springframework.beans.BeansException;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
|
||||||
public class InMemoryResourceMatcher {
|
public class InMemoryResourceMatcher {
|
||||||
|
|
||||||
private enum ValidationSupportInitializationState {NOT_INITIALIZED, INITIALIZED, FAILED}
|
public static final Set<String> UNSUPPORTED_PARAMETER_NAMES = Sets.newHashSet(Constants.PARAM_HAS);
|
||||||
|
|
||||||
public static final Set<String> UNSUPPORTED_PARAMETER_NAMES = Sets.newHashSet(Constants.PARAM_HAS, Constants.PARAM_TAG, Constants.PARAM_PROFILE, Constants.PARAM_SECURITY);
|
|
||||||
private static final org.slf4j.Logger ourLog = LoggerFactory.getLogger(InMemoryResourceMatcher.class);
|
private static final org.slf4j.Logger ourLog = LoggerFactory.getLogger(InMemoryResourceMatcher.class);
|
||||||
@Autowired
|
@Autowired
|
||||||
ApplicationContext myApplicationContext;
|
ApplicationContext myApplicationContext;
|
||||||
@Autowired
|
@Autowired
|
||||||
private MatchUrlService myMatchUrlService;
|
|
||||||
@Autowired
|
|
||||||
ISearchParamRegistry mySearchParamRegistry;
|
ISearchParamRegistry mySearchParamRegistry;
|
||||||
@Autowired
|
@Autowired
|
||||||
StorageSettings myStorageSettings;
|
StorageSettings myStorageSettings;
|
||||||
@Autowired
|
@Autowired
|
||||||
FhirContext myFhirContext;
|
FhirContext myFhirContext;
|
||||||
|
@Autowired
|
||||||
|
SearchParamExtractorService mySearchParamExtractorService;
|
||||||
|
@Autowired
|
||||||
|
IndexedSearchParamExtractor myIndexedSearchParamExtractor;
|
||||||
|
@Autowired
|
||||||
|
private MatchUrlService myMatchUrlService;
|
||||||
private ValidationSupportInitializationState validationSupportState = ValidationSupportInitializationState.NOT_INITIALIZED;
|
private ValidationSupportInitializationState validationSupportState = ValidationSupportInitializationState.NOT_INITIALIZED;
|
||||||
private IValidationSupport myValidationSupport = null;
|
private IValidationSupport myValidationSupport = null;
|
||||||
|
public InMemoryResourceMatcher() {
|
||||||
public InMemoryResourceMatcher() {}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lazy loads a {@link IValidationSupport} implementation just-in-time.
|
* Lazy loads a {@link IValidationSupport} implementation just-in-time.
|
||||||
|
@ -106,15 +118,30 @@ public class InMemoryResourceMatcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is called in two different scenarios. With a null theResource, it determines whether database matching might be required.
|
* @deprecated Use {@link #match(String, IBaseResource, ResourceIndexedSearchParams, RequestDetails)}
|
||||||
* Otherwise, it tries to perform the match in-memory, returning UNSUPPORTED if it's not possible.
|
|
||||||
* <p>
|
|
||||||
* Note that there will be cases where it returns UNSUPPORTED with a null resource, but when a non-null resource it returns supported and no match.
|
|
||||||
* This is because an earlier parameter may be matchable in-memory in which case processing stops and we never get to the parameter
|
|
||||||
* that would have required a database call.
|
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public InMemoryMatchResult match(String theCriteria, IBaseResource theResource, @Nullable ResourceIndexedSearchParams theIndexedSearchParams) {
|
||||||
|
return match(theCriteria, theResource, theIndexedSearchParams, null);
|
||||||
|
}
|
||||||
|
|
||||||
public InMemoryMatchResult match(String theCriteria, IBaseResource theResource, ResourceIndexedSearchParams theSearchParams) {
|
|
||||||
|
/**
|
||||||
|
* This method is called in two different scenarios. With a null theResource, it determines whether database matching might be required.
|
||||||
|
* Otherwise, it tries to perform the match in-memory, returning UNSUPPORTED if it's not possible.
|
||||||
|
* <p>
|
||||||
|
* Note that there will be cases where it returns UNSUPPORTED with a null resource, but when a non-null resource it returns supported and no match.
|
||||||
|
* This is because an earlier parameter may be matchable in-memory in which case processing stops and we never get to the parameter
|
||||||
|
* that would have required a database call.
|
||||||
|
*
|
||||||
|
* @param theIndexedSearchParams If the search params have already been calculated for the given resource,
|
||||||
|
* they can be passed in. Passing in {@literal null} is also fine, in which
|
||||||
|
* case they will be calculated for the resource. It can be preferable to
|
||||||
|
* pass in {@literal null} unless you already actually had to calculate the
|
||||||
|
* indexes for another reason, since we can be efficient here and only calculate
|
||||||
|
* the params that are actually relevant for the given search expression.
|
||||||
|
*/
|
||||||
|
public InMemoryMatchResult match(String theCriteria, IBaseResource theResource, @Nullable ResourceIndexedSearchParams theIndexedSearchParams, RequestDetails theRequestDetails) {
|
||||||
RuntimeResourceDefinition resourceDefinition;
|
RuntimeResourceDefinition resourceDefinition;
|
||||||
if (theResource == null) {
|
if (theResource == null) {
|
||||||
Validate.isTrue(!theCriteria.startsWith("?"), "Invalid match URL format (must match \"[resourceType]?[params]\")");
|
Validate.isTrue(!theCriteria.startsWith("?"), "Invalid match URL format (must match \"[resourceType]?[params]\")");
|
||||||
|
@ -129,25 +156,32 @@ public class InMemoryResourceMatcher {
|
||||||
} catch (UnsupportedOperationException e) {
|
} catch (UnsupportedOperationException e) {
|
||||||
return InMemoryMatchResult.unsupportedFromReason(InMemoryMatchResult.PARSE_FAIL);
|
return InMemoryMatchResult.unsupportedFromReason(InMemoryMatchResult.PARSE_FAIL);
|
||||||
}
|
}
|
||||||
// wipjv consider merging InMemoryMatchResult with IAuthorizationSearchParamMatcher.Match match type
|
|
||||||
// } catch (MatchUrlService.UnrecognizedSearchParameterException e) {
|
|
||||||
// return InMemoryMatchResult.unsupportedFromReason(InMemoryMatchResult.PARAM);
|
|
||||||
|
|
||||||
searchParameterMap.clean();
|
searchParameterMap.clean();
|
||||||
return match(searchParameterMap, theResource, resourceDefinition, theSearchParams);
|
|
||||||
|
ResourceIndexedSearchParams relevantSearchParams = null;
|
||||||
|
if (theIndexedSearchParams != null) {
|
||||||
|
relevantSearchParams = theIndexedSearchParams;
|
||||||
|
} else if (theResource != null) {
|
||||||
|
// Don't index search params we don't actully need for the given criteria
|
||||||
|
ISearchParamExtractor.ISearchParamFilter filter = theSearchParams -> theSearchParams
|
||||||
|
.stream()
|
||||||
|
.filter(t -> searchParameterMap.containsKey(t.getName()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
relevantSearchParams = myIndexedSearchParamExtractor.extractIndexedSearchParams(theResource, theRequestDetails, filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
return match(searchParameterMap, theResource, resourceDefinition, relevantSearchParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @param theCriteria
|
* @param theCriteria
|
||||||
* @return result.supported() will be true if theCriteria can be evaluated in-memory
|
* @return result.supported() will be true if theCriteria can be evaluated in-memory
|
||||||
*/
|
*/
|
||||||
public InMemoryMatchResult canBeEvaluatedInMemory(String theCriteria) {
|
public InMemoryMatchResult canBeEvaluatedInMemory(String theCriteria) {
|
||||||
return match(theCriteria, null, null);
|
return match(theCriteria, null, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @param theSearchParameterMap
|
* @param theSearchParameterMap
|
||||||
* @param theResourceDefinition
|
* @param theResourceDefinition
|
||||||
* @return result.supported() will be true if theSearchParameterMap can be evaluated in-memory
|
* @return result.supported() will be true if theSearchParameterMap can be evaluated in-memory
|
||||||
|
@ -156,7 +190,6 @@ public class InMemoryResourceMatcher {
|
||||||
return match(theSearchParameterMap, null, theResourceDefinition, null);
|
return match(theSearchParameterMap, null, theResourceDefinition, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
public InMemoryMatchResult match(SearchParameterMap theSearchParameterMap, IBaseResource theResource, RuntimeResourceDefinition theResourceDefinition, ResourceIndexedSearchParams theSearchParams) {
|
public InMemoryMatchResult match(SearchParameterMap theSearchParameterMap, IBaseResource theResource, RuntimeResourceDefinition theResourceDefinition, ResourceIndexedSearchParams theSearchParams) {
|
||||||
if (theSearchParameterMap.getLastUpdated() != null) {
|
if (theSearchParameterMap.getLastUpdated() != null) {
|
||||||
|
@ -195,6 +228,12 @@ public class InMemoryResourceMatcher {
|
||||||
return InMemoryMatchResult.fromBoolean(matchIdsAndOr(theAndOrParams, theResource));
|
return InMemoryMatchResult.fromBoolean(matchIdsAndOr(theAndOrParams, theResource));
|
||||||
case Constants.PARAM_SOURCE:
|
case Constants.PARAM_SOURCE:
|
||||||
return InMemoryMatchResult.fromBoolean(matchSourcesAndOr(theAndOrParams, theResource));
|
return InMemoryMatchResult.fromBoolean(matchSourcesAndOr(theAndOrParams, theResource));
|
||||||
|
case Constants.PARAM_TAG:
|
||||||
|
return InMemoryMatchResult.fromBoolean(matchTagsOrSecurityAndOr(theAndOrParams, theResource, true));
|
||||||
|
case Constants.PARAM_SECURITY:
|
||||||
|
return InMemoryMatchResult.fromBoolean(matchTagsOrSecurityAndOr(theAndOrParams, theResource, false));
|
||||||
|
case Constants.PARAM_PROFILE:
|
||||||
|
return InMemoryMatchResult.fromBoolean(matchProfilesAndOr(theAndOrParams, theResource));
|
||||||
default:
|
default:
|
||||||
return matchResourceParam(myStorageSettings, theParamName, theAndOrParams, theSearchParams, resourceName, paramDef);
|
return matchResourceParam(myStorageSettings, theParamName, theAndOrParams, theSearchParams, resourceName, paramDef);
|
||||||
}
|
}
|
||||||
|
@ -225,7 +264,7 @@ public class InMemoryResourceMatcher {
|
||||||
InMemoryMatchResult checkUnsupportedResult = InMemoryMatchResult.successfulMatch();
|
InMemoryMatchResult checkUnsupportedResult = InMemoryMatchResult.successfulMatch();
|
||||||
|
|
||||||
if (hasChain(theParam)) {
|
if (hasChain(theParam)) {
|
||||||
checkUnsupportedResult = InMemoryMatchResult.unsupportedFromParameterAndReason(theParamName + "." + ((ReferenceParam)theParam).getChain(), InMemoryMatchResult.CHAIN);
|
checkUnsupportedResult = InMemoryMatchResult.unsupportedFromParameterAndReason(theParamName + "." + ((ReferenceParam) theParam).getChain(), InMemoryMatchResult.CHAIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (checkUnsupportedResult.supported()) {
|
if (checkUnsupportedResult.supported()) {
|
||||||
|
@ -239,6 +278,30 @@ public class InMemoryResourceMatcher {
|
||||||
return checkUnsupportedResult;
|
return checkUnsupportedResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean matchProfilesAndOr(List<List<IQueryParameterType>> theAndOrParams, IBaseResource theResource) {
|
||||||
|
if (theResource == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return theAndOrParams.stream().allMatch(nextAnd -> matchProfilesOr(nextAnd, theResource));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean matchProfilesOr(List<IQueryParameterType> theOrParams, IBaseResource theResource) {
|
||||||
|
return theOrParams.stream().anyMatch(param -> matchProfile(param, theResource));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean matchProfile(IQueryParameterType theProfileParam, IBaseResource theResource) {
|
||||||
|
UriParam paramProfile = new UriParam(theProfileParam.getValueAsQueryToken(myFhirContext));
|
||||||
|
|
||||||
|
String paramProfileValue = paramProfile.getValue();
|
||||||
|
if (isBlank(paramProfileValue)) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return theResource.getMeta().getProfile().stream()
|
||||||
|
.map(IPrimitiveType::getValueAsString)
|
||||||
|
.anyMatch(profileValue -> profileValue != null && profileValue.equals(paramProfileValue));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private boolean matchSourcesAndOr(List<List<IQueryParameterType>> theAndOrParams, IBaseResource theResource) {
|
private boolean matchSourcesAndOr(List<List<IQueryParameterType>> theAndOrParams, IBaseResource theResource) {
|
||||||
if (theResource == null) {
|
if (theResource == null) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -263,6 +326,54 @@ public class InMemoryResourceMatcher {
|
||||||
return matches;
|
return matches;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean matchTagsOrSecurityAndOr(List<List<IQueryParameterType>> theAndOrParams, IBaseResource theResource, boolean theTag) {
|
||||||
|
if (theResource == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return theAndOrParams.stream().allMatch(nextAnd -> matchTagsOrSecurityOr(nextAnd, theResource, theTag));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean matchTagsOrSecurityOr(List<IQueryParameterType> theOrParams, IBaseResource theResource, boolean theTag) {
|
||||||
|
return theOrParams.stream().anyMatch(param -> matchTagOrSecurity(param, theResource, theTag));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean matchTagOrSecurity(IQueryParameterType theParam, IBaseResource theResource, boolean theTag) {
|
||||||
|
TokenParam param = (TokenParam) theParam;
|
||||||
|
|
||||||
|
List<? extends IBaseCoding> list;
|
||||||
|
if (theTag) {
|
||||||
|
list = theResource.getMeta().getTag();
|
||||||
|
} else {
|
||||||
|
list = theResource.getMeta().getSecurity();
|
||||||
|
}
|
||||||
|
boolean haveMatch = false;
|
||||||
|
boolean haveCandidate = false;
|
||||||
|
for (IBaseCoding next : list) {
|
||||||
|
if (param.getSystem() == null && param.getValue() == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
haveCandidate = true;
|
||||||
|
if (isNotBlank(param.getSystem())) {
|
||||||
|
if (!param.getSystem().equals(next.getSystem())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isNotBlank(param.getValue())) {
|
||||||
|
if (!param.getValue().equals(next.getCode())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
haveMatch = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (param.getModifier() == TokenParamModifier.NOT) {
|
||||||
|
haveMatch = !haveMatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
return haveMatch && haveCandidate;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean matchIdsAndOr(List<List<IQueryParameterType>> theAndOrParams, IBaseResource theResource) {
|
private boolean matchIdsAndOr(List<List<IQueryParameterType>> theAndOrParams, IBaseResource theResource) {
|
||||||
if (theResource == null) {
|
if (theResource == null) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -319,7 +430,9 @@ public class InMemoryResourceMatcher {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Some modifiers are negative, and must match NONE of their or-list */
|
/**
|
||||||
|
* Some modifiers are negative, and must match NONE of their or-list
|
||||||
|
*/
|
||||||
private boolean isNegative(RuntimeSearchParam theParamDef, List<? extends IQueryParameterType> theOrList) {
|
private boolean isNegative(RuntimeSearchParam theParamDef, List<? extends IQueryParameterType> theOrList) {
|
||||||
if (theParamDef.getParamType().equals(RestSearchParameterTypeEnum.TOKEN)) {
|
if (theParamDef.getParamType().equals(RestSearchParameterTypeEnum.TOKEN)) {
|
||||||
TokenParam tokenParam = (TokenParam) theOrList.get(0);
|
TokenParam tokenParam = (TokenParam) theOrList.get(0);
|
||||||
|
@ -344,12 +457,13 @@ public class InMemoryResourceMatcher {
|
||||||
* The :not modifier is supported.
|
* The :not modifier is supported.
|
||||||
* The :in and :not-in qualifiers are supported only if a bean implementing IValidationSupport is available.
|
* The :in and :not-in qualifiers are supported only if a bean implementing IValidationSupport is available.
|
||||||
* Any other qualifier will be ignored and the match will be treated as unqualified.
|
* Any other qualifier will be ignored and the match will be treated as unqualified.
|
||||||
|
*
|
||||||
* @param theStorageSettings a model configuration
|
* @param theStorageSettings a model configuration
|
||||||
* @param theResourceName the name of the resource type being matched
|
* @param theResourceName the name of the resource type being matched
|
||||||
* @param theParamName the name of the parameter
|
* @param theParamName the name of the parameter
|
||||||
* @param theParamDef the definition of the search parameter
|
* @param theParamDef the definition of the search parameter
|
||||||
* @param theSearchParams the search parameters derived from the target resource
|
* @param theSearchParams the search parameters derived from the target resource
|
||||||
* @param theQueryParam the query parameter to compare with theSearchParams
|
* @param theQueryParam the query parameter to compare with theSearchParams
|
||||||
* @return true if theQueryParam matches the collection of theSearchParams, otherwise false
|
* @return true if theQueryParam matches the collection of theSearchParams, otherwise false
|
||||||
*/
|
*/
|
||||||
private boolean matchTokenParam(StorageSettings theStorageSettings, String theResourceName, String theParamName, RuntimeSearchParam theParamDef, ResourceIndexedSearchParams theSearchParams, TokenParam theQueryParam) {
|
private boolean matchTokenParam(StorageSettings theStorageSettings, String theResourceName, String theParamName, RuntimeSearchParam theParamDef, ResourceIndexedSearchParams theSearchParams, TokenParam theQueryParam) {
|
||||||
|
@ -458,4 +572,6 @@ public class InMemoryResourceMatcher {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private enum ValidationSupportInitializationState {NOT_INITIALIZED, INITIALIZED, FAILED}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.searchparam.matcher;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||||
|
import ca.uhn.fhir.jpa.searchparam.extractor.ISearchParamExtractor;
|
||||||
import ca.uhn.fhir.jpa.searchparam.extractor.ResourceIndexedSearchParams;
|
import ca.uhn.fhir.jpa.searchparam.extractor.ResourceIndexedSearchParams;
|
||||||
import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorService;
|
import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorService;
|
||||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
|
@ -28,19 +29,27 @@ import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
public class IndexedSearchParamExtractor {
|
public class IndexedSearchParamExtractor {
|
||||||
@Autowired
|
@Autowired
|
||||||
private FhirContext myContext;
|
private FhirContext myContext;
|
||||||
@Autowired
|
@Autowired
|
||||||
private SearchParamExtractorService mySearchParamExtractorService;
|
private SearchParamExtractorService mySearchParamExtractorService;
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
public ResourceIndexedSearchParams extractIndexedSearchParams(IBaseResource theResource, RequestDetails theRequest) {
|
public ResourceIndexedSearchParams extractIndexedSearchParams(IBaseResource theResource, RequestDetails theRequest) {
|
||||||
|
return extractIndexedSearchParams(theResource, theRequest, ISearchParamExtractor.ALL_PARAMS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
public ResourceIndexedSearchParams extractIndexedSearchParams(IBaseResource theResource, RequestDetails theRequest, ISearchParamExtractor.ISearchParamFilter filter) {
|
||||||
ResourceTable entity = new ResourceTable();
|
ResourceTable entity = new ResourceTable();
|
||||||
TransactionDetails transactionDetails = new TransactionDetails();
|
TransactionDetails transactionDetails = new TransactionDetails();
|
||||||
String resourceType = myContext.getResourceType(theResource);
|
String resourceType = myContext.getResourceType(theResource);
|
||||||
entity.setResourceType(resourceType);
|
entity.setResourceType(resourceType);
|
||||||
ResourceIndexedSearchParams resourceIndexedSearchParams = new ResourceIndexedSearchParams();
|
ResourceIndexedSearchParams resourceIndexedSearchParams = new ResourceIndexedSearchParams();
|
||||||
mySearchParamExtractorService.extractFromResource(null, theRequest, resourceIndexedSearchParams, entity, theResource, transactionDetails, false);
|
mySearchParamExtractorService.extractFromResource(null, theRequest, resourceIndexedSearchParams, new ResourceIndexedSearchParams(), entity, theResource, transactionDetails, false, filter);
|
||||||
return resourceIndexedSearchParams;
|
return resourceIndexedSearchParams;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,8 +38,7 @@ public class SearchParamMatcher {
|
||||||
private InMemoryResourceMatcher myInMemoryResourceMatcher;
|
private InMemoryResourceMatcher myInMemoryResourceMatcher;
|
||||||
|
|
||||||
public InMemoryMatchResult match(String theCriteria, IBaseResource theResource, RequestDetails theRequest) {
|
public InMemoryMatchResult match(String theCriteria, IBaseResource theResource, RequestDetails theRequest) {
|
||||||
ResourceIndexedSearchParams resourceIndexedSearchParams = myIndexedSearchParamExtractor.extractIndexedSearchParams(theResource, theRequest);
|
return myInMemoryResourceMatcher.match(theCriteria, theResource, null, theRequest);
|
||||||
return myInMemoryResourceMatcher.match(theCriteria, theResource, resourceIndexedSearchParams);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public InMemoryMatchResult match(SearchParameterMap theSearchParameterMap, IBaseResource theResource) {
|
public InMemoryMatchResult match(SearchParameterMap theSearchParameterMap, IBaseResource theResource) {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import ca.uhn.fhir.jpa.model.entity.StorageSettings;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken;
|
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken;
|
||||||
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
|
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
|
||||||
import ca.uhn.fhir.jpa.searchparam.extractor.ResourceIndexedSearchParams;
|
import ca.uhn.fhir.jpa.searchparam.extractor.ResourceIndexedSearchParams;
|
||||||
|
import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorService;
|
||||||
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
|
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
|
||||||
import ca.uhn.fhir.rest.param.TokenParamModifier;
|
import ca.uhn.fhir.rest.param.TokenParamModifier;
|
||||||
import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
|
import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
|
||||||
|
@ -28,6 +29,7 @@ import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||||
|
|
||||||
|
import static ca.uhn.fhir.jpa.searchparam.matcher.InMemoryResourceMatcherR5Test.newRequest;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
@ -47,6 +49,10 @@ public class InMemoryResourceMatcherConfigurationR5Test {
|
||||||
ISearchParamRegistry mySearchParamRegistry;
|
ISearchParamRegistry mySearchParamRegistry;
|
||||||
@Autowired
|
@Autowired
|
||||||
private InMemoryResourceMatcher myInMemoryResourceMatcher;
|
private InMemoryResourceMatcher myInMemoryResourceMatcher;
|
||||||
|
@MockBean
|
||||||
|
private SearchParamExtractorService mySearchParamExtractorService;
|
||||||
|
@MockBean
|
||||||
|
private IndexedSearchParamExtractor myIndexedSearchParamExtractor;
|
||||||
private Observation myObservation;
|
private Observation myObservation;
|
||||||
private ResourceIndexedSearchParams mySearchParams;
|
private ResourceIndexedSearchParams mySearchParams;
|
||||||
|
|
||||||
|
@ -71,7 +77,7 @@ public class InMemoryResourceMatcherConfigurationR5Test {
|
||||||
myInMemoryResourceMatcher.myApplicationContext = applicationContext;
|
myInMemoryResourceMatcher.myApplicationContext = applicationContext;
|
||||||
|
|
||||||
for (int i = 0; i < 10; i++) {
|
for (int i = 0; i < 10; i++) {
|
||||||
myInMemoryResourceMatcher.match("code" + TokenParamModifier.IN.getValue() + "=" + OBSERVATION_CODE_VALUE_SET_URI, myObservation, mySearchParams);
|
myInMemoryResourceMatcher.match("code" + TokenParamModifier.IN.getValue() + "=" + OBSERVATION_CODE_VALUE_SET_URI, myObservation, mySearchParams, newRequest());
|
||||||
}
|
}
|
||||||
|
|
||||||
verify(applicationContext, times(1)).getBean(IValidationSupport.class);
|
verify(applicationContext, times(1)).getBean(IValidationSupport.class);
|
||||||
|
@ -83,7 +89,7 @@ public class InMemoryResourceMatcherConfigurationR5Test {
|
||||||
Tests the case where the :in qualifier can not be supported because no bean implementing IValidationSupport was registered
|
Tests the case where the :in qualifier can not be supported because no bean implementing IValidationSupport was registered
|
||||||
*/
|
*/
|
||||||
public void testUnsupportedIn() {
|
public void testUnsupportedIn() {
|
||||||
InMemoryMatchResult result = myInMemoryResourceMatcher.match("code" + TokenParamModifier.IN.getValue() + "=" + OBSERVATION_CODE_VALUE_SET_URI, myObservation, mySearchParams);
|
InMemoryMatchResult result = myInMemoryResourceMatcher.match("code" + TokenParamModifier.IN.getValue() + "=" + OBSERVATION_CODE_VALUE_SET_URI, myObservation, mySearchParams, newRequest());
|
||||||
assertFalse(result.supported());
|
assertFalse(result.supported());
|
||||||
assertEquals("Parameter: <code:in> Reason: Qualified parameter not supported", result.getUnsupportedReason());
|
assertEquals("Parameter: <code:in> Reason: Qualified parameter not supported", result.getUnsupportedReason());
|
||||||
}
|
}
|
||||||
|
@ -91,7 +97,7 @@ public class InMemoryResourceMatcherConfigurationR5Test {
|
||||||
@Test
|
@Test
|
||||||
@Order(3)
|
@Order(3)
|
||||||
public void testUnsupportedNotIn() {
|
public void testUnsupportedNotIn() {
|
||||||
InMemoryMatchResult result = myInMemoryResourceMatcher.match("code" + TokenParamModifier.NOT_IN.getValue() + "=" + OBSERVATION_CODE_VALUE_SET_URI, myObservation, mySearchParams);
|
InMemoryMatchResult result = myInMemoryResourceMatcher.match("code" + TokenParamModifier.NOT_IN.getValue() + "=" + OBSERVATION_CODE_VALUE_SET_URI, myObservation, mySearchParams, newRequest());
|
||||||
assertFalse(result.supported());
|
assertFalse(result.supported());
|
||||||
assertEquals("Parameter: <code:not-in> Reason: Qualified parameter not supported", result.getUnsupportedReason());
|
assertEquals("Parameter: <code:not-in> Reason: Qualified parameter not supported", result.getUnsupportedReason());
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,9 +9,12 @@ import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken;
|
||||||
import ca.uhn.fhir.jpa.model.entity.StorageSettings;
|
import ca.uhn.fhir.jpa.model.entity.StorageSettings;
|
||||||
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
|
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
|
||||||
import ca.uhn.fhir.jpa.searchparam.extractor.ResourceIndexedSearchParams;
|
import ca.uhn.fhir.jpa.searchparam.extractor.ResourceIndexedSearchParams;
|
||||||
|
import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorService;
|
||||||
import ca.uhn.fhir.model.primitive.BaseDateTimeDt;
|
import ca.uhn.fhir.model.primitive.BaseDateTimeDt;
|
||||||
import ca.uhn.fhir.rest.api.Constants;
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
|
import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum;
|
||||||
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
|
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
|
||||||
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
|
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
|
||||||
import ca.uhn.fhir.rest.param.TokenParamModifier;
|
import ca.uhn.fhir.rest.param.TokenParamModifier;
|
||||||
import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
|
import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
|
||||||
|
@ -63,6 +66,10 @@ public class InMemoryResourceMatcherR5Test {
|
||||||
ISearchParamRegistry mySearchParamRegistry;
|
ISearchParamRegistry mySearchParamRegistry;
|
||||||
@MockBean
|
@MockBean
|
||||||
IValidationSupport myValidationSupport;
|
IValidationSupport myValidationSupport;
|
||||||
|
@MockBean
|
||||||
|
SearchParamExtractorService mySearchParamExtractorService;
|
||||||
|
@MockBean
|
||||||
|
IndexedSearchParamExtractor myIndexedSearchParamExtractor;
|
||||||
@Autowired
|
@Autowired
|
||||||
private InMemoryResourceMatcher myInMemoryResourceMatcher;
|
private InMemoryResourceMatcher myInMemoryResourceMatcher;
|
||||||
private Observation myObservation;
|
private Observation myObservation;
|
||||||
|
@ -92,47 +99,51 @@ public class InMemoryResourceMatcherR5Test {
|
||||||
@Test
|
@Test
|
||||||
public void testSupportedSource() {
|
public void testSupportedSource() {
|
||||||
{
|
{
|
||||||
InMemoryMatchResult result = myInMemoryResourceMatcher.match(Constants.PARAM_SOURCE + "=" + TEST_SOURCE, myObservation, mySearchParams);
|
InMemoryMatchResult result = myInMemoryResourceMatcher.match(Constants.PARAM_SOURCE + "=" + TEST_SOURCE, myObservation, mySearchParams, newRequest());
|
||||||
assertTrue(result.matched());
|
assertTrue(result.matched());
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
InMemoryMatchResult result = myInMemoryResourceMatcher.match(Constants.PARAM_SOURCE + "=" + SOURCE_URI, myObservation, mySearchParams);
|
InMemoryMatchResult result = myInMemoryResourceMatcher.match(Constants.PARAM_SOURCE + "=" + SOURCE_URI, myObservation, mySearchParams, newRequest());
|
||||||
assertTrue(result.matched());
|
assertTrue(result.matched());
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
InMemoryMatchResult result = myInMemoryResourceMatcher.match(Constants.PARAM_SOURCE + "=" + REQUEST_ID, myObservation, mySearchParams);
|
InMemoryMatchResult result = myInMemoryResourceMatcher.match(Constants.PARAM_SOURCE + "=" + REQUEST_ID, myObservation, mySearchParams, newRequest());
|
||||||
assertFalse(result.matched());
|
assertFalse(result.matched());
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
InMemoryMatchResult result = myInMemoryResourceMatcher.match(Constants.PARAM_SOURCE + "=#" + REQUEST_ID, myObservation, mySearchParams);
|
InMemoryMatchResult result = myInMemoryResourceMatcher.match(Constants.PARAM_SOURCE + "=#" + REQUEST_ID, myObservation, mySearchParams, newRequest());
|
||||||
assertTrue(result.matched());
|
assertTrue(result.matched());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static RequestDetails newRequest() {
|
||||||
|
return new SystemRequestDetails();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSupportedSource_ResourceWithNoSourceValue() {
|
public void testSupportedSource_ResourceWithNoSourceValue() {
|
||||||
myObservation.getMeta().getSourceElement().setValue(null);
|
myObservation.getMeta().getSourceElement().setValue(null);
|
||||||
{
|
{
|
||||||
InMemoryMatchResult result = myInMemoryResourceMatcher.match(Constants.PARAM_SOURCE + "=" + TEST_SOURCE, myObservation, mySearchParams);
|
InMemoryMatchResult result = myInMemoryResourceMatcher.match(Constants.PARAM_SOURCE + "=" + TEST_SOURCE, myObservation, mySearchParams, newRequest());
|
||||||
assertFalse(result.matched());
|
assertFalse(result.matched());
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
InMemoryMatchResult result = myInMemoryResourceMatcher.match(Constants.PARAM_SOURCE + "=" + SOURCE_URI, myObservation, mySearchParams);
|
InMemoryMatchResult result = myInMemoryResourceMatcher.match(Constants.PARAM_SOURCE + "=" + SOURCE_URI, myObservation, mySearchParams, newRequest());
|
||||||
assertFalse(result.matched());
|
assertFalse(result.matched());
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
InMemoryMatchResult result = myInMemoryResourceMatcher.match(Constants.PARAM_SOURCE + "=" + REQUEST_ID, myObservation, mySearchParams);
|
InMemoryMatchResult result = myInMemoryResourceMatcher.match(Constants.PARAM_SOURCE + "=" + REQUEST_ID, myObservation, mySearchParams, newRequest());
|
||||||
assertFalse(result.matched());
|
assertFalse(result.matched());
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
InMemoryMatchResult result = myInMemoryResourceMatcher.match(Constants.PARAM_SOURCE + "=#" + REQUEST_ID, myObservation, mySearchParams);
|
InMemoryMatchResult result = myInMemoryResourceMatcher.match(Constants.PARAM_SOURCE + "=#" + REQUEST_ID, myObservation, mySearchParams, newRequest());
|
||||||
assertFalse(result.matched());
|
assertFalse(result.matched());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUnsupportedChained() {
|
public void testUnsupportedChained() {
|
||||||
InMemoryMatchResult result = myInMemoryResourceMatcher.match("encounter.class=FOO", myObservation, mySearchParams);
|
InMemoryMatchResult result = myInMemoryResourceMatcher.match("encounter.class=FOO", myObservation, mySearchParams, newRequest());
|
||||||
assertFalse(result.supported());
|
assertFalse(result.supported());
|
||||||
assertEquals("Parameter: <encounter.class> Reason: Chained parameters are not supported", result.getUnsupportedReason());
|
assertEquals("Parameter: <encounter.class> Reason: Chained parameters are not supported", result.getUnsupportedReason());
|
||||||
}
|
}
|
||||||
|
@ -140,11 +151,11 @@ public class InMemoryResourceMatcherR5Test {
|
||||||
@Test
|
@Test
|
||||||
public void testSupportedNot() {
|
public void testSupportedNot() {
|
||||||
String criteria = "code" + TokenParamModifier.NOT.getValue() + "=" + OBSERVATION_CODE + ",a_different_code";
|
String criteria = "code" + TokenParamModifier.NOT.getValue() + "=" + OBSERVATION_CODE + ",a_different_code";
|
||||||
InMemoryMatchResult result = myInMemoryResourceMatcher.match(criteria, myObservation, mySearchParams);
|
InMemoryMatchResult result = myInMemoryResourceMatcher.match(criteria, myObservation, mySearchParams, newRequest());
|
||||||
assertTrue(result.supported());
|
assertTrue(result.supported());
|
||||||
assertFalse(result.matched(), ":not must not match any of the OR-list");
|
assertFalse(result.matched(), ":not must not match any of the OR-list");
|
||||||
|
|
||||||
result = myInMemoryResourceMatcher.match("code:not=a_different_code,and_another", myObservation, mySearchParams);
|
result = myInMemoryResourceMatcher.match("code:not=a_different_code,and_another", myObservation, mySearchParams, newRequest());
|
||||||
assertTrue(result.supported());
|
assertTrue(result.supported());
|
||||||
assertTrue(result.matched(), ":not matches when NONE match");
|
assertTrue(result.matched(), ":not matches when NONE match");
|
||||||
}
|
}
|
||||||
|
@ -154,7 +165,7 @@ public class InMemoryResourceMatcherR5Test {
|
||||||
IValidationSupport.CodeValidationResult codeValidationResult = new IValidationSupport.CodeValidationResult().setCode(OBSERVATION_CODE);
|
IValidationSupport.CodeValidationResult codeValidationResult = new IValidationSupport.CodeValidationResult().setCode(OBSERVATION_CODE);
|
||||||
when(myValidationSupport.validateCode(any(), any(), any(), any(), any(), any())).thenReturn(codeValidationResult);
|
when(myValidationSupport.validateCode(any(), any(), any(), any(), any(), any())).thenReturn(codeValidationResult);
|
||||||
|
|
||||||
InMemoryMatchResult result = myInMemoryResourceMatcher.match("code" + TokenParamModifier.IN.getValue() + "=" + OBSERVATION_CODE_VALUE_SET_URI, myObservation, mySearchParams);
|
InMemoryMatchResult result = myInMemoryResourceMatcher.match("code" + TokenParamModifier.IN.getValue() + "=" + OBSERVATION_CODE_VALUE_SET_URI, myObservation, mySearchParams, newRequest());
|
||||||
assertTrue(result.supported());
|
assertTrue(result.supported());
|
||||||
assertTrue(result.matched());
|
assertTrue(result.matched());
|
||||||
|
|
||||||
|
@ -166,7 +177,7 @@ public class InMemoryResourceMatcherR5Test {
|
||||||
IValidationSupport.CodeValidationResult codeValidationResult = new IValidationSupport.CodeValidationResult();
|
IValidationSupport.CodeValidationResult codeValidationResult = new IValidationSupport.CodeValidationResult();
|
||||||
when(myValidationSupport.validateCode(any(), any(), any(), any(), any(), any())).thenReturn(codeValidationResult);
|
when(myValidationSupport.validateCode(any(), any(), any(), any(), any(), any())).thenReturn(codeValidationResult);
|
||||||
|
|
||||||
InMemoryMatchResult result = myInMemoryResourceMatcher.match("code" + TokenParamModifier.IN.getValue() + "=" + OBSERVATION_CODE_VALUE_SET_URI, myObservation, mySearchParams);
|
InMemoryMatchResult result = myInMemoryResourceMatcher.match("code" + TokenParamModifier.IN.getValue() + "=" + OBSERVATION_CODE_VALUE_SET_URI, myObservation, mySearchParams, newRequest());
|
||||||
assertTrue(result.supported());
|
assertTrue(result.supported());
|
||||||
assertFalse(result.matched());
|
assertFalse(result.matched());
|
||||||
|
|
||||||
|
@ -178,7 +189,7 @@ public class InMemoryResourceMatcherR5Test {
|
||||||
IValidationSupport.CodeValidationResult codeValidationResult = new IValidationSupport.CodeValidationResult();
|
IValidationSupport.CodeValidationResult codeValidationResult = new IValidationSupport.CodeValidationResult();
|
||||||
when(myValidationSupport.validateCode(any(), any(), any(), any(), any(), any())).thenReturn(codeValidationResult);
|
when(myValidationSupport.validateCode(any(), any(), any(), any(), any(), any())).thenReturn(codeValidationResult);
|
||||||
|
|
||||||
InMemoryMatchResult result = myInMemoryResourceMatcher.match("code" + TokenParamModifier.NOT_IN.getValue() + "=" + OBSERVATION_CODE_VALUE_SET_URI, myObservation, mySearchParams);
|
InMemoryMatchResult result = myInMemoryResourceMatcher.match("code" + TokenParamModifier.NOT_IN.getValue() + "=" + OBSERVATION_CODE_VALUE_SET_URI, myObservation, mySearchParams, newRequest());
|
||||||
assertTrue(result.supported());
|
assertTrue(result.supported());
|
||||||
assertTrue(result.matched());
|
assertTrue(result.matched());
|
||||||
|
|
||||||
|
@ -198,7 +209,7 @@ public class InMemoryResourceMatcherR5Test {
|
||||||
when(myValidationSupport.validateCode(any(), any(), any(), any(), any(), eq(otherValueSet))).thenReturn(noMatchResult);
|
when(myValidationSupport.validateCode(any(), any(), any(), any(), any(), eq(otherValueSet))).thenReturn(noMatchResult);
|
||||||
|
|
||||||
String criteria = "code" + TokenParamModifier.NOT_IN.getValue() + "=" + OBSERVATION_CODE_VALUE_SET_URI + "," + otherValueSet;
|
String criteria = "code" + TokenParamModifier.NOT_IN.getValue() + "=" + OBSERVATION_CODE_VALUE_SET_URI + "," + otherValueSet;
|
||||||
InMemoryMatchResult result = myInMemoryResourceMatcher.match(criteria, myObservation, mySearchParams);
|
InMemoryMatchResult result = myInMemoryResourceMatcher.match(criteria, myObservation, mySearchParams, newRequest());
|
||||||
assertTrue(result.supported());
|
assertTrue(result.supported());
|
||||||
assertFalse(result.matched(), ":not-in matches when NONE of the OR-list match");
|
assertFalse(result.matched(), ":not-in matches when NONE of the OR-list match");
|
||||||
|
|
||||||
|
@ -208,7 +219,7 @@ public class InMemoryResourceMatcherR5Test {
|
||||||
@Test
|
@Test
|
||||||
public void testUnrecognizedParam() {
|
public void testUnrecognizedParam() {
|
||||||
try {
|
try {
|
||||||
InMemoryMatchResult result = myInMemoryResourceMatcher.match("foo=bar", myObservation, mySearchParams);
|
InMemoryMatchResult result = myInMemoryResourceMatcher.match("foo=bar", myObservation, mySearchParams, newRequest());
|
||||||
} catch (MatchUrlService.UnrecognizedSearchParameterException e) {
|
} catch (MatchUrlService.UnrecognizedSearchParameterException e) {
|
||||||
// expected
|
// expected
|
||||||
}
|
}
|
||||||
|
@ -223,7 +234,7 @@ public class InMemoryResourceMatcherR5Test {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testDateUnsupportedDateOp(ParamPrefixEnum theOperator) {
|
private void testDateUnsupportedDateOp(ParamPrefixEnum theOperator) {
|
||||||
InMemoryMatchResult result = myInMemoryResourceMatcher.match("date=" + theOperator.getValue() + OBSERVATION_DATETIME, myObservation, mySearchParams);
|
InMemoryMatchResult result = myInMemoryResourceMatcher.match("date=" + theOperator.getValue() + OBSERVATION_DATETIME, myObservation, mySearchParams, newRequest());
|
||||||
assertFalse(result.supported());
|
assertFalse(result.supported());
|
||||||
assertEquals("Parameter: <date> Reason: The prefix " + theOperator + " is not supported for param type DATE", result.getUnsupportedReason());
|
assertEquals("Parameter: <date> Reason: The prefix " + theOperator + " is not supported for param type DATE", result.getUnsupportedReason());
|
||||||
}
|
}
|
||||||
|
@ -258,17 +269,17 @@ public class InMemoryResourceMatcherR5Test {
|
||||||
|
|
||||||
String equation = "date=" + theOperator.getValue();
|
String equation = "date=" + theOperator.getValue();
|
||||||
{
|
{
|
||||||
InMemoryMatchResult result = myInMemoryResourceMatcher.match(equation + earlyDate, myObservation, mySearchParams);
|
InMemoryMatchResult result = myInMemoryResourceMatcher.match(equation + earlyDate, myObservation, mySearchParams, newRequest());
|
||||||
assertTrue(result.supported(), result.getUnsupportedReason());
|
assertTrue(result.supported(), result.getUnsupportedReason());
|
||||||
assertEquals(result.matched(), theEarly);
|
assertEquals(result.matched(), theEarly);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
InMemoryMatchResult result = myInMemoryResourceMatcher.match(equation + observationDate, myObservation, mySearchParams);
|
InMemoryMatchResult result = myInMemoryResourceMatcher.match(equation + observationDate, myObservation, mySearchParams, newRequest());
|
||||||
assertTrue(result.supported(), result.getUnsupportedReason());
|
assertTrue(result.supported(), result.getUnsupportedReason());
|
||||||
assertEquals(result.matched(), theSame);
|
assertEquals(result.matched(), theSame);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
InMemoryMatchResult result = myInMemoryResourceMatcher.match(equation + lateDate, myObservation, mySearchParams);
|
InMemoryMatchResult result = myInMemoryResourceMatcher.match(equation + lateDate, myObservation, mySearchParams, newRequest());
|
||||||
assertTrue(result.supported(), result.getUnsupportedReason());
|
assertTrue(result.supported(), result.getUnsupportedReason());
|
||||||
assertEquals(result.matched(), theLater);
|
assertEquals(result.matched(), theLater);
|
||||||
}
|
}
|
||||||
|
@ -276,7 +287,7 @@ public class InMemoryResourceMatcherR5Test {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNowPast() {
|
public void testNowPast() {
|
||||||
InMemoryMatchResult result = myInMemoryResourceMatcher.match("date=lt" + BaseDateTimeDt.NOW_DATE_CONSTANT, myObservation, mySearchParams);
|
InMemoryMatchResult result = myInMemoryResourceMatcher.match("date=lt" + BaseDateTimeDt.NOW_DATE_CONSTANT, myObservation, mySearchParams, newRequest());
|
||||||
assertTrue(result.supported(), result.getUnsupportedReason());
|
assertTrue(result.supported(), result.getUnsupportedReason());
|
||||||
assertTrue(result.matched());
|
assertTrue(result.matched());
|
||||||
}
|
}
|
||||||
|
@ -288,7 +299,7 @@ public class InMemoryResourceMatcherR5Test {
|
||||||
futureObservation.setEffective(new DateTimeType(Date.from(nextWeek)));
|
futureObservation.setEffective(new DateTimeType(Date.from(nextWeek)));
|
||||||
ResourceIndexedSearchParams searchParams = extractSearchParams(futureObservation);
|
ResourceIndexedSearchParams searchParams = extractSearchParams(futureObservation);
|
||||||
|
|
||||||
InMemoryMatchResult result = myInMemoryResourceMatcher.match("date=gt" + BaseDateTimeDt.NOW_DATE_CONSTANT, futureObservation, searchParams);
|
InMemoryMatchResult result = myInMemoryResourceMatcher.match("date=gt" + BaseDateTimeDt.NOW_DATE_CONSTANT, futureObservation, searchParams, newRequest());
|
||||||
assertTrue(result.supported(), result.getUnsupportedReason());
|
assertTrue(result.supported(), result.getUnsupportedReason());
|
||||||
assertTrue(result.matched());
|
assertTrue(result.matched());
|
||||||
}
|
}
|
||||||
|
@ -302,7 +313,7 @@ public class InMemoryResourceMatcherR5Test {
|
||||||
futureObservation.setEffective(new DateTimeType(Date.from(nextMinute)));
|
futureObservation.setEffective(new DateTimeType(Date.from(nextMinute)));
|
||||||
ResourceIndexedSearchParams searchParams = extractSearchParams(futureObservation);
|
ResourceIndexedSearchParams searchParams = extractSearchParams(futureObservation);
|
||||||
|
|
||||||
InMemoryMatchResult result = myInMemoryResourceMatcher.match("date=gt" + BaseDateTimeDt.NOW_DATE_CONSTANT, futureObservation, searchParams);
|
InMemoryMatchResult result = myInMemoryResourceMatcher.match("date=gt" + BaseDateTimeDt.NOW_DATE_CONSTANT, futureObservation, searchParams, newRequest());
|
||||||
assertTrue(result.supported(), result.getUnsupportedReason());
|
assertTrue(result.supported(), result.getUnsupportedReason());
|
||||||
assertEquals(1, searchParams.myDateParams.size());
|
assertEquals(1, searchParams.myDateParams.size());
|
||||||
ResourceIndexedSearchParamDate searchParamDate = searchParams.myDateParams.iterator().next();
|
ResourceIndexedSearchParamDate searchParamDate = searchParams.myDateParams.iterator().next();
|
||||||
|
@ -313,7 +324,7 @@ public class InMemoryResourceMatcherR5Test {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTodayPast() {
|
public void testTodayPast() {
|
||||||
InMemoryMatchResult result = myInMemoryResourceMatcher.match("date=lt" + BaseDateTimeDt.TODAY_DATE_CONSTANT, myObservation, mySearchParams);
|
InMemoryMatchResult result = myInMemoryResourceMatcher.match("date=lt" + BaseDateTimeDt.TODAY_DATE_CONSTANT, myObservation, mySearchParams, newRequest());
|
||||||
assertTrue(result.supported(), result.getUnsupportedReason());
|
assertTrue(result.supported(), result.getUnsupportedReason());
|
||||||
assertTrue(result.matched());
|
assertTrue(result.matched());
|
||||||
}
|
}
|
||||||
|
@ -325,7 +336,7 @@ public class InMemoryResourceMatcherR5Test {
|
||||||
futureObservation.setEffective(new DateTimeType(Date.from(nextWeek)));
|
futureObservation.setEffective(new DateTimeType(Date.from(nextWeek)));
|
||||||
ResourceIndexedSearchParams searchParams = extractSearchParams(futureObservation);
|
ResourceIndexedSearchParams searchParams = extractSearchParams(futureObservation);
|
||||||
|
|
||||||
InMemoryMatchResult result = myInMemoryResourceMatcher.match("date=gt" + BaseDateTimeDt.TODAY_DATE_CONSTANT, futureObservation, searchParams);
|
InMemoryMatchResult result = myInMemoryResourceMatcher.match("date=gt" + BaseDateTimeDt.TODAY_DATE_CONSTANT, futureObservation, searchParams, newRequest());
|
||||||
assertTrue(result.supported(), result.getUnsupportedReason());
|
assertTrue(result.supported(), result.getUnsupportedReason());
|
||||||
assertTrue(result.matched());
|
assertTrue(result.matched());
|
||||||
}
|
}
|
||||||
|
@ -337,7 +348,7 @@ public class InMemoryResourceMatcherR5Test {
|
||||||
futureObservation.setEffective(new DateTimeType(Date.from(nextWeek)));
|
futureObservation.setEffective(new DateTimeType(Date.from(nextWeek)));
|
||||||
ResourceIndexedSearchParams searchParams = extractSearchParams(futureObservation);
|
ResourceIndexedSearchParams searchParams = extractSearchParams(futureObservation);
|
||||||
|
|
||||||
InMemoryMatchResult result = myInMemoryResourceMatcher.match("date=gt" + BaseDateTimeDt.TODAY_DATE_CONSTANT, futureObservation, searchParams);
|
InMemoryMatchResult result = myInMemoryResourceMatcher.match("date=gt" + BaseDateTimeDt.TODAY_DATE_CONSTANT, futureObservation, searchParams, newRequest());
|
||||||
assertTrue(result.supported(), result.getUnsupportedReason());
|
assertTrue(result.supported(), result.getUnsupportedReason());
|
||||||
assertFalse(result.matched());
|
assertFalse(result.matched());
|
||||||
}
|
}
|
||||||
|
@ -354,11 +365,11 @@ public class InMemoryResourceMatcherR5Test {
|
||||||
|
|
||||||
String search = "date=gt" + EARLY_DATE + "&date=le" + LATE_DATE;
|
String search = "date=gt" + EARLY_DATE + "&date=le" + LATE_DATE;
|
||||||
|
|
||||||
InMemoryMatchResult resultInsidePeriod = myInMemoryResourceMatcher.match(search, insidePeriodObservation, insidePeriodSearchParams);
|
InMemoryMatchResult resultInsidePeriod = myInMemoryResourceMatcher.match(search, insidePeriodObservation, insidePeriodSearchParams, newRequest());
|
||||||
assertTrue(resultInsidePeriod.supported(), resultInsidePeriod.getUnsupportedReason());
|
assertTrue(resultInsidePeriod.supported(), resultInsidePeriod.getUnsupportedReason());
|
||||||
assertTrue(resultInsidePeriod.matched());
|
assertTrue(resultInsidePeriod.matched());
|
||||||
|
|
||||||
InMemoryMatchResult resultOutsidePeriod = myInMemoryResourceMatcher.match(search, outsidePeriodObservation, outsidePeriodSearchParams);
|
InMemoryMatchResult resultOutsidePeriod = myInMemoryResourceMatcher.match(search, outsidePeriodObservation, outsidePeriodSearchParams, newRequest());
|
||||||
assertTrue(resultOutsidePeriod.supported(), resultOutsidePeriod.getUnsupportedReason());
|
assertTrue(resultOutsidePeriod.supported(), resultOutsidePeriod.getUnsupportedReason());
|
||||||
assertFalse(resultOutsidePeriod.matched());
|
assertFalse(resultOutsidePeriod.matched());
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,19 +4,15 @@ import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||||
import ca.uhn.fhir.interceptor.api.IInterceptorService;
|
import ca.uhn.fhir.interceptor.api.IInterceptorService;
|
||||||
import ca.uhn.fhir.jpa.cache.IResourceChangeListenerRegistry;
|
import ca.uhn.fhir.jpa.cache.*;
|
||||||
import ca.uhn.fhir.jpa.cache.IResourceVersionSvc;
|
|
||||||
import ca.uhn.fhir.jpa.cache.ResourceChangeListenerCacheFactory;
|
|
||||||
import ca.uhn.fhir.jpa.cache.ResourceChangeListenerCacheRefresherImpl;
|
|
||||||
import ca.uhn.fhir.jpa.cache.ResourceChangeListenerRegistryImpl;
|
|
||||||
import ca.uhn.fhir.jpa.cache.ResourceChangeResult;
|
|
||||||
import ca.uhn.fhir.jpa.cache.ResourceVersionMap;
|
|
||||||
import ca.uhn.fhir.jpa.cache.config.RegisteredResourceListenerFactoryConfig;
|
import ca.uhn.fhir.jpa.cache.config.RegisteredResourceListenerFactoryConfig;
|
||||||
import ca.uhn.fhir.jpa.model.entity.StorageSettings;
|
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||||
|
import ca.uhn.fhir.jpa.model.entity.StorageSettings;
|
||||||
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
|
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
|
||||||
|
import ca.uhn.fhir.jpa.searchparam.extractor.SearchParamExtractorService;
|
||||||
import ca.uhn.fhir.jpa.searchparam.matcher.InMemoryMatchResult;
|
import ca.uhn.fhir.jpa.searchparam.matcher.InMemoryMatchResult;
|
||||||
import ca.uhn.fhir.jpa.searchparam.matcher.InMemoryResourceMatcher;
|
import ca.uhn.fhir.jpa.searchparam.matcher.InMemoryResourceMatcher;
|
||||||
|
import ca.uhn.fhir.jpa.searchparam.matcher.IndexedSearchParamExtractor;
|
||||||
import ca.uhn.fhir.jpa.searchparam.matcher.SearchParamMatcher;
|
import ca.uhn.fhir.jpa.searchparam.matcher.SearchParamMatcher;
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
import ca.uhn.fhir.rest.server.SimpleBundleProvider;
|
import ca.uhn.fhir.rest.server.SimpleBundleProvider;
|
||||||
|
@ -27,11 +23,7 @@ import ca.uhn.fhir.rest.server.util.ResourceSearchParams;
|
||||||
import ca.uhn.fhir.util.HapiExtensions;
|
import ca.uhn.fhir.util.HapiExtensions;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
import org.hl7.fhir.r4.model.Enumerations;
|
import org.hl7.fhir.r4.model.*;
|
||||||
import org.hl7.fhir.r4.model.SearchParameter;
|
|
||||||
import org.hl7.fhir.r4.model.StringType;
|
|
||||||
import org.hl7.fhir.r4.model.CodeType;
|
|
||||||
import org.hl7.fhir.r4.model.Extension;
|
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
@ -46,36 +38,26 @@ import org.testcontainers.shaded.com.google.common.collect.Sets;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.empty;
|
import static org.hamcrest.Matchers.empty;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.*;
|
||||||
import static org.mockito.Mockito.never;
|
|
||||||
import static org.mockito.Mockito.reset;
|
|
||||||
import static org.mockito.Mockito.times;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
@ExtendWith(SpringExtension.class)
|
@ExtendWith(SpringExtension.class)
|
||||||
public class SearchParamRegistryImplTest {
|
public class SearchParamRegistryImplTest {
|
||||||
|
public static final int TEST_SEARCH_PARAMS = 3;
|
||||||
private static final FhirContext ourFhirContext = FhirContext.forR4();
|
private static final FhirContext ourFhirContext = FhirContext.forR4();
|
||||||
private static final ReadOnlySearchParamCache ourBuiltInSearchParams = ReadOnlySearchParamCache.fromFhirContext(ourFhirContext, new SearchParameterCanonicalizer(ourFhirContext));
|
private static final ReadOnlySearchParamCache ourBuiltInSearchParams = ReadOnlySearchParamCache.fromFhirContext(ourFhirContext, new SearchParameterCanonicalizer(ourFhirContext));
|
||||||
|
|
||||||
public static final int TEST_SEARCH_PARAMS = 3;
|
|
||||||
private static final List<ResourceTable> ourEntities;
|
private static final List<ResourceTable> ourEntities;
|
||||||
private static final ResourceVersionMap ourResourceVersionMap;
|
private static final ResourceVersionMap ourResourceVersionMap;
|
||||||
private static int ourLastId;
|
|
||||||
private static final int ourBuiltinPatientSearchParamCount;
|
private static final int ourBuiltinPatientSearchParamCount;
|
||||||
|
private static int ourLastId;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
ourEntities = new ArrayList<>();
|
ourEntities = new ArrayList<>();
|
||||||
|
@ -90,8 +72,6 @@ public class SearchParamRegistryImplTest {
|
||||||
SearchParamRegistryImpl mySearchParamRegistry;
|
SearchParamRegistryImpl mySearchParamRegistry;
|
||||||
@Autowired
|
@Autowired
|
||||||
private ResourceChangeListenerRegistryImpl myResourceChangeListenerRegistry;
|
private ResourceChangeListenerRegistryImpl myResourceChangeListenerRegistry;
|
||||||
@Autowired
|
|
||||||
private ResourceChangeListenerCacheRefresherImpl myChangeListenerCacheRefresher;
|
|
||||||
|
|
||||||
@MockBean
|
@MockBean
|
||||||
private IResourceVersionSvc myResourceVersionSvc;
|
private IResourceVersionSvc myResourceVersionSvc;
|
||||||
|
@ -103,55 +83,11 @@ public class SearchParamRegistryImplTest {
|
||||||
private SearchParamMatcher mySearchParamMatcher;
|
private SearchParamMatcher mySearchParamMatcher;
|
||||||
@MockBean
|
@MockBean
|
||||||
private MatchUrlService myMatchUrlService;
|
private MatchUrlService myMatchUrlService;
|
||||||
|
@MockBean
|
||||||
@Configuration
|
private SearchParamExtractorService mySearchParamExtractorService;
|
||||||
@Import(RegisteredResourceListenerFactoryConfig.class)
|
@MockBean
|
||||||
static class SpringConfig {
|
private IndexedSearchParamExtractor myIndexedSearchParamExtractor;
|
||||||
@Bean
|
private int myAnswerCount = 0;
|
||||||
FhirContext fhirContext() {
|
|
||||||
return ourFhirContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
StorageSettings storageSettings() {
|
|
||||||
StorageSettings storageSettings = new StorageSettings();
|
|
||||||
storageSettings.setDefaultSearchParamsCanBeOverridden(true);
|
|
||||||
return storageSettings;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
ISearchParamRegistry searchParamRegistry() {
|
|
||||||
return new SearchParamRegistryImpl();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
SearchParameterCanonicalizer searchParameterCanonicalizer(FhirContext theFhirContext) {
|
|
||||||
return new SearchParameterCanonicalizer(theFhirContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
IResourceChangeListenerRegistry resourceChangeListenerRegistry(FhirContext theFhirContext, ResourceChangeListenerCacheFactory theResourceChangeListenerCacheFactory, InMemoryResourceMatcher theInMemoryResourceMatcher) {
|
|
||||||
return new ResourceChangeListenerRegistryImpl(theFhirContext, theResourceChangeListenerCacheFactory, theInMemoryResourceMatcher);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
ResourceChangeListenerCacheRefresherImpl resourceChangeListenerCacheRefresher() {
|
|
||||||
return new ResourceChangeListenerCacheRefresherImpl();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
InMemoryResourceMatcher inMemoryResourceMatcher() {
|
|
||||||
InMemoryResourceMatcher retval = mock(InMemoryResourceMatcher.class);
|
|
||||||
when(retval.canBeEvaluatedInMemory(any(), any())).thenReturn(InMemoryMatchResult.successfulMatch());
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
IValidationSupport validationSupport() {
|
|
||||||
return mock(IValidationSupport.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
private static ResourceTable createEntity(long theId, int theVersion) {
|
private static ResourceTable createEntity(long theId, int theVersion) {
|
||||||
|
@ -162,8 +98,6 @@ public class SearchParamRegistryImplTest {
|
||||||
return searchParamEntity;
|
return searchParamEntity;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int myAnswerCount = 0;
|
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void before() {
|
public void before() {
|
||||||
myAnswerCount = 0;
|
myAnswerCount = 0;
|
||||||
|
@ -408,4 +342,53 @@ public class SearchParamRegistryImplTest {
|
||||||
return searchParameter;
|
return searchParameter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@Import(RegisteredResourceListenerFactoryConfig.class)
|
||||||
|
static class SpringConfig {
|
||||||
|
@Bean
|
||||||
|
FhirContext fhirContext() {
|
||||||
|
return ourFhirContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
StorageSettings storageSettings() {
|
||||||
|
StorageSettings storageSettings = new StorageSettings();
|
||||||
|
storageSettings.setDefaultSearchParamsCanBeOverridden(true);
|
||||||
|
return storageSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
ISearchParamRegistry searchParamRegistry() {
|
||||||
|
return new SearchParamRegistryImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
SearchParameterCanonicalizer searchParameterCanonicalizer(FhirContext theFhirContext) {
|
||||||
|
return new SearchParameterCanonicalizer(theFhirContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
IResourceChangeListenerRegistry resourceChangeListenerRegistry(FhirContext theFhirContext, ResourceChangeListenerCacheFactory theResourceChangeListenerCacheFactory, InMemoryResourceMatcher theInMemoryResourceMatcher) {
|
||||||
|
return new ResourceChangeListenerRegistryImpl(theFhirContext, theResourceChangeListenerCacheFactory, theInMemoryResourceMatcher);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
ResourceChangeListenerCacheRefresherImpl resourceChangeListenerCacheRefresher() {
|
||||||
|
return new ResourceChangeListenerCacheRefresherImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
InMemoryResourceMatcher inMemoryResourceMatcher() {
|
||||||
|
InMemoryResourceMatcher retval = mock(InMemoryResourceMatcher.class);
|
||||||
|
when(retval.canBeEvaluatedInMemory(any(), any())).thenReturn(InMemoryMatchResult.successfulMatch());
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
IValidationSupport validationSupport() {
|
||||||
|
return mock(IValidationSupport.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR Subscription Server
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2023 Smile CDR, Inc.
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
package ca.uhn.fhir.jpa.subscription.match.matcher.subscriber;
|
package ca.uhn.fhir.jpa.subscription.match.matcher.subscriber;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
|
|
@ -28,6 +28,8 @@ import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||||
import ca.uhn.fhir.jpa.api.svc.ISearchCoordinatorSvc;
|
import ca.uhn.fhir.jpa.api.svc.ISearchCoordinatorSvc;
|
||||||
import ca.uhn.fhir.jpa.api.svc.ISearchSvc;
|
import ca.uhn.fhir.jpa.api.svc.ISearchSvc;
|
||||||
|
import ca.uhn.fhir.jpa.dao.ISearchBuilder;
|
||||||
|
import ca.uhn.fhir.jpa.dao.SearchBuilderFactory;
|
||||||
import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService;
|
import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService;
|
||||||
import ca.uhn.fhir.jpa.model.sched.HapiJob;
|
import ca.uhn.fhir.jpa.model.sched.HapiJob;
|
||||||
import ca.uhn.fhir.jpa.model.sched.IHasScheduledJobs;
|
import ca.uhn.fhir.jpa.model.sched.IHasScheduledJobs;
|
||||||
|
@ -38,7 +40,6 @@ import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
import ca.uhn.fhir.jpa.subscription.match.matcher.matching.IResourceModifiedConsumer;
|
import ca.uhn.fhir.jpa.subscription.match.matcher.matching.IResourceModifiedConsumer;
|
||||||
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage;
|
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage;
|
||||||
import ca.uhn.fhir.model.dstu2.valueset.ResourceTypeEnum;
|
import ca.uhn.fhir.model.dstu2.valueset.ResourceTypeEnum;
|
||||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
|
||||||
import ca.uhn.fhir.rest.api.CacheControlDirective;
|
import ca.uhn.fhir.rest.api.CacheControlDirective;
|
||||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
|
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
|
||||||
|
@ -65,6 +66,7 @@ import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -110,9 +112,11 @@ public class SubscriptionTriggeringSvcImpl implements ISubscriptionTriggeringSvc
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ISearchSvc mySearchService;
|
private ISearchSvc mySearchService;
|
||||||
|
@Autowired
|
||||||
|
private SearchBuilderFactory mySearchBuilderFactory;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IBaseParameters triggerSubscription(List<IPrimitiveType<String>> theResourceIds, List<IPrimitiveType<String>> theSearchUrls, @IdParam IIdType theSubscriptionId) {
|
public IBaseParameters triggerSubscription(@Nullable List<IPrimitiveType<String>> theResourceIds, @Nullable List<IPrimitiveType<String>> theSearchUrls, @Nullable IIdType theSubscriptionId) {
|
||||||
|
|
||||||
if (myStorageSettings.getSupportedSubscriptionTypes().isEmpty()) {
|
if (myStorageSettings.getSupportedSubscriptionTypes().isEmpty()) {
|
||||||
throw new PreconditionFailedException(Msg.code(22) + "Subscription processing not active on this server");
|
throw new PreconditionFailedException(Msg.code(22) + "Subscription processing not active on this server");
|
||||||
|
@ -229,7 +233,7 @@ public class SubscriptionTriggeringSvcImpl implements ISubscriptionTriggeringSvc
|
||||||
|
|
||||||
// This is the job initial step where we set ourselves up to do the actual re-submitting of resources
|
// This is the job initial step where we set ourselves up to do the actual re-submitting of resources
|
||||||
// to the broker. Note that querying of resource can be done synchronously or asynchronously
|
// to the broker. Note that querying of resource can be done synchronously or asynchronously
|
||||||
if ( isInitialStep(theJobDetails) && isNotEmpty(theJobDetails.getRemainingSearchUrls()) && totalSubmitted < myMaxSubmitPerPass){
|
if (isInitialStep(theJobDetails) && isNotEmpty(theJobDetails.getRemainingSearchUrls()) && totalSubmitted < myMaxSubmitPerPass) {
|
||||||
|
|
||||||
String nextSearchUrl = theJobDetails.getRemainingSearchUrls().remove(0);
|
String nextSearchUrl = theJobDetails.getRemainingSearchUrls().remove(0);
|
||||||
RuntimeResourceDefinition resourceDef = UrlUtil.parseUrlResourceType(myFhirContext, nextSearchUrl);
|
RuntimeResourceDefinition resourceDef = UrlUtil.parseUrlResourceType(myFhirContext, nextSearchUrl);
|
||||||
|
@ -266,7 +270,7 @@ public class SubscriptionTriggeringSvcImpl implements ISubscriptionTriggeringSvc
|
||||||
|
|
||||||
String searchUrl = theJobDetails.getCurrentSearchUrl();
|
String searchUrl = theJobDetails.getCurrentSearchUrl();
|
||||||
|
|
||||||
ourLog.info("Triggered job [{}] - Starting synchronous processing at offset {} and index {}", theJobDetails.getJobId(), theJobDetails.getCurrentOffset(), fromIndex );
|
ourLog.info("Triggered job [{}] - Starting synchronous processing at offset {} and index {}", theJobDetails.getJobId(), theJobDetails.getCurrentOffset(), fromIndex);
|
||||||
|
|
||||||
int submittableCount = myMaxSubmitPerPass - totalSubmitted;
|
int submittableCount = myMaxSubmitPerPass - totalSubmitted;
|
||||||
int toIndex = fromIndex + submittableCount;
|
int toIndex = fromIndex + submittableCount;
|
||||||
|
@ -339,6 +343,8 @@ public class SubscriptionTriggeringSvcImpl implements ISubscriptionTriggeringSvc
|
||||||
}
|
}
|
||||||
|
|
||||||
ourLog.info("Triggering job[{}] search {} requesting resources {} - {}", theJobDetails.getJobId(), theJobDetails.getCurrentSearchUuid(), fromIndex, toIndex);
|
ourLog.info("Triggering job[{}] search {} requesting resources {} - {}", theJobDetails.getJobId(), theJobDetails.getCurrentSearchUuid(), fromIndex, toIndex);
|
||||||
|
|
||||||
|
|
||||||
List<IResourcePersistentId<?>> resourceIds;
|
List<IResourcePersistentId<?>> resourceIds;
|
||||||
RequestPartitionId requestPartitionId = RequestPartitionId.allPartitions();
|
RequestPartitionId requestPartitionId = RequestPartitionId.allPartitions();
|
||||||
resourceIds = mySearchCoordinatorSvc.getResources(theJobDetails.getCurrentSearchUuid(), fromIndex, toIndex, null, requestPartitionId);
|
resourceIds = mySearchCoordinatorSvc.getResources(theJobDetails.getCurrentSearchUuid(), fromIndex, toIndex, null, requestPartitionId);
|
||||||
|
@ -346,8 +352,18 @@ public class SubscriptionTriggeringSvcImpl implements ISubscriptionTriggeringSvc
|
||||||
ourLog.info("Triggering job[{}] delivering {} resources", theJobDetails.getJobId(), resourceIds.size());
|
ourLog.info("Triggering job[{}] delivering {} resources", theJobDetails.getJobId(), resourceIds.size());
|
||||||
int highestIndexSubmitted = theJobDetails.getCurrentSearchLastUploadedIndex();
|
int highestIndexSubmitted = theJobDetails.getCurrentSearchLastUploadedIndex();
|
||||||
|
|
||||||
for (IResourcePersistentId next : resourceIds) {
|
String resourceType = myFhirContext.getResourceType(theJobDetails.getCurrentSearchResourceType());
|
||||||
IBaseResource nextResource = resourceDao.readByPid(next);
|
RuntimeResourceDefinition resourceDef = myFhirContext.getResourceDefinition(theJobDetails.getCurrentSearchResourceType());
|
||||||
|
ISearchBuilder searchBuilder = mySearchBuilderFactory.newSearchBuilder(resourceDao, resourceType, resourceDef.getImplementingClass());
|
||||||
|
List<IBaseResource> listToPopulate = new ArrayList<>();
|
||||||
|
|
||||||
|
myTransactionService
|
||||||
|
.withSystemRequest()
|
||||||
|
.execute(() -> {
|
||||||
|
searchBuilder.loadResourcesByPid(resourceIds, Collections.emptyList(), listToPopulate, false, new SystemRequestDetails());
|
||||||
|
});
|
||||||
|
|
||||||
|
for (IBaseResource nextResource : listToPopulate) {
|
||||||
Future<Void> future = submitResource(theJobDetails.getSubscriptionId(), nextResource);
|
Future<Void> future = submitResource(theJobDetails.getSubscriptionId(), nextResource);
|
||||||
futures.add(Pair.of(nextResource.getIdElement().getIdPart(), future));
|
futures.add(Pair.of(nextResource.getIdElement().getIdPart(), future));
|
||||||
totalSubmitted++;
|
totalSubmitted++;
|
||||||
|
@ -376,7 +392,7 @@ public class SubscriptionTriggeringSvcImpl implements ISubscriptionTriggeringSvc
|
||||||
return isBlank(theJobDetails.myCurrentSearchUuid) && isBlank(theJobDetails.myCurrentSearchUrl);
|
return isBlank(theJobDetails.myCurrentSearchUuid) && isBlank(theJobDetails.myCurrentSearchUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean jobHasCompleted(SubscriptionTriggeringJobDetails theJobDetails){
|
private boolean jobHasCompleted(SubscriptionTriggeringJobDetails theJobDetails) {
|
||||||
return isInitialStep(theJobDetails);
|
return isInitialStep(theJobDetails);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -599,11 +615,11 @@ public class SubscriptionTriggeringSvcImpl implements ISubscriptionTriggeringSvc
|
||||||
myCurrentSearchLastUploadedIndex = theCurrentSearchLastUploadedIndex;
|
myCurrentSearchLastUploadedIndex = theCurrentSearchLastUploadedIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearCurrentSearchUrl(){
|
public void clearCurrentSearchUrl() {
|
||||||
myCurrentSearchUrl = null;
|
myCurrentSearchUrl = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getCurrentOffset(){
|
public int getCurrentOffset() {
|
||||||
return myCurrentOffset;
|
return myCurrentOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR Subscription Server
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2023 Smile CDR, Inc.
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
package ca.uhn.fhir.jpa.topic;
|
package ca.uhn.fhir.jpa.topic;
|
||||||
|
|
||||||
import org.hl7.fhir.r5.model.SubscriptionTopic;
|
import org.hl7.fhir.r5.model.SubscriptionTopic;
|
||||||
|
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR Subscription Server
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2023 Smile CDR, Inc.
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
package ca.uhn.fhir.jpa.topic;
|
package ca.uhn.fhir.jpa.topic;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR Subscription Server
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2023 Smile CDR, Inc.
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
package ca.uhn.fhir.jpa.topic;
|
package ca.uhn.fhir.jpa.topic;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.searchparam.matcher.InMemoryMatchResult;
|
import ca.uhn.fhir.jpa.searchparam.matcher.InMemoryMatchResult;
|
||||||
|
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR Subscription Server
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2023 Smile CDR, Inc.
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
package ca.uhn.fhir.jpa.topic;
|
package ca.uhn.fhir.jpa.topic;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR Subscription Server
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2023 Smile CDR, Inc.
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
package ca.uhn.fhir.jpa.topic;
|
package ca.uhn.fhir.jpa.topic;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR Subscription Server
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2023 Smile CDR, Inc.
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
package ca.uhn.fhir.jpa.topic;
|
package ca.uhn.fhir.jpa.topic;
|
||||||
|
|
||||||
import org.hl7.fhir.r5.model.SubscriptionTopic;
|
import org.hl7.fhir.r5.model.SubscriptionTopic;
|
||||||
|
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR Subscription Server
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2023 Smile CDR, Inc.
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
package ca.uhn.fhir.jpa.topic;
|
package ca.uhn.fhir.jpa.topic;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR Subscription Server
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2023 Smile CDR, Inc.
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
package ca.uhn.fhir.jpa.topic;
|
package ca.uhn.fhir.jpa.topic;
|
||||||
|
|
||||||
import ca.uhn.fhir.rest.server.messaging.BaseResourceMessage;
|
import ca.uhn.fhir.rest.server.messaging.BaseResourceMessage;
|
||||||
|
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR Subscription Server
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2023 Smile CDR, Inc.
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
package ca.uhn.fhir.jpa.topic;
|
package ca.uhn.fhir.jpa.topic;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||||
|
|
|
@ -766,8 +766,8 @@ public class FhirResourceDaoR4ComboUniqueParamIT extends BaseComboParamsR4Test {
|
||||||
try {
|
try {
|
||||||
myPatientDao.create(pt1).getId().toUnqualifiedVersionless();
|
myPatientDao.create(pt1).getId().toUnqualifiedVersionless();
|
||||||
fail();
|
fail();
|
||||||
} catch (PreconditionFailedException e) {
|
} catch (ResourceVersionConflictException e) {
|
||||||
assertEquals(Msg.code(1093) + "Can not create resource of type Patient as it would create a duplicate unique index matching query: Patient?birthdate=2011-01-01&gender=http%3A%2F%2Fhl7.org%2Ffhir%2Fadministrative-gender%7Cmale (existing index belongs to Patient/" + id1.getIdPart() + ", new unique index created by SearchParameter/patient-gender-birthdate)", e.getMessage());
|
assertThat(e.getMessage(), containsString("new unique index created by SearchParameter/patient-gender-birthdate"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -786,7 +786,7 @@ public class FhirResourceDaoR4ComboUniqueParamIT extends BaseComboParamsR4Test {
|
||||||
try {
|
try {
|
||||||
myPatientDao.create(pt);
|
myPatientDao.create(pt);
|
||||||
fail();
|
fail();
|
||||||
} catch (PreconditionFailedException e) {
|
} catch (ResourceVersionConflictException e) {
|
||||||
// good
|
// good
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1536,7 +1536,7 @@ public class FhirResourceDaoR4ComboUniqueParamIT extends BaseComboParamsR4Test {
|
||||||
try {
|
try {
|
||||||
myPatientDao.create(pt1).getId().toUnqualifiedVersionless();
|
myPatientDao.create(pt1).getId().toUnqualifiedVersionless();
|
||||||
fail();
|
fail();
|
||||||
} catch (PreconditionFailedException e) {
|
} catch (ResourceVersionConflictException e) {
|
||||||
assertThat(e.getMessage(), containsString("new unique index created by SearchParameter/patient-gender-birthdate"));
|
assertThat(e.getMessage(), containsString("new unique index created by SearchParameter/patient-gender-birthdate"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1551,7 +1551,7 @@ public class FhirResourceDaoR4ComboUniqueParamIT extends BaseComboParamsR4Test {
|
||||||
try {
|
try {
|
||||||
myPatientDao.update(pt2);
|
myPatientDao.update(pt2);
|
||||||
fail();
|
fail();
|
||||||
} catch (PreconditionFailedException e) {
|
} catch (ResourceVersionConflictException e) {
|
||||||
assertThat(e.getMessage(), containsString("new unique index created by SearchParameter/patient-gender-birthdate"));
|
assertThat(e.getMessage(), containsString("new unique index created by SearchParameter/patient-gender-birthdate"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package ca.uhn.fhir.jpa.dao.r4;
|
package ca.uhn.fhir.jpa.dao.r4;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.support.ValidationSupportContext;
|
import ca.uhn.fhir.context.support.ValidationSupportContext;
|
||||||
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
|
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
|
||||||
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
|
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
|
||||||
|
@ -13,8 +14,11 @@ import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||||
import ca.uhn.fhir.jpa.provider.BaseResourceProviderR4Test;
|
import ca.uhn.fhir.jpa.provider.BaseResourceProviderR4Test;
|
||||||
import ca.uhn.fhir.jpa.search.PersistedJpaSearchFirstPageBundleProvider;
|
import ca.uhn.fhir.jpa.search.PersistedJpaSearchFirstPageBundleProvider;
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.submit.interceptor.SubscriptionMatcherInterceptor;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.triggering.ISubscriptionTriggeringSvc;
|
||||||
import ca.uhn.fhir.jpa.term.TermReadSvcImpl;
|
import ca.uhn.fhir.jpa.term.TermReadSvcImpl;
|
||||||
import ca.uhn.fhir.jpa.util.SqlQuery;
|
import ca.uhn.fhir.jpa.util.SqlQuery;
|
||||||
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||||
import ca.uhn.fhir.rest.api.SortSpec;
|
import ca.uhn.fhir.rest.api.SortSpec;
|
||||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
|
@ -25,6 +29,8 @@ import ca.uhn.fhir.rest.server.SimpleBundleProvider;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor;
|
import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.auth.PolicyEnum;
|
import ca.uhn.fhir.rest.server.interceptor.auth.PolicyEnum;
|
||||||
|
import ca.uhn.fhir.test.utilities.server.HashMapResourceProviderExtension;
|
||||||
|
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
||||||
import ca.uhn.fhir.util.BundleBuilder;
|
import ca.uhn.fhir.util.BundleBuilder;
|
||||||
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;
|
||||||
|
@ -49,6 +55,7 @@ import org.hl7.fhir.r4.model.Quantity;
|
||||||
import org.hl7.fhir.r4.model.Reference;
|
import org.hl7.fhir.r4.model.Reference;
|
||||||
import org.hl7.fhir.r4.model.ServiceRequest;
|
import org.hl7.fhir.r4.model.ServiceRequest;
|
||||||
import org.hl7.fhir.r4.model.StringType;
|
import org.hl7.fhir.r4.model.StringType;
|
||||||
|
import org.hl7.fhir.r4.model.Subscription;
|
||||||
import org.hl7.fhir.r4.model.ValueSet;
|
import org.hl7.fhir.r4.model.ValueSet;
|
||||||
import org.hl7.fhir.r4.model.BooleanType;
|
import org.hl7.fhir.r4.model.BooleanType;
|
||||||
import org.hl7.fhir.r4.model.CodeType;
|
import org.hl7.fhir.r4.model.CodeType;
|
||||||
|
@ -56,8 +63,10 @@ import org.hl7.fhir.r4.model.Parameters;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.MethodOrderer;
|
import org.junit.jupiter.api.MethodOrderer;
|
||||||
|
import org.junit.jupiter.api.Order;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.TestMethodOrder;
|
import org.junit.jupiter.api.TestMethodOrder;
|
||||||
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.domain.PageRequest;
|
import org.springframework.data.domain.PageRequest;
|
||||||
import org.springframework.data.domain.Slice;
|
import org.springframework.data.domain.Slice;
|
||||||
|
@ -103,6 +112,19 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4QueryCountTest.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoR4QueryCountTest.class);
|
||||||
@Autowired
|
@Autowired
|
||||||
private ISearchParamPresentDao mySearchParamPresentDao;
|
private ISearchParamPresentDao mySearchParamPresentDao;
|
||||||
|
@Autowired
|
||||||
|
private ISubscriptionTriggeringSvc mySubscriptionTriggeringSvc;
|
||||||
|
@Autowired
|
||||||
|
private SubscriptionMatcherInterceptor mySubscriptionMatcherInterceptor;
|
||||||
|
|
||||||
|
@RegisterExtension
|
||||||
|
@Order(0)
|
||||||
|
public static final RestfulServerExtension ourServer = new RestfulServerExtension(FhirContext.forR4Cached())
|
||||||
|
.keepAliveBetweenTests();
|
||||||
|
@RegisterExtension
|
||||||
|
@Order(1)
|
||||||
|
public static final HashMapResourceProviderExtension<Patient> ourPatientProvider = new HashMapResourceProviderExtension<>(ourServer, Patient.class);
|
||||||
|
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
public void afterResetDao() {
|
public void afterResetDao() {
|
||||||
|
@ -120,6 +142,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test
|
||||||
myStorageSettings.setPopulateIdentifierInAutoCreatedPlaceholderReferenceTargets(new JpaStorageSettings().isPopulateIdentifierInAutoCreatedPlaceholderReferenceTargets());
|
myStorageSettings.setPopulateIdentifierInAutoCreatedPlaceholderReferenceTargets(new JpaStorageSettings().isPopulateIdentifierInAutoCreatedPlaceholderReferenceTargets());
|
||||||
myStorageSettings.setResourceClientIdStrategy(new JpaStorageSettings().getResourceClientIdStrategy());
|
myStorageSettings.setResourceClientIdStrategy(new JpaStorageSettings().getResourceClientIdStrategy());
|
||||||
myStorageSettings.setTagStorageMode(new JpaStorageSettings().getTagStorageMode());
|
myStorageSettings.setTagStorageMode(new JpaStorageSettings().getTagStorageMode());
|
||||||
|
myStorageSettings.clearSupportedSubscriptionTypesForUnitTest();
|
||||||
|
|
||||||
TermReadSvcImpl.setForceDisableHibernateSearchForUnitTest(false);
|
TermReadSvcImpl.setForceDisableHibernateSearchForUnitTest(false);
|
||||||
}
|
}
|
||||||
|
@ -2864,6 +2887,46 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See the class javadoc before changing the counts in this test!
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Test
|
||||||
|
public void testTriggerSubscription() throws Exception {
|
||||||
|
// Setup
|
||||||
|
|
||||||
|
myStorageSettings.addSupportedSubscriptionType(org.hl7.fhir.dstu2.model.Subscription.SubscriptionChannelType.RESTHOOK);
|
||||||
|
mySubscriptionMatcherInterceptor.startIfNeeded();
|
||||||
|
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
createPatient(withActiveTrue());
|
||||||
|
}
|
||||||
|
|
||||||
|
Subscription subscription = new Subscription();
|
||||||
|
subscription.getChannel().setEndpoint(ourServer.getBaseUrl());
|
||||||
|
subscription.getChannel().setType(Subscription.SubscriptionChannelType.RESTHOOK);
|
||||||
|
subscription.getChannel().setPayload(Constants.CT_FHIR_JSON_NEW);
|
||||||
|
subscription.setStatus(Subscription.SubscriptionStatus.REQUESTED);
|
||||||
|
subscription.setCriteria("Patient?active=true");
|
||||||
|
IIdType subscriptionId = mySubscriptionDao.create(subscription, mySrd).getId().toUnqualifiedVersionless();
|
||||||
|
waitForActivatedSubscriptionCount(1);
|
||||||
|
|
||||||
|
mySubscriptionTriggeringSvc.triggerSubscription(null, List.of(new StringType("Patient?active=true")), subscriptionId);
|
||||||
|
|
||||||
|
// Test
|
||||||
|
myCaptureQueriesListener.clear();
|
||||||
|
mySubscriptionTriggeringSvc.runDeliveryPass();
|
||||||
|
ourPatientProvider.waitForUpdateCount(10);
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
assertEquals(6, myCaptureQueriesListener.countSelectQueriesForCurrentThread());
|
||||||
|
assertEquals(1, myCaptureQueriesListener.countUpdateQueriesForCurrentThread());
|
||||||
|
assertEquals(11, myCaptureQueriesListener.countInsertQueriesForCurrentThread());
|
||||||
|
assertEquals(0, myCaptureQueriesListener.countDeleteQueriesForCurrentThread());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See the class javadoc before changing the counts in this test!
|
* See the class javadoc before changing the counts in this test!
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -2,6 +2,7 @@ package ca.uhn.fhir.jpa.subscription.module.matcher;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.i18n.Msg;
|
import ca.uhn.fhir.i18n.Msg;
|
||||||
|
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||||
import ca.uhn.fhir.jpa.model.entity.StorageSettings;
|
import ca.uhn.fhir.jpa.model.entity.StorageSettings;
|
||||||
import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel;
|
import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
|
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
|
||||||
|
@ -19,6 +20,8 @@ import ca.uhn.fhir.jpa.test.config.TestHSearchAddInConfig;
|
||||||
import ca.uhn.fhir.jpa.test.config.TestR4Config;
|
import ca.uhn.fhir.jpa.test.config.TestR4Config;
|
||||||
import ca.uhn.fhir.jpa.util.CoordCalculatorTestUtil;
|
import ca.uhn.fhir.jpa.util.CoordCalculatorTestUtil;
|
||||||
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
||||||
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
|
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
|
||||||
import ca.uhn.fhir.rest.param.CompositeParam;
|
import ca.uhn.fhir.rest.param.CompositeParam;
|
||||||
import ca.uhn.fhir.rest.param.DateParam;
|
import ca.uhn.fhir.rest.param.DateParam;
|
||||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||||
|
@ -33,10 +36,15 @@ 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.TokenOrListParam;
|
import ca.uhn.fhir.rest.param.TokenOrListParam;
|
||||||
import ca.uhn.fhir.rest.param.TokenParam;
|
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.UriParam;
|
||||||
|
import ca.uhn.fhir.rest.param.UriParamQualifierEnum;
|
||||||
|
import org.apache.commons.io.Charsets;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
import org.hl7.fhir.r4.model.Basic;
|
||||||
|
import org.hl7.fhir.r4.model.Bundle;
|
||||||
import org.hl7.fhir.r4.model.CodeableConcept;
|
import org.hl7.fhir.r4.model.CodeableConcept;
|
||||||
import org.hl7.fhir.r4.model.Coding;
|
import org.hl7.fhir.r4.model.Coding;
|
||||||
import org.hl7.fhir.r4.model.Condition;
|
import org.hl7.fhir.r4.model.Condition;
|
||||||
|
@ -46,19 +54,24 @@ import org.hl7.fhir.r4.model.DateType;
|
||||||
import org.hl7.fhir.r4.model.DecimalType;
|
import org.hl7.fhir.r4.model.DecimalType;
|
||||||
import org.hl7.fhir.r4.model.Encounter;
|
import org.hl7.fhir.r4.model.Encounter;
|
||||||
import org.hl7.fhir.r4.model.Enumerations;
|
import org.hl7.fhir.r4.model.Enumerations;
|
||||||
|
import org.hl7.fhir.r4.model.HealthcareService;
|
||||||
import org.hl7.fhir.r4.model.IdType;
|
import org.hl7.fhir.r4.model.IdType;
|
||||||
|
import org.hl7.fhir.r4.model.InsurancePlan;
|
||||||
import org.hl7.fhir.r4.model.Location;
|
import org.hl7.fhir.r4.model.Location;
|
||||||
import org.hl7.fhir.r4.model.MedicationAdministration;
|
import org.hl7.fhir.r4.model.MedicationAdministration;
|
||||||
import org.hl7.fhir.r4.model.MolecularSequence;
|
import org.hl7.fhir.r4.model.MolecularSequence;
|
||||||
import org.hl7.fhir.r4.model.Observation;
|
import org.hl7.fhir.r4.model.Observation;
|
||||||
import org.hl7.fhir.r4.model.Organization;
|
import org.hl7.fhir.r4.model.Organization;
|
||||||
|
import org.hl7.fhir.r4.model.OrganizationAffiliation;
|
||||||
import org.hl7.fhir.r4.model.Patient;
|
import org.hl7.fhir.r4.model.Patient;
|
||||||
import org.hl7.fhir.r4.model.Practitioner;
|
import org.hl7.fhir.r4.model.Practitioner;
|
||||||
|
import org.hl7.fhir.r4.model.PractitionerRole;
|
||||||
import org.hl7.fhir.r4.model.Quantity;
|
import org.hl7.fhir.r4.model.Quantity;
|
||||||
import org.hl7.fhir.r4.model.Range;
|
import org.hl7.fhir.r4.model.Range;
|
||||||
import org.hl7.fhir.r4.model.Reference;
|
import org.hl7.fhir.r4.model.Reference;
|
||||||
import org.hl7.fhir.r4.model.Resource;
|
import org.hl7.fhir.r4.model.Resource;
|
||||||
import org.hl7.fhir.r4.model.RiskAssessment;
|
import org.hl7.fhir.r4.model.RiskAssessment;
|
||||||
|
import org.hl7.fhir.r4.model.SearchParameter;
|
||||||
import org.hl7.fhir.r4.model.SimpleQuantity;
|
import org.hl7.fhir.r4.model.SimpleQuantity;
|
||||||
import org.hl7.fhir.r4.model.StringType;
|
import org.hl7.fhir.r4.model.StringType;
|
||||||
import org.hl7.fhir.r4.model.Subscription;
|
import org.hl7.fhir.r4.model.Subscription;
|
||||||
|
@ -67,16 +80,23 @@ import org.hl7.fhir.r4.model.ValueSet;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.CsvSource;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.test.context.ContextConfiguration;
|
import org.springframework.test.context.ContextConfiguration;
|
||||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileReader;
|
||||||
|
import java.io.IOException;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
@ -365,6 +385,100 @@ public class InMemorySubscriptionMatcherR4Test {
|
||||||
assertUnsupported(patient, params);
|
assertUnsupported(patient, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvSource({
|
||||||
|
"true, http://profile1",
|
||||||
|
"true, http://profile2",
|
||||||
|
"false, http://profile99"
|
||||||
|
})
|
||||||
|
public void testSearchMetaProfile(boolean theExpectMatched, String theProfile) {
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.getMeta().addProfile("http://profile1");
|
||||||
|
patient.getMeta().addProfile("http://profile2");
|
||||||
|
patient.getMeta().addProfile(null);
|
||||||
|
|
||||||
|
SearchParameterMap params = new SearchParameterMap();
|
||||||
|
params.add(Constants.PARAM_PROFILE, new UriParam(theProfile));
|
||||||
|
if (theExpectMatched) {
|
||||||
|
assertMatched(patient, params);
|
||||||
|
} else {
|
||||||
|
assertNotMatched(patient, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvSource({
|
||||||
|
"true, http://system1, value1",
|
||||||
|
"true, , value1",
|
||||||
|
"true, http://system1, ",
|
||||||
|
"false, http://system1, value2",
|
||||||
|
"false, , value99",
|
||||||
|
"true, , ",
|
||||||
|
})
|
||||||
|
public void testSearchMetaTag(boolean theExpectMatched, String theSystem, String theValue) {
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.getMeta().addTag("http://system1", "value1", "display1");
|
||||||
|
patient.getMeta().addTag("http://system2", "value2", "display2");
|
||||||
|
patient.getMeta().addTag(null, null, "display3");
|
||||||
|
|
||||||
|
SearchParameterMap params = new SearchParameterMap();
|
||||||
|
params.add(Constants.PARAM_TAG, new TokenParam(theSystem, theValue));
|
||||||
|
if (theExpectMatched) {
|
||||||
|
assertMatched(patient, params);
|
||||||
|
} else {
|
||||||
|
assertNotMatched(patient, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvSource({
|
||||||
|
"false, http://system1, value1",
|
||||||
|
"false, , value1",
|
||||||
|
"false, http://system1, ",
|
||||||
|
"true, http://system1, value2",
|
||||||
|
"true, , value99",
|
||||||
|
"true, , ",
|
||||||
|
})
|
||||||
|
public void testSearchMetaTagNot(boolean theExpectMatched, String theSystem, String theValue) {
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.getMeta().addTag("http://system1", "value1", "display1");
|
||||||
|
patient.getMeta().addTag("http://system2", "value2", "display2");
|
||||||
|
patient.getMeta().addTag(null, null, "display3");
|
||||||
|
|
||||||
|
SearchParameterMap params = new SearchParameterMap();
|
||||||
|
params.add(Constants.PARAM_TAG, new TokenParam(theSystem, theValue).setModifier(TokenParamModifier.NOT));
|
||||||
|
if (theExpectMatched) {
|
||||||
|
assertMatched(patient, params);
|
||||||
|
} else {
|
||||||
|
assertNotMatched(patient, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvSource({
|
||||||
|
"true, http://system1, value1",
|
||||||
|
"true, , value1",
|
||||||
|
"true, http://system1, ",
|
||||||
|
"false, http://system1, value2",
|
||||||
|
"false, , value99",
|
||||||
|
"true, , ",
|
||||||
|
})
|
||||||
|
public void testSearchMetaSecurity(boolean theExpectMatched, String theSystem, String theValue) {
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.getMeta().addSecurity("http://system1", "value1", "display1");
|
||||||
|
patient.getMeta().addSecurity("http://system2", "value2", "display2");
|
||||||
|
patient.getMeta().addSecurity(null, null, "display3");
|
||||||
|
|
||||||
|
SearchParameterMap params = new SearchParameterMap();
|
||||||
|
params.add(Constants.PARAM_SECURITY, new TokenParam(theSystem, theValue));
|
||||||
|
if (theExpectMatched) {
|
||||||
|
assertMatched(patient, params);
|
||||||
|
} else {
|
||||||
|
assertNotMatched(patient, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSearchNameParam() {
|
public void testSearchNameParam() {
|
||||||
Patient patient = new Patient();
|
Patient patient = new Patient();
|
||||||
|
@ -930,41 +1044,6 @@ public class InMemorySubscriptionMatcherR4Test {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSearchWithSecurityAndProfileParamsUnsupported() {
|
|
||||||
String methodName = "testSearchWithSecurityAndProfileParams";
|
|
||||||
|
|
||||||
Organization org = new Organization();
|
|
||||||
org.getNameElement().setValue("FOO");
|
|
||||||
org.getMeta().addSecurity("urn:taglist", methodName + "1a", null);
|
|
||||||
{
|
|
||||||
SearchParameterMap params = new SearchParameterMap();
|
|
||||||
params.add("_security", new TokenParam("urn:taglist", methodName + "1a"));
|
|
||||||
assertUnsupported(org, params);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
SearchParameterMap params = new SearchParameterMap();
|
|
||||||
params.add("_profile", new UriParam("http://" + methodName));
|
|
||||||
assertUnsupported(org, params);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSearchWithTagParameterUnsupported() {
|
|
||||||
String methodName = "testSearchWithTagParameter";
|
|
||||||
|
|
||||||
Organization org = new Organization();
|
|
||||||
org.getNameElement().setValue("FOO");
|
|
||||||
org.getMeta().addTag("urn:taglist", methodName + "1a", null);
|
|
||||||
org.getMeta().addTag("urn:taglist", methodName + "1b", null);
|
|
||||||
|
|
||||||
{
|
|
||||||
// One tag
|
|
||||||
SearchParameterMap params = new SearchParameterMap();
|
|
||||||
params.add("_tag", new TokenParam("urn:taglist", methodName + "1a"));
|
|
||||||
assertUnsupported(org, params);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSearchWithVeryLongUrlLonger() {
|
public void testSearchWithVeryLongUrlLonger() {
|
||||||
|
@ -1070,4 +1149,34 @@ public class InMemorySubscriptionMatcherR4Test {
|
||||||
map.add(Patient.SP_NAME, or);
|
map.add(Patient.SP_NAME, or);
|
||||||
assertMatched(p, map);
|
assertMatched(p, map);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IFhirResourceDao<SearchParameter> mySearchParameterDao;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMatchCustomSearchParameter() {
|
||||||
|
SearchParameter sp = new SearchParameter();
|
||||||
|
sp.setId("display-contracted");
|
||||||
|
sp.setName("display-contracted");
|
||||||
|
sp.setCode("display-contracted");
|
||||||
|
sp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE);
|
||||||
|
sp.addBase("PractitionerRole");
|
||||||
|
sp.setType(Enumerations.SearchParamType.TOKEN);
|
||||||
|
sp.setExpression("PractitionerRole.active");
|
||||||
|
sp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
|
||||||
|
mySearchParameterDao.update(sp, new SystemRequestDetails());
|
||||||
|
|
||||||
|
PractitionerRole pr = new PractitionerRole();
|
||||||
|
pr.setActive(true);
|
||||||
|
|
||||||
|
SearchParameterMap params;
|
||||||
|
|
||||||
|
params = SearchParameterMap.newSynchronous("display-contracted", new TokenParam("true"));
|
||||||
|
assertMatched(pr, params);
|
||||||
|
|
||||||
|
params = SearchParameterMap.newSynchronous("display-contracted", new TokenParam("false"));
|
||||||
|
assertNotMatched(pr, params);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - Clinical Reasoning
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2023 Smile CDR, Inc.
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
package ca.uhn.fhir.cr.constant;
|
package ca.uhn.fhir.cr.constant;
|
||||||
|
|
||||||
public class CareCapsConstants {
|
public class CareCapsConstants {
|
||||||
|
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - Clinical Reasoning
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2023 Smile CDR, Inc.
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
package ca.uhn.fhir.cr.constant;
|
package ca.uhn.fhir.cr.constant;
|
||||||
|
|
||||||
public class HtmlConstants {
|
public class HtmlConstants {
|
||||||
|
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - Clinical Reasoning
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2023 Smile CDR, Inc.
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
package ca.uhn.fhir.cr.enumeration;
|
package ca.uhn.fhir.cr.enumeration;
|
||||||
|
|
||||||
import ca.uhn.fhir.i18n.Msg;
|
import ca.uhn.fhir.i18n.Msg;
|
||||||
|
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - Clinical Reasoning
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2023 Smile CDR, Inc.
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
package ca.uhn.fhir.cr.r4.measure;
|
package ca.uhn.fhir.cr.r4.measure;
|
||||||
|
|
||||||
import ca.uhn.fhir.model.api.annotation.Description;
|
import ca.uhn.fhir.model.api.annotation.Description;
|
||||||
|
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - Clinical Reasoning
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2023 Smile CDR, Inc.
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
package ca.uhn.fhir.cr.r4.measure;
|
package ca.uhn.fhir.cr.r4.measure;
|
||||||
|
|
||||||
import ca.uhn.fhir.cr.enumeration.CareGapsStatusCode;
|
import ca.uhn.fhir.cr.enumeration.CareGapsStatusCode;
|
||||||
|
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - Clinical Reasoning
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2023 Smile CDR, Inc.
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
package ca.uhn.fhir.cr.r4.measure;
|
package ca.uhn.fhir.cr.r4.measure;
|
||||||
|
|
||||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
|
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - Clinical Reasoning
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2023 Smile CDR, Inc.
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
package ca.uhn.fhir.cr.r4.measure;
|
package ca.uhn.fhir.cr.r4.measure;
|
||||||
|
|
||||||
import ca.uhn.fhir.model.api.annotation.Description;
|
import ca.uhn.fhir.model.api.annotation.Description;
|
||||||
|
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - Clinical Reasoning
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2023 Smile CDR, Inc.
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
package ca.uhn.fhir.cr.r4.measure;
|
package ca.uhn.fhir.cr.r4.measure;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||||
|
|
|
@ -1,3 +1,22 @@
|
||||||
|
/*-
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR Storage api
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2023 Smile CDR, Inc.
|
||||||
|
* %%
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
* #L%
|
||||||
|
*/
|
||||||
package ca.uhn.fhir.cache;
|
package ca.uhn.fhir.cache;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||||
|
|
|
@ -1581,7 +1581,7 @@ public abstract class BaseTransactionProcessor {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (myInMemoryResourceMatcher.match(matchUrl, resource, indexedParams).matched()) {
|
if (myInMemoryResourceMatcher.match(matchUrl, resource, indexedParams, theRequest).matched()) {
|
||||||
counter++;
|
counter++;
|
||||||
if (counter > 1) {
|
if (counter > 1) {
|
||||||
throw new InvalidRequestException(Msg.code(542) + "Unable to process " + theActionName + " - Request would cause multiple resources to match URL: \"" + matchUrl + "\". Does transaction request contain duplicates?");
|
throw new InvalidRequestException(Msg.code(542) + "Unable to process " + theActionName + " - Request would cause multiple resources to match URL: \"" + matchUrl + "\". Does transaction request contain duplicates?");
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
*/
|
*/
|
||||||
package ca.uhn.fhir.jpa.dao;
|
package ca.uhn.fhir.jpa.dao;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
|
|
||||||
import ca.uhn.fhir.jpa.api.dao.IDao;
|
import ca.uhn.fhir.jpa.api.dao.IDao;
|
||||||
import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId;
|
import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
@ -30,8 +29,6 @@ public class SearchBuilderFactory<T extends IResourcePersistentId<?>> {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ApplicationContext myApplicationContext;
|
private ApplicationContext myApplicationContext;
|
||||||
@Autowired
|
|
||||||
private JpaStorageSettings myStorageSettings;
|
|
||||||
|
|
||||||
public ISearchBuilder<T> newSearchBuilder(IDao theDao, String theResourceName, Class<? extends IBaseResource> theResourceType) {
|
public ISearchBuilder<T> newSearchBuilder(IDao theDao, String theResourceName, Class<? extends IBaseResource> theResourceType) {
|
||||||
return (ISearchBuilder<T>) myApplicationContext.getBean(ISearchBuilder.SEARCH_BUILDER_BEAN_NAME, theDao, theResourceName, theResourceType);
|
return (ISearchBuilder<T>) myApplicationContext.getBean(ISearchBuilder.SEARCH_BUILDER_BEAN_NAME, theDao, theResourceName, theResourceType);
|
||||||
|
|
|
@ -24,11 +24,12 @@ import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public interface ISubscriptionTriggeringSvc {
|
public interface ISubscriptionTriggeringSvc {
|
||||||
|
|
||||||
IBaseParameters triggerSubscription(List<IPrimitiveType<String>> theResourceIds, List<IPrimitiveType<String>> theSearchUrls, @IdParam IIdType theSubscriptionId);
|
IBaseParameters triggerSubscription(@Nullable List<IPrimitiveType<String>> theResourceIds, @Nullable List<IPrimitiveType<String>> theSearchUrls, @Nullable IIdType theSubscriptionId);
|
||||||
|
|
||||||
void runDeliveryPass();
|
void runDeliveryPass();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue