Allow multi-resource Subscription criteria (#3110)
* Allow start criteria subscriptions * Rework subscription tests * Subscription cleanup * Add changelog * Test fixes * Test fix
This commit is contained in:
parent
2cf8e74414
commit
9e057574a6
|
@ -276,6 +276,10 @@ public class Constants {
|
||||||
public static final String RESOURCE_PARTITION_ID = Constants.class.getName() + "_RESOURCE_PARTITION_ID";
|
public static final String RESOURCE_PARTITION_ID = Constants.class.getName() + "_RESOURCE_PARTITION_ID";
|
||||||
public static final String CT_APPLICATION_GZIP = "application/gzip";
|
public static final String CT_APPLICATION_GZIP = "application/gzip";
|
||||||
public static final String[] EMPTY_STRING_ARRAY = new String[0];
|
public static final String[] EMPTY_STRING_ARRAY = new String[0];
|
||||||
|
public static final String SUBSCRIPTION_MULTITYPE_PREFIX = "[";
|
||||||
|
public static final String SUBSCRIPTION_MULTITYPE_SUFFIX = "]";
|
||||||
|
public static final String SUBSCRIPTION_MULTITYPE_STAR = "*";
|
||||||
|
public static final String SUBSCRIPTION_STAR_CRITERIA = SUBSCRIPTION_MULTITYPE_PREFIX + SUBSCRIPTION_MULTITYPE_STAR + SUBSCRIPTION_MULTITYPE_SUFFIX;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
CHARSET_UTF8 = StandardCharsets.UTF_8;
|
CHARSET_UTF8 = StandardCharsets.UTF_8;
|
||||||
|
|
|
@ -1402,6 +1402,28 @@ public class FhirTerser {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clones a resource object, copying all data elements from theSource into a new copy of the same type.
|
||||||
|
*
|
||||||
|
* Note that:
|
||||||
|
* <ul>
|
||||||
|
* <li>Only FHIR data elements are copied (i.e. user data maps are not copied)</li>
|
||||||
|
* <li>If a class extending a HAPI FHIR type (e.g. an instance of a class extending the Patient class) is supplied, an instance of the base type will be returned.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @param theSource The source resource
|
||||||
|
* @return A copy of the source resource
|
||||||
|
* @since 5.6.0
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T extends IBaseResource> T clone(T theSource) {
|
||||||
|
Validate.notNull(theSource, "theSource must not be null");
|
||||||
|
T target = (T) myContext.getResourceDefinition(theSource).newInstance();
|
||||||
|
cloneInto(theSource, target, false);
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public enum OptionsEnum {
|
public enum OptionsEnum {
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
type: add
|
||||||
|
issue: 3110
|
||||||
|
title: "Subscription criteria in the HAPI FHIR JPA server now supports an optional alternate syntax of
|
||||||
|
`[*]` (all resources of all types) and `[resourcename,resourcename,...]` (all resources of the
|
||||||
|
given types. Note that no search parameters may be specitied with this syntax."
|
|
@ -53,7 +53,7 @@ public class FhirResourceDaoR4InvalidSubscriptionTest extends BaseJpaR4Test {
|
||||||
mySubscriptionDao.update(s);
|
mySubscriptionDao.update(s);
|
||||||
fail();
|
fail();
|
||||||
} catch (UnprocessableEntityException e) {
|
} catch (UnprocessableEntityException e) {
|
||||||
assertEquals("Subscription.criteria must be in the form \"{Resource Type}?[params]\"", e.getMessage());
|
assertEquals("Subscription.criteria contains invalid/unsupported resource type: FOO", e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,62 +1,58 @@
|
||||||
package ca.uhn.fhir.jpa.subscription;
|
package ca.uhn.fhir.jpa.subscription;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
|
||||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.provider.r4.BaseResourceProviderR4Test;
|
import ca.uhn.fhir.jpa.provider.r4.BaseResourceProviderR4Test;
|
||||||
import ca.uhn.fhir.jpa.subscription.channel.impl.LinkedBlockingChannel;
|
import ca.uhn.fhir.jpa.subscription.channel.impl.LinkedBlockingChannel;
|
||||||
import ca.uhn.fhir.jpa.subscription.submit.interceptor.SubscriptionMatcherInterceptor;
|
import ca.uhn.fhir.jpa.subscription.submit.interceptor.SubscriptionMatcherInterceptor;
|
||||||
import ca.uhn.fhir.rest.annotation.Create;
|
|
||||||
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
|
||||||
import ca.uhn.fhir.rest.annotation.Transaction;
|
|
||||||
import ca.uhn.fhir.rest.annotation.TransactionParam;
|
|
||||||
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.api.MethodOutcome;
|
||||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
import ca.uhn.fhir.test.utilities.server.HashMapResourceProviderExtension;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
||||||
import ca.uhn.fhir.test.utilities.JettyUtil;
|
import ca.uhn.fhir.test.utilities.server.TransactionCapturingProviderExtension;
|
||||||
import ca.uhn.fhir.util.BundleUtil;
|
import ca.uhn.fhir.util.BundleUtil;
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import net.ttddyy.dsproxy.QueryCount;
|
import net.ttddyy.dsproxy.QueryCount;
|
||||||
import net.ttddyy.dsproxy.listener.SingleQueryCountHolder;
|
import net.ttddyy.dsproxy.listener.SingleQueryCountHolder;
|
||||||
import org.eclipse.jetty.server.Server;
|
|
||||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
|
||||||
import org.eclipse.jetty.servlet.ServletHolder;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.hl7.fhir.r4.model.Bundle;
|
import org.hl7.fhir.r4.model.Bundle;
|
||||||
import org.hl7.fhir.r4.model.CodeableConcept;
|
import org.hl7.fhir.r4.model.CodeableConcept;
|
||||||
import org.hl7.fhir.r4.model.Coding;
|
import org.hl7.fhir.r4.model.Coding;
|
||||||
import org.hl7.fhir.r4.model.IdType;
|
|
||||||
import org.hl7.fhir.r4.model.Observation;
|
import org.hl7.fhir.r4.model.Observation;
|
||||||
|
import org.hl7.fhir.r4.model.Organization;
|
||||||
|
import org.hl7.fhir.r4.model.Patient;
|
||||||
import org.hl7.fhir.r4.model.Subscription;
|
import org.hl7.fhir.r4.model.Subscription;
|
||||||
import org.junit.jupiter.api.AfterAll;
|
import org.junit.jupiter.api.AfterAll;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Disabled;
|
import org.junit.jupiter.api.Order;
|
||||||
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Enumeration;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public abstract class BaseSubscriptionsR4Test extends BaseResourceProviderR4Test {
|
public abstract class BaseSubscriptionsR4Test extends BaseResourceProviderR4Test {
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseSubscriptionsR4Test.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseSubscriptionsR4Test.class);
|
||||||
protected static int ourListenerPort;
|
protected static int ourListenerPort;
|
||||||
protected static List<String> ourContentTypes = Collections.synchronizedList(new ArrayList<>());
|
|
||||||
protected static List<String> ourHeaders = Collections.synchronizedList(new ArrayList<>());
|
@Order(0)
|
||||||
protected static List<Bundle> ourTransactions = Collections.synchronizedList(Lists.newArrayList());
|
@RegisterExtension
|
||||||
protected static List<Observation> ourCreatedObservations = Collections.synchronizedList(Lists.newArrayList());
|
protected static RestfulServerExtension ourRestfulServer = new RestfulServerExtension(FhirContext.forR4Cached());
|
||||||
protected static List<Observation> ourUpdatedObservations = Collections.synchronizedList(Lists.newArrayList());
|
@Order(1)
|
||||||
private static Server ourListenerServer;
|
@RegisterExtension
|
||||||
private static SingleQueryCountHolder ourCountHolder;
|
protected static HashMapResourceProviderExtension<Patient> ourPatientProvider = new HashMapResourceProviderExtension<>(ourRestfulServer, Patient.class);
|
||||||
private static String ourListenerServerBase;
|
@Order(1)
|
||||||
|
@RegisterExtension
|
||||||
|
protected static HashMapResourceProviderExtension<Observation> ourObservationProvider = new HashMapResourceProviderExtension<>(ourRestfulServer, Observation.class);
|
||||||
|
@Order(1)
|
||||||
|
@RegisterExtension
|
||||||
|
protected static TransactionCapturingProviderExtension<Bundle> ourTransactionProvider = new TransactionCapturingProviderExtension<>(ourRestfulServer, Bundle.class);
|
||||||
|
protected static SingleQueryCountHolder ourCountHolder;
|
||||||
|
@Order(1)
|
||||||
|
@RegisterExtension
|
||||||
|
protected static HashMapResourceProviderExtension<Organization> ourOrganizationProvider = new HashMapResourceProviderExtension<>(ourRestfulServer, Organization.class);
|
||||||
@Autowired
|
@Autowired
|
||||||
protected SubscriptionTestUtil mySubscriptionTestUtil;
|
protected SubscriptionTestUtil mySubscriptionTestUtil;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -92,12 +88,6 @@ public abstract class BaseSubscriptionsR4Test extends BaseResourceProviderR4Test
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void beforeReset() throws Exception {
|
public void beforeReset() throws Exception {
|
||||||
ourCreatedObservations.clear();
|
|
||||||
ourUpdatedObservations.clear();
|
|
||||||
ourTransactions.clear();
|
|
||||||
ourContentTypes.clear();
|
|
||||||
ourHeaders.clear();
|
|
||||||
|
|
||||||
// Delete all Subscriptions
|
// Delete all Subscriptions
|
||||||
if (myClient != null) {
|
if (myClient != null) {
|
||||||
Bundle allSubscriptions = myClient.search().forResource(Subscription.class).returnBundle(Bundle.class).execute();
|
Bundle allSubscriptions = myClient.search().forResource(Subscription.class).returnBundle(Bundle.class).execute();
|
||||||
|
@ -137,7 +127,7 @@ public abstract class BaseSubscriptionsR4Test extends BaseResourceProviderR4Test
|
||||||
Subscription.SubscriptionChannelComponent channel = subscription.getChannel();
|
Subscription.SubscriptionChannelComponent channel = subscription.getChannel();
|
||||||
channel.setType(Subscription.SubscriptionChannelType.RESTHOOK);
|
channel.setType(Subscription.SubscriptionChannelType.RESTHOOK);
|
||||||
channel.setPayload(thePayload);
|
channel.setPayload(thePayload);
|
||||||
channel.setEndpoint(ourListenerServerBase);
|
channel.setEndpoint(ourRestfulServer.getBaseUrl());
|
||||||
return subscription;
|
return subscription;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,55 +159,24 @@ public abstract class BaseSubscriptionsR4Test extends BaseResourceProviderR4Test
|
||||||
return observation;
|
return observation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Patient sendPatient() {
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.setActive(true);
|
||||||
|
|
||||||
public static class ObservationResourceProvider implements IResourceProvider {
|
IIdType id = myPatientDao.create(patient).getId();
|
||||||
|
patient.setId(id);
|
||||||
@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);
|
|
||||||
extractHeaders(theRequest);
|
|
||||||
return new MethodOutcome(new IdType("Observation/1"), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void extractHeaders(HttpServletRequest theRequest) {
|
|
||||||
java.util.Enumeration<String> headerNamesEnum = theRequest.getHeaderNames();
|
|
||||||
while (headerNamesEnum.hasMoreElements()) {
|
|
||||||
String nextName = headerNamesEnum.nextElement();
|
|
||||||
Enumeration<String> valueEnum = theRequest.getHeaders(nextName);
|
|
||||||
while (valueEnum.hasMoreElements()) {
|
|
||||||
String nextValue = valueEnum.nextElement();
|
|
||||||
ourHeaders.add(nextName + ": " + nextValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class<? extends IBaseResource> getResourceType() {
|
|
||||||
return Observation.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Update
|
|
||||||
public MethodOutcome update(@ResourceParam Observation theObservation, HttpServletRequest theRequest) {
|
|
||||||
ourLog.info("Received Listener Update");
|
|
||||||
ourContentTypes.add(theRequest.getHeader(Constants.HEADER_CONTENT_TYPE).replaceAll(";.*", ""));
|
|
||||||
ourUpdatedObservations.add(theObservation);
|
|
||||||
extractHeaders(theRequest);
|
|
||||||
return new MethodOutcome(new IdType("Observation/1"), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
return patient;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class PlainProvider {
|
protected Organization sendOrganization() {
|
||||||
|
Organization org = new Organization();
|
||||||
|
org.setName("ORG");
|
||||||
|
|
||||||
@Transaction
|
IIdType id = myOrganizationDao.create(org).getId();
|
||||||
public Bundle transaction(@TransactionParam Bundle theInput) {
|
org.setId(id);
|
||||||
ourLog.info("Received transaction update");
|
|
||||||
ourTransactions.add(theInput);
|
|
||||||
return theInput;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
return org;
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterAll
|
@AfterAll
|
||||||
|
@ -229,31 +188,4 @@ public abstract class BaseSubscriptionsR4Test extends BaseResourceProviderR4Test
|
||||||
return ourCountHolder.getQueryCountMap().get("");
|
return ourCountHolder.getQueryCountMap().get("");
|
||||||
}
|
}
|
||||||
|
|
||||||
@BeforeAll
|
|
||||||
public static void startListenerServer() throws Exception {
|
|
||||||
RestfulServer ourListenerRestServer = new RestfulServer(FhirContext.forR4());
|
|
||||||
|
|
||||||
ourListenerRestServer.registerProvider(new ObservationResourceProvider());
|
|
||||||
ourListenerRestServer.registerProvider(new PlainProvider());
|
|
||||||
|
|
||||||
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";
|
|
||||||
}
|
|
||||||
|
|
||||||
@AfterAll
|
|
||||||
public static void stopListenerServer() throws Exception {
|
|
||||||
JettyUtil.closeServer(ourListenerServer);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,12 +11,13 @@ import java.util.List;
|
||||||
|
|
||||||
public class CountingInterceptor implements ChannelInterceptor {
|
public class CountingInterceptor implements ChannelInterceptor {
|
||||||
|
|
||||||
|
private static final Logger ourLog = LoggerFactory.getLogger(CountingInterceptor.class);
|
||||||
private List<String> mySent = new ArrayList<>();
|
private List<String> mySent = new ArrayList<>();
|
||||||
|
|
||||||
public int getSentCount(String theContainingKeyword) {
|
public int getSentCount(String theContainingKeyword) {
|
||||||
return (int)mySent.stream().filter(t -> t.contains(theContainingKeyword)).count();
|
return (int) mySent.stream().filter(t -> t.contains(theContainingKeyword)).count();
|
||||||
}
|
}
|
||||||
private static final Logger ourLog = LoggerFactory.getLogger(CountingInterceptor.class);
|
|
||||||
@Override
|
@Override
|
||||||
public void afterSendCompletion(Message<?> theMessage, MessageChannel theChannel, boolean theSent, Exception theException) {
|
public void afterSendCompletion(Message<?> theMessage, MessageChannel theChannel, boolean theSent, Exception theException) {
|
||||||
ourLog.info("Counting another instance: {}", theMessage);
|
ourLog.info("Counting another instance: {}", theMessage);
|
||||||
|
|
|
@ -83,6 +83,25 @@ public class SubscriptionValidatingInterceptorTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidate_RestHook_MultitypeResourceTypeNotSupported() {
|
||||||
|
when(myDaoRegistry.isResourceTypeSupported(eq("Patient"))).thenReturn(false);
|
||||||
|
|
||||||
|
Subscription subscription = new Subscription();
|
||||||
|
subscription.setStatus(Subscription.SubscriptionStatus.ACTIVE);
|
||||||
|
subscription.setCriteria("[Patient]");
|
||||||
|
subscription.getChannel().setType(Subscription.SubscriptionChannelType.RESTHOOK);
|
||||||
|
subscription.getChannel().setPayload("application/fhir+json");
|
||||||
|
subscription.getChannel().setEndpoint("http://foo");
|
||||||
|
|
||||||
|
try {
|
||||||
|
mySvc.validateSubmittedSubscription(subscription);
|
||||||
|
fail();
|
||||||
|
} catch (UnprocessableEntityException e) {
|
||||||
|
assertThat(e.getMessage(), containsString("Subscription.criteria contains invalid/unsupported resource type: Patient"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testValidate_RestHook_NoEndpoint() {
|
public void testValidate_RestHook_NoEndpoint() {
|
||||||
when(myDaoRegistry.isResourceTypeSupported(eq("Patient"))).thenReturn(true);
|
when(myDaoRegistry.isResourceTypeSupported(eq("Patient"))).thenReturn(true);
|
||||||
|
|
|
@ -8,10 +8,20 @@ import ca.uhn.fhir.rest.api.Constants;
|
||||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||||
import ca.uhn.fhir.util.HapiExtensions;
|
import ca.uhn.fhir.util.HapiExtensions;
|
||||||
import ca.uhn.fhir.util.TestUtil;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.hl7.fhir.r4.model.*;
|
import org.hl7.fhir.r4.model.BooleanType;
|
||||||
|
import org.hl7.fhir.r4.model.Bundle;
|
||||||
|
import org.hl7.fhir.r4.model.CodeableConcept;
|
||||||
|
import org.hl7.fhir.r4.model.Coding;
|
||||||
|
import org.hl7.fhir.r4.model.Enumerations;
|
||||||
|
import org.hl7.fhir.r4.model.IdType;
|
||||||
|
import org.hl7.fhir.r4.model.Meta;
|
||||||
|
import org.hl7.fhir.r4.model.Observation;
|
||||||
|
import org.hl7.fhir.r4.model.Patient;
|
||||||
|
import org.hl7.fhir.r4.model.SearchParameter;
|
||||||
|
import org.hl7.fhir.r4.model.StringType;
|
||||||
|
import org.hl7.fhir.r4.model.Subscription;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -68,9 +78,10 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
|
|
||||||
// Should see 1 subscription notification
|
// Should see 1 subscription notification
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
waitForSize(0, ourCreatedObservations);
|
assertEquals(0, ourObservationProvider.getCountCreate());
|
||||||
waitForSize(1, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(1);
|
||||||
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(0));
|
|
||||||
|
assertEquals(Constants.CT_FHIR_JSON_NEW, ourRestfulServer.getRequestContentTypes().get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -92,15 +103,14 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
|
|
||||||
// Should see 1 subscription notification
|
// Should see 1 subscription notification
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
int idx = 0;
|
assertEquals(0, ourObservationProvider.getCountCreate());
|
||||||
waitForSize(0, ourCreatedObservations);
|
ourObservationProvider.waitForUpdateCount(1);
|
||||||
waitForSize(1, ourUpdatedObservations);
|
assertEquals(Constants.CT_FHIR_JSON_NEW, ourRestfulServer.getRequestContentTypes().get(0));
|
||||||
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(idx));
|
assertEquals("1", ourObservationProvider.getStoredResources().get(0).getIdElement().getVersionIdPart());
|
||||||
assertEquals("1", ourUpdatedObservations.get(idx).getIdElement().getVersionIdPart());
|
assertEquals("1", ourObservationProvider.getStoredResources().get(0).getMeta().getVersionId());
|
||||||
assertEquals("1", ourUpdatedObservations.get(idx).getMeta().getVersionId());
|
assertEquals(obs.getMeta().getLastUpdatedElement().getValueAsString(), ourObservationProvider.getStoredResources().get(0).getMeta().getLastUpdatedElement().getValueAsString());
|
||||||
assertEquals(obs.getMeta().getLastUpdatedElement().getValueAsString(), ourUpdatedObservations.get(idx).getMeta().getLastUpdatedElement().getValueAsString());
|
assertEquals(obs.getMeta().getLastUpdatedElement().getValueAsString(), ourObservationProvider.getStoredResources().get(0).getMeta().getLastUpdatedElement().getValueAsString());
|
||||||
assertEquals(obs.getMeta().getLastUpdatedElement().getValueAsString(), ourUpdatedObservations.get(idx).getMeta().getLastUpdatedElement().getValueAsString());
|
assertEquals("1", ourObservationProvider.getStoredResources().get(0).getIdentifierFirstRep().getValue());
|
||||||
assertEquals("1", ourUpdatedObservations.get(idx).getIdentifierFirstRep().getValue());
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Send version 2
|
* Send version 2
|
||||||
|
@ -112,15 +122,14 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
|
|
||||||
// Should see 1 subscription notification
|
// Should see 1 subscription notification
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
idx++;
|
assertEquals(0, ourObservationProvider.getCountCreate());
|
||||||
waitForSize(0, ourCreatedObservations);
|
ourObservationProvider.waitForUpdateCount(2);
|
||||||
waitForSize(2, ourUpdatedObservations);
|
assertEquals(Constants.CT_FHIR_JSON_NEW, ourRestfulServer.getRequestContentTypes().get(0));
|
||||||
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(idx));
|
assertEquals("2", ourObservationProvider.getStoredResources().get(0).getIdElement().getVersionIdPart());
|
||||||
assertEquals("2", ourUpdatedObservations.get(idx).getIdElement().getVersionIdPart());
|
assertEquals("2", ourObservationProvider.getStoredResources().get(0).getMeta().getVersionId());
|
||||||
assertEquals("2", ourUpdatedObservations.get(idx).getMeta().getVersionId());
|
assertEquals(obs.getMeta().getLastUpdatedElement().getValueAsString(), ourObservationProvider.getStoredResources().get(0).getMeta().getLastUpdatedElement().getValueAsString());
|
||||||
assertEquals(obs.getMeta().getLastUpdatedElement().getValueAsString(), ourUpdatedObservations.get(idx).getMeta().getLastUpdatedElement().getValueAsString());
|
assertEquals(obs.getMeta().getLastUpdatedElement().getValueAsString(), ourObservationProvider.getStoredResources().get(0).getMeta().getLastUpdatedElement().getValueAsString());
|
||||||
assertEquals(obs.getMeta().getLastUpdatedElement().getValueAsString(), ourUpdatedObservations.get(idx).getMeta().getLastUpdatedElement().getValueAsString());
|
assertEquals("2", ourObservationProvider.getStoredResources().get(0).getIdentifierFirstRep().getValue());
|
||||||
assertEquals("2", ourUpdatedObservations.get(idx).getIdentifierFirstRep().getValue());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -151,9 +160,9 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
// Send the transaction
|
// Send the transaction
|
||||||
mySystemDao.transaction(null, bundle);
|
mySystemDao.transaction(null, bundle);
|
||||||
|
|
||||||
waitForSize(1, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(1);
|
||||||
|
|
||||||
assertThat(ourUpdatedObservations.get(0).getSubject().getReference(), matchesPattern("Patient/[0-9]+"));
|
assertThat(ourObservationProvider.getStoredResources().get(0).getSubject().getReference(), matchesPattern("Patient/[0-9]+"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -183,14 +192,13 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
|
|
||||||
// Should see 1 subscription notification
|
// Should see 1 subscription notification
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
int idx = 0;
|
assertEquals(0, ourObservationProvider.getCountCreate());
|
||||||
waitForSize(0, ourCreatedObservations);
|
ourObservationProvider.waitForUpdateCount(1);
|
||||||
waitForSize(1, ourUpdatedObservations);
|
assertEquals(Constants.CT_FHIR_JSON_NEW, ourRestfulServer.getRequestContentTypes().get(0));
|
||||||
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(idx));
|
assertEquals("1", ourObservationProvider.getStoredResources().get(0).getIdElement().getVersionIdPart());
|
||||||
assertEquals("1", ourUpdatedObservations.get(idx).getIdElement().getVersionIdPart());
|
assertEquals("1", ourObservationProvider.getStoredResources().get(0).getMeta().getVersionId());
|
||||||
assertEquals("1", ourUpdatedObservations.get(idx).getMeta().getVersionId());
|
assertEquals(obs.getMeta().getLastUpdatedElement().getValueAsString(), ourObservationProvider.getStoredResources().get(0).getMeta().getLastUpdatedElement().getValueAsString());
|
||||||
assertEquals(obs.getMeta().getLastUpdatedElement().getValueAsString(), ourUpdatedObservations.get(idx).getMeta().getLastUpdatedElement().getValueAsString());
|
assertEquals("1", ourObservationProvider.getStoredResources().get(0).getIdentifierFirstRep().getValue());
|
||||||
assertEquals("1", ourUpdatedObservations.get(idx).getIdentifierFirstRep().getValue());
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Send version 2
|
* Send version 2
|
||||||
|
@ -209,14 +217,13 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
|
|
||||||
// Should see 1 subscription notification
|
// Should see 1 subscription notification
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
idx++;
|
assertEquals(0, ourObservationProvider.getCountCreate());
|
||||||
waitForSize(0, ourCreatedObservations);
|
ourObservationProvider.waitForUpdateCount(2);
|
||||||
waitForSize(2, ourUpdatedObservations);
|
assertEquals(Constants.CT_FHIR_JSON_NEW, ourRestfulServer.getRequestContentTypes().get(0));
|
||||||
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(idx));
|
assertEquals("2", ourObservationProvider.getStoredResources().get(0).getIdElement().getVersionIdPart());
|
||||||
assertEquals("2", ourUpdatedObservations.get(idx).getIdElement().getVersionIdPart());
|
assertEquals("2", ourObservationProvider.getStoredResources().get(0).getMeta().getVersionId());
|
||||||
assertEquals("2", ourUpdatedObservations.get(idx).getMeta().getVersionId());
|
assertEquals(obs.getMeta().getLastUpdatedElement().getValueAsString(), ourObservationProvider.getStoredResources().get(0).getMeta().getLastUpdatedElement().getValueAsString());
|
||||||
assertEquals(obs.getMeta().getLastUpdatedElement().getValueAsString(), ourUpdatedObservations.get(idx).getMeta().getLastUpdatedElement().getValueAsString());
|
assertEquals("2", ourObservationProvider.getStoredResources().get(0).getIdentifierFirstRep().getValue());
|
||||||
assertEquals("2", ourUpdatedObservations.get(idx).getIdentifierFirstRep().getValue());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -237,7 +244,7 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
myObservationDao.create(observation);
|
myObservationDao.create(observation);
|
||||||
}
|
}
|
||||||
|
|
||||||
waitForSize(100, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(100);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -265,7 +272,7 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
observation.setStatus(Observation.ObservationStatus.FINAL);
|
observation.setStatus(Observation.ObservationStatus.FINAL);
|
||||||
myObservationDao.create(observation);
|
myObservationDao.create(observation);
|
||||||
|
|
||||||
waitForSize(1, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -297,9 +304,9 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
|
|
||||||
// Should see 1 subscription notification
|
// Should see 1 subscription notification
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
waitForSize(0, ourCreatedObservations);
|
assertEquals(0, ourObservationProvider.getCountCreate());
|
||||||
waitForSize(1, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(1);
|
||||||
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(0));
|
assertEquals(Constants.CT_FHIR_JSON_NEW, ourRestfulServer.getRequestContentTypes().get(0));
|
||||||
|
|
||||||
// Send a meta-add
|
// Send a meta-add
|
||||||
obs.setId(obs.getIdElement().toUnqualifiedVersionless());
|
obs.setId(obs.getIdElement().toUnqualifiedVersionless());
|
||||||
|
@ -312,8 +319,8 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
// Should be no further deliveries
|
// Should be no further deliveries
|
||||||
Thread.sleep(1000);
|
Thread.sleep(1000);
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
waitForSize(0, ourCreatedObservations);
|
assertEquals(0, ourObservationProvider.getCountCreate());
|
||||||
waitForSize(1, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(1);
|
||||||
|
|
||||||
// Send a meta-delete
|
// Send a meta-delete
|
||||||
obs.setId(obs.getIdElement().toUnqualifiedVersionless());
|
obs.setId(obs.getIdElement().toUnqualifiedVersionless());
|
||||||
|
@ -326,8 +333,8 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
// Should be no further deliveries
|
// Should be no further deliveries
|
||||||
Thread.sleep(1000);
|
Thread.sleep(1000);
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
waitForSize(0, ourCreatedObservations);
|
assertEquals(0, ourObservationProvider.getCountCreate());
|
||||||
waitForSize(1, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(1);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -349,9 +356,9 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
|
|
||||||
// Should see 1 subscription notification
|
// Should see 1 subscription notification
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
waitForSize(0, ourCreatedObservations);
|
assertEquals(0, ourObservationProvider.getCountCreate());
|
||||||
waitForSize(1, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(1);
|
||||||
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(0));
|
assertEquals(Constants.CT_FHIR_JSON_NEW, ourRestfulServer.getRequestContentTypes().get(0));
|
||||||
|
|
||||||
// Send a meta-add
|
// Send a meta-add
|
||||||
obs.setId(obs.getIdElement().toUnqualifiedVersionless());
|
obs.setId(obs.getIdElement().toUnqualifiedVersionless());
|
||||||
|
@ -364,8 +371,8 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
// Should be no further deliveries
|
// Should be no further deliveries
|
||||||
Thread.sleep(1000);
|
Thread.sleep(1000);
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
waitForSize(0, ourCreatedObservations);
|
assertEquals(0, ourObservationProvider.getCountCreate());
|
||||||
waitForSize(3, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(3);
|
||||||
|
|
||||||
// Send a meta-delete
|
// Send a meta-delete
|
||||||
obs.setId(obs.getIdElement().toUnqualifiedVersionless());
|
obs.setId(obs.getIdElement().toUnqualifiedVersionless());
|
||||||
|
@ -378,8 +385,8 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
// Should be no further deliveries
|
// Should be no further deliveries
|
||||||
Thread.sleep(1000);
|
Thread.sleep(1000);
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
waitForSize(0, ourCreatedObservations);
|
assertEquals(0, ourObservationProvider.getCountCreate());
|
||||||
waitForSize(5, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(5);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -399,9 +406,9 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
|
|
||||||
// Should see 1 subscription notification
|
// Should see 1 subscription notification
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
waitForSize(0, ourCreatedObservations);
|
assertEquals(0, ourObservationProvider.getCountCreate());
|
||||||
waitForSize(1, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(1);
|
||||||
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(0));
|
assertEquals(Constants.CT_FHIR_JSON_NEW, ourRestfulServer.getRequestContentTypes().get(0));
|
||||||
|
|
||||||
// Send an update with no changes
|
// Send an update with no changes
|
||||||
obs.setId(obs.getIdElement().toUnqualifiedVersionless());
|
obs.setId(obs.getIdElement().toUnqualifiedVersionless());
|
||||||
|
@ -410,8 +417,8 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
// Should be no further deliveries
|
// Should be no further deliveries
|
||||||
Thread.sleep(1000);
|
Thread.sleep(1000);
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
waitForSize(0, ourCreatedObservations);
|
assertEquals(0, ourObservationProvider.getCountCreate());
|
||||||
waitForSize(1, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(1);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -432,11 +439,11 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
Observation observation1 = sendObservation(code, "SNOMED-CT");
|
Observation observation1 = sendObservation(code, "SNOMED-CT");
|
||||||
|
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
waitForSize(0, ourCreatedObservations);
|
assertEquals(0, ourObservationProvider.getCountCreate());
|
||||||
waitForSize(1, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(1);
|
||||||
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(0));
|
assertEquals(Constants.CT_FHIR_JSON_NEW, ourRestfulServer.getRequestContentTypes().get(0));
|
||||||
|
|
||||||
IdType idElement = ourUpdatedObservations.get(0).getIdElement();
|
IdType idElement = ourObservationProvider.getStoredResources().get(0).getIdElement();
|
||||||
assertEquals(observation1.getIdElement().getIdPart(), idElement.getIdPart());
|
assertEquals(observation1.getIdElement().getIdPart(), idElement.getIdPart());
|
||||||
// VersionId is present
|
// VersionId is present
|
||||||
assertEquals(observation1.getIdElement().getVersionIdPart(), idElement.getVersionIdPart());
|
assertEquals(observation1.getIdElement().getVersionIdPart(), idElement.getVersionIdPart());
|
||||||
|
@ -454,11 +461,11 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
Observation observation2 = sendObservation(code, "SNOMED-CT");
|
Observation observation2 = sendObservation(code, "SNOMED-CT");
|
||||||
|
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
waitForSize(0, ourCreatedObservations);
|
assertEquals(0, ourObservationProvider.getCountCreate());
|
||||||
waitForSize(2, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(2);
|
||||||
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(1));
|
assertEquals(Constants.CT_FHIR_JSON_NEW, ourRestfulServer.getRequestContentTypes().get(1));
|
||||||
|
|
||||||
idElement = ourUpdatedObservations.get(1).getIdElement();
|
idElement = ourObservationProvider.getResourceUpdates().get(1).getIdElement();
|
||||||
assertEquals(observation2.getIdElement().getIdPart(), idElement.getIdPart());
|
assertEquals(observation2.getIdElement().getIdPart(), idElement.getIdPart());
|
||||||
// Now VersionId is stripped
|
// Now VersionId is stripped
|
||||||
assertEquals(null, idElement.getVersionIdPart());
|
assertEquals(null, idElement.getVersionIdPart());
|
||||||
|
@ -495,11 +502,11 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
myStoppableSubscriptionDeliveringRestHookSubscriber.unPause();
|
myStoppableSubscriptionDeliveringRestHookSubscriber.unPause();
|
||||||
|
|
||||||
|
|
||||||
waitForSize(0, ourCreatedObservations);
|
assertEquals(0, ourObservationProvider.getCountCreate());
|
||||||
waitForSize(2, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(2);
|
||||||
|
|
||||||
Observation observation1 = ourUpdatedObservations.get(0);
|
Observation observation1 = ourObservationProvider.getResourceUpdates().get(0);
|
||||||
Observation observation2 = ourUpdatedObservations.get(1);
|
Observation observation2 = ourObservationProvider.getResourceUpdates().get(1);
|
||||||
|
|
||||||
assertEquals("1", observation1.getIdElement().getVersionIdPart());
|
assertEquals("1", observation1.getIdElement().getVersionIdPart());
|
||||||
assertNull(observation1.getNoteFirstRep().getText());
|
assertNull(observation1.getNoteFirstRep().getText());
|
||||||
|
@ -544,11 +551,11 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
myStoppableSubscriptionDeliveringRestHookSubscriber.unPause();
|
myStoppableSubscriptionDeliveringRestHookSubscriber.unPause();
|
||||||
|
|
||||||
|
|
||||||
waitForSize(0, ourCreatedObservations);
|
assertEquals(0, ourObservationProvider.getCountCreate());
|
||||||
waitForSize(2, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(2);
|
||||||
|
|
||||||
Observation observation1 = ourUpdatedObservations.get(0);
|
Observation observation1 = ourObservationProvider.getResourceUpdates().get(0);
|
||||||
Observation observation2 = ourUpdatedObservations.get(1);
|
Observation observation2 = ourObservationProvider.getResourceUpdates().get(1);
|
||||||
|
|
||||||
assertEquals("2", observation1.getIdElement().getVersionIdPart());
|
assertEquals("2", observation1.getIdElement().getVersionIdPart());
|
||||||
assertEquals("changed", observation1.getNoteFirstRep().getText());
|
assertEquals("changed", observation1.getNoteFirstRep().getText());
|
||||||
|
@ -572,11 +579,11 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
|
|
||||||
// Should see 1 subscription notification
|
// Should see 1 subscription notification
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
waitForSize(0, ourCreatedObservations);
|
assertEquals(0, ourObservationProvider.getCountCreate());
|
||||||
waitForSize(1, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(1);
|
||||||
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(0));
|
assertEquals(Constants.CT_FHIR_JSON_NEW, ourRestfulServer.getRequestContentTypes().get(0));
|
||||||
|
|
||||||
assertEquals("1", ourUpdatedObservations.get(0).getIdElement().getVersionIdPart());
|
assertEquals("1", ourObservationProvider.getStoredResources().get(0).getIdElement().getVersionIdPart());
|
||||||
|
|
||||||
Subscription subscriptionTemp = myClient.read(Subscription.class, subscription2.getId());
|
Subscription subscriptionTemp = myClient.read(Subscription.class, subscription2.getId());
|
||||||
assertNotNull(subscriptionTemp);
|
assertNotNull(subscriptionTemp);
|
||||||
|
@ -589,8 +596,8 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
|
|
||||||
// Should see two subscription notifications
|
// Should see two subscription notifications
|
||||||
waitForSize(0, ourCreatedObservations);
|
assertEquals(0, ourObservationProvider.getCountCreate());
|
||||||
waitForSize(3, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(3);
|
||||||
|
|
||||||
myClient.delete().resourceById(new IdType("Subscription/" + subscription2.getId())).execute();
|
myClient.delete().resourceById(new IdType("Subscription/" + subscription2.getId())).execute();
|
||||||
waitForActivatedSubscriptionCount(1);
|
waitForActivatedSubscriptionCount(1);
|
||||||
|
@ -599,8 +606,8 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
|
|
||||||
// Should see only one subscription notification
|
// Should see only one subscription notification
|
||||||
waitForSize(0, ourCreatedObservations);
|
assertEquals(0, ourObservationProvider.getCountCreate());
|
||||||
waitForSize(4, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(4);
|
||||||
|
|
||||||
Observation observation3 = myClient.read(Observation.class, observationTemp3.getId());
|
Observation observation3 = myClient.read(Observation.class, observationTemp3.getId());
|
||||||
CodeableConcept codeableConcept = new CodeableConcept();
|
CodeableConcept codeableConcept = new CodeableConcept();
|
||||||
|
@ -612,8 +619,8 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
|
|
||||||
// Should see no subscription notification
|
// Should see no subscription notification
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
waitForSize(0, ourCreatedObservations);
|
assertEquals(0, ourObservationProvider.getCountCreate());
|
||||||
waitForSize(4, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(4);
|
||||||
|
|
||||||
Observation observation3a = myClient.read(Observation.class, observationTemp3.getId());
|
Observation observation3a = myClient.read(Observation.class, observationTemp3.getId());
|
||||||
|
|
||||||
|
@ -626,8 +633,8 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
|
|
||||||
// Should see only one subscription notification
|
// Should see only one subscription notification
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
waitForSize(0, ourCreatedObservations);
|
assertEquals(0, ourObservationProvider.getCountCreate());
|
||||||
waitForSize(5, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(5);
|
||||||
|
|
||||||
assertFalse(subscription1.getId().equals(subscription2.getId()));
|
assertFalse(subscription1.getId().equals(subscription2.getId()));
|
||||||
assertFalse(observation1.getId().isEmpty());
|
assertFalse(observation1.getId().isEmpty());
|
||||||
|
@ -652,11 +659,11 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
|
|
||||||
// Should see 1 subscription notification
|
// Should see 1 subscription notification
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
waitForSize(0, ourCreatedObservations);
|
assertEquals(0, ourObservationProvider.getCountCreate());
|
||||||
waitForSize(1, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(1);
|
||||||
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(0));
|
assertEquals(Constants.CT_FHIR_JSON_NEW, ourRestfulServer.getRequestContentTypes().get(0));
|
||||||
|
|
||||||
assertEquals("1", ourUpdatedObservations.get(0).getIdElement().getVersionIdPart());
|
assertEquals("1", ourObservationProvider.getStoredResources().get(0).getIdElement().getVersionIdPart());
|
||||||
|
|
||||||
Subscription subscriptionTemp = myClient.read(Subscription.class, subscription2.getId());
|
Subscription subscriptionTemp = myClient.read(Subscription.class, subscription2.getId());
|
||||||
assertNotNull(subscriptionTemp);
|
assertNotNull(subscriptionTemp);
|
||||||
|
@ -669,8 +676,8 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
|
|
||||||
// Should see two subscription notifications
|
// Should see two subscription notifications
|
||||||
waitForSize(0, ourCreatedObservations);
|
assertEquals(0, ourObservationProvider.getCountCreate());
|
||||||
waitForSize(3, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(3);
|
||||||
|
|
||||||
myClient.delete().resourceById(new IdType("Subscription/" + subscription2.getId())).execute();
|
myClient.delete().resourceById(new IdType("Subscription/" + subscription2.getId())).execute();
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
|
@ -679,8 +686,8 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
|
|
||||||
// Should see only one subscription notification
|
// Should see only one subscription notification
|
||||||
waitForSize(0, ourCreatedObservations);
|
assertEquals(0, ourObservationProvider.getCountCreate());
|
||||||
waitForSize(4, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(4);
|
||||||
|
|
||||||
Observation observation3 = myClient.read(Observation.class, observationTemp3.getId());
|
Observation observation3 = myClient.read(Observation.class, observationTemp3.getId());
|
||||||
CodeableConcept codeableConcept = new CodeableConcept();
|
CodeableConcept codeableConcept = new CodeableConcept();
|
||||||
|
@ -692,8 +699,8 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
|
|
||||||
// Should see no subscription notification
|
// Should see no subscription notification
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
waitForSize(0, ourCreatedObservations);
|
assertEquals(0, ourObservationProvider.getCountCreate());
|
||||||
waitForSize(4, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(4);
|
||||||
|
|
||||||
Observation observation3a = myClient.read(Observation.class, observationTemp3.getId());
|
Observation observation3a = myClient.read(Observation.class, observationTemp3.getId());
|
||||||
|
|
||||||
|
@ -706,8 +713,8 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
|
|
||||||
// Should see only one subscription notification
|
// Should see only one subscription notification
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
waitForSize(0, ourCreatedObservations);
|
assertEquals(0, ourObservationProvider.getCountCreate());
|
||||||
waitForSize(5, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(5);
|
||||||
|
|
||||||
assertFalse(subscription1.getId().equals(subscription2.getId()));
|
assertFalse(subscription1.getId().equals(subscription2.getId()));
|
||||||
assertFalse(observation1.getId().isEmpty());
|
assertFalse(observation1.getId().isEmpty());
|
||||||
|
@ -730,9 +737,9 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
Observation observation1 = sendObservation(code, "SNOMED-CT");
|
Observation observation1 = sendObservation(code, "SNOMED-CT");
|
||||||
|
|
||||||
// Should see 1 subscription notification
|
// Should see 1 subscription notification
|
||||||
waitForSize(0, ourCreatedObservations);
|
assertEquals(0, ourObservationProvider.getCountCreate());
|
||||||
waitForSize(1, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(1);
|
||||||
assertEquals(Constants.CT_FHIR_XML_NEW, ourContentTypes.get(0));
|
assertEquals(Constants.CT_FHIR_XML_NEW, ourRestfulServer.getRequestContentTypes().get(0));
|
||||||
|
|
||||||
Subscription subscriptionTemp = myClient.read(Subscription.class, subscription2.getId());
|
Subscription subscriptionTemp = myClient.read(Subscription.class, subscription2.getId());
|
||||||
assertNotNull(subscriptionTemp);
|
assertNotNull(subscriptionTemp);
|
||||||
|
@ -744,8 +751,8 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
|
|
||||||
// Should see two subscription notifications
|
// Should see two subscription notifications
|
||||||
waitForSize(0, ourCreatedObservations);
|
assertEquals(0, ourObservationProvider.getCountCreate());
|
||||||
waitForSize(3, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(3);
|
||||||
|
|
||||||
myClient.delete().resourceById(new IdType("Subscription/" + subscription2.getId())).execute();
|
myClient.delete().resourceById(new IdType("Subscription/" + subscription2.getId())).execute();
|
||||||
|
|
||||||
|
@ -753,8 +760,8 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
|
|
||||||
// Should see only one subscription notification
|
// Should see only one subscription notification
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
waitForSize(0, ourCreatedObservations);
|
assertEquals(0, ourObservationProvider.getCountCreate());
|
||||||
waitForSize(4, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(4);
|
||||||
|
|
||||||
Observation observation3 = myClient.read(Observation.class, observationTemp3.getId());
|
Observation observation3 = myClient.read(Observation.class, observationTemp3.getId());
|
||||||
CodeableConcept codeableConcept = new CodeableConcept();
|
CodeableConcept codeableConcept = new CodeableConcept();
|
||||||
|
@ -766,8 +773,8 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
|
|
||||||
// Should see no subscription notification
|
// Should see no subscription notification
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
waitForSize(0, ourCreatedObservations);
|
assertEquals(0, ourObservationProvider.getCountCreate());
|
||||||
waitForSize(4, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(4);
|
||||||
|
|
||||||
Observation observation3a = myClient.read(Observation.class, observationTemp3.getId());
|
Observation observation3a = myClient.read(Observation.class, observationTemp3.getId());
|
||||||
|
|
||||||
|
@ -780,14 +787,68 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
|
|
||||||
// Should see only one subscription notification
|
// Should see only one subscription notification
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
waitForSize(0, ourCreatedObservations);
|
assertEquals(0, ourObservationProvider.getCountCreate());
|
||||||
waitForSize(5, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(5);
|
||||||
|
|
||||||
assertFalse(subscription1.getId().equals(subscription2.getId()));
|
assertFalse(subscription1.getId().equals(subscription2.getId()));
|
||||||
assertFalse(observation1.getId().isEmpty());
|
assertFalse(observation1.getId().isEmpty());
|
||||||
assertFalse(observation2.getId().isEmpty());
|
assertFalse(observation2.getId().isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRestHookSubscriptionStarCriteria() throws Exception {
|
||||||
|
String payload = "application/json";
|
||||||
|
|
||||||
|
String code = "1000000050";
|
||||||
|
String criteria1 = "[*]";
|
||||||
|
|
||||||
|
createSubscription(criteria1, payload);
|
||||||
|
waitForActivatedSubscriptionCount(1);
|
||||||
|
|
||||||
|
sendObservation(code, "SNOMED-CT");
|
||||||
|
sendPatient();
|
||||||
|
|
||||||
|
waitForQueueToDrain();
|
||||||
|
|
||||||
|
// Should see 1 subscription notification for each type
|
||||||
|
ourObservationProvider.waitForCreateCount(0);
|
||||||
|
ourObservationProvider.waitForUpdateCount(1);
|
||||||
|
assertEquals(Constants.CT_FHIR_JSON_NEW, ourRestfulServer.getRequestContentTypes().get(0));
|
||||||
|
ourPatientProvider.waitForCreateCount(0);
|
||||||
|
ourPatientProvider.waitForUpdateCount(1);
|
||||||
|
assertEquals(Constants.CT_FHIR_JSON_NEW, ourRestfulServer.getRequestContentTypes().get(1));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRestHookSubscriptionMultiTypeCriteria() throws Exception {
|
||||||
|
String payload = "application/json";
|
||||||
|
|
||||||
|
String code = "1000000050";
|
||||||
|
String criteria1 = "[Observation,Patient]";
|
||||||
|
|
||||||
|
createSubscription(criteria1, payload);
|
||||||
|
waitForActivatedSubscriptionCount(1);
|
||||||
|
|
||||||
|
sendOrganization();
|
||||||
|
sendObservation(code, "SNOMED-CT");
|
||||||
|
sendPatient();
|
||||||
|
|
||||||
|
waitForQueueToDrain();
|
||||||
|
|
||||||
|
// Should see 1 subscription notification for each type
|
||||||
|
ourObservationProvider.waitForCreateCount(0);
|
||||||
|
ourObservationProvider.waitForUpdateCount(1);
|
||||||
|
assertEquals(Constants.CT_FHIR_JSON_NEW, ourRestfulServer.getRequestContentTypes().get(0));
|
||||||
|
ourPatientProvider.waitForCreateCount(0);
|
||||||
|
ourPatientProvider.waitForUpdateCount(1);
|
||||||
|
assertEquals(Constants.CT_FHIR_JSON_NEW, ourRestfulServer.getRequestContentTypes().get(1));
|
||||||
|
ourOrganizationProvider.waitForCreateCount(0);
|
||||||
|
ourOrganizationProvider.waitForUpdateCount(0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSubscriptionTriggerViaSubscription() throws Exception {
|
public void testSubscriptionTriggerViaSubscription() throws Exception {
|
||||||
String payload = "application/xml";
|
String payload = "application/xml";
|
||||||
|
@ -831,11 +892,11 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
myClient.transaction().withBundle(requestBundle).execute();
|
myClient.transaction().withBundle(requestBundle).execute();
|
||||||
|
|
||||||
// Should see 1 subscription notification
|
// Should see 1 subscription notification
|
||||||
waitForSize(0, ourCreatedObservations);
|
assertEquals(0, ourObservationProvider.getCountCreate());
|
||||||
waitForSize(1, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(1);
|
||||||
assertEquals(Constants.CT_FHIR_XML_NEW, ourContentTypes.get(0));
|
assertEquals(Constants.CT_FHIR_XML_NEW, ourRestfulServer.getRequestContentTypes().get(0));
|
||||||
|
|
||||||
Observation obs = ourUpdatedObservations.get(0);
|
Observation obs = ourObservationProvider.getStoredResources().get(0);
|
||||||
ourLog.info("Observation content: {}", myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(obs));
|
ourLog.info("Observation content: {}", myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(obs));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -857,7 +918,7 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
// Criteria didn't match, shouldn't see any updates
|
// Criteria didn't match, shouldn't see any updates
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
Thread.sleep(1000);
|
Thread.sleep(1000);
|
||||||
assertEquals(0, ourUpdatedObservations.size());
|
assertEquals(0, ourObservationProvider.getCountUpdate());
|
||||||
|
|
||||||
Subscription subscriptionTemp = myClient.read().resource(Subscription.class).withId(subscription2.getId()).execute();
|
Subscription subscriptionTemp = myClient.read().resource(Subscription.class).withId(subscription2.getId()).execute();
|
||||||
assertNotNull(subscriptionTemp);
|
assertNotNull(subscriptionTemp);
|
||||||
|
@ -872,8 +933,8 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
|
|
||||||
// Should see a subscription notification this time
|
// Should see a subscription notification this time
|
||||||
waitForSize(0, ourCreatedObservations);
|
assertEquals(0, ourObservationProvider.getCountCreate());
|
||||||
waitForSize(1, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(1);
|
||||||
|
|
||||||
myClient.delete().resourceById(new IdType("Subscription/" + subscription2.getId())).execute();
|
myClient.delete().resourceById(new IdType("Subscription/" + subscription2.getId())).execute();
|
||||||
|
|
||||||
|
@ -881,7 +942,7 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
|
|
||||||
// No more matches
|
// No more matches
|
||||||
Thread.sleep(1000);
|
Thread.sleep(1000);
|
||||||
assertEquals(1, ourUpdatedObservations.size());
|
assertEquals(1, ourObservationProvider.getCountUpdate());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -900,9 +961,9 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
|
|
||||||
// Should see 1 subscription notification
|
// Should see 1 subscription notification
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
waitForSize(0, ourCreatedObservations);
|
assertEquals(0, ourObservationProvider.getCountCreate());
|
||||||
waitForSize(1, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(1);
|
||||||
assertEquals(Constants.CT_FHIR_XML_NEW, ourContentTypes.get(0));
|
assertEquals(Constants.CT_FHIR_XML_NEW, ourRestfulServer.getRequestContentTypes().get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -940,11 +1001,11 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
|
|
||||||
// Should see 1 subscription notification
|
// Should see 1 subscription notification
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
waitForSize(0, ourCreatedObservations);
|
assertEquals(0, ourObservationProvider.getCountCreate());
|
||||||
waitForSize(1, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(1);
|
||||||
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(0));
|
assertEquals(Constants.CT_FHIR_JSON_NEW, ourRestfulServer.getRequestContentTypes().get(0));
|
||||||
assertThat(ourHeaders, hasItem("X-Foo: FOO"));
|
assertThat(ourRestfulServer.getRequestHeaders().get(0), hasItem("X-Foo: FOO"));
|
||||||
assertThat(ourHeaders, hasItem("X-Bar: BAR"));
|
assertThat(ourRestfulServer.getRequestHeaders().get(0), hasItem("X-Bar: BAR"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -961,8 +1022,8 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
|
|
||||||
// Should see 1 subscription notification
|
// Should see 1 subscription notification
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
waitForSize(0, ourCreatedObservations);
|
assertEquals(0, ourObservationProvider.getCountCreate());
|
||||||
waitForSize(1, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(1);
|
||||||
|
|
||||||
// Disable
|
// Disable
|
||||||
subscription.setStatus(Subscription.SubscriptionStatus.OFF);
|
subscription.setStatus(Subscription.SubscriptionStatus.OFF);
|
||||||
|
@ -974,8 +1035,8 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
|
|
||||||
// Should see 1 subscription notification
|
// Should see 1 subscription notification
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
waitForSize(0, ourCreatedObservations);
|
assertEquals(0, ourObservationProvider.getCountCreate());
|
||||||
waitForSize(1, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(1);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1096,7 +1157,7 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
MethodOutcome methodOutcome = myClient.create().resource(bodySite).execute();
|
MethodOutcome methodOutcome = myClient.create().resource(bodySite).execute();
|
||||||
assertEquals(true, methodOutcome.getCreated());
|
assertEquals(true, methodOutcome.getCreated());
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
waitForSize(1, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(1);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
Observation observation = new Observation();
|
Observation observation = new Observation();
|
||||||
|
@ -1104,14 +1165,14 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
MethodOutcome methodOutcome = myClient.create().resource(observation).execute();
|
MethodOutcome methodOutcome = myClient.create().resource(observation).execute();
|
||||||
assertEquals(true, methodOutcome.getCreated());
|
assertEquals(true, methodOutcome.getCreated());
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
waitForSize(2, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(2);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
Observation observation = new Observation();
|
Observation observation = new Observation();
|
||||||
MethodOutcome methodOutcome = myClient.create().resource(observation).execute();
|
MethodOutcome methodOutcome = myClient.create().resource(observation).execute();
|
||||||
assertEquals(true, methodOutcome.getCreated());
|
assertEquals(true, methodOutcome.getCreated());
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
waitForSize(2, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(2);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
Observation observation = new Observation();
|
Observation observation = new Observation();
|
||||||
|
@ -1119,7 +1180,7 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
MethodOutcome methodOutcome = myClient.create().resource(observation).execute();
|
MethodOutcome methodOutcome = myClient.create().resource(observation).execute();
|
||||||
assertEquals(true, methodOutcome.getCreated());
|
assertEquals(true, methodOutcome.getCreated());
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
waitForSize(2, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1148,14 +1209,11 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
|
||||||
assertEquals(true, methodOutcome.getCreated());
|
assertEquals(true, methodOutcome.getCreated());
|
||||||
|
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
waitForSize(1, ourTransactions);
|
|
||||||
|
|
||||||
ourLog.info("Received transaction: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(ourTransactions.get(0)));
|
ourTransactionProvider.waitForTransactionCount(1);
|
||||||
|
|
||||||
Bundle xact = ourTransactions.get(0);
|
Bundle xact = ourTransactionProvider.getTransactions().get(0);
|
||||||
assertEquals(2, xact.getEntry().size());
|
assertEquals(2, xact.getEntry().size());
|
||||||
|
|
||||||
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(ourTransactions.get(0)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,10 +103,11 @@ public class RestHookWithInterceptorR4Test extends BaseSubscriptionsR4Test {
|
||||||
sendObservation();
|
sendObservation();
|
||||||
deliveryLatch.await(10, TimeUnit.SECONDS);
|
deliveryLatch.await(10, TimeUnit.SECONDS);
|
||||||
|
|
||||||
assertEquals(0, ourCreatedObservations.size());
|
|
||||||
assertEquals(1, ourUpdatedObservations.size());
|
ourObservationProvider.waitForCreateCount(0);
|
||||||
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(0));
|
ourObservationProvider.waitForUpdateCount(1);
|
||||||
assertEquals("Observation/A", ourUpdatedObservations.get(0).getId());
|
assertEquals(Constants.CT_FHIR_JSON_NEW, ourRestfulServer.getRequestContentTypes().get(0));
|
||||||
|
assertEquals("Observation/A/_history/1", ourObservationProvider.getStoredResources().get(0).getId());
|
||||||
assertTrue(ourHitBeforeRestHookDelivery);
|
assertTrue(ourHitBeforeRestHookDelivery);
|
||||||
assertTrue(ourHitAfterRestHookDelivery);
|
assertTrue(ourHitAfterRestHookDelivery);
|
||||||
}
|
}
|
||||||
|
@ -125,12 +126,12 @@ public class RestHookWithInterceptorR4Test extends BaseSubscriptionsR4Test {
|
||||||
sendObservation();
|
sendObservation();
|
||||||
deliveryLatch.await(10, TimeUnit.SECONDS);
|
deliveryLatch.await(10, TimeUnit.SECONDS);
|
||||||
|
|
||||||
assertEquals(0, ourCreatedObservations.size());
|
ourObservationProvider.waitForCreateCount(0);
|
||||||
assertEquals(1, ourUpdatedObservations.size());
|
ourObservationProvider.waitForUpdateCount(1);
|
||||||
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(0));
|
assertEquals(Constants.CT_FHIR_JSON_NEW, ourRestfulServer.getRequestContentTypes().get(0));
|
||||||
assertTrue(ourHitBeforeRestHookDelivery);
|
assertTrue(ourHitBeforeRestHookDelivery);
|
||||||
assertTrue(ourHitAfterRestHookDelivery);
|
assertTrue(ourHitAfterRestHookDelivery);
|
||||||
assertThat(ourHeaders, hasItem("X-Foo: Bar"));
|
assertThat(ourRestfulServer.getRequestHeaders().get(0), hasItem("X-Foo: Bar"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -174,7 +175,7 @@ public class RestHookWithInterceptorR4Test extends BaseSubscriptionsR4Test {
|
||||||
sendObservation();
|
sendObservation();
|
||||||
|
|
||||||
Thread.sleep(1000);
|
Thread.sleep(1000);
|
||||||
assertEquals(0, ourUpdatedObservations.size());
|
ourObservationProvider.waitForUpdateCount(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -242,11 +243,11 @@ public class RestHookWithInterceptorR4Test extends BaseSubscriptionsR4Test {
|
||||||
|
|
||||||
// Should see 1 subscription notification
|
// Should see 1 subscription notification
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
waitForSize(0, ourCreatedObservations);
|
ourObservationProvider.waitForCreateCount(0);
|
||||||
waitForSize(1, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(1);
|
||||||
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(0));
|
assertEquals(Constants.CT_FHIR_JSON_NEW, ourRestfulServer.getRequestContentTypes().get(0));
|
||||||
|
|
||||||
assertEquals("1", ourUpdatedObservations.get(0).getIdElement().getVersionIdPart());
|
assertEquals("1", ourObservationProvider.getStoredResources().get(0).getIdElement().getVersionIdPart());
|
||||||
|
|
||||||
Subscription subscriptionTemp = myClient.read(Subscription.class, subscription2.getId());
|
Subscription subscriptionTemp = myClient.read(Subscription.class, subscription2.getId());
|
||||||
assertNotNull(subscriptionTemp);
|
assertNotNull(subscriptionTemp);
|
||||||
|
@ -259,8 +260,8 @@ public class RestHookWithInterceptorR4Test extends BaseSubscriptionsR4Test {
|
||||||
waitForQueueToDrain();
|
waitForQueueToDrain();
|
||||||
|
|
||||||
// Should see two subscription notifications
|
// Should see two subscription notifications
|
||||||
waitForSize(0, ourCreatedObservations);
|
ourObservationProvider.waitForCreateCount(0);
|
||||||
waitForSize(3, ourUpdatedObservations);
|
ourObservationProvider.waitForUpdateCount(3);
|
||||||
|
|
||||||
ourLog.info("Messages:\n " + messages.stream().collect(Collectors.joining("\n ")));
|
ourLog.info("Messages:\n " + messages.stream().collect(Collectors.joining("\n ")));
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,6 @@ import org.apache.commons.lang3.ObjectUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.apache.commons.text.StringTokenizer;
|
import org.apache.commons.text.StringTokenizer;
|
||||||
import org.apache.commons.text.matcher.StringMatcher;
|
|
||||||
import org.fhir.ucum.Pair;
|
import org.fhir.ucum.Pair;
|
||||||
import org.hl7.fhir.exceptions.FHIRException;
|
import org.hl7.fhir.exceptions.FHIRException;
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
|
@ -1556,8 +1555,7 @@ public abstract class BaseSearchParamExtractor implements ISearchParamExtractor
|
||||||
@Nonnull
|
@Nonnull
|
||||||
public static String[] splitPathsR4(@Nonnull String thePaths) {
|
public static String[] splitPathsR4(@Nonnull String thePaths) {
|
||||||
StringTokenizer tok = new StringTokenizer(thePaths, " |");
|
StringTokenizer tok = new StringTokenizer(thePaths, " |");
|
||||||
StringMatcher trimmerMatcher = (buffer, start, bufferStart, bufferEnd) -> (buffer[start] <= 32) ? 1 : 0;
|
tok.setTrimmerMatcher(new StringTrimmingTrimmerMatcher());
|
||||||
tok.setTrimmerMatcher(trimmerMatcher);
|
|
||||||
return tok.getTokenArray();
|
return tok.getTokenArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
package ca.uhn.fhir.jpa.searchparam.extractor;
|
||||||
|
|
||||||
|
import org.apache.commons.text.matcher.StringMatcher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class that works with the commons-text
|
||||||
|
* {@link org.apache.commons.text.StringTokenizer}
|
||||||
|
* class to return tokens that are whitespace trimmed.
|
||||||
|
*/
|
||||||
|
public class StringTrimmingTrimmerMatcher implements StringMatcher {
|
||||||
|
@Override
|
||||||
|
public int isMatch(char[] buffer, int start, int bufferStart, int bufferEnd) {
|
||||||
|
return (buffer[start] <= 32) ? 1 : 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,6 +22,8 @@ package ca.uhn.fhir.jpa.subscription.match.matcher.matching;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.searchparam.matcher.InMemoryMatchResult;
|
import ca.uhn.fhir.jpa.searchparam.matcher.InMemoryMatchResult;
|
||||||
import ca.uhn.fhir.jpa.searchparam.matcher.InMemoryResourceMatcher;
|
import ca.uhn.fhir.jpa.searchparam.matcher.InMemoryResourceMatcher;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.match.matcher.subscriber.SubscriptionCriteriaParser;
|
||||||
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
public class SubscriptionStrategyEvaluator {
|
public class SubscriptionStrategyEvaluator {
|
||||||
|
@ -37,9 +39,16 @@ public class SubscriptionStrategyEvaluator {
|
||||||
}
|
}
|
||||||
|
|
||||||
public SubscriptionMatchingStrategy determineStrategy(String theCriteria) {
|
public SubscriptionMatchingStrategy determineStrategy(String theCriteria) {
|
||||||
InMemoryMatchResult result = myInMemoryResourceMatcher.canBeEvaluatedInMemory(theCriteria);
|
SubscriptionCriteriaParser.SubscriptionCriteria criteria = SubscriptionCriteriaParser.parse(theCriteria);
|
||||||
if (result.supported()) {
|
if (criteria != null) {
|
||||||
return SubscriptionMatchingStrategy.IN_MEMORY;
|
if (criteria.getCriteria() != null) {
|
||||||
|
InMemoryMatchResult result = myInMemoryResourceMatcher.canBeEvaluatedInMemory(theCriteria);
|
||||||
|
if (result.supported()) {
|
||||||
|
return SubscriptionMatchingStrategy.IN_MEMORY;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return SubscriptionMatchingStrategy.IN_MEMORY;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return SubscriptionMatchingStrategy.DATABASE;
|
return SubscriptionMatchingStrategy.DATABASE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
package ca.uhn.fhir.jpa.subscription.match.matcher.subscriber;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.searchparam.extractor.StringTrimmingTrimmerMatcher;
|
||||||
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||||
|
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||||
|
import org.apache.commons.text.StringTokenizer;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
import static org.apache.commons.lang3.StringUtils.trim;
|
||||||
|
|
||||||
|
public enum SubscriptionCriteriaParser {
|
||||||
|
;
|
||||||
|
|
||||||
|
public enum TypeEnum {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normal search URL expression
|
||||||
|
*/
|
||||||
|
SEARCH_EXPRESSION,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collection of resource types
|
||||||
|
*/
|
||||||
|
MULTITYPE_EXPRESSION,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All types
|
||||||
|
*/
|
||||||
|
STARTYPE_EXPRESSION
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SubscriptionCriteria {
|
||||||
|
|
||||||
|
private final TypeEnum myType;
|
||||||
|
private final String myCriteria;
|
||||||
|
private final Set<String> myApplicableResourceTypes;
|
||||||
|
|
||||||
|
private SubscriptionCriteria(TypeEnum theType, String theCriteria, Set<String> theApplicableResourceTypes) {
|
||||||
|
myType = theType;
|
||||||
|
myCriteria = theCriteria;
|
||||||
|
myApplicableResourceTypes = theApplicableResourceTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
ToStringBuilder retVal = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
|
||||||
|
retVal.append("type", myType);
|
||||||
|
if (isNotBlank(myCriteria)) {
|
||||||
|
retVal.append("criteria", myCriteria);
|
||||||
|
}
|
||||||
|
if (myApplicableResourceTypes != null) {
|
||||||
|
retVal.append("applicableResourceTypes", myApplicableResourceTypes);
|
||||||
|
}
|
||||||
|
return retVal.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TypeEnum getType() {
|
||||||
|
return myType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCriteria() {
|
||||||
|
return myCriteria;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> getApplicableResourceTypes() {
|
||||||
|
return myApplicableResourceTypes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static SubscriptionCriteria parse(String theCriteria) {
|
||||||
|
String criteria = trim(theCriteria);
|
||||||
|
if (isBlank(criteria)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (criteria.startsWith(Constants.SUBSCRIPTION_MULTITYPE_PREFIX)) {
|
||||||
|
if (criteria.endsWith(Constants.SUBSCRIPTION_MULTITYPE_SUFFIX)) {
|
||||||
|
String multitypeExpression = criteria.substring(1, criteria.length() - 1);
|
||||||
|
StringTokenizer tok = new StringTokenizer(multitypeExpression, ",");
|
||||||
|
tok.setTrimmerMatcher(new StringTrimmingTrimmerMatcher());
|
||||||
|
List<String> types = tok.getTokenList();
|
||||||
|
if (types.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (types.contains(Constants.SUBSCRIPTION_MULTITYPE_STAR)) {
|
||||||
|
return new SubscriptionCriteria(TypeEnum.STARTYPE_EXPRESSION, null, null);
|
||||||
|
}
|
||||||
|
Set<String> typesSet = Sets.newHashSet(types);
|
||||||
|
return new SubscriptionCriteria(TypeEnum.MULTITYPE_EXPRESSION, null, typesSet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Character.isLetter(criteria.charAt(0))) {
|
||||||
|
String criteriaType = criteria;
|
||||||
|
int questionMarkIdx = criteriaType.indexOf('?');
|
||||||
|
if (questionMarkIdx > 0) {
|
||||||
|
criteriaType = criteriaType.substring(0, questionMarkIdx);
|
||||||
|
}
|
||||||
|
Set<String> types = Collections.singleton(criteriaType);
|
||||||
|
return new SubscriptionCriteria(TypeEnum.SEARCH_EXPRESSION, criteria, types);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -15,7 +15,6 @@ import ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryMessage;
|
||||||
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.rest.api.EncodingEnum;
|
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -136,18 +135,24 @@ public class SubscriptionMatchingSubscriber implements MessageHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!validCriteria(nextActiveSubscription, resourceId)) {
|
if (!resourceTypeIsAppropriateForSubscription(nextActiveSubscription, resourceId)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
InMemoryMatchResult matchResult = mySubscriptionMatcher.match(nextActiveSubscription.getSubscription(), theMsg);
|
InMemoryMatchResult matchResult;
|
||||||
if (!matchResult.matched()) {
|
if (nextActiveSubscription.getCriteria().getType() == SubscriptionCriteriaParser.TypeEnum.SEARCH_EXPRESSION) {
|
||||||
continue;
|
matchResult = mySubscriptionMatcher.match(nextActiveSubscription.getSubscription(), theMsg);
|
||||||
|
if (!matchResult.matched()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ourLog.debug("Subscription {} was matched by resource {} {}",
|
||||||
|
nextActiveSubscription.getId(),
|
||||||
|
resourceId.toUnqualifiedVersionless().getValue(),
|
||||||
|
matchResult.isInMemory() ? "in-memory" : "by querying the repository");
|
||||||
|
} else {
|
||||||
|
matchResult = InMemoryMatchResult.successfulMatch();
|
||||||
|
matchResult.setInMemory(true);
|
||||||
}
|
}
|
||||||
ourLog.debug("Subscription {} was matched by resource {} {}",
|
|
||||||
nextActiveSubscription.getId(),
|
|
||||||
resourceId.toUnqualifiedVersionless().getValue(),
|
|
||||||
matchResult.isInMemory() ? "in-memory" : "by querying the repository");
|
|
||||||
|
|
||||||
IBaseResource payload = theMsg.getNewPayload(myFhirContext);
|
IBaseResource payload = theMsg.getNewPayload(myFhirContext);
|
||||||
CanonicalSubscription subscription = nextActiveSubscription.getSubscription();
|
CanonicalSubscription subscription = nextActiveSubscription.getSubscription();
|
||||||
|
@ -215,28 +220,26 @@ public class SubscriptionMatchingSubscriber implements MessageHandler {
|
||||||
return theActiveSubscription.getId();
|
return theActiveSubscription.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean validCriteria(ActiveSubscription theActiveSubscription, IIdType theResourceId) {
|
private boolean resourceTypeIsAppropriateForSubscription(ActiveSubscription theActiveSubscription, IIdType theResourceId) {
|
||||||
String criteriaString = theActiveSubscription.getCriteriaString();
|
SubscriptionCriteriaParser.SubscriptionCriteria criteria = theActiveSubscription.getCriteria();
|
||||||
String subscriptionId = getId(theActiveSubscription);
|
String subscriptionId = getId(theActiveSubscription);
|
||||||
String resourceType = theResourceId.getResourceType();
|
String resourceType = theResourceId.getResourceType();
|
||||||
|
|
||||||
if (StringUtils.isBlank(criteriaString)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// see if the criteria matches the created object
|
// see if the criteria matches the created object
|
||||||
ourLog.trace("Checking subscription {} for {} with criteria {}", subscriptionId, resourceType, criteriaString);
|
ourLog.trace("Checking subscription {} for {} with criteria {}", subscriptionId, resourceType, criteria);
|
||||||
String criteriaResource = criteriaString;
|
|
||||||
int index = criteriaResource.indexOf("?");
|
|
||||||
if (index != -1) {
|
|
||||||
criteriaResource = criteriaResource.substring(0, criteriaResource.indexOf("?"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resourceType != null && !criteriaResource.equals(resourceType)) {
|
if (criteria == null) {
|
||||||
ourLog.trace("Skipping subscription search for {} because it does not match the criteria {}", resourceType, criteriaString);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
switch (criteria.getType()) {
|
||||||
|
default:
|
||||||
|
case SEARCH_EXPRESSION:
|
||||||
|
case MULTITYPE_EXPRESSION:
|
||||||
|
return criteria.getApplicableResourceTypes().contains(resourceType);
|
||||||
|
case STARTYPE_EXPRESSION:
|
||||||
|
return !resourceType.equals("Subscription");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,41 +20,41 @@ package ca.uhn.fhir.jpa.subscription.match.registry;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.subscription.match.matcher.subscriber.SubscriptionCriteriaParser;
|
||||||
import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription;
|
import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription;
|
||||||
import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscriptionChannelType;
|
import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscriptionChannelType;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
public class ActiveSubscription {
|
public class ActiveSubscription {
|
||||||
private static final Logger ourLog = LoggerFactory.getLogger(ActiveSubscription.class);
|
|
||||||
|
|
||||||
private CanonicalSubscription mySubscription;
|
private SubscriptionCriteriaParser.SubscriptionCriteria myCriteria;
|
||||||
private final String myChannelName;
|
private final String myChannelName;
|
||||||
private final String myId;
|
private final String myId;
|
||||||
|
private CanonicalSubscription mySubscription;
|
||||||
private boolean flagForDeletion;
|
private boolean flagForDeletion;
|
||||||
|
|
||||||
public ActiveSubscription(CanonicalSubscription theSubscription, String theChannelName) {
|
public ActiveSubscription(CanonicalSubscription theSubscription, String theChannelName) {
|
||||||
mySubscription = theSubscription;
|
|
||||||
myChannelName = theChannelName;
|
myChannelName = theChannelName;
|
||||||
myId = theSubscription.getIdPart();
|
myId = theSubscription.getIdPart();
|
||||||
|
setSubscription(theSubscription);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SubscriptionCriteriaParser.SubscriptionCriteria getCriteria() {
|
||||||
|
return myCriteria;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CanonicalSubscription getSubscription() {
|
public CanonicalSubscription getSubscription() {
|
||||||
return mySubscription;
|
return mySubscription;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final void setSubscription(CanonicalSubscription theSubscription) {
|
||||||
|
mySubscription = theSubscription;
|
||||||
|
myCriteria = SubscriptionCriteriaParser.parse(theSubscription.getCriteriaString());
|
||||||
|
}
|
||||||
|
|
||||||
public String getChannelName() {
|
public String getChannelName() {
|
||||||
return myChannelName;
|
return myChannelName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getCriteriaString() {
|
|
||||||
return mySubscription.getCriteriaString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setSubscription(CanonicalSubscription theCanonicalizedSubscription) {
|
|
||||||
mySubscription = theCanonicalizedSubscription;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isFlagForDeletion() {
|
public boolean isFlagForDeletion() {
|
||||||
return flagForDeletion;
|
return flagForDeletion;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,9 +25,9 @@ import ca.uhn.fhir.interceptor.api.Hook;
|
||||||
import ca.uhn.fhir.interceptor.api.Interceptor;
|
import ca.uhn.fhir.interceptor.api.Interceptor;
|
||||||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
|
||||||
import ca.uhn.fhir.jpa.subscription.match.matcher.matching.SubscriptionMatchingStrategy;
|
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.match.matcher.matching.SubscriptionStrategyEvaluator;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.match.matcher.subscriber.SubscriptionCriteriaParser;
|
||||||
import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionCanonicalizer;
|
import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionCanonicalizer;
|
||||||
import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription;
|
import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription;
|
||||||
import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscriptionChannelType;
|
import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscriptionChannelType;
|
||||||
|
@ -128,6 +128,25 @@ public class SubscriptionValidatingInterceptor {
|
||||||
throw new UnprocessableEntityException(theFieldName + " must be populated");
|
throw new UnprocessableEntityException(theFieldName + " must be populated");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SubscriptionCriteriaParser.SubscriptionCriteria parsedCriteria = SubscriptionCriteriaParser.parse(theQuery);
|
||||||
|
if (parsedCriteria == null) {
|
||||||
|
throw new UnprocessableEntityException(theFieldName + " can not be parsed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parsedCriteria.getType() == SubscriptionCriteriaParser.TypeEnum.STARTYPE_EXPRESSION) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String next : parsedCriteria.getApplicableResourceTypes()) {
|
||||||
|
if (!myDaoRegistry.isResourceTypeSupported(next)) {
|
||||||
|
throw new UnprocessableEntityException(theFieldName + " contains invalid/unsupported resource type: " + next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parsedCriteria.getType() != SubscriptionCriteriaParser.TypeEnum.SEARCH_EXPRESSION) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int sep = theQuery.indexOf('?');
|
int sep = theQuery.indexOf('?');
|
||||||
if (sep <= 1) {
|
if (sep <= 1) {
|
||||||
throw new UnprocessableEntityException(theFieldName + " must be in the form \"{Resource Type}?[params]\"");
|
throw new UnprocessableEntityException(theFieldName + " must be in the form \"{Resource Type}?[params]\"");
|
||||||
|
@ -138,9 +157,6 @@ public class SubscriptionValidatingInterceptor {
|
||||||
throw new UnprocessableEntityException(theFieldName + " must be in the form \"{Resource Type}?[params]\"");
|
throw new UnprocessableEntityException(theFieldName + " must be in the form \"{Resource Type}?[params]\"");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!myDaoRegistry.isResourceTypeSupported(resType)) {
|
|
||||||
throw new UnprocessableEntityException(theFieldName + " contains invalid/unsupported resource type: " + resType);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void validateMessageSubscriptionEndpoint(String theEndpointUrl) {
|
public void validateMessageSubscriptionEndpoint(String theEndpointUrl) {
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
package ca.uhn.fhir.jpa.subscription.match.matcher.subscriber;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
public class SubscriptionCriteriaParserTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearchExpression() {
|
||||||
|
String expression = "Patient?foo=bar";
|
||||||
|
SubscriptionCriteriaParser.SubscriptionCriteria criteria = SubscriptionCriteriaParser.parse(expression);
|
||||||
|
assertEquals(SubscriptionCriteriaParser.TypeEnum.SEARCH_EXPRESSION, criteria.getType());
|
||||||
|
assertEquals(expression, criteria.getCriteria());
|
||||||
|
assertThat(criteria.getApplicableResourceTypes(), containsInAnyOrder("Patient"));
|
||||||
|
assertEquals("SubscriptionCriteriaParser.SubscriptionCriteria[type=SEARCH_EXPRESSION,criteria=Patient?foo=bar,applicableResourceTypes=[Patient]]", criteria.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTypeExpression() {
|
||||||
|
String expression = "Patient";
|
||||||
|
SubscriptionCriteriaParser.SubscriptionCriteria criteria = SubscriptionCriteriaParser.parse(expression);
|
||||||
|
assertEquals(SubscriptionCriteriaParser.TypeEnum.SEARCH_EXPRESSION, criteria.getType());
|
||||||
|
assertEquals(expression, criteria.getCriteria());
|
||||||
|
assertThat(criteria.getApplicableResourceTypes(), containsInAnyOrder("Patient"));
|
||||||
|
assertEquals("SubscriptionCriteriaParser.SubscriptionCriteria[type=SEARCH_EXPRESSION,criteria=Patient,applicableResourceTypes=[Patient]]", criteria.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStarExpression() {
|
||||||
|
String expression = "[*]";
|
||||||
|
SubscriptionCriteriaParser.SubscriptionCriteria criteria = SubscriptionCriteriaParser.parse(expression);
|
||||||
|
assertEquals(SubscriptionCriteriaParser.TypeEnum.STARTYPE_EXPRESSION, criteria.getType());
|
||||||
|
assertEquals(null, criteria.getCriteria());
|
||||||
|
assertEquals(null, criteria.getApplicableResourceTypes());
|
||||||
|
assertEquals("SubscriptionCriteriaParser.SubscriptionCriteria[type=STARTYPE_EXPRESSION]", criteria.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultitypeExpression() {
|
||||||
|
String expression = "[Patient , Observation]";
|
||||||
|
SubscriptionCriteriaParser.SubscriptionCriteria criteria = SubscriptionCriteriaParser.parse(expression);
|
||||||
|
assertEquals(SubscriptionCriteriaParser.TypeEnum.MULTITYPE_EXPRESSION, criteria.getType());
|
||||||
|
assertEquals(null, criteria.getCriteria());
|
||||||
|
assertThat(criteria.getApplicableResourceTypes(), containsInAnyOrder("Patient", "Observation"));
|
||||||
|
assertEquals("SubscriptionCriteriaParser.SubscriptionCriteria[type=MULTITYPE_EXPRESSION,applicableResourceTypes=[Observation, Patient]]", criteria.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInvalidExpression() {
|
||||||
|
assertNull(SubscriptionCriteriaParser.parse("[]"));
|
||||||
|
assertNull(SubscriptionCriteriaParser.parse(""));
|
||||||
|
assertNull(SubscriptionCriteriaParser.parse(null));
|
||||||
|
assertNull(SubscriptionCriteriaParser.parse(" "));
|
||||||
|
assertNull(SubscriptionCriteriaParser.parse("#123"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -14,14 +14,14 @@ public class SubscriptionRegistryTest extends BaseSubscriptionRegistryTest {
|
||||||
mySubscriptionRegistry.registerSubscriptionUnlessAlreadyRegistered(subscription);
|
mySubscriptionRegistry.registerSubscriptionUnlessAlreadyRegistered(subscription);
|
||||||
assertRegistrySize(1);
|
assertRegistrySize(1);
|
||||||
ActiveSubscription origActiveSubscription = mySubscriptionRegistry.get(SUBSCRIPTION_ID);
|
ActiveSubscription origActiveSubscription = mySubscriptionRegistry.get(SUBSCRIPTION_ID);
|
||||||
assertEquals(ORIG_CRITERIA, origActiveSubscription.getCriteriaString());
|
assertEquals(ORIG_CRITERIA, origActiveSubscription.getCriteria().getCriteria());
|
||||||
|
|
||||||
subscription.setCriteria(NEW_CRITERIA);
|
subscription.setCriteria(NEW_CRITERIA);
|
||||||
assertEquals(ORIG_CRITERIA, origActiveSubscription.getCriteriaString());
|
assertEquals(ORIG_CRITERIA, origActiveSubscription.getCriteria().getCriteria());
|
||||||
mySubscriptionRegistry.registerSubscriptionUnlessAlreadyRegistered(subscription);
|
mySubscriptionRegistry.registerSubscriptionUnlessAlreadyRegistered(subscription);
|
||||||
assertRegistrySize(1);
|
assertRegistrySize(1);
|
||||||
ActiveSubscription newActiveSubscription = mySubscriptionRegistry.get(SUBSCRIPTION_ID);
|
ActiveSubscription newActiveSubscription = mySubscriptionRegistry.get(SUBSCRIPTION_ID);
|
||||||
assertEquals(NEW_CRITERIA, newActiveSubscription.getCriteriaString());
|
assertEquals(NEW_CRITERIA, newActiveSubscription.getCriteria().getCriteria());
|
||||||
// The same object
|
// The same object
|
||||||
assertTrue(newActiveSubscription == origActiveSubscription);
|
assertTrue(newActiveSubscription == origActiveSubscription);
|
||||||
}
|
}
|
||||||
|
@ -34,11 +34,11 @@ public class SubscriptionRegistryTest extends BaseSubscriptionRegistryTest {
|
||||||
assertRegistrySize(1);
|
assertRegistrySize(1);
|
||||||
|
|
||||||
ActiveSubscription origActiveSubscription = mySubscriptionRegistry.get(SUBSCRIPTION_ID);
|
ActiveSubscription origActiveSubscription = mySubscriptionRegistry.get(SUBSCRIPTION_ID);
|
||||||
assertEquals(ORIG_CRITERIA, origActiveSubscription.getCriteriaString());
|
assertEquals(ORIG_CRITERIA, origActiveSubscription.getCriteria().getCriteria());
|
||||||
|
|
||||||
setChannel(subscription, Subscription.SubscriptionChannelType.EMAIL);
|
setChannel(subscription, Subscription.SubscriptionChannelType.EMAIL);
|
||||||
|
|
||||||
assertEquals(ORIG_CRITERIA, origActiveSubscription.getCriteriaString());
|
assertEquals(ORIG_CRITERIA, origActiveSubscription.getCriteria().getCriteria());
|
||||||
mySubscriptionRegistry.registerSubscriptionUnlessAlreadyRegistered(subscription);
|
mySubscriptionRegistry.registerSubscriptionUnlessAlreadyRegistered(subscription);
|
||||||
assertRegistrySize(1);
|
assertRegistrySize(1);
|
||||||
|
|
||||||
|
|
|
@ -77,4 +77,30 @@ public class SubscriptionMatchingSubscriberTest extends BaseBlockingQueueSubscri
|
||||||
|
|
||||||
assertEquals(0, ourContentTypes.size());
|
assertEquals(0, ourContentTypes.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCriteriaStarOnly() throws InterruptedException {
|
||||||
|
String payload = "application/fhir+xml";
|
||||||
|
|
||||||
|
String code = "1000000050";
|
||||||
|
String criteria1 = "[*]";
|
||||||
|
String criteria2 = "[*]";
|
||||||
|
String criteria3 = "Observation?code=FOO"; // won't match
|
||||||
|
|
||||||
|
sendSubscription(criteria1, payload, ourListenerServerBase);
|
||||||
|
sendSubscription(criteria2, payload, ourListenerServerBase);
|
||||||
|
sendSubscription(criteria3, payload, ourListenerServerBase);
|
||||||
|
|
||||||
|
assertEquals(3, mySubscriptionRegistry.size());
|
||||||
|
|
||||||
|
ourObservationListener.setExpectedCount(2);
|
||||||
|
sendObservation(code, "SNOMED-CT");
|
||||||
|
ourObservationListener.awaitExpected();
|
||||||
|
|
||||||
|
assertEquals(2, ourContentTypes.size());
|
||||||
|
assertEquals(Constants.CT_FHIR_XML_NEW, ourContentTypes.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,15 +20,28 @@ package ca.uhn.fhir.test.utilities.server;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
import ca.uhn.fhir.rest.server.provider.HashMapResourceProvider;
|
import ca.uhn.fhir.rest.server.provider.HashMapResourceProvider;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.junit.jupiter.api.extension.AfterEachCallback;
|
import org.junit.jupiter.api.extension.AfterEachCallback;
|
||||||
import org.junit.jupiter.api.extension.BeforeEachCallback;
|
import org.junit.jupiter.api.extension.BeforeEachCallback;
|
||||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.awaitility.Awaitility.await;
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
|
||||||
|
|
||||||
public class HashMapResourceProviderExtension<T extends IBaseResource> extends HashMapResourceProvider<T> implements BeforeEachCallback, AfterEachCallback {
|
public class HashMapResourceProviderExtension<T extends IBaseResource> extends HashMapResourceProvider<T> implements BeforeEachCallback, AfterEachCallback {
|
||||||
|
|
||||||
private final RestfulServerExtension myRestfulServerExtension;
|
private final RestfulServerExtension myRestfulServerExtension;
|
||||||
|
private boolean myClearBetweenTests = true;
|
||||||
|
private final List<T> myUpdates = new ArrayList<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
|
@ -46,9 +59,47 @@ public class HashMapResourceProviderExtension<T extends IBaseResource> extends H
|
||||||
myRestfulServerExtension.getRestfulServer().unregisterProvider(HashMapResourceProviderExtension.this);
|
myRestfulServerExtension.getRestfulServer().unregisterProvider(HashMapResourceProviderExtension.this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized MethodOutcome update(T theResource, String theConditional, RequestDetails theRequestDetails) {
|
||||||
|
T resourceClone = getFhirContext().newTerser().clone(theResource);
|
||||||
|
myUpdates.add(resourceClone);
|
||||||
|
return super.update(theResource, theConditional, theRequestDetails);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void clear() {
|
||||||
|
super.clear();
|
||||||
|
if (myUpdates != null) {
|
||||||
|
myUpdates.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void beforeEach(ExtensionContext context) throws Exception {
|
public void beforeEach(ExtensionContext context) throws Exception {
|
||||||
clear();
|
if (myClearBetweenTests) {
|
||||||
|
clear();
|
||||||
|
clearCounts();
|
||||||
|
}
|
||||||
myRestfulServerExtension.getRestfulServer().registerProvider(HashMapResourceProviderExtension.this);
|
myRestfulServerExtension.getRestfulServer().registerProvider(HashMapResourceProviderExtension.this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public HashMapResourceProviderExtension<T> dontClearBetweenTests() {
|
||||||
|
myClearBetweenTests = false;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void waitForUpdateCount(long theCount) {
|
||||||
|
assertThat(theCount, greaterThanOrEqualTo(getCountUpdate()));
|
||||||
|
await().until(()->getCountUpdate(), equalTo(theCount));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void waitForCreateCount(long theCount) {
|
||||||
|
assertThat(theCount, greaterThanOrEqualTo(getCountCreate()));
|
||||||
|
await().until(()->getCountCreate(), equalTo(theCount));
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<T> getResourceUpdates() {
|
||||||
|
return Collections.unmodifiableList(myUpdates);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,10 @@ package ca.uhn.fhir.test.utilities.server;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||||
|
import ca.uhn.fhir.interceptor.api.Hook;
|
||||||
|
import ca.uhn.fhir.interceptor.api.Interceptor;
|
||||||
|
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||||
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||||
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
|
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
|
@ -33,23 +37,28 @@ import org.apache.http.impl.client.HttpClientBuilder;
|
||||||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||||
import org.eclipse.jetty.server.Server;
|
import org.eclipse.jetty.server.Server;
|
||||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||||
import org.eclipse.jetty.servlet.ServletHandler;
|
|
||||||
import org.eclipse.jetty.servlet.ServletHolder;
|
import org.eclipse.jetty.servlet.ServletHolder;
|
||||||
|
import org.junit.jupiter.api.extension.AfterAllCallback;
|
||||||
import org.junit.jupiter.api.extension.AfterEachCallback;
|
import org.junit.jupiter.api.extension.AfterEachCallback;
|
||||||
import org.junit.jupiter.api.extension.BeforeEachCallback;
|
import org.junit.jupiter.api.extension.BeforeEachCallback;
|
||||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Enumeration;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public class RestfulServerExtension implements BeforeEachCallback, AfterEachCallback {
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
private static final Logger ourLog = LoggerFactory.getLogger(RestfulServerExtension.class);
|
|
||||||
|
|
||||||
|
public class RestfulServerExtension implements BeforeEachCallback, AfterEachCallback, AfterAllCallback {
|
||||||
|
private static final Logger ourLog = LoggerFactory.getLogger(RestfulServerExtension.class);
|
||||||
|
private final List<List<String>> myRequestHeaders = new ArrayList<>();
|
||||||
|
private final List<String> myRequestContentTypes = new ArrayList<>();
|
||||||
private FhirContext myFhirContext;
|
private FhirContext myFhirContext;
|
||||||
private List<Object> myProviders = new ArrayList<>();
|
private List<Object> myProviders = new ArrayList<>();
|
||||||
private FhirVersionEnum myFhirVersion;
|
private FhirVersionEnum myFhirVersion;
|
||||||
|
@ -60,6 +69,7 @@ public class RestfulServerExtension implements BeforeEachCallback, AfterEachCall
|
||||||
private IGenericClient myFhirClient;
|
private IGenericClient myFhirClient;
|
||||||
private List<Consumer<RestfulServer>> myConsumers = new ArrayList<>();
|
private List<Consumer<RestfulServer>> myConsumers = new ArrayList<>();
|
||||||
private String myServletPath = "/*";
|
private String myServletPath = "/*";
|
||||||
|
private boolean myKeepAliveBetweenTests;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
|
@ -87,6 +97,9 @@ public class RestfulServerExtension implements BeforeEachCallback, AfterEachCall
|
||||||
}
|
}
|
||||||
|
|
||||||
private void stopServer() throws Exception {
|
private void stopServer() throws Exception {
|
||||||
|
if (myServer == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
JettyUtil.closeServer(myServer);
|
JettyUtil.closeServer(myServer);
|
||||||
myServer = null;
|
myServer = null;
|
||||||
myFhirClient = null;
|
myFhirClient = null;
|
||||||
|
@ -96,10 +109,15 @@ public class RestfulServerExtension implements BeforeEachCallback, AfterEachCall
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startServer() throws Exception {
|
private void startServer() throws Exception {
|
||||||
|
if (myServer != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
myServer = new Server(myPort);
|
myServer = new Server(myPort);
|
||||||
|
|
||||||
myServlet = new RestfulServer(myFhirContext);
|
myServlet = new RestfulServer(myFhirContext);
|
||||||
myServlet.setDefaultPrettyPrint(true);
|
myServlet.setDefaultPrettyPrint(true);
|
||||||
|
myServlet.registerInterceptor(new ListenerExtension());
|
||||||
if (myProviders != null) {
|
if (myProviders != null) {
|
||||||
myServlet.registerProviders(myProviders);
|
myServlet.registerProviders(myProviders);
|
||||||
}
|
}
|
||||||
|
@ -124,7 +142,6 @@ public class RestfulServerExtension implements BeforeEachCallback, AfterEachCall
|
||||||
myFhirClient = myFhirContext.newRestfulGenericClient("http://localhost:" + myPort);
|
myFhirClient = myFhirContext.newRestfulGenericClient("http://localhost:" + myPort);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public IGenericClient getFhirClient() {
|
public IGenericClient getFhirClient() {
|
||||||
return myFhirClient;
|
return myFhirClient;
|
||||||
}
|
}
|
||||||
|
@ -142,15 +159,34 @@ public class RestfulServerExtension implements BeforeEachCallback, AfterEachCall
|
||||||
return myPort;
|
return myPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public List<String> getRequestContentTypes() {
|
||||||
public void afterEach(ExtensionContext context) throws Exception {
|
return myRequestContentTypes;
|
||||||
stopServer();
|
}
|
||||||
|
|
||||||
|
public List<List<String>> getRequestHeaders() {
|
||||||
|
return myRequestHeaders;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void beforeEach(ExtensionContext context) throws Exception {
|
public void beforeEach(ExtensionContext context) throws Exception {
|
||||||
createContextIfNeeded();
|
createContextIfNeeded();
|
||||||
startServer();
|
startServer();
|
||||||
|
myRequestContentTypes.clear();
|
||||||
|
myRequestHeaders.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterEach(ExtensionContext context) throws Exception {
|
||||||
|
if (!myKeepAliveBetweenTests) {
|
||||||
|
stopServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterAll(ExtensionContext context) throws Exception {
|
||||||
|
if (myKeepAliveBetweenTests) {
|
||||||
|
stopServer();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public RestfulServerExtension registerProvider(Object theProvider) {
|
public RestfulServerExtension registerProvider(Object theProvider) {
|
||||||
|
@ -188,4 +224,43 @@ public class RestfulServerExtension implements BeforeEachCallback, AfterEachCall
|
||||||
myPort = thePort;
|
myPort = thePort;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RestfulServerExtension keepAliveBetweenTests() {
|
||||||
|
myKeepAliveBetweenTests = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBaseUrl() {
|
||||||
|
return "http://localhost:" + myPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Interceptor
|
||||||
|
private class ListenerExtension {
|
||||||
|
|
||||||
|
|
||||||
|
@Hook(Pointcut.SERVER_INCOMING_REQUEST_POST_PROCESSED)
|
||||||
|
public void postProcessed(HttpServletRequest theRequest) {
|
||||||
|
String header = theRequest.getHeader(Constants.HEADER_CONTENT_TYPE);
|
||||||
|
if (isNotBlank(header)) {
|
||||||
|
myRequestContentTypes.add(header.replaceAll(";.*", ""));
|
||||||
|
} else {
|
||||||
|
myRequestContentTypes.add(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
java.util.Enumeration<String> headerNamesEnum = theRequest.getHeaderNames();
|
||||||
|
List<String> requestHeaders = new ArrayList<>();
|
||||||
|
myRequestHeaders.add(requestHeaders);
|
||||||
|
while (headerNamesEnum.hasMoreElements()) {
|
||||||
|
String nextName = headerNamesEnum.nextElement();
|
||||||
|
Enumeration<String> valueEnum = theRequest.getHeaders(nextName);
|
||||||
|
while (valueEnum.hasMoreElements()) {
|
||||||
|
String nextValue = valueEnum.nextElement();
|
||||||
|
requestHeaders.add(nextName + ": " + nextValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
package ca.uhn.fhir.test.utilities.server;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.rest.annotation.Transaction;
|
||||||
|
import ca.uhn.fhir.rest.annotation.TransactionParam;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||||
|
import org.junit.jupiter.api.extension.AfterEachCallback;
|
||||||
|
import org.junit.jupiter.api.extension.BeforeEachCallback;
|
||||||
|
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.awaitility.Awaitility.await;
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
|
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
|
||||||
|
|
||||||
|
public class TransactionCapturingProviderExtension<T extends IBaseBundle> implements BeforeEachCallback, AfterEachCallback {
|
||||||
|
|
||||||
|
private static final Logger ourLog = LoggerFactory.getLogger(TransactionCapturingProviderExtension.class);
|
||||||
|
private final RestfulServerExtension myRestfulServerExtension;
|
||||||
|
private final List<T> myInputBundles = Collections.synchronizedList(new ArrayList<>());
|
||||||
|
private PlainProvider myProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
|
public TransactionCapturingProviderExtension(RestfulServerExtension theRestfulServerExtension, Class<T> theBundleType) {
|
||||||
|
myRestfulServerExtension = theRestfulServerExtension;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterEach(ExtensionContext context) throws Exception {
|
||||||
|
myProvider = new PlainProvider();
|
||||||
|
myRestfulServerExtension.getRestfulServer().unregisterProvider(myProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void beforeEach(ExtensionContext context) throws Exception {
|
||||||
|
myRestfulServerExtension.getRestfulServer().registerProvider(myProvider);
|
||||||
|
myInputBundles.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void waitForTransactionCount(int theCount) {
|
||||||
|
assertThat(theCount, greaterThanOrEqualTo(myInputBundles.size()));
|
||||||
|
await().until(()->myInputBundles.size(), equalTo(theCount));
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<T> getTransactions() {
|
||||||
|
return Collections.unmodifiableList(myInputBundles);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class PlainProvider {
|
||||||
|
|
||||||
|
@Transaction
|
||||||
|
public T transaction(@TransactionParam T theInput) {
|
||||||
|
ourLog.info("Received transaction update");
|
||||||
|
myInputBundles.add(theInput);
|
||||||
|
return theInput;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue