Address review changes

This commit is contained in:
jamesagnew 2020-04-07 19:07:07 -04:00
parent 47786660d5
commit edc30568f2
37 changed files with 1781 additions and 161 deletions

View File

@ -68,13 +68,13 @@ import com.google.common.collect.Lists;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import javax.annotation.Nonnull;
import javax.persistence.criteria.From;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
@ -988,14 +988,14 @@ class PredicateBuilderReference extends BasePredicateBuilder {
return retVal;
}
@NotNull
@Nonnull
private InvalidRequestException newInvalidTargetTypeForChainException(String theResourceName, String theParamName, String theTypeValue) {
String searchParamName = theResourceName + ":" + theParamName;
String msg = myContext.getLocalizer().getMessage(PredicateBuilderReference.class, "invalidTargetTypeForChain", theTypeValue, searchParamName);
return new InvalidRequestException(msg);
}
@NotNull
@Nonnull
private InvalidRequestException newInvalidResourceTypeException(String theResourceType) {
String msg = myContext.getLocalizer().getMessageSanitized(PredicateBuilderReference.class, "invalidResourceType", theResourceType);
throw new InvalidRequestException(msg);

View File

@ -22,6 +22,7 @@ package ca.uhn.fhir.jpa.provider;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.model.util.ProviderConstants;
import ca.uhn.fhir.jpa.subscription.triggering.ISubscriptionTriggeringSvc;
import ca.uhn.fhir.model.dstu2.valueset.ResourceTypeEnum;
import ca.uhn.fhir.rest.annotation.IdParam;
@ -45,8 +46,8 @@ public class SubscriptionTriggeringProvider implements IResourceProvider {
@Operation(name = JpaConstants.OPERATION_TRIGGER_SUBSCRIPTION)
public IBaseParameters triggerSubscription(
@OperationParam(name = JpaConstants.SUBSCRIPTION_TRIGGERING_PARAM_RESOURCE_ID, min = 0, max = OperationParam.MAX_UNLIMITED, typeName = "uri") List<IPrimitiveType<String>> theResourceIds,
@OperationParam(name = JpaConstants.SUBSCRIPTION_TRIGGERING_PARAM_SEARCH_URL, min = 0, max = OperationParam.MAX_UNLIMITED, typeName = "string") List<IPrimitiveType<String>> theSearchUrls
@OperationParam(name = ProviderConstants.SUBSCRIPTION_TRIGGERING_PARAM_RESOURCE_ID, min = 0, max = OperationParam.MAX_UNLIMITED, typeName = "uri") List<IPrimitiveType<String>> theResourceIds,
@OperationParam(name = ProviderConstants.SUBSCRIPTION_TRIGGERING_PARAM_SEARCH_URL, min = 0, max = OperationParam.MAX_UNLIMITED, typeName = "string") List<IPrimitiveType<String>> theSearchUrls
) {
return mySubscriptionTriggeringSvc.triggerSubscription(theResourceIds, theSearchUrls, null);
}
@ -54,8 +55,8 @@ public class SubscriptionTriggeringProvider implements IResourceProvider {
@Operation(name = JpaConstants.OPERATION_TRIGGER_SUBSCRIPTION)
public IBaseParameters triggerSubscription(
@IdParam IIdType theSubscriptionId,
@OperationParam(name = JpaConstants.SUBSCRIPTION_TRIGGERING_PARAM_RESOURCE_ID, min = 0, max = OperationParam.MAX_UNLIMITED, typeName = "uri") List<IPrimitiveType<String>> theResourceIds,
@OperationParam(name = JpaConstants.SUBSCRIPTION_TRIGGERING_PARAM_SEARCH_URL, min = 0, max = OperationParam.MAX_UNLIMITED, typeName = "string") List<IPrimitiveType<String>> theSearchUrls
@OperationParam(name = ProviderConstants.SUBSCRIPTION_TRIGGERING_PARAM_RESOURCE_ID, min = 0, max = OperationParam.MAX_UNLIMITED, typeName = "uri") List<IPrimitiveType<String>> theResourceIds,
@OperationParam(name = ProviderConstants.SUBSCRIPTION_TRIGGERING_PARAM_SEARCH_URL, min = 0, max = OperationParam.MAX_UNLIMITED, typeName = "string") List<IPrimitiveType<String>> theSearchUrls
) {
return mySubscriptionTriggeringSvc.triggerSubscription(theResourceIds, theSearchUrls, theSubscriptionId);
}

View File

@ -27,7 +27,6 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.Sets;
import org.apache.commons.lang3.Validate;
import org.jetbrains.annotations.NotNull;
import org.quartz.*;
import org.quartz.impl.JobDetailImpl;
import org.quartz.impl.StdSchedulerFactory;
@ -35,6 +34,7 @@ import org.quartz.impl.matchers.GroupMatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
@ -85,7 +85,7 @@ public abstract class BaseHapiScheduler implements IHapiScheduler {
addProperty("org.quartz.threadPool.threadNamePrefix", getThreadPrefix());
}
@NotNull
@Nonnull
private String getThreadPrefix() {
return myThreadNamePrefix + "-" + myInstanceName;
}

View File

@ -47,7 +47,6 @@ import ca.uhn.fhir.rest.api.server.SimplePreResourceShowDetails;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import com.google.common.annotations.VisibleForTesting;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@ -256,7 +255,7 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
template.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(@NotNull TransactionStatus theStatus) {
protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) {
boolean entityLoaded = ensureSearchEntityLoaded();
assert entityLoaded;
}

View File

@ -46,8 +46,6 @@ import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@Scope("prototype")
@Component
public class PersistedJpaSearchFirstPageBundleProvider extends PersistedJpaBundleProvider {
private static final Logger ourLog = LoggerFactory.getLogger(PersistedJpaSearchFirstPageBundleProvider.class);
private SearchTask mySearchTask;

View File

@ -26,7 +26,6 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
import ca.uhn.fhir.util.TestUtil;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.jetbrains.annotations.NotNull;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
@ -42,6 +41,7 @@ import org.springframework.data.domain.Pageable;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import javax.annotation.Nonnull;
import javax.persistence.EntityManager;
import java.util.ArrayList;
import java.util.Collection;
@ -493,7 +493,7 @@ public class SearchCoordinatorSvcImplTest {
myExpectedNumberOfSearchBuildersCreated = 3;
}
@NotNull
@Nonnull
private PersistedJpaBundleProvider newPersistedJpaBundleProvider(String theUuid) {
PersistedJpaBundleProvider provider;
provider = new PersistedJpaBundleProvider(null, theUuid);

View File

@ -4,6 +4,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.model.sched.ISchedulerService;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.model.util.ProviderConstants;
import ca.uhn.fhir.jpa.provider.dstu3.BaseResourceProviderDstu3Test;
import ca.uhn.fhir.jpa.subscription.SubscriptionTestUtil;
import ca.uhn.fhir.jpa.subscription.triggering.SubscriptionTriggeringSvcImpl;
@ -180,7 +181,7 @@ public class SubscriptionTriggeringDstu3Test extends BaseResourceProviderDstu3Te
.operation()
.onInstance(subscriptionId)
.named(JpaConstants.OPERATION_TRIGGER_SUBSCRIPTION)
.withParameter(Parameters.class, JpaConstants.SUBSCRIPTION_TRIGGERING_PARAM_RESOURCE_ID, new UriType(obsId.toUnqualifiedVersionless().getValue()))
.withParameter(Parameters.class, ProviderConstants.SUBSCRIPTION_TRIGGERING_PARAM_RESOURCE_ID, new UriType(obsId.toUnqualifiedVersionless().getValue()))
.execute();
String responseValue = response.getParameter().get(0).getValue().primitiveValue();
@ -229,8 +230,8 @@ public class SubscriptionTriggeringDstu3Test extends BaseResourceProviderDstu3Te
.operation()
.onInstance(sub1id)
.named(JpaConstants.OPERATION_TRIGGER_SUBSCRIPTION)
.withParameter(Parameters.class, JpaConstants.SUBSCRIPTION_TRIGGERING_PARAM_SEARCH_URL, new StringType("Observation?"))
.andParameter(JpaConstants.SUBSCRIPTION_TRIGGERING_PARAM_RESOURCE_ID, new UriType("Observation/O2"))
.withParameter(Parameters.class, ProviderConstants.SUBSCRIPTION_TRIGGERING_PARAM_SEARCH_URL, new StringType("Observation?"))
.andParameter(ProviderConstants.SUBSCRIPTION_TRIGGERING_PARAM_RESOURCE_ID, new UriType("Observation/O2"))
.execute();
String responseValue = response.getParameter().get(0).getValue().primitiveValue();
assertThat(responseValue, containsString("Subscription triggering job submitted as JOB ID"));
@ -239,7 +240,7 @@ public class SubscriptionTriggeringDstu3Test extends BaseResourceProviderDstu3Te
.operation()
.onInstance(sub2id)
.named(JpaConstants.OPERATION_TRIGGER_SUBSCRIPTION)
.withParameter(Parameters.class, JpaConstants.SUBSCRIPTION_TRIGGERING_PARAM_SEARCH_URL, new StringType("Patient?"))
.withParameter(Parameters.class, ProviderConstants.SUBSCRIPTION_TRIGGERING_PARAM_SEARCH_URL, new StringType("Patient?"))
.execute();
responseValue = response.getParameter().get(0).getValue().primitiveValue();
assertThat(responseValue, containsString("Subscription triggering job submitted as JOB ID"));
@ -278,9 +279,9 @@ public class SubscriptionTriggeringDstu3Test extends BaseResourceProviderDstu3Te
.operation()
.onInstance(sub2id)
.named(JpaConstants.OPERATION_TRIGGER_SUBSCRIPTION)
.withParameter(Parameters.class, JpaConstants.SUBSCRIPTION_TRIGGERING_PARAM_SEARCH_URL, new StringType("Patient?_id=P0"))
.andParameter(JpaConstants.SUBSCRIPTION_TRIGGERING_PARAM_SEARCH_URL, new StringType("Patient?_id=P1"))
.andParameter(JpaConstants.SUBSCRIPTION_TRIGGERING_PARAM_SEARCH_URL, new StringType("Patient?_id=P2"))
.withParameter(Parameters.class, ProviderConstants.SUBSCRIPTION_TRIGGERING_PARAM_SEARCH_URL, new StringType("Patient?_id=P0"))
.andParameter(ProviderConstants.SUBSCRIPTION_TRIGGERING_PARAM_SEARCH_URL, new StringType("Patient?_id=P1"))
.andParameter(ProviderConstants.SUBSCRIPTION_TRIGGERING_PARAM_SEARCH_URL, new StringType("Patient?_id=P2"))
.execute();
String responseValue = response.getParameter().get(0).getValue().primitiveValue();
assertThat(responseValue, containsString("Subscription triggering job submitted as JOB ID"));
@ -322,7 +323,7 @@ public class SubscriptionTriggeringDstu3Test extends BaseResourceProviderDstu3Te
.operation()
.onInstance(sub2id)
.named(JpaConstants.OPERATION_TRIGGER_SUBSCRIPTION)
.withParameter(Parameters.class, JpaConstants.SUBSCRIPTION_TRIGGERING_PARAM_SEARCH_URL, new StringType("Patient?_id=P0,P1,P2"))
.withParameter(Parameters.class, ProviderConstants.SUBSCRIPTION_TRIGGERING_PARAM_SEARCH_URL, new StringType("Patient?_id=P0,P1,P2"))
.execute();
String responseValue = response.getParameter().get(0).getValue().primitiveValue();
assertThat(responseValue, containsString("Subscription triggering job submitted as JOB ID"));
@ -366,7 +367,7 @@ public class SubscriptionTriggeringDstu3Test extends BaseResourceProviderDstu3Te
.operation()
.onInstance(sub1id)
.named(JpaConstants.OPERATION_TRIGGER_SUBSCRIPTION)
.withParameter(Parameters.class, JpaConstants.SUBSCRIPTION_TRIGGERING_PARAM_SEARCH_URL, new StringType("Observation?_count=10"))
.withParameter(Parameters.class, ProviderConstants.SUBSCRIPTION_TRIGGERING_PARAM_SEARCH_URL, new StringType("Observation?_count=10"))
.execute();
String responseValue = response.getParameter().get(0).getValue().primitiveValue();
assertThat(responseValue, containsString("Subscription triggering job submitted as JOB ID"));
@ -375,7 +376,7 @@ public class SubscriptionTriggeringDstu3Test extends BaseResourceProviderDstu3Te
.operation()
.onInstance(sub2id)
.named(JpaConstants.OPERATION_TRIGGER_SUBSCRIPTION)
.withParameter(Parameters.class, JpaConstants.SUBSCRIPTION_TRIGGERING_PARAM_SEARCH_URL, new StringType("Patient?_count=16"))
.withParameter(Parameters.class, ProviderConstants.SUBSCRIPTION_TRIGGERING_PARAM_SEARCH_URL, new StringType("Patient?_count=16"))
.execute();
responseValue = response.getParameter().get(0).getValue().primitiveValue();
assertThat(responseValue, containsString("Subscription triggering job submitted as JOB ID"));
@ -398,7 +399,7 @@ public class SubscriptionTriggeringDstu3Test extends BaseResourceProviderDstu3Te
.operation()
.onType(Subscription.class)
.named(JpaConstants.OPERATION_TRIGGER_SUBSCRIPTION)
.withParameter(Parameters.class, JpaConstants.SUBSCRIPTION_TRIGGERING_PARAM_SEARCH_URL, new StringType("Observation"))
.withParameter(Parameters.class, ProviderConstants.SUBSCRIPTION_TRIGGERING_PARAM_SEARCH_URL, new StringType("Observation"))
.execute();
fail();
} catch (InvalidRequestException e) {
@ -432,7 +433,7 @@ public class SubscriptionTriggeringDstu3Test extends BaseResourceProviderDstu3Te
.operation()
.onType(Subscription.class)
.named(JpaConstants.OPERATION_TRIGGER_SUBSCRIPTION)
.withParameter(Parameters.class, JpaConstants.SUBSCRIPTION_TRIGGERING_PARAM_SEARCH_URL, new StringType("Observation?"))
.withParameter(Parameters.class, ProviderConstants.SUBSCRIPTION_TRIGGERING_PARAM_SEARCH_URL, new StringType("Observation?"))
.execute();
String responseValue = response.getParameter().get(0).getValue().primitiveValue();
assertThat(responseValue, containsString("Subscription triggering job submitted as JOB ID"));
@ -469,7 +470,7 @@ public class SubscriptionTriggeringDstu3Test extends BaseResourceProviderDstu3Te
.operation()
.onInstance(subscriptionId)
.named(JpaConstants.OPERATION_TRIGGER_SUBSCRIPTION)
.withParameter(Parameters.class, JpaConstants.SUBSCRIPTION_TRIGGERING_PARAM_RESOURCE_ID, new UriType(obsId.toUnqualifiedVersionless().getValue()))
.withParameter(Parameters.class, ProviderConstants.SUBSCRIPTION_TRIGGERING_PARAM_RESOURCE_ID, new UriType(obsId.toUnqualifiedVersionless().getValue()))
.execute();
String responseValue = response.getParameter().get(0).getValue().primitiveValue();

View File

@ -2,10 +2,10 @@ package ca.uhn.fhir.jpa.migrate;
import ca.uhn.fhir.jpa.migrate.taskdef.BaseTask;
import ca.uhn.fhir.jpa.migrate.taskdef.DropIndexTask;
import org.jetbrains.annotations.NotNull;
import org.junit.Before;
import org.junit.Test;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@ -108,8 +108,8 @@ public class MigrationTaskSkipperTest {
assertThat(taskVersions, equalTo(expectedVersions));
}
@NotNull
private Stream<String> integersToVersions(Integer[] theVersions) {
@Nonnull
private Stream<String> integersToVersions(Integer[] theVersions) {
return Stream.of(theVersions).map(s -> VERSION_PREFIX + s);
}
}

View File

@ -24,9 +24,6 @@ import ca.uhn.fhir.rest.api.Constants;
public class JpaConstants {
public static final String SUBSCRIPTION_TRIGGERING_PARAM_RESOURCE_ID = "resourceId";
public static final String SUBSCRIPTION_TRIGGERING_PARAM_SEARCH_URL = "searchUrl";
/**
* Non-instantiable
*/

View File

@ -0,0 +1,6 @@
package ca.uhn.fhir.jpa.model.util;
public class ProviderConstants {
public static final String SUBSCRIPTION_TRIGGERING_PARAM_RESOURCE_ID = "resourceId";
public static final String SUBSCRIPTION_TRIGGERING_PARAM_SEARCH_URL = "searchUrl";
}

View File

@ -38,7 +38,7 @@ public interface IChannelFactory {
* @param theMessageType The object type that will be placed on this queue. Objects will be Jackson-annotated structures.
* @param theConfig Contains the configuration for subscribers. Note that this parameter is provided for
* both {@link #getOrCreateReceiver} and
* {@link #getOrCreateSender(String, Class, ChannelConsumerOptions)}
* {@link #getOrCreateProducer(String, Class, ChannelConsumerOptions)}
* even though this object is used to configure the sender only. We do this because the factory
* may want to create a single object to be used for both the sender and receiver, so this allows
* the config details to be known regardless of which method is returned first.
@ -57,11 +57,11 @@ public interface IChannelFactory {
* @param theMessageType The object type that will be placed on this queue. Objects will be Jackson-annotated structures.
* @param theConfig Contains the configuration for subscribers. Note that this parameter is provided for
* both {@link #getOrCreateReceiver} and
* {@link #getOrCreateSender(String, Class, ChannelConsumerOptions)}
* {@link #getOrCreateProducer(String, Class, ChannelConsumerOptions)}
* even though this object is used to configure the sender only. We do this because the factory
* may want to create a single object to be used for both the sender and receiver, so this allows
* the config details to be known regardless of which method is returned first.
*/
IChannelProducer getOrCreateSender(String theChannelName, Class<?> theMessageType, ChannelConsumerOptions theConfig);
IChannelProducer getOrCreateProducer(String theChannelName, Class<?> theMessageType, ChannelConsumerOptions theConfig);
}

View File

@ -30,6 +30,7 @@ import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.PreDestroy;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@ -42,8 +43,8 @@ import java.util.concurrent.TimeUnit;
public class LinkedBlockingChannelFactory implements IChannelFactory {
private Map<String, LinkedBlockingChannel> myChannels = Collections.synchronizedMap(new HashMap<>());
private static final Logger ourLog = LoggerFactory.getLogger(LinkedBlockingChannelFactory.class);
private Map<String, LinkedBlockingChannel> myChannels = Collections.synchronizedMap(new HashMap<>());
/**
* Constructor
@ -58,7 +59,7 @@ public class LinkedBlockingChannelFactory implements IChannelFactory {
}
@Override
public IChannelProducer getOrCreateSender(String theChannelName, Class<?> theMessageType, ChannelConsumerOptions theConfig) {
public IChannelProducer getOrCreateProducer(String theChannelName, Class<?> theMessageType, ChannelConsumerOptions theConfig) {
return getOrCreateChannel(theChannelName, theConfig.getConcurrentConsumers());
}
@ -99,4 +100,10 @@ public class LinkedBlockingChannelFactory implements IChannelFactory {
});
}
@PreDestroy
public void stop() {
myChannels.clear();
}
}

View File

@ -49,7 +49,7 @@ public class SubscriptionChannelFactory {
public IChannelProducer newDeliverySendingChannel(String theChannelName, ChannelConsumerOptions theOptions) {
ChannelConsumerOptions config = newConfigForDeliveryChannel(theOptions);
return myQueueChannelFactory.getOrCreateSender(theChannelName, ResourceDeliveryJsonMessage.class, config);
return myQueueChannelFactory.getOrCreateProducer(theChannelName, ResourceDeliveryJsonMessage.class, config);
}
public IChannelReceiver newDeliveryReceivingChannel(String theChannelName, ChannelConsumerOptions theOptions) {
@ -60,7 +60,7 @@ public class SubscriptionChannelFactory {
public IChannelProducer newMatchingSendingChannel(String theChannelName, ChannelConsumerOptions theOptions) {
ChannelConsumerOptions config = newConfigForMatchingChannel(theOptions);
return myQueueChannelFactory.getOrCreateSender(theChannelName, ResourceModifiedJsonMessage.class, config);
return myQueueChannelFactory.getOrCreateProducer(theChannelName, ResourceModifiedJsonMessage.class, config);
}
public IChannelReceiver newMatchingReceivingChannel(String theChannelName, ChannelConsumerOptions theOptions) {

View File

@ -36,19 +36,19 @@ public class SubscriptionDeliveryHandlerFactory {
@Autowired
private ApplicationContext myApplicationContext;
protected SubscriptionDeliveringEmailSubscriber getSubscriptionDeliveringEmailSubscriber(IEmailSender theEmailSender) {
protected SubscriptionDeliveringEmailSubscriber newSubscriptionDeliveringEmailSubscriber(IEmailSender theEmailSender) {
return myApplicationContext.getBean(SubscriptionDeliveringEmailSubscriber.class, theEmailSender);
}
protected SubscriptionDeliveringRestHookSubscriber getSubscriptionDeliveringRestHookSubscriber() {
protected SubscriptionDeliveringRestHookSubscriber newSubscriptionDeliveringRestHookSubscriber() {
return myApplicationContext.getBean(SubscriptionDeliveringRestHookSubscriber.class);
}
public Optional<MessageHandler> createDeliveryHandler(CanonicalSubscriptionChannelType theChannelType) {
if (theChannelType == CanonicalSubscriptionChannelType.EMAIL) {
return Optional.of(getSubscriptionDeliveringEmailSubscriber(myEmailSender));
return Optional.of(newSubscriptionDeliveringEmailSubscriber(myEmailSender));
} else if (theChannelType == CanonicalSubscriptionChannelType.RESTHOOK) {
return Optional.of(getSubscriptionDeliveringRestHookSubscriber());
return Optional.of(newSubscriptionDeliveringRestHookSubscriber());
} else {
return Optional.empty();
}

View File

@ -24,8 +24,6 @@ import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionChannelRegi
import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionDeliveryChannelNamer;
import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionDeliveryHandlerFactory;
import ca.uhn.fhir.jpa.subscription.model.config.SubscriptionModelConfig;
import ca.uhn.fhir.jpa.subscription.match.deliver.DaoResourceRetriever;
import ca.uhn.fhir.jpa.subscription.match.deliver.IResourceRetriever;
import ca.uhn.fhir.jpa.subscription.match.deliver.email.IEmailSender;
import ca.uhn.fhir.jpa.subscription.match.deliver.email.SubscriptionDeliveringEmailSubscriber;
import ca.uhn.fhir.jpa.subscription.match.deliver.resthook.SubscriptionDeliveringRestHookSubscriber;
@ -88,11 +86,6 @@ public class SubscriptionProcessorConfig {
return new DaoSubscriptionProvider();
}
@Bean
public IResourceRetriever resourceRetriever() {
return new DaoResourceRetriever();
}
@Bean
public SubscriptionLoader subscriptionLoader() {
return new SubscriptionLoader();

View File

@ -1,64 +0,0 @@
package ca.uhn.fhir.jpa.subscription.match.deliver;
/*-
* #%L
* HAPI FHIR Subscription Server
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
* 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%
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.subscription.match.registry.ActiveSubscription;
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
public class DaoResourceRetriever implements IResourceRetriever {
private static final Logger ourLog = LoggerFactory.getLogger(ActiveSubscription.class);
/**
* Constructor
*/
public DaoResourceRetriever() {
super();
}
/**
* Constructor
*/
public DaoResourceRetriever(FhirContext theFhirContext, DaoRegistry theDaoRegistry) {
myFhirContext = theFhirContext;
myDaoRegistry = theDaoRegistry;
}
@Autowired
private FhirContext myFhirContext;
@Autowired
private DaoRegistry myDaoRegistry;
@Override
public IBaseResource getResource(IIdType payloadId) throws ResourceGoneException {
RuntimeResourceDefinition resourceDef = myFhirContext.getResourceDefinition(payloadId.getResourceType());
IFhirResourceDao dao = myDaoRegistry.getResourceDao(resourceDef.getImplementingClass());
return dao.read(payloadId.toVersionless());
}
}

View File

@ -1,28 +0,0 @@
package ca.uhn.fhir.jpa.subscription.match.deliver;
/*-
* #%L
* HAPI FHIR Subscription Server
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
* 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%
*/
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
public interface IResourceRetriever {
IBaseResource getResource(IIdType theId);
}

View File

@ -37,7 +37,6 @@ import java.util.List;
import static org.apache.commons.lang3.StringUtils.*;
@Scope("prototype")
public class SubscriptionDeliveringEmailSubscriber extends BaseSubscriptionDeliverySubscriber {
private Logger ourLog = LoggerFactory.getLogger(SubscriptionDeliveringEmailSubscriber.class);

View File

@ -20,15 +20,22 @@ package ca.uhn.fhir.jpa.subscription.match.deliver.resthook;
* #L%
*/
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryMessage;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.subscription.match.deliver.BaseSubscriptionDeliverySubscriber;
import ca.uhn.fhir.jpa.subscription.match.deliver.IResourceRetriever;
import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription;
import ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryMessage;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.client.api.*;
import ca.uhn.fhir.rest.client.api.Header;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.api.IHttpClient;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.api.IHttpResponse;
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
import ca.uhn.fhir.rest.client.interceptor.SimpleRequestHeaderInterceptor;
import ca.uhn.fhir.rest.gclient.IClientExecutable;
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
@ -43,16 +50,23 @@ import org.springframework.context.annotation.Scope;
import org.springframework.messaging.MessagingException;
import java.io.IOException;
import java.util.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
@Scope("prototype")
public class SubscriptionDeliveringRestHookSubscriber extends BaseSubscriptionDeliverySubscriber {
@Autowired
IResourceRetriever myResourceRetriever;
private DaoRegistry myDaoRegistry;
private Logger ourLog = LoggerFactory.getLogger(SubscriptionDeliveringRestHookSubscriber.class);
/**
* Constructor
*/
@ -115,6 +129,13 @@ public class SubscriptionDeliveringRestHookSubscriber extends BaseSubscriptionDe
}
}
public IBaseResource getResource(IIdType payloadId) throws ResourceGoneException {
RuntimeResourceDefinition resourceDef = myFhirContext.getResourceDefinition(payloadId.getResourceType());
IFhirResourceDao dao = myDaoRegistry.getResourceDao(resourceDef.getImplementingClass());
return dao.read(payloadId.toVersionless());
}
protected IBaseResource getAndMassagePayload(ResourceDeliveryMessage theMsg, CanonicalSubscription theSubscription) {
IBaseResource payloadResource = theMsg.getPayload(myFhirContext);
@ -123,7 +144,7 @@ public class SubscriptionDeliveringRestHookSubscriber extends BaseSubscriptionDe
try {
if (payloadId != null) {
payloadResource = myResourceRetriever.getResource(payloadId.toVersionless());
payloadResource = getResource(payloadId.toVersionless());
} else {
return null;
}

View File

@ -93,14 +93,9 @@ public class SubscriptionLoader {
syncSubscriptions();
}
public static class Job implements HapiJob {
@Autowired
private SubscriptionLoader myTarget;
@Override
public void execute(JobExecutionContext theContext) {
myTarget.syncSubscriptions();
}
@VisibleForTesting
public void acquireSemaphoreForUnitTest() throws InterruptedException {
mySyncSubscriptionsSemaphore.acquire();
}
@VisibleForTesting
@ -161,5 +156,15 @@ public class SubscriptionLoader {
}
}
public static class Job implements HapiJob {
@Autowired
private SubscriptionLoader myTarget;
@Override
public void execute(JobExecutionContext theContext) {
myTarget.syncSubscriptions();
}
}
}

View File

@ -74,7 +74,7 @@ import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static ca.uhn.fhir.jpa.model.util.JpaConstants.SUBSCRIPTION_TRIGGERING_PARAM_RESOURCE_ID;
import static ca.uhn.fhir.jpa.model.util.ProviderConstants.SUBSCRIPTION_TRIGGERING_PARAM_RESOURCE_ID;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;

View File

@ -0,0 +1,83 @@
package ca.uhn.fhir.jpa.subscription.module;
import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionChannelRegistry;
import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionRegistry;
import ca.uhn.fhir.jpa.subscription.module.config.TestSubscriptionDstu3Config;
import ca.uhn.fhir.util.StopWatch;
import org.hl7.fhir.dstu3.model.Subscription;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import java.util.List;
import java.util.stream.Collectors;
import static org.awaitility.Awaitility.await;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
@ContextConfiguration(classes = {TestSubscriptionDstu3Config.class})
public abstract class BaseSubscriptionDstu3Test extends BaseSubscriptionTest {
@Autowired
protected SubscriptionRegistry mySubscriptionRegistry;
@Autowired
protected SubscriptionChannelRegistry mySubscriptionChannelRegistry;
private final SubscriptionTestHelper mySubscriptionTestHelper = new SubscriptionTestHelper();
public static void waitForSize(int theTarget, List<?> theList) {
StopWatch sw = new StopWatch();
while (theList.size() != theTarget && sw.getMillis() <= 16000) {
try {
Thread.sleep(50);
} catch (InterruptedException theE) {
throw new Error(theE);
}
}
if (sw.getMillis() >= 16000) {
String describeResults = theList
.stream()
.map(t -> {
if (t == null) {
return "null";
}
if (t instanceof IBaseResource) {
return ((IBaseResource) t).getIdElement().getValue();
}
return t.toString();
})
.collect(Collectors.joining(", "));
fail("Size " + theList.size() + " is != target " + theTarget + " - Got: " + describeResults);
}
}
protected long nextId() {
return mySubscriptionTestHelper.nextId();
}
protected Subscription makeActiveSubscription(String theCriteria, String thePayload, String theEndpoint) {
return mySubscriptionTestHelper.makeActiveSubscription(theCriteria, thePayload, theEndpoint);
}
protected Subscription makeSubscriptionWithStatus(String theCriteria, String thePayload, String theEndpoint, Subscription.SubscriptionStatus status) {
return mySubscriptionTestHelper.makeSubscriptionWithStatus(theCriteria, thePayload, theEndpoint, status);
}
protected void clearRegistry() {
mySubscriptionRegistry.unregisterAllSubscriptions();
await().until(this::registryEmpty);
}
private boolean registryEmpty() {
return mySubscriptionRegistry.size() == 0 && mySubscriptionChannelRegistry.size() == 0;
}
protected void assertRegistrySize(int theSize) {
assertRegistrySize(theSize, theSize);
}
protected void assertRegistrySize(int theSubscriptionRegistrySize, int theSubscriptionChannelRegistrySize) {
assertEquals(theSubscriptionRegistrySize, mySubscriptionRegistry.size());
assertEquals(theSubscriptionChannelRegistrySize, mySubscriptionChannelRegistry.size());
}
}

View File

@ -0,0 +1,74 @@
package ca.uhn.fhir.jpa.subscription.module;
import ca.uhn.fhir.interceptor.api.IInterceptorService;
import ca.uhn.fhir.interceptor.executor.InterceptorService;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.searchparam.config.SearchParamConfig;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.jpa.subscription.channel.impl.LinkedBlockingChannelFactory;
import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionChannelFactory;
import ca.uhn.fhir.jpa.subscription.match.config.SubscriptionProcessorConfig;
import ca.uhn.fhir.jpa.subscription.module.config.MockFhirClientSearchParamProvider;
import ca.uhn.fhir.jpa.subscription.module.config.MockFhirClientSubscriptionProvider;
import ca.uhn.fhir.jpa.subscription.module.config.TestSubscriptionConfig;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import org.junit.After;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {
SearchParamConfig.class,
SubscriptionProcessorConfig.class,
TestSubscriptionConfig.class,
BaseSubscriptionTest.MyConfig.class
})
public abstract class BaseSubscriptionTest {
@Autowired
protected IInterceptorService myInterceptorRegistry;
@Autowired
MockFhirClientSubscriptionProvider myMockFhirClientSubscriptionProvider;
@Autowired
ISearchParamRegistry mySearchParamRegistry;
@Autowired
MockFhirClientSearchParamProvider myMockFhirClientSearchParamProvider;
@After
public void afterClearAnonymousLambdas() {
myInterceptorRegistry.unregisterAllInterceptors();
}
public void initSearchParamRegistry(IBundleProvider theBundleProvider) {
myMockFhirClientSearchParamProvider.setBundleProvider(theBundleProvider);
mySearchParamRegistry.forceRefresh();
}
@Configuration
public static class MyConfig {
@Bean
public DaoConfig daoConfig() {
return new DaoConfig();
}
@Bean
public SubscriptionChannelFactory mySubscriptionChannelFactory() {
return new SubscriptionChannelFactory(new LinkedBlockingChannelFactory());
}
@Bean
public IInterceptorService interceptorService() {
return new InterceptorService();
}
}
}

View File

@ -0,0 +1,33 @@
package ca.uhn.fhir.jpa.subscription.module.cache;
import ca.uhn.fhir.jpa.subscription.module.BaseSubscriptionDstu3Test;
import org.hl7.fhir.dstu3.model.Subscription;
import org.junit.After;
public abstract class BaseSubscriptionRegistryTest extends BaseSubscriptionDstu3Test {
public static final String SUBSCRIPTION_ID = "1";
public static final String ORIG_CRITERIA = "Patient?";
public static final String NEW_CRITERIA = "Observation?";
@After
public void clearRegistryAfter() {
super.clearRegistry();
}
protected Subscription createSubscription() {
Subscription subscription = new Subscription();
subscription.setId(SUBSCRIPTION_ID);
subscription.setCriteria(ORIG_CRITERIA);
subscription.setStatus(Subscription.SubscriptionStatus.ACTIVE);
setChannel(subscription, Subscription.SubscriptionChannelType.RESTHOOK);
return subscription;
}
protected void setChannel(Subscription theSubscription, Subscription.SubscriptionChannelType theResthook) {
Subscription.SubscriptionChannelComponent channel = new Subscription.SubscriptionChannelComponent();
channel.setType(theResthook);
channel.setPayload("application/json");
channel.setEndpoint("http://unused.test.endpoint/");
theSubscription.setChannel(channel);
}
}

View File

@ -0,0 +1,71 @@
package ca.uhn.fhir.jpa.subscription.module.cache;
import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionLoader;
import ca.uhn.fhir.jpa.subscription.module.config.MockFhirClientSubscriptionProvider;
import ca.uhn.fhir.jpa.subscription.module.standalone.BaseBlockingQueueSubscribableChannelDstu3Test;
import org.hl7.fhir.dstu3.model.Subscription;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertEquals;
public class SubscriptionLoaderTest extends BaseBlockingQueueSubscribableChannelDstu3Test {
private static final int MOCK_FHIR_CLIENT_FAILURES = 3;
@Autowired
private MockFhirClientSubscriptionProvider myMockFhirClientSubscriptionProvider;
@Before
public void setFailCount() {
myMockFhirClientSubscriptionProvider.setFailCount(MOCK_FHIR_CLIENT_FAILURES);
}
@After
public void restoreFailCount() {
myMockFhirClientSubscriptionProvider.setFailCount(0);
}
@Test
@Ignore
public void testSubscriptionLoaderFhirClientDown() throws Exception {
String payload = "application/fhir+json";
String criteria1 = "Observation?code=SNOMED-CT|" + myCode + "&_format=xml";
String criteria2 = "Observation?code=SNOMED-CT|" + myCode + "111&_format=xml";
List<Subscription> subs = new ArrayList<>();
subs.add(makeActiveSubscription(criteria1, payload, ourListenerServerBase));
subs.add(makeActiveSubscription(criteria2, payload, ourListenerServerBase));
mySubscriptionActivatedPost.setExpectedCount(2);
initSubscriptionLoader(subs, "uuid");
mySubscriptionActivatedPost.awaitExpected();
assertEquals(0, myMockFhirClientSubscriptionProvider.getFailCount());
}
@Test
public void testMultipleThreadsDontBlock() throws InterruptedException {
SubscriptionLoader svc = new SubscriptionLoader();
CountDownLatch latch = new CountDownLatch(1);
new Thread(()->{
try {
svc.acquireSemaphoreForUnitTest();
latch.countDown();
} catch (InterruptedException theE) {
// ignore
}
}).start();
latch.await(10, TimeUnit.SECONDS);
svc.syncSubscriptions();
}
}

View File

@ -0,0 +1,50 @@
package ca.uhn.fhir.jpa.subscription.module.cache;
import ca.uhn.fhir.jpa.subscription.channel.subscription.ISubscriptionDeliveryChannelNamer;
import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription;
import org.hl7.fhir.dstu3.model.Subscription;
import org.junit.Test;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
@ContextConfiguration(classes = {
SubscriptionRegistrySharedTest.SpringConfig.class
})
public class SubscriptionRegistrySharedTest extends BaseSubscriptionRegistryTest {
private static final String OTHER_ID = "OTHER_ID";
@Configuration
public static class SpringConfig {
@Primary
@Bean
ISubscriptionDeliveryChannelNamer subscriptionDeliveryChannelNamer() {
return new SharedNamer();
}
private class SharedNamer implements ISubscriptionDeliveryChannelNamer {
@Override
public String nameFromSubscription(CanonicalSubscription theCanonicalSubscription) {
return "shared";
}
}
}
@Test
public void testTwoSubscriptionsOneChannel() {
Subscription subscription = createSubscription();
assertRegistrySize(0);
mySubscriptionRegistry.registerSubscriptionUnlessAlreadyRegistered(subscription);
assertRegistrySize(1);
Subscription otherSubscription = createSubscription();
otherSubscription.setId(OTHER_ID);
mySubscriptionRegistry.registerSubscriptionUnlessAlreadyRegistered(otherSubscription);
assertRegistrySize(2, 1);
}
}

View File

@ -0,0 +1,60 @@
package ca.uhn.fhir.jpa.subscription.module.cache;
import ca.uhn.fhir.jpa.subscription.match.registry.ActiveSubscription;
import org.hl7.fhir.dstu3.model.Subscription;
import org.junit.Test;
import static org.junit.Assert.*;
public class SubscriptionRegistryTest extends BaseSubscriptionRegistryTest {
@Test
public void updateSubscriptionReusesActiveSubscription() {
Subscription subscription = createSubscription();
assertRegistrySize(0);
mySubscriptionRegistry.registerSubscriptionUnlessAlreadyRegistered(subscription);
assertRegistrySize(1);
ActiveSubscription origActiveSubscription = mySubscriptionRegistry.get(SUBSCRIPTION_ID);
assertEquals(ORIG_CRITERIA, origActiveSubscription.getCriteriaString());
subscription.setCriteria(NEW_CRITERIA);
assertEquals(ORIG_CRITERIA, origActiveSubscription.getCriteriaString());
mySubscriptionRegistry.registerSubscriptionUnlessAlreadyRegistered(subscription);
assertRegistrySize(1);
ActiveSubscription newActiveSubscription = mySubscriptionRegistry.get(SUBSCRIPTION_ID);
assertEquals(NEW_CRITERIA, newActiveSubscription.getCriteriaString());
// The same object
assertTrue(newActiveSubscription == origActiveSubscription);
}
@Test
public void updateSubscriptionDoesntReusesActiveSubscriptionWhenChannelChanges() {
Subscription subscription = createSubscription();
assertRegistrySize(0);
mySubscriptionRegistry.registerSubscriptionUnlessAlreadyRegistered(subscription);
assertRegistrySize(1);
ActiveSubscription origActiveSubscription = mySubscriptionRegistry.get(SUBSCRIPTION_ID);
assertEquals(ORIG_CRITERIA, origActiveSubscription.getCriteriaString());
setChannel(subscription, Subscription.SubscriptionChannelType.EMAIL);
assertEquals(ORIG_CRITERIA, origActiveSubscription.getCriteriaString());
mySubscriptionRegistry.registerSubscriptionUnlessAlreadyRegistered(subscription);
assertRegistrySize(1);
ActiveSubscription newActiveSubscription = mySubscriptionRegistry.get(SUBSCRIPTION_ID);
// A new object
assertFalse(newActiveSubscription == origActiveSubscription);
}
@Test
public void updateRemove() {
Subscription subscription = createSubscription();
assertRegistrySize(0);
mySubscriptionRegistry.registerSubscriptionUnlessAlreadyRegistered(subscription);
assertRegistrySize(1);
mySubscriptionRegistry.unregisterSubscriptionIfRegistered(subscription.getId());
assertRegistrySize(0);
}
}

View File

@ -0,0 +1,33 @@
package ca.uhn.fhir.jpa.subscription.module.config;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamProvider;
import ca.uhn.fhir.jpa.searchparam.registry.SearchParamRegistryImpl;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import org.springframework.beans.factory.annotation.Autowired;
public class MockFhirClientSearchParamProvider implements ISearchParamProvider {
private final MockProvider myMockProvider = new MockProvider();
@Autowired
private SearchParamRegistryImpl mySearchParamRegistry;
public MockFhirClientSearchParamProvider() {
super();
}
public void setBundleProvider(IBundleProvider theBundleProvider) { myMockProvider.setBundleProvider(theBundleProvider); }
public void setFailCount(int theFailCount) { myMockProvider.setFailCount(theFailCount); }
public int getFailCount() { return myMockProvider.getFailCount(); }
@Override
public IBundleProvider search(SearchParameterMap theParams) { return myMockProvider.search(theParams); }
@Override
public int refreshCache(SearchParamRegistryImpl theSearchParamRegistry, long theRefreshInterval) {
mySearchParamRegistry.doRefresh(0);
return 0;
}
}

View File

@ -0,0 +1,26 @@
package ca.uhn.fhir.jpa.subscription.module.config;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.subscription.match.registry.ISubscriptionProvider;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import org.hl7.fhir.instance.model.api.IBaseResource;
public class MockFhirClientSubscriptionProvider implements ISubscriptionProvider {
private final MockProvider myMockProvider = new MockProvider();
public MockFhirClientSubscriptionProvider() {
super();
}
public void setBundleProvider(IBundleProvider theBundleProvider) { myMockProvider.setBundleProvider(theBundleProvider); }
public void setFailCount(int theFailCount) { myMockProvider.setFailCount(theFailCount); }
public int getFailCount() { return myMockProvider.getFailCount(); }
@Override
public IBundleProvider search(SearchParameterMap theParams) { return myMockProvider.search(theParams); }
@Override
public boolean loadSubscription(IBaseResource theResource) {
throw new UnsupportedOperationException();
}
}

View File

@ -0,0 +1,31 @@
package ca.uhn.fhir.jpa.subscription.module.config;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.server.SimpleBundleProvider;
public class MockProvider {
private IBundleProvider myBundleProvider = new SimpleBundleProvider();
private int myFailCount = 0;
public void setBundleProvider(IBundleProvider theBundleProvider) {
myBundleProvider = theBundleProvider;
}
public IBundleProvider search(SearchParameterMap theParams) {
if (myFailCount > 0) {
--myFailCount;
throw new RuntimeException("Mock Search Failed");
}
return myBundleProvider;
}
public void setFailCount(int theFailCount) {
myFailCount = theFailCount;
}
public int getFailCount() {
return myFailCount;
}
}

View File

@ -0,0 +1,34 @@
package ca.uhn.fhir.jpa.subscription.module.config;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.subscription.match.matcher.matching.ISubscriptionMatcher;
import ca.uhn.fhir.jpa.subscription.match.matcher.matching.InMemorySubscriptionMatcher;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import org.mockito.Mockito;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.TestPropertySource;
@Configuration
@TestPropertySource(properties = {
"scheduling_disabled=true"
})
public class TestSubscriptionConfig {
@Bean
public ModelConfig modelConfig() {
return new ModelConfig();
}
@Bean
public IGenericClient fhirClient(FhirContext theFhirContext) {
return Mockito.mock(IGenericClient.class);
};
@Bean
public InMemorySubscriptionMatcher inMemorySubscriptionMatcher() {
return new InMemorySubscriptionMatcher();
}
}

View File

@ -0,0 +1,52 @@
package ca.uhn.fhir.jpa.subscription.module.config;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.model.sched.ISchedulerService;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamProvider;
import ca.uhn.fhir.jpa.subscription.match.registry.ISubscriptionProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary;
import static org.mockito.Mockito.mock;
@Configuration
@Import(TestSubscriptionConfig.class)
public class TestSubscriptionDstu3Config {
@Bean
public FhirContext fhirContext() {
return FhirContext.forDstu3();
}
@Bean
public IValidationSupport validationSupport() {
return FhirContext.forDstu3().getValidationSupport();
}
@Bean
@Primary
public ISearchParamProvider searchParamProvider() {
return new MockFhirClientSearchParamProvider();
}
@Bean
@Primary
public ISubscriptionProvider subscriptionProvider() {
return new MockFhirClientSubscriptionProvider();
}
@Bean
public ISchedulerService schedulerService() {
return mock(ISchedulerService.class);
}
@Bean
public DaoRegistry daoRegistry() {
return new DaoRegistry();
}
}

View File

@ -0,0 +1,649 @@
package ca.uhn.fhir.jpa.subscription.module.matcher;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.searchparam.matcher.InMemoryMatchResult;
import ca.uhn.fhir.jpa.searchparam.matcher.SearchParamMatcher;
import ca.uhn.fhir.jpa.subscription.match.matcher.matching.SubscriptionMatchingStrategy;
import ca.uhn.fhir.jpa.subscription.match.matcher.matching.SubscriptionStrategyEvaluator;
import ca.uhn.fhir.jpa.subscription.module.BaseSubscriptionDstu3Test;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.server.SimpleBundleProvider;
import ca.uhn.fhir.util.UrlUtil;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.dstu3.model.codesystems.MedicationRequestCategory;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.After;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class InMemorySubscriptionMatcherR3Test extends BaseSubscriptionDstu3Test {
@Autowired
SubscriptionStrategyEvaluator mySubscriptionStrategyEvaluator;
@Autowired
SearchParamMatcher mySearchParamMatcher;
@Autowired
ModelConfig myModelConfig;
@Autowired
FhirContext myFhirContext;
private void assertUnsupported(IBaseResource resource, String criteria) {
assertFalse(mySearchParamMatcher.match(criteria, resource, null).supported());
assertEquals(SubscriptionMatchingStrategy.DATABASE, mySubscriptionStrategyEvaluator.determineStrategy(criteria));
}
private void assertMatched(IBaseResource resource, String criteria) {
InMemoryMatchResult result = mySearchParamMatcher.match(criteria, resource, null);
assertTrue(result.supported());
assertTrue(result.matched());
assertEquals(SubscriptionMatchingStrategy.IN_MEMORY, mySubscriptionStrategyEvaluator.determineStrategy(criteria));
}
private void assertNotMatched(IBaseResource resource, String criteria) {
assertNotMatched(resource, criteria, SubscriptionMatchingStrategy.IN_MEMORY);
}
private void assertNotMatched(IBaseResource resource, String criteria, SubscriptionMatchingStrategy theSubscriptionMatchingStrategy) {
InMemoryMatchResult result = mySearchParamMatcher.match(criteria, resource, null);
assertTrue(result.supported());
assertFalse(result.matched());
assertEquals(theSubscriptionMatchingStrategy, mySubscriptionStrategyEvaluator.determineStrategy(criteria));
}
@After
public void after() {
myModelConfig.setTreatBaseUrlsAsLocal(new ModelConfig().getTreatBaseUrlsAsLocal());
}
/**
* Technically this is an invalid reference in most cases, but this shouldn't choke
* the matcher in the case that it gets used.
*/
@Test
public void testPlaceholderIdInReference() {
ProcedureRequest pr = new ProcedureRequest();
pr.setId("ProcedureRequest/123");
pr.setIntent(ProcedureRequest.ProcedureRequestIntent.ORIGINALORDER);
pr.setSubject(new Reference("urn:uuid:aaaaaaaaaa"));
assertMatched(pr, "ProcedureRequest?intent=original-order");
assertNotMatched(pr, "ProcedureRequest?subject=Patient/123");
pr.setSubject(new Reference("Foo/123"));
assertMatched(pr, "ProcedureRequest?intent=original-order");
assertNotMatched(pr, "ProcedureRequest?subject=Patient/123");
pr.setSubject(new Reference("Patient/"));
assertMatched(pr, "ProcedureRequest?intent=original-order");
assertNotMatched(pr, "ProcedureRequest?subject=Patient/123");
}
@Test
public void testResourceById() {
ProcedureRequest pr = new ProcedureRequest();
pr.setId("ProcedureRequest/123");
pr.setIntent(ProcedureRequest.ProcedureRequestIntent.ORIGINALORDER);
assertMatched(pr, "ProcedureRequest?_id=123");
assertMatched(pr, "ProcedureRequest?_id=ProcedureRequest/123");
assertMatched(pr, "ProcedureRequest?_id=ProcedureRequest/123,ProcedureRequest/999");
assertMatched(pr, "ProcedureRequest?_id=ProcedureRequest/123&_id=ProcedureRequest/123");
assertNotMatched(pr, "ProcedureRequest?_id=ProcedureRequest/888");
assertNotMatched(pr, "ProcedureRequest?_id=ProcedureRequest/888,ProcedureRequest/999");
assertNotMatched(pr, "ProcedureRequest?_id=ProcedureRequest/123&_id=ProcedureRequest/888");
}
/*
The following tests are copied from an e-mail from a site using HAPI FHIR
*/
@Test
public void testQuestionnaireResponse() {
String criteria = "QuestionnaireResponse?questionnaire=HomeAbsenceHospitalizationRecord,ARIncenterAbsRecord";
{
QuestionnaireResponse qr = new QuestionnaireResponse();
qr.getQuestionnaire().setReference("Questionnaire/HomeAbsenceHospitalizationRecord");
assertMatched(qr, criteria);
}
{
QuestionnaireResponse qr = new QuestionnaireResponse();
qr.getQuestionnaire().setReference("Questionnaire/Other");
assertNotMatched(qr, criteria);
}
{
QuestionnaireResponse qr = new QuestionnaireResponse();
qr.getQuestionnaire().setDisplay("Questionnaire/HomeAbsenceHospitalizationRecord");
assertNotMatched(qr, criteria);
}
}
@Test
public void testCommunicationRequest() {
String criteria = "CommunicationRequest?occurrence==2018-10-17";
{
CommunicationRequest cr = new CommunicationRequest();
cr.setOccurrence(new DateTimeType("2018-10-17"));
assertMatched(cr, criteria);
}
{
CommunicationRequest cr = new CommunicationRequest();
cr.setOccurrence(new DateTimeType("2018-10-16"));
assertNotMatched(cr, criteria);
}
{
CommunicationRequest cr = new CommunicationRequest();
cr.setOccurrence(new DateTimeType("2018-10-16"));
assertNotMatched(cr, criteria);
}
}
@Test
public void testProcedureRequest() {
String criteria = "ProcedureRequest?intent=original-order";
{
ProcedureRequest pr = new ProcedureRequest();
pr.setIntent(ProcedureRequest.ProcedureRequestIntent.ORIGINALORDER);
assertMatched(pr, criteria);
}
{
ProcedureRequest pr = new ProcedureRequest();
pr.setIntent(ProcedureRequest.ProcedureRequestIntent.ORDER);
assertNotMatched(pr, criteria);
}
}
@Test
public void testObservationContextTypeUnsupported() {
String criteria = "Observation?code=17861-6&context.type=IHD";
{
Observation obs = new Observation();
obs.getCode().addCoding().setCode("XXX");
assertNotMatched(obs, criteria, SubscriptionMatchingStrategy.DATABASE);
}
{
Observation obs = new Observation();
obs.getCode().addCoding().setCode("17861-6");
assertUnsupported(obs, criteria);
}
}
// Check that it still fails fast even if the chained parameter is first
@Test
public void testObservationContextTypeUnsupportedReverse() {
String criteria = "Observation?context.type=IHD&code=17861-6";
{
Observation obs = new Observation();
obs.getCode().addCoding().setCode("XXX");
assertNotMatched(obs, criteria, SubscriptionMatchingStrategy.DATABASE);
}
{
Observation obs = new Observation();
obs.getCode().addCoding().setCode("17861-6");
assertUnsupported(obs, criteria);
}
}
@Test
public void medicationRequestOutpatient() {
// Note the date== evaluates to date=eq which is a legacy format supported by hapi fhir
String criteria = "MedicationRequest?intent=instance-order&category=outpatient&date==2018-10-19";
{
MedicationRequest mr = new MedicationRequest();
mr.setIntent(MedicationRequest.MedicationRequestIntent.INSTANCEORDER);
mr.getCategory().addCoding().setCode(MedicationRequestCategory.OUTPATIENT.toCode());
Dosage dosage = new Dosage();
Timing timing = new Timing();
timing.getEvent().add(new DateTimeType("2018-10-19"));
dosage.setTiming(timing);
mr.getDosageInstruction().add(dosage);
assertMatched(mr, criteria);
}
{
MedicationRequest mr = new MedicationRequest();
mr.setIntent(MedicationRequest.MedicationRequestIntent.INSTANCEORDER);
mr.getCategory().addCoding().setCode(MedicationRequestCategory.INPATIENT.toCode());
Dosage dosage = new Dosage();
Timing timing = new Timing();
timing.getEvent().add(new DateTimeType("2018-10-19"));
dosage.setTiming(timing);
mr.getDosageInstruction().add(dosage);
assertNotMatched(mr, criteria);
}
{
MedicationRequest mr = new MedicationRequest();
mr.setIntent(MedicationRequest.MedicationRequestIntent.INSTANCEORDER);
mr.getCategory().addCoding().setCode(MedicationRequestCategory.OUTPATIENT.toCode());
Dosage dosage = new Dosage();
Timing timing = new Timing();
timing.getEvent().add(new DateTimeType("2018-10-20"));
dosage.setTiming(timing);
mr.getDosageInstruction().add(dosage);
assertNotMatched(mr, criteria);
}
}
@Test
public void testMedicationRequestStatuses() {
String criteria = "MedicationRequest?intent=plan&category=outpatient&status=suspended,entered-in-error,cancelled,stopped";
// Note suspended is an invalid status and will never match
{
MedicationRequest mr = new MedicationRequest();
mr.setIntent(MedicationRequest.MedicationRequestIntent.PLAN);
mr.getCategory().addCoding().setCode(MedicationRequestCategory.OUTPATIENT.toCode());
mr.setStatus(MedicationRequest.MedicationRequestStatus.ENTEREDINERROR);
assertMatched(mr, criteria);
}
{
MedicationRequest mr = new MedicationRequest();
mr.setIntent(MedicationRequest.MedicationRequestIntent.PLAN);
mr.getCategory().addCoding().setCode(MedicationRequestCategory.OUTPATIENT.toCode());
mr.setStatus(MedicationRequest.MedicationRequestStatus.CANCELLED);
assertMatched(mr, criteria);
}
{
MedicationRequest mr = new MedicationRequest();
mr.setIntent(MedicationRequest.MedicationRequestIntent.PLAN);
mr.getCategory().addCoding().setCode(MedicationRequestCategory.OUTPATIENT.toCode());
mr.setStatus(MedicationRequest.MedicationRequestStatus.STOPPED);
assertMatched(mr, criteria);
}
{
MedicationRequest mr = new MedicationRequest();
mr.setIntent(MedicationRequest.MedicationRequestIntent.PLAN);
mr.getCategory().addCoding().setCode(MedicationRequestCategory.OUTPATIENT.toCode());
mr.setStatus(MedicationRequest.MedicationRequestStatus.ACTIVE);
assertNotMatched(mr, criteria);
}
}
@Test
public void testBloodTest() {
String criteria = "Observation?code=FR_Org1Blood2nd,FR_Org1Blood3rd,FR_Org%201BldCult,FR_Org2Blood2nd,FR_Org2Blood3rd,FR_Org%202BldCult,FR_Org3Blood2nd,FR_Org3Blood3rd,FR_Org3BldCult,FR_Org4Blood2nd,FR_Org4Blood3rd,FR_Org4BldCult,FR_Org5Blood2nd,FR_Org5Blood3rd,FR_Org%205BldCult,FR_Org6Blood2nd,FR_Org6Blood3rd,FR_Org6BldCult,FR_Org7Blood2nd,FR_Org7Blood3rd,FR_Org7BldCult,FR_Org8Blood2nd,FR_Org8Blood3rd,FR_Org8BldCult,FR_Org9Blood2nd,FR_Org9Blood3rd,FR_Org9BldCult,FR_Bld2ndCulture,FR_Bld3rdCulture,FR_Blood%20Culture,FR_Com1Bld3rd,FR_Com1BldCult,FR_Com2Bld2nd,FR_Com2Bld3rd,FR_Com2BldCult,FR_CultureBld2nd,FR_CultureBld3rd,FR_CultureBldCul,FR_GmStainBldCul,FR_GramStain2Bld,FR_GramStain3Bld,FR_GramStNegBac&context.type=IHD";
{
Observation obs = new Observation();
obs.getCode().addCoding().setCode("FR_Org1Blood2nd");
assertUnsupported(obs, criteria);
}
{
Observation obs = new Observation();
obs.getCode().addCoding().setCode("XXX");
assertNotMatched(obs, criteria, SubscriptionMatchingStrategy.DATABASE);
}
}
@Test
public void testProcedureHemodialysis() {
String criteria = "Procedure?category=Hemodialysis";
{
Procedure proc = new Procedure();
proc.getCategory().addCoding().setCode("Hemodialysis");
assertMatched(proc, criteria);
}
{
Procedure proc = new Procedure();
proc.getCategory().addCoding().setCode("XXX");
assertNotMatched(proc, criteria);
}
}
@Test
public void testProcedureHDStandard() {
String criteria = "Procedure?code=HD_Standard&status=completed&location=Lab123";
{
Procedure proc = new Procedure();
proc.getCode().addCoding().setCode("HD_Standard");
proc.setStatus(Procedure.ProcedureStatus.COMPLETED);
IIdType locId = new IdType("Location", "Lab123");
proc.getLocation().setReference(locId.getValue());
assertMatched(proc, criteria);
}
{
Procedure proc = new Procedure();
proc.getCode().addCoding().setCode("HD_Standard");
proc.setStatus(Procedure.ProcedureStatus.COMPLETED);
IIdType locId = new IdType("Location", "XXX");
proc.getLocation().setReference(locId.getValue());
assertNotMatched(proc, criteria);
}
{
Procedure proc = new Procedure();
proc.getCode().addCoding().setCode("XXX");
proc.setStatus(Procedure.ProcedureStatus.COMPLETED);
IIdType locId = new IdType("Location", "Lab123");
proc.getLocation().setReference(locId.getValue());
assertNotMatched(proc, criteria);
}
}
@Test
public void testProvenance() {
String criteria = "Provenance?activity=http://hl7.org/fhir/v3/DocumentCompletion%7CAU";
SearchParameter sp = new SearchParameter();
sp.addBase("Provenance");
sp.setCode("activity");
sp.setType(Enumerations.SearchParamType.TOKEN);
sp.setExpression("Provenance.activity");
sp.setXpathUsage(SearchParameter.XPathUsageType.NORMAL);
sp.setStatus(Enumerations.PublicationStatus.ACTIVE);
IBundleProvider bundle = new SimpleBundleProvider(Collections.singletonList(sp), "uuid");
initSearchParamRegistry(bundle);
{
Provenance prov = new Provenance();
prov.setActivity(new Coding().setSystem("http://hl7.org/fhir/v3/DocumentCompletion").setCode("AU"));
assertMatched(prov, criteria);
}
{
Provenance prov = new Provenance();
assertNotMatched(prov, criteria);
}
{
Provenance prov = new Provenance();
prov.setActivity(new Coding().setCode("XXX"));
assertNotMatched(prov, criteria);
}
}
@Test
public void testBodySite() {
String criteria = "BodySite?accessType=Catheter,PD%20Catheter";
SearchParameter sp = new SearchParameter();
sp.addBase("BodySite");
sp.setCode("accessType");
sp.setType(Enumerations.SearchParamType.TOKEN);
sp.setExpression("BodySite.extension('BodySite#accessType')");
sp.setXpathUsage(SearchParameter.XPathUsageType.NORMAL);
sp.setStatus(Enumerations.PublicationStatus.ACTIVE);
IBundleProvider bundle = new SimpleBundleProvider(Collections.singletonList(sp), "uuid");
initSearchParamRegistry(bundle);
{
BodySite bodySite = new BodySite();
bodySite.addExtension().setUrl("BodySite#accessType").setValue(new Coding().setCode("Catheter"));
assertMatched(bodySite, criteria);
}
{
BodySite bodySite = new BodySite();
bodySite.addExtension().setUrl("BodySite#accessType").setValue(new Coding().setCode("PD Catheter"));
assertMatched(bodySite, criteria);
}
{
BodySite bodySite = new BodySite();
assertNotMatched(bodySite, criteria);
}
{
BodySite bodySite = new BodySite();
bodySite.addExtension().setUrl("BodySite#accessType").setValue(new Coding().setCode("XXX"));
assertNotMatched(bodySite, criteria);
}
}
@Test
public void testProcedureAnyLocation() {
String criteria = "Procedure?code=HD_Standard&status=completed";
{
Procedure proc = new Procedure();
proc.getCode().addCoding().setCode("HD_Standard");
proc.setStatus(Procedure.ProcedureStatus.COMPLETED);
IIdType locId = new IdType("Location", "Lab456");
proc.getLocation().setReference(locId.getValue());
assertMatched(proc, criteria);
}
{
Procedure proc = new Procedure();
proc.getCode().addCoding().setCode("HD_Standard");
proc.setStatus(Procedure.ProcedureStatus.ABORTED);
assertNotMatched(proc, criteria);
}
{
Procedure proc = new Procedure();
proc.getCode().addCoding().setCode("XXX");
proc.setStatus(Procedure.ProcedureStatus.COMPLETED);
assertNotMatched(proc, criteria);
}
}
@Test
public void testQuestionnaireResponseLong() {
String criteria = "QuestionnaireResponse?questionnaire=HomeAbsenceHospitalizationRecord,ARIncenterAbsRecord,FMCSWDepressionSymptomsScreener,FMCAKIComprehensiveSW,FMCSWIntensiveScreener,FMCESRDComprehensiveSW,FMCNutritionProgressNote,FMCAKIComprehensiveRN";
{
QuestionnaireResponse qr = new QuestionnaireResponse();
qr.getQuestionnaire().setReference("Questionnaire/HomeAbsenceHospitalizationRecord");
assertMatched(qr, criteria);
}
{
QuestionnaireResponse qr = new QuestionnaireResponse();
qr.getQuestionnaire().setReference("Questionnaire/FMCSWIntensiveScreener");
assertMatched(qr, criteria);
}
{
QuestionnaireResponse qr = new QuestionnaireResponse();
qr.getQuestionnaire().setReference("Questionnaire/FMCAKIComprehensiveRN");
assertMatched(qr, criteria);
}
{
QuestionnaireResponse qr = new QuestionnaireResponse();
assertNotMatched(qr, criteria);
}
{
QuestionnaireResponse qr = new QuestionnaireResponse();
qr.getQuestionnaire().setReference("Questionnaire/FMCAKIComprehensiveRM");
assertNotMatched(qr, criteria);
}
}
@Test
public void testProcedureRequestCategory() {
String criteria = "ProcedureRequest?intent=instance-order&category=Laboratory,Ancillary%20Orders,Hemodialysis&occurrence==2018-10-19";
SearchParameter sp = new SearchParameter();
sp.addBase("ProcedureRequest");
sp.setCode("category");
sp.setType(Enumerations.SearchParamType.TOKEN);
sp.setExpression("ProcedureRequest.category");
sp.setXpathUsage(SearchParameter.XPathUsageType.NORMAL);
sp.setStatus(Enumerations.PublicationStatus.ACTIVE);
IBundleProvider bundle = new SimpleBundleProvider(Collections.singletonList(sp), "uuid");
initSearchParamRegistry(bundle);
{
ProcedureRequest pr = new ProcedureRequest();
pr.setIntent(ProcedureRequest.ProcedureRequestIntent.INSTANCEORDER);
CodeableConcept code = new CodeableConcept();
code.addCoding().setCode("Laboratory");
pr.getCategory().add(code);
pr.setOccurrence(new DateTimeType("2018-10-19"));
assertMatched(pr, criteria);
}
{
ProcedureRequest pr = new ProcedureRequest();
pr.setIntent(ProcedureRequest.ProcedureRequestIntent.INSTANCEORDER);
CodeableConcept code = new CodeableConcept();
code.addCoding().setCode("Ancillary Orders");
pr.getCategory().add(code);
pr.setOccurrence(new DateTimeType("2018-10-19"));
assertMatched(pr, criteria);
}
{
ProcedureRequest pr = new ProcedureRequest();
pr.setIntent(ProcedureRequest.ProcedureRequestIntent.INSTANCEORDER);
CodeableConcept code = new CodeableConcept();
code.addCoding().setCode("Hemodialysis");
pr.getCategory().add(code);
pr.setOccurrence(new DateTimeType("2018-10-19"));
assertMatched(pr, criteria);
}
{
ProcedureRequest pr = new ProcedureRequest();
pr.setIntent(ProcedureRequest.ProcedureRequestIntent.INSTANCEORDER);
pr.setOccurrence(new DateTimeType("2018-10-19"));
assertNotMatched(pr, criteria);
}
{
ProcedureRequest pr = new ProcedureRequest();
CodeableConcept code = new CodeableConcept();
code.addCoding().setCode("Hemodialysis");
pr.getCategory().add(code);
pr.setOccurrence(new DateTimeType("2018-10-19"));
assertNotMatched(pr, criteria);
}
{
ProcedureRequest pr = new ProcedureRequest();
pr.setIntent(ProcedureRequest.ProcedureRequestIntent.INSTANCEORDER);
CodeableConcept code = new CodeableConcept();
code.addCoding().setCode("Hemodialysis");
pr.getCategory().add(code);
assertNotMatched(pr, criteria);
}
{
ProcedureRequest pr = new ProcedureRequest();
pr.setIntent(ProcedureRequest.ProcedureRequestIntent.INSTANCEORDER);
CodeableConcept code = new CodeableConcept();
code.addCoding().setCode("XXX");
pr.getCategory().add(code);
pr.setOccurrence(new DateTimeType("2018-10-19"));
assertNotMatched(pr, criteria);
}
}
@Test
public void testEposideOfCare() {
String criteria = "EpisodeOfCare?status=active";
{
EpisodeOfCare eoc = new EpisodeOfCare();
eoc.setStatus(EpisodeOfCare.EpisodeOfCareStatus.ACTIVE);
assertMatched(eoc, criteria);
}
{
EpisodeOfCare eoc = new EpisodeOfCare();
assertNotMatched(eoc, criteria);
}
{
EpisodeOfCare eoc = new EpisodeOfCare();
eoc.setStatus(EpisodeOfCare.EpisodeOfCareStatus.CANCELLED);
assertNotMatched(eoc, criteria);
}
}
@Test
public void testCommunicationRequestWithRefAndDate() {
String criteria = "CommunicationRequest?requester=O1271,O1276&occurrence=ge2019-02-08T00:00:00-05:00&occurrence=le2019-02-09T00:00:00-05:00";
CommunicationRequest cr = new CommunicationRequest();
cr.getRequester().getAgent().setReference("Organization/O1276");
cr.setOccurrence(new DateTimeType("2019-02-08T00:01:00-05:00"));
assertMatched(cr, criteria);
}
@Test
public void testCommunicationRequestWithRef() {
String criteria = "CommunicationRequest?requester=O1271,O1276";
CommunicationRequest cr = new CommunicationRequest();
cr.getRequester().getAgent().setReference("Organization/O1276");
assertMatched(cr, criteria);
}
@Test
public void testSystemWithNullValue() {
String criteria = "Observation?code=17861-6";
Observation observation = new Observation();
CodeableConcept code = new CodeableConcept();
observation.getCode().addCoding().setSystem("http://loinc.org");
assertNotMatched(observation, criteria);
}
@Test
public void testNullSystemNotNullValue() {
String criteria = "Observation?code=17861-6";
Observation observation = new Observation();
CodeableConcept code = new CodeableConcept();
observation.getCode().addCoding().setCode("look ma no system");
assertNotMatched(observation, criteria);
}
@Test
public void testExternalReferenceMatches() {
String goodReference = "http://example.com/base/Organization/FOO";
String goodCriteria = "Patient?organization=" + UrlUtil.escapeUrlParam(goodReference);
String badReference1 = "http://example.com/bad/Organization/FOO";
String badCriteria1 = "Patient?organization=" + UrlUtil.escapeUrlParam(badReference1);
String badReference2 = "http://example.org/base/Organization/FOO";
String badCriteria2 = "Patient?organization=" + UrlUtil.escapeUrlParam(badReference2);
String badReference3 = "https://example.com/base/Organization/FOO";
String badCriteria3 = "Patient?organization=" + UrlUtil.escapeUrlParam(badReference3);
String badReference4 = "http://example.com/base/Organization/GOO";
String badCriteria4 = "Patient?organization=" + UrlUtil.escapeUrlParam(badReference4);
Set<String> urls = new HashSet<>();
urls.add("http://example.com/base/");
myModelConfig.setTreatBaseUrlsAsLocal(urls);
Patient patient = new Patient();
patient.getManagingOrganization().setReference("Organization/FOO");
assertMatched(patient, goodCriteria);
assertNotMatched(patient, badCriteria1);
assertNotMatched(patient, badCriteria2);
assertNotMatched(patient, badCriteria3);
assertNotMatched(patient, badCriteria4);
}
@Test
public void testLocationPositionNotSupported() {
Location loc = new Location();
double latitude = 30.0;
double longitude = 40.0;
Location.LocationPositionComponent position = new Location.LocationPositionComponent().setLatitude(latitude).setLongitude(longitude);
loc.setPosition(position);
double bigEnoughDistance = 100.0;
String badCriteria =
"Location?" +
Location.SP_NEAR + "=" + latitude + ":" + longitude +
"&" +
Location.SP_NEAR_DISTANCE + "=" + bigEnoughDistance + "|http://unitsofmeasure.org|km";
assertUnsupported(loc, badCriteria);
}
}

View File

@ -0,0 +1,55 @@
package ca.uhn.fhir.jpa.subscription.module.matcher;
import ca.uhn.fhir.jpa.subscription.match.matcher.matching.SubscriptionMatchingStrategy;
import ca.uhn.fhir.jpa.subscription.match.matcher.matching.SubscriptionStrategyEvaluator;
import ca.uhn.fhir.jpa.subscription.module.BaseSubscriptionDstu3Test;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.beans.factory.annotation.Autowired;
import static org.junit.Assert.assertEquals;
import static org.junit.matchers.JUnitMatchers.containsString;
public class SubscriptionStrategyEvaluatorTest extends BaseSubscriptionDstu3Test {
@Autowired
SubscriptionStrategyEvaluator mySubscriptionStrategyEvaluator;
@Rule
public ExpectedException exception = ExpectedException.none();
@Test
public void testInMemory() {
assertInMemory("Observation?");
assertInMemory("QuestionnaireResponse?questionnaire=HomeAbsenceHospitalizationRecord,ARIncenterAbsRecord");
assertInMemory("CommunicationRequest?occurrence==2018-10-17");
assertInMemory("ProcedureRequest?intent=original-order");
assertInMemory("MedicationRequest?intent=instance-order&category=outpatient&date==2018-10-19");
assertInMemory("MedicationRequest?intent=plan&category=outpatient&status=suspended,entered-in-error,cancelled,stopped");
assertDatabase("Observation?code=FR_Org1Blood2nd,FR_Org1Blood3rd,FR_Org%201BldCult,FR_Org2Blood2nd,FR_Org2Blood3rd,FR_Org%202BldCult,FR_Org3Blood2nd,FR_Org3Blood3rd,FR_Org3BldCult,FR_Org4Blood2nd,FR_Org4Blood3rd,FR_Org4BldCult,FR_Org5Blood2nd,FR_Org5Blood3rd,FR_Org%205BldCult,FR_Org6Blood2nd,FR_Org6Blood3rd,FR_Org6BldCult,FR_Org7Blood2nd,FR_Org7Blood3rd,FR_Org7BldCult,FR_Org8Blood2nd,FR_Org8Blood3rd,FR_Org8BldCult,FR_Org9Blood2nd,FR_Org9Blood3rd,FR_Org9BldCult,FR_Bld2ndCulture,FR_Bld3rdCulture,FR_Blood%20Culture,FR_Com1Bld3rd,FR_Com1BldCult,FR_Com2Bld2nd,FR_Com2Bld3rd,FR_Com2BldCult,FR_CultureBld2nd,FR_CultureBld3rd,FR_CultureBldCul,FR_GmStainBldCul,FR_GramStain2Bld,FR_GramStain3Bld,FR_GramStNegBac&context.type=IHD");
assertInMemory("Procedure?category=Hemodialysis");
assertInMemory("Procedure?code=HD_Standard&status=completed&location=Lab123");
assertInMemory("Procedure?code=HD_Standard&status=completed");
assertInMemory("QuestionnaireResponse?questionnaire=HomeAbsenceHospitalizationRecord,ARIncenterAbsRecord,FMCSWDepressionSymptomsScreener,FMCAKIComprehensiveSW,FMCSWIntensiveScreener,FMCESRDComprehensiveSW,FMCNutritionProgressNote,FMCAKIComprehensiveRN");
assertInMemory("EpisodeOfCare?status=active");
assertInMemory("Observation?code=111111111&_format=xml");
assertInMemory("Observation?code=SNOMED-CT|123&_format=xml");
assertDatabase("Observation?code=17861-6&context.type=IHD");
assertDatabase("Observation?context.type=IHD&code=17861-6");
exception.expect(InvalidRequestException.class);
exception.expectMessage(containsString("Resource type Observation does not have a parameter with name: codeee"));
assertInMemory("Observation?codeee=SNOMED-CT|123&_format=xml");
}
private void assertDatabase(String theCriteria) {
assertEquals(SubscriptionMatchingStrategy.DATABASE, mySubscriptionStrategyEvaluator.determineStrategy(theCriteria));
}
private void assertInMemory(String theCriteria) {
assertEquals(SubscriptionMatchingStrategy.IN_MEMORY, mySubscriptionStrategyEvaluator.determineStrategy(theCriteria));
}
}

View File

@ -0,0 +1,237 @@
package ca.uhn.fhir.jpa.subscription.module.standalone;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.interceptor.api.IInterceptorService;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.subscription.channel.api.ChannelConsumerOptions;
import ca.uhn.fhir.jpa.subscription.channel.subscription.ISubscriptionDeliveryChannelNamer;
import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionChannelFactory;
import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionLoader;
import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionRegistry;
import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription;
import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscriptionChannelType;
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedJsonMessage;
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage;
import ca.uhn.fhir.jpa.subscription.module.BaseSubscriptionDstu3Test;
import ca.uhn.fhir.jpa.subscription.module.config.MockFhirClientSubscriptionProvider;
import ca.uhn.fhir.jpa.subscription.module.subscriber.SubscriptionMatchingSubscriberTest;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.annotation.Create;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.annotation.Update;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.SimpleBundleProvider;
import ca.uhn.fhir.test.utilities.JettyUtil;
import ca.uhn.test.concurrency.IPointcutLatch;
import ca.uhn.test.concurrency.PointcutLatch;
import com.google.common.collect.Lists;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.dstu3.model.CodeableConcept;
import org.hl7.fhir.dstu3.model.Coding;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.dstu3.model.Observation;
import org.hl7.fhir.dstu3.model.Subscription;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.SubscribableChannel;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public abstract class BaseBlockingQueueSubscribableChannelDstu3Test extends BaseSubscriptionDstu3Test {
private static final Logger ourLog = LoggerFactory.getLogger(SubscriptionMatchingSubscriberTest.class);
public static final ChannelConsumerOptions CONSUMER_OPTIONS = new ChannelConsumerOptions().setConcurrentConsumers(1);
protected static ObservationListener ourObservationListener;
@Autowired
FhirContext myFhirContext;
// Caused by: java.lang.IllegalStateException: Unable to register mock bean org.springframework.messaging.MessageHandler expected a single matching bean to replace but found [subscriptionActivatingSubscriber, subscriptionDeliveringEmailSubscriber, subscriptionDeliveringRestHookSubscriber, subscriptionMatchingSubscriber, subscriptionRegisteringSubscriber]
@Autowired
@Qualifier("subscriptionActivatingSubscriber")
MessageHandler mySubscriptionActivatingSubscriber;
@Autowired
@Qualifier("subscriptionRegisteringSubscriber")
MessageHandler subscriptionRegisteringSubscriber;
@Autowired
@Qualifier("subscriptionMatchingSubscriber")
MessageHandler subscriptionMatchingSubscriber;
@Autowired
SubscriptionChannelFactory mySubscriptionChannelFactory;
@Autowired
IInterceptorService myInterceptorRegistry;
@Autowired
protected SubscriptionRegistry mySubscriptionRegistry;
@Autowired
private MockFhirClientSubscriptionProvider myMockFhirClientSubscriptionProvider;
@Autowired
private SubscriptionLoader mySubscriptionLoader;
@Autowired
private ISubscriptionDeliveryChannelNamer mySubscriptionDeliveryChannelNamer;
protected String myCode = "1000000050";
private static int ourListenerPort;
private static RestfulServer ourListenerRestServer;
private static Server ourListenerServer;
protected static String ourListenerServerBase;
protected static final List<Observation> ourCreatedObservations = Collections.synchronizedList(Lists.newArrayList());
protected static final List<Observation> ourUpdatedObservations = Collections.synchronizedList(Lists.newArrayList());
protected static final List<String> ourContentTypes = Collections.synchronizedList(new ArrayList<>());
private static SubscribableChannel ourSubscribableChannel;
protected final PointcutLatch mySubscriptionMatchingPost = new PointcutLatch(Pointcut.SUBSCRIPTION_AFTER_PERSISTED_RESOURCE_CHECKED);
protected final PointcutLatch mySubscriptionActivatedPost = new PointcutLatch(Pointcut.SUBSCRIPTION_AFTER_ACTIVE_SUBSCRIPTION_REGISTERED);
@Before
public void beforeReset() {
ourCreatedObservations.clear();
ourUpdatedObservations.clear();
ourContentTypes.clear();
CanonicalSubscription canonicalSubscription = new CanonicalSubscription();
canonicalSubscription.setIdElement(new IdDt("test"));
canonicalSubscription.setChannelType(CanonicalSubscriptionChannelType.RESTHOOK);
mySubscriptionRegistry.unregisterAllSubscriptions();
ourSubscribableChannel = mySubscriptionChannelFactory.newDeliveryReceivingChannel(mySubscriptionDeliveryChannelNamer.nameFromSubscription(canonicalSubscription), CONSUMER_OPTIONS);
ourSubscribableChannel.subscribe(mySubscriptionActivatingSubscriber);
ourSubscribableChannel.subscribe(subscriptionMatchingSubscriber);
ourSubscribableChannel.subscribe(subscriptionRegisteringSubscriber);
myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.SUBSCRIPTION_AFTER_PERSISTED_RESOURCE_CHECKED, mySubscriptionMatchingPost);
myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.SUBSCRIPTION_AFTER_ACTIVE_SUBSCRIPTION_REGISTERED, mySubscriptionActivatedPost);
}
@After
public void cleanup() {
myInterceptorRegistry.unregisterAllInterceptors();
mySubscriptionMatchingPost.clear();
mySubscriptionActivatedPost.clear();
ourObservationListener.clear();
super.clearRegistry();
}
public <T extends IBaseResource> T sendResource(T theResource) throws InterruptedException {
ResourceModifiedMessage msg = new ResourceModifiedMessage(myFhirContext, theResource, ResourceModifiedMessage.OperationTypeEnum.CREATE);
ResourceModifiedJsonMessage message = new ResourceModifiedJsonMessage(msg);
mySubscriptionMatchingPost.setExpectedCount(1);
ourSubscribableChannel.send(message);
mySubscriptionMatchingPost.awaitExpected();
return theResource;
}
protected void initSubscriptionLoader(List<Subscription> subscriptions, String uuid) throws InterruptedException {
myMockFhirClientSubscriptionProvider.setBundleProvider(new SimpleBundleProvider(new ArrayList<>(subscriptions), uuid));
mySubscriptionLoader.doSyncSubscriptionsForUnitTest();
}
protected Subscription sendSubscription(String theCriteria, String thePayload, String theEndpoint) throws InterruptedException {
Subscription subscription = makeActiveSubscription(theCriteria, thePayload, theEndpoint);
mySubscriptionActivatedPost.setExpectedCount(1);
Subscription retval = sendResource(subscription);
mySubscriptionActivatedPost.awaitExpected();
return retval;
}
protected Observation sendObservation(String code, String system) throws InterruptedException {
Observation observation = new Observation();
IdType id = new IdType("Observation", nextId());
observation.setId(id);
CodeableConcept codeableConcept = new CodeableConcept();
observation.setCode(codeableConcept);
Coding coding = codeableConcept.addCoding();
coding.setCode(code);
coding.setSystem(system);
observation.setStatus(Observation.ObservationStatus.FINAL);
return sendResource(observation);
}
@BeforeClass
public static void startListenerServer() throws Exception {
ourListenerRestServer = new RestfulServer(FhirContext.forDstu3());
ourObservationListener = new ObservationListener();
ourListenerRestServer.setResourceProviders(ourObservationListener);
ourListenerServer = new Server(0);
ServletContextHandler proxyHandler = new ServletContextHandler();
proxyHandler.setContextPath("/");
ServletHolder servletHolder = new ServletHolder();
servletHolder.setServlet(ourListenerRestServer);
proxyHandler.addServlet(servletHolder, "/fhir/context/*");
ourListenerServer.setHandler(proxyHandler);
JettyUtil.startServer(ourListenerServer);
ourListenerPort = JettyUtil.getPortForStartedServer(ourListenerServer);
ourListenerServerBase = "http://localhost:" + ourListenerPort + "/fhir/context";
FhirContext context = ourListenerRestServer.getFhirContext();
//Preload structure definitions so the load doesn't happen during the test (first load can be a little slow)
context.getValidationSupport().fetchAllStructureDefinitions();
}
@AfterClass
public static void stopListenerServer() throws Exception {
JettyUtil.closeServer(ourListenerServer);
}
public static class ObservationListener implements IResourceProvider, IPointcutLatch {
private final PointcutLatch updateLatch = new PointcutLatch("Observation Update");
@Create
public MethodOutcome create(@ResourceParam Observation theObservation, HttpServletRequest theRequest) {
ourLog.info("Received Listener Create");
ourContentTypes.add(theRequest.getHeader(ca.uhn.fhir.rest.api.Constants.HEADER_CONTENT_TYPE).replaceAll(";.*", ""));
ourCreatedObservations.add(theObservation);
return new MethodOutcome(new IdType("Observation/1"), true);
}
@Override
public Class<? extends IBaseResource> getResourceType() {
return Observation.class;
}
@Update
public MethodOutcome update(@ResourceParam Observation theObservation, HttpServletRequest theRequest) {
ourContentTypes.add(theRequest.getHeader(Constants.HEADER_CONTENT_TYPE).replaceAll(";.*", ""));
ourUpdatedObservations.add(theObservation);
updateLatch.invoke(null, new HookParams().add(Observation.class, theObservation));
ourLog.info("Received Listener Update (now have {} updates)", ourUpdatedObservations.size());
return new MethodOutcome(new IdType("Observation/1"), false);
}
@Override
public void setExpectedCount(int count) {
updateLatch.setExpectedCount(count);
}
@Override
public List<HookParams> awaitExpected() throws InterruptedException {
return updateLatch.awaitExpected();
}
@Override
public void clear() { updateLatch.clear();}
}
}

View File

@ -0,0 +1,119 @@
package ca.uhn.fhir.jpa.subscription.module.subscriber;
import ca.uhn.fhir.jpa.subscription.module.standalone.BaseBlockingQueueSubscribableChannelDstu3Test;
import ca.uhn.fhir.rest.api.Constants;
import org.hl7.fhir.dstu3.model.CodeableConcept;
import org.hl7.fhir.dstu3.model.Coding;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.dstu3.model.Observation;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.junit.Assert.assertEquals;
/**
* Tests copied from jpa.subscription.resthook.RestHookTestDstu3Test
*/
public class SubscriptionCheckingSubscriberTest extends BaseBlockingQueueSubscribableChannelDstu3Test {
private static final Logger ourLog = LoggerFactory.getLogger(SubscriptionCheckingSubscriberTest.class);
@Test
public void testRestHookSubscriptionApplicationFhirJson() throws Exception {
String payload = "application/fhir+json";
String code = "1000000050";
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
String criteria2 = "Observation?code=SNOMED-CT|" + code + "111&_format=xml";
sendSubscription(criteria1, payload, ourListenerServerBase);
sendSubscription(criteria2, payload, ourListenerServerBase);
assertEquals(2, mySubscriptionRegistry.size());
ourObservationListener.setExpectedCount(1);
sendObservation(code, "SNOMED-CT");
ourObservationListener.awaitExpected();
assertEquals(1, ourContentTypes.size());
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(0));
}
@Test
public void testRestHookSubscriptionApplicationXmlJson() throws Exception {
String payload = "application/fhir+xml";
String code = "1000000050";
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
String criteria2 = "Observation?code=SNOMED-CT|" + code + "111&_format=xml";
sendSubscription(criteria1, payload, ourListenerServerBase);
sendSubscription(criteria2, payload, ourListenerServerBase);
assertEquals(2, mySubscriptionRegistry.size());
ourObservationListener.setExpectedCount(1);
sendObservation(code, "SNOMED-CT");
ourObservationListener.awaitExpected();
assertEquals(1, ourContentTypes.size());
assertEquals(Constants.CT_FHIR_XML_NEW, ourContentTypes.get(0));
}
@Test
public void testRestHookSubscriptionWithoutPayload() throws Exception {
String payload = "";
String code = "1000000050";
String criteria1 = "Observation?code=SNOMED-CT|" + code;
String criteria2 = "Observation?code=SNOMED-CT|" + code + "111";
sendSubscription(criteria1, payload, ourListenerServerBase);
sendSubscription(criteria2, payload, ourListenerServerBase);
assertEquals(2, mySubscriptionRegistry.size());
ourObservationListener.setExpectedCount(0);
sendObservation(code, "SNOMED-CT");
ourObservationListener.clear();
assertEquals(0, ourContentTypes.size());
}
@Test
public void testReferenceWithDisplayOnly() throws Exception {
String payload = "application/fhir+json";
String code = "1000000050";
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
String criteria2 = "Observation?code=SNOMED-CT|" + code + "111&_format=xml";
sendSubscription(criteria1, payload, ourListenerServerBase);
sendSubscription(criteria2, payload, ourListenerServerBase);
assertEquals(2, mySubscriptionRegistry.size());
ourObservationListener.setExpectedCount(1);
Observation observation = new Observation();
IdType id = new IdType("Observation", nextId());
observation.setId(id);
// Reference has display only!
observation.getSubject().setDisplay("Mr Jones");
CodeableConcept codeableConcept = new CodeableConcept();
observation.setCode(codeableConcept);
Coding coding = codeableConcept.addCoding();
coding.setCode(code);
coding.setSystem("SNOMED-CT");
observation.setStatus(Observation.ObservationStatus.FINAL);
sendResource(observation);
ourObservationListener.awaitExpected();
assertEquals(1, ourContentTypes.size());
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(0));
}
}

View File

@ -0,0 +1,78 @@
package ca.uhn.fhir.jpa.subscription.module.subscriber;
import ca.uhn.fhir.jpa.subscription.module.standalone.BaseBlockingQueueSubscribableChannelDstu3Test;
import ca.uhn.fhir.rest.api.Constants;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.junit.Assert.assertEquals;
/**
* Tests copied from jpa.subscription.resthook.RestHookTestDstu3Test
*/
public class SubscriptionMatchingSubscriberTest extends BaseBlockingQueueSubscribableChannelDstu3Test {
private static final Logger ourLog = LoggerFactory.getLogger(SubscriptionMatchingSubscriberTest.class);
@Test
public void testRestHookSubscriptionApplicationFhirJson() throws Exception {
String payload = "application/fhir+json";
String code = "1000000050";
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
String criteria2 = "Observation?code=SNOMED-CT|" + code + "111&_format=xml";
sendSubscription(criteria1, payload, ourListenerServerBase);
sendSubscription(criteria2, payload, ourListenerServerBase);
assertEquals(2, mySubscriptionRegistry.size());
ourObservationListener.setExpectedCount(1);
sendObservation(code, "SNOMED-CT");
ourObservationListener.awaitExpected();
assertEquals(1, ourContentTypes.size());
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(0));
}
@Test
public void testRestHookSubscriptionApplicationXmlJson() throws Exception {
String payload = "application/fhir+xml";
String code = "1000000050";
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
String criteria2 = "Observation?code=SNOMED-CT|" + code + "111&_format=xml";
sendSubscription(criteria1, payload, ourListenerServerBase);
sendSubscription(criteria2, payload, ourListenerServerBase);
assertEquals(2, mySubscriptionRegistry.size());
ourObservationListener.setExpectedCount(1);
sendObservation(code, "SNOMED-CT");
ourObservationListener.awaitExpected();
assertEquals(1, ourContentTypes.size());
assertEquals(Constants.CT_FHIR_XML_NEW, ourContentTypes.get(0));
}
@Test
public void testRestHookSubscriptionWithoutPayload() throws Exception {
String payload = "";
String code = "1000000050";
String criteria1 = "Observation?code=SNOMED-CT|" + code;
String criteria2 = "Observation?code=SNOMED-CT|" + code + "111";
sendSubscription(criteria1, payload, ourListenerServerBase);
sendSubscription(criteria2, payload, ourListenerServerBase);
assertEquals(2, mySubscriptionRegistry.size());
ourObservationListener.setExpectedCount(0);
sendObservation(code, "SNOMED-CT");
ourObservationListener.clear();
assertEquals(0, ourContentTypes.size());
}
}