diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/TransactionBuilder.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/TransactionBuilder.java
new file mode 100644
index 00000000000..80202430033
--- /dev/null
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/TransactionBuilder.java
@@ -0,0 +1,110 @@
+package ca.uhn.fhir.util;
+
+import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
+import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.context.RuntimeResourceDefinition;
+import org.hl7.fhir.instance.model.api.IBase;
+import org.hl7.fhir.instance.model.api.IBaseBundle;
+import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.hl7.fhir.instance.model.api.IPrimitiveType;
+import org.thymeleaf.util.Validate;
+
+/**
+ * This class can be used to build a Bundle resource to be used as a FHIR transaction.
+ *
+ * This is not yet complete, and doesn't support all FHIR features. USE WITH CAUTION as the API
+ * may change.
+ *
+ * @since 5.1.0
+ */
+public class TransactionBuilder {
+
+ private final FhirContext myContext;
+ private final IBaseBundle myBundle;
+ private final RuntimeResourceDefinition myBundleDef;
+ private final BaseRuntimeChildDefinition myEntryChild;
+ private final BaseRuntimeElementDefinition> myEntryDef;
+ private final BaseRuntimeChildDefinition myEntryResourceChild;
+ private final BaseRuntimeChildDefinition myEntryFullUrlChild;
+ private final BaseRuntimeChildDefinition myEntryRequestChild;
+ private final BaseRuntimeElementDefinition> myEntryRequestDef;
+ private final BaseRuntimeChildDefinition myEntryRequestUrlChild;
+ private final BaseRuntimeChildDefinition myEntryRequestMethodChild;
+ private final BaseRuntimeElementDefinition> myEntryRequestMethodDef;
+
+ /**
+ * Constructor
+ */
+ public TransactionBuilder(FhirContext theContext) {
+ myContext = theContext;
+
+ myBundleDef = myContext.getResourceDefinition("Bundle");
+ myBundle = (IBaseBundle) myBundleDef.newInstance();
+
+ BaseRuntimeChildDefinition typeChild = myBundleDef.getChildByName("type");
+ IPrimitiveType> type = (IPrimitiveType>) typeChild.getChildByName("type").newInstance(typeChild.getInstanceConstructorArguments());
+ type.setValueAsString("transaction");
+ typeChild.getMutator().setValue(myBundle, type);
+
+ myEntryChild = myBundleDef.getChildByName("entry");
+ myEntryDef = myEntryChild.getChildByName("entry");
+
+ myEntryResourceChild = myEntryDef.getChildByName("resource");
+ myEntryFullUrlChild = myEntryDef.getChildByName("fullUrl");
+
+ myEntryRequestChild = myEntryDef.getChildByName("request");
+ myEntryRequestDef = myEntryRequestChild.getChildByName("request");
+
+ myEntryRequestUrlChild = myEntryRequestDef.getChildByName("url");
+
+ myEntryRequestMethodChild = myEntryRequestDef.getChildByName("method");
+ myEntryRequestMethodDef = myEntryRequestMethodChild.getChildByName("method");
+
+
+ }
+
+ /**
+ * Adds an entry containing an update (PUT) request
+ *
+ * @param theResource The resource to update
+ */
+ public TransactionBuilder addUpdateEntry(IBaseResource theResource) {
+ Validate.notNull(theResource, "theResource must not be null");
+ Validate.notEmpty(theResource.getIdElement().getValue(), "theResource must have an ID");
+
+ IBase entry = myEntryDef.newInstance();
+ myEntryChild.getMutator().addValue(myBundle, entry);
+
+ // Bundle.entry.fullUrl
+ IPrimitiveType> fullUrl = (IPrimitiveType>) myContext.getElementDefinition("uri").newInstance();
+ fullUrl.setValueAsString(theResource.getIdElement().getValue());
+ myEntryFullUrlChild.getMutator().setValue(entry, fullUrl);
+
+ // Bundle.entry.resource
+ myEntryResourceChild.getMutator().setValue(entry, theResource);
+
+ // Bundle.entry.request
+ IBase request = myEntryRequestDef.newInstance();
+ myEntryRequestChild.getMutator().setValue(entry, request);
+
+ // Bundle.entry.request.url
+ IPrimitiveType> url = (IPrimitiveType>) myContext.getElementDefinition("uri").newInstance();
+ url.setValueAsString(theResource.getIdElement().toUnqualifiedVersionless().getValue());
+ myEntryRequestUrlChild.getMutator().setValue(request, url);
+
+ // Bundle.entry.request.url
+ IPrimitiveType> method = (IPrimitiveType>) myEntryRequestMethodDef.newInstance(myEntryRequestMethodChild.getInstanceConstructorArguments());
+ method.setValueAsString("PUT");
+ myEntryRequestMethodChild.getMutator().setValue(request, method);
+
+ return this;
+ }
+
+
+
+ public IBaseBundle getBundle() {
+ return myBundle;
+ }
+
+}
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionsR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionsR4Test.java
index b32f8696886..4f3f6c0b38a 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionsR4Test.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionsR4Test.java
@@ -8,6 +8,7 @@ import ca.uhn.fhir.jpa.subscription.submit.interceptor.SubscriptionMatcherInterc
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;
@@ -211,7 +212,8 @@ public abstract class BaseSubscriptionsR4Test extends BaseResourceProviderR4Test
public static class PlainProvider {
@Transaction
- public Bundle transaction(@RequestParam Bundle theInput) {
+ public Bundle transaction(@TransactionParam Bundle theInput) {
+ ourLog.info("Received transaction update");
ourTransactions.add(theInput);
return theInput;
}
@@ -231,8 +233,8 @@ public abstract class BaseSubscriptionsR4Test extends BaseResourceProviderR4Test
public static void startListenerServer() throws Exception {
RestfulServer ourListenerRestServer = new RestfulServer(FhirContext.forR4());
- ObservationResourceProvider observationResourceProvider = new ObservationResourceProvider();
- ourListenerRestServer.setResourceProviders(observationResourceProvider);
+ ourListenerRestServer.registerProvider(new ObservationResourceProvider());
+ ourListenerRestServer.registerProvider(new PlainProvider());
ourListenerServer = new Server(0);
diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestR4Test.java
index 3d64b37a81a..f9099ce9317 100644
--- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestR4Test.java
+++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/resthook/RestHookTestR4Test.java
@@ -1014,10 +1014,10 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
@Test
- public void testDeliverSearchSet() throws Exception {
+ public void testDeliverSearchResult() throws Exception {
{
Subscription subscription = newSubscription("Observation?", "application/json");
- subscription.addExtension(JpaConstants.EXT_SUBSCRIPTION_DELIVER_BUNDLE_SEARCH_RESULT, new StringType("Observation?_id=${resource_id}&_include=*"));
+ subscription.addExtension(JpaConstants.EXT_SUBSCRIPTION_PAYLOAD_SEARCH_RESULT, new StringType("Observation?_id=${matched_resource_id}&_include=*"));
MethodOutcome methodOutcome = ourClient.create().resource(subscription).execute();
mySubscriptionIds.add(methodOutcome.getId());
waitForActivatedSubscriptionCount(1);
@@ -1030,11 +1030,17 @@ public class RestHookTestR4Test extends BaseSubscriptionsR4Test {
Observation observation = new Observation();
observation.addExtension().setUrl("Observation#accessType").setValue(new Coding().setCode("Catheter"));
- observation.getSubject().setReferenceElement(patientId);
+ observation.getSubject().setReferenceElement(patientId.toUnqualifiedVersionless());
MethodOutcome methodOutcome = ourClient.create().resource(observation).execute();
assertEquals(true, methodOutcome.getCreated());
+
waitForQueueToDrain();
waitForSize(1, ourTransactions);
+
+ ourLog.info("Received transaction: {}", myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(ourTransactions.get(0)));
+
+ Bundle xact = ourTransactions.get(0);
+ assertEquals(2, xact.getEntry().size());
}
}
diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/util/JpaConstants.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/util/JpaConstants.java
index 404b4a7e334..ab7e5c45324 100644
--- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/util/JpaConstants.java
+++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/util/JpaConstants.java
@@ -215,9 +215,9 @@ public class JpaConstants {
*/
public static final String EXT_EXTERNALIZED_BINARY_ID = "http://hapifhir.io/fhir/StructureDefinition/externalized-binary-id";
/**
- * For subscription, deliver a bundle containinf a search result instead of just a single resource
+ * For subscription, deliver a bundle containing a search result instead of just a single resource
*/
- public static final String EXT_SUBSCRIPTION_DELIVER_BUNDLE_SEARCH_RESULT = "http://hapifhir.io/fhir/StructureDefinition/subscription-deliver-bundle-search-result";
+ public static final String EXT_SUBSCRIPTION_PAYLOAD_SEARCH_RESULT = "http://hapifhir.io/fhir/StructureDefinition/subscription-payload-search-result";
/**
* Placed in system-generated extensions
*/
diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/message/SubscriptionDeliveringMessageSubscriber.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/message/SubscriptionDeliveringMessageSubscriber.java
index 9c27cd42584..561904a423f 100644
--- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/message/SubscriptionDeliveringMessageSubscriber.java
+++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/message/SubscriptionDeliveringMessageSubscriber.java
@@ -74,7 +74,7 @@ public class SubscriptionDeliveringMessageSubscriber extends BaseSubscriptionDel
public void handleMessage(ResourceDeliveryMessage theMessage) throws MessagingException, URISyntaxException {
CanonicalSubscription subscription = theMessage.getSubscription();
- // Interceptor call: SUBSCRIPTION_BEFORE_REST_HOOK_DELIVERY
+ // Interceptor call: SUBSCRIPTION_BEFORE_MESSAGE_DELIVERY
HookParams params = new HookParams()
.add(CanonicalSubscription.class, subscription)
.add(ResourceDeliveryMessage.class, theMessage);
@@ -104,7 +104,7 @@ public class SubscriptionDeliveringMessageSubscriber extends BaseSubscriptionDel
deliverPayload(theMessage, subscription, channelProducer);
- // Interceptor call: SUBSCRIPTION_AFTER_REST_HOOK_DELIVERY
+ // Interceptor call: SUBSCRIPTION_AFTER_MESSAGE_DELIVERY
params = new HookParams()
.add(CanonicalSubscription.class, subscription)
.add(ResourceDeliveryMessage.class, theMessage);
diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/resthook/SubscriptionDeliveringRestHookSubscriber.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/resthook/SubscriptionDeliveringRestHookSubscriber.java
index cc5dfc428af..f4032eb709e 100644
--- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/resthook/SubscriptionDeliveringRestHookSubscriber.java
+++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/resthook/SubscriptionDeliveringRestHookSubscriber.java
@@ -25,11 +25,14 @@ import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
+import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
+import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.subscription.match.deliver.BaseSubscriptionDeliverySubscriber;
import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription;
import ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryMessage;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
+import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.client.api.Header;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.api.IHttpClient;
@@ -40,7 +43,10 @@ import ca.uhn.fhir.rest.client.interceptor.SimpleRequestHeaderInterceptor;
import ca.uhn.fhir.rest.gclient.IClientExecutable;
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
+import ca.uhn.fhir.util.TransactionBuilder;
import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.text.StringSubstitutor;
+import org.apache.http.NameValuePair;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.slf4j.Logger;
@@ -65,6 +71,9 @@ public class SubscriptionDeliveringRestHookSubscriber extends BaseSubscriptionDe
@Autowired
private DaoRegistry myDaoRegistry;
+ @Autowired
+ private MatchUrlService myMatchUrlService;
+
/**
* Constructor
*/
@@ -81,31 +90,57 @@ public class SubscriptionDeliveringRestHookSubscriber extends BaseSubscriptionDe
protected void doDelivery(ResourceDeliveryMessage theMsg, CanonicalSubscription theSubscription, EncodingEnum thePayloadType, IGenericClient theClient, IBaseResource thePayloadResource) {
IClientExecutable, ?> operation;
- switch (theMsg.getOperationType()) {
- case CREATE:
- case UPDATE:
- if (thePayloadResource == null || thePayloadResource.isEmpty()) {
- if (thePayloadType != null) {
- operation = theClient.create().resource(thePayloadResource);
+
+ if (isNotBlank(theSubscription.getPayloadSearchResult())) {
+ String resType = theSubscription.getPayloadSearchResult().substring(0, theSubscription.getPayloadSearchResult().indexOf('?'));
+ IFhirResourceDao> dao = myDaoRegistry.getResourceDao(resType);
+ RuntimeResourceDefinition resourceDefinition = myFhirContext.getResourceDefinition(resType);
+
+ String payloadUrl = theSubscription.getPayloadSearchResult();
+ Map valueMap = new HashMap<>(1);
+ valueMap.put("matched_resource_id", thePayloadResource.getIdElement().toUnqualifiedVersionless().getValue());
+ payloadUrl = new StringSubstitutor(valueMap).replace(payloadUrl);
+ SearchParameterMap payloadSearchMap = myMatchUrlService.translateMatchUrl(payloadUrl, resourceDefinition);
+ payloadSearchMap.setLoadSynchronous(true);
+
+ IBundleProvider searchResults = dao.search(payloadSearchMap);
+
+ TransactionBuilder builder = new TransactionBuilder(myFhirContext);
+ for (IBaseResource next : searchResults.getResources(0, searchResults.size())) {
+ builder.addUpdateEntry(next);
+ }
+
+ operation = theClient.transaction().withBundle(builder.getBundle());
+
+ } else {
+
+ switch (theMsg.getOperationType()) {
+ case CREATE:
+ case UPDATE:
+ if (thePayloadResource == null || thePayloadResource.isEmpty()) {
+ if (thePayloadType != null) {
+ operation = theClient.create().resource(thePayloadResource);
+ } else {
+ sendNotification(theMsg);
+ return;
+ }
} else {
- sendNotification(theMsg);
- return;
+ if (thePayloadType != null) {
+ operation = theClient.update().resource(thePayloadResource);
+ } else {
+ sendNotification(theMsg);
+ return;
+ }
}
- } else {
- if (thePayloadType != null) {
- operation = theClient.update().resource(thePayloadResource);
- } else {
- sendNotification(theMsg);
- return;
- }
- }
- break;
- case DELETE:
- operation = theClient.delete().resourceById(theMsg.getPayloadId(myFhirContext));
- break;
- default:
- ourLog.warn("Ignoring delivery message of type: {}", theMsg.getOperationType());
- return;
+ break;
+ case DELETE:
+ operation = theClient.delete().resourceById(theMsg.getPayloadId(myFhirContext));
+ break;
+ default:
+ ourLog.warn("Ignoring delivery message of type: {}", theMsg.getOperationType());
+ return;
+ }
+
}
if (thePayloadType != null) {
diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/registry/SubscriptionCanonicalizer.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/registry/SubscriptionCanonicalizer.java
index 0f60e299fa3..8440c9f7e6e 100644
--- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/registry/SubscriptionCanonicalizer.java
+++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/registry/SubscriptionCanonicalizer.java
@@ -113,6 +113,7 @@ public class SubscriptionCanonicalizer {
retVal.setChannelExtensions(extractExtension(subscription));
retVal.setIdElement(subscription.getIdElement());
retVal.setPayloadString(subscription.getChannel().getPayload());
+ retVal.setPayloadSearchResult(getExtensionString(subscription, JpaConstants.EXT_SUBSCRIPTION_PAYLOAD_SEARCH_RESULT));
if (retVal.getChannelType() == CanonicalSubscriptionChannelType.EMAIL) {
String from;
@@ -208,6 +209,7 @@ public class SubscriptionCanonicalizer {
retVal.setChannelExtensions(extractExtension(subscription));
retVal.setIdElement(subscription.getIdElement());
retVal.setPayloadString(subscription.getChannel().getPayload());
+ retVal.setPayloadSearchResult(getExtensionString(subscription, JpaConstants.EXT_SUBSCRIPTION_PAYLOAD_SEARCH_RESULT));
if (retVal.getChannelType() == CanonicalSubscriptionChannelType.EMAIL) {
String from;
@@ -261,7 +263,7 @@ public class SubscriptionCanonicalizer {
retVal.setChannelExtensions(extractExtension(subscription));
retVal.setIdElement(subscription.getIdElement());
retVal.setPayloadString(subscription.getContentType());
- retVal.setDeliverBundleSearchResult(getExtensionString(subscription, JpaConstants.EXT_SUBSCRIPTION_DELIVER_BUNDLE_SEARCH_RESULT));
+ retVal.setPayloadSearchResult(getExtensionString(subscription, JpaConstants.EXT_SUBSCRIPTION_PAYLOAD_SEARCH_RESULT));
if (retVal.getChannelType() == CanonicalSubscriptionChannelType.EMAIL) {
String from;
diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/model/CanonicalSubscription.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/model/CanonicalSubscription.java
index fcee7de1178..c78780153e4 100644
--- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/model/CanonicalSubscription.java
+++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/model/CanonicalSubscription.java
@@ -34,7 +34,11 @@ import org.hl7.fhir.r4.model.Subscription;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.Serializable;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
@@ -64,8 +68,8 @@ public class CanonicalSubscription implements Serializable, Cloneable, IModelJso
private RestHookDetails myRestHookDetails;
@JsonProperty("extensions")
private Map> myChannelExtensions;
- @JsonProperty("deliverBundleSearchResult")
- private String myDeliverBundleSearchResult;
+ @JsonProperty("payloadSearchResult")
+ private String myPayloadSearchResult;
/**
* Constructor
@@ -74,6 +78,14 @@ public class CanonicalSubscription implements Serializable, Cloneable, IModelJso
super();
}
+ public String getPayloadSearchResult() {
+ return myPayloadSearchResult;
+ }
+
+ public void setPayloadSearchResult(String thePayloadSearchResult) {
+ myPayloadSearchResult = thePayloadSearchResult;
+ }
+
/**
* For now we're using the R4 TriggerDefinition, but this
* may change in the future when things stabilize
@@ -82,7 +94,6 @@ public class CanonicalSubscription implements Serializable, Cloneable, IModelJso
myTrigger = theTrigger;
}
-
public CanonicalSubscriptionChannelType getChannelType() {
return myChannelType;
}
@@ -138,7 +149,7 @@ public class CanonicalSubscription implements Serializable, Cloneable, IModelJso
public String getChannelExtension(String theUrl) {
String retVal = null;
List strings = myChannelExtensions.get(theUrl);
- if (strings != null && strings.isEmpty()==false) {
+ if (strings != null && strings.isEmpty() == false) {
retVal = strings.get(0);
}
return retVal;
@@ -278,11 +289,24 @@ public class CanonicalSubscription implements Serializable, Cloneable, IModelJso
}
}
- public void setDeliverBundleSearchResult(String theDeliverBundleSearchResult) {
- myDeliverBundleSearchResult = theDeliverBundleSearchResult;
- }
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .append("myIdElement", myIdElement)
+ .append("myStatus", myStatus)
+ .append("myCriteriaString", myCriteriaString)
+ .append("myEndpointUrl", myEndpointUrl)
+ .append("myPayloadString", myPayloadString)
+// .append("myHeaders", myHeaders)
+ .append("myChannelType", myChannelType)
+// .append("myTrigger", myTrigger)
+// .append("myEmailDetails", myEmailDetails)
+// .append("myRestHookDetails", myRestHookDetails)
+// .append("myChannelExtensions", myChannelExtensions)
+ .toString();
+ }
- public static class EmailDetails implements IModelJson {
+ public static class EmailDetails implements IModelJson {
@JsonProperty("from")
private String myFrom;
@@ -400,21 +424,4 @@ public class CanonicalSubscription implements Serializable, Cloneable, IModelJso
}
}
-
- @Override
- public String toString() {
- return new ToStringBuilder(this)
- .append("myIdElement", myIdElement)
- .append("myStatus", myStatus)
- .append("myCriteriaString", myCriteriaString)
- .append("myEndpointUrl", myEndpointUrl)
- .append("myPayloadString", myPayloadString)
-// .append("myHeaders", myHeaders)
- .append("myChannelType", myChannelType)
-// .append("myTrigger", myTrigger)
-// .append("myEmailDetails", myEmailDetails)
-// .append("myRestHookDetails", myRestHookDetails)
-// .append("myChannelExtensions", myChannelExtensions)
- .toString();
- }
}
diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/submit/interceptor/SubscriptionValidatingInterceptor.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/submit/interceptor/SubscriptionValidatingInterceptor.java
index 267308f9b4a..3c5b7a1dc91 100644
--- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/submit/interceptor/SubscriptionValidatingInterceptor.java
+++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/submit/interceptor/SubscriptionValidatingInterceptor.java
@@ -25,6 +25,7 @@ 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.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.SubscriptionStrategyEvaluator;
import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionCanonicalizer;
@@ -96,32 +97,19 @@ public class SubscriptionValidatingInterceptor {
if (!finished) {
- String query = subscription.getCriteriaString();
- if (isBlank(query)) {
- throw new UnprocessableEntityException("Subscription.criteria must be populated");
- }
+ validateQuery(subscription.getCriteriaString(), "Subscription.criteria");
- int sep = query.indexOf('?');
- if (sep <= 1) {
- throw new UnprocessableEntityException("Subscription.criteria must be in the form \"{Resource Type}?[params]\"");
- }
-
- String resType = query.substring(0, sep);
- if (resType.contains("/")) {
- throw new UnprocessableEntityException("Subscription.criteria must be in the form \"{Resource Type}?[params]\"");
+ if (subscription.getPayloadSearchResult() != null) {
+ validateQuery(subscription.getPayloadSearchResult(), "Subscription.extension(url='" + JpaConstants.EXT_SUBSCRIPTION_PAYLOAD_SEARCH_RESULT + "')");
}
validateChannelType(subscription);
- if (!myDaoRegistry.isResourceTypeSupported(resType)) {
- throw new UnprocessableEntityException("Subscription.criteria contains invalid/unsupported resource type: " + resType);
- }
-
try {
- SubscriptionMatchingStrategy strategy = mySubscriptionStrategyEvaluator.determineStrategy(query);
+ SubscriptionMatchingStrategy strategy = mySubscriptionStrategyEvaluator.determineStrategy(subscription.getCriteriaString());
mySubscriptionCanonicalizer.setMatchingStrategyTag(theSubscription, strategy);
} catch (InvalidRequestException | DataFormatException e) {
- throw new UnprocessableEntityException("Invalid subscription criteria submitted: " + query + " " + e.getMessage());
+ throw new UnprocessableEntityException("Invalid subscription criteria submitted: " + subscription.getCriteriaString() + " " + e.getMessage());
}
if (subscription.getChannelType() == null) {
@@ -129,6 +117,28 @@ public class SubscriptionValidatingInterceptor {
} else if (subscription.getChannelType() == CanonicalSubscriptionChannelType.MESSAGE) {
validateMessageSubscriptionEndpoint(subscription.getEndpointUrl());
}
+
+
+ }
+ }
+
+ public void validateQuery(String theQuery, String theFieldName) {
+ if (isBlank(theQuery)) {
+ throw new UnprocessableEntityException(theFieldName + " must be populated");
+ }
+
+ int sep = theQuery.indexOf('?');
+ if (sep <= 1) {
+ throw new UnprocessableEntityException(theFieldName + " must be in the form \"{Resource Type}?[params]\"");
+ }
+
+ String resType = theQuery.substring(0, sep);
+ if (resType.contains("/")) {
+ 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);
}
}
diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/util/TransactionBuilderTest.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/util/TransactionBuilderTest.java
new file mode 100644
index 00000000000..2d7887131a2
--- /dev/null
+++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/util/TransactionBuilderTest.java
@@ -0,0 +1,40 @@
+package ca.uhn.fhir.util;
+
+import ca.uhn.fhir.context.FhirContext;
+import org.hl7.fhir.r4.model.Bundle;
+import org.hl7.fhir.r4.model.Patient;
+import org.hl7.fhir.r4.model.codesystems.HttpVerb;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+
+public class TransactionBuilderTest {
+ private static final Logger ourLog = LoggerFactory.getLogger(TransactionBuilderTest.class);
+ private FhirContext myFhirContext = FhirContext.forR4();
+
+ @Test
+ public void testAddEntryUpdate() {
+ TransactionBuilder builder = new TransactionBuilder(myFhirContext);
+
+ Patient patient = new Patient();
+ patient.setId("http://foo/Patient/123");
+ patient.setActive(true);
+ builder.addUpdateEntry(patient);
+
+ Bundle bundle = (Bundle) builder.getBundle();
+ ourLog.info("Bundle:\n{}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle));
+
+ assertEquals(Bundle.BundleType.TRANSACTION, bundle.getType());
+ assertEquals(1, bundle.getEntry().size());
+ assertSame(patient, bundle.getEntry().get(0).getResource());
+ assertEquals("http://foo/Patient/123", bundle.getEntry().get(0).getFullUrl());
+ assertEquals("Patient/123", bundle.getEntry().get(0).getRequest().getUrl());
+ assertEquals(Bundle.HTTPVerb.PUT, bundle.getEntry().get(0).getRequest().getMethod());
+
+
+ }
+
+}