Subscription fhirpath criteria (#5975)
* Added fhirpath-criteria evaluation * Added test for valid fhirpath that does not evaluate to boolean * Added resolving the variables %current and %previous * Added test using only FP criteria * Added test cases for valid FhirPath expressions that return non-booleans * Added use of central cache * Added more elaborate tests for non-sunshine scenarios * Added changelog * CheckStyle'd errorcode added. * Added spotless formatting and converted FhirPathR5 expression to be Android compatible * Applied more spotless
This commit is contained in:
parent
0397b9ddc8
commit
f767058239
|
@ -24,6 +24,8 @@ import jakarta.annotation.Nullable;
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public interface IFhirPathEvaluationContext {
|
public interface IFhirPathEvaluationContext {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -36,4 +38,15 @@ public interface IFhirPathEvaluationContext {
|
||||||
default IBase resolveReference(@Nonnull IIdType theReference, @Nullable IBase theContext) {
|
default IBase resolveReference(@Nonnull IIdType theReference, @Nullable IBase theContext) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param appContext
|
||||||
|
* @param name The name of the constant(s) to be resolved
|
||||||
|
* @param beforeContext
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
default List<IBase> resolveConstant(Object appContext, String name, boolean beforeContext) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
type: add
|
||||||
|
issue: 6031
|
||||||
|
title: "Subscriptions now support the evaluation use of FhirPath criteria and the use of the variables %current and %previous. Thanks to Jens Villadsen (@jkiddo) for the contribution!"
|
|
@ -24,6 +24,7 @@ import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||||
import ca.uhn.fhir.jpa.searchparam.matcher.SearchParamMatcher;
|
import ca.uhn.fhir.jpa.searchparam.matcher.SearchParamMatcher;
|
||||||
import ca.uhn.fhir.jpa.subscription.config.SubscriptionConfig;
|
import ca.uhn.fhir.jpa.subscription.config.SubscriptionConfig;
|
||||||
import ca.uhn.fhir.jpa.subscription.submit.interceptor.SubscriptionQueryValidator;
|
import ca.uhn.fhir.jpa.subscription.submit.interceptor.SubscriptionQueryValidator;
|
||||||
|
import ca.uhn.fhir.jpa.util.MemoryCacheService;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
|
@ -33,11 +34,12 @@ import org.springframework.context.annotation.Lazy;
|
||||||
@Import(SubscriptionConfig.class)
|
@Import(SubscriptionConfig.class)
|
||||||
public class SubscriptionTopicConfig {
|
public class SubscriptionTopicConfig {
|
||||||
@Bean
|
@Bean
|
||||||
SubscriptionTopicMatchingSubscriber subscriptionTopicMatchingSubscriber(FhirContext theFhirContext) {
|
SubscriptionTopicMatchingSubscriber subscriptionTopicMatchingSubscriber(
|
||||||
|
FhirContext theFhirContext, MemoryCacheService memoryCacheService) {
|
||||||
switch (theFhirContext.getVersion().getVersion()) {
|
switch (theFhirContext.getVersion().getVersion()) {
|
||||||
case R5:
|
case R5:
|
||||||
case R4B:
|
case R4B:
|
||||||
return new SubscriptionTopicMatchingSubscriber(theFhirContext);
|
return new SubscriptionTopicMatchingSubscriber(theFhirContext, memoryCacheService);
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.topic;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.searchparam.matcher.InMemoryMatchResult;
|
import ca.uhn.fhir.jpa.searchparam.matcher.InMemoryMatchResult;
|
||||||
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage;
|
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage;
|
||||||
|
import ca.uhn.fhir.jpa.util.MemoryCacheService;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.r5.model.SubscriptionTopic;
|
import org.hl7.fhir.r5.model.SubscriptionTopic;
|
||||||
|
|
||||||
|
@ -29,10 +30,15 @@ import java.util.List;
|
||||||
public class SubscriptionTopicMatcher {
|
public class SubscriptionTopicMatcher {
|
||||||
private final SubscriptionTopicSupport mySubscriptionTopicSupport;
|
private final SubscriptionTopicSupport mySubscriptionTopicSupport;
|
||||||
private final SubscriptionTopic myTopic;
|
private final SubscriptionTopic myTopic;
|
||||||
|
private final MemoryCacheService myMemoryCacheService;
|
||||||
|
|
||||||
public SubscriptionTopicMatcher(SubscriptionTopicSupport theSubscriptionTopicSupport, SubscriptionTopic theTopic) {
|
public SubscriptionTopicMatcher(
|
||||||
|
SubscriptionTopicSupport theSubscriptionTopicSupport,
|
||||||
|
SubscriptionTopic theTopic,
|
||||||
|
MemoryCacheService memoryCacheService) {
|
||||||
mySubscriptionTopicSupport = theSubscriptionTopicSupport;
|
mySubscriptionTopicSupport = theSubscriptionTopicSupport;
|
||||||
myTopic = theTopic;
|
myTopic = theTopic;
|
||||||
|
myMemoryCacheService = memoryCacheService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InMemoryMatchResult match(ResourceModifiedMessage theMsg) {
|
public InMemoryMatchResult match(ResourceModifiedMessage theMsg) {
|
||||||
|
@ -43,7 +49,7 @@ public class SubscriptionTopicMatcher {
|
||||||
for (SubscriptionTopic.SubscriptionTopicResourceTriggerComponent next : triggers) {
|
for (SubscriptionTopic.SubscriptionTopicResourceTriggerComponent next : triggers) {
|
||||||
if (resourceName.equals(next.getResource())) {
|
if (resourceName.equals(next.getResource())) {
|
||||||
SubscriptionTriggerMatcher matcher =
|
SubscriptionTriggerMatcher matcher =
|
||||||
new SubscriptionTriggerMatcher(mySubscriptionTopicSupport, theMsg, next);
|
new SubscriptionTriggerMatcher(mySubscriptionTopicSupport, theMsg, next, myMemoryCacheService);
|
||||||
InMemoryMatchResult result = matcher.match();
|
InMemoryMatchResult result = matcher.match();
|
||||||
if (result.matched()) {
|
if (result.matched()) {
|
||||||
// as soon as one trigger matches, we're done
|
// as soon as one trigger matches, we're done
|
||||||
|
|
|
@ -27,6 +27,7 @@ import ca.uhn.fhir.jpa.searchparam.matcher.InMemoryMatchResult;
|
||||||
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedJsonMessage;
|
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedJsonMessage;
|
||||||
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage;
|
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage;
|
||||||
import ca.uhn.fhir.jpa.topic.filter.InMemoryTopicFilterMatcher;
|
import ca.uhn.fhir.jpa.topic.filter.InMemoryTopicFilterMatcher;
|
||||||
|
import ca.uhn.fhir.jpa.util.MemoryCacheService;
|
||||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||||
import ca.uhn.fhir.subscription.api.IResourceModifiedMessagePersistenceSvc;
|
import ca.uhn.fhir.subscription.api.IResourceModifiedMessagePersistenceSvc;
|
||||||
import ca.uhn.fhir.util.Logs;
|
import ca.uhn.fhir.util.Logs;
|
||||||
|
@ -67,8 +68,11 @@ public class SubscriptionTopicMatchingSubscriber implements MessageHandler {
|
||||||
@Autowired
|
@Autowired
|
||||||
private IResourceModifiedMessagePersistenceSvc myResourceModifiedMessagePersistenceSvc;
|
private IResourceModifiedMessagePersistenceSvc myResourceModifiedMessagePersistenceSvc;
|
||||||
|
|
||||||
public SubscriptionTopicMatchingSubscriber(FhirContext theFhirContext) {
|
private MemoryCacheService myMemoryCacheService;
|
||||||
|
|
||||||
|
public SubscriptionTopicMatchingSubscriber(FhirContext theFhirContext, MemoryCacheService memoryCacheService) {
|
||||||
myFhirContext = theFhirContext;
|
myFhirContext = theFhirContext;
|
||||||
|
this.myMemoryCacheService = memoryCacheService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -110,7 +114,8 @@ public class SubscriptionTopicMatchingSubscriber implements MessageHandler {
|
||||||
|
|
||||||
Collection<SubscriptionTopic> topics = mySubscriptionTopicRegistry.getAll();
|
Collection<SubscriptionTopic> topics = mySubscriptionTopicRegistry.getAll();
|
||||||
for (SubscriptionTopic topic : topics) {
|
for (SubscriptionTopic topic : topics) {
|
||||||
SubscriptionTopicMatcher matcher = new SubscriptionTopicMatcher(mySubscriptionTopicSupport, topic);
|
SubscriptionTopicMatcher matcher =
|
||||||
|
new SubscriptionTopicMatcher(mySubscriptionTopicSupport, topic, myMemoryCacheService);
|
||||||
InMemoryMatchResult result = matcher.match(theMsg);
|
InMemoryMatchResult result = matcher.match(theMsg);
|
||||||
if (result.matched()) {
|
if (result.matched()) {
|
||||||
int deliveries = deliverToTopicSubscriptions(theMsg, topic, result);
|
int deliveries = deliverToTopicSubscriptions(theMsg, topic, result);
|
||||||
|
|
|
@ -19,17 +19,26 @@
|
||||||
*/
|
*/
|
||||||
package ca.uhn.fhir.jpa.topic;
|
package ca.uhn.fhir.jpa.topic;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.fhirpath.IFhirPath;
|
||||||
|
import ca.uhn.fhir.fhirpath.IFhirPathEvaluationContext;
|
||||||
|
import ca.uhn.fhir.i18n.Msg;
|
||||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||||
import ca.uhn.fhir.jpa.searchparam.matcher.InMemoryMatchResult;
|
import ca.uhn.fhir.jpa.searchparam.matcher.InMemoryMatchResult;
|
||||||
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage;
|
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage;
|
||||||
|
import ca.uhn.fhir.jpa.util.MemoryCacheService;
|
||||||
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
|
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
|
||||||
import ca.uhn.fhir.rest.server.messaging.BaseResourceMessage;
|
import ca.uhn.fhir.rest.server.messaging.BaseResourceMessage;
|
||||||
import ca.uhn.fhir.storage.PreviousVersionReader;
|
import ca.uhn.fhir.storage.PreviousVersionReader;
|
||||||
import ca.uhn.fhir.util.Logs;
|
import ca.uhn.fhir.util.Logs;
|
||||||
|
import com.google.common.base.Strings;
|
||||||
|
import org.hl7.fhir.exceptions.FHIRException;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
import org.hl7.fhir.r5.model.BooleanType;
|
||||||
import org.hl7.fhir.r5.model.Enumeration;
|
import org.hl7.fhir.r5.model.Enumeration;
|
||||||
import org.hl7.fhir.r5.model.SubscriptionTopic;
|
import org.hl7.fhir.r5.model.SubscriptionTopic;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.helpers.MessageFormatter;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
@ -45,11 +54,13 @@ public class SubscriptionTriggerMatcher {
|
||||||
private final IFhirResourceDao myDao;
|
private final IFhirResourceDao myDao;
|
||||||
private final PreviousVersionReader myPreviousVersionReader;
|
private final PreviousVersionReader myPreviousVersionReader;
|
||||||
private final SystemRequestDetails mySrd;
|
private final SystemRequestDetails mySrd;
|
||||||
|
private final MemoryCacheService myMemoryCacheService;
|
||||||
|
|
||||||
public SubscriptionTriggerMatcher(
|
public SubscriptionTriggerMatcher(
|
||||||
SubscriptionTopicSupport theSubscriptionTopicSupport,
|
SubscriptionTopicSupport theSubscriptionTopicSupport,
|
||||||
ResourceModifiedMessage theMsg,
|
ResourceModifiedMessage theMsg,
|
||||||
SubscriptionTopic.SubscriptionTopicResourceTriggerComponent theTrigger) {
|
SubscriptionTopic.SubscriptionTopicResourceTriggerComponent theTrigger,
|
||||||
|
MemoryCacheService theMemoryCacheService) {
|
||||||
mySubscriptionTopicSupport = theSubscriptionTopicSupport;
|
mySubscriptionTopicSupport = theSubscriptionTopicSupport;
|
||||||
myOperation = theMsg.getOperationType();
|
myOperation = theMsg.getOperationType();
|
||||||
myResource = theMsg.getPayload(theSubscriptionTopicSupport.getFhirContext());
|
myResource = theMsg.getPayload(theSubscriptionTopicSupport.getFhirContext());
|
||||||
|
@ -58,6 +69,7 @@ public class SubscriptionTriggerMatcher {
|
||||||
myTrigger = theTrigger;
|
myTrigger = theTrigger;
|
||||||
myPreviousVersionReader = new PreviousVersionReader(myDao);
|
myPreviousVersionReader = new PreviousVersionReader(myDao);
|
||||||
mySrd = new SystemRequestDetails();
|
mySrd = new SystemRequestDetails();
|
||||||
|
myMemoryCacheService = theMemoryCacheService;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InMemoryMatchResult match() {
|
public InMemoryMatchResult match() {
|
||||||
|
@ -66,21 +78,22 @@ public class SubscriptionTriggerMatcher {
|
||||||
if (SubscriptionTopicUtil.matches(myOperation, supportedInteractions)) {
|
if (SubscriptionTopicUtil.matches(myOperation, supportedInteractions)) {
|
||||||
SubscriptionTopic.SubscriptionTopicResourceTriggerQueryCriteriaComponent queryCriteria =
|
SubscriptionTopic.SubscriptionTopicResourceTriggerQueryCriteriaComponent queryCriteria =
|
||||||
myTrigger.getQueryCriteria();
|
myTrigger.getQueryCriteria();
|
||||||
InMemoryMatchResult result = match(queryCriteria);
|
String fhirPathCriteria = myTrigger.getFhirPathCriteria();
|
||||||
if (result.matched()) {
|
return match(queryCriteria, fhirPathCriteria);
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return InMemoryMatchResult.noMatch();
|
return InMemoryMatchResult.noMatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
private InMemoryMatchResult match(
|
private InMemoryMatchResult match(
|
||||||
SubscriptionTopic.SubscriptionTopicResourceTriggerQueryCriteriaComponent theQueryCriteria) {
|
SubscriptionTopic.SubscriptionTopicResourceTriggerQueryCriteriaComponent theQueryCriteria,
|
||||||
|
String theFhirPathCriteria) {
|
||||||
String previousCriteria = theQueryCriteria.getPrevious();
|
String previousCriteria = theQueryCriteria.getPrevious();
|
||||||
String currentCriteria = theQueryCriteria.getCurrent();
|
String currentCriteria = theQueryCriteria.getCurrent();
|
||||||
InMemoryMatchResult previousMatches = InMemoryMatchResult.fromBoolean(previousCriteria == null);
|
InMemoryMatchResult previousMatches = InMemoryMatchResult.fromBoolean(previousCriteria == null);
|
||||||
InMemoryMatchResult currentMatches = InMemoryMatchResult.fromBoolean(currentCriteria == null);
|
InMemoryMatchResult currentMatches = InMemoryMatchResult.fromBoolean(currentCriteria == null);
|
||||||
|
|
||||||
|
InMemoryMatchResult fhirPathCriteriaEvaluationResult = evaluateFhirPathCriteria(theFhirPathCriteria);
|
||||||
|
|
||||||
// WIP STR5 implement fhirPathCriteria per https://build.fhir.org/subscriptiontopic.html#fhirpath-criteria
|
// WIP STR5 implement fhirPathCriteria per https://build.fhir.org/subscriptiontopic.html#fhirpath-criteria
|
||||||
if (currentCriteria != null) {
|
if (currentCriteria != null) {
|
||||||
currentMatches = matchResource(myResource, currentCriteria);
|
currentMatches = matchResource(myResource, currentCriteria);
|
||||||
|
@ -105,12 +118,89 @@ public class SubscriptionTriggerMatcher {
|
||||||
}
|
}
|
||||||
// WIP STR5 implement resultForCreate and resultForDelete
|
// WIP STR5 implement resultForCreate and resultForDelete
|
||||||
if (theQueryCriteria.getRequireBoth()) {
|
if (theQueryCriteria.getRequireBoth()) {
|
||||||
return InMemoryMatchResult.and(previousMatches, currentMatches);
|
return InMemoryMatchResult.and(
|
||||||
|
InMemoryMatchResult.and(previousMatches, currentMatches), fhirPathCriteriaEvaluationResult);
|
||||||
} else {
|
} else {
|
||||||
return InMemoryMatchResult.or(previousMatches, currentMatches);
|
return InMemoryMatchResult.and(
|
||||||
|
InMemoryMatchResult.or(previousMatches, currentMatches), fhirPathCriteriaEvaluationResult);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private InMemoryMatchResult evaluateFhirPathCriteria(String theFhirPathCriteria) {
|
||||||
|
if (!Strings.isNullOrEmpty(theFhirPathCriteria)) {
|
||||||
|
IFhirPath fhirPathEngine =
|
||||||
|
mySubscriptionTopicSupport.getFhirContext().newFhirPath();
|
||||||
|
fhirPathEngine.setEvaluationContext(new IFhirPathEvaluationContext() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<IBase> resolveConstant(Object appContext, String name, boolean beforeContext) {
|
||||||
|
if ("current".equalsIgnoreCase(name)) return List.of(myResource);
|
||||||
|
|
||||||
|
if ("previous".equalsIgnoreCase(name)) {
|
||||||
|
Optional previousResource = myPreviousVersionReader.readPreviousVersion(myResource);
|
||||||
|
if (previousResource.isPresent()) return List.of((IBase) previousResource.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
IFhirPath.IParsedExpression expression = myMemoryCacheService.get(
|
||||||
|
MemoryCacheService.CacheEnum.FHIRPATH_EXPRESSION, theFhirPathCriteria, exp -> {
|
||||||
|
try {
|
||||||
|
return fhirPathEngine.parse(exp);
|
||||||
|
} catch (FHIRException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(Msg.code(2534) + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
List<IBase> result = fhirPathEngine.evaluate(myResource, expression, IBase.class);
|
||||||
|
|
||||||
|
return parseResult(theFhirPathCriteria, result);
|
||||||
|
|
||||||
|
} catch (FHIRException fhirException) {
|
||||||
|
ourLog.warn(
|
||||||
|
"Subscription topic {} has a fhirPathCriteria that is not valid: {}",
|
||||||
|
myTrigger.getId(),
|
||||||
|
theFhirPathCriteria,
|
||||||
|
fhirException);
|
||||||
|
return InMemoryMatchResult.unsupportedFromReason(fhirException.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return InMemoryMatchResult.fromBoolean(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private InMemoryMatchResult parseResult(String theFhirPathCriteria, List<IBase> result) {
|
||||||
|
if (result == null) {
|
||||||
|
return InMemoryMatchResult.unsupportedFromReason(MessageFormatter.format(
|
||||||
|
"FhirPath evaluation criteria '{}' from Subscription topic: '{}' resulted in null results.",
|
||||||
|
theFhirPathCriteria,
|
||||||
|
myTrigger.getId())
|
||||||
|
.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.size() != 1) {
|
||||||
|
return InMemoryMatchResult.unsupportedFromReason(MessageFormatter.arrayFormat(
|
||||||
|
"FhirPath evaluation criteria '{}' from Subscription topic: '{}' resulted in '{}' results. Expected 1.",
|
||||||
|
new String[] {theFhirPathCriteria, myTrigger.getId(), String.valueOf(result.size())})
|
||||||
|
.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(result.get(0) instanceof BooleanType)) {
|
||||||
|
return InMemoryMatchResult.unsupportedFromReason(MessageFormatter.arrayFormat(
|
||||||
|
"FhirPath evaluation criteria '{}' from Subscription topic: '{}' resulted in a non-boolean result: '{}'",
|
||||||
|
new String[] {
|
||||||
|
theFhirPathCriteria,
|
||||||
|
myTrigger.getId(),
|
||||||
|
result.get(0).getClass().getName()
|
||||||
|
})
|
||||||
|
.getMessage());
|
||||||
|
}
|
||||||
|
return InMemoryMatchResult.fromBoolean(((BooleanType) result.get(0)).booleanValue());
|
||||||
|
}
|
||||||
|
|
||||||
private InMemoryMatchResult matchResource(IBaseResource theResource, String theCriteria) {
|
private InMemoryMatchResult matchResource(IBaseResource theResource, String theCriteria) {
|
||||||
InMemoryMatchResult result =
|
InMemoryMatchResult result =
|
||||||
mySubscriptionTopicSupport.getSearchParamMatcher().match(theCriteria, theResource, mySrd);
|
mySubscriptionTopicSupport.getSearchParamMatcher().match(theCriteria, theResource, mySrd);
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
package ca.uhn.fhir.jpa.topic;
|
package ca.uhn.fhir.jpa.topic;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
|
||||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||||
import ca.uhn.fhir.jpa.searchparam.matcher.InMemoryMatchResult;
|
import ca.uhn.fhir.jpa.searchparam.matcher.InMemoryMatchResult;
|
||||||
import ca.uhn.fhir.jpa.searchparam.matcher.SearchParamMatcher;
|
import ca.uhn.fhir.jpa.searchparam.matcher.SearchParamMatcher;
|
||||||
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage;
|
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage;
|
||||||
|
import ca.uhn.fhir.jpa.util.MemoryCacheService;
|
||||||
import org.hl7.fhir.r5.model.Encounter;
|
import org.hl7.fhir.r5.model.Encounter;
|
||||||
|
import org.hl7.fhir.r5.model.Enumerations;
|
||||||
import org.hl7.fhir.r5.model.IdType;
|
import org.hl7.fhir.r5.model.IdType;
|
||||||
import org.hl7.fhir.r5.model.SubscriptionTopic;
|
import org.hl7.fhir.r5.model.SubscriptionTopic;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
@ -15,7 +18,10 @@ import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
@ -30,11 +36,14 @@ class SubscriptionTriggerMatcherTest {
|
||||||
@Mock
|
@Mock
|
||||||
SearchParamMatcher mySearchParamMatcher;
|
SearchParamMatcher mySearchParamMatcher;
|
||||||
|
|
||||||
|
MemoryCacheService myMemoryCacheService;
|
||||||
|
|
||||||
private SubscriptionTopicSupport mySubscriptionTopicSupport;
|
private SubscriptionTopicSupport mySubscriptionTopicSupport;
|
||||||
private Encounter myEncounter;
|
private Encounter myEncounter;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void before() {
|
public void before() {
|
||||||
|
myMemoryCacheService = new MemoryCacheService(new JpaStorageSettings());
|
||||||
mySubscriptionTopicSupport = new SubscriptionTopicSupport(ourFhirContext, myDaoRegistry, mySearchParamMatcher);
|
mySubscriptionTopicSupport = new SubscriptionTopicSupport(ourFhirContext, myDaoRegistry, mySearchParamMatcher);
|
||||||
myEncounter = new Encounter();
|
myEncounter = new Encounter();
|
||||||
myEncounter.setIdElement(new IdType("Encounter", "123", "2"));
|
myEncounter.setIdElement(new IdType("Encounter", "123", "2"));
|
||||||
|
@ -48,7 +57,7 @@ class SubscriptionTriggerMatcherTest {
|
||||||
SubscriptionTopic.SubscriptionTopicResourceTriggerComponent trigger = new SubscriptionTopic.SubscriptionTopicResourceTriggerComponent();
|
SubscriptionTopic.SubscriptionTopicResourceTriggerComponent trigger = new SubscriptionTopic.SubscriptionTopicResourceTriggerComponent();
|
||||||
|
|
||||||
// run
|
// run
|
||||||
SubscriptionTriggerMatcher svc = new SubscriptionTriggerMatcher(mySubscriptionTopicSupport, msg, trigger);
|
SubscriptionTriggerMatcher svc = new SubscriptionTriggerMatcher(mySubscriptionTopicSupport, msg, trigger, myMemoryCacheService);
|
||||||
InMemoryMatchResult result = svc.match();
|
InMemoryMatchResult result = svc.match();
|
||||||
|
|
||||||
// verify
|
// verify
|
||||||
|
@ -65,7 +74,7 @@ class SubscriptionTriggerMatcherTest {
|
||||||
trigger.addSupportedInteraction(SubscriptionTopic.InteractionTrigger.CREATE);
|
trigger.addSupportedInteraction(SubscriptionTopic.InteractionTrigger.CREATE);
|
||||||
|
|
||||||
// run
|
// run
|
||||||
SubscriptionTriggerMatcher svc = new SubscriptionTriggerMatcher(mySubscriptionTopicSupport, msg, trigger);
|
SubscriptionTriggerMatcher svc = new SubscriptionTriggerMatcher(mySubscriptionTopicSupport, msg, trigger, myMemoryCacheService);
|
||||||
InMemoryMatchResult result = svc.match();
|
InMemoryMatchResult result = svc.match();
|
||||||
|
|
||||||
// verify
|
// verify
|
||||||
|
@ -82,7 +91,7 @@ class SubscriptionTriggerMatcherTest {
|
||||||
trigger.addSupportedInteraction(SubscriptionTopic.InteractionTrigger.UPDATE);
|
trigger.addSupportedInteraction(SubscriptionTopic.InteractionTrigger.UPDATE);
|
||||||
|
|
||||||
// run
|
// run
|
||||||
SubscriptionTriggerMatcher svc = new SubscriptionTriggerMatcher(mySubscriptionTopicSupport, msg, trigger);
|
SubscriptionTriggerMatcher svc = new SubscriptionTriggerMatcher(mySubscriptionTopicSupport, msg, trigger, myMemoryCacheService);
|
||||||
InMemoryMatchResult result = svc.match();
|
InMemoryMatchResult result = svc.match();
|
||||||
|
|
||||||
// verify
|
// verify
|
||||||
|
@ -99,7 +108,7 @@ class SubscriptionTriggerMatcherTest {
|
||||||
trigger.addSupportedInteraction(SubscriptionTopic.InteractionTrigger.UPDATE);
|
trigger.addSupportedInteraction(SubscriptionTopic.InteractionTrigger.UPDATE);
|
||||||
|
|
||||||
// run
|
// run
|
||||||
SubscriptionTriggerMatcher svc = new SubscriptionTriggerMatcher(mySubscriptionTopicSupport, msg, trigger);
|
SubscriptionTriggerMatcher svc = new SubscriptionTriggerMatcher(mySubscriptionTopicSupport, msg, trigger, myMemoryCacheService);
|
||||||
InMemoryMatchResult result = svc.match();
|
InMemoryMatchResult result = svc.match();
|
||||||
|
|
||||||
// verify
|
// verify
|
||||||
|
@ -124,11 +133,253 @@ class SubscriptionTriggerMatcherTest {
|
||||||
when(mySearchParamMatcher.match(any(), any(), any())).thenReturn(InMemoryMatchResult.successfulMatch());
|
when(mySearchParamMatcher.match(any(), any(), any())).thenReturn(InMemoryMatchResult.successfulMatch());
|
||||||
|
|
||||||
// run
|
// run
|
||||||
SubscriptionTriggerMatcher svc = new SubscriptionTriggerMatcher(mySubscriptionTopicSupport, msg, trigger);
|
SubscriptionTriggerMatcher svc = new SubscriptionTriggerMatcher(mySubscriptionTopicSupport, msg, trigger, myMemoryCacheService);
|
||||||
InMemoryMatchResult result = svc.match();
|
InMemoryMatchResult result = svc.match();
|
||||||
|
|
||||||
// verify
|
// verify
|
||||||
assertTrue(result.matched());
|
assertTrue(result.matched());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFalseFhirPathCriteriaEvaluation() {
|
||||||
|
ResourceModifiedMessage msg = new ResourceModifiedMessage(ourFhirContext, myEncounter, ResourceModifiedMessage.OperationTypeEnum.UPDATE);
|
||||||
|
|
||||||
|
// setup
|
||||||
|
SubscriptionTopic.SubscriptionTopicResourceTriggerComponent trigger = new SubscriptionTopic.SubscriptionTopicResourceTriggerComponent();
|
||||||
|
trigger.setResource("Encounter");
|
||||||
|
trigger.addSupportedInteraction(SubscriptionTopic.InteractionTrigger.UPDATE);
|
||||||
|
trigger.setFhirPathCriteria("false");
|
||||||
|
|
||||||
|
// run
|
||||||
|
SubscriptionTriggerMatcher svc = new SubscriptionTriggerMatcher(mySubscriptionTopicSupport, msg, trigger, myMemoryCacheService);
|
||||||
|
InMemoryMatchResult result = svc.match();
|
||||||
|
|
||||||
|
// verify
|
||||||
|
assertFalse(result.matched());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInvalidFhirPathCriteriaEvaluation() {
|
||||||
|
ResourceModifiedMessage msg = new ResourceModifiedMessage(ourFhirContext, myEncounter, ResourceModifiedMessage.OperationTypeEnum.UPDATE);
|
||||||
|
|
||||||
|
// setup
|
||||||
|
SubscriptionTopic.SubscriptionTopicResourceTriggerComponent trigger = new SubscriptionTopic.SubscriptionTopicResourceTriggerComponent();
|
||||||
|
trigger.setResource("Encounter");
|
||||||
|
trigger.addSupportedInteraction(SubscriptionTopic.InteractionTrigger.UPDATE);
|
||||||
|
trigger.setFhirPathCriteria("random text");
|
||||||
|
|
||||||
|
// run
|
||||||
|
SubscriptionTriggerMatcher svc = new SubscriptionTriggerMatcher(mySubscriptionTopicSupport, msg, trigger, myMemoryCacheService);
|
||||||
|
InMemoryMatchResult result = svc.match();
|
||||||
|
|
||||||
|
// verify
|
||||||
|
assertFalse(result.matched());
|
||||||
|
assertEquals("Error @1, 2: Premature ExpressionNode termination at unexpected token \"text\"", result.getUnsupportedReason());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInvalidBooleanOutcomeOfFhirPathCriteriaEvaluation() {
|
||||||
|
ResourceModifiedMessage msg = new ResourceModifiedMessage(ourFhirContext, myEncounter, ResourceModifiedMessage.OperationTypeEnum.UPDATE);
|
||||||
|
|
||||||
|
// setup
|
||||||
|
SubscriptionTopic.SubscriptionTopicResourceTriggerComponent trigger = new SubscriptionTopic.SubscriptionTopicResourceTriggerComponent();
|
||||||
|
trigger.setResource("Encounter");
|
||||||
|
trigger.addSupportedInteraction(SubscriptionTopic.InteractionTrigger.UPDATE);
|
||||||
|
trigger.setFhirPathCriteria("id");
|
||||||
|
|
||||||
|
// run
|
||||||
|
SubscriptionTriggerMatcher svc = new SubscriptionTriggerMatcher(mySubscriptionTopicSupport, msg, trigger, myMemoryCacheService);
|
||||||
|
InMemoryMatchResult result = svc.match();
|
||||||
|
|
||||||
|
// verify
|
||||||
|
assertFalse(result.matched());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidFhirPathCriteriaEvaluation() {
|
||||||
|
ResourceModifiedMessage msg = new ResourceModifiedMessage(ourFhirContext, myEncounter, ResourceModifiedMessage.OperationTypeEnum.UPDATE);
|
||||||
|
|
||||||
|
// setup
|
||||||
|
SubscriptionTopic.SubscriptionTopicResourceTriggerComponent trigger = new SubscriptionTopic.SubscriptionTopicResourceTriggerComponent();
|
||||||
|
trigger.setResource("Encounter");
|
||||||
|
trigger.addSupportedInteraction(SubscriptionTopic.InteractionTrigger.UPDATE);
|
||||||
|
trigger.setFhirPathCriteria("id = " + myEncounter.getIdElement().getIdPart());
|
||||||
|
|
||||||
|
// run
|
||||||
|
SubscriptionTriggerMatcher svc = new SubscriptionTriggerMatcher(mySubscriptionTopicSupport, msg, trigger, myMemoryCacheService);
|
||||||
|
InMemoryMatchResult result = svc.match();
|
||||||
|
|
||||||
|
// verify
|
||||||
|
assertTrue(result.matched());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidFhirPathCriteriaEvaluationUsingCurrent() {
|
||||||
|
ResourceModifiedMessage msg = new ResourceModifiedMessage(ourFhirContext, myEncounter, ResourceModifiedMessage.OperationTypeEnum.UPDATE);
|
||||||
|
|
||||||
|
// setup
|
||||||
|
SubscriptionTopic.SubscriptionTopicResourceTriggerComponent trigger = new SubscriptionTopic.SubscriptionTopicResourceTriggerComponent();
|
||||||
|
trigger.setResource("Encounter");
|
||||||
|
trigger.addSupportedInteraction(SubscriptionTopic.InteractionTrigger.UPDATE);
|
||||||
|
trigger.setFhirPathCriteria("%current.id = " + myEncounter.getIdElement().getIdPart());
|
||||||
|
|
||||||
|
// run
|
||||||
|
SubscriptionTriggerMatcher svc = new SubscriptionTriggerMatcher(mySubscriptionTopicSupport, msg, trigger, myMemoryCacheService);
|
||||||
|
InMemoryMatchResult result = svc.match();
|
||||||
|
|
||||||
|
// verify
|
||||||
|
assertTrue(result.matched());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidFhirPathCriteriaEvaluationReturningNonBoolean() {
|
||||||
|
ResourceModifiedMessage msg = new ResourceModifiedMessage(ourFhirContext, myEncounter, ResourceModifiedMessage.OperationTypeEnum.UPDATE);
|
||||||
|
|
||||||
|
// setup
|
||||||
|
SubscriptionTopic.SubscriptionTopicResourceTriggerComponent trigger = new SubscriptionTopic.SubscriptionTopicResourceTriggerComponent();
|
||||||
|
trigger.setId("1");
|
||||||
|
trigger.setResource("Encounter");
|
||||||
|
trigger.addSupportedInteraction(SubscriptionTopic.InteractionTrigger.UPDATE);
|
||||||
|
trigger.setFhirPathCriteria("%current.id");
|
||||||
|
|
||||||
|
// run
|
||||||
|
SubscriptionTriggerMatcher svc = new SubscriptionTriggerMatcher(mySubscriptionTopicSupport, msg, trigger, myMemoryCacheService);
|
||||||
|
InMemoryMatchResult result = svc.match();
|
||||||
|
|
||||||
|
// verify
|
||||||
|
assertFalse(result.matched());
|
||||||
|
assertEquals("FhirPath evaluation criteria '%current.id' from Subscription topic: '1' resulted in a non-boolean result: 'org.hl7.fhir.r5.model.IdType'", result.getUnsupportedReason());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidFhirPathReturningCollection() {
|
||||||
|
ResourceModifiedMessage msg = new ResourceModifiedMessage(ourFhirContext, myEncounter, ResourceModifiedMessage.OperationTypeEnum.UPDATE);
|
||||||
|
|
||||||
|
// setup
|
||||||
|
SubscriptionTopic.SubscriptionTopicResourceTriggerComponent trigger = new SubscriptionTopic.SubscriptionTopicResourceTriggerComponent();
|
||||||
|
trigger.setId("1");
|
||||||
|
trigger.setResource("Encounter");
|
||||||
|
trigger.addSupportedInteraction(SubscriptionTopic.InteractionTrigger.UPDATE);
|
||||||
|
trigger.setFhirPathCriteria("%current | %previous");
|
||||||
|
|
||||||
|
IFhirResourceDao mockEncounterDao = mock(IFhirResourceDao.class);
|
||||||
|
when(myDaoRegistry.getResourceDao("Encounter")).thenReturn(mockEncounterDao);
|
||||||
|
Encounter encounterPreviousVersion = new Encounter();
|
||||||
|
when(mockEncounterDao.read(any(), any(), eq(false))).thenReturn(encounterPreviousVersion);
|
||||||
|
|
||||||
|
// run
|
||||||
|
SubscriptionTriggerMatcher svc = new SubscriptionTriggerMatcher(mySubscriptionTopicSupport, msg, trigger, myMemoryCacheService);
|
||||||
|
InMemoryMatchResult result = svc.match();
|
||||||
|
|
||||||
|
// verify
|
||||||
|
assertFalse(result.matched());
|
||||||
|
assertEquals("FhirPath evaluation criteria '%current | %previous' from Subscription topic: '1' resulted in '2' results. Expected 1.", result.getUnsupportedReason());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateWithPrevCriteriaMatchAndFailingFhirPathCriteria() {
|
||||||
|
ResourceModifiedMessage msg = new ResourceModifiedMessage(ourFhirContext, myEncounter, ResourceModifiedMessage.OperationTypeEnum.UPDATE);
|
||||||
|
|
||||||
|
// setup
|
||||||
|
SubscriptionTopic.SubscriptionTopicResourceTriggerComponent trigger = new SubscriptionTopic.SubscriptionTopicResourceTriggerComponent();
|
||||||
|
trigger.setResource("Encounter");
|
||||||
|
trigger.addSupportedInteraction(SubscriptionTopic.InteractionTrigger.UPDATE);
|
||||||
|
trigger.getQueryCriteria().setPrevious("Encounter?status=in-progress");
|
||||||
|
trigger.setFhirPathCriteria("random text");
|
||||||
|
|
||||||
|
|
||||||
|
IFhirResourceDao mockEncounterDao = mock(IFhirResourceDao.class);
|
||||||
|
when(myDaoRegistry.getResourceDao("Encounter")).thenReturn(mockEncounterDao);
|
||||||
|
Encounter encounterPreviousVersion = new Encounter();
|
||||||
|
when(mockEncounterDao.read(any(), any(), eq(false))).thenReturn(encounterPreviousVersion);
|
||||||
|
when(mySearchParamMatcher.match(any(), any(), any())).thenReturn(InMemoryMatchResult.successfulMatch());
|
||||||
|
|
||||||
|
// run
|
||||||
|
SubscriptionTriggerMatcher svc = new SubscriptionTriggerMatcher(mySubscriptionTopicSupport, msg, trigger, myMemoryCacheService);
|
||||||
|
InMemoryMatchResult result = svc.match();
|
||||||
|
|
||||||
|
// verify
|
||||||
|
assertFalse(result.matched());
|
||||||
|
assertEquals("Error @1, 2: Premature ExpressionNode termination at unexpected token \"text\"", result.getUnsupportedReason());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateWithPrevCriteriaMatchAndFhirPathCriteriaUsingPreviousVersion() {
|
||||||
|
myEncounter.setStatus(Enumerations.EncounterStatus.INPROGRESS);
|
||||||
|
ResourceModifiedMessage msg = new ResourceModifiedMessage(ourFhirContext, myEncounter, ResourceModifiedMessage.OperationTypeEnum.UPDATE);
|
||||||
|
|
||||||
|
// setup
|
||||||
|
SubscriptionTopic.SubscriptionTopicResourceTriggerComponent trigger = new SubscriptionTopic.SubscriptionTopicResourceTriggerComponent();
|
||||||
|
trigger.setResource("Encounter");
|
||||||
|
trigger.addSupportedInteraction(SubscriptionTopic.InteractionTrigger.UPDATE);
|
||||||
|
trigger.getQueryCriteria().setPrevious("Encounter?status=in-progress");
|
||||||
|
trigger.setFhirPathCriteria("%current.status='in-progress' and %previous.status.exists().not()");
|
||||||
|
|
||||||
|
|
||||||
|
IFhirResourceDao mockEncounterDao = mock(IFhirResourceDao.class);
|
||||||
|
when(myDaoRegistry.getResourceDao("Encounter")).thenReturn(mockEncounterDao);
|
||||||
|
Encounter encounterPreviousVersion = new Encounter();
|
||||||
|
when(mockEncounterDao.read(any(), any(), eq(false))).thenReturn(encounterPreviousVersion);
|
||||||
|
when(mySearchParamMatcher.match(any(), any(), any())).thenReturn(InMemoryMatchResult.successfulMatch());
|
||||||
|
|
||||||
|
// run
|
||||||
|
SubscriptionTriggerMatcher svc = new SubscriptionTriggerMatcher(mySubscriptionTopicSupport, msg, trigger, myMemoryCacheService);
|
||||||
|
InMemoryMatchResult result = svc.match();
|
||||||
|
|
||||||
|
// verify
|
||||||
|
assertTrue(result.matched());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateOnlyFhirPathCriteriaUsingPreviousVersion() {
|
||||||
|
myEncounter.setStatus(Enumerations.EncounterStatus.INPROGRESS);
|
||||||
|
ResourceModifiedMessage msg = new ResourceModifiedMessage(ourFhirContext, myEncounter, ResourceModifiedMessage.OperationTypeEnum.UPDATE);
|
||||||
|
|
||||||
|
// setup
|
||||||
|
SubscriptionTopic.SubscriptionTopicResourceTriggerComponent trigger = new SubscriptionTopic.SubscriptionTopicResourceTriggerComponent();
|
||||||
|
trigger.setResource("Encounter");
|
||||||
|
trigger.addSupportedInteraction(SubscriptionTopic.InteractionTrigger.UPDATE);
|
||||||
|
trigger.setFhirPathCriteria("%current.status='in-progress' and %previous.status.exists().not()");
|
||||||
|
|
||||||
|
|
||||||
|
IFhirResourceDao mockEncounterDao = mock(IFhirResourceDao.class);
|
||||||
|
when(myDaoRegistry.getResourceDao("Encounter")).thenReturn(mockEncounterDao);
|
||||||
|
Encounter encounterPreviousVersion = new Encounter();
|
||||||
|
when(mockEncounterDao.read(any(), any(), eq(false))).thenReturn(encounterPreviousVersion);
|
||||||
|
|
||||||
|
// run
|
||||||
|
SubscriptionTriggerMatcher svc = new SubscriptionTriggerMatcher(mySubscriptionTopicSupport, msg, trigger, myMemoryCacheService);
|
||||||
|
InMemoryMatchResult result = svc.match();
|
||||||
|
|
||||||
|
// verify
|
||||||
|
assertTrue(result.matched());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCacheUsage() {
|
||||||
|
myEncounter.setStatus(Enumerations.EncounterStatus.INPROGRESS);
|
||||||
|
ResourceModifiedMessage msg = new ResourceModifiedMessage(ourFhirContext, myEncounter, ResourceModifiedMessage.OperationTypeEnum.UPDATE);
|
||||||
|
|
||||||
|
// setup
|
||||||
|
SubscriptionTopic.SubscriptionTopicResourceTriggerComponent trigger = new SubscriptionTopic.SubscriptionTopicResourceTriggerComponent();
|
||||||
|
trigger.setResource("Encounter");
|
||||||
|
trigger.addSupportedInteraction(SubscriptionTopic.InteractionTrigger.UPDATE);
|
||||||
|
String fhirPathCriteria = "%current.status='in-progress'";
|
||||||
|
trigger.setFhirPathCriteria(fhirPathCriteria);
|
||||||
|
|
||||||
|
|
||||||
|
IFhirResourceDao mockEncounterDao = mock(IFhirResourceDao.class);
|
||||||
|
when(myDaoRegistry.getResourceDao("Encounter")).thenReturn(mockEncounterDao);
|
||||||
|
|
||||||
|
assertNull(myMemoryCacheService.getIfPresent(MemoryCacheService.CacheEnum.FHIRPATH_EXPRESSION, fhirPathCriteria));
|
||||||
|
|
||||||
|
// run
|
||||||
|
SubscriptionTriggerMatcher svc = new SubscriptionTriggerMatcher(mySubscriptionTopicSupport, msg, trigger, myMemoryCacheService);
|
||||||
|
InMemoryMatchResult result = svc.match();
|
||||||
|
|
||||||
|
|
||||||
|
// verify
|
||||||
|
assertTrue(result.matched());
|
||||||
|
assertNotNull(myMemoryCacheService.getIfPresent(MemoryCacheService.CacheEnum.FHIRPATH_EXPRESSION, fhirPathCriteria));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,6 +77,7 @@ public class MemoryCacheService {
|
||||||
case HISTORY_COUNT:
|
case HISTORY_COUNT:
|
||||||
case TAG_DEFINITION:
|
case TAG_DEFINITION:
|
||||||
case RESOURCE_CONDITIONAL_CREATE_VERSION:
|
case RESOURCE_CONDITIONAL_CREATE_VERSION:
|
||||||
|
case FHIRPATH_EXPRESSION:
|
||||||
default:
|
default:
|
||||||
timeoutSeconds = SECONDS.convert(1, MINUTES);
|
timeoutSeconds = SECONDS.convert(1, MINUTES);
|
||||||
maximumSize = 10000;
|
maximumSize = 10000;
|
||||||
|
@ -193,6 +194,7 @@ public class MemoryCacheService {
|
||||||
TAG_DEFINITION(TagDefinitionCacheKey.class),
|
TAG_DEFINITION(TagDefinitionCacheKey.class),
|
||||||
RESOURCE_LOOKUP(String.class),
|
RESOURCE_LOOKUP(String.class),
|
||||||
FORCED_ID_TO_PID(String.class),
|
FORCED_ID_TO_PID(String.class),
|
||||||
|
FHIRPATH_EXPRESSION(String.class),
|
||||||
/**
|
/**
|
||||||
* Key type: {@literal Long}
|
* Key type: {@literal Long}
|
||||||
* Value type: {@literal Optional<String>}
|
* Value type: {@literal Optional<String>}
|
||||||
|
|
|
@ -19,8 +19,10 @@ import org.hl7.fhir.r5.model.Base;
|
||||||
import org.hl7.fhir.r5.model.IdType;
|
import org.hl7.fhir.r5.model.IdType;
|
||||||
import org.hl7.fhir.r5.model.ValueSet;
|
import org.hl7.fhir.r5.model.ValueSet;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class FhirPathR5 implements IFhirPath {
|
public class FhirPathR5 implements IFhirPath {
|
||||||
|
|
||||||
|
@ -99,7 +101,11 @@ public class FhirPathR5 implements IFhirPath {
|
||||||
boolean beforeContext,
|
boolean beforeContext,
|
||||||
boolean explicitConstant)
|
boolean explicitConstant)
|
||||||
throws PathEngineException {
|
throws PathEngineException {
|
||||||
return null;
|
|
||||||
|
return Collections.unmodifiableList(
|
||||||
|
theEvaluationContext.resolveConstant(appContext, name, beforeContext).stream()
|
||||||
|
.map(Base.class::cast)
|
||||||
|
.collect(Collectors.toList()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
Loading…
Reference in New Issue