mirror of
https://github.com/hapifhir/hapi-fhir.git
synced 2025-02-17 02:15:22 +00:00
Added ability to create a resthook subscription without returning a payload using an HTTP POST for notification
This commit is contained in:
parent
502f58022e
commit
eb2d21ac4b
@ -2105,14 +2105,14 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IUpdateTyped resource(IBaseResource theResource) {
|
public IUpdateTyped resource(IBaseResource theResource) {
|
||||||
Validate.notNull(theResource, "Resource can not be null");
|
//Validate.notNull(theResource, "Resource can not be null");
|
||||||
myResource = theResource;
|
myResource = theResource;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IUpdateTyped resource(String theResourceBody) {
|
public IUpdateTyped resource(String theResourceBody) {
|
||||||
Validate.notBlank(theResourceBody, "Body can not be null or blank");
|
//Validate.notBlank(theResourceBody, "Body can not be null or blank");
|
||||||
myResourceBody = theResourceBody;
|
myResourceBody = theResourceBody;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -98,11 +98,7 @@ public class FhirResourceDaoSubscriptionDstu3 extends FhirResourceDaoDstu3<Subsc
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void validateChannelPayload(Subscription theResource) {
|
protected void validateChannelPayload(Subscription theResource) {
|
||||||
if (isBlank(theResource.getChannel().getPayload())) {
|
if (!isBlank(theResource.getChannel().getPayload()) && EncodingEnum.forContentType(theResource.getChannel().getPayload()) == null) {
|
||||||
throw new UnprocessableEntityException("Subscription.channel.payload must be populated for rest-hook subscriptions");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (EncodingEnum.forContentType(theResource.getChannel().getPayload()) == null) {
|
|
||||||
throw new UnprocessableEntityException("Invalid value for Subscription.channel.payload: " + theResource.getChannel().getPayload());
|
throw new UnprocessableEntityException("Invalid value for Subscription.channel.payload: " + theResource.getChannel().getPayload());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,11 +99,7 @@ public class FhirResourceDaoSubscriptionR4 extends FhirResourceDaoR4<Subscriptio
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void validateChannelPayload(Subscription theResource) {
|
protected void validateChannelPayload(Subscription theResource) {
|
||||||
if (isBlank(theResource.getChannel().getPayload())) {
|
if (!isBlank(theResource.getChannel().getPayload()) && EncodingEnum.forContentType(theResource.getChannel().getPayload()) == null) {
|
||||||
throw new UnprocessableEntityException("Subscription.channel.payload must be populated for rest-hook subscriptions");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (EncodingEnum.forContentType(theResource.getChannel().getPayload()) == null) {
|
|
||||||
throw new UnprocessableEntityException("Invalid value for Subscription.channel.payload: " + theResource.getChannel().getPayload());
|
throw new UnprocessableEntityException("Invalid value for Subscription.channel.payload: " + theResource.getChannel().getPayload());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,24 +20,26 @@ package ca.uhn.fhir.jpa.subscription.resthook;
|
|||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
||||||
import ca.uhn.fhir.jpa.subscription.*;
|
import ca.uhn.fhir.jpa.subscription.*;
|
||||||
import ca.uhn.fhir.rest.api.Constants;
|
|
||||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||||
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
||||||
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
|
import ca.uhn.fhir.rest.client.api.*;
|
||||||
import ca.uhn.fhir.rest.client.interceptor.SimpleRequestHeaderInterceptor;
|
import ca.uhn.fhir.rest.client.interceptor.SimpleRequestHeaderInterceptor;
|
||||||
import ca.uhn.fhir.rest.gclient.IClientExecutable;
|
import ca.uhn.fhir.rest.gclient.IClientExecutable;
|
||||||
import org.apache.commons.lang3.ObjectUtils;
|
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.r4.model.Subscription;
|
import org.hl7.fhir.r4.model.Subscription;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.messaging.Message;
|
|
||||||
import org.springframework.messaging.MessagingException;
|
import org.springframework.messaging.MessagingException;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
|
||||||
@ -54,10 +56,38 @@ public class SubscriptionDeliveringRestHookSubscriber extends BaseSubscriptionDe
|
|||||||
IClientExecutable<?, ?> operation;
|
IClientExecutable<?, ?> operation;
|
||||||
switch (theMsg.getOperationType()) {
|
switch (theMsg.getOperationType()) {
|
||||||
case CREATE:
|
case CREATE:
|
||||||
operation = theClient.update().resource(payloadResource);
|
if (payloadResource == null || payloadResource.isEmpty()) {
|
||||||
|
if (thePayloadType != null ) {
|
||||||
|
operation = theClient.create().resource(payloadResource);
|
||||||
|
} else {
|
||||||
|
sendNotification(theMsg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (thePayloadType != null ) {
|
||||||
|
operation = theClient.update().resource(payloadResource);
|
||||||
|
} else {
|
||||||
|
sendNotification(theMsg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case UPDATE:
|
case UPDATE:
|
||||||
operation = theClient.update().resource(payloadResource);
|
if (payloadResource == null || payloadResource.isEmpty()) {
|
||||||
|
if (thePayloadType != null ) {
|
||||||
|
operation = theClient.create().resource(payloadResource);
|
||||||
|
} else {
|
||||||
|
sendNotification(theMsg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (thePayloadType != null ) {
|
||||||
|
operation = theClient.update().resource(payloadResource);
|
||||||
|
} else {
|
||||||
|
sendNotification(theMsg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case DELETE:
|
case DELETE:
|
||||||
operation = theClient.delete().resourceById(theMsg.getPayloadId(getContext()));
|
operation = theClient.delete().resourceById(theMsg.getPayloadId(getContext()));
|
||||||
@ -67,11 +97,19 @@ public class SubscriptionDeliveringRestHookSubscriber extends BaseSubscriptionDe
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
operation.encoded(thePayloadType);
|
if (thePayloadType != null) {
|
||||||
|
operation.encoded(thePayloadType);
|
||||||
|
}
|
||||||
|
|
||||||
ourLog.info("Delivering {} rest-hook payload {} for {}", theMsg.getOperationType(), payloadResource.getIdElement().toUnqualified().getValue(), theSubscription.getIdElement(getContext()).toUnqualifiedVersionless().getValue());
|
ourLog.info("Delivering {} rest-hook payload {} for {}", theMsg.getOperationType(), payloadResource.getIdElement().toUnqualified().getValue(), theSubscription.getIdElement(getContext()).toUnqualifiedVersionless().getValue());
|
||||||
|
|
||||||
operation.execute();
|
try {
|
||||||
|
operation.execute();
|
||||||
|
} catch (ResourceNotFoundException e) {
|
||||||
|
ourLog.error("Cannot reach "+ theMsg.getSubscription().getEndpointUrl());
|
||||||
|
e.printStackTrace();
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -83,13 +121,14 @@ public class SubscriptionDeliveringRestHookSubscriber extends BaseSubscriptionDe
|
|||||||
|
|
||||||
// Grab the payload type (encoding mimetype) from the subscription
|
// Grab the payload type (encoding mimetype) from the subscription
|
||||||
String payloadString = subscription.getPayloadString();
|
String payloadString = subscription.getPayloadString();
|
||||||
payloadString = StringUtils.defaultString(payloadString, Constants.CT_FHIR_XML_NEW);
|
EncodingEnum payloadType = null;
|
||||||
if (payloadString.contains(";")) {
|
if(payloadString != null) {
|
||||||
payloadString = payloadString.substring(0, payloadString.indexOf(';'));
|
if (payloadString.contains(";")) {
|
||||||
|
payloadString = payloadString.substring(0, payloadString.indexOf(';'));
|
||||||
|
}
|
||||||
|
payloadString = payloadString.trim();
|
||||||
|
payloadType = EncodingEnum.forContentType(payloadString);
|
||||||
}
|
}
|
||||||
payloadString = payloadString.trim();
|
|
||||||
EncodingEnum payloadType = EncodingEnum.forContentType(payloadString);
|
|
||||||
payloadType = ObjectUtils.defaultIfNull(payloadType, EncodingEnum.XML);
|
|
||||||
|
|
||||||
// Create the client request
|
// Create the client request
|
||||||
getContext().getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
|
getContext().getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
|
||||||
@ -109,4 +148,23 @@ public class SubscriptionDeliveringRestHookSubscriber extends BaseSubscriptionDe
|
|||||||
deliverPayload(theMessage, subscription, payloadType, client);
|
deliverPayload(theMessage, subscription, payloadType, client);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a POST notification without a payload
|
||||||
|
* @param theMsg
|
||||||
|
*/
|
||||||
|
protected void sendNotification(ResourceDeliveryMessage theMsg) {
|
||||||
|
FhirContext context= getContext();
|
||||||
|
Map<String, List<String>> params = new HashMap();
|
||||||
|
List<Header> headers = new ArrayList<>();
|
||||||
|
StringBuilder url = new StringBuilder(theMsg.getSubscription().getEndpointUrl());
|
||||||
|
IHttpClient client = context.getRestfulClientFactory().getHttpClient(url, params, "", RequestTypeEnum.POST, headers);
|
||||||
|
IHttpRequest request = client.createParamRequest(context, params, null);
|
||||||
|
try {
|
||||||
|
IHttpResponse response = request.execute();
|
||||||
|
} catch (IOException e) {
|
||||||
|
ourLog.error("Error trying to reach "+ theMsg.getSubscription().getEndpointUrl());
|
||||||
|
e.printStackTrace();
|
||||||
|
throw new ResourceNotFoundException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ import static org.junit.Assert.fail;
|
|||||||
*/
|
*/
|
||||||
public class RestHookTestDstu3Test extends BaseResourceProviderDstu3Test {
|
public class RestHookTestDstu3Test extends BaseResourceProviderDstu3Test {
|
||||||
|
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestHookTestDstu2Test.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestHookTestDstu3Test.class);
|
||||||
private static List<Observation> ourCreatedObservations = Lists.newArrayList();
|
private static List<Observation> ourCreatedObservations = Lists.newArrayList();
|
||||||
private static int ourListenerPort;
|
private static int ourListenerPort;
|
||||||
private static RestfulServer ourListenerRestServer;
|
private static RestfulServer ourListenerRestServer;
|
||||||
@ -311,6 +311,25 @@ public class RestHookTestDstu3Test extends BaseResourceProviderDstu3Test {
|
|||||||
assertEquals(Constants.CT_FHIR_XML_NEW, ourContentTypes.get(0));
|
assertEquals(Constants.CT_FHIR_XML_NEW, ourContentTypes.get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRestHookSubscriptionWithoutPayload() throws Exception {
|
||||||
|
String payload = "";
|
||||||
|
|
||||||
|
String code = "1000000050";
|
||||||
|
String criteria1 = "Observation?code=SNOMED-CT|" + code;
|
||||||
|
String criteria2 = "Observation?code=SNOMED-CT|" + code + "111";
|
||||||
|
|
||||||
|
Subscription subscription1 = createSubscription(criteria1, payload, ourListenerServerBase);
|
||||||
|
Subscription subscription2 = createSubscription(criteria2, payload, ourListenerServerBase);
|
||||||
|
|
||||||
|
Observation observation1 = sendObservation(code, "SNOMED-CT");
|
||||||
|
|
||||||
|
// Should see 1 subscription notification, but no payload
|
||||||
|
waitForQueueToDrain();
|
||||||
|
waitForSize(0, ourCreatedObservations);
|
||||||
|
waitForSize(0, ourUpdatedObservations);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Reenable this
|
// TODO: Reenable this
|
||||||
@Test
|
@Test
|
||||||
@Ignore
|
@Ignore
|
||||||
|
Loading…
x
Reference in New Issue
Block a user