From 8bfd7913c8e9c091be37ce6dc64300a780c4b9a4 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Sun, 15 Apr 2018 17:19:34 -0400 Subject: [PATCH] More work on loinc loader --- .../uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java | 2 +- .../jpa/dao/dstu3/FhirSystemDaoDstu3.java | 2 +- .../uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java | 2 +- .../BaseSubscriptionInterceptor.java | 26 ++++++++++ .../subscription/CanonicalSubscription.java | 51 ++++++++++++++++--- ...scriptionDeliveringRestHookSubscriber.java | 44 +++++++++++++--- .../jpa/term/BaseHapiTerminologySvcImpl.java | 4 +- .../ca/uhn/fhir/jpa/util/JpaConstants.java | 29 +++++++++++ .../subscription/r4/RestHookTestR4Test.java | 35 +++++++++++++ .../derby_maintenance.txt | 1 - 10 files changed, 174 insertions(+), 22 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java index b6a5e7e8de9..b6893c13e14 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java @@ -209,7 +209,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao { for (int i = 0; i < theRequest.getEntry().size(); i++) { if (i % 100 == 0) { - ourLog.info("Processed {} non-GET entries out of {}", i, theRequest.getEntry().size()); + ourLog.debug("Processed {} non-GET entries out of {}", i, theRequest.getEntry().size()); } Entry nextReqEntry = theRequest.getEntry().get(i); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java index 19ac3bf479a..3bbb31c17e2 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java @@ -318,7 +318,7 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao { for (int i = 0; i < theEntries.size(); i++) { if (i % 100 == 0) { - ourLog.info("Processed {} non-GET entries out of {}", i, theEntries.size()); + ourLog.debug("Processed {} non-GET entries out of {}", i, theEntries.size()); } BundleEntryComponent nextReqEntry = theEntries.get(i); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java index 04e42e6d6e4..fa241a6b37e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java @@ -333,7 +333,7 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao { for (int i = 0; i < theEntries.size(); i++) { if (i % 100 == 0) { - ourLog.info("Processed {} non-GET entries out of {}", i, theEntries.size()); + ourLog.debug("Processed {} non-GET entries out of {}", i, theEntries.size()); } BundleEntryComponent nextReqEntry = theEntries.get(i); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionInterceptor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionInterceptor.java index a22396e22ff..c427ed579a9 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionInterceptor.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionInterceptor.java @@ -168,6 +168,19 @@ public abstract class BaseSubscriptionInterceptor exten retVal.getEmailDetails().setSubjectTemplate(subjectTemplate); } + if (retVal.getChannelType() == Subscription.SubscriptionChannelType.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)); + } + } catch (FHIRException theE) { throw new InternalErrorException(theE); } @@ -201,6 +214,19 @@ public abstract class BaseSubscriptionInterceptor exten retVal.getEmailDetails().setSubjectTemplate(subjectTemplate); } + if (retVal.getChannelType() == Subscription.SubscriptionChannelType.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 topicExts = subscription.getExtensionsByUrl("http://hl7.org/fhir/subscription/topics"); if (topicExts.size() > 0) { IBaseReference ref = (IBaseReference) topicExts.get(0).getValueAsPrimitive(); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/CanonicalSubscription.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/CanonicalSubscription.java index 0c54a644790..aa75ce9dfb6 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/CanonicalSubscription.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/CanonicalSubscription.java @@ -67,6 +67,8 @@ public class CanonicalSubscription implements Serializable { private CanonicalEventDefinition myTrigger; @JsonProperty("emailDetails") private EmailDetails myEmailDetails; + @JsonProperty("restHookDetails") + private RestHookDetails myRestHookDetails; /** * For now we're using the R4 TriggerDefinition, but this @@ -131,12 +133,10 @@ public class CanonicalSubscription implements Serializable { return myHeaders; } - public void setHeaders(List> theHeader) { + public void setHeaders(String theHeaders) { myHeaders = new ArrayList<>(); - for (IPrimitiveType next : theHeader) { - if (isNotBlank(next.getValueAsString())) { - myHeaders.add(next.getValueAsString()); - } + if (isNotBlank(theHeaders)) { + myHeaders.add(theHeaders); } } @@ -160,6 +160,13 @@ public class CanonicalSubscription implements Serializable { myPayloadString = thePayloadString; } + public RestHookDetails getRestHookDetails() { + if (myRestHookDetails == null) { + myRestHookDetails = new RestHookDetails(); + } + return myRestHookDetails; + } + public Subscription.SubscriptionStatus getStatus() { return myStatus; } @@ -191,10 +198,12 @@ public class CanonicalSubscription implements Serializable { } } - public void setHeaders(String theHeaders) { + public void setHeaders(List> theHeader) { myHeaders = new ArrayList<>(); - if (isNotBlank(theHeaders)) { - myHeaders.add(theHeaders); + for (IPrimitiveType next : theHeader) { + if (isNotBlank(next.getValueAsString())) { + myHeaders.add(next.getValueAsString()); + } } } @@ -230,6 +239,32 @@ public class CanonicalSubscription implements Serializable { } } + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonAutoDetect(creatorVisibility = JsonAutoDetect.Visibility.NONE, fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE) + public static class RestHookDetails { + @JsonProperty("stripVersionId") + private boolean myStripVersionId; + @JsonProperty("deliverLatestVersion") + private boolean myDeliverLatestVersion; + + public boolean isDeliverLatestVersion() { + return myDeliverLatestVersion; + } + + public void setDeliverLatestVersion(boolean theDeliverLatestVersion) { + myDeliverLatestVersion = theDeliverLatestVersion; + } + + public boolean isStripVersionId() { + return myStripVersionId; + } + + public void setStripVersionId(boolean theStripVersionId) { + myStripVersionId = theStripVersionId; + } + + } + @JsonInclude(JsonInclude.Include.NON_NULL) @JsonAutoDetect(creatorVisibility = JsonAutoDetect.Visibility.NONE, fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE) public static class CanonicalEventDefinition { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/resthook/SubscriptionDeliveringRestHookSubscriber.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/resthook/SubscriptionDeliveringRestHookSubscriber.java index a04b44af8c1..65881b7d2b3 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/resthook/SubscriptionDeliveringRestHookSubscriber.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/resthook/SubscriptionDeliveringRestHookSubscriber.java @@ -28,8 +28,10 @@ import ca.uhn.fhir.rest.api.RequestTypeEnum; import ca.uhn.fhir.rest.client.api.*; 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 org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.Subscription; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -51,21 +53,26 @@ public class SubscriptionDeliveringRestHookSubscriber extends BaseSubscriptionDe } protected void deliverPayload(ResourceDeliveryMessage theMsg, CanonicalSubscription theSubscription, EncodingEnum thePayloadType, IGenericClient theClient) { - IBaseResource payloadResource = theMsg.getPayload(getContext()); + IBaseResource payloadResource = getAndMassagePayload(theMsg, theSubscription); + if (payloadResource == null) return; + doDelivery(theMsg, theSubscription, thePayloadType, theClient, payloadResource); + } + + protected void doDelivery(ResourceDeliveryMessage theMsg, CanonicalSubscription theSubscription, EncodingEnum thePayloadType, IGenericClient theClient, IBaseResource thePayloadResource) { IClientExecutable operation; switch (theMsg.getOperationType()) { case CREATE: - if (payloadResource == null || payloadResource.isEmpty()) { + if (thePayloadResource == null || thePayloadResource.isEmpty()) { if (thePayloadType != null ) { - operation = theClient.create().resource(payloadResource); + operation = theClient.create().resource(thePayloadResource); } else { sendNotification(theMsg); return; } } else { if (thePayloadType != null ) { - operation = theClient.update().resource(payloadResource); + operation = theClient.update().resource(thePayloadResource); } else { sendNotification(theMsg); return; @@ -73,16 +80,16 @@ public class SubscriptionDeliveringRestHookSubscriber extends BaseSubscriptionDe } break; case UPDATE: - if (payloadResource == null || payloadResource.isEmpty()) { + if (thePayloadResource == null || thePayloadResource.isEmpty()) { if (thePayloadType != null ) { - operation = theClient.create().resource(payloadResource); + operation = theClient.create().resource(thePayloadResource); } else { sendNotification(theMsg); return; } } else { if (thePayloadType != null ) { - operation = theClient.update().resource(payloadResource); + operation = theClient.update().resource(thePayloadResource); } else { sendNotification(theMsg); return; @@ -101,7 +108,7 @@ public class SubscriptionDeliveringRestHookSubscriber extends BaseSubscriptionDe 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(), thePayloadResource.getIdElement().toUnqualified().getValue(), theSubscription.getIdElement(getContext()).toUnqualifiedVersionless().getValue()); try { operation.execute(); @@ -112,6 +119,27 @@ public class SubscriptionDeliveringRestHookSubscriber extends BaseSubscriptionDe } } + protected IBaseResource getAndMassagePayload(ResourceDeliveryMessage theMsg, CanonicalSubscription theSubscription) { + IBaseResource payloadResource = theMsg.getPayload(getContext()); + + if (theSubscription.getRestHookDetails().isDeliverLatestVersion()) { + IFhirResourceDao dao = getSubscriptionDao().getDao(payloadResource.getClass()); + try { + payloadResource = dao.read(payloadResource.getIdElement().toVersionless()); + } catch (ResourceGoneException e) { + ourLog.warn("Resource {} is deleted, not going to deliver for subscription {}", payloadResource.getIdElement(), theSubscription.getIdElement(getContext())); + return null; + } + } + + IIdType resourceId = payloadResource.getIdElement(); + if (theSubscription.getRestHookDetails().isStripVersionId()) { + resourceId = resourceId.toVersionless(); + payloadResource.setId(resourceId); + } + return payloadResource; + } + @Override public void handleMessage(ResourceDeliveryMessage theMessage) throws MessagingException { CanonicalSubscription subscription = theMessage.getSubscription(); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java index c1b43c2ebbc..80330fcc85e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.term; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/JpaConstants.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/JpaConstants.java index 39a3a2cea09..a9b7206c9ce 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/JpaConstants.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/JpaConstants.java @@ -40,4 +40,33 @@ public class JpaConstants { */ public static final String EXT_SUBSCRIPTION_SUBJECT_TEMPLATE = "http://hapifhir.io/fhir/StructureDefinition/subscription-email-subject-template"; + + /** + * This extension URL indicates whether a REST HOOK delivery should + * include the version ID when delivering. + *

+ * This extension should be of type boolean and should be + * placed on the Subscription.channel element. + *

+ */ + public static final String EXT_SUBSCRIPTION_RESTHOOK_STRIP_VERSION_IDS = "http://hapifhir.io/fhir/StructureDefinition/subscription-resthook-strip-version-ids"; + + /** + * This extension URL indicates whether a REST HOOK delivery should + * reload the resource and deliver the latest version always. This + * could be useful for example if a resource which triggers a + * subscription gets updated many times in short succession and there + * is no value in delivering the older versions. + *

+ * Note that if the resource is now deleted, this may cause + * the delivery to be cancelled altogether. + *

+ * + *

+ * This extension should be of type boolean and should be + * placed on the Subscription.channel element. + *

+ */ + public static final String EXT_SUBSCRIPTION_RESTHOOK_DELIVER_LATEST_VERSION = "http://hapifhir.io/fhir/StructureDefinition/subscription-resthook-deliver-latest-version"; + } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/r4/RestHookTestR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/r4/RestHookTestR4Test.java index 5a9c817e71e..4de1ab758ac 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/r4/RestHookTestR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/r4/RestHookTestR4Test.java @@ -2,8 +2,10 @@ package ca.uhn.fhir.jpa.subscription.r4; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.dao.DaoConfig; +import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu2; import ca.uhn.fhir.jpa.provider.r4.BaseResourceProviderR4Test; import ca.uhn.fhir.jpa.subscription.RestHookTestDstu2Test; +import ca.uhn.fhir.jpa.util.JpaConstants; import ca.uhn.fhir.rest.annotation.Create; import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.Update; @@ -172,7 +174,38 @@ public class RestHookTestR4Test extends BaseResourceProviderR4Test { } + @Test + public void testRestHookSubscriptionApplicationJsonDisableVersionIdInDelivery() throws Exception { + String payload = "application/json"; + + String code = "1000000050"; + String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml"; + + Subscription subscription1 = createSubscription(criteria1, payload, ourListenerServerBase); + subscription1 + .getChannel() + .addExtension(JpaConstants.EXT_SUBSCRIPTION_RESTHOOK_STRIP_VERSION_IDS, new BooleanType("true")); + subscription1 + .getChannel() + .addExtension(JpaConstants.EXT_SUBSCRIPTION_RESTHOOK_DELIVER_LATEST_VERSION, new BooleanType("true")); + myClient.update().resource(subscription1).execute(); + waitForQueueToDrain(); + + 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(observation1.getIdElement().getIdPart(), ourUpdatedObservations.get(0).getIdElement().getIdPart()); + assertEquals(null, ourUpdatedObservations.get(0).getIdElement().getVersionIdPart()); + } + + + @Test public void testRestHookSubscriptionApplicationJson() throws Exception { String payload = "application/json"; @@ -191,6 +224,8 @@ public class RestHookTestR4Test extends BaseResourceProviderR4Test { waitForSize(1, ourUpdatedObservations); assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(0)); + assertEquals("1", ourUpdatedObservations.get(0).getIdElement().getVersionIdPart()); + Subscription subscriptionTemp = myClient.read(Subscription.class, subscription2.getId()); Assert.assertNotNull(subscriptionTemp); diff --git a/hapi-fhir-jpaserver-uhnfhirtest/derby_maintenance.txt b/hapi-fhir-jpaserver-uhnfhirtest/derby_maintenance.txt index a8fe4c2edd0..a09a63c8e1a 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/derby_maintenance.txt +++ b/hapi-fhir-jpaserver-uhnfhirtest/derby_maintenance.txt @@ -108,7 +108,6 @@ where res_id in ( drop table hfj_history_tag cascade constraints; drop table hfj_forced_id cascade constraints; -drop table HFJ_SUBSCRIPTION_STATS cascade constraints; drop table hfj_res_link cascade constraints; drop table hfj_spidx_coords cascade constraints; drop table hfj_spidx_date cascade constraints;