diff --git a/hapi-deployable-pom/pom.xml b/hapi-deployable-pom/pom.xml index 85dc12952a0..622a4da67c2 100644 --- a/hapi-deployable-pom/pom.xml +++ b/hapi-deployable-pom/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-android/pom.xml b/hapi-fhir-android/pom.xml index 89b110dc730..16684e41d8d 100644 --- a/hapi-fhir-android/pom.xml +++ b/hapi-fhir-android/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-base/pom.xml b/hapi-fhir-base/pom.xml index 439d47119f0..e8ba8a7fb4c 100644 --- a/hapi-fhir-base/pom.xml +++ b/hapi-fhir-base/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-batch/pom.xml b/hapi-fhir-batch/pom.xml index 4a75321076b..cc98d74f3f4 100644 --- a/hapi-fhir-batch/pom.xml +++ b/hapi-fhir-batch/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-bom/pom.xml b/hapi-fhir-bom/pom.xml index 7c6ba3f7ad6..8f0467b3469 100644 --- a/hapi-fhir-bom/pom.xml +++ b/hapi-fhir-bom/pom.xml @@ -10,7 +10,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml index 81e4980769b..dbeee28e3b9 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml +++ b/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml index 340898f7527..c5070ec8087 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml +++ b/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir-cli - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml index 2ceb0f92c71..c74e9fe5851 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml +++ b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../../hapi-deployable-pom diff --git a/hapi-fhir-cli/pom.xml b/hapi-fhir-cli/pom.xml index acc22c0edc9..8c9f81646e2 100644 --- a/hapi-fhir-cli/pom.xml +++ b/hapi-fhir-cli/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-client-okhttp/pom.xml b/hapi-fhir-client-okhttp/pom.xml index c69a7458598..b4a824d6424 100644 --- a/hapi-fhir-client-okhttp/pom.xml +++ b/hapi-fhir-client-okhttp/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-client/pom.xml b/hapi-fhir-client/pom.xml index a9790bb406d..d2d57ab0860 100644 --- a/hapi-fhir-client/pom.xml +++ b/hapi-fhir-client/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-converter/pom.xml b/hapi-fhir-converter/pom.xml index 3506fe85676..f9f64b19d55 100644 --- a/hapi-fhir-converter/pom.xml +++ b/hapi-fhir-converter/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-dist/pom.xml b/hapi-fhir-dist/pom.xml index 5688d3d3d88..da7c5fd3f35 100644 --- a/hapi-fhir-dist/pom.xml +++ b/hapi-fhir-dist/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-docs/pom.xml b/hapi-fhir-docs/pom.xml index 5cf297785c9..08c5184347b 100644 --- a/hapi-fhir-docs/pom.xml +++ b/hapi-fhir-docs/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_7_0/2480-partition-aware-subscription.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_7_0/2480-partition-aware-subscription.yaml new file mode 100644 index 00000000000..20c70cfdf18 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_7_0/2480-partition-aware-subscription.yaml @@ -0,0 +1,4 @@ +--- +type: add +issue: 2480 +title: "Added partition support for subscriptions. Subscriptions will now only match resource from the same partition" diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa_partitioning/partitioning.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa_partitioning/partitioning.md index dc6f3ac868f..3aa760a56bf 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa_partitioning/partitioning.md +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa_partitioning/partitioning.md @@ -95,13 +95,13 @@ The following resource types may not be placed in any partition except the defau * CodeSystem * CompartmentDefinition * ConceptMap +* Library * NamingSystem * OperationDefinition * Questionnaire * SearchParameter * StructureDefinition * StructureMap -* Subscription * ValueSet ## Examples @@ -150,18 +150,16 @@ None of the limitations listed here are considered permanent. Over time the HAPI * CodeSystem * CompartmentDefinition * ConceptMap + * Library * NamingSystem * OperationDefinition * Questionnaire * SearchParameter * StructureDefinition * StructureMap - * Subscription * ValueSet -* **Server Capability Statement is not partition aware**: The server creates and exposes a single server capability statement, covering all partitions. This can be misleading when partitioning us used as a multitenancy strategy. - -* **Subscriptions may not be partitioned**: All subscriptions must be placed in the default partition, and subscribers will receive deliveries for any matching resources from all partitions. +* **Server Capability Statement is not partition aware**: The server creates and exposes a single server capability statement, covering all partitions. This can be misleading when partitioning us used as a multitenancy strategy. * **Conformance resources may not be partitioned**: Conformance resources must be placed in the default partition, and will be shared for any validation activities across all partitions. @@ -174,3 +172,5 @@ None of the limitations listed here are considered permanent. Over time the HAPI * **Package Operations are not partition aware**: Package operations will only create, update and query resources in the default partition. * **Advanced Elasticsearch indexing is not partition optimized**: The results are correctly partitioned, but the extended indexing is not optimized to account for partitions. + +* **Subscriptions are partition aware**: Subscriptions can be placed on any partition and will deliver matching resources from the same partition. diff --git a/hapi-fhir-jacoco/pom.xml b/hapi-fhir-jacoco/pom.xml index f38b69c835b..e530332a91b 100644 --- a/hapi-fhir-jacoco/pom.xml +++ b/hapi-fhir-jacoco/pom.xml @@ -11,7 +11,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jaxrsserver-base/pom.xml b/hapi-fhir-jaxrsserver-base/pom.xml index df2bc57b79f..88f21095090 100644 --- a/hapi-fhir-jaxrsserver-base/pom.xml +++ b/hapi-fhir-jaxrsserver-base/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpa/pom.xml b/hapi-fhir-jpa/pom.xml index f2b67e44a85..6dc6e8f2ed3 100644 --- a/hapi-fhir-jpa/pom.xml +++ b/hapi-fhir-jpa/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../hapi-deployable-pom/pom.xml 4.0.0 diff --git a/hapi-fhir-jpaserver-base/pom.xml b/hapi-fhir-jpaserver-base/pom.xml index 689cc8d0b4b..4445397dc00 100644 --- a/hapi-fhir-jpaserver-base/pom.xml +++ b/hapi-fhir-jpaserver-base/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/RequestPartitionHelperSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/RequestPartitionHelperSvc.java index b63df986ba1..611af210c3d 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/RequestPartitionHelperSvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/RequestPartitionHelperSvc.java @@ -75,7 +75,6 @@ public class RequestPartitionHelperSvc implements IRequestPartitionHelperSvc { myNonPartitionableResourceNames = new HashSet<>(); // Infrastructure - myNonPartitionableResourceNames.add("Subscription"); myNonPartitionableResourceNames.add("SearchParameter"); // Validation and Conformance @@ -85,6 +84,8 @@ public class RequestPartitionHelperSvc implements IRequestPartitionHelperSvc { myNonPartitionableResourceNames.add("CompartmentDefinition"); myNonPartitionableResourceNames.add("OperationDefinition"); + myNonPartitionableResourceNames.add("Library"); + // Terminology myNonPartitionableResourceNames.add("ConceptMap"); myNonPartitionableResourceNames.add("CodeSystem"); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BasePartitioningR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BasePartitioningR4Test.java index d4e1028dda4..457fcd7b80d 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BasePartitioningR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/BasePartitioningR4Test.java @@ -200,7 +200,7 @@ public abstract class BasePartitioningR4Test extends BaseJpaR4SystemTest { } @Hook(Pointcut.STORAGE_PARTITION_IDENTIFY_READ) - public RequestPartitionId PartitionIdentifyRead(ServletRequestDetails theRequestDetails) { + public RequestPartitionId partitionIdentifyRead(ServletRequestDetails theRequestDetails) { RequestPartitionId retVal = myReadRequestPartitionIds.remove(0); ourLog.info("Returning partition for read: {}", retVal); return retVal; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/partition/PartitionedSubscriptionTriggeringR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/partition/PartitionedSubscriptionTriggeringR4Test.java new file mode 100644 index 00000000000..5ff7626ade5 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/partition/PartitionedSubscriptionTriggeringR4Test.java @@ -0,0 +1,212 @@ +package ca.uhn.fhir.jpa.partition; + +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.interceptor.model.RequestPartitionId; +import ca.uhn.fhir.jpa.api.config.DaoConfig; +import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome; +import ca.uhn.fhir.jpa.api.model.ExpungeOptions; +import ca.uhn.fhir.jpa.config.StoppableSubscriptionDeliveringRestHookSubscriber; +import ca.uhn.fhir.jpa.dao.r4.BasePartitioningR4Test; +import ca.uhn.fhir.jpa.entity.PartitionEntity; +import ca.uhn.fhir.jpa.model.config.PartitionSettings; +import ca.uhn.fhir.jpa.subscription.BaseSubscriptionsR4Test; +import ca.uhn.fhir.jpa.subscription.resthook.RestHookTestR4Test; +import ca.uhn.fhir.jpa.subscription.triggering.ISubscriptionTriggeringSvc; +import ca.uhn.fhir.rest.api.Constants; +import org.awaitility.core.ConditionTimeoutException; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.hl7.fhir.r4.model.Observation; +import org.hl7.fhir.r4.model.Parameters; +import org.hl7.fhir.r4.model.Patient; +import org.hl7.fhir.r4.model.Subscription; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +import javax.servlet.ServletException; +import java.time.LocalDate; +import java.time.Month; +import java.util.ArrayList; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +public class PartitionedSubscriptionTriggeringR4Test extends BaseSubscriptionsR4Test { + private static final Logger ourLog = LoggerFactory.getLogger(RestHookTestR4Test.class); + + @Autowired + StoppableSubscriptionDeliveringRestHookSubscriber myStoppableSubscriptionDeliveringRestHookSubscriber; + + @Autowired + private ISubscriptionTriggeringSvc mySubscriptionTriggeringSvc; + + static final String PARTITION_1 = "PART-1"; + static final String PARTITION_2 = "PART-2"; + + protected MyReadWriteInterceptor myPartitionInterceptor; + protected LocalDate myPartitionDate; + protected LocalDate myPartitionDate2; + protected int myPartitionId; + protected int myPartitionId2; + + + @BeforeEach + public void beforeEach() throws ServletException { + myPartitionSettings.setPartitioningEnabled(true); + myPartitionSettings.setIncludePartitionInSearchHashes(new PartitionSettings().isIncludePartitionInSearchHashes()); + + myDaoConfig.setUniqueIndexesEnabled(true); + + myModelConfig.setDefaultSearchParamsCanBeOverridden(true); + + myPartitionDate = LocalDate.of(2020, Month.JANUARY, 14); + myPartitionDate2 = LocalDate.of(2020, Month.JANUARY, 15); + myPartitionId = 1; + myPartitionId2 = 2; + + myPartitionInterceptor = new MyReadWriteInterceptor(); + myPartitionInterceptor.setResultPartitionId(RequestPartitionId.fromPartitionNames(PARTITION_1)); + + mySrdInterceptorService.registerInterceptor(myPartitionInterceptor); + + myPartitionConfigSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1)); + myPartitionConfigSvc.createPartition(new PartitionEntity().setId(2).setName(PARTITION_2)); + + myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.ENABLED); + } + + @AfterEach + @Override + public void afterUnregisterRestHookListener() { + myStoppableSubscriptionDeliveringRestHookSubscriber.setCountDownLatch(null); + myStoppableSubscriptionDeliveringRestHookSubscriber.unPause(); + myDaoConfig.setTriggerSubscriptionsForNonVersioningChanges(new DaoConfig().isTriggerSubscriptionsForNonVersioningChanges()); + + myDaoRegistry.getSystemDao().expunge(new ExpungeOptions().setExpungeEverything(true), null); + + myPartitionSettings.setUnnamedPartitionMode(false); + + mySrdInterceptorService.unregisterInterceptorsIf(t -> t instanceof BasePartitioningR4Test.MyReadWriteInterceptor); + myInterceptor = null; + } + + @Test + public void testCreateSubscriptionInPartition() throws Exception { + String payload = "application/fhir+json"; + + String code = "1000000050"; + String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml"; + Subscription subscription = newSubscription(criteria1, payload); + + assertEquals(mySrdInterceptorService.getAllRegisteredInterceptors().size(), 1); + + myDaoRegistry.getResourceDao("Subscription").create(subscription, mySrd); + + waitForActivatedSubscriptionCount(1); + + Observation observation = createBaseObservation(code, "SNOMED-CT"); + myDaoRegistry.getResourceDao("Observation").create(observation, mySrd); + + // Should see 1 subscription notification + waitForQueueToDrain(); + assertEquals(0, ourObservationProvider.getCountCreate()); + ourObservationProvider.waitForUpdateCount(1); + + assertEquals(Constants.CT_FHIR_JSON_NEW, ourRestfulServer.getRequestContentTypes().get(0)); + } + + @Test + public void testCreateSubscriptionInPartitionAndResourceInDifferentPartition() throws Exception { + String payload = "application/fhir+json"; + + String code = "1000000050"; + String criteria1 = "Patient?active=true"; + Subscription subscription = newSubscription(criteria1, payload); + + assertEquals(mySrdInterceptorService.getAllRegisteredInterceptors().size(), 1); + + myDaoRegistry.getResourceDao("Subscription").create(subscription, mySrd); + + waitForActivatedSubscriptionCount(1); + + Patient patient = new Patient(); + patient.setActive(true); + myDaoRegistry.getResourceDao("Patient").create(patient, new SystemRequestDetails().setRequestPartitionId(RequestPartitionId.fromPartitionId(2))); + + // Should see 0 subscription notification + waitForQueueToDrain(); + assertEquals(0, ourPatientProvider.getCountCreate()); + + try { + // Should have 0 matching subscription, if we get 1 update count then the test fails + ourPatientProvider.waitForUpdateCount(1); + fail(); + } catch (ConditionTimeoutException e) { + assertEquals(0, ourRestfulServer.getRequestContentTypes().size()); + } + } + + @Test + public void testManualTriggeredSubscriptionInPartition() throws Exception { + String payload = "application/fhir+json"; + String code = "1000000050"; + String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml"; + + // Create the resource first + DaoMethodOutcome observationOutcome = myDaoRegistry.getResourceDao("Observation").create(createBaseObservation(code, "SNOMED-CT"), mySrd); + + Observation observation = (Observation) observationOutcome.getResource(); + + // Create the subscription now + DaoMethodOutcome subscriptionOutcome = myDaoRegistry.getResourceDao("Subscription").create(newSubscription(criteria1, payload), mySrd); + + assertEquals(mySrdInterceptorService.getAllRegisteredInterceptors().size(), 1); + + Subscription subscription = (Subscription) subscriptionOutcome.getResource(); + + waitForActivatedSubscriptionCount(1); + + ArrayList> resourceIdList = new ArrayList<>(); + resourceIdList.add(observation.getIdElement()); + + + Parameters resultParameters = (Parameters) mySubscriptionTriggeringSvc.triggerSubscription(resourceIdList, null, subscription.getIdElement()); + + waitForQueueToDrain(); + assertEquals(0, ourObservationProvider.getCountCreate()); + + String responseValue = resultParameters.getParameter().get(0).getValue().primitiveValue(); + assertThat(responseValue, containsString("Subscription triggering job submitted as JOB ID")); + } + + @Interceptor + public static class MyReadWriteInterceptor { + private RequestPartitionId myReadPartitionId; + + public void setResultPartitionId(RequestPartitionId theRequestPartitionId) { + myReadPartitionId = theRequestPartitionId; + } + + @Hook(Pointcut.STORAGE_PARTITION_IDENTIFY_READ) + public RequestPartitionId read() { + RequestPartitionId retVal = myReadPartitionId; + ourLog.info("Returning partition for read: {}", retVal); + return retVal; + } + + @Hook(Pointcut.STORAGE_PARTITION_IDENTIFY_CREATE) + public RequestPartitionId create() { + RequestPartitionId retVal = myReadPartitionId; + ourLog.info("Returning partition for write: {}", retVal); + return retVal; + } + } +} 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 8b094564192..d8e7d0db0d9 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 @@ -142,20 +142,25 @@ public abstract class BaseSubscriptionsR4Test extends BaseResourceProviderR4Test } - protected Observation sendObservation(String code, String system) { + protected Observation sendObservation(String theCode, String theSystem) { + Observation observation = createBaseObservation(theCode, theSystem); + + IIdType id = myObservationDao.create(observation).getId(); + observation.setId(id); + + return observation; + } + + protected Observation createBaseObservation(String theCode, String theSystem) { 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); + coding.setCode(theCode); + coding.setSystem(theSystem); observation.setStatus(Observation.ObservationStatus.FINAL); - - IIdType id = myObservationDao.create(observation).getId(); - observation.setId(id); - return observation; } diff --git a/hapi-fhir-jpaserver-cql/pom.xml b/hapi-fhir-jpaserver-cql/pom.xml index 2c0549a0ba4..1ca4426f245 100644 --- a/hapi-fhir-jpaserver-cql/pom.xml +++ b/hapi-fhir-jpaserver-cql/pom.xml @@ -7,7 +7,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-mdm/pom.xml b/hapi-fhir-jpaserver-mdm/pom.xml index b2ec0a4a5a8..f9f5b8d7be0 100644 --- a/hapi-fhir-jpaserver-mdm/pom.xml +++ b/hapi-fhir-jpaserver-mdm/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-model/pom.xml b/hapi-fhir-jpaserver-model/pom.xml index caa3f0ec03d..4cfc9649195 100644 --- a/hapi-fhir-jpaserver-model/pom.xml +++ b/hapi-fhir-jpaserver-model/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-searchparam/pom.xml b/hapi-fhir-jpaserver-searchparam/pom.xml index 87bcd4cb904..c69f45cfd4c 100755 --- a/hapi-fhir-jpaserver-searchparam/pom.xml +++ b/hapi-fhir-jpaserver-searchparam/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-subscription/pom.xml b/hapi-fhir-jpaserver-subscription/pom.xml index e9f4dbc64cc..80dc77b0e51 100644 --- a/hapi-fhir-jpaserver-subscription/pom.xml +++ b/hapi-fhir-jpaserver-subscription/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/channel/models/ProducingChannelParameters.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/channel/models/ProducingChannelParameters.java index cd9ec3fb53d..d492bb254e5 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/channel/models/ProducingChannelParameters.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/channel/models/ProducingChannelParameters.java @@ -24,7 +24,7 @@ public class ProducingChannelParameters extends BaseChannelParameters { /** * Constructor - * + *

* Producing channels are sending channels. They send data to topics/queues. * * @param theChannelName diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/channel/models/ReceivingChannelParameters.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/channel/models/ReceivingChannelParameters.java index a658bd99fb7..4c5ff762a40 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/channel/models/ReceivingChannelParameters.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/channel/models/ReceivingChannelParameters.java @@ -24,7 +24,7 @@ public class ReceivingChannelParameters extends BaseChannelParameters { /** * Constructor - * + *

* Receiving channels are channels that receive data from topics/queues * * @param theChannelName 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 5939c9ab2d2..13dd80952aa 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 @@ -23,8 +23,10 @@ package ca.uhn.fhir.jpa.subscription.match.deliver.resthook; import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.interceptor.api.HookParams; import ca.uhn.fhir.interceptor.api.Pointcut; +import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; +import ca.uhn.fhir.jpa.partition.SystemRequestDetails; import ca.uhn.fhir.jpa.searchparam.MatchUrlService; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.subscription.match.deliver.BaseSubscriptionDeliverySubscriber; @@ -162,10 +164,11 @@ public class SubscriptionDeliveringRestHookSubscriber extends BaseSubscriptionDe return operation; } - public IBaseResource getResource(IIdType payloadId) throws ResourceGoneException { + public IBaseResource getResource(IIdType payloadId, RequestPartitionId thePartitionId) throws ResourceGoneException { RuntimeResourceDefinition resourceDef = myFhirContext.getResourceDefinition(payloadId.getResourceType()); + SystemRequestDetails systemRequestDetails = new SystemRequestDetails().setRequestPartitionId(thePartitionId); IFhirResourceDao dao = myDaoRegistry.getResourceDao(resourceDef.getImplementingClass()); - return dao.read(payloadId.toVersionless()); + return dao.read(payloadId.toVersionless(), systemRequestDetails); } @@ -177,7 +180,7 @@ public class SubscriptionDeliveringRestHookSubscriber extends BaseSubscriptionDe try { if (payloadId != null) { - payloadResource = getResource(payloadId.toVersionless()); + payloadResource = getResource(payloadId.toVersionless(), theMsg.getRequestPartitionId()); } else { return null; } diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/matching/DaoSubscriptionMatcher.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/matching/DaoSubscriptionMatcher.java index 55c846d7b23..cd9e5eb1b12 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/matching/DaoSubscriptionMatcher.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/matching/DaoSubscriptionMatcher.java @@ -24,12 +24,15 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; +import ca.uhn.fhir.jpa.model.entity.PartitionablePartitionId; +import ca.uhn.fhir.jpa.partition.SystemRequestDetails; import ca.uhn.fhir.jpa.searchparam.MatchUrlService; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.matcher.InMemoryMatchResult; import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription; import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage; import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.rest.api.server.RequestDetails; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; import org.slf4j.Logger; @@ -37,7 +40,7 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; public class DaoSubscriptionMatcher implements ISubscriptionMatcher { - private Logger ourLog = LoggerFactory.getLogger(DaoSubscriptionMatcher.class); + private final Logger ourLog = LoggerFactory.getLogger(DaoSubscriptionMatcher.class); @Autowired DaoRegistry myDaoRegistry; @@ -56,7 +59,7 @@ public class DaoSubscriptionMatcher implements ISubscriptionMatcher { // Run the subscriptions query and look for matches, add the id as part of the criteria to avoid getting matches of previous resources rather than the recent resource criteria += "&_id=" + id.toUnqualifiedVersionless().getValue(); - IBundleProvider results = performSearch(criteria); + IBundleProvider results = performSearch(criteria, theSubscription); ourLog.debug("Subscription check found {} results for query: {}", results.size(), criteria); @@ -66,7 +69,7 @@ public class DaoSubscriptionMatcher implements ISubscriptionMatcher { /** * Search based on a query criteria */ - private IBundleProvider performSearch(String theCriteria) { + private IBundleProvider performSearch(String theCriteria, CanonicalSubscription theSubscription) { IFhirResourceDao subscriptionDao = myDaoRegistry.getSubscriptionDao(); RuntimeResourceDefinition responseResourceDef = subscriptionDao.validateCriteriaAndReturnResourceDefinition(theCriteria); SearchParameterMap responseCriteriaUrl = myMatchUrlService.translateMatchUrl(theCriteria, responseResourceDef); @@ -74,7 +77,9 @@ public class DaoSubscriptionMatcher implements ISubscriptionMatcher { IFhirResourceDao responseDao = myDaoRegistry.getResourceDao(responseResourceDef.getImplementingClass()); responseCriteriaUrl.setLoadSynchronousUpTo(1); - return responseDao.search(responseCriteriaUrl); + PartitionablePartitionId partitionId = new PartitionablePartitionId(theSubscription.getRequestPartitionId(), null); + RequestDetails systemRequestDetails = new SystemRequestDetails().setRequestPartitionId(partitionId.toPartitionId()); + return responseDao.search(responseCriteriaUrl, systemRequestDetails); } } diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionActivatingSubscriber.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionActivatingSubscriber.java index 119b7128ebe..af52d72b41b 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionActivatingSubscriber.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionActivatingSubscriber.java @@ -23,6 +23,7 @@ package ca.uhn.fhir.jpa.subscription.match.matcher.subscriber; import ca.uhn.fhir.jpa.api.config.DaoConfig; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; +import ca.uhn.fhir.jpa.partition.SystemRequestDetails; import ca.uhn.fhir.jpa.subscription.match.matcher.matching.SubscriptionStrategyEvaluator; import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionCanonicalizer; import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionConstants; @@ -49,7 +50,7 @@ import javax.annotation.Nonnull; * Also validates criteria. If invalid, rejects the subscription without persisting the subscription. */ public class SubscriptionActivatingSubscriber extends BaseSubscriberForSubscriptionResources implements MessageHandler { - private Logger ourLog = LoggerFactory.getLogger(SubscriptionActivatingSubscriber.class); + private final Logger ourLog = LoggerFactory.getLogger(SubscriptionActivatingSubscriber.class); @Autowired private SubscriptionRegistry mySubscriptionRegistry; @Autowired @@ -115,19 +116,21 @@ public class SubscriptionActivatingSubscriber extends BaseSubscriberForSubscript @SuppressWarnings("unchecked") private boolean activateSubscription(final IBaseResource theSubscription) { IFhirResourceDao subscriptionDao = myDaoRegistry.getSubscriptionDao(); - IBaseResource subscription = subscriptionDao.read(theSubscription.getIdElement()); + SystemRequestDetails srd = SystemRequestDetails.forAllPartition(); + + IBaseResource subscription = subscriptionDao.read(theSubscription.getIdElement(), SystemRequestDetails.forAllPartition()); subscription.setId(subscription.getIdElement().toVersionless()); ourLog.info("Activating subscription {} from status {} to {}", subscription.getIdElement().toUnqualified().getValue(), SubscriptionConstants.REQUESTED_STATUS, SubscriptionConstants.ACTIVE_STATUS); try { SubscriptionUtil.setStatus(myFhirContext, subscription, SubscriptionConstants.ACTIVE_STATUS); - subscriptionDao.update(subscription); + subscriptionDao.update(subscription, srd); return true; } catch (final UnprocessableEntityException e) { ourLog.info("Changing status of {} to ERROR", subscription.getIdElement()); SubscriptionUtil.setStatus(myFhirContext, subscription, "error"); SubscriptionUtil.setReason(myFhirContext, subscription, e.getMessage()); - subscriptionDao.update(subscription); + subscriptionDao.update(subscription, srd); return false; } } diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionMatchingSubscriber.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionMatchingSubscriber.java index 2301d536071..9bcb5d26463 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionMatchingSubscriber.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionMatchingSubscriber.java @@ -52,7 +52,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; */ public class SubscriptionMatchingSubscriber implements MessageHandler { - private Logger ourLog = LoggerFactory.getLogger(SubscriptionMatchingSubscriber.class); + private final Logger ourLog = LoggerFactory.getLogger(SubscriptionMatchingSubscriber.class); public static final String SUBSCRIPTION_MATCHING_CHANNEL_NAME = "subscription-matching"; @Autowired @@ -123,7 +123,12 @@ public class SubscriptionMatchingSubscriber implements MessageHandler { boolean resourceMatched = false; for (ActiveSubscription nextActiveSubscription : subscriptions) { - + // skip if the partitions don't match + CanonicalSubscription subscription = nextActiveSubscription.getSubscription(); + if (subscription != null && subscription.getRequestPartitionId() != null && theMsg.getPartitionId() != null + && !theMsg.getPartitionId().hasPartitionId(subscription.getRequestPartitionId())) { + continue; + } String nextSubscriptionId = getId(nextActiveSubscription); if (isNotBlank(theMsg.getSubscriptionId())) { @@ -154,15 +159,15 @@ public class SubscriptionMatchingSubscriber implements MessageHandler { } IBaseResource payload = theMsg.getNewPayload(myFhirContext); - CanonicalSubscription subscription = nextActiveSubscription.getSubscription(); EncodingEnum encoding = null; - if (subscription.getPayloadString() != null && !subscription.getPayloadString().isEmpty()) { + if (subscription != null && subscription.getPayloadString() != null && !subscription.getPayloadString().isEmpty()) { encoding = EncodingEnum.forContentType(subscription.getPayloadString()); } encoding = defaultIfNull(encoding, EncodingEnum.JSON); ResourceDeliveryMessage deliveryMsg = new ResourceDeliveryMessage(); + deliveryMsg.setPartitionId(theMsg.getPartitionId()); deliveryMsg.setPayload(myFhirContext, payload, encoding); deliveryMsg.setSubscription(subscription); diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionRegisteringSubscriber.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionRegisteringSubscriber.java index b97352be12a..049af79763d 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionRegisteringSubscriber.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/matcher/subscriber/SubscriptionRegisteringSubscriber.java @@ -21,10 +21,15 @@ package ca.uhn.fhir.jpa.subscription.match.matcher.subscriber; */ import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.api.dao.DaoRegistry; +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; +import ca.uhn.fhir.jpa.model.config.PartitionSettings; +import ca.uhn.fhir.jpa.partition.SystemRequestDetails; import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionCanonicalizer; import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionRegistry; import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedJsonMessage; import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage; +import ca.uhn.fhir.rest.api.server.RequestDetails; import org.hl7.fhir.instance.model.api.IBaseResource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -42,13 +47,17 @@ import javax.annotation.Nonnull; * Also validates criteria. If invalid, rejects the subscription without persisting the subscription. */ public class SubscriptionRegisteringSubscriber extends BaseSubscriberForSubscriptionResources implements MessageHandler { - private Logger ourLog = LoggerFactory.getLogger(SubscriptionRegisteringSubscriber.class); + private static final Logger ourLog = LoggerFactory.getLogger(SubscriptionRegisteringSubscriber.class); @Autowired private FhirContext myFhirContext; @Autowired private SubscriptionRegistry mySubscriptionRegistry; @Autowired private SubscriptionCanonicalizer mySubscriptionCanonicalizer; + @Autowired + private PartitionSettings myPartitionSettings; + @Autowired + private DaoRegistry myDaoRegistry; /** * Constructor @@ -77,9 +86,18 @@ public class SubscriptionRegisteringSubscriber extends BaseSubscriberForSubscrip case CREATE: case UPDATE: IBaseResource subscription = payload.getNewPayload(myFhirContext); + IBaseResource subscriptionToRegister = subscription; String statusString = mySubscriptionCanonicalizer.getSubscriptionStatus(subscription); + + // reading resource back from db in order to store partition id in the userdata of the resource for partitioned subscriptions + if (myPartitionSettings.isPartitioningEnabled()) { + IFhirResourceDao subscriptionDao = myDaoRegistry.getSubscriptionDao(); + RequestDetails systemRequestDetails = new SystemRequestDetails().setRequestPartitionId(payload.getPartitionId()); + subscriptionToRegister = subscriptionDao.read(subscription.getIdElement(), systemRequestDetails); + } + if ("active".equals(statusString)) { - mySubscriptionRegistry.registerSubscriptionUnlessAlreadyRegistered(payload.getNewPayload(myFhirContext)); + mySubscriptionRegistry.registerSubscriptionUnlessAlreadyRegistered(subscriptionToRegister); } else { mySubscriptionRegistry.unregisterSubscriptionIfRegistered(payload.getPayloadId(myFhirContext).getIdPart()); } diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/registry/SubscriptionLoader.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/registry/SubscriptionLoader.java index 51093138df4..cd988a34939 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/registry/SubscriptionLoader.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/registry/SubscriptionLoader.java @@ -27,13 +27,14 @@ import ca.uhn.fhir.jpa.cache.IResourceChangeListener; import ca.uhn.fhir.jpa.cache.IResourceChangeListenerCache; import ca.uhn.fhir.jpa.cache.IResourceChangeListenerRegistry; import ca.uhn.fhir.jpa.model.sched.ISchedulerService; +import ca.uhn.fhir.jpa.partition.SystemRequestDetails; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; -import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; import ca.uhn.fhir.jpa.searchparam.retry.Retrier; import ca.uhn.fhir.jpa.subscription.match.matcher.subscriber.SubscriptionActivatingSubscriber; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.param.TokenOrListParam; import ca.uhn.fhir.rest.param.TokenParam; +import ca.uhn.fhir.rest.server.util.ISearchParamRegistry; import com.google.common.annotations.VisibleForTesting; import org.apache.commons.lang3.time.DateUtils; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -75,6 +76,7 @@ public class SubscriptionLoader implements IResourceChangeListener { private IResourceChangeListenerRegistry myResourceChangeListenerRegistry; private SearchParameterMap mySearchParameterMap; + private SystemRequestDetails mySystemRequestDetails; /** * Constructor @@ -86,6 +88,8 @@ public class SubscriptionLoader implements IResourceChangeListener { @PostConstruct public void registerListener() { mySearchParameterMap = getSearchParameterMap(); + mySystemRequestDetails = SystemRequestDetails.forAllPartition(); + IResourceChangeListenerCache subscriptionCache = myResourceChangeListenerRegistry.registerResourceResourceChangeListener("Subscription", mySearchParameterMap, this, REFRESH_INTERVAL); subscriptionCache.forceRefresh(); } @@ -142,7 +146,7 @@ public class SubscriptionLoader implements IResourceChangeListener { synchronized (mySyncSubscriptionsLock) { ourLog.debug("Starting sync subscriptions"); - IBundleProvider subscriptionBundleList = getSubscriptionDao().search(mySearchParameterMap); + IBundleProvider subscriptionBundleList = getSubscriptionDao().search(mySearchParameterMap, mySystemRequestDetails); Integer subscriptionCount = subscriptionBundleList.size(); assert subscriptionCount != null; diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/package-info.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/package-info.java new file mode 100644 index 00000000000..bd0a3b23bd1 --- /dev/null +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/package-info.java @@ -0,0 +1,11 @@ +/** + * Module to support Subscriptions + *

+ * Subscriptions are partition aware + *

+ * The functionalities of this module follows the HL7 spec on Subscriptions: + * http://hl7.org/fhir/subscription.html + *

+ * Activated by {@link ca.uhn.fhir.jpa.model.config.PartitionSettings#setPartitioningEnabled(boolean)} + */ +package ca.uhn.fhir.jpa.subscription; diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/submit/interceptor/SubscriptionMatcherInterceptor.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/submit/interceptor/SubscriptionMatcherInterceptor.java index 2b7adf3f0dd..355bc780073 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/submit/interceptor/SubscriptionMatcherInterceptor.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/submit/interceptor/SubscriptionMatcherInterceptor.java @@ -6,7 +6,9 @@ import ca.uhn.fhir.interceptor.api.HookParams; import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; import ca.uhn.fhir.interceptor.api.Interceptor; import ca.uhn.fhir.interceptor.api.Pointcut; +import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.api.config.DaoConfig; +import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc; import ca.uhn.fhir.jpa.subscription.channel.impl.LinkedBlockingChannel; import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionChannelFactory; import ca.uhn.fhir.jpa.subscription.match.matcher.matching.IResourceModifiedConsumer; @@ -60,6 +62,8 @@ public class SubscriptionMatcherInterceptor implements IResourceModifiedConsumer private SubscriptionChannelFactory mySubscriptionChannelFactory; @Autowired private DaoConfig myDaoConfig; + @Autowired + private IRequestPartitionHelperSvc myRequestPartitionHelperSvc; private volatile MessageChannel myMatchingChannel; @@ -114,7 +118,9 @@ public class SubscriptionMatcherInterceptor implements IResourceModifiedConsumer */ @Override public void submitResourceModified(IBaseResource theNewResource, ResourceModifiedMessage.OperationTypeEnum theOperationType, RequestDetails theRequest) { - ResourceModifiedMessage msg = new ResourceModifiedMessage(myFhirContext, theNewResource, theOperationType, theRequest); + // Even though the resource is being written, the subscription will be interacting with it by effectively "reading" it so we set the RequestPartitionId as a read request + RequestPartitionId requestPartitionId = myRequestPartitionHelperSvc.determineReadPartitionForRequestForRead(theRequest, theNewResource.getIdElement().getResourceType(), theNewResource.getIdElement()); + ResourceModifiedMessage msg = new ResourceModifiedMessage(myFhirContext, theNewResource, theOperationType, theRequest, requestPartitionId); // Interceptor call: SUBSCRIPTION_RESOURCE_MODIFIED HookParams params = new HookParams() diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/triggering/SubscriptionTriggeringSvcImpl.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/triggering/SubscriptionTriggeringSvcImpl.java index 98dca268821..3ed829e871d 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/triggering/SubscriptionTriggeringSvcImpl.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/triggering/SubscriptionTriggeringSvcImpl.java @@ -30,6 +30,7 @@ import ca.uhn.fhir.jpa.api.svc.ISearchCoordinatorSvc; import ca.uhn.fhir.jpa.model.sched.HapiJob; import ca.uhn.fhir.jpa.model.sched.ISchedulerService; import ca.uhn.fhir.jpa.model.sched.ScheduledJobDefinition; +import ca.uhn.fhir.jpa.partition.SystemRequestDetails; import ca.uhn.fhir.jpa.searchparam.MatchUrlService; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.subscription.match.matcher.matching.IResourceModifiedConsumer; @@ -114,7 +115,7 @@ public class SubscriptionTriggeringSvcImpl implements ISubscriptionTriggeringSvc if (!subscriptionId.hasResourceType()) { subscriptionId = subscriptionId.withResourceType(ResourceTypeEnum.SUBSCRIPTION.getCode()); } - subscriptionDao.read(subscriptionId); + subscriptionDao.read(subscriptionId, SystemRequestDetails.forAllPartition()); } List> resourceIds = ObjectUtils.defaultIfNull(theResourceIds, Collections.emptyList()); @@ -298,7 +299,7 @@ public class SubscriptionTriggeringSvcImpl implements ISubscriptionTriggeringSvc private Future submitResource(String theSubscriptionId, String theResourceIdToTrigger) { org.hl7.fhir.r4.model.IdType resourceId = new org.hl7.fhir.r4.model.IdType(theResourceIdToTrigger); IFhirResourceDao dao = myDaoRegistry.getResourceDao(resourceId.getResourceType()); - IBaseResource resourceToTrigger = dao.read(resourceId); + IBaseResource resourceToTrigger = dao.read(resourceId, SystemRequestDetails.forAllPartition()); return submitResource(theSubscriptionId, resourceToTrigger); } diff --git a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/match/deliver/BaseSubscriptionDeliverySubscriberTest.java b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/match/deliver/BaseSubscriptionDeliverySubscriberTest.java index 17e3f471021..1d392470367 100644 --- a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/match/deliver/BaseSubscriptionDeliverySubscriberTest.java +++ b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/match/deliver/BaseSubscriptionDeliverySubscriberTest.java @@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.subscription.match.deliver; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; import ca.uhn.fhir.interceptor.api.Pointcut; +import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.subscription.match.deliver.resthook.SubscriptionDeliveringRestHookSubscriber; import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionRegistry; import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription; @@ -13,19 +14,28 @@ import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.client.api.IGenericClient; import ca.uhn.fhir.rest.client.api.IRestfulClientFactory; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import com.fasterxml.jackson.core.JsonProcessingException; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Patient; +import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Answers; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.messaging.Message; import org.springframework.messaging.MessagingException; import org.springframework.messaging.support.GenericMessage; +import java.time.LocalDate; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.eq; @@ -35,9 +45,10 @@ import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) public class BaseSubscriptionDeliverySubscriberTest { + private static final Logger ourLog = LoggerFactory.getLogger(BaseSubscriptionDeliverySubscriberTest.class); private SubscriptionDeliveringRestHookSubscriber mySubscriber; - private FhirContext myCtx = FhirContext.forR4(); + private final FhirContext myCtx = FhirContext.forR4(); @Mock private IInterceptorBroadcaster myInterceptorBroadcaster; @@ -79,13 +90,9 @@ public class BaseSubscriptionDeliverySubscriberTest { public void testRestHookDeliverySuccessful() { when(myInterceptorBroadcaster.callHooks(any(), any())).thenReturn(true); - Patient patient = new Patient(); - patient.setActive(true); + Patient patient = generatePatient(); - CanonicalSubscription subscription = new CanonicalSubscription(); - subscription.setIdElement(new IdType("Subscription/123")); - subscription.setEndpointUrl("http://example.com/fhir"); - subscription.setPayloadString("application/fhir+json"); + CanonicalSubscription subscription = generateSubscription(); ResourceDeliveryMessage payload = new ResourceDeliveryMessage(); payload.setSubscription(subscription); @@ -101,13 +108,9 @@ public class BaseSubscriptionDeliverySubscriberTest { public void testRestHookDeliveryFails_ShouldRollBack() { when(myInterceptorBroadcaster.callHooks(any(), any())).thenReturn(true); - Patient patient = new Patient(); - patient.setActive(true); + Patient patient = generatePatient(); - CanonicalSubscription subscription = new CanonicalSubscription(); - subscription.setIdElement(new IdType("Subscription/123")); - subscription.setEndpointUrl("http://example.com/fhir"); - subscription.setPayloadString("application/fhir+json"); + CanonicalSubscription subscription = generateSubscription(); ResourceDeliveryMessage payload = new ResourceDeliveryMessage(); payload.setSubscription(subscription); @@ -132,13 +135,9 @@ public class BaseSubscriptionDeliverySubscriberTest { when(myInterceptorBroadcaster.callHooks(eq(Pointcut.SUBSCRIPTION_BEFORE_REST_HOOK_DELIVERY), any())).thenReturn(true); when(myInterceptorBroadcaster.callHooks(eq(Pointcut.SUBSCRIPTION_AFTER_DELIVERY_FAILED), any())).thenReturn(false); - Patient patient = new Patient(); - patient.setActive(true); + Patient patient = generatePatient(); - CanonicalSubscription subscription = new CanonicalSubscription(); - subscription.setIdElement(new IdType("Subscription/123")); - subscription.setEndpointUrl("http://example.com/fhir"); - subscription.setPayloadString("application/fhir+json"); + CanonicalSubscription subscription = generateSubscription(); ResourceDeliveryMessage payload = new ResourceDeliveryMessage(); payload.setSubscription(subscription); @@ -158,13 +157,9 @@ public class BaseSubscriptionDeliverySubscriberTest { when(myInterceptorBroadcaster.callHooks(eq(Pointcut.SUBSCRIPTION_BEFORE_DELIVERY), any())).thenReturn(true); when(myInterceptorBroadcaster.callHooks(eq(Pointcut.SUBSCRIPTION_BEFORE_REST_HOOK_DELIVERY), any())).thenReturn(false); - Patient patient = new Patient(); - patient.setActive(true); + Patient patient = generatePatient(); - CanonicalSubscription subscription = new CanonicalSubscription(); - subscription.setIdElement(new IdType("Subscription/123")); - subscription.setEndpointUrl("http://example.com/fhir"); - subscription.setPayloadString("application/fhir+json"); + CanonicalSubscription subscription = generateSubscription(); ResourceDeliveryMessage payload = new ResourceDeliveryMessage(); payload.setSubscription(subscription); @@ -181,4 +176,77 @@ public class BaseSubscriptionDeliverySubscriberTest { } + @Test + public void testSerializeDeliveryMessageWithRequestPartition() throws JsonProcessingException { + CanonicalSubscription subscription = generateSubscription(); + Patient patient = generatePatient(); + + ResourceDeliveryMessage message = new ResourceDeliveryMessage(); + message.setPartitionId(RequestPartitionId.fromPartitionId(123, LocalDate.of(2020, 1, 1))); + message.setSubscription(subscription); + message.setPayload(myCtx, patient, EncodingEnum.JSON); + message.setOperationType(ResourceModifiedMessage.OperationTypeEnum.CREATE); + + ResourceDeliveryJsonMessage jsonMessage = new ResourceDeliveryJsonMessage(message); + String jsonString = jsonMessage.asJson(); + + ourLog.info(jsonString); + + + + // Assert that the partitionID is being serialized in JSON + assertThat(jsonString, containsString("\"partitionDate\":[2020,1,1]")); + assertThat(jsonString, containsString("\"partitionIds\":[123]")); + } + + @Test + public void testSerializeDeliveryMessageWithNoPartition() throws JsonProcessingException { + CanonicalSubscription subscription = generateSubscription(); + Patient patient = generatePatient(); + + ResourceDeliveryMessage message = new ResourceDeliveryMessage(); + message.setSubscription(subscription); + message.setPayload(myCtx, patient, EncodingEnum.JSON); + message.setOperationType(ResourceModifiedMessage.OperationTypeEnum.CREATE); + + ResourceDeliveryJsonMessage jsonMessage = new ResourceDeliveryJsonMessage(message); + String jsonString = jsonMessage.asJson(); + + ourLog.info(jsonString); + + assertThat(jsonString, containsString("\"operationType\":\"CREATE")); + assertThat(jsonString, containsString("\"canonicalSubscription\":")); + + // Assert that the default partitionID is being generated and is being serialized in JSON + assertThat(jsonString, containsString("\"allPartitions\":false")); + assertThat(jsonString, containsString("\"partitionIds\":[null]")); + } + + @Test + public void testSerializeLegacyDeliveryMessage() throws JsonProcessingException { + String legacyDeliveryMessageJson = "{\"headers\":{\"retryCount\":0,\"customHeaders\":{}},\"payload\":{\"operationType\":\"CREATE\",\"canonicalSubscription\":{\"id\":\"Subscription/123\",\"endpointUrl\":\"http://example.com/fhir\",\"payload\":\"application/fhir+json\"},\"payload\":\"{\\\"resourceType\\\":\\\"Patient\\\",\\\"active\\\":true}\"}}"; + + ResourceDeliveryJsonMessage jsonMessage = ResourceDeliveryJsonMessage.fromJson(legacyDeliveryMessageJson); + + ourLog.info(jsonMessage.getPayload().getRequestPartitionId().asJson()); + + assertNotNull(jsonMessage.getPayload().getRequestPartitionId()); + assertEquals(jsonMessage.getPayload().getRequestPartitionId().toJson(), RequestPartitionId.defaultPartition().toJson()); + } + + @NotNull + private Patient generatePatient() { + Patient patient = new Patient(); + patient.setActive(true); + return patient; + } + + @NotNull + private CanonicalSubscription generateSubscription() { + CanonicalSubscription subscription = new CanonicalSubscription(); + subscription.setIdElement(new IdType("Subscription/123")); + subscription.setEndpointUrl("http://example.com/fhir"); + subscription.setPayloadString("application/fhir+json"); + return subscription; + } } diff --git a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/ResourceModifiedTest.java b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/ResourceModifiedTest.java index 34c70aaf33d..e8660e1b366 100644 --- a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/ResourceModifiedTest.java +++ b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/ResourceModifiedTest.java @@ -1,10 +1,13 @@ package ca.uhn.fhir.jpa.subscription.module; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage; import org.hl7.fhir.r4.model.Organization; import org.junit.jupiter.api.Test; +import java.time.LocalDate; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; @@ -22,6 +25,7 @@ public class ResourceModifiedTest { Organization decodedOrg = (Organization) msg.getNewPayload(myFhirContext); assertEquals(org.getId(), decodedOrg.getId()); assertEquals(org.getName(), decodedOrg.getName()); + assertEquals(msg.getPartitionId().toJson(), RequestPartitionId.defaultPartition().toJson()); } @Test @@ -35,6 +39,7 @@ public class ResourceModifiedTest { Organization decodedOrg = (Organization) msg.getNewPayload(myFhirContext); assertEquals(org.getId(), decodedOrg.getId()); assertEquals(org.getName(), decodedOrg.getName()); + assertEquals(msg.getPartitionId().toJson(), RequestPartitionId.defaultPartition().toJson()); } @Test @@ -46,6 +51,19 @@ public class ResourceModifiedTest { assertEquals("Organization/testOrgId", msg.getPayloadId(myFhirContext).getValue()); assertEquals(ResourceModifiedMessage.OperationTypeEnum.DELETE, msg.getOperationType()); assertNull(msg.getNewPayload(myFhirContext)); + assertEquals(msg.getPartitionId().toJson(), RequestPartitionId.defaultPartition().toJson()); + } + + @Test + public void testCreateWithPartition() { + Organization org = new Organization(); + org.setName("testOrgName"); + org.setId("Organization/testOrgId"); + ResourceModifiedMessage msg = new ResourceModifiedMessage(myFhirContext, org, ResourceModifiedMessage.OperationTypeEnum.CREATE); + msg.setPartitionId(RequestPartitionId.fromPartitionId(123, LocalDate.of(2020, 1, 1))); + + assertEquals(msg.getPartitionId().getPartitionIds().size(), 1); + assertEquals(msg.getPartitionId().getPartitionIds().get(0), 123); } } diff --git a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/standalone/BaseBlockingQueueSubscribableChannelDstu3Test.java b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/standalone/BaseBlockingQueueSubscribableChannelDstu3Test.java index aaa6e1c0276..5dd320818ac 100644 --- a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/standalone/BaseBlockingQueueSubscribableChannelDstu3Test.java +++ b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/standalone/BaseBlockingQueueSubscribableChannelDstu3Test.java @@ -4,6 +4,11 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.interceptor.api.HookParams; import ca.uhn.fhir.interceptor.api.IInterceptorService; import ca.uhn.fhir.interceptor.api.Pointcut; +import ca.uhn.fhir.interceptor.model.RequestPartitionId; +import ca.uhn.fhir.jpa.api.config.DaoConfig; +import ca.uhn.fhir.jpa.api.dao.DaoRegistry; +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; +import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.jpa.subscription.channel.api.ChannelConsumerSettings; import ca.uhn.fhir.jpa.subscription.channel.subscription.ISubscriptionDeliveryChannelNamer; import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionChannelFactory; @@ -40,6 +45,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; +import org.mockito.Mockito; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -59,9 +65,11 @@ public abstract class BaseBlockingQueueSubscribableChannelDstu3Test extends Base @Autowired FhirContext myFhirContext; + @Autowired + protected DaoRegistry myDaoRegistry; // Caused by: java.lang.IllegalStateException: Unable to register mock bean org.springframework.messaging.MessageHandler expected a single matching bean to replace but found [subscriptionActivatingSubscriber, subscriptionDeliveringEmailSubscriber, subscriptionDeliveringRestHookSubscriber, subscriptionMatchingSubscriber, subscriptionRegisteringSubscriber] - + @Autowired @Qualifier("subscriptionActivatingSubscriber") MessageHandler mySubscriptionActivatingSubscriber; @@ -82,6 +90,8 @@ public abstract class BaseBlockingQueueSubscribableChannelDstu3Test extends Base private SubscriptionLoader mySubscriptionLoader; @Autowired private ISubscriptionDeliveryChannelNamer mySubscriptionDeliveryChannelNamer; + @Autowired + protected PartitionSettings myPartitionSettings; protected String myCode = "1000000050"; @@ -96,6 +106,8 @@ public abstract class BaseBlockingQueueSubscribableChannelDstu3Test extends Base protected final PointcutLatch mySubscriptionMatchingPost = new PointcutLatch(Pointcut.SUBSCRIPTION_AFTER_PERSISTED_RESOURCE_CHECKED); protected final PointcutLatch mySubscriptionActivatedPost = new PointcutLatch(Pointcut.SUBSCRIPTION_AFTER_ACTIVE_SUBSCRIPTION_REGISTERED); protected final PointcutLatch mySubscriptionAfterDelivery = new PointcutLatch(Pointcut.SUBSCRIPTION_AFTER_DELIVERY); + protected final PointcutLatch mySubscriptionResourceMatched = new PointcutLatch(Pointcut.SUBSCRIPTION_RESOURCE_MATCHED); + protected final PointcutLatch mySubscriptionResourceNotMatched = new PointcutLatch(Pointcut.SUBSCRIPTION_RESOURCE_DID_NOT_MATCH_ANY_SUBSCRIPTIONS); @BeforeEach public void beforeReset() { @@ -113,10 +125,13 @@ public abstract class BaseBlockingQueueSubscribableChannelDstu3Test extends Base myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.SUBSCRIPTION_AFTER_PERSISTED_RESOURCE_CHECKED, mySubscriptionMatchingPost); myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.SUBSCRIPTION_AFTER_ACTIVE_SUBSCRIPTION_REGISTERED, mySubscriptionActivatedPost); myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.SUBSCRIPTION_AFTER_DELIVERY, mySubscriptionAfterDelivery); + myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.SUBSCRIPTION_RESOURCE_MATCHED, mySubscriptionResourceMatched); + myInterceptorRegistry.registerAnonymousInterceptor(Pointcut.SUBSCRIPTION_RESOURCE_DID_NOT_MATCH_ANY_SUBSCRIPTIONS, mySubscriptionResourceNotMatched); } @AfterEach public void cleanup() { + myPartitionSettings.setPartitioningEnabled(false); myInterceptorRegistry.unregisterAllInterceptors(); mySubscriptionMatchingPost.clear(); mySubscriptionActivatedPost.clear(); @@ -125,7 +140,11 @@ public abstract class BaseBlockingQueueSubscribableChannelDstu3Test extends Base } public T sendResource(T theResource) throws InterruptedException { - ResourceModifiedMessage msg = new ResourceModifiedMessage(myFhirContext, theResource, ResourceModifiedMessage.OperationTypeEnum.CREATE); + return sendResource(theResource, null); + } + + public T sendResource(T theResource, RequestPartitionId theRequestPartitionId) throws InterruptedException { + ResourceModifiedMessage msg = new ResourceModifiedMessage(myFhirContext, theResource, ResourceModifiedMessage.OperationTypeEnum.CREATE, null, theRequestPartitionId); ResourceModifiedJsonMessage message = new ResourceModifiedJsonMessage(msg); mySubscriptionMatchingPost.setExpectedCount(1); ourSubscribableChannel.send(message); @@ -133,15 +152,18 @@ public abstract class BaseBlockingQueueSubscribableChannelDstu3Test extends Base return theResource; } - protected Subscription sendSubscription(String theCriteria, String thePayload, String theEndpoint) throws InterruptedException { - Subscription subscription = makeActiveSubscription(theCriteria, thePayload, theEndpoint); + protected Subscription sendSubscription(Subscription theSubscription, RequestPartitionId theRequestPartitionId, Boolean mockDao) throws InterruptedException { mySubscriptionActivatedPost.setExpectedCount(1); - Subscription retval = sendResource(subscription); + Subscription retVal = sendResource(theSubscription, theRequestPartitionId); mySubscriptionActivatedPost.awaitExpected(); - return retval; + return retVal; } protected Observation sendObservation(String code, String system) throws InterruptedException { + return sendObservation(code, system, null); + } + + protected Observation sendObservation(String code, String system, RequestPartitionId theRequestPartitionId) throws InterruptedException { Observation observation = new Observation(); IdType id = new IdType("Observation", nextId()); observation.setId(id); @@ -154,7 +176,7 @@ public abstract class BaseBlockingQueueSubscribableChannelDstu3Test extends Base observation.setStatus(Observation.ObservationStatus.FINAL); - return sendResource(observation); + return sendResource(observation, theRequestPartitionId); } @BeforeAll @@ -224,6 +246,8 @@ public abstract class BaseBlockingQueueSubscribableChannelDstu3Test extends Base } @Override - public void clear() { updateLatch.clear();} + public void clear() { + updateLatch.clear(); + } } } diff --git a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/subscriber/SubscriptionCheckingSubscriberTest.java b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/subscriber/SubscriptionCheckingSubscriberTest.java index 71af344a8fb..a53f5f72998 100644 --- a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/subscriber/SubscriptionCheckingSubscriberTest.java +++ b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/subscriber/SubscriptionCheckingSubscriberTest.java @@ -6,6 +6,7 @@ import org.hl7.fhir.dstu3.model.CodeableConcept; import org.hl7.fhir.dstu3.model.Coding; import org.hl7.fhir.dstu3.model.IdType; import org.hl7.fhir.dstu3.model.Observation; +import org.hl7.fhir.dstu3.model.Subscription; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -26,8 +27,10 @@ public class SubscriptionCheckingSubscriberTest extends BaseBlockingQueueSubscri String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml"; String criteria2 = "Observation?code=SNOMED-CT|" + code + "111&_format=xml"; - sendSubscription(criteria1, payload, ourListenerServerBase); - sendSubscription(criteria2, payload, ourListenerServerBase); + Subscription subscription1 = makeActiveSubscription(criteria1, payload, ourListenerServerBase); + sendSubscription(subscription1, null, false); + Subscription subscription2 = makeActiveSubscription(criteria2, payload, ourListenerServerBase); + sendSubscription(subscription2, null, false); assertEquals(2, mySubscriptionRegistry.size()); @@ -47,8 +50,10 @@ public class SubscriptionCheckingSubscriberTest extends BaseBlockingQueueSubscri String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml"; String criteria2 = "Observation?code=SNOMED-CT|" + code + "111&_format=xml"; - sendSubscription(criteria1, payload, ourListenerServerBase); - sendSubscription(criteria2, payload, ourListenerServerBase); + Subscription subscription1 = makeActiveSubscription(criteria1, payload, ourListenerServerBase); + sendSubscription(subscription1, null, false); + Subscription subscription2 = makeActiveSubscription(criteria2, payload, ourListenerServerBase); + sendSubscription(subscription2, null, false); assertEquals(2, mySubscriptionRegistry.size()); @@ -68,8 +73,10 @@ public class SubscriptionCheckingSubscriberTest extends BaseBlockingQueueSubscri String criteria1 = "Observation?code=SNOMED-CT|" + code; String criteria2 = "Observation?code=SNOMED-CT|" + code + "111"; - sendSubscription(criteria1, payload, ourListenerServerBase); - sendSubscription(criteria2, payload, ourListenerServerBase); + Subscription subscription1 = makeActiveSubscription(criteria1, payload, ourListenerServerBase); + sendSubscription(subscription1, null, false); + Subscription subscription2 = makeActiveSubscription(criteria2, payload, ourListenerServerBase); + sendSubscription(subscription2, null, false); assertEquals(2, mySubscriptionRegistry.size()); @@ -90,8 +97,10 @@ public class SubscriptionCheckingSubscriberTest extends BaseBlockingQueueSubscri String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml"; String criteria2 = "Observation?code=SNOMED-CT|" + code + "111&_format=xml"; - sendSubscription(criteria1, payload, ourListenerServerBase); - sendSubscription(criteria2, payload, ourListenerServerBase); + Subscription subscription1 = makeActiveSubscription(criteria1, payload, ourListenerServerBase); + sendSubscription(subscription1, null, false); + Subscription subscription2 = makeActiveSubscription(criteria2, payload, ourListenerServerBase); + sendSubscription(subscription2, null, false); assertEquals(2, mySubscriptionRegistry.size()); diff --git a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/subscriber/SubscriptionMatchingSubscriberTest.java b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/subscriber/SubscriptionMatchingSubscriberTest.java index 19950ee21c8..a9b084e995c 100644 --- a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/subscriber/SubscriptionMatchingSubscriberTest.java +++ b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/module/subscriber/SubscriptionMatchingSubscriberTest.java @@ -1,19 +1,37 @@ package ca.uhn.fhir.jpa.subscription.module.subscriber; +import ca.uhn.fhir.interceptor.model.RequestPartitionId; +import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.subscription.module.standalone.BaseBlockingQueueSubscribableChannelDstu3Test; import ca.uhn.fhir.rest.api.Constants; +import com.google.common.collect.Lists; import org.hl7.fhir.dstu3.model.Observation; +import org.hl7.fhir.dstu3.model.Subscription; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.Collections; +import java.util.List; + import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; /** * Tests copied from jpa.subscription.resthook.RestHookTestDstu3Test */ public class SubscriptionMatchingSubscriberTest extends BaseBlockingQueueSubscribableChannelDstu3Test { private static final Logger ourLog = LoggerFactory.getLogger(SubscriptionMatchingSubscriberTest.class); + private final IFhirResourceDao myMockSubscriptionDao = Mockito.mock(IFhirResourceDao.class); + + @BeforeEach + public void beforeEach() { + Mockito.when(myMockSubscriptionDao.getResourceType()).thenReturn(Subscription.class); + myDaoRegistry.register(myMockSubscriptionDao); + } @Test public void testRestHookSubscriptionApplicationFhirJson() throws Exception { @@ -23,8 +41,10 @@ public class SubscriptionMatchingSubscriberTest extends BaseBlockingQueueSubscri String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml"; String criteria2 = "Observation?code=SNOMED-CT|" + code + "111&_format=xml"; - sendSubscription(criteria1, payload, ourListenerServerBase); - sendSubscription(criteria2, payload, ourListenerServerBase); + Subscription subscription1 = makeActiveSubscription(criteria1, payload, ourListenerServerBase); + sendSubscription(subscription1, null, false); + Subscription subscription2 = makeActiveSubscription(criteria2, payload, ourListenerServerBase); + sendSubscription(subscription2, null, false); assertEquals(2, mySubscriptionRegistry.size()); @@ -44,8 +64,10 @@ public class SubscriptionMatchingSubscriberTest extends BaseBlockingQueueSubscri String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml"; String criteria2 = "Observation?code=SNOMED-CT|" + code + "111&_format=xml"; - sendSubscription(criteria1, payload, ourListenerServerBase); - sendSubscription(criteria2, payload, ourListenerServerBase); + Subscription subscription1 = makeActiveSubscription(criteria1, payload, ourListenerServerBase); + sendSubscription(subscription1, null, false); + Subscription subscription2 = makeActiveSubscription(criteria2, payload, ourListenerServerBase); + sendSubscription(subscription2, null, false); assertEquals(2, mySubscriptionRegistry.size()); @@ -59,16 +81,16 @@ public class SubscriptionMatchingSubscriberTest extends BaseBlockingQueueSubscri @Test public void testRestHookSubscription_NoResourceTypeInPayloadId() throws Exception { - sendSubscription("Observation?", "application/fhir+xml", ourListenerServerBase); - - assertEquals(1, mySubscriptionRegistry.size()); - ourObservationListener.setExpectedCount(1); - Observation observation = new Observation(); observation.setId("OBS"); observation.setStatus(Observation.ObservationStatus.CORRECTED); - sendResource(observation); + Subscription subscription = makeActiveSubscription("Observation?", "application/fhir+xml", ourListenerServerBase); + sendSubscription(subscription, null, false); + + assertEquals(1, mySubscriptionRegistry.size()); + ourObservationListener.setExpectedCount(1); + sendResource(observation); ourObservationListener.awaitExpected(); assertEquals(1, ourContentTypes.size()); @@ -83,8 +105,10 @@ public class SubscriptionMatchingSubscriberTest extends BaseBlockingQueueSubscri String criteria1 = "Observation?code=SNOMED-CT|" + code; String criteria2 = "Observation?code=SNOMED-CT|" + code + "111"; - sendSubscription(criteria1, payload, ourListenerServerBase); - sendSubscription(criteria2, payload, ourListenerServerBase); + Subscription subscription1 = makeActiveSubscription(criteria1, payload, ourListenerServerBase); + sendSubscription(subscription1, null, false); + Subscription subscription2 = makeActiveSubscription(criteria2, payload, ourListenerServerBase); + sendSubscription(subscription2, null, false); assertEquals(2, mySubscriptionRegistry.size()); @@ -107,9 +131,12 @@ public class SubscriptionMatchingSubscriberTest extends BaseBlockingQueueSubscri String criteria2 = "[*]"; String criteria3 = "Observation?code=FOO"; // won't match - sendSubscription(criteria1, payload, ourListenerServerBase); - sendSubscription(criteria2, payload, ourListenerServerBase); - sendSubscription(criteria3, payload, ourListenerServerBase); + Subscription subscription1 = makeActiveSubscription(criteria1, payload, ourListenerServerBase); + sendSubscription(subscription1, null, false); + Subscription subscription2 = makeActiveSubscription(criteria2, payload, ourListenerServerBase); + sendSubscription(subscription2, null, false); + Subscription subscription3 = makeActiveSubscription(criteria3, payload, ourListenerServerBase); + sendSubscription(subscription3, null, false); assertEquals(3, mySubscriptionRegistry.size()); @@ -121,5 +148,127 @@ public class SubscriptionMatchingSubscriberTest extends BaseBlockingQueueSubscri assertEquals(Constants.CT_FHIR_XML_NEW, ourContentTypes.get(0)); } + @Test + public void testSubscriptionAndResourceOnTheSamePartitionMatch() throws InterruptedException { + myPartitionSettings.setPartitioningEnabled(true); + String payload = "application/fhir+json"; + String code = "1000000050"; + String criteria = "Observation?code=SNOMED-CT|" + code + "&_format=xml"; + + RequestPartitionId requestPartitionId = RequestPartitionId.fromPartitionId(0); + Subscription subscription = makeActiveSubscription(criteria, payload, ourListenerServerBase); + mockSubscriptionRead(requestPartitionId, subscription); + sendSubscription(subscription, requestPartitionId, true); + + ourObservationListener.setExpectedCount(1); + mySubscriptionResourceMatched.setExpectedCount(1); + sendObservation(code, "SNOMED-CT", requestPartitionId); + mySubscriptionResourceMatched.awaitExpected(); + ourObservationListener.awaitExpected(); + } + + @Test + public void testSubscriptionAndResourceOnTheSamePartitionMatchPart2() throws InterruptedException { + myPartitionSettings.setPartitioningEnabled(true); + String payload = "application/fhir+json"; + + String code = "1000000050"; + String criteria = "Observation?code=SNOMED-CT|" + code + "&_format=xml"; + + RequestPartitionId requestPartitionId = RequestPartitionId.fromPartitionId(1); + Subscription subscription = makeActiveSubscription(criteria, payload, ourListenerServerBase); + mockSubscriptionRead(requestPartitionId, subscription); + sendSubscription(subscription, requestPartitionId, true); + + ourObservationListener.setExpectedCount(1); + mySubscriptionResourceMatched.setExpectedCount(1); + sendObservation(code, "SNOMED-CT", requestPartitionId); + mySubscriptionResourceMatched.awaitExpected(); + ourObservationListener.awaitExpected(); + } + + @Test + public void testSubscriptionAndResourceOnDiffPartitionNotMatch() throws InterruptedException { + myPartitionSettings.setPartitioningEnabled(true); + String payload = "application/fhir+json"; + + String code = "1000000050"; + String criteria = "Observation?code=SNOMED-CT|" + code + "&_format=xml"; + + RequestPartitionId requestPartitionId = RequestPartitionId.fromPartitionId(1); + Subscription subscription = makeActiveSubscription(criteria, payload, ourListenerServerBase); + mockSubscriptionRead(requestPartitionId, subscription); + sendSubscription(subscription, requestPartitionId, true); + + mySubscriptionResourceNotMatched.setExpectedCount(1); + sendObservation(code, "SNOMED-CT", RequestPartitionId.fromPartitionId(0)); + mySubscriptionResourceNotMatched.awaitExpected(); + } + + @Test + public void testSubscriptionAndResourceOnDiffPartitionNotMatchPart2() throws InterruptedException { + myPartitionSettings.setPartitioningEnabled(true); + String payload = "application/fhir+json"; + + String code = "1000000050"; + String criteria = "Observation?code=SNOMED-CT|" + code + "&_format=xml"; + + RequestPartitionId requestPartitionId = RequestPartitionId.fromPartitionId(0); + Subscription subscription = makeActiveSubscription(criteria, payload, ourListenerServerBase); + mockSubscriptionRead(requestPartitionId, subscription); + sendSubscription(subscription, requestPartitionId, true); + + mySubscriptionResourceNotMatched.setExpectedCount(1); + sendObservation(code, "SNOMED-CT", RequestPartitionId.fromPartitionId(1)); + mySubscriptionResourceNotMatched.awaitExpected(); + } + + @Test + public void testSubscriptionOnOnePartitionMatchResourceOnMultiplePartitions() throws InterruptedException { + myPartitionSettings.setPartitioningEnabled(true); + String payload = "application/fhir+json"; + + String code = "1000000050"; + String criteria = "Observation?code=SNOMED-CT|" + code + "&_format=xml"; + + RequestPartitionId requestPartitionId = RequestPartitionId.fromPartitionId(1); + Subscription subscription = makeActiveSubscription(criteria, payload, ourListenerServerBase); + mockSubscriptionRead(requestPartitionId, subscription); + sendSubscription(subscription, requestPartitionId, true); + + ourObservationListener.setExpectedCount(1); + mySubscriptionResourceMatched.setExpectedCount(1); + List partitionId = Collections.synchronizedList(Lists.newArrayList(0, 1, 2)); + sendObservation(code, "SNOMED-CT", RequestPartitionId.fromPartitionIds(partitionId)); + mySubscriptionResourceMatched.awaitExpected(); + ourObservationListener.awaitExpected(); + } + + @Test + public void testSubscriptionOnOnePartitionDoNotMatchResourceOnMultiplePartitions() throws InterruptedException { + myPartitionSettings.setPartitioningEnabled(true); + String payload = "application/fhir+json"; + + String code = "1000000050"; + String criteria = "Observation?code=SNOMED-CT|" + code + "&_format=xml"; + + RequestPartitionId requestPartitionId = RequestPartitionId.fromPartitionId(1); + Subscription subscription = makeActiveSubscription(criteria, payload, ourListenerServerBase); + mockSubscriptionRead(requestPartitionId, subscription); + sendSubscription(subscription, requestPartitionId, true); + + mySubscriptionResourceNotMatched.setExpectedCount(1); + List partitionId = Collections.synchronizedList(Lists.newArrayList(0, 2)); + sendObservation(code, "SNOMED-CT", RequestPartitionId.fromPartitionIds(partitionId)); + mySubscriptionResourceNotMatched.awaitExpected(); + } + + + private void mockSubscriptionRead(RequestPartitionId theRequestPartitionId, Subscription subscription) { + Subscription modifiedSubscription = subscription.copy(); + // the original partition info was the request info, but we need the actual storage partition. + modifiedSubscription.setUserData(Constants.RESOURCE_PARTITION_ID, theRequestPartitionId); + Mockito.when(myMockSubscriptionDao.read(eq(subscription.getIdElement()), any())).thenReturn(modifiedSubscription); + } } diff --git a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/submit/interceptor/SubscriptionSubmitInterceptorLoaderTest.java b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/submit/interceptor/SubscriptionSubmitInterceptorLoaderTest.java index e99c1f57d21..2136837d37b 100644 --- a/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/submit/interceptor/SubscriptionSubmitInterceptorLoaderTest.java +++ b/hapi-fhir-jpaserver-subscription/src/test/java/ca/uhn/fhir/jpa/subscription/submit/interceptor/SubscriptionSubmitInterceptorLoaderTest.java @@ -9,6 +9,7 @@ import ca.uhn.fhir.jpa.cache.IResourceVersionSvc; import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.jpa.model.entity.ModelConfig; import ca.uhn.fhir.jpa.model.sched.ISchedulerService; +import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc; import ca.uhn.fhir.jpa.searchparam.config.SearchParamConfig; import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamProvider; import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionChannelFactory; @@ -53,6 +54,8 @@ public class SubscriptionSubmitInterceptorLoaderTest { private SubscriptionMatcherInterceptor mySubscriptionMatcherInterceptor; @MockBean private IResourceVersionSvc myResourceVersionSvc; + @MockBean + private IRequestPartitionHelperSvc myRequestPartitionHelperSvc; /** * It should be possible to run only the {@link SubscriptionSubmitterConfig} without the diff --git a/hapi-fhir-jpaserver-test-utilities/pom.xml b/hapi-fhir-jpaserver-test-utilities/pom.xml index 3d2d5b7a27e..147c949a526 100644 --- a/hapi-fhir-jpaserver-test-utilities/pom.xml +++ b/hapi-fhir-jpaserver-test-utilities/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml index 425abab9f43..31a85f0de48 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml +++ b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-server-mdm/pom.xml b/hapi-fhir-server-mdm/pom.xml index 542a2ebbc6f..f20733c9973 100644 --- a/hapi-fhir-server-mdm/pom.xml +++ b/hapi-fhir-server-mdm/pom.xml @@ -7,7 +7,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server-openapi/pom.xml b/hapi-fhir-server-openapi/pom.xml index 97a83b16787..0b8cd4f48b2 100644 --- a/hapi-fhir-server-openapi/pom.xml +++ b/hapi-fhir-server-openapi/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server/pom.xml b/hapi-fhir-server/pom.xml index e4a9cab76f2..33ac5682d5e 100644 --- a/hapi-fhir-server/pom.xml +++ b/hapi-fhir-server/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml index 71c4eece69f..f62e86965a2 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml index 27b79222bb8..9b36af9ca77 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT hapi-fhir-spring-boot-sample-client-apache diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml index a63004c0987..9879c905a7f 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT hapi-fhir-spring-boot-sample-client-okhttp diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml index fa9440765f8..1f03e9f84d2 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT hapi-fhir-spring-boot-sample-server-jersey diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml index bec79ba5225..3ccb4920ed1 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT hapi-fhir-spring-boot-samples diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml index 847f7d5a0cd..ea724d5621f 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/pom.xml b/hapi-fhir-spring-boot/pom.xml index 487b2021e49..32cab9b70fb 100644 --- a/hapi-fhir-spring-boot/pom.xml +++ b/hapi-fhir-spring-boot/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-sql-migrate/pom.xml b/hapi-fhir-sql-migrate/pom.xml index 098d61a50c8..9473cb0b3b5 100644 --- a/hapi-fhir-sql-migrate/pom.xml +++ b/hapi-fhir-sql-migrate/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage/pom.xml b/hapi-fhir-storage/pom.xml index 3c75f26c4e4..6185d829ea7 100644 --- a/hapi-fhir-storage/pom.xml +++ b/hapi-fhir-storage/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/IRequestPartitionHelperSvc.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/partition/IRequestPartitionHelperSvc.java similarity index 100% rename from hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/partition/IRequestPartitionHelperSvc.java rename to hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/partition/IRequestPartitionHelperSvc.java diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/partition/SystemRequestDetails.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/partition/SystemRequestDetails.java index c8cf2d87134..445a507942d 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/partition/SystemRequestDetails.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/partition/SystemRequestDetails.java @@ -57,6 +57,10 @@ public class SystemRequestDetails extends RequestDetails { super(new MyInterceptorBroadcaster()); } + public static SystemRequestDetails forAllPartition(){ + return new SystemRequestDetails().setRequestPartitionId(RequestPartitionId.allPartitions()); + } + private ListMultimap myHeaders; /** diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/match/registry/SubscriptionCanonicalizer.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/match/registry/SubscriptionCanonicalizer.java index 9d5ff2af782..18a5344bcc9 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/match/registry/SubscriptionCanonicalizer.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/match/registry/SubscriptionCanonicalizer.java @@ -22,15 +22,15 @@ package ca.uhn.fhir.jpa.subscription.match.registry; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.subscription.match.matcher.matching.SubscriptionMatchingStrategy; import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription; import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscriptionChannelType; -import ca.uhn.fhir.model.dstu2.composite.CodingDt; import ca.uhn.fhir.model.dstu2.resource.Subscription; +import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; import ca.uhn.fhir.util.HapiExtensions; -import org.hl7.fhir.dstu3.model.Coding; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.instance.model.api.IBaseCoding; import org.hl7.fhir.instance.model.api.IBaseHasExtensions; @@ -46,7 +46,6 @@ import org.springframework.beans.factory.annotation.Autowired; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -124,6 +123,7 @@ public class SubscriptionCanonicalizer { if (status != null) { retVal.setStatus(org.hl7.fhir.r4.model.Subscription.SubscriptionStatus.fromCode(status.toCode())); } + setPartitionIdOnReturnValue(theSubscription, retVal); retVal.setChannelType(getChannelType(theSubscription)); retVal.setCriteriaString(subscription.getCriteria()); retVal.setEndpointUrl(subscription.getChannel().getEndpoint()); @@ -232,6 +232,7 @@ public class SubscriptionCanonicalizer { retVal.setPayloadString(subscription.getChannel().getPayload()); retVal.setPayloadSearchCriteria(getExtensionString(subscription, HapiExtensions.EXT_SUBSCRIPTION_PAYLOAD_SEARCH_CRITERIA)); retVal.setTags(extractTags(subscription)); + setPartitionIdOnReturnValue(theSubscription, retVal); if (retVal.getChannelType() == CanonicalSubscriptionChannelType.EMAIL) { String from; @@ -278,6 +279,7 @@ public class SubscriptionCanonicalizer { if (status != null) { retVal.setStatus(org.hl7.fhir.r4.model.Subscription.SubscriptionStatus.fromCode(status.toCode())); } + setPartitionIdOnReturnValue(theSubscription, retVal); retVal.setChannelType(getChannelType(subscription)); retVal.setCriteriaString(getCriteria(theSubscription)); retVal.setEndpointUrl(subscription.getEndpoint()); @@ -325,6 +327,13 @@ public class SubscriptionCanonicalizer { return retVal; } + private void setPartitionIdOnReturnValue(IBaseResource theSubscription, CanonicalSubscription retVal) { + RequestPartitionId requestPartitionId = (RequestPartitionId) theSubscription.getUserData(Constants.RESOURCE_PARTITION_ID); + if (requestPartitionId != null) { + retVal.setPartitionId(requestPartitionId.getFirstPartitionIdOrNull()); + } + } + private String getExtensionString(IBaseHasExtensions theBase, String theUrl) { return theBase .getExtension() diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/CanonicalSubscription.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/CanonicalSubscription.java index 58046647861..0662ea076c3 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/CanonicalSubscription.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/CanonicalSubscription.java @@ -43,7 +43,6 @@ import java.util.Map; import static org.apache.commons.lang3.StringUtils.isNotBlank; public class CanonicalSubscription implements Serializable, Cloneable, IModelJson { - private static final long serialVersionUID = 1L; @JsonProperty("id") @@ -72,6 +71,8 @@ public class CanonicalSubscription implements Serializable, Cloneable, IModelJso private Map myTags; @JsonProperty("payloadSearchCriteria") private String myPayloadSearchCriteria; + @JsonProperty("partitionId") + private Integer myPartitionId; /** * Constructor @@ -89,7 +90,7 @@ public class CanonicalSubscription implements Serializable, Cloneable, IModelJso } /** - * For now we're using the R4 TriggerDefinition, but this + * For now, we're using the R4 TriggerDefinition, but this * may change in the future when things stabilize */ public void addTrigger(CanonicalEventDefinition theTrigger) { @@ -159,9 +160,9 @@ 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) { - retVal = strings.get(0); + List channelExtensions = myChannelExtensions.get(theUrl); + if (channelExtensions != null && !channelExtensions.isEmpty()) { + retVal = channelExtensions.get(0); } return retVal; } @@ -227,8 +228,16 @@ public class CanonicalSubscription implements Serializable, Cloneable, IModelJso myStatus = theStatus; } + public Integer getRequestPartitionId() { + return myPartitionId; + } + + public void setPartitionId(Integer thePartitionId) { + myPartitionId = thePartitionId; + } + /** - * For now we're using the R4 triggerdefinition, but this + * For now, we're using the R4 triggerdefinition, but this * may change in the future when things stabilize */ public CanonicalEventDefinition getTrigger() { @@ -325,7 +334,7 @@ public class CanonicalSubscription implements Serializable, Cloneable, IModelJso private String mySubjectTemplate; /** - * Construcor + * Constructor */ public EmailDetails() { super(); diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/ResourceDeliveryJsonMessage.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/ResourceDeliveryJsonMessage.java index 518111ea8ab..3cb1dc71846 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/ResourceDeliveryJsonMessage.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/subscription/model/ResourceDeliveryJsonMessage.java @@ -22,9 +22,12 @@ package ca.uhn.fhir.jpa.subscription.model; import ca.uhn.fhir.rest.server.messaging.json.BaseJsonMessage; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.lang3.builder.ToStringBuilder; public class ResourceDeliveryJsonMessage extends BaseJsonMessage { + private static final ObjectMapper ourObjectMapper = new ObjectMapper().registerModule(new com.fasterxml.jackson.datatype.jsr310.JavaTimeModule()); @JsonProperty("payload") private ResourceDeliveryMessage myPayload; @@ -58,4 +61,12 @@ public class ResourceDeliveryJsonMessage extends BaseJsonMessage ca.uhn.hapi.fhir hapi-deployable-pom - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu2/pom.xml b/hapi-fhir-structures-dstu2/pom.xml index 76d0f3506c3..3ce51051bae 100644 --- a/hapi-fhir-structures-dstu2/pom.xml +++ b/hapi-fhir-structures-dstu2/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu3/pom.xml b/hapi-fhir-structures-dstu3/pom.xml index 3a342ac99de..d98c9daa845 100644 --- a/hapi-fhir-structures-dstu3/pom.xml +++ b/hapi-fhir-structures-dstu3/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-hl7org-dstu2/pom.xml b/hapi-fhir-structures-hl7org-dstu2/pom.xml index 7326f806c3d..804d736245e 100644 --- a/hapi-fhir-structures-hl7org-dstu2/pom.xml +++ b/hapi-fhir-structures-hl7org-dstu2/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r4/pom.xml b/hapi-fhir-structures-r4/pom.xml index fac21deba0e..d85868df6c2 100644 --- a/hapi-fhir-structures-r4/pom.xml +++ b/hapi-fhir-structures-r4/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r5/pom.xml b/hapi-fhir-structures-r5/pom.xml index b14b96cd88b..f240e989ee7 100644 --- a/hapi-fhir-structures-r5/pom.xml +++ b/hapi-fhir-structures-r5/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-test-utilities/pom.xml b/hapi-fhir-test-utilities/pom.xml index 5e3c55e38b1..37aa32e960a 100644 --- a/hapi-fhir-test-utilities/pom.xml +++ b/hapi-fhir-test-utilities/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-testpage-overlay/pom.xml b/hapi-fhir-testpage-overlay/pom.xml index 42e79edc220..93c38c7387b 100644 --- a/hapi-fhir-testpage-overlay/pom.xml +++ b/hapi-fhir-testpage-overlay/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-validation-resources-dstu2.1/pom.xml b/hapi-fhir-validation-resources-dstu2.1/pom.xml index 96f90b2a6f5..040fa9d44c0 100644 --- a/hapi-fhir-validation-resources-dstu2.1/pom.xml +++ b/hapi-fhir-validation-resources-dstu2.1/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-dstu2/pom.xml b/hapi-fhir-validation-resources-dstu2/pom.xml index 95ce0f6756e..f0df73a091b 100644 --- a/hapi-fhir-validation-resources-dstu2/pom.xml +++ b/hapi-fhir-validation-resources-dstu2/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-dstu3/pom.xml b/hapi-fhir-validation-resources-dstu3/pom.xml index 1f17694f61e..d7cd544f579 100644 --- a/hapi-fhir-validation-resources-dstu3/pom.xml +++ b/hapi-fhir-validation-resources-dstu3/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-r4/pom.xml b/hapi-fhir-validation-resources-r4/pom.xml index 8598a179c13..c759b5201a4 100644 --- a/hapi-fhir-validation-resources-r4/pom.xml +++ b/hapi-fhir-validation-resources-r4/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-r5/pom.xml b/hapi-fhir-validation-resources-r5/pom.xml index d5888dd5bf3..646e911159a 100644 --- a/hapi-fhir-validation-resources-r5/pom.xml +++ b/hapi-fhir-validation-resources-r5/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation/pom.xml b/hapi-fhir-validation/pom.xml index 3b226ca5fd1..d53526e1399 100644 --- a/hapi-fhir-validation/pom.xml +++ b/hapi-fhir-validation/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-tinder-plugin/pom.xml b/hapi-tinder-plugin/pom.xml index cb1ebec1b46..849e6bf464a 100644 --- a/hapi-tinder-plugin/pom.xml +++ b/hapi-tinder-plugin/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../pom.xml @@ -58,37 +58,37 @@ ca.uhn.hapi.fhir hapi-fhir-structures-dstu3 - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-structures-hl7org-dstu2 - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-structures-r4 - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-structures-r5 - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-validation-resources-dstu2 - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-validation-resources-dstu3 - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-validation-resources-r4 - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT org.apache.velocity diff --git a/hapi-tinder-test/pom.xml b/hapi-tinder-test/pom.xml index c68568be045..0ec06ed0f85 100644 --- a/hapi-tinder-test/pom.xml +++ b/hapi-tinder-test/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index 08c57ba6f9f..6399768a7a3 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir pom - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT HAPI-FHIR An open-source implementation of the FHIR specification in Java. https://hapifhir.io diff --git a/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml b/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml index a45687cc42f..3b887ea344f 100644 --- a/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml +++ b/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../../pom.xml diff --git a/tests/hapi-fhir-base-test-mindeps-client/pom.xml b/tests/hapi-fhir-base-test-mindeps-client/pom.xml index 636958f1e29..ab44d1f2ac6 100644 --- a/tests/hapi-fhir-base-test-mindeps-client/pom.xml +++ b/tests/hapi-fhir-base-test-mindeps-client/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../../pom.xml diff --git a/tests/hapi-fhir-base-test-mindeps-server/pom.xml b/tests/hapi-fhir-base-test-mindeps-server/pom.xml index 4d36de0c4ab..19ad8ce30dd 100644 --- a/tests/hapi-fhir-base-test-mindeps-server/pom.xml +++ b/tests/hapi-fhir-base-test-mindeps-server/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 5.7.0-PRE6-SNAPSHOT + 5.7.0-PRE7-SNAPSHOT ../../pom.xml