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.servlet.ServletRequestDetails;
|
||||
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.HapiExtensions;
|
||||
import ca.uhn.fhir.util.MetaUtil;
|
||||
|
@ -160,7 +159,6 @@ import java.util.HashMap;
|
|||
import java.util.HashSet;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -914,9 +912,9 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
|||
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
|
||||
InMemoryMatchResult outcome = myInMemoryResourceMatcher.match(theIfNoneExist, theResource, theParams);
|
||||
InMemoryMatchResult outcome = myInMemoryResourceMatcher.match(theIfNoneExist, theResource, theParams, theRequestDetails);
|
||||
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.");
|
||||
}
|
||||
|
@ -1032,7 +1030,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
|||
// matches. We could certainly make this configurable though in the
|
||||
// future.
|
||||
if (entity.getVersion() <= 1L && entity.getCreatedByMatchUrl() != null && thePerformIndexing) {
|
||||
verifyMatchUrlForConditionalCreate(theResource, entity.getCreatedByMatchUrl(), entity, newParams);
|
||||
verifyMatchUrlForConditionalCreate(theResource, entity.getCreatedByMatchUrl(), newParams, theRequest);
|
||||
}
|
||||
|
||||
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.LESSTHAN_OR_EQUALS;
|
||||
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.isNotBlank;
|
||||
|
||||
|
@ -487,6 +488,7 @@ public class SearchParameterMap implements Serializable {
|
|||
b.append(',');
|
||||
}
|
||||
String valueAsQueryToken = nextValueOr.getValueAsQueryToken(theCtx);
|
||||
valueAsQueryToken = defaultString(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.ValidationSupportContext;
|
||||
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.StorageSettings;
|
||||
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
|
||||
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.SearchParamExtractorService;
|
||||
import ca.uhn.fhir.jpa.searchparam.util.SourceParam;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
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.ParamPrefixEnum;
|
||||
import ca.uhn.fhir.rest.param.ReferenceParam;
|
||||
import ca.uhn.fhir.rest.param.StringParam;
|
||||
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.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
|
||||
import ca.uhn.fhir.util.MetaUtil;
|
||||
|
@ -50,39 +54,47 @@ import com.google.common.collect.Sets;
|
|||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.dstu3.model.Location;
|
||||
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.IIdType;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
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 {
|
||||
|
||||
private enum ValidationSupportInitializationState {NOT_INITIALIZED, INITIALIZED, FAILED}
|
||||
|
||||
public static final Set<String> UNSUPPORTED_PARAMETER_NAMES = Sets.newHashSet(Constants.PARAM_HAS, Constants.PARAM_TAG, Constants.PARAM_PROFILE, Constants.PARAM_SECURITY);
|
||||
public static final Set<String> UNSUPPORTED_PARAMETER_NAMES = Sets.newHashSet(Constants.PARAM_HAS);
|
||||
private static final org.slf4j.Logger ourLog = LoggerFactory.getLogger(InMemoryResourceMatcher.class);
|
||||
@Autowired
|
||||
ApplicationContext myApplicationContext;
|
||||
@Autowired
|
||||
private MatchUrlService myMatchUrlService;
|
||||
@Autowired
|
||||
ISearchParamRegistry mySearchParamRegistry;
|
||||
@Autowired
|
||||
StorageSettings myStorageSettings;
|
||||
@Autowired
|
||||
FhirContext myFhirContext;
|
||||
|
||||
@Autowired
|
||||
SearchParamExtractorService mySearchParamExtractorService;
|
||||
@Autowired
|
||||
IndexedSearchParamExtractor myIndexedSearchParamExtractor;
|
||||
@Autowired
|
||||
private MatchUrlService myMatchUrlService;
|
||||
private ValidationSupportInitializationState validationSupportState = ValidationSupportInitializationState.NOT_INITIALIZED;
|
||||
private IValidationSupport myValidationSupport = null;
|
||||
|
||||
public InMemoryResourceMatcher() {}
|
||||
public InMemoryResourceMatcher() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazy loads a {@link IValidationSupport} implementation just-in-time.
|
||||
|
@ -105,6 +117,15 @@ public class InMemoryResourceMatcher {
|
|||
return myValidationSupport;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #match(String, IBaseResource, ResourceIndexedSearchParams, RequestDetails)}
|
||||
*/
|
||||
@Deprecated
|
||||
public InMemoryMatchResult match(String theCriteria, IBaseResource theResource, @Nullable ResourceIndexedSearchParams theIndexedSearchParams) {
|
||||
return match(theCriteria, theResource, theIndexedSearchParams, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
@ -112,9 +133,15 @@ public class InMemoryResourceMatcher {
|
|||
* 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, ResourceIndexedSearchParams theSearchParams) {
|
||||
public InMemoryMatchResult match(String theCriteria, IBaseResource theResource, @Nullable ResourceIndexedSearchParams theIndexedSearchParams, RequestDetails theRequestDetails) {
|
||||
RuntimeResourceDefinition resourceDefinition;
|
||||
if (theResource == null) {
|
||||
Validate.isTrue(!theCriteria.startsWith("?"), "Invalid match URL format (must match \"[resourceType]?[params]\")");
|
||||
|
@ -129,25 +156,32 @@ public class InMemoryResourceMatcher {
|
|||
} catch (UnsupportedOperationException e) {
|
||||
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();
|
||||
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
|
||||
* @return result.supported() will be true if theCriteria can be evaluated in-memory
|
||||
*/
|
||||
public InMemoryMatchResult canBeEvaluatedInMemory(String theCriteria) {
|
||||
return match(theCriteria, null, null);
|
||||
return match(theCriteria, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param theSearchParameterMap
|
||||
* @param theResourceDefinition
|
||||
* @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);
|
||||
}
|
||||
|
||||
|
||||
@Nonnull
|
||||
public InMemoryMatchResult match(SearchParameterMap theSearchParameterMap, IBaseResource theResource, RuntimeResourceDefinition theResourceDefinition, ResourceIndexedSearchParams theSearchParams) {
|
||||
if (theSearchParameterMap.getLastUpdated() != null) {
|
||||
|
@ -195,6 +228,12 @@ public class InMemoryResourceMatcher {
|
|||
return InMemoryMatchResult.fromBoolean(matchIdsAndOr(theAndOrParams, theResource));
|
||||
case Constants.PARAM_SOURCE:
|
||||
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:
|
||||
return matchResourceParam(myStorageSettings, theParamName, theAndOrParams, theSearchParams, resourceName, paramDef);
|
||||
}
|
||||
|
@ -239,6 +278,30 @@ public class InMemoryResourceMatcher {
|
|||
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) {
|
||||
if (theResource == null) {
|
||||
return true;
|
||||
|
@ -263,6 +326,54 @@ public class InMemoryResourceMatcher {
|
|||
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) {
|
||||
if (theResource == null) {
|
||||
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) {
|
||||
if (theParamDef.getParamType().equals(RestSearchParameterTypeEnum.TOKEN)) {
|
||||
TokenParam tokenParam = (TokenParam) theOrList.get(0);
|
||||
|
@ -344,6 +457,7 @@ public class InMemoryResourceMatcher {
|
|||
* The :not modifier is supported.
|
||||
* 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.
|
||||
*
|
||||
* @param theStorageSettings a model configuration
|
||||
* @param theResourceName the name of the resource type being matched
|
||||
* @param theParamName the name of the parameter
|
||||
|
@ -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.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.SearchParamExtractorService;
|
||||
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.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class IndexedSearchParamExtractor {
|
||||
@Autowired
|
||||
private FhirContext myContext;
|
||||
@Autowired
|
||||
private SearchParamExtractorService mySearchParamExtractorService;
|
||||
|
||||
@Nonnull
|
||||
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();
|
||||
TransactionDetails transactionDetails = new TransactionDetails();
|
||||
String resourceType = myContext.getResourceType(theResource);
|
||||
entity.setResourceType(resourceType);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,8 +38,7 @@ public class SearchParamMatcher {
|
|||
private InMemoryResourceMatcher myInMemoryResourceMatcher;
|
||||
|
||||
public InMemoryMatchResult match(String theCriteria, IBaseResource theResource, RequestDetails theRequest) {
|
||||
ResourceIndexedSearchParams resourceIndexedSearchParams = myIndexedSearchParamExtractor.extractIndexedSearchParams(theResource, theRequest);
|
||||
return myInMemoryResourceMatcher.match(theCriteria, theResource, resourceIndexedSearchParams);
|
||||
return myInMemoryResourceMatcher.match(theCriteria, theResource, null, theRequest);
|
||||
}
|
||||
|
||||
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.searchparam.MatchUrlService;
|
||||
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.param.TokenParamModifier;
|
||||
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.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.assertFalse;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
@ -47,6 +49,10 @@ public class InMemoryResourceMatcherConfigurationR5Test {
|
|||
ISearchParamRegistry mySearchParamRegistry;
|
||||
@Autowired
|
||||
private InMemoryResourceMatcher myInMemoryResourceMatcher;
|
||||
@MockBean
|
||||
private SearchParamExtractorService mySearchParamExtractorService;
|
||||
@MockBean
|
||||
private IndexedSearchParamExtractor myIndexedSearchParamExtractor;
|
||||
private Observation myObservation;
|
||||
private ResourceIndexedSearchParams mySearchParams;
|
||||
|
||||
|
@ -71,7 +77,7 @@ public class InMemoryResourceMatcherConfigurationR5Test {
|
|||
myInMemoryResourceMatcher.myApplicationContext = applicationContext;
|
||||
|
||||
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);
|
||||
|
@ -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
|
||||
*/
|
||||
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());
|
||||
assertEquals("Parameter: <code:in> Reason: Qualified parameter not supported", result.getUnsupportedReason());
|
||||
}
|
||||
|
@ -91,7 +97,7 @@ public class InMemoryResourceMatcherConfigurationR5Test {
|
|||
@Test
|
||||
@Order(3)
|
||||
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());
|
||||
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.searchparam.MatchUrlService;
|
||||
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.rest.api.Constants;
|
||||
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.TokenParamModifier;
|
||||
import ca.uhn.fhir.rest.server.util.ISearchParamRegistry;
|
||||
|
@ -63,6 +66,10 @@ public class InMemoryResourceMatcherR5Test {
|
|||
ISearchParamRegistry mySearchParamRegistry;
|
||||
@MockBean
|
||||
IValidationSupport myValidationSupport;
|
||||
@MockBean
|
||||
SearchParamExtractorService mySearchParamExtractorService;
|
||||
@MockBean
|
||||
IndexedSearchParamExtractor myIndexedSearchParamExtractor;
|
||||
@Autowired
|
||||
private InMemoryResourceMatcher myInMemoryResourceMatcher;
|
||||
private Observation myObservation;
|
||||
|
@ -92,47 +99,51 @@ public class InMemoryResourceMatcherR5Test {
|
|||
@Test
|
||||
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());
|
||||
}
|
||||
{
|
||||
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());
|
||||
}
|
||||
{
|
||||
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());
|
||||
}
|
||||
{
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
static RequestDetails newRequest() {
|
||||
return new SystemRequestDetails();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSupportedSource_ResourceWithNoSourceValue() {
|
||||
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());
|
||||
}
|
||||
{
|
||||
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());
|
||||
}
|
||||
{
|
||||
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());
|
||||
}
|
||||
{
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
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());
|
||||
assertEquals("Parameter: <encounter.class> Reason: Chained parameters are not supported", result.getUnsupportedReason());
|
||||
}
|
||||
|
@ -140,11 +151,11 @@ public class InMemoryResourceMatcherR5Test {
|
|||
@Test
|
||||
public void testSupportedNot() {
|
||||
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());
|
||||
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.matched(), ":not matches when NONE match");
|
||||
}
|
||||
|
@ -154,7 +165,7 @@ public class InMemoryResourceMatcherR5Test {
|
|||
IValidationSupport.CodeValidationResult codeValidationResult = new IValidationSupport.CodeValidationResult().setCode(OBSERVATION_CODE);
|
||||
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.matched());
|
||||
|
||||
|
@ -166,7 +177,7 @@ public class InMemoryResourceMatcherR5Test {
|
|||
IValidationSupport.CodeValidationResult codeValidationResult = new IValidationSupport.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());
|
||||
assertFalse(result.matched());
|
||||
|
||||
|
@ -178,7 +189,7 @@ public class InMemoryResourceMatcherR5Test {
|
|||
IValidationSupport.CodeValidationResult codeValidationResult = new IValidationSupport.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.matched());
|
||||
|
||||
|
@ -198,7 +209,7 @@ public class InMemoryResourceMatcherR5Test {
|
|||
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;
|
||||
InMemoryMatchResult result = myInMemoryResourceMatcher.match(criteria, myObservation, mySearchParams);
|
||||
InMemoryMatchResult result = myInMemoryResourceMatcher.match(criteria, myObservation, mySearchParams, newRequest());
|
||||
assertTrue(result.supported());
|
||||
assertFalse(result.matched(), ":not-in matches when NONE of the OR-list match");
|
||||
|
||||
|
@ -208,7 +219,7 @@ public class InMemoryResourceMatcherR5Test {
|
|||
@Test
|
||||
public void testUnrecognizedParam() {
|
||||
try {
|
||||
InMemoryMatchResult result = myInMemoryResourceMatcher.match("foo=bar", myObservation, mySearchParams);
|
||||
InMemoryMatchResult result = myInMemoryResourceMatcher.match("foo=bar", myObservation, mySearchParams, newRequest());
|
||||
} catch (MatchUrlService.UnrecognizedSearchParameterException e) {
|
||||
// expected
|
||||
}
|
||||
|
@ -223,7 +234,7 @@ public class InMemoryResourceMatcherR5Test {
|
|||
}
|
||||
|
||||
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());
|
||||
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();
|
||||
{
|
||||
InMemoryMatchResult result = myInMemoryResourceMatcher.match(equation + earlyDate, myObservation, mySearchParams);
|
||||
InMemoryMatchResult result = myInMemoryResourceMatcher.match(equation + earlyDate, myObservation, mySearchParams, newRequest());
|
||||
assertTrue(result.supported(), result.getUnsupportedReason());
|
||||
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());
|
||||
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());
|
||||
assertEquals(result.matched(), theLater);
|
||||
}
|
||||
|
@ -276,7 +287,7 @@ public class InMemoryResourceMatcherR5Test {
|
|||
|
||||
@Test
|
||||
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.matched());
|
||||
}
|
||||
|
@ -288,7 +299,7 @@ public class InMemoryResourceMatcherR5Test {
|
|||
futureObservation.setEffective(new DateTimeType(Date.from(nextWeek)));
|
||||
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.matched());
|
||||
}
|
||||
|
@ -302,7 +313,7 @@ public class InMemoryResourceMatcherR5Test {
|
|||
futureObservation.setEffective(new DateTimeType(Date.from(nextMinute)));
|
||||
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());
|
||||
assertEquals(1, searchParams.myDateParams.size());
|
||||
ResourceIndexedSearchParamDate searchParamDate = searchParams.myDateParams.iterator().next();
|
||||
|
@ -313,7 +324,7 @@ public class InMemoryResourceMatcherR5Test {
|
|||
|
||||
@Test
|
||||
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.matched());
|
||||
}
|
||||
|
@ -325,7 +336,7 @@ public class InMemoryResourceMatcherR5Test {
|
|||
futureObservation.setEffective(new DateTimeType(Date.from(nextWeek)));
|
||||
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.matched());
|
||||
}
|
||||
|
@ -337,7 +348,7 @@ public class InMemoryResourceMatcherR5Test {
|
|||
futureObservation.setEffective(new DateTimeType(Date.from(nextWeek)));
|
||||
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());
|
||||
assertFalse(result.matched());
|
||||
}
|
||||
|
@ -354,11 +365,11 @@ public class InMemoryResourceMatcherR5Test {
|
|||
|
||||
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.matched());
|
||||
|
||||
InMemoryMatchResult resultOutsidePeriod = myInMemoryResourceMatcher.match(search, outsidePeriodObservation, outsidePeriodSearchParams);
|
||||
InMemoryMatchResult resultOutsidePeriod = myInMemoryResourceMatcher.match(search, outsidePeriodObservation, outsidePeriodSearchParams, newRequest());
|
||||
assertTrue(resultOutsidePeriod.supported(), resultOutsidePeriod.getUnsupportedReason());
|
||||
assertFalse(resultOutsidePeriod.matched());
|
||||
}
|
||||
|
|
|
@ -4,19 +4,15 @@ import ca.uhn.fhir.context.FhirContext;
|
|||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.interceptor.api.IInterceptorService;
|
||||
import ca.uhn.fhir.jpa.cache.IResourceChangeListenerRegistry;
|
||||
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.*;
|
||||
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.StorageSettings;
|
||||
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.InMemoryResourceMatcher;
|
||||
import ca.uhn.fhir.jpa.searchparam.matcher.IndexedSearchParamExtractor;
|
||||
import ca.uhn.fhir.jpa.searchparam.matcher.SearchParamMatcher;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
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 org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
import org.hl7.fhir.r4.model.Enumerations;
|
||||
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.hl7.fhir.r4.model.*;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
@ -46,36 +38,26 @@ import org.testcontainers.shaded.com.google.common.collect.Sets;
|
|||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.empty;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
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.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
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;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(SpringExtension.class)
|
||||
public class SearchParamRegistryImplTest {
|
||||
public static final int TEST_SEARCH_PARAMS = 3;
|
||||
private static final FhirContext ourFhirContext = FhirContext.forR4();
|
||||
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 ResourceVersionMap ourResourceVersionMap;
|
||||
private static int ourLastId;
|
||||
private static final int ourBuiltinPatientSearchParamCount;
|
||||
private static int ourLastId;
|
||||
|
||||
static {
|
||||
ourEntities = new ArrayList<>();
|
||||
|
@ -90,8 +72,6 @@ public class SearchParamRegistryImplTest {
|
|||
SearchParamRegistryImpl mySearchParamRegistry;
|
||||
@Autowired
|
||||
private ResourceChangeListenerRegistryImpl myResourceChangeListenerRegistry;
|
||||
@Autowired
|
||||
private ResourceChangeListenerCacheRefresherImpl myChangeListenerCacheRefresher;
|
||||
|
||||
@MockBean
|
||||
private IResourceVersionSvc myResourceVersionSvc;
|
||||
|
@ -103,55 +83,11 @@ public class SearchParamRegistryImplTest {
|
|||
private SearchParamMatcher mySearchParamMatcher;
|
||||
@MockBean
|
||||
private MatchUrlService myMatchUrlService;
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
}
|
||||
@MockBean
|
||||
private SearchParamExtractorService mySearchParamExtractorService;
|
||||
@MockBean
|
||||
private IndexedSearchParamExtractor myIndexedSearchParamExtractor;
|
||||
private int myAnswerCount = 0;
|
||||
|
||||
@Nonnull
|
||||
private static ResourceTable createEntity(long theId, int theVersion) {
|
||||
|
@ -162,8 +98,6 @@ public class SearchParamRegistryImplTest {
|
|||
return searchParamEntity;
|
||||
}
|
||||
|
||||
private int myAnswerCount = 0;
|
||||
|
||||
@BeforeEach
|
||||
public void before() {
|
||||
myAnswerCount = 0;
|
||||
|
@ -408,4 +342,53 @@ public class SearchParamRegistryImplTest {
|
|||
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;
|
||||
|
||||
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.svc.ISearchCoordinatorSvc;
|
||||
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.model.sched.HapiJob;
|
||||
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.model.ResourceModifiedMessage;
|
||||
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.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
|
||||
|
@ -65,6 +66,7 @@ import org.slf4j.Logger;
|
|||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
@ -110,9 +112,11 @@ public class SubscriptionTriggeringSvcImpl implements ISubscriptionTriggeringSvc
|
|||
|
||||
@Autowired
|
||||
private ISearchSvc mySearchService;
|
||||
@Autowired
|
||||
private SearchBuilderFactory mySearchBuilderFactory;
|
||||
|
||||
@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()) {
|
||||
throw new PreconditionFailedException(Msg.code(22) + "Subscription processing not active on this server");
|
||||
|
@ -339,6 +343,8 @@ public class SubscriptionTriggeringSvcImpl implements ISubscriptionTriggeringSvc
|
|||
}
|
||||
|
||||
ourLog.info("Triggering job[{}] search {} requesting resources {} - {}", theJobDetails.getJobId(), theJobDetails.getCurrentSearchUuid(), fromIndex, toIndex);
|
||||
|
||||
|
||||
List<IResourcePersistentId<?>> resourceIds;
|
||||
RequestPartitionId requestPartitionId = RequestPartitionId.allPartitions();
|
||||
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());
|
||||
int highestIndexSubmitted = theJobDetails.getCurrentSearchLastUploadedIndex();
|
||||
|
||||
for (IResourcePersistentId next : resourceIds) {
|
||||
IBaseResource nextResource = resourceDao.readByPid(next);
|
||||
String resourceType = myFhirContext.getResourceType(theJobDetails.getCurrentSearchResourceType());
|
||||
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);
|
||||
futures.add(Pair.of(nextResource.getIdElement().getIdPart(), future));
|
||||
totalSubmitted++;
|
||||
|
|
|
@ -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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||
|
|
|
@ -766,8 +766,8 @@ public class FhirResourceDaoR4ComboUniqueParamIT extends BaseComboParamsR4Test {
|
|||
try {
|
||||
myPatientDao.create(pt1).getId().toUnqualifiedVersionless();
|
||||
fail();
|
||||
} catch (PreconditionFailedException 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());
|
||||
} catch (ResourceVersionConflictException e) {
|
||||
assertThat(e.getMessage(), containsString("new unique index created by SearchParameter/patient-gender-birthdate"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -786,7 +786,7 @@ public class FhirResourceDaoR4ComboUniqueParamIT extends BaseComboParamsR4Test {
|
|||
try {
|
||||
myPatientDao.create(pt);
|
||||
fail();
|
||||
} catch (PreconditionFailedException e) {
|
||||
} catch (ResourceVersionConflictException e) {
|
||||
// good
|
||||
}
|
||||
|
||||
|
@ -1536,7 +1536,7 @@ public class FhirResourceDaoR4ComboUniqueParamIT extends BaseComboParamsR4Test {
|
|||
try {
|
||||
myPatientDao.create(pt1).getId().toUnqualifiedVersionless();
|
||||
fail();
|
||||
} catch (PreconditionFailedException e) {
|
||||
} catch (ResourceVersionConflictException e) {
|
||||
assertThat(e.getMessage(), containsString("new unique index created by SearchParameter/patient-gender-birthdate"));
|
||||
}
|
||||
|
||||
|
@ -1551,7 +1551,7 @@ public class FhirResourceDaoR4ComboUniqueParamIT extends BaseComboParamsR4Test {
|
|||
try {
|
||||
myPatientDao.update(pt2);
|
||||
fail();
|
||||
} catch (PreconditionFailedException e) {
|
||||
} catch (ResourceVersionConflictException e) {
|
||||
assertThat(e.getMessage(), containsString("new unique index created by SearchParameter/patient-gender-birthdate"));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
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.ValueSetExpansionOptions;
|
||||
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.search.PersistedJpaSearchFirstPageBundleProvider;
|
||||
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.util.SqlQuery;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.SortSpec;
|
||||
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.interceptor.auth.AuthorizationInterceptor;
|
||||
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 org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
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.ServiceRequest;
|
||||
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.BooleanType;
|
||||
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.BeforeEach;
|
||||
import org.junit.jupiter.api.MethodOrderer;
|
||||
import org.junit.jupiter.api.Order;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
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);
|
||||
@Autowired
|
||||
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
|
||||
public void afterResetDao() {
|
||||
|
@ -120,6 +142,7 @@ public class FhirResourceDaoR4QueryCountTest extends BaseResourceProviderR4Test
|
|||
myStorageSettings.setPopulateIdentifierInAutoCreatedPlaceholderReferenceTargets(new JpaStorageSettings().isPopulateIdentifierInAutoCreatedPlaceholderReferenceTargets());
|
||||
myStorageSettings.setResourceClientIdStrategy(new JpaStorageSettings().getResourceClientIdStrategy());
|
||||
myStorageSettings.setTagStorageMode(new JpaStorageSettings().getTagStorageMode());
|
||||
myStorageSettings.clearSupportedSubscriptionTypesForUnitTest();
|
||||
|
||||
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!
|
||||
*/
|
||||
|
|
|
@ -2,6 +2,7 @@ package ca.uhn.fhir.jpa.subscription.module.matcher;
|
|||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
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.NormalizedQuantitySearchLevel;
|
||||
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.util.CoordCalculatorTestUtil;
|
||||
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.DateParam;
|
||||
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.TokenOrListParam;
|
||||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
import ca.uhn.fhir.rest.param.TokenParamModifier;
|
||||
import ca.uhn.fhir.rest.param.UriParam;
|
||||
import ca.uhn.fhir.rest.param.UriParamQualifierEnum;
|
||||
import org.apache.commons.io.Charsets;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
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.Coding;
|
||||
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.Encounter;
|
||||
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.InsurancePlan;
|
||||
import org.hl7.fhir.r4.model.Location;
|
||||
import org.hl7.fhir.r4.model.MedicationAdministration;
|
||||
import org.hl7.fhir.r4.model.MolecularSequence;
|
||||
import org.hl7.fhir.r4.model.Observation;
|
||||
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.Practitioner;
|
||||
import org.hl7.fhir.r4.model.PractitionerRole;
|
||||
import org.hl7.fhir.r4.model.Quantity;
|
||||
import org.hl7.fhir.r4.model.Range;
|
||||
import org.hl7.fhir.r4.model.Reference;
|
||||
import org.hl7.fhir.r4.model.Resource;
|
||||
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.StringType;
|
||||
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.Test;
|
||||
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.test.context.ContextConfiguration;
|
||||
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.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
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.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
@ -365,6 +385,100 @@ public class InMemorySubscriptionMatcherR4Test {
|
|||
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
|
||||
public void testSearchNameParam() {
|
||||
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
|
||||
public void testSearchWithVeryLongUrlLonger() {
|
||||
|
@ -1070,4 +1149,34 @@ public class InMemorySubscriptionMatcherR4Test {
|
|||
map.add(Patient.SP_NAME, or);
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||
|
|
|
@ -1581,7 +1581,7 @@ public abstract class BaseTransactionProcessor {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (myInMemoryResourceMatcher.match(matchUrl, resource, indexedParams).matched()) {
|
||||
if (myInMemoryResourceMatcher.match(matchUrl, resource, indexedParams, theRequest).matched()) {
|
||||
counter++;
|
||||
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?");
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
*/
|
||||
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.rest.api.server.storage.IResourcePersistentId;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
@ -30,8 +29,6 @@ public class SearchBuilderFactory<T extends IResourcePersistentId<?>> {
|
|||
|
||||
@Autowired
|
||||
private ApplicationContext myApplicationContext;
|
||||
@Autowired
|
||||
private JpaStorageSettings myStorageSettings;
|
||||
|
||||
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);
|
||||
|
|
|
@ -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.IPrimitiveType;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
|
||||
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();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue