Merge branch 'master' of github.com:jamesagnew/hapi-fhir
This commit is contained in:
commit
c4a48fe373
|
@ -138,7 +138,7 @@ public class DaoRegistry implements ApplicationContextAware, IDaoRegistry {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isResourceTypeSupported(String theResourceType) {
|
public boolean isResourceTypeSupported(String theResourceType) {
|
||||||
return mySupportedResourceTypes.contains(theResourceType);
|
return mySupportedResourceTypes == null || mySupportedResourceTypes.contains(theResourceType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void init() {
|
private void init() {
|
||||||
|
|
|
@ -0,0 +1,240 @@
|
||||||
|
package ca.uhn.fhir.jpa.subscription;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||||
|
import ca.uhn.fhir.jpa.provider.r5.BaseResourceProviderR5Test;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.module.LinkedBlockingQueueSubscribableChannel;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Create;
|
||||||
|
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Update;
|
||||||
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
|
import ca.uhn.fhir.test.utilities.JettyUtil;
|
||||||
|
import ca.uhn.fhir.util.BundleUtil;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
|
import net.ttddyy.dsproxy.QueryCount;
|
||||||
|
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.IIdType;
|
||||||
|
import org.hl7.fhir.r5.model.*;
|
||||||
|
import org.junit.*;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Ignore
|
||||||
|
public abstract class BaseSubscriptionsR5Test extends BaseResourceProviderR5Test {
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseSubscriptionsR5Test.class);
|
||||||
|
|
||||||
|
private static Server ourListenerServer;
|
||||||
|
protected static int ourListenerPort;
|
||||||
|
protected static List<String> ourContentTypes = Collections.synchronizedList(new ArrayList<>());
|
||||||
|
protected static List<String> ourHeaders = Collections.synchronizedList(new ArrayList<>());
|
||||||
|
private static SingleQueryCountHolder ourCountHolder;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SingleQueryCountHolder myCountHolder;
|
||||||
|
@Autowired
|
||||||
|
protected SubscriptionTestUtil mySubscriptionTestUtil;
|
||||||
|
@Autowired
|
||||||
|
protected SubscriptionMatcherInterceptor mySubscriptionMatcherInterceptor;
|
||||||
|
|
||||||
|
protected CountingInterceptor myCountingInterceptor;
|
||||||
|
|
||||||
|
protected static List<Observation> ourCreatedObservations = Collections.synchronizedList(Lists.newArrayList());
|
||||||
|
protected static List<Observation> ourUpdatedObservations = Collections.synchronizedList(Lists.newArrayList());
|
||||||
|
private static String ourListenerServerBase;
|
||||||
|
|
||||||
|
protected List<IIdType> mySubscriptionIds = Collections.synchronizedList(new ArrayList<>());
|
||||||
|
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void afterUnregisterRestHookListener() {
|
||||||
|
for (IIdType next : mySubscriptionIds) {
|
||||||
|
IIdType nextId = next.toUnqualifiedVersionless();
|
||||||
|
ourLog.info("Deleting: {}", nextId);
|
||||||
|
ourClient.delete().resourceById(nextId).execute();
|
||||||
|
}
|
||||||
|
mySubscriptionIds.clear();
|
||||||
|
|
||||||
|
myDaoConfig.setAllowMultipleDelete(true);
|
||||||
|
ourLog.info("Deleting all subscriptions");
|
||||||
|
ourClient.delete().resourceConditionalByUrl("Subscription?status=active").execute();
|
||||||
|
ourClient.delete().resourceConditionalByUrl("Observation?code:missing=false").execute();
|
||||||
|
ourLog.info("Done deleting all subscriptions");
|
||||||
|
myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete());
|
||||||
|
|
||||||
|
mySubscriptionTestUtil.unregisterSubscriptionInterceptor();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void beforeRegisterRestHookListener() {
|
||||||
|
mySubscriptionTestUtil.registerRestHookInterceptor();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void beforeReset() throws Exception {
|
||||||
|
ourCreatedObservations.clear();
|
||||||
|
ourUpdatedObservations.clear();
|
||||||
|
ourContentTypes.clear();
|
||||||
|
ourHeaders.clear();
|
||||||
|
|
||||||
|
// Delete all Subscriptions
|
||||||
|
if (ourClient != null) {
|
||||||
|
Bundle allSubscriptions = ourClient.search().forResource(Subscription.class).returnBundle(Bundle.class).execute();
|
||||||
|
for (IBaseResource next : BundleUtil.toListOfResources(myFhirCtx, allSubscriptions)) {
|
||||||
|
ourClient.delete().resource(next).execute();
|
||||||
|
}
|
||||||
|
waitForActivatedSubscriptionCount(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
LinkedBlockingQueueSubscribableChannel processingChannel = mySubscriptionMatcherInterceptor.getProcessingChannelForUnitTest();
|
||||||
|
if (processingChannel != null) {
|
||||||
|
processingChannel.clearInterceptorsForUnitTest();
|
||||||
|
}
|
||||||
|
myCountingInterceptor = new CountingInterceptor();
|
||||||
|
if (processingChannel != null) {
|
||||||
|
processingChannel.addInterceptorForUnitTest(myCountingInterceptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected Subscription createSubscription(String theCriteria, String thePayload) {
|
||||||
|
Subscription subscription = newSubscription(theCriteria, thePayload);
|
||||||
|
|
||||||
|
MethodOutcome methodOutcome = ourClient.create().resource(subscription).execute();
|
||||||
|
subscription.setId(methodOutcome.getId().getIdPart());
|
||||||
|
mySubscriptionIds.add(methodOutcome.getId());
|
||||||
|
|
||||||
|
return subscription;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Subscription newSubscription(String theCriteria, String thePayload) {
|
||||||
|
Subscription subscription = new Subscription();
|
||||||
|
subscription.setReason("Monitor new neonatal function (note, age will be determined by the monitor)");
|
||||||
|
subscription.setStatus(Subscription.SubscriptionStatus.REQUESTED);
|
||||||
|
subscription.setCriteria(theCriteria);
|
||||||
|
|
||||||
|
Subscription.SubscriptionChannelComponent channel = subscription.getChannel();
|
||||||
|
channel.setType(Subscription.SubscriptionChannelType.RESTHOOK);
|
||||||
|
channel.setPayload(thePayload);
|
||||||
|
channel.setEndpoint(ourListenerServerBase);
|
||||||
|
return subscription;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected void waitForQueueToDrain() throws InterruptedException {
|
||||||
|
mySubscriptionTestUtil.waitForQueueToDrain();
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void initializeOurCountHolder() {
|
||||||
|
ourCountHolder = myCountHolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected Observation sendObservation(String code, String system) {
|
||||||
|
Observation observation = new Observation();
|
||||||
|
CodeableConcept codeableConcept = new CodeableConcept();
|
||||||
|
observation.setCode(codeableConcept);
|
||||||
|
observation.getIdentifierFirstRep().setSystem("foo").setValue("1");
|
||||||
|
Coding coding = codeableConcept.addCoding();
|
||||||
|
coding.setCode(code);
|
||||||
|
coding.setSystem(system);
|
||||||
|
|
||||||
|
observation.setStatus(Observation.ObservationStatus.FINAL);
|
||||||
|
|
||||||
|
IIdType id = myObservationDao.create(observation).getId();
|
||||||
|
observation.setId(id);
|
||||||
|
|
||||||
|
return observation;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static class ObservationListener implements IResourceProvider {
|
||||||
|
|
||||||
|
@Create
|
||||||
|
public MethodOutcome create(@ResourceParam Observation theObservation, HttpServletRequest theRequest) {
|
||||||
|
ourLog.info("Received Listener Create");
|
||||||
|
ourContentTypes.add(theRequest.getHeader(Constants.HEADER_CONTENT_TYPE).replaceAll(";.*", ""));
|
||||||
|
ourCreatedObservations.add(theObservation);
|
||||||
|
extractHeaders(theRequest);
|
||||||
|
return new MethodOutcome(new IdType("Observation/1"), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void extractHeaders(HttpServletRequest theRequest) {
|
||||||
|
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");
|
||||||
|
ourUpdatedObservations.add(theObservation);
|
||||||
|
ourContentTypes.add(theRequest.getHeader(Constants.HEADER_CONTENT_TYPE).replaceAll(";.*", ""));
|
||||||
|
extractHeaders(theRequest);
|
||||||
|
return new MethodOutcome(new IdType("Observation/1"), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void reportTotalSelects() {
|
||||||
|
ourLog.info("Total database select queries: {}", getQueryCount().getSelect());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static QueryCount getQueryCount() {
|
||||||
|
return ourCountHolder.getQueryCountMap().get("");
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void startListenerServer() throws Exception {
|
||||||
|
RestfulServer ourListenerRestServer = new RestfulServer(FhirContext.forR5());
|
||||||
|
|
||||||
|
ObservationListener obsListener = new ObservationListener();
|
||||||
|
ourListenerRestServer.setResourceProviders(obsListener);
|
||||||
|
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void stopListenerServer() throws Exception {
|
||||||
|
JettyUtil.closeServer(ourListenerServer);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,957 @@
|
||||||
|
package ca.uhn.fhir.jpa.subscription.resthook;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.config.StoppableSubscriptionDeliveringRestHookSubscriber;
|
||||||
|
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.BaseSubscriptionsR5Test;
|
||||||
|
import ca.uhn.fhir.rest.api.CacheControlDirective;
|
||||||
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||||
|
import org.hl7.fhir.r5.model.*;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.*;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test the rest-hook subscriptions
|
||||||
|
*/
|
||||||
|
public class RestHookTestR5Test extends BaseSubscriptionsR5Test {
|
||||||
|
private static final Logger ourLog = LoggerFactory.getLogger(RestHookTestR5Test.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
StoppableSubscriptionDeliveringRestHookSubscriber myStoppableSubscriptionDeliveringRestHookSubscriber;
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void cleanupStoppableSubscriptionDeliveringRestHookSubscriber() {
|
||||||
|
ourLog.info("@After");
|
||||||
|
myStoppableSubscriptionDeliveringRestHookSubscriber.setCountDownLatch(null);
|
||||||
|
myStoppableSubscriptionDeliveringRestHookSubscriber.unPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRestHookSubscriptionApplicationFhirJson() throws Exception {
|
||||||
|
String payload = "application/fhir+json";
|
||||||
|
|
||||||
|
String code = "1000000050";
|
||||||
|
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
||||||
|
String criteria2 = "Observation?code=SNOMED-CT|" + code + "111&_format=xml";
|
||||||
|
|
||||||
|
createSubscription(criteria1, payload);
|
||||||
|
createSubscription(criteria2, payload);
|
||||||
|
waitForActivatedSubscriptionCount(2);
|
||||||
|
|
||||||
|
sendObservation(code, "SNOMED-CT");
|
||||||
|
|
||||||
|
// Should see 1 subscription notification
|
||||||
|
waitForQueueToDrain();
|
||||||
|
waitForSize(0, ourCreatedObservations);
|
||||||
|
waitForSize(1, ourUpdatedObservations);
|
||||||
|
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdatesHaveCorrectMetadata() throws Exception {
|
||||||
|
String payload = "application/fhir+json";
|
||||||
|
|
||||||
|
String code = "1000000050";
|
||||||
|
String criteria1 = "Observation?";
|
||||||
|
|
||||||
|
createSubscription(criteria1, payload);
|
||||||
|
waitForActivatedSubscriptionCount(1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send version 1
|
||||||
|
*/
|
||||||
|
|
||||||
|
Observation obs = sendObservation(code, "SNOMED-CT");
|
||||||
|
obs = myObservationDao.read(obs.getIdElement().toUnqualifiedVersionless());
|
||||||
|
|
||||||
|
// Should see 1 subscription notification
|
||||||
|
waitForQueueToDrain();
|
||||||
|
int idx = 0;
|
||||||
|
waitForSize(0, ourCreatedObservations);
|
||||||
|
waitForSize(1, ourUpdatedObservations);
|
||||||
|
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(idx));
|
||||||
|
assertEquals("1", ourUpdatedObservations.get(idx).getIdElement().getVersionIdPart());
|
||||||
|
assertEquals("1", ourUpdatedObservations.get(idx).getMeta().getVersionId());
|
||||||
|
assertEquals(obs.getMeta().getLastUpdatedElement().getValueAsString(), ourUpdatedObservations.get(idx).getMeta().getLastUpdatedElement().getValueAsString());
|
||||||
|
assertEquals(obs.getMeta().getLastUpdatedElement().getValueAsString(), ourUpdatedObservations.get(idx).getMeta().getLastUpdatedElement().getValueAsString());
|
||||||
|
assertEquals("1", ourUpdatedObservations.get(idx).getIdentifierFirstRep().getValue());
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send version 2
|
||||||
|
*/
|
||||||
|
|
||||||
|
obs.getIdentifierFirstRep().setSystem("foo").setValue("2");
|
||||||
|
myObservationDao.update(obs);
|
||||||
|
obs = myObservationDao.read(obs.getIdElement().toUnqualifiedVersionless());
|
||||||
|
|
||||||
|
// Should see 1 subscription notification
|
||||||
|
waitForQueueToDrain();
|
||||||
|
idx++;
|
||||||
|
waitForSize(0, ourCreatedObservations);
|
||||||
|
waitForSize(2, ourUpdatedObservations);
|
||||||
|
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(idx));
|
||||||
|
assertEquals("2", ourUpdatedObservations.get(idx).getIdElement().getVersionIdPart());
|
||||||
|
assertEquals("2", ourUpdatedObservations.get(idx).getMeta().getVersionId());
|
||||||
|
assertEquals(obs.getMeta().getLastUpdatedElement().getValueAsString(), ourUpdatedObservations.get(idx).getMeta().getLastUpdatedElement().getValueAsString());
|
||||||
|
assertEquals(obs.getMeta().getLastUpdatedElement().getValueAsString(), ourUpdatedObservations.get(idx).getMeta().getLastUpdatedElement().getValueAsString());
|
||||||
|
assertEquals("2", ourUpdatedObservations.get(idx).getIdentifierFirstRep().getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPlaceholderReferencesInTransactionAreResolvedCorrectly() throws Exception {
|
||||||
|
|
||||||
|
String payload = "application/fhir+json";
|
||||||
|
String code = "1000000050";
|
||||||
|
String criteria1 = "Observation?";
|
||||||
|
createSubscription(criteria1, payload);
|
||||||
|
waitForActivatedSubscriptionCount(1);
|
||||||
|
|
||||||
|
// Create a transaction that should match
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.setType(Bundle.BundleType.TRANSACTION);
|
||||||
|
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.setId(IdType.newRandomUuid());
|
||||||
|
patient.getIdentifierFirstRep().setSystem("foo").setValue("AAA");
|
||||||
|
bundle.addEntry().setResource(patient).getRequest().setMethod(Bundle.HTTPVerb.POST).setUrl("Patient");
|
||||||
|
|
||||||
|
Observation observation = new Observation();
|
||||||
|
observation.getIdentifierFirstRep().setSystem("foo").setValue("1");
|
||||||
|
observation.getCode().addCoding().setCode(code).setSystem("SNOMED-CT");
|
||||||
|
observation.setStatus(Observation.ObservationStatus.FINAL);
|
||||||
|
observation.getSubject().setReference(patient.getId());
|
||||||
|
bundle.addEntry().setResource(observation).getRequest().setMethod(Bundle.HTTPVerb.POST).setUrl("Observation");
|
||||||
|
|
||||||
|
// Send the transaction
|
||||||
|
mySystemDao.transaction(null, bundle);
|
||||||
|
|
||||||
|
waitForSize(1, ourUpdatedObservations);
|
||||||
|
|
||||||
|
assertThat(ourUpdatedObservations.get(0).getSubject().getReference(), matchesPattern("Patient/[0-9]+"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdatesHaveCorrectMetadataUsingTransactions() throws Exception {
|
||||||
|
String payload = "application/fhir+json";
|
||||||
|
|
||||||
|
String code = "1000000050";
|
||||||
|
String criteria1 = "Observation?";
|
||||||
|
|
||||||
|
createSubscription(criteria1, payload);
|
||||||
|
waitForActivatedSubscriptionCount(1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send version 1
|
||||||
|
*/
|
||||||
|
|
||||||
|
Observation observation = new Observation();
|
||||||
|
observation.getIdentifierFirstRep().setSystem("foo").setValue("1");
|
||||||
|
observation.getCode().addCoding().setCode(code).setSystem("SNOMED-CT");
|
||||||
|
observation.setStatus(Observation.ObservationStatus.FINAL);
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.setType(Bundle.BundleType.TRANSACTION);
|
||||||
|
bundle.addEntry().setResource(observation).getRequest().setMethod(Bundle.HTTPVerb.POST).setUrl("Observation");
|
||||||
|
Bundle responseBundle = mySystemDao.transaction(null, bundle);
|
||||||
|
|
||||||
|
Observation obs = myObservationDao.read(new IdType(responseBundle.getEntry().get(0).getResponse().getLocation()));
|
||||||
|
|
||||||
|
// Should see 1 subscription notification
|
||||||
|
waitForQueueToDrain();
|
||||||
|
int idx = 0;
|
||||||
|
waitForSize(0, ourCreatedObservations);
|
||||||
|
waitForSize(1, ourUpdatedObservations);
|
||||||
|
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(idx));
|
||||||
|
assertEquals("1", ourUpdatedObservations.get(idx).getIdElement().getVersionIdPart());
|
||||||
|
assertEquals("1", ourUpdatedObservations.get(idx).getMeta().getVersionId());
|
||||||
|
assertEquals(obs.getMeta().getLastUpdatedElement().getValueAsString(), ourUpdatedObservations.get(idx).getMeta().getLastUpdatedElement().getValueAsString());
|
||||||
|
assertEquals("1", ourUpdatedObservations.get(idx).getIdentifierFirstRep().getValue());
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Send version 2
|
||||||
|
*/
|
||||||
|
|
||||||
|
observation = new Observation();
|
||||||
|
observation.setId(obs.getId());
|
||||||
|
observation.getIdentifierFirstRep().setSystem("foo").setValue("2");
|
||||||
|
observation.getCode().addCoding().setCode(code).setSystem("SNOMED-CT");
|
||||||
|
observation.setStatus(Observation.ObservationStatus.FINAL);
|
||||||
|
bundle = new Bundle();
|
||||||
|
bundle.setType(Bundle.BundleType.TRANSACTION);
|
||||||
|
bundle.addEntry().setResource(observation).getRequest().setMethod(Bundle.HTTPVerb.PUT).setUrl(obs.getIdElement().toUnqualifiedVersionless().getValue());
|
||||||
|
mySystemDao.transaction(null, bundle);
|
||||||
|
obs = myObservationDao.read(obs.getIdElement().toUnqualifiedVersionless());
|
||||||
|
|
||||||
|
// Should see 1 subscription notification
|
||||||
|
waitForQueueToDrain();
|
||||||
|
idx++;
|
||||||
|
waitForSize(0, ourCreatedObservations);
|
||||||
|
waitForSize(2, ourUpdatedObservations);
|
||||||
|
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(idx));
|
||||||
|
assertEquals("2", ourUpdatedObservations.get(idx).getIdElement().getVersionIdPart());
|
||||||
|
assertEquals("2", ourUpdatedObservations.get(idx).getMeta().getVersionId());
|
||||||
|
assertEquals(obs.getMeta().getLastUpdatedElement().getValueAsString(), ourUpdatedObservations.get(idx).getMeta().getLastUpdatedElement().getValueAsString());
|
||||||
|
assertEquals("2", ourUpdatedObservations.get(idx).getIdentifierFirstRep().getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRepeatedDeliveries() throws Exception {
|
||||||
|
String payload = "application/fhir+json";
|
||||||
|
|
||||||
|
String code = "1000000050";
|
||||||
|
String criteria1 = "Observation?";
|
||||||
|
|
||||||
|
createSubscription(criteria1, payload);
|
||||||
|
waitForActivatedSubscriptionCount(1);
|
||||||
|
|
||||||
|
for (int i = 0; i < 100; i++) {
|
||||||
|
Observation observation = new Observation();
|
||||||
|
observation.getIdentifierFirstRep().setSystem("foo").setValue("ID" + i);
|
||||||
|
observation.getCode().addCoding().setCode(code).setSystem("SNOMED-CT");
|
||||||
|
observation.setStatus(Observation.ObservationStatus.FINAL);
|
||||||
|
myObservationDao.create(observation);
|
||||||
|
}
|
||||||
|
|
||||||
|
waitForSize(100, ourUpdatedObservations);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testActiveSubscriptionShouldntReActivate() throws Exception {
|
||||||
|
String criteria = "Observation?code=111111111&_format=xml";
|
||||||
|
String payload = "application/fhir+json";
|
||||||
|
createSubscription(criteria, payload);
|
||||||
|
|
||||||
|
waitForActivatedSubscriptionCount(1);
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
int changes = this.mySubscriptionLoader.doSyncSubscriptionsForUnitTest();
|
||||||
|
assertEquals(0, changes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRestHookSubscriptionNoopUpdateDoesntTriggerNewDelivery() throws Exception {
|
||||||
|
String payload = "application/fhir+json";
|
||||||
|
|
||||||
|
String code = "1000000050";
|
||||||
|
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
||||||
|
String criteria2 = "Observation?code=SNOMED-CT|" + code + "111&_format=xml";
|
||||||
|
|
||||||
|
createSubscription(criteria1, payload);
|
||||||
|
createSubscription(criteria2, payload);
|
||||||
|
waitForActivatedSubscriptionCount(2);
|
||||||
|
|
||||||
|
Observation obs = sendObservation(code, "SNOMED-CT");
|
||||||
|
|
||||||
|
// Should see 1 subscription notification
|
||||||
|
waitForQueueToDrain();
|
||||||
|
waitForSize(0, ourCreatedObservations);
|
||||||
|
waitForSize(1, ourUpdatedObservations);
|
||||||
|
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(0));
|
||||||
|
|
||||||
|
// Send an update with no changes
|
||||||
|
obs.setId(obs.getIdElement().toUnqualifiedVersionless());
|
||||||
|
ourClient.update().resource(obs).execute();
|
||||||
|
|
||||||
|
// Should be no further deliveries
|
||||||
|
Thread.sleep(1000);
|
||||||
|
waitForQueueToDrain();
|
||||||
|
waitForSize(0, ourCreatedObservations);
|
||||||
|
waitForSize(1, ourUpdatedObservations);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRestHookSubscriptionApplicationJsonDisableVersionIdInDelivery() throws Exception {
|
||||||
|
String payload = "application/json";
|
||||||
|
|
||||||
|
String code = "1000000050";
|
||||||
|
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
||||||
|
|
||||||
|
waitForActivatedSubscriptionCount(0);
|
||||||
|
Subscription subscription1 = createSubscription(criteria1, payload);
|
||||||
|
waitForActivatedSubscriptionCount(1);
|
||||||
|
|
||||||
|
|
||||||
|
ourLog.info("** About to send observation");
|
||||||
|
Observation observation1 = sendObservation(code, "SNOMED-CT");
|
||||||
|
|
||||||
|
waitForQueueToDrain();
|
||||||
|
waitForSize(0, ourCreatedObservations);
|
||||||
|
waitForSize(1, ourUpdatedObservations);
|
||||||
|
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(0));
|
||||||
|
|
||||||
|
IdType idElement = ourUpdatedObservations.get(0).getIdElement();
|
||||||
|
assertEquals(observation1.getIdElement().getIdPart(), idElement.getIdPart());
|
||||||
|
// VersionId is present
|
||||||
|
assertEquals(observation1.getIdElement().getVersionIdPart(), idElement.getVersionIdPart());
|
||||||
|
|
||||||
|
subscription1
|
||||||
|
.getChannel()
|
||||||
|
.addExtension(JpaConstants.EXT_SUBSCRIPTION_RESTHOOK_STRIP_VERSION_IDS, new BooleanType("true"));
|
||||||
|
ourLog.info("** About to update subscription");
|
||||||
|
|
||||||
|
int modCount = myCountingInterceptor.getSentCount();
|
||||||
|
ourClient.update().resource(subscription1).execute();
|
||||||
|
waitForSize(modCount + 1, () -> myCountingInterceptor.getSentCount());
|
||||||
|
|
||||||
|
ourLog.info("** About to send observation");
|
||||||
|
Observation observation2 = sendObservation(code, "SNOMED-CT");
|
||||||
|
|
||||||
|
waitForQueueToDrain();
|
||||||
|
waitForSize(0, ourCreatedObservations);
|
||||||
|
waitForSize(2, ourUpdatedObservations);
|
||||||
|
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(1));
|
||||||
|
|
||||||
|
idElement = ourUpdatedObservations.get(1).getIdElement();
|
||||||
|
assertEquals(observation2.getIdElement().getIdPart(), idElement.getIdPart());
|
||||||
|
// Now VersionId is stripped
|
||||||
|
assertEquals(null, idElement.getVersionIdPart());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRestHookSubscriptionDoesntGetLatestVersionByDefault() throws Exception {
|
||||||
|
String payload = "application/json";
|
||||||
|
|
||||||
|
String code = "1000000050";
|
||||||
|
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
||||||
|
|
||||||
|
waitForActivatedSubscriptionCount(0);
|
||||||
|
createSubscription(criteria1, payload);
|
||||||
|
waitForActivatedSubscriptionCount(1);
|
||||||
|
|
||||||
|
myStoppableSubscriptionDeliveringRestHookSubscriber.pause();
|
||||||
|
final CountDownLatch countDownLatch = new CountDownLatch(1);
|
||||||
|
myStoppableSubscriptionDeliveringRestHookSubscriber.setCountDownLatch(countDownLatch);
|
||||||
|
|
||||||
|
ourLog.info("** About to send observation");
|
||||||
|
Observation observation = sendObservation(code, "SNOMED-CT");
|
||||||
|
assertEquals("1", observation.getIdElement().getVersionIdPart());
|
||||||
|
assertNull(observation.getNoteFirstRep().getText());
|
||||||
|
|
||||||
|
observation.getNoteFirstRep().setText("changed");
|
||||||
|
MethodOutcome methodOutcome = ourClient.update().resource(observation).execute();
|
||||||
|
assertEquals("2", methodOutcome.getId().getVersionIdPart());
|
||||||
|
assertEquals("changed", observation.getNoteFirstRep().getText());
|
||||||
|
|
||||||
|
// Wait for our two delivery channel threads to be paused
|
||||||
|
assertTrue(countDownLatch.await(5L, TimeUnit.SECONDS));
|
||||||
|
// Open the floodgates!
|
||||||
|
myStoppableSubscriptionDeliveringRestHookSubscriber.unPause();
|
||||||
|
|
||||||
|
|
||||||
|
waitForSize(0, ourCreatedObservations);
|
||||||
|
waitForSize(2, ourUpdatedObservations);
|
||||||
|
|
||||||
|
Observation observation1 = ourUpdatedObservations.get(0);
|
||||||
|
Observation observation2 = ourUpdatedObservations.get(1);
|
||||||
|
|
||||||
|
assertEquals("1", observation1.getIdElement().getVersionIdPart());
|
||||||
|
assertNull(observation1.getNoteFirstRep().getText());
|
||||||
|
assertEquals("2", observation2.getIdElement().getVersionIdPart());
|
||||||
|
assertEquals("changed", observation2.getNoteFirstRep().getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRestHookSubscriptionGetsLatestVersionWithFlag() throws Exception {
|
||||||
|
String payload = "application/json";
|
||||||
|
|
||||||
|
String code = "1000000050";
|
||||||
|
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
||||||
|
|
||||||
|
waitForActivatedSubscriptionCount(0);
|
||||||
|
|
||||||
|
Subscription subscription = newSubscription(criteria1, payload);
|
||||||
|
subscription
|
||||||
|
.getChannel()
|
||||||
|
.addExtension(JpaConstants.EXT_SUBSCRIPTION_RESTHOOK_DELIVER_LATEST_VERSION, new BooleanType("true"));
|
||||||
|
ourClient.create().resource(subscription).execute();
|
||||||
|
|
||||||
|
waitForActivatedSubscriptionCount(1);
|
||||||
|
|
||||||
|
myStoppableSubscriptionDeliveringRestHookSubscriber.pause();
|
||||||
|
final CountDownLatch countDownLatch = new CountDownLatch(1);
|
||||||
|
myStoppableSubscriptionDeliveringRestHookSubscriber.setCountDownLatch(countDownLatch);
|
||||||
|
|
||||||
|
ourLog.info("** About to send observation");
|
||||||
|
Observation observation = sendObservation(code, "SNOMED-CT");
|
||||||
|
assertEquals("1", observation.getIdElement().getVersionIdPart());
|
||||||
|
assertNull(observation.getNoteFirstRep().getText());
|
||||||
|
|
||||||
|
observation.getNoteFirstRep().setText("changed");
|
||||||
|
MethodOutcome methodOutcome = ourClient.update().resource(observation).execute();
|
||||||
|
assertEquals("2", methodOutcome.getId().getVersionIdPart());
|
||||||
|
assertEquals("changed", observation.getNoteFirstRep().getText());
|
||||||
|
|
||||||
|
// Wait for our two delivery channel threads to be paused
|
||||||
|
assertTrue(countDownLatch.await(5L, TimeUnit.SECONDS));
|
||||||
|
// Open the floodgates!
|
||||||
|
myStoppableSubscriptionDeliveringRestHookSubscriber.unPause();
|
||||||
|
|
||||||
|
|
||||||
|
waitForSize(0, ourCreatedObservations);
|
||||||
|
waitForSize(2, ourUpdatedObservations);
|
||||||
|
|
||||||
|
Observation observation1 = ourUpdatedObservations.get(0);
|
||||||
|
Observation observation2 = ourUpdatedObservations.get(1);
|
||||||
|
|
||||||
|
assertEquals("2", observation1.getIdElement().getVersionIdPart());
|
||||||
|
assertEquals("changed", observation1.getNoteFirstRep().getText());
|
||||||
|
assertEquals("2", observation2.getIdElement().getVersionIdPart());
|
||||||
|
assertEquals("changed", observation2.getNoteFirstRep().getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRestHookSubscriptionApplicationJson() throws Exception {
|
||||||
|
String payload = "application/json";
|
||||||
|
|
||||||
|
String code = "1000000050";
|
||||||
|
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
||||||
|
String criteria2 = "Observation?code=SNOMED-CT|" + code + "111&_format=xml";
|
||||||
|
|
||||||
|
Subscription subscription1 = createSubscription(criteria1, payload);
|
||||||
|
Subscription subscription2 = createSubscription(criteria2, payload);
|
||||||
|
waitForActivatedSubscriptionCount(2);
|
||||||
|
|
||||||
|
Observation observation1 = sendObservation(code, "SNOMED-CT");
|
||||||
|
|
||||||
|
// Should see 1 subscription notification
|
||||||
|
waitForQueueToDrain();
|
||||||
|
waitForSize(0, ourCreatedObservations);
|
||||||
|
waitForSize(1, ourUpdatedObservations);
|
||||||
|
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(0));
|
||||||
|
|
||||||
|
assertEquals("1", ourUpdatedObservations.get(0).getIdElement().getVersionIdPart());
|
||||||
|
|
||||||
|
Subscription subscriptionTemp = ourClient.read(Subscription.class, subscription2.getId());
|
||||||
|
Assert.assertNotNull(subscriptionTemp);
|
||||||
|
|
||||||
|
subscriptionTemp.setCriteria(criteria1);
|
||||||
|
ourClient.update().resource(subscriptionTemp).withId(subscriptionTemp.getIdElement()).execute();
|
||||||
|
waitForQueueToDrain();
|
||||||
|
|
||||||
|
Observation observation2 = sendObservation(code, "SNOMED-CT");
|
||||||
|
waitForQueueToDrain();
|
||||||
|
|
||||||
|
// Should see two subscription notifications
|
||||||
|
waitForSize(0, ourCreatedObservations);
|
||||||
|
waitForSize(3, ourUpdatedObservations);
|
||||||
|
|
||||||
|
ourClient.delete().resourceById(new IdType("Subscription/" + subscription2.getId())).execute();
|
||||||
|
waitForQueueToDrain();
|
||||||
|
|
||||||
|
Observation observationTemp3 = sendObservation(code, "SNOMED-CT");
|
||||||
|
waitForQueueToDrain();
|
||||||
|
|
||||||
|
// Should see only one subscription notification
|
||||||
|
waitForSize(0, ourCreatedObservations);
|
||||||
|
waitForSize(4, ourUpdatedObservations);
|
||||||
|
|
||||||
|
Observation observation3 = ourClient.read(Observation.class, observationTemp3.getId());
|
||||||
|
CodeableConcept codeableConcept = new CodeableConcept();
|
||||||
|
observation3.setCode(codeableConcept);
|
||||||
|
Coding coding = codeableConcept.addCoding();
|
||||||
|
coding.setCode(code + "111");
|
||||||
|
coding.setSystem("SNOMED-CT");
|
||||||
|
ourClient.update().resource(observation3).withId(observation3.getIdElement()).execute();
|
||||||
|
|
||||||
|
// Should see no subscription notification
|
||||||
|
waitForQueueToDrain();
|
||||||
|
waitForSize(0, ourCreatedObservations);
|
||||||
|
waitForSize(4, ourUpdatedObservations);
|
||||||
|
|
||||||
|
Observation observation3a = ourClient.read(Observation.class, observationTemp3.getId());
|
||||||
|
|
||||||
|
CodeableConcept codeableConcept1 = new CodeableConcept();
|
||||||
|
observation3a.setCode(codeableConcept1);
|
||||||
|
Coding coding1 = codeableConcept1.addCoding();
|
||||||
|
coding1.setCode(code);
|
||||||
|
coding1.setSystem("SNOMED-CT");
|
||||||
|
ourClient.update().resource(observation3a).withId(observation3a.getIdElement()).execute();
|
||||||
|
|
||||||
|
// Should see only one subscription notification
|
||||||
|
waitForQueueToDrain();
|
||||||
|
waitForSize(0, ourCreatedObservations);
|
||||||
|
waitForSize(5, ourUpdatedObservations);
|
||||||
|
|
||||||
|
assertFalse(subscription1.getId().equals(subscription2.getId()));
|
||||||
|
assertFalse(observation1.getId().isEmpty());
|
||||||
|
assertFalse(observation2.getId().isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRestHookSubscriptionApplicationJsonDatabase() throws Exception {
|
||||||
|
// Same test as above, but now run it using database matching
|
||||||
|
myDaoConfig.setEnableInMemorySubscriptionMatching(false);
|
||||||
|
String payload = "application/json";
|
||||||
|
|
||||||
|
String code = "1000000050";
|
||||||
|
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
||||||
|
String criteria2 = "Observation?code=SNOMED-CT|" + code + "111&_format=xml";
|
||||||
|
|
||||||
|
Subscription subscription1 = createSubscription(criteria1, payload);
|
||||||
|
Subscription subscription2 = createSubscription(criteria2, payload);
|
||||||
|
waitForActivatedSubscriptionCount(2);
|
||||||
|
|
||||||
|
Observation observation1 = sendObservation(code, "SNOMED-CT");
|
||||||
|
|
||||||
|
// Should see 1 subscription notification
|
||||||
|
waitForQueueToDrain();
|
||||||
|
waitForSize(0, ourCreatedObservations);
|
||||||
|
waitForSize(1, ourUpdatedObservations);
|
||||||
|
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(0));
|
||||||
|
|
||||||
|
assertEquals("1", ourUpdatedObservations.get(0).getIdElement().getVersionIdPart());
|
||||||
|
|
||||||
|
Subscription subscriptionTemp = ourClient.read(Subscription.class, subscription2.getId());
|
||||||
|
Assert.assertNotNull(subscriptionTemp);
|
||||||
|
|
||||||
|
subscriptionTemp.setCriteria(criteria1);
|
||||||
|
ourClient.update().resource(subscriptionTemp).withId(subscriptionTemp.getIdElement()).execute();
|
||||||
|
waitForQueueToDrain();
|
||||||
|
|
||||||
|
Observation observation2 = sendObservation(code, "SNOMED-CT");
|
||||||
|
waitForQueueToDrain();
|
||||||
|
|
||||||
|
// Should see two subscription notifications
|
||||||
|
waitForSize(0, ourCreatedObservations);
|
||||||
|
waitForSize(3, ourUpdatedObservations);
|
||||||
|
|
||||||
|
ourClient.delete().resourceById(new IdType("Subscription/" + subscription2.getId())).execute();
|
||||||
|
waitForQueueToDrain();
|
||||||
|
|
||||||
|
Observation observationTemp3 = sendObservation(code, "SNOMED-CT");
|
||||||
|
waitForQueueToDrain();
|
||||||
|
|
||||||
|
// Should see only one subscription notification
|
||||||
|
waitForSize(0, ourCreatedObservations);
|
||||||
|
waitForSize(4, ourUpdatedObservations);
|
||||||
|
|
||||||
|
Observation observation3 = ourClient.read(Observation.class, observationTemp3.getId());
|
||||||
|
CodeableConcept codeableConcept = new CodeableConcept();
|
||||||
|
observation3.setCode(codeableConcept);
|
||||||
|
Coding coding = codeableConcept.addCoding();
|
||||||
|
coding.setCode(code + "111");
|
||||||
|
coding.setSystem("SNOMED-CT");
|
||||||
|
ourClient.update().resource(observation3).withId(observation3.getIdElement()).execute();
|
||||||
|
|
||||||
|
// Should see no subscription notification
|
||||||
|
waitForQueueToDrain();
|
||||||
|
waitForSize(0, ourCreatedObservations);
|
||||||
|
waitForSize(4, ourUpdatedObservations);
|
||||||
|
|
||||||
|
Observation observation3a = ourClient.read(Observation.class, observationTemp3.getId());
|
||||||
|
|
||||||
|
CodeableConcept codeableConcept1 = new CodeableConcept();
|
||||||
|
observation3a.setCode(codeableConcept1);
|
||||||
|
Coding coding1 = codeableConcept1.addCoding();
|
||||||
|
coding1.setCode(code);
|
||||||
|
coding1.setSystem("SNOMED-CT");
|
||||||
|
ourClient.update().resource(observation3a).withId(observation3a.getIdElement()).execute();
|
||||||
|
|
||||||
|
// Should see only one subscription notification
|
||||||
|
waitForQueueToDrain();
|
||||||
|
waitForSize(0, ourCreatedObservations);
|
||||||
|
waitForSize(5, ourUpdatedObservations);
|
||||||
|
|
||||||
|
assertFalse(subscription1.getId().equals(subscription2.getId()));
|
||||||
|
assertFalse(observation1.getId().isEmpty());
|
||||||
|
assertFalse(observation2.getId().isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRestHookSubscriptionApplicationXml() throws Exception {
|
||||||
|
String payload = "application/xml";
|
||||||
|
|
||||||
|
String code = "1000000050";
|
||||||
|
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
||||||
|
String criteria2 = "Observation?code=SNOMED-CT|" + code + "111&_format=xml";
|
||||||
|
|
||||||
|
Subscription subscription1 = createSubscription(criteria1, payload);
|
||||||
|
Subscription subscription2 = createSubscription(criteria2, payload);
|
||||||
|
waitForActivatedSubscriptionCount(2);
|
||||||
|
|
||||||
|
ourLog.info("** About to send obervation");
|
||||||
|
Observation observation1 = sendObservation(code, "SNOMED-CT");
|
||||||
|
|
||||||
|
// Should see 1 subscription notification
|
||||||
|
waitForSize(0, ourCreatedObservations);
|
||||||
|
waitForSize(1, ourUpdatedObservations);
|
||||||
|
assertEquals(Constants.CT_FHIR_XML_NEW, ourContentTypes.get(0));
|
||||||
|
|
||||||
|
Subscription subscriptionTemp = ourClient.read(Subscription.class, subscription2.getId());
|
||||||
|
Assert.assertNotNull(subscriptionTemp);
|
||||||
|
subscriptionTemp.setCriteria(criteria1);
|
||||||
|
ourClient.update().resource(subscriptionTemp).withId(subscriptionTemp.getIdElement()).execute();
|
||||||
|
waitForQueueToDrain();
|
||||||
|
|
||||||
|
Observation observation2 = sendObservation(code, "SNOMED-CT");
|
||||||
|
waitForQueueToDrain();
|
||||||
|
|
||||||
|
// Should see two subscription notifications
|
||||||
|
waitForSize(0, ourCreatedObservations);
|
||||||
|
waitForSize(3, ourUpdatedObservations);
|
||||||
|
|
||||||
|
ourClient.delete().resourceById(new IdType("Subscription/" + subscription2.getId())).execute();
|
||||||
|
|
||||||
|
Observation observationTemp3 = sendObservation(code, "SNOMED-CT");
|
||||||
|
|
||||||
|
// Should see only one subscription notification
|
||||||
|
waitForQueueToDrain();
|
||||||
|
waitForSize(0, ourCreatedObservations);
|
||||||
|
waitForSize(4, ourUpdatedObservations);
|
||||||
|
|
||||||
|
Observation observation3 = ourClient.read(Observation.class, observationTemp3.getId());
|
||||||
|
CodeableConcept codeableConcept = new CodeableConcept();
|
||||||
|
observation3.setCode(codeableConcept);
|
||||||
|
Coding coding = codeableConcept.addCoding();
|
||||||
|
coding.setCode(code + "111");
|
||||||
|
coding.setSystem("SNOMED-CT");
|
||||||
|
ourClient.update().resource(observation3).withId(observation3.getIdElement()).execute();
|
||||||
|
|
||||||
|
// Should see no subscription notification
|
||||||
|
waitForQueueToDrain();
|
||||||
|
waitForSize(0, ourCreatedObservations);
|
||||||
|
waitForSize(4, ourUpdatedObservations);
|
||||||
|
|
||||||
|
Observation observation3a = ourClient.read(Observation.class, observationTemp3.getId());
|
||||||
|
|
||||||
|
CodeableConcept codeableConcept1 = new CodeableConcept();
|
||||||
|
observation3a.setCode(codeableConcept1);
|
||||||
|
Coding coding1 = codeableConcept1.addCoding();
|
||||||
|
coding1.setCode(code);
|
||||||
|
coding1.setSystem("SNOMED-CT");
|
||||||
|
ourClient.update().resource(observation3a).withId(observation3a.getIdElement()).execute();
|
||||||
|
|
||||||
|
// Should see only one subscription notification
|
||||||
|
waitForQueueToDrain();
|
||||||
|
waitForSize(0, ourCreatedObservations);
|
||||||
|
waitForSize(5, ourUpdatedObservations);
|
||||||
|
|
||||||
|
assertFalse(subscription1.getId().equals(subscription2.getId()));
|
||||||
|
assertFalse(observation1.getId().isEmpty());
|
||||||
|
assertFalse(observation2.getId().isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSubscriptionTriggerViaSubscription() throws Exception {
|
||||||
|
String payload = "application/xml";
|
||||||
|
|
||||||
|
String code = "1000000050";
|
||||||
|
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
||||||
|
|
||||||
|
createSubscription(criteria1, payload);
|
||||||
|
waitForActivatedSubscriptionCount(1);
|
||||||
|
|
||||||
|
ourLog.info("** About to send obervation");
|
||||||
|
|
||||||
|
Observation observation = new Observation();
|
||||||
|
observation.addIdentifier().setSystem("foo").setValue("bar1");
|
||||||
|
observation.setId(IdType.newRandomUuid().getValue());
|
||||||
|
CodeableConcept codeableConcept = new CodeableConcept()
|
||||||
|
.addCoding(new Coding().setCode(code).setSystem("SNOMED-CT"));
|
||||||
|
observation.setCode(codeableConcept);
|
||||||
|
observation.setStatus(Observation.ObservationStatus.FINAL);
|
||||||
|
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.addIdentifier().setSystem("foo").setValue("bar2");
|
||||||
|
patient.setId(IdType.newRandomUuid().getValue());
|
||||||
|
patient.setActive(true);
|
||||||
|
observation.getSubject().setReference(patient.getId());
|
||||||
|
|
||||||
|
Bundle requestBundle = new Bundle();
|
||||||
|
requestBundle.setType(Bundle.BundleType.TRANSACTION);
|
||||||
|
requestBundle.addEntry()
|
||||||
|
.setResource(observation)
|
||||||
|
.setFullUrl(observation.getId())
|
||||||
|
.getRequest()
|
||||||
|
.setUrl("Obervation?identifier=foo|bar1")
|
||||||
|
.setMethod(Bundle.HTTPVerb.PUT);
|
||||||
|
requestBundle.addEntry()
|
||||||
|
.setResource(patient)
|
||||||
|
.setFullUrl(patient.getId())
|
||||||
|
.getRequest()
|
||||||
|
.setUrl("Patient?identifier=foo|bar2")
|
||||||
|
.setMethod(Bundle.HTTPVerb.PUT);
|
||||||
|
ourClient.transaction().withBundle(requestBundle).execute();
|
||||||
|
|
||||||
|
// Should see 1 subscription notification
|
||||||
|
waitForSize(0, ourCreatedObservations);
|
||||||
|
waitForSize(1, ourUpdatedObservations);
|
||||||
|
assertEquals(Constants.CT_FHIR_XML_NEW, ourContentTypes.get(0));
|
||||||
|
|
||||||
|
Observation obs = ourUpdatedObservations.get(0);
|
||||||
|
ourLog.info("Observation content: {}", myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(obs));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateSubscriptionToMatchLater() throws Exception {
|
||||||
|
String payload = "application/xml";
|
||||||
|
|
||||||
|
String code = "1000000050";
|
||||||
|
String criteriaBad = "Observation?code=SNOMED-CT|" + code + "111&_format=xml";
|
||||||
|
|
||||||
|
ourLog.info("** About to create non-matching subscription");
|
||||||
|
|
||||||
|
Subscription subscription2 = createSubscription(criteriaBad, payload);
|
||||||
|
|
||||||
|
ourLog.info("** About to send observation that wont match");
|
||||||
|
|
||||||
|
Observation observation1 = sendObservation(code, "SNOMED-CT");
|
||||||
|
|
||||||
|
// Criteria didn't match, shouldn't see any updates
|
||||||
|
waitForQueueToDrain();
|
||||||
|
Thread.sleep(1000);
|
||||||
|
assertEquals(0, ourUpdatedObservations.size());
|
||||||
|
|
||||||
|
Subscription subscriptionTemp = ourClient.read().resource(Subscription.class).withId(subscription2.getId()).execute();
|
||||||
|
Assert.assertNotNull(subscriptionTemp);
|
||||||
|
String criteriaGood = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
||||||
|
subscriptionTemp.setCriteria(criteriaGood);
|
||||||
|
ourLog.info("** About to update subscription");
|
||||||
|
ourClient.update().resource(subscriptionTemp).withId(subscriptionTemp.getIdElement()).execute();
|
||||||
|
waitForQueueToDrain();
|
||||||
|
|
||||||
|
ourLog.info("** About to send Observation 2");
|
||||||
|
Observation observation2 = sendObservation(code, "SNOMED-CT");
|
||||||
|
waitForQueueToDrain();
|
||||||
|
|
||||||
|
// Should see a subscription notification this time
|
||||||
|
waitForSize(0, ourCreatedObservations);
|
||||||
|
waitForSize(1, ourUpdatedObservations);
|
||||||
|
|
||||||
|
ourClient.delete().resourceById(new IdType("Subscription/" + subscription2.getId())).execute();
|
||||||
|
|
||||||
|
Observation observationTemp3 = sendObservation(code, "SNOMED-CT");
|
||||||
|
|
||||||
|
// No more matches
|
||||||
|
Thread.sleep(1000);
|
||||||
|
assertEquals(1, ourUpdatedObservations.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRestHookSubscriptionApplicationXmlJson() throws Exception {
|
||||||
|
String payload = "application/fhir+xml";
|
||||||
|
|
||||||
|
String code = "1000000050";
|
||||||
|
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
||||||
|
String criteria2 = "Observation?code=SNOMED-CT|" + code + "111&_format=xml";
|
||||||
|
|
||||||
|
Subscription subscription1 = createSubscription(criteria1, payload);
|
||||||
|
Subscription subscription2 = createSubscription(criteria2, payload);
|
||||||
|
waitForActivatedSubscriptionCount(2);
|
||||||
|
|
||||||
|
Observation observation1 = sendObservation(code, "SNOMED-CT");
|
||||||
|
|
||||||
|
// Should see 1 subscription notification
|
||||||
|
waitForQueueToDrain();
|
||||||
|
waitForSize(0, ourCreatedObservations);
|
||||||
|
waitForSize(1, ourUpdatedObservations);
|
||||||
|
assertEquals(Constants.CT_FHIR_XML_NEW, ourContentTypes.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRestHookSubscriptionInvalidCriteria() throws Exception {
|
||||||
|
String payload = "application/xml";
|
||||||
|
|
||||||
|
String criteria1 = "Observation?codeeeee=SNOMED-CT";
|
||||||
|
|
||||||
|
try {
|
||||||
|
createSubscription(criteria1, payload);
|
||||||
|
fail();
|
||||||
|
} catch (UnprocessableEntityException e) {
|
||||||
|
assertEquals("HTTP 422 Unprocessable Entity: Invalid subscription criteria submitted: Observation?codeeeee=SNOMED-CT Failed to parse match URL[Observation?codeeeee=SNOMED-CT] - Resource type Observation does not have a parameter with name: codeeeee", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSubscriptionWithHeaders() throws Exception {
|
||||||
|
String payload = "application/fhir+json";
|
||||||
|
|
||||||
|
String code = "1000000050";
|
||||||
|
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
||||||
|
|
||||||
|
// Add some headers, and we'll also turn back to requested status for fun
|
||||||
|
Subscription subscription = createSubscription(criteria1, payload);
|
||||||
|
waitForActivatedSubscriptionCount(1);
|
||||||
|
|
||||||
|
subscription.getChannel().addHeader("X-Foo: FOO");
|
||||||
|
subscription.getChannel().addHeader("X-Bar: BAR");
|
||||||
|
subscription.setStatus(Subscription.SubscriptionStatus.REQUESTED);
|
||||||
|
ourClient.update().resource(subscription).execute();
|
||||||
|
waitForQueueToDrain();
|
||||||
|
|
||||||
|
sendObservation(code, "SNOMED-CT");
|
||||||
|
|
||||||
|
// Should see 1 subscription notification
|
||||||
|
waitForQueueToDrain();
|
||||||
|
waitForSize(0, ourCreatedObservations);
|
||||||
|
waitForSize(1, ourUpdatedObservations);
|
||||||
|
assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(0));
|
||||||
|
assertThat(ourHeaders, hasItem("X-Foo: FOO"));
|
||||||
|
assertThat(ourHeaders, hasItem("X-Bar: BAR"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDisableSubscription() throws Exception {
|
||||||
|
String payload = "application/fhir+json";
|
||||||
|
|
||||||
|
String code = "1000000050";
|
||||||
|
String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml";
|
||||||
|
|
||||||
|
Subscription subscription = createSubscription(criteria1, payload);
|
||||||
|
waitForActivatedSubscriptionCount(1);
|
||||||
|
|
||||||
|
sendObservation(code, "SNOMED-CT");
|
||||||
|
|
||||||
|
// Should see 1 subscription notification
|
||||||
|
waitForQueueToDrain();
|
||||||
|
waitForSize(0, ourCreatedObservations);
|
||||||
|
waitForSize(1, ourUpdatedObservations);
|
||||||
|
|
||||||
|
// Disable
|
||||||
|
subscription.setStatus(Subscription.SubscriptionStatus.OFF);
|
||||||
|
ourClient.update().resource(subscription).execute();
|
||||||
|
waitForQueueToDrain();
|
||||||
|
|
||||||
|
// Send another object
|
||||||
|
sendObservation(code, "SNOMED-CT");
|
||||||
|
|
||||||
|
// Should see 1 subscription notification
|
||||||
|
waitForQueueToDrain();
|
||||||
|
waitForSize(0, ourCreatedObservations);
|
||||||
|
waitForSize(1, ourUpdatedObservations);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = UnprocessableEntityException.class)
|
||||||
|
public void testInvalidProvenanceParam() {
|
||||||
|
String payload = "application/fhir+json";
|
||||||
|
String criteriabad = "Provenance?activity=http://hl7.org/fhir/v3/DocumentCompletion%7CAU";
|
||||||
|
Subscription subscription = newSubscription(criteriabad, payload);
|
||||||
|
ourClient.create().resource(subscription).execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = UnprocessableEntityException.class)
|
||||||
|
public void testInvalidProcedureRequestParam() {
|
||||||
|
String payload = "application/fhir+json";
|
||||||
|
String criteriabad = "ProcedureRequest?intent=instance-order&category=Laboratory";
|
||||||
|
Subscription subscription = newSubscription(criteriabad, payload);
|
||||||
|
ourClient.create().resource(subscription).execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = UnprocessableEntityException.class)
|
||||||
|
public void testInvalidBodySiteParam() {
|
||||||
|
String payload = "application/fhir+json";
|
||||||
|
String criteriabad = "BodySite?accessType=Catheter";
|
||||||
|
Subscription subscription = newSubscription(criteriabad, payload);
|
||||||
|
ourClient.create().resource(subscription).execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGoodSubscriptionPersists() {
|
||||||
|
assertEquals(0, subscriptionCount());
|
||||||
|
String payload = "application/fhir+json";
|
||||||
|
String criteriaGood = "Patient?gender=male";
|
||||||
|
Subscription subscription = newSubscription(criteriaGood, payload);
|
||||||
|
ourClient.create().resource(subscription).execute();
|
||||||
|
assertEquals(1, subscriptionCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
private int subscriptionCount() {
|
||||||
|
IBaseBundle found = ourClient.search().forResource(Subscription.class).cacheControl(new CacheControlDirective().setNoCache(true)).execute();
|
||||||
|
return toUnqualifiedVersionlessIdValues(found).size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSubscriptionWithNoStatusIsRejected() {
|
||||||
|
Subscription subscription = newSubscription("Observation?", "application/json");
|
||||||
|
subscription.setStatus(null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
ourClient.create().resource(subscription).execute();
|
||||||
|
fail();
|
||||||
|
} catch (UnprocessableEntityException e) {
|
||||||
|
assertThat(e.getMessage(), containsString("Can not process submitted Subscription - Subscription.status must be populated on this server"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBadSubscriptionDoesntPersist() {
|
||||||
|
assertEquals(0, subscriptionCount());
|
||||||
|
String payload = "application/fhir+json";
|
||||||
|
String criteriaBad = "BodySite?accessType=Catheter";
|
||||||
|
Subscription subscription = newSubscription(criteriaBad, payload);
|
||||||
|
try {
|
||||||
|
ourClient.create().resource(subscription).execute();
|
||||||
|
} catch (UnprocessableEntityException e) {
|
||||||
|
ourLog.info("Expected exception", e);
|
||||||
|
}
|
||||||
|
assertEquals(0, subscriptionCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCustomSearchParam() throws Exception {
|
||||||
|
String criteria = "Observation?accessType=Catheter,PD%20Catheter";
|
||||||
|
|
||||||
|
SearchParameter sp = new SearchParameter();
|
||||||
|
sp.addBase("Observation");
|
||||||
|
sp.setCode("accessType");
|
||||||
|
sp.setType(Enumerations.SearchParamType.TOKEN);
|
||||||
|
sp.setExpression("Observation.extension('Observation#accessType')");
|
||||||
|
sp.setXpathUsage(SearchParameter.XPathUsageType.NORMAL);
|
||||||
|
sp.setStatus(Enumerations.PublicationStatus.ACTIVE);
|
||||||
|
mySearchParameterDao.create(sp);
|
||||||
|
mySearchParamRegistry.forceRefresh();
|
||||||
|
createSubscription(criteria, "application/json");
|
||||||
|
waitForActivatedSubscriptionCount(1);
|
||||||
|
|
||||||
|
{
|
||||||
|
Observation bodySite = new Observation();
|
||||||
|
bodySite.addExtension().setUrl("Observation#accessType").setValue(new Coding().setCode("Catheter"));
|
||||||
|
MethodOutcome methodOutcome = ourClient.create().resource(bodySite).execute();
|
||||||
|
assertEquals(true, methodOutcome.getCreated());
|
||||||
|
waitForQueueToDrain();
|
||||||
|
waitForSize(1, ourUpdatedObservations);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Observation observation = new Observation();
|
||||||
|
observation.addExtension().setUrl("Observation#accessType").setValue(new Coding().setCode("PD Catheter"));
|
||||||
|
MethodOutcome methodOutcome = ourClient.create().resource(observation).execute();
|
||||||
|
assertEquals(true, methodOutcome.getCreated());
|
||||||
|
waitForQueueToDrain();
|
||||||
|
waitForSize(2, ourUpdatedObservations);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Observation observation = new Observation();
|
||||||
|
MethodOutcome methodOutcome = ourClient.create().resource(observation).execute();
|
||||||
|
assertEquals(true, methodOutcome.getCreated());
|
||||||
|
waitForQueueToDrain();
|
||||||
|
waitForSize(2, ourUpdatedObservations);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Observation observation = new Observation();
|
||||||
|
observation.addExtension().setUrl("Observation#accessType").setValue(new Coding().setCode("XXX"));
|
||||||
|
MethodOutcome methodOutcome = ourClient.create().resource(observation).execute();
|
||||||
|
assertEquals(true, methodOutcome.getCreated());
|
||||||
|
waitForQueueToDrain();
|
||||||
|
waitForSize(2, ourUpdatedObservations);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -34,6 +34,8 @@ import org.hl7.fhir.instance.model.api.IBaseReference;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
import org.hl7.fhir.r4.model.Extension;
|
import org.hl7.fhir.r4.model.Extension;
|
||||||
|
import org.hl7.fhir.r4.model.codesystems.SubscriptionStatus;
|
||||||
|
import org.hl7.fhir.r5.model.Subscription;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
@ -67,6 +69,8 @@ public class SubscriptionCanonicalizer<S extends IBaseResource> {
|
||||||
return canonicalizeDstu3(theSubscription);
|
return canonicalizeDstu3(theSubscription);
|
||||||
case R4:
|
case R4:
|
||||||
return canonicalizeR4(theSubscription);
|
return canonicalizeR4(theSubscription);
|
||||||
|
case R5:
|
||||||
|
return canonicalizeR5(theSubscription);
|
||||||
case DSTU2_HL7ORG:
|
case DSTU2_HL7ORG:
|
||||||
case DSTU2_1:
|
case DSTU2_1:
|
||||||
default:
|
default:
|
||||||
|
@ -169,6 +173,14 @@ public class SubscriptionCanonicalizer<S extends IBaseResource> {
|
||||||
.stream()
|
.stream()
|
||||||
.collect(Collectors.groupingBy(t -> t.getUrl(), mapping(t -> t.getValueAsPrimitive().getValueAsString(), toList())));
|
.collect(Collectors.groupingBy(t -> t.getUrl(), mapping(t -> t.getValueAsPrimitive().getValueAsString(), toList())));
|
||||||
}
|
}
|
||||||
|
case R5: {
|
||||||
|
org.hl7.fhir.r5.model.Subscription subscription = (org.hl7.fhir.r5.model.Subscription) theSubscription;
|
||||||
|
return subscription
|
||||||
|
.getChannel()
|
||||||
|
.getExtension()
|
||||||
|
.stream()
|
||||||
|
.collect(Collectors.groupingBy(t -> t.getUrl(), mapping(t -> t.getValueAsPrimitive().getValueAsString(), toList())));
|
||||||
|
}
|
||||||
case DSTU2_HL7ORG:
|
case DSTU2_HL7ORG:
|
||||||
case DSTU2_1:
|
case DSTU2_1:
|
||||||
default: {
|
default: {
|
||||||
|
@ -232,6 +244,56 @@ public class SubscriptionCanonicalizer<S extends IBaseResource> {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private CanonicalSubscription canonicalizeR5(IBaseResource theSubscription) {
|
||||||
|
org.hl7.fhir.r5.model.Subscription subscription = (org.hl7.fhir.r5.model.Subscription) theSubscription;
|
||||||
|
|
||||||
|
CanonicalSubscription retVal = new CanonicalSubscription();
|
||||||
|
retVal.setStatus(org.hl7.fhir.r4.model.Subscription.SubscriptionStatus.fromCode(subscription.getStatus().toCode()));
|
||||||
|
retVal.setChannelType(CanonicalSubscriptionChannelType.fromCode(subscription.getChannel().getType().toCode()));
|
||||||
|
retVal.setCriteriaString(subscription.getCriteria());
|
||||||
|
retVal.setEndpointUrl(subscription.getChannel().getEndpoint());
|
||||||
|
retVal.setHeaders(subscription.getChannel().getHeader());
|
||||||
|
retVal.setChannelExtensions(extractExtension(subscription));
|
||||||
|
retVal.setIdElement(subscription.getIdElement());
|
||||||
|
retVal.setPayloadString(subscription.getChannel().getPayload());
|
||||||
|
|
||||||
|
if (retVal.getChannelType() == CanonicalSubscriptionChannelType.EMAIL) {
|
||||||
|
String from;
|
||||||
|
String subjectTemplate;
|
||||||
|
try {
|
||||||
|
from = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_EMAIL_FROM);
|
||||||
|
subjectTemplate = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_SUBJECT_TEMPLATE);
|
||||||
|
} catch (FHIRException theE) {
|
||||||
|
throw new ConfigurationException("Failed to extract subscription extension(s): " + theE.getMessage(), theE);
|
||||||
|
}
|
||||||
|
retVal.getEmailDetails().setFrom(from);
|
||||||
|
retVal.getEmailDetails().setSubjectTemplate(subjectTemplate);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (retVal.getChannelType() == CanonicalSubscriptionChannelType.RESTHOOK) {
|
||||||
|
String stripVersionIds;
|
||||||
|
String deliverLatestVersion;
|
||||||
|
try {
|
||||||
|
stripVersionIds = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_RESTHOOK_STRIP_VERSION_IDS);
|
||||||
|
deliverLatestVersion = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_RESTHOOK_DELIVER_LATEST_VERSION);
|
||||||
|
} catch (FHIRException theE) {
|
||||||
|
throw new ConfigurationException("Failed to extract subscription extension(s): " + theE.getMessage(), theE);
|
||||||
|
}
|
||||||
|
retVal.getRestHookDetails().setStripVersionId(Boolean.parseBoolean(stripVersionIds));
|
||||||
|
retVal.getRestHookDetails().setDeliverLatestVersion(Boolean.parseBoolean(deliverLatestVersion));
|
||||||
|
}
|
||||||
|
|
||||||
|
List<org.hl7.fhir.r5.model.Extension> topicExts = subscription.getExtensionsByUrl("http://hl7.org/fhir/subscription/topics");
|
||||||
|
if (topicExts.size() > 0) {
|
||||||
|
IBaseReference ref = (IBaseReference) topicExts.get(0).getValueAsPrimitive();
|
||||||
|
if (!"EventDefinition".equals(ref.getReferenceElement().getResourceType())) {
|
||||||
|
throw new PreconditionFailedException("Topic reference must be an EventDefinition");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
public String getCriteria(IBaseResource theSubscription) {
|
public String getCriteria(IBaseResource theSubscription) {
|
||||||
switch (myFhirContext.getVersion().getVersion()) {
|
switch (myFhirContext.getVersion().getVersion()) {
|
||||||
case DSTU2:
|
case DSTU2:
|
||||||
|
@ -240,6 +302,8 @@ public class SubscriptionCanonicalizer<S extends IBaseResource> {
|
||||||
return ((org.hl7.fhir.dstu3.model.Subscription) theSubscription).getCriteria();
|
return ((org.hl7.fhir.dstu3.model.Subscription) theSubscription).getCriteria();
|
||||||
case R4:
|
case R4:
|
||||||
return ((org.hl7.fhir.r4.model.Subscription) theSubscription).getCriteria();
|
return ((org.hl7.fhir.r4.model.Subscription) theSubscription).getCriteria();
|
||||||
|
case R5:
|
||||||
|
return ((org.hl7.fhir.r5.model.Subscription) theSubscription).getCriteria();
|
||||||
case DSTU2_1:
|
case DSTU2_1:
|
||||||
case DSTU2_HL7ORG:
|
case DSTU2_HL7ORG:
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -25,10 +25,29 @@
|
||||||
<li>commons-lang3 (Core): 3.8.1 -> 3.9</li>
|
<li>commons-lang3 (Core): 3.8.1 -> 3.9</li>
|
||||||
<li>commons-text (Core): 1.6 -> 1.7</li>
|
<li>commons-text (Core): 1.6 -> 1.7</li>
|
||||||
<li>Guava (JPA): 27.1-jre -> 28.0-jre</li>
|
<li>Guava (JPA): 27.1-jre -> 28.0-jre</li>
|
||||||
<li>
|
|
||||||
</ul>
|
</ul>
|
||||||
]]>
|
]]>
|
||||||
</action>
|
</action>
|
||||||
|
<action type="change">
|
||||||
|
<![CDATA[
|
||||||
|
<b>Breaking Change</b>:
|
||||||
|
The HL7.org DSTU2 structures (and <i>ONLY</i> the HL7.org DSTU2 structures) have been
|
||||||
|
moved to a new package. Where they were previously found in
|
||||||
|
<code>org.hl7.fhir.instance.model</code>
|
||||||
|
they are now found in
|
||||||
|
<code>org.hl7.fhir.dstu2.model</code>. This was done in order to complete the harmonization
|
||||||
|
between the
|
||||||
|
<a href="https://github.com/jamesagnew/hapi-fhir">HAPI FHIR</a>
|
||||||
|
GitHub repository and the
|
||||||
|
<a href="https://github.com/hapifhir/org.hl7.fhir.core/">org.hl7.fhir.core</a>
|
||||||
|
GitHub repository. This is the kind of change we don't make lightly, as we do know that it
|
||||||
|
will be annoying for users of the existing library. It is a change however that will allow us
|
||||||
|
to apply validator fixes much more quickly, and will greatly reduce the amount of effort
|
||||||
|
required to keep up with R5 changes as they come out, so we're hoping it is worth it.
|
||||||
|
Note that no classes are removed, they have only been moved, so it should be fairly straightforward
|
||||||
|
to migrate existing code with an IDE.
|
||||||
|
]]>
|
||||||
|
</action>
|
||||||
<action type="change">
|
<action type="change">
|
||||||
<![CDATA[
|
<![CDATA[
|
||||||
<b>Breaking Change</b>:
|
<b>Breaking Change</b>:
|
||||||
|
|
Loading…
Reference in New Issue