From 01d6e15f905a344dc0b833c1b0e59808ffea973d Mon Sep 17 00:00:00 2001 From: longma1 <32119004+longma1@users.noreply.github.com> Date: Wed, 6 Apr 2022 13:48:30 -0600 Subject: [PATCH] 2579 partitioned support for mdm (#3485) * committed changes discussed at tasking * Changed the subscription message to store the request partition * modified mdm subscription loader to create a cross partition subscription in the default partition if multitenancy is enabled * Fix errors when running Multitenant IT for mdm * Added partition id to mdm golden resource search and creating/updating golden resource * added partition id column to the mdm link table * Added partition id of the source resource to partition id column of the mdm link table * broken build * working -links, missing hapi tests * Fixed the mdm link db table due to feedback * added IT for create link mdm operation, also added changes to pass the IT * Fixed test for create mdm links, added test for update mdm link on partitioned server * changed mdmLink search from searchbyExample to criteriaBuilder, added partitionHelperSvc to determine partition * added test for merge golden resource and search golden resource on partitioned server * added unit test for not duplicate on partitioned and non-partitioned server * added criteriabuilder search and tests * code cleanup * added partition support to -merge-golden-resources operation * test cleanup * fixed operations with multitenancy * added test for mdm clear operation * added waiting for batch job to finish in mdm clear operation test * added query to dao to support partitioned server for mdm clear operation * resolve mistake from merge conflict * added mdm-submit operations, also fixed paging for -query * added partitionIds constant * fixed build issue where there was no Code.msg in mdm partition exceptions * code review fixes * fix broken test, also fix a bug where query link could not query by linkSource properly * fix test stubbing * bump hapi version to 6.0.0-PRE10-SNAPSHOT Co-authored-by: Ken Stevens Co-authored-by: Steven Li Co-authored-by: Long Ma --- hapi-deployable-pom/pom.xml | 2 +- hapi-fhir-android/pom.xml | 2 +- hapi-fhir-base/pom.xml | 2 +- .../src/main/java/ca/uhn/fhir/i18n/Msg.java | 2 +- .../java/ca/uhn/fhir/rest/api/Constants.java | 3 +- hapi-fhir-batch/pom.xml | 2 +- hapi-fhir-bom/pom.xml | 4 +- hapi-fhir-checkstyle/pom.xml | 2 +- hapi-fhir-cli/hapi-fhir-cli-api/pom.xml | 2 +- hapi-fhir-cli/hapi-fhir-cli-app/pom.xml | 2 +- hapi-fhir-cli/hapi-fhir-cli-jpaserver/pom.xml | 2 +- hapi-fhir-cli/pom.xml | 2 +- hapi-fhir-client-okhttp/pom.xml | 2 +- hapi-fhir-client/pom.xml | 2 +- hapi-fhir-converter/pom.xml | 2 +- hapi-fhir-dist/pom.xml | 2 +- hapi-fhir-docs/pom.xml | 2 +- hapi-fhir-jacoco/pom.xml | 2 +- hapi-fhir-jaxrsserver-base/pom.xml | 2 +- hapi-fhir-jpa/pom.xml | 2 +- hapi-fhir-jpaserver-base/pom.xml | 2 +- ...erseCronologicalBatchMdmLinkPidReader.java | 9 +- .../ca/uhn/fhir/jpa/dao/data/IMdmLinkDao.java | 3 + .../java/ca/uhn/fhir/jpa/entity/MdmLink.java | 6 +- .../tasks/HapiFhirJpaMigrationTasks.java | 33 ++-- .../partition/RequestPartitionHelperSvc.java | 21 ++- .../jpa/validation/JpaResourceLoader.java | 4 +- hapi-fhir-jpaserver-cql/pom.xml | 2 +- hapi-fhir-jpaserver-mdm/pom.xml | 2 +- .../jpa/mdm/broker/MdmMessageHandler.java | 5 +- .../jpa/mdm/config/MdmConsumerConfig.java | 10 +- .../jpa/mdm/config/MdmSubscriptionLoader.java | 11 +- .../uhn/fhir/jpa/mdm/dao/MdmLinkDaoSvc.java | 75 ++++++++- .../mdm/svc/GoldenResourceMergerSvcImpl.java | 9 + .../mdm/svc/MdmChannelSubmitterSvcImpl.java | 4 +- .../jpa/mdm/svc/MdmControllerSvcImpl.java | 60 ++++++- .../jpa/mdm/svc/MdmLinkCreateSvcImpl.java | 16 ++ .../jpa/mdm/svc/MdmLinkQuerySvcImplSvc.java | 45 ++--- .../jpa/mdm/svc/MdmLinkUpdaterSvcImpl.java | 19 +++ .../jpa/mdm/svc/MdmMatchFinderSvcImpl.java | 5 +- .../uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java | 10 +- .../fhir/jpa/mdm/svc/MdmResourceDaoSvc.java | 21 ++- .../fhir/jpa/mdm/svc/MdmSubmitSvcImpl.java | 32 ++-- .../mdm/svc/candidate/CandidateSearcher.java | 21 ++- .../svc/candidate/FindCandidateByEidSvc.java | 6 +- .../candidate/FindCandidateByExampleSvc.java | 5 +- .../svc/candidate/MdmCandidateSearchSvc.java | 18 +- .../fhir/jpa/mdm/util/MdmPartitionHelper.java | 25 +++ .../ca/uhn/fhir/jpa/mdm/BaseMdmR4Test.java | 87 ++++++++-- .../mdm/config/MdmSubscriptionLoaderTest.java | 66 +++++++- .../fhir/jpa/mdm/provider/BaseLinkR4Test.java | 2 + .../jpa/mdm/provider/BaseProviderR4Test.java | 2 +- .../provider/MdmProviderCreateLinkR4Test.java | 52 +++++- .../mdm/provider/MdmProviderMatchR4Test.java | 15 +- ...MdmProviderMergeGoldenResourcesR4Test.java | 70 +++++++- ...viderNotDuplicateGoldenResourceR4Test.java | 125 ++++++++++++++ .../provider/MdmProviderUpdateLinkR4Test.java | 48 ++++++ .../fhir/jpa/mdm/svc/MdmBatchSvcImplIT.java | 11 +- .../jpa/mdm/svc/MdmCandidateSearchSvcIT.java | 13 +- .../jpa/mdm/svc/MdmControllerSvcImplTest.java | 158 ++++++++++++++++++ .../uhn/fhir/jpa/mdm/svc/MdmLinkSvcTest.java | 17 ++ .../svc/MdmMatchLinkSvcSurvivorshipTest.java | 14 -- .../jpa/mdm/svc/MdmResourceDaoSvcTest.java | 48 +++++- .../svc/candidate/CandidateSearcherTest.java | 4 +- hapi-fhir-jpaserver-model/pom.xml | 2 +- hapi-fhir-jpaserver-searchparam/pom.xml | 2 +- hapi-fhir-jpaserver-subscription/pom.xml | 2 +- .../config/SubscriptionProcessorConfig.java | 6 +- ...bscriptionDeliveringMessageSubscriber.java | 10 +- .../SubscriptionActivatingSubscriber.java | 4 +- .../match/registry/SubscriptionLoader.java | 4 +- .../SubscriptionTriggeringSvcImpl.java | 4 +- ...aseSubscriptionDeliverySubscriberTest.java | 44 ++++- hapi-fhir-jpaserver-test-utilities/pom.xml | 2 +- hapi-fhir-jpaserver-uhnfhirtest/pom.xml | 2 +- hapi-fhir-server-mdm/pom.xml | 2 +- .../uhn/fhir/mdm/api/IMdmControllerSvc.java | 8 + .../ca/uhn/fhir/mdm/api/IMdmLinkQuerySvc.java | 4 + .../uhn/fhir/mdm/api/IMdmMatchFinderSvc.java | 3 +- .../ca/uhn/fhir/mdm/api/IMdmSubmitSvc.java | 11 +- .../fhir/mdm/provider/BaseMdmProvider.java | 2 +- .../mdm/provider/MdmControllerHelper.java | 13 +- .../mdm/provider/MdmProviderDstu3Plus.java | 28 ++-- .../fhir/mdm/util/GoldenResourceHelper.java | 4 + .../ca/uhn/fhir/mdm/util/MessageHelper.java | 11 +- hapi-fhir-server-openapi/pom.xml | 2 +- hapi-fhir-server/pom.xml | 2 +- .../server/provider/ProviderConstants.java | 1 + .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../hapi-fhir-spring-boot-samples/pom.xml | 2 +- .../hapi-fhir-spring-boot-starter/pom.xml | 2 +- hapi-fhir-spring-boot/pom.xml | 2 +- hapi-fhir-sql-migrate/pom.xml | 2 +- hapi-fhir-storage-batch2-jobs/pom.xml | 2 +- hapi-fhir-storage-batch2/pom.xml | 2 +- hapi-fhir-storage/pom.xml | 2 +- .../partition/IRequestPartitionHelperSvc.java | 5 +- .../jpa/partition/SystemRequestDetails.java | 2 +- hapi-fhir-structures-dstu2.1/pom.xml | 2 +- hapi-fhir-structures-dstu2/pom.xml | 2 +- hapi-fhir-structures-dstu3/pom.xml | 2 +- hapi-fhir-structures-hl7org-dstu2/pom.xml | 2 +- hapi-fhir-structures-r4/pom.xml | 2 +- hapi-fhir-structures-r5/pom.xml | 2 +- hapi-fhir-test-utilities/pom.xml | 2 +- hapi-fhir-testpage-overlay/pom.xml | 2 +- .../pom.xml | 2 +- hapi-fhir-validation-resources-dstu2/pom.xml | 2 +- hapi-fhir-validation-resources-dstu3/pom.xml | 2 +- hapi-fhir-validation-resources-r4/pom.xml | 2 +- hapi-fhir-validation-resources-r5/pom.xml | 2 +- hapi-fhir-validation/pom.xml | 2 +- hapi-tinder-plugin/pom.xml | 16 +- hapi-tinder-test/pom.xml | 2 +- pom.xml | 4 +- .../pom.xml | 2 +- .../pom.xml | 2 +- .../pom.xml | 2 +- 121 files changed, 1246 insertions(+), 266 deletions(-) create mode 100644 hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/util/MdmPartitionHelper.java create mode 100644 hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderNotDuplicateGoldenResourceR4Test.java create mode 100644 hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmControllerSvcImplTest.java diff --git a/hapi-deployable-pom/pom.xml b/hapi-deployable-pom/pom.xml index f355e618585..da68153960c 100644 --- a/hapi-deployable-pom/pom.xml +++ b/hapi-deployable-pom/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-android/pom.xml b/hapi-fhir-android/pom.xml index 133fc5b84a3..87e1a34e732 100644 --- a/hapi-fhir-android/pom.xml +++ b/hapi-fhir-android/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-base/pom.xml b/hapi-fhir-base/pom.xml index d462afc3955..33f1131d643 100644 --- a/hapi-fhir-base/pom.xml +++ b/hapi-fhir-base/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/i18n/Msg.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/i18n/Msg.java index d330f036673..645b10e659d 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/i18n/Msg.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/i18n/Msg.java @@ -25,7 +25,7 @@ public final class Msg { /** * IMPORTANT: Please update the following comment after you add a new code - * Last code value: 2073 + * Last code value: 2075 */ private Msg() {} diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java index cf0ffaaeda6..c66c2b8296a 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/api/Constants.java @@ -85,7 +85,7 @@ public class Constants { public static final String EXTOP_VALIDATE_RESOURCE = "resource"; public static final String FORMAT_HTML = "html"; public static final String FORMAT_JSON = "json"; - public static final String FORMAT_NDJSON = "ndjson"; + public static final String FORMAT_NDJSON = "ndjson"; public static final String FORMAT_XML = "xml"; public static final String CT_RDF_TURTLE_LEGACY = "text/turtle"; public static final String FORMAT_TURTLE = "ttl"; @@ -285,6 +285,7 @@ public class Constants { * key will be of type {@link ca.uhn.fhir.interceptor.model.RequestPartitionId}. */ public static final String RESOURCE_PARTITION_ID = Constants.class.getName() + "_RESOURCE_PARTITION_ID"; + public static final String PARTITION_IDS = "partitionIds"; public static final String CT_APPLICATION_GZIP = "application/gzip"; public static final String[] EMPTY_STRING_ARRAY = new String[0]; public static final String SUBSCRIPTION_MULTITYPE_PREFIX = "["; diff --git a/hapi-fhir-batch/pom.xml b/hapi-fhir-batch/pom.xml index 1f1d66e96f0..2772d9bc7df 100644 --- a/hapi-fhir-batch/pom.xml +++ b/hapi-fhir-batch/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-bom/pom.xml b/hapi-fhir-bom/pom.xml index a12b5cdae53..56570535d76 100644 --- a/hapi-fhir-bom/pom.xml +++ b/hapi-fhir-bom/pom.xml @@ -3,14 +3,14 @@ 4.0.0 ca.uhn.hapi.fhir hapi-fhir-bom - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT pom HAPI FHIR BOM ca.uhn.hapi.fhir hapi-deployable-pom - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-checkstyle/pom.xml b/hapi-fhir-checkstyle/pom.xml index 4c844ee23ad..b4e608a18b8 100644 --- a/hapi-fhir-checkstyle/pom.xml +++ b/hapi-fhir-checkstyle/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../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 066edbc4620..e5baefcefcc 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 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-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 d9a94154152..8ba8d4ec4fe 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 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-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 804231857ea..f960b2aaf83 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 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../../hapi-deployable-pom diff --git a/hapi-fhir-cli/pom.xml b/hapi-fhir-cli/pom.xml index f77fb638d69..ac947055ef7 100644 --- a/hapi-fhir-cli/pom.xml +++ b/hapi-fhir-cli/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-client-okhttp/pom.xml b/hapi-fhir-client-okhttp/pom.xml index ec6e3695a25..9b6f9c2afd9 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 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-client/pom.xml b/hapi-fhir-client/pom.xml index 39788725a86..888c2a7351f 100644 --- a/hapi-fhir-client/pom.xml +++ b/hapi-fhir-client/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-converter/pom.xml b/hapi-fhir-converter/pom.xml index 674f0d6ba85..ab42df96803 100644 --- a/hapi-fhir-converter/pom.xml +++ b/hapi-fhir-converter/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-dist/pom.xml b/hapi-fhir-dist/pom.xml index cc9995e267f..c91ddfa728a 100644 --- a/hapi-fhir-dist/pom.xml +++ b/hapi-fhir-dist/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-docs/pom.xml b/hapi-fhir-docs/pom.xml index 694fc7f8c28..63aaa6b1269 100644 --- a/hapi-fhir-docs/pom.xml +++ b/hapi-fhir-docs/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jacoco/pom.xml b/hapi-fhir-jacoco/pom.xml index c6d0495f30a..b13bae1377d 100644 --- a/hapi-fhir-jacoco/pom.xml +++ b/hapi-fhir-jacoco/pom.xml @@ -11,7 +11,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jaxrsserver-base/pom.xml b/hapi-fhir-jaxrsserver-base/pom.xml index 0a9f6b0184e..5334eefc48c 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 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpa/pom.xml b/hapi-fhir-jpa/pom.xml index cbcee55b472..d265bab29ec 100644 --- a/hapi-fhir-jpa/pom.xml +++ b/hapi-fhir-jpa/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-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 fe123ed2975..7e1d64298a8 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 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/batch/mdm/job/ReverseCronologicalBatchMdmLinkPidReader.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/batch/mdm/job/ReverseCronologicalBatchMdmLinkPidReader.java index 2d4a483fb1f..816db083fb1 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/batch/mdm/job/ReverseCronologicalBatchMdmLinkPidReader.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/batch/mdm/job/ReverseCronologicalBatchMdmLinkPidReader.java @@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.batch.mdm.job; * #L% */ +import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.batch.reader.BaseReverseCronologicalBatchPidReader; import ca.uhn.fhir.jpa.dao.data.IMdmLinkDao; import ca.uhn.fhir.jpa.searchparam.ResourceSearch; @@ -28,6 +29,7 @@ import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import java.util.HashSet; +import java.util.List; import java.util.Set; /** @@ -41,8 +43,13 @@ public class ReverseCronologicalBatchMdmLinkPidReader extends BaseReverseCronolo protected Set getNextPidBatch(ResourceSearch resourceSearch) { String resourceName = resourceSearch.getResourceName(); Pageable pageable = PageRequest.of(0, getBatchSize()); + RequestPartitionId requestPartitionId = resourceSearch.getRequestPartitionId(); + if (requestPartitionId.isAllPartitions()){ + return new HashSet<>(myMdmLinkDao.findPidByResourceNameAndThreshold(resourceName, getCurrentHighThreshold(), pageable)); + } + List partitionIds = requestPartitionId.getPartitionIds(); //Expand out the list to handle the REDIRECT/POSSIBLE DUPLICATE ones. - return new HashSet<>(myMdmLinkDao.findPidByResourceNameAndThreshold(resourceName, getCurrentHighThreshold(), pageable)); + return new HashSet<>(myMdmLinkDao.findPidByResourceNameAndThresholdAndPartitionId(resourceName, getCurrentHighThreshold(), partitionIds, pageable)); } @Override diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkDao.java index 8a366cdfaa8..08f3904fbd2 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkDao.java @@ -73,4 +73,7 @@ public interface IMdmLinkDao extends JpaRepository, IHapiFhirJpaR @Query("SELECT ml.myId FROM MdmLink ml WHERE ml.myMdmSourceType = :resourceName AND ml.myCreated <= :highThreshold ORDER BY ml.myCreated DESC") List findPidByResourceNameAndThreshold(@Param("resourceName") String theResourceName, @Param("highThreshold") Date theHighThreshold, Pageable thePageable); + + @Query("SELECT ml.myId FROM MdmLink ml WHERE ml.myMdmSourceType = :resourceName AND ml.myCreated <= :highThreshold AND ml.myPartitionIdValue IN :partitionId ORDER BY ml.myCreated DESC") + List findPidByResourceNameAndThresholdAndPartitionId(@Param("resourceName") String theResourceName, @Param("highThreshold") Date theHighThreshold, @Param("partitionId") List thePartitionIds, Pageable thePageable); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/MdmLink.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/MdmLink.java index 8a09013cca2..1934c3fc60e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/MdmLink.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/MdmLink.java @@ -20,10 +20,11 @@ package ca.uhn.fhir.jpa.entity; * #L% */ +import ca.uhn.fhir.jpa.model.entity.BasePartitionable; +import ca.uhn.fhir.jpa.model.entity.ResourceTable; import ca.uhn.fhir.mdm.api.IMdmLink; import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; -import ca.uhn.fhir.jpa.model.entity.ResourceTable; import org.apache.commons.lang3.builder.ToStringBuilder; import javax.persistence.Column; @@ -52,7 +53,7 @@ import java.util.Date; //TODO GGG revisit adding this: @UniqueConstraint(name = "IDX_EMPI_GR_TGT", columnNames = {"GOLDEN_RESOURCE_PID", "TARGET_PID"}), //TODO GGG Should i make individual indices for PERSON/TARGET? }) -public class MdmLink implements IMdmLink { +public class MdmLink extends BasePartitionable implements IMdmLink { public static final int VERSION_LENGTH = 16; private static final int MATCH_RESULT_LENGTH = 16; private static final int LINK_SOURCE_LENGTH = 16; @@ -331,6 +332,7 @@ public class MdmLink implements IMdmLink { .append("myHadToCreateNewResource", myHadToCreateNewGoldenResource) .append("myScore", myScore) .append("myRuleCount", myRuleCount) + .append("myPartitionId", getPartitionId()) .toString(); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java index 58b9f66a28f..e1186bc9113 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java @@ -49,11 +49,12 @@ import java.util.stream.Collectors; @SuppressWarnings({"SqlNoDataSourceInspection", "SpellCheckingInspection"}) public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { - // H2, Derby, MariaDB, and MySql automatically add indexes to foreign keys - public static final DriverTypeEnum[] NON_AUTOMATIC_FK_INDEX_PLATFORMS = new DriverTypeEnum[] { - DriverTypeEnum.POSTGRES_9_4, DriverTypeEnum.ORACLE_12C, DriverTypeEnum.MSSQL_2012 }; private final Set myFlags; + // H2, Derby, MariaDB, and MySql automatically add indexes to foreign keys + public static final DriverTypeEnum[] NON_AUTOMATIC_FK_INDEX_PLATFORMS = new DriverTypeEnum[]{ + DriverTypeEnum.POSTGRES_9_4, DriverTypeEnum.ORACLE_12C, DriverTypeEnum.MSSQL_2012}; + /** * Constructor @@ -434,14 +435,14 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { */ private void addIndexesForDeleteExpunge(Builder theVersion) { - theVersion.onTable( "HFJ_HISTORY_TAG") - .addIndex("20211210.2", "IDX_RESHISTTAG_RESID" ) + theVersion.onTable("HFJ_HISTORY_TAG") + .addIndex("20211210.2", "IDX_RESHISTTAG_RESID") .unique(false) .withColumns("RES_ID"); - theVersion.onTable( "HFJ_RES_VER_PROV") - .addIndex("20211210.3", "FK_RESVERPROV_RES_PID" ) + theVersion.onTable("HFJ_RES_VER_PROV") + .addIndex("20211210.3", "FK_RESVERPROV_RES_PID") .unique(false) .withColumns("RES_PID") .onlyAppliesToPlatforms(NON_AUTOMATIC_FK_INDEX_PLATFORMS); @@ -494,6 +495,15 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { .nullable() .type(ColumnTypeEnum.STRING, 4000); + // Add partition id column for mdm + Builder.BuilderWithTableName empiLink = version.onTable("MPI_LINK"); + + empiLink.addColumn("20220324.1", "PARTITION_ID") + .nullable() + .type(ColumnTypeEnum.INT); + empiLink.addColumn("20220324.2", "PARTITION_DATE") + .nullable() + .type(ColumnTypeEnum.DATE_ONLY); } @@ -523,15 +533,15 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { // Bump ConceptMap display lengths version.onTable("TRM_CONCEPT_MAP_GRP_ELM_TGT") - .modifyColumn("20210617.1","TARGET_DISPLAY").nullable().withType(ColumnTypeEnum.STRING, 500); + .modifyColumn("20210617.1", "TARGET_DISPLAY").nullable().withType(ColumnTypeEnum.STRING, 500); version.onTable("TRM_CONCEPT_MAP_GRP_ELEMENT") .modifyColumn("20210617.2", "SOURCE_DISPLAY").nullable().withType(ColumnTypeEnum.STRING, 500); version.onTable("HFJ_BLK_EXPORT_JOB") - .modifyColumn("20210624.1","REQUEST").nonNullable().withType(ColumnTypeEnum.STRING, 1024); + .modifyColumn("20210624.1", "REQUEST").nonNullable().withType(ColumnTypeEnum.STRING, 1024); version.onTable("HFJ_IDX_CMP_STRING_UNIQ") - .modifyColumn("20210713.1","IDX_STRING").nonNullable().withType(ColumnTypeEnum.STRING, 500); + .modifyColumn("20210713.1", "IDX_STRING").nonNullable().withType(ColumnTypeEnum.STRING, 500); version.onTable("HFJ_RESOURCE") .addColumn("20210720.1", "SP_CMPTOKS_PRESENT").nullable().type(ColumnTypeEnum.BOOLEAN); @@ -563,7 +573,7 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { .addColumn("20210915.1", "EXPANDED_AT") .nullable() .type(ColumnTypeEnum.DATE_TIMESTAMP); - + /* * Replace CLOB columns with BLOB columns */ @@ -579,7 +589,6 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { // HFJ_SEARCH.SEARCH_QUERY_STRING version.onTable("HFJ_SEARCH") .migratePostgresTextClobToBinaryClob("20211003.3", "SEARCH_QUERY_STRING"); - } 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 4102f4bf746..07c7dccc5f6 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 @@ -37,6 +37,8 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import ca.uhn.fhir.util.StringUtil; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.hl7.fhir.instance.model.api.IBaseResource; import org.slf4j.Logger; @@ -249,7 +251,7 @@ public class RequestPartitionHelperSvc implements IRequestPartitionHelperSvc { * If the partition has both, they are validated to ensure that they correspond. */ @Nonnull - private RequestPartitionId validateNormalizeAndNotifyHooksForRead(@Nonnull RequestPartitionId theRequestPartitionId, RequestDetails theRequest, String theResourceType) { + private RequestPartitionId validateNormalizeAndNotifyHooksForRead(@Nonnull RequestPartitionId theRequestPartitionId, RequestDetails theRequest, @Nonnull String theResourceType) { RequestPartitionId retVal = theRequestPartitionId; if (retVal.getPartitionNames() != null) { @@ -260,18 +262,25 @@ public class RequestPartitionHelperSvc implements IRequestPartitionHelperSvc { // Note: It's still possible that the partition only has a date but no name/id + if (StringUtils.isNotBlank(theResourceType)) { + validateHasPartitionPermissions(theRequest, theResourceType, retVal); + } + + return retVal; + + } + + public void validateHasPartitionPermissions(RequestDetails theRequest, String theResourceType, RequestPartitionId theRequestPartitionId) { if (myInterceptorBroadcaster.hasHooks(Pointcut.STORAGE_PARTITION_SELECTED)) { - RuntimeResourceDefinition runtimeResourceDefinition = myFhirContext.getResourceDefinition(theResourceType); + RuntimeResourceDefinition runtimeResourceDefinition; + runtimeResourceDefinition = myFhirContext.getResourceDefinition(theResourceType); HookParams params = new HookParams() - .add(RequestPartitionId.class, retVal) + .add(RequestPartitionId.class, theRequestPartitionId) .add(RequestDetails.class, theRequest) .addIfMatchesType(ServletRequestDetails.class, theRequest) .add(RuntimeResourceDefinition.class, runtimeResourceDefinition); doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PARTITION_SELECTED, params); } - - return retVal; - } private RequestPartitionId validateAndNormalizePartitionIds(RequestPartitionId theRequestPartitionId) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/validation/JpaResourceLoader.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/validation/JpaResourceLoader.java index 8cf7fdfaf85..9f9e56e1c23 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/validation/JpaResourceLoader.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/validation/JpaResourceLoader.java @@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.validation; */ import ca.uhn.fhir.jpa.api.dao.DaoRegistry; +import ca.uhn.fhir.jpa.partition.SystemRequestDetails; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.validation.IResourceLoader; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -35,6 +36,7 @@ public class JpaResourceLoader implements IResourceLoader { @Override public T load(Class theType, IIdType theId) throws ResourceNotFoundException { - return myDaoRegistry.getResourceDao(theType).read(theId); + SystemRequestDetails systemRequestDetails = SystemRequestDetails.forAllPartitions(); + return myDaoRegistry.getResourceDao(theType).read(theId, systemRequestDetails); } } diff --git a/hapi-fhir-jpaserver-cql/pom.xml b/hapi-fhir-jpaserver-cql/pom.xml index 88c8303e659..00a7850e6f1 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 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-mdm/pom.xml b/hapi-fhir-jpaserver-mdm/pom.xml index 1dbf2e29ca4..e158b4c1c91 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 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java index 41a30250705..e8e2b575ad5 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java @@ -37,6 +37,7 @@ import ca.uhn.fhir.mdm.api.IMdmSettings; import ca.uhn.fhir.mdm.api.MdmLinkEvent; import ca.uhn.fhir.mdm.log.Logs; import ca.uhn.fhir.mdm.model.MdmTransactionContext; +import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.server.TransactionLogMessages; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.messaging.ResourceOperationMessage; @@ -166,7 +167,9 @@ public class MdmMessageHandler implements MessageHandler { } private IAnyResource getResourceFromPayload(ResourceModifiedMessage theMsg) { - return (IAnyResource) theMsg.getNewPayload(myFhirContext); + IBaseResource newPayload = theMsg.getNewPayload(myFhirContext); + newPayload.setUserData(Constants.RESOURCE_PARTITION_ID, theMsg.getPartitionId()); + return (IAnyResource) newPayload; } private void handleUpdateResource(ResourceModifiedMessage theMsg, MdmTransactionContext theMdmTransactionContext) { diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmConsumerConfig.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmConsumerConfig.java index 2316df7165e..52e69e34d3d 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmConsumerConfig.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmConsumerConfig.java @@ -51,6 +51,8 @@ import ca.uhn.fhir.jpa.mdm.svc.candidate.FindCandidateByLinkSvc; import ca.uhn.fhir.jpa.mdm.svc.candidate.MdmCandidateSearchCriteriaBuilderSvc; import ca.uhn.fhir.jpa.mdm.svc.candidate.MdmCandidateSearchSvc; import ca.uhn.fhir.jpa.mdm.svc.candidate.MdmGoldenResourceFindingSvc; +import ca.uhn.fhir.jpa.mdm.util.MdmPartitionHelper; +import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc; import ca.uhn.fhir.mdm.api.IGoldenResourceMergerSvc; import ca.uhn.fhir.mdm.api.IMdmControllerSvc; import ca.uhn.fhir.mdm.api.IMdmLinkCreateSvc; @@ -244,12 +246,14 @@ public class MdmConsumerConfig { IResourceLoader theResourceLoader, IMdmSettings theMdmSettings, IMdmMatchFinderSvc theMdmMatchFinderSvc, - MessageHelper messageHelper) { + MessageHelper messageHelper, + IRequestPartitionHelperSvc partitionHelperSvc) { return new MdmControllerHelper(theFhirContext, theResourceLoader, theMdmMatchFinderSvc, theMdmSettings, - messageHelper); + messageHelper, + partitionHelperSvc); } @Bean @@ -257,4 +261,6 @@ public class MdmConsumerConfig { return new MdmControllerSvcImpl(); } + @Bean + MdmPartitionHelper mdmPartitionHelper() {return new MdmPartitionHelper();} } diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmSubscriptionLoader.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmSubscriptionLoader.java index b3ea94080cd..1a129b73867 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmSubscriptionLoader.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmSubscriptionLoader.java @@ -25,6 +25,7 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.i18n.Msg; 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.dao.index.IJpaIdHelperService; import ca.uhn.fhir.jpa.subscription.channel.api.ChannelProducerSettings; import ca.uhn.fhir.jpa.subscription.channel.subscription.IChannelNamer; @@ -33,7 +34,9 @@ import ca.uhn.fhir.mdm.api.MdmConstants; import ca.uhn.fhir.mdm.log.Logs; import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import ca.uhn.fhir.util.HapiExtensions; import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r4.model.BooleanType; import org.hl7.fhir.r4.model.Subscription; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; @@ -88,10 +91,10 @@ public class MdmSubscriptionLoader { synchronized void updateIfNotPresent(IBaseResource theSubscription) { try { - mySubscriptionDao.read(theSubscription.getIdElement()); + mySubscriptionDao.read(theSubscription.getIdElement(), SystemRequestDetails.forAllPartitions()); } catch (ResourceNotFoundException | ResourceGoneException e) { - ourLog.info("Creating subsription " + theSubscription.getIdElement()); - mySubscriptionDao.update(theSubscription); + ourLog.info("Creating subscription " + theSubscription.getIdElement()); + mySubscriptionDao.update(theSubscription, SystemRequestDetails.forAllPartitions()); } } @@ -102,6 +105,7 @@ public class MdmSubscriptionLoader { retval.setStatus(org.hl7.fhir.dstu3.model.Subscription.SubscriptionStatus.REQUESTED); retval.setCriteria(theCriteria); retval.getMeta().addTag().setSystem(MdmConstants.SYSTEM_MDM_MANAGED).setCode(MdmConstants.CODE_HAPI_MDM_MANAGED); + retval.addExtension().setUrl(HapiExtensions.EXTENSION_SUBSCRIPTION_CROSS_PARTITION).setValue(new org.hl7.fhir.dstu3.model.BooleanType().setValue(true)); org.hl7.fhir.dstu3.model.Subscription.SubscriptionChannelComponent channel = retval.getChannel(); channel.setType(org.hl7.fhir.dstu3.model.Subscription.SubscriptionChannelType.MESSAGE); channel.setEndpoint("channel:" + myChannelNamer.getChannelName(IMdmSettings.EMPI_CHANNEL_NAME, new ChannelProducerSettings())); @@ -116,6 +120,7 @@ public class MdmSubscriptionLoader { retval.setStatus(Subscription.SubscriptionStatus.REQUESTED); retval.setCriteria(theCriteria); retval.getMeta().addTag().setSystem(MdmConstants.SYSTEM_MDM_MANAGED).setCode(MdmConstants.CODE_HAPI_MDM_MANAGED); + retval.addExtension().setUrl(HapiExtensions.EXTENSION_SUBSCRIPTION_CROSS_PARTITION).setValue(new BooleanType().setValue(true)); Subscription.SubscriptionChannelComponent channel = retval.getChannel(); channel.setType(Subscription.SubscriptionChannelType.MESSAGE); channel.setEndpoint("channel:" + myChannelNamer.getChannelName(IMdmSettings.EMPI_CHANNEL_NAME, new ChannelProducerSettings())); diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/dao/MdmLinkDaoSvc.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/dao/MdmLinkDaoSvc.java index afd66b90280..1212bb4a7ba 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/dao/MdmLinkDaoSvc.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/dao/MdmLinkDaoSvc.java @@ -21,26 +21,42 @@ package ca.uhn.fhir.jpa.mdm.dao; */ import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.dao.data.IMdmLinkDao; import ca.uhn.fhir.jpa.dao.index.IJpaIdHelperService; import ca.uhn.fhir.jpa.entity.MdmLink; +import ca.uhn.fhir.jpa.model.entity.PartitionablePartitionId; import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; import ca.uhn.fhir.mdm.api.MdmMatchOutcome; import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; import ca.uhn.fhir.mdm.api.paging.MdmPageRequest; import ca.uhn.fhir.mdm.log.Logs; import ca.uhn.fhir.mdm.model.MdmTransactionContext; +import ca.uhn.fhir.rest.api.Constants; +import org.apache.commons.collections4.CollectionUtils; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Example; import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import javax.persistence.EntityManager; +import javax.persistence.TypedQuery; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Expression; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; +import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; @@ -58,6 +74,8 @@ public class MdmLinkDaoSvc { private IJpaIdHelperService myJpaIdHelperService; @Autowired private FhirContext myFhirContext; + @Autowired + protected EntityManager myEntityManager; @Transactional public MdmLink createOrUpdateLinkEntity(IBaseResource theGoldenResource, IBaseResource theSourceResource, MdmMatchOutcome theMatchOutcome, MdmLinkSourceEnum theLinkSource, @Nullable MdmTransactionContext theMdmTransactionContext) { @@ -76,6 +94,11 @@ public class MdmLinkDaoSvc { } else { mdmLink.setScore(theMatchOutcome.score); } + // Add partition for the mdm link if it's available in the source resource + RequestPartitionId partitionId = (RequestPartitionId) theSourceResource.getUserData(Constants.RESOURCE_PARTITION_ID); + if (partitionId != null && partitionId.getFirstPartitionIdOrNull() != null) { + mdmLink.setPartitionId(new PartitionablePartitionId(partitionId.getFirstPartitionIdOrNull(), partitionId.getPartitionDate())); + } String message = String.format("Creating MdmLink from %s to %s -> %s", theGoldenResource.getIdElement().toUnqualifiedVersionless(), theSourceResource.getIdElement().toUnqualifiedVersionless(), theMatchOutcome); theMdmTransactionContext.addTransactionLogMessage(message); @@ -253,13 +276,57 @@ public class MdmLinkDaoSvc { /** - * Given an example {@link MdmLink}, return all links from the database which match the example. + * Given a list of criteria, return all links from the database which fits the criteria provided * - * @param theExampleLink The MDM link containing the data we would like to search for. + * @param theGoldenResourceId The resource ID of the golden resource being searched. + * @param theSourceId The resource ID of the source resource being searched. + * @param theMatchResult the {@link MdmMatchResultEnum} being searched. + * @param theLinkSource the {@link MdmLinkSourceEnum} being searched. + * @param thePageRequest the {@link MdmPageRequest} paging information + * @param thePartitionId List of partitions ID being searched, where the link's partition must be in the list. * @return a list of {@link MdmLink} entities which match the example. */ - public Page findMdmLinkByExample(Example theExampleLink, MdmPageRequest thePageRequest) { - return myMdmLinkDao.findAll(theExampleLink, thePageRequest.toPageRequest()); + public PageImpl executeTypedQuery(IIdType theGoldenResourceId, IIdType theSourceId, MdmMatchResultEnum theMatchResult, MdmLinkSourceEnum theLinkSource, MdmPageRequest thePageRequest, List thePartitionId) { + CriteriaBuilder criteriaBuilder = myEntityManager.getCriteriaBuilder(); + CriteriaQuery criteriaQuery = criteriaBuilder.createQuery(MdmLink.class); + Root from = criteriaQuery.from(MdmLink.class); + + List andPredicates = new ArrayList<>(); + + if (theGoldenResourceId != null) { + Predicate goldenResourcePredicate = criteriaBuilder.equal(from.get("myGoldenResourcePid").as(Long.class), myJpaIdHelperService.getPidOrThrowException(theGoldenResourceId)); + andPredicates.add(goldenResourcePredicate); + } + if (theSourceId != null) { + Predicate sourceIdPredicate = criteriaBuilder.equal(from.get("mySourcePid").as(Long.class), myJpaIdHelperService.getPidOrThrowException(theSourceId)); + andPredicates.add(sourceIdPredicate); + } + if (theMatchResult != null) { + Predicate matchResultPredicate = criteriaBuilder.equal(from.get("myMatchResult").as(MdmMatchResultEnum.class), theMatchResult); + andPredicates.add(matchResultPredicate); + } + if (theLinkSource != null) { + Predicate linkSourcePredicate = criteriaBuilder.equal(from.get("myLinkSource").as(MdmLinkSourceEnum.class), theLinkSource); + andPredicates.add(linkSourcePredicate); + } + if (!CollectionUtils.isEmpty(thePartitionId)) { + Expression exp = from.get("myPartitionId").get("myPartitionId").as(Integer.class); + Predicate linkSourcePredicate = exp.in(thePartitionId); + andPredicates.add(linkSourcePredicate); + } + + Predicate finalQuery = criteriaBuilder.and(andPredicates.toArray(new Predicate[0])); + TypedQuery typedQuery = myEntityManager.createQuery(criteriaQuery.where(finalQuery)); + + CriteriaQuery countQuery = criteriaBuilder.createQuery(Long.class); + countQuery.select(criteriaBuilder.count(countQuery.from(MdmLink.class))) + .where(finalQuery); + + Long totalResults = myEntityManager.createQuery(countQuery).getSingleResult(); + + return new PageImpl<>(typedQuery.setFirstResult(thePageRequest.getOffset()).setMaxResults(thePageRequest.getCount()).getResultList(), + PageRequest.of(thePageRequest.getPage(), thePageRequest.getCount()), + totalResults); } /** diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/GoldenResourceMergerSvcImpl.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/GoldenResourceMergerSvcImpl.java index 86352d5e221..7b0ed9f41f0 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/GoldenResourceMergerSvcImpl.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/GoldenResourceMergerSvcImpl.java @@ -24,6 +24,7 @@ import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.dao.index.IJpaIdHelperService; import ca.uhn.fhir.jpa.entity.MdmLink; import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc; +import ca.uhn.fhir.jpa.mdm.util.MdmPartitionHelper; import ca.uhn.fhir.mdm.api.IGoldenResourceMergerSvc; import ca.uhn.fhir.mdm.api.IMdmLinkSvc; import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; @@ -32,6 +33,7 @@ import ca.uhn.fhir.mdm.log.Logs; import ca.uhn.fhir.mdm.model.MdmTransactionContext; import ca.uhn.fhir.mdm.util.GoldenResourceHelper; import ca.uhn.fhir.mdm.util.MdmResourceUtil; +import ca.uhn.fhir.mdm.util.MessageHelper; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import org.hl7.fhir.instance.model.api.IAnyResource; import org.slf4j.Logger; @@ -58,6 +60,10 @@ public class GoldenResourceMergerSvcImpl implements IGoldenResourceMergerSvc { IJpaIdHelperService myIdHelperService; @Autowired MdmResourceDaoSvc myMdmResourceDaoSvc; + @Autowired + MessageHelper myMessageHelper; + @Autowired + MdmPartitionHelper myMdmPartitionHelper; @Override @Transactional @@ -82,6 +88,9 @@ public class GoldenResourceMergerSvcImpl implements IGoldenResourceMergerSvc { myMdmResourceDaoSvc.upsertGoldenResource(theToGoldenResource, resourceType); } + // check if the golden resource and the source resource are in the same partition, throw error if not + myMdmPartitionHelper.validateResourcesInSamePartition(theFromGoldenResource, theToGoldenResource); + //Merge the links from the FROM to the TO resource. Clean up dangling links. mergeGoldenResourceLinks(theFromGoldenResource, theToGoldenResource, toGoldenResourcePid, theMdmTransactionContext); diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmChannelSubmitterSvcImpl.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmChannelSubmitterSvcImpl.java index fcad44daf7f..cd4d41e61b8 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmChannelSubmitterSvcImpl.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmChannelSubmitterSvcImpl.java @@ -21,12 +21,14 @@ package ca.uhn.fhir.jpa.mdm.svc; */ import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.subscription.channel.api.ChannelProducerSettings; import ca.uhn.fhir.jpa.subscription.channel.api.IChannelFactory; import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedJsonMessage; import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage; import ca.uhn.fhir.mdm.api.IMdmChannelSubmitterSvc; import ca.uhn.fhir.mdm.log.Logs; +import ca.uhn.fhir.rest.api.Constants; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBaseResource; import org.slf4j.Logger; @@ -50,7 +52,7 @@ public class MdmChannelSubmitterSvcImpl implements IMdmChannelSubmitterSvc { @Override public void submitResourceToMdmChannel(IBaseResource theResource) { ResourceModifiedJsonMessage resourceModifiedJsonMessage = new ResourceModifiedJsonMessage(); - ResourceModifiedMessage resourceModifiedMessage = new ResourceModifiedMessage(myFhirContext, theResource, ResourceModifiedMessage.OperationTypeEnum.MANUALLY_TRIGGERED); + ResourceModifiedMessage resourceModifiedMessage = new ResourceModifiedMessage(myFhirContext, theResource, ResourceModifiedMessage.OperationTypeEnum.MANUALLY_TRIGGERED, null, (RequestPartitionId) theResource.getUserData(Constants.RESOURCE_PARTITION_ID)); resourceModifiedMessage.setOperationType(ResourceModifiedMessage.OperationTypeEnum.MANUALLY_TRIGGERED); resourceModifiedJsonMessage.setPayload(resourceModifiedMessage); boolean success = getMdmChannelProducer().send(resourceModifiedJsonMessage); diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmControllerSvcImpl.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmControllerSvcImpl.java index 3399fd7617e..87aad2e4742 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmControllerSvcImpl.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmControllerSvcImpl.java @@ -21,6 +21,8 @@ package ca.uhn.fhir.jpa.mdm.svc; */ import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.interceptor.model.RequestPartitionId; +import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc; import ca.uhn.fhir.mdm.api.IGoldenResourceMergerSvc; import ca.uhn.fhir.mdm.api.IMdmBatchJobSubmitterFactory; import ca.uhn.fhir.mdm.api.IMdmControllerSvc; @@ -34,6 +36,8 @@ import ca.uhn.fhir.mdm.api.paging.MdmPageRequest; import ca.uhn.fhir.mdm.model.MdmTransactionContext; import ca.uhn.fhir.mdm.provider.MdmControllerHelper; import ca.uhn.fhir.mdm.provider.MdmControllerUtil; +import ca.uhn.fhir.model.primitive.IdDt; +import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.provider.MultiUrlProcessor; import ca.uhn.fhir.rest.server.provider.ProviderConstants; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; @@ -47,7 +51,9 @@ import org.springframework.stereotype.Service; import javax.annotation.Nullable; import java.math.BigDecimal; +import java.util.HashSet; import java.util.List; +import java.util.Set; /** * This class acts as a layer between MdmProviders and MDM services to support a REST API that's not a FHIR Operation API. @@ -69,6 +75,8 @@ public class MdmControllerSvcImpl implements IMdmControllerSvc { IMdmLinkCreateSvc myIMdmLinkCreateSvc; @Autowired IMdmBatchJobSubmitterFactory myMdmBatchJobSubmitterFactory; + @Autowired + IRequestPartitionHelperSvc myRequestPartitionHelperSvc; public MdmControllerSvcImpl() { } @@ -86,11 +94,33 @@ public class MdmControllerSvcImpl implements IMdmControllerSvc { @Override public Page queryLinks(@Nullable String theGoldenResourceId, @Nullable String theSourceResourceId, @Nullable String theMatchResult, @Nullable String theLinkSource, MdmTransactionContext theMdmTransactionContext, MdmPageRequest thePageRequest) { + return queryLinksFromPartitionList(theGoldenResourceId, theSourceResourceId, theMatchResult, theLinkSource, theMdmTransactionContext, thePageRequest, null); + } + + + @Override + public Page queryLinks(@Nullable String theGoldenResourceId, @Nullable String theSourceResourceId, @Nullable String theMatchResult, @Nullable String theLinkSource, MdmTransactionContext theMdmTransactionContext, MdmPageRequest thePageRequest, @Nullable RequestDetails theRequestDetails) { + RequestPartitionId theReadPartitionId = myRequestPartitionHelperSvc.determineReadPartitionForRequest(theRequestDetails, null, null); + Page resultPage; + if (theReadPartitionId.hasPartitionIds()) { + resultPage = queryLinksFromPartitionList(theGoldenResourceId, theSourceResourceId, theMatchResult, theLinkSource, theMdmTransactionContext, thePageRequest, theReadPartitionId.getPartitionIds()); + validateMdmQueryPermissions(theReadPartitionId, resultPage.getContent(), theRequestDetails); + } + else { + resultPage = queryLinksFromPartitionList(theGoldenResourceId, theSourceResourceId, theMatchResult, theLinkSource, theMdmTransactionContext, thePageRequest, null); + } + + return resultPage; + } + + @Override + public Page queryLinksFromPartitionList(@Nullable String theGoldenResourceId, @Nullable String theSourceResourceId, @Nullable String theMatchResult, @Nullable String theLinkSource, MdmTransactionContext theMdmTransactionContext, MdmPageRequest thePageRequest, @Nullable List thePartitionIds) { IIdType goldenResourceId = MdmControllerUtil.extractGoldenResourceIdDtOrNull(ProviderConstants.MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID, theGoldenResourceId); IIdType sourceId = MdmControllerUtil.extractSourceIdDtOrNull(ProviderConstants.MDM_QUERY_LINKS_RESOURCE_ID, theSourceResourceId); MdmMatchResultEnum matchResult = MdmControllerUtil.extractMatchResultOrNull(theMatchResult); MdmLinkSourceEnum linkSource = MdmControllerUtil.extractLinkSourceOrNull(theLinkSource); - return myMdmLinkQuerySvc.queryLinks(goldenResourceId, sourceId, matchResult, linkSource, theMdmTransactionContext, thePageRequest); + + return myMdmLinkQuerySvc.queryLinks(goldenResourceId, sourceId, matchResult, linkSource, theMdmTransactionContext, thePageRequest, thePartitionIds); } @Override @@ -98,6 +128,22 @@ public class MdmControllerSvcImpl implements IMdmControllerSvc { return myMdmLinkQuerySvc.getDuplicateGoldenResources(theMdmTransactionContext, thePageRequest); } + @Override + public Page getDuplicateGoldenResources(MdmTransactionContext theMdmTransactionContext, MdmPageRequest thePageRequest, RequestDetails theRequestDetails) { + Page resultPage; + RequestPartitionId readPartitionId = myRequestPartitionHelperSvc.determineReadPartitionForRequest(theRequestDetails, null, null); + if (readPartitionId.isAllPartitions()){ + resultPage = myMdmLinkQuerySvc.getDuplicateGoldenResources(theMdmTransactionContext, thePageRequest); + } + else { + resultPage = myMdmLinkQuerySvc.getDuplicateGoldenResources(theMdmTransactionContext, thePageRequest, readPartitionId.getPartitionIds()); + } + + validateMdmQueryPermissions(readPartitionId, resultPage.getContent(), theRequestDetails); + + return resultPage; + } + @Override public IAnyResource updateLink(String theGoldenResourceId, String theSourceResourceId, String theMatchResult, MdmTransactionContext theMdmTransactionContext) { MdmMatchResultEnum matchResult = MdmControllerUtil.extractMatchResultOrNull(theMatchResult); @@ -133,4 +179,16 @@ public class MdmControllerSvcImpl implements IMdmControllerSvc { myIMdmLinkUpdaterSvc.notDuplicateGoldenResource(goldenResource, target, theMdmTransactionContext); } + + private void validateMdmQueryPermissions(RequestPartitionId theRequestPartitionId, List theMdmLinkJsonList, RequestDetails theRequestDetails){ + Set seenResourceTypes = new HashSet<>(); + for (MdmLinkJson mdmLinkJson : theMdmLinkJsonList){ + IdDt idDt = new IdDt(mdmLinkJson.getSourceId()); + + if (!seenResourceTypes.contains(idDt.getResourceType())){ + myRequestPartitionHelperSvc.validateHasPartitionPermissions(theRequestDetails, idDt.getResourceType(), theRequestPartitionId); + seenResourceTypes.add(idDt.getResourceType()); + } + } + } } diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkCreateSvcImpl.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkCreateSvcImpl.java index 974f0131958..649b1bc46e7 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkCreateSvcImpl.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkCreateSvcImpl.java @@ -21,10 +21,14 @@ package ca.uhn.fhir.jpa.mdm.svc; */ import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.interceptor.model.RequestPartitionId; +import ca.uhn.fhir.jpa.dao.index.IdHelperService; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.dao.index.IJpaIdHelperService; import ca.uhn.fhir.jpa.entity.MdmLink; import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc; +import ca.uhn.fhir.jpa.mdm.util.MdmPartitionHelper; +import ca.uhn.fhir.jpa.model.entity.PartitionablePartitionId; import ca.uhn.fhir.mdm.api.IMdmLinkCreateSvc; import ca.uhn.fhir.mdm.api.IMdmSettings; import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; @@ -33,6 +37,7 @@ import ca.uhn.fhir.mdm.log.Logs; import ca.uhn.fhir.mdm.model.MdmTransactionContext; import ca.uhn.fhir.mdm.util.MdmResourceUtil; import ca.uhn.fhir.mdm.util.MessageHelper; +import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import org.hl7.fhir.instance.model.api.IAnyResource; import org.slf4j.Logger; @@ -56,6 +61,8 @@ public class MdmLinkCreateSvcImpl implements IMdmLinkCreateSvc { IMdmSettings myMdmSettings; @Autowired MessageHelper myMessageHelper; + @Autowired + MdmPartitionHelper myMdmPartitionHelper; @Transactional @Override @@ -67,6 +74,9 @@ public class MdmLinkCreateSvcImpl implements IMdmLinkCreateSvc { Long goldenResourceId = myIdHelperService.getPidOrThrowException(theGoldenResource); Long targetId = myIdHelperService.getPidOrThrowException(theSourceResource); + // check if the golden resource and the source resource are in the same partition, throw error if not + myMdmPartitionHelper.validateResourcesInSamePartition(theGoldenResource, theSourceResource); + Optional optionalMdmLink = myMdmLinkDaoSvc.getLinkByGoldenResourcePidAndSourceResourcePid(goldenResourceId, targetId); if (optionalMdmLink.isPresent()) { throw new InvalidRequestException(Msg.code(753) + myMessageHelper.getMessageForPresentLink(theGoldenResource, theSourceResource)); @@ -84,6 +94,12 @@ public class MdmLinkCreateSvcImpl implements IMdmLinkCreateSvc { } else { mdmLink.setMatchResult(theMatchResult); } + // Add partition for the mdm link if it doesn't exist + RequestPartitionId goldenResourcePartitionId = (RequestPartitionId) theGoldenResource.getUserData(Constants.RESOURCE_PARTITION_ID); + if (goldenResourcePartitionId != null && goldenResourcePartitionId.hasPartitionIds() && goldenResourcePartitionId.getFirstPartitionIdOrNull() != null && + (mdmLink.getPartitionId() == null || mdmLink.getPartitionId().getPartitionId() == null)) { + mdmLink.setPartitionId(new PartitionablePartitionId(goldenResourcePartitionId.getFirstPartitionIdOrNull(), goldenResourcePartitionId.getPartitionDate())); + } ourLog.info("Manually creating a " + theGoldenResource.getIdElement().toVersionless() + " to " + theSourceResource.getIdElement().toVersionless() + " mdm link."); myMdmLinkDaoSvc.save(mdmLink); diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkQuerySvcImplSvc.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkQuerySvcImplSvc.java index 3e2773fc68b..e7be7b46279 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkQuerySvcImplSvc.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkQuerySvcImplSvc.java @@ -20,7 +20,6 @@ package ca.uhn.fhir.jpa.mdm.svc; * #L% */ -import ca.uhn.fhir.jpa.dao.index.IJpaIdHelperService; import ca.uhn.fhir.jpa.entity.MdmLink; import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc; import ca.uhn.fhir.mdm.api.IMdmLinkQuerySvc; @@ -33,10 +32,11 @@ import org.hl7.fhir.instance.model.api.IIdType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.domain.Example; import org.springframework.data.domain.Page; import org.springframework.transaction.annotation.Transactional; +import java.util.List; + public class MdmLinkQuerySvcImplSvc implements IMdmLinkQuerySvc { private static final Logger ourLog = LoggerFactory.getLogger(MdmLinkQuerySvcImplSvc.class); @@ -44,44 +44,33 @@ public class MdmLinkQuerySvcImplSvc implements IMdmLinkQuerySvc { @Autowired MdmLinkDaoSvc myMdmLinkDaoSvc; - @Autowired - IJpaIdHelperService myIdHelperService; - @Autowired IMdmModelConverterSvc myMdmModelConverterSvc; @Override + @Deprecated @Transactional public Page queryLinks(IIdType theGoldenResourceId, IIdType theSourceResourceId, MdmMatchResultEnum theMatchResult, MdmLinkSourceEnum theLinkSource, MdmTransactionContext theMdmContext, MdmPageRequest thePageRequest) { - Example exampleLink = exampleLinkFromParameters(theGoldenResourceId, theSourceResourceId, theMatchResult, theLinkSource); - Page mdmLinkByExample = myMdmLinkDaoSvc.findMdmLinkByExample(exampleLink, thePageRequest); - Page map = mdmLinkByExample.map(myMdmModelConverterSvc::toJson); - return map; + return queryLinks(theGoldenResourceId, theSourceResourceId, theMatchResult, theLinkSource, theMdmContext, thePageRequest, null); + } + + @Override + @Transactional + public Page queryLinks(IIdType theGoldenResourceId, IIdType theSourceResourceId, MdmMatchResultEnum theMatchResult, MdmLinkSourceEnum theLinkSource, MdmTransactionContext theMdmContext, MdmPageRequest thePageRequest, List thePartitionId) { + Page mdmLinks = myMdmLinkDaoSvc.executeTypedQuery(theGoldenResourceId, theSourceResourceId, theMatchResult, theLinkSource, thePageRequest, thePartitionId); + return mdmLinks.map(myMdmModelConverterSvc::toJson); } @Override @Transactional public Page getDuplicateGoldenResources(MdmTransactionContext theMdmContext, MdmPageRequest thePageRequest) { - Example exampleLink = exampleLinkFromParameters(null, null, MdmMatchResultEnum.POSSIBLE_DUPLICATE, null); - Page mdmLinkPage = myMdmLinkDaoSvc.findMdmLinkByExample(exampleLink, thePageRequest); - Page map = mdmLinkPage.map(myMdmModelConverterSvc::toJson); - return map; + return getDuplicateGoldenResources(theMdmContext, thePageRequest, null); } - private Example exampleLinkFromParameters(IIdType theGoldenResourceId, IIdType theSourceId, MdmMatchResultEnum theMatchResult, MdmLinkSourceEnum theLinkSource) { - MdmLink mdmLink = myMdmLinkDaoSvc.newMdmLink(); - if (theGoldenResourceId != null) { - mdmLink.setGoldenResourcePid(myIdHelperService.getPidOrThrowException(theGoldenResourceId)); - } - if (theSourceId != null) { - mdmLink.setSourcePid(myIdHelperService.getPidOrThrowException(theSourceId)); - } - if (theMatchResult != null) { - mdmLink.setMatchResult(theMatchResult); - } - if (theLinkSource != null) { - mdmLink.setLinkSource(theLinkSource); - } - return Example.of(mdmLink); + @Override + @Transactional + public Page getDuplicateGoldenResources(MdmTransactionContext theMdmContext, MdmPageRequest thePageRequest, List thePartitionId) { + Page mdmLinkPage = myMdmLinkDaoSvc.executeTypedQuery(null, null, MdmMatchResultEnum.POSSIBLE_DUPLICATE, null, thePageRequest, thePartitionId); + return mdmLinkPage.map(myMdmModelConverterSvc::toJson); } } diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkUpdaterSvcImpl.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkUpdaterSvcImpl.java index 1aa5d272dbf..07ccb9bcf42 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkUpdaterSvcImpl.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkUpdaterSvcImpl.java @@ -21,6 +21,12 @@ package ca.uhn.fhir.jpa.mdm.svc; */ import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.interceptor.model.RequestPartitionId; +import ca.uhn.fhir.jpa.dao.index.IdHelperService; +import ca.uhn.fhir.jpa.entity.MdmLink; +import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc; +import ca.uhn.fhir.jpa.mdm.util.MdmPartitionHelper; +import ca.uhn.fhir.jpa.model.entity.PartitionablePartitionId; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.dao.index.IJpaIdHelperService; import ca.uhn.fhir.jpa.entity.MdmLink; @@ -35,6 +41,7 @@ import ca.uhn.fhir.mdm.log.Logs; import ca.uhn.fhir.mdm.model.MdmTransactionContext; import ca.uhn.fhir.mdm.util.MdmResourceUtil; import ca.uhn.fhir.mdm.util.MessageHelper; +import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.provider.ProviderConstants; import org.hl7.fhir.instance.model.api.IAnyResource; @@ -67,6 +74,8 @@ public class MdmLinkUpdaterSvcImpl implements IMdmLinkUpdaterSvc { MessageHelper myMessageHelper; @Autowired IMdmSurvivorshipService myMdmSurvivorshipService; + @Autowired + MdmPartitionHelper myMdmPartitionHelper; @Transactional @Override @@ -78,6 +87,9 @@ public class MdmLinkUpdaterSvcImpl implements IMdmLinkUpdaterSvc { Long goldenResourceId = myIdHelperService.getPidOrThrowException(theGoldenResource); Long targetId = myIdHelperService.getPidOrThrowException(theSourceResource); + // check if the golden resource and the source resource are in the same partition, throw error if not + myMdmPartitionHelper.validateResourcesInSamePartition(theGoldenResource, theSourceResource); + Optional optionalMdmLink = myMdmLinkDaoSvc.getLinkByGoldenResourcePidAndSourceResourcePid(goldenResourceId, targetId); if (!optionalMdmLink.isPresent()) { throw new InvalidRequestException(Msg.code(738) + myMessageHelper.getMessageForNoLink(theGoldenResource, theSourceResource)); @@ -92,6 +104,13 @@ public class MdmLinkUpdaterSvcImpl implements IMdmLinkUpdaterSvc { ourLog.info("Manually updating MDM Link for " + theGoldenResource.getIdElement().toVersionless() + ", " + theSourceResource.getIdElement().toVersionless() + " from " + mdmLink.getMatchResult() + " to " + theMatchResult + "."); mdmLink.setMatchResult(theMatchResult); mdmLink.setLinkSource(MdmLinkSourceEnum.MANUAL); + + // Add partition for the mdm link if it doesn't exist + RequestPartitionId goldenResourcePartitionId = (RequestPartitionId) theGoldenResource.getUserData(Constants.RESOURCE_PARTITION_ID); + if (goldenResourcePartitionId != null && goldenResourcePartitionId.hasPartitionIds() && goldenResourcePartitionId.getFirstPartitionIdOrNull() != null && + (mdmLink.getPartitionId() == null || mdmLink.getPartitionId().getPartitionId() == null)) { + mdmLink.setPartitionId(new PartitionablePartitionId(goldenResourcePartitionId.getFirstPartitionIdOrNull(), goldenResourcePartitionId.getPartitionDate())); + } myMdmLinkDaoSvc.save(mdmLink); if (theMatchResult == MdmMatchResultEnum.MATCH) { diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchFinderSvcImpl.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchFinderSvcImpl.java index df770f256b4..a224e14293c 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchFinderSvcImpl.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchFinderSvcImpl.java @@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.mdm.svc; * #L% */ +import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.mdm.api.IMdmMatchFinderSvc; import ca.uhn.fhir.mdm.api.MatchedTarget; import ca.uhn.fhir.mdm.log.Logs; @@ -49,8 +50,8 @@ public class MdmMatchFinderSvcImpl implements IMdmMatchFinderSvc { @Override @Nonnull @Transactional - public List getMatchedTargets(String theResourceType, IAnyResource theResource) { - Collection targetCandidates = myMdmCandidateSearchSvc.findCandidates(theResourceType, theResource); + public List getMatchedTargets(String theResourceType, IAnyResource theResource, RequestPartitionId theRequestPartitionId) { + Collection targetCandidates = myMdmCandidateSearchSvc.findCandidates(theResourceType, theResource, theRequestPartitionId); List matches = targetCandidates.stream() .map(candidate -> new MatchedTarget(candidate, myMdmResourceMatcherSvc.getMatchResult(theResource, candidate))) diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java index 9fecea10157..c73399f5804 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java @@ -20,16 +20,16 @@ package ca.uhn.fhir.jpa.mdm.svc; * #L% */ +import ca.uhn.fhir.jpa.mdm.svc.candidate.CandidateList; +import ca.uhn.fhir.jpa.mdm.svc.candidate.MatchedGoldenResourceCandidate; +import ca.uhn.fhir.jpa.mdm.svc.candidate.MdmGoldenResourceFindingSvc; +import ca.uhn.fhir.mdm.api.IMdmLinkSvc; import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; import ca.uhn.fhir.mdm.api.MdmMatchOutcome; -import ca.uhn.fhir.mdm.api.IMdmLinkSvc; import ca.uhn.fhir.mdm.log.Logs; import ca.uhn.fhir.mdm.model.MdmTransactionContext; -import ca.uhn.fhir.mdm.util.MdmResourceUtil; import ca.uhn.fhir.mdm.util.GoldenResourceHelper; -import ca.uhn.fhir.jpa.mdm.svc.candidate.CandidateList; -import ca.uhn.fhir.jpa.mdm.svc.candidate.MdmGoldenResourceFindingSvc; -import ca.uhn.fhir.jpa.mdm.svc.candidate.MatchedGoldenResourceCandidate; +import ca.uhn.fhir.mdm.util.MdmResourceUtil; import ca.uhn.fhir.rest.server.TransactionLogMessages; import org.hl7.fhir.instance.model.api.IAnyResource; import org.slf4j.Logger; diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmResourceDaoSvc.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmResourceDaoSvc.java index e5d3ab7ff44..92b6cf52d17 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmResourceDaoSvc.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmResourceDaoSvc.java @@ -20,15 +20,19 @@ package ca.uhn.fhir.jpa.mdm.svc; * #L% */ +import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome; import ca.uhn.fhir.jpa.model.entity.TagTypeEnum; +import ca.uhn.fhir.jpa.partition.SystemRequestDetails; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.mdm.api.IMdmSettings; import ca.uhn.fhir.mdm.api.MdmConstants; +import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId; import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; @@ -53,10 +57,11 @@ public class MdmResourceDaoSvc { public DaoMethodOutcome upsertGoldenResource(IAnyResource theGoldenResource, String theResourceType) { IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(theResourceType); + RequestDetails requestDetails = new SystemRequestDetails().setRequestPartitionId((RequestPartitionId) theGoldenResource.getUserData(Constants.RESOURCE_PARTITION_ID)); if (theGoldenResource.getIdElement().hasIdPart()) { - return resourceDao.update(theGoldenResource); + return resourceDao.update(theGoldenResource, requestDetails); } else { - return resourceDao.create(theGoldenResource); + return resourceDao.create(theGoldenResource, requestDetails); } } @@ -68,7 +73,8 @@ public class MdmResourceDaoSvc { */ public void removeGoldenResourceTag(IAnyResource theGoldenResource, String theResourcetype) { IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(theResourcetype); - resourceDao.removeTag(theGoldenResource.getIdElement(), TagTypeEnum.TAG, MdmConstants.SYSTEM_GOLDEN_RECORD_STATUS, MdmConstants.CODE_GOLDEN_RECORD); + RequestDetails requestDetails = new SystemRequestDetails().setRequestPartitionId((RequestPartitionId) theGoldenResource.getUserData(Constants.RESOURCE_PARTITION_ID)); + resourceDao.removeTag(theGoldenResource.getIdElement(), TagTypeEnum.TAG, MdmConstants.SYSTEM_GOLDEN_RECORD_STATUS, MdmConstants.CODE_GOLDEN_RECORD, requestDetails); } public IAnyResource readGoldenResourceByPid(ResourcePersistentId theGoldenResourcePid, String theResourceType) { @@ -76,12 +82,17 @@ public class MdmResourceDaoSvc { return (IAnyResource) resourceDao.readByPid(theGoldenResourcePid); } - //TODO GGG MDM address this public Optional searchGoldenResourceByEID(String theEid, String theResourceType) { + return this.searchGoldenResourceByEID(theEid, theResourceType, null); + } + + public Optional searchGoldenResourceByEID(String theEid, String theResourceType, RequestPartitionId thePartitionId) { SearchParameterMap map = buildEidSearchParameterMap(theEid, theResourceType); IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(theResourceType); - IBundleProvider search = resourceDao.search(map); + SystemRequestDetails systemRequestDetails = new SystemRequestDetails(); + systemRequestDetails.setRequestPartitionId(thePartitionId); + IBundleProvider search = resourceDao.search(map, systemRequestDetails); List resources = search.getResources(0, MAX_MATCHING_GOLDEN_RESOURCES); if (resources.isEmpty()) { diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmSubmitSvcImpl.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmSubmitSvcImpl.java index a5bc5403c62..6cd321dc494 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmSubmitSvcImpl.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmSubmitSvcImpl.java @@ -27,11 +27,13 @@ import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.dao.IResultIterator; import ca.uhn.fhir.jpa.dao.ISearchBuilder; import ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails; +import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.mdm.api.IMdmChannelSubmitterSvc; import ca.uhn.fhir.mdm.api.IMdmSettings; import ca.uhn.fhir.mdm.api.IMdmSubmitSvc; import ca.uhn.fhir.mdm.log.Logs; +import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; @@ -42,6 +44,7 @@ import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; +import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.io.IOException; import java.util.ArrayList; @@ -66,6 +69,9 @@ public class MdmSubmitSvcImpl implements IMdmSubmitSvc { @Autowired private IMdmSettings myMdmSettings; + @Autowired + private IRequestPartitionHelperSvc myRequestPartitionHelperSvc; + public static final int DEFAULT_BUFFER_SIZE = 100; private int myBufferSize = DEFAULT_BUFFER_SIZE; @@ -75,9 +81,9 @@ public class MdmSubmitSvcImpl implements IMdmSubmitSvc { @Override @Transactional - public long submitAllSourceTypesToMdm(@Nullable String theCriteria) { + public long submitAllSourceTypesToMdm(@Nullable String theCriteria, @Nonnull RequestDetails theRequestDetails) { long submittedCount = myMdmSettings.getMdmRules().getMdmTypes().stream() - .mapToLong(type -> submitSourceResourceTypeToMdm(type, theCriteria)) + .mapToLong(type -> submitSourceResourceTypeToMdm(type, theCriteria, theRequestDetails)) .sum(); return submittedCount; @@ -85,7 +91,7 @@ public class MdmSubmitSvcImpl implements IMdmSubmitSvc { @Override @Transactional - public long submitSourceResourceTypeToMdm(String theSourceResourceType, @Nullable String theCriteria) { + public long submitSourceResourceTypeToMdm(String theSourceResourceType, @Nullable String theCriteria, @Nonnull RequestDetails theRequestDetails) { if (theCriteria == null) { ourLog.info("Submitting all resources of type {} to MDM", theSourceResourceType); } else { @@ -97,13 +103,15 @@ public class MdmSubmitSvcImpl implements IMdmSubmitSvc { spMap.setLoadSynchronous(true); spMap.setCount(myBufferSize); ISearchBuilder searchBuilder = myMdmSearchParamSvc.generateSearchBuilderForType(theSourceResourceType); - return submitAllMatchingResourcesToMdmChannel(spMap, searchBuilder); + + RequestPartitionId requestPartitionId = myRequestPartitionHelperSvc.determineReadPartitionForRequestForSearchType(theRequestDetails, theSourceResourceType, spMap, null); + return submitAllMatchingResourcesToMdmChannel(spMap, searchBuilder, requestPartitionId); } - private long submitAllMatchingResourcesToMdmChannel(SearchParameterMap theSpMap, ISearchBuilder theSearchBuilder) { + private long submitAllMatchingResourcesToMdmChannel(SearchParameterMap theSpMap, ISearchBuilder theSearchBuilder, RequestPartitionId theRequestPartitionId) { SearchRuntimeDetails searchRuntimeDetails = new SearchRuntimeDetails(null, UUID.randomUUID().toString()); long total = 0; - try (IResultIterator query = theSearchBuilder.createQuery(theSpMap, searchRuntimeDetails, null, RequestPartitionId.defaultPartition())) { + try (IResultIterator query = theSearchBuilder.createQuery(theSpMap, searchRuntimeDetails, null, theRequestPartitionId)) { Collection pidBatch; do { pidBatch = query.getNextResultBatch(myBufferSize); @@ -136,22 +144,22 @@ public class MdmSubmitSvcImpl implements IMdmSubmitSvc { @Override @Transactional - public long submitPractitionerTypeToMdm(@Nullable String theCriteria) { - return submitSourceResourceTypeToMdm("Practitioner", theCriteria); + public long submitPractitionerTypeToMdm(@Nullable String theCriteria, @Nonnull RequestDetails theRequestDetails) { + return submitSourceResourceTypeToMdm("Practitioner", theCriteria, theRequestDetails); } @Override @Transactional - public long submitPatientTypeToMdm(@Nullable String theCriteria) { - return submitSourceResourceTypeToMdm("Patient", theCriteria); + public long submitPatientTypeToMdm(@Nullable String theCriteria, @Nonnull RequestDetails theRequestDetails) { + return submitSourceResourceTypeToMdm("Patient", theCriteria, theRequestDetails); } @Override @Transactional - public long submitSourceResourceToMdm(IIdType theId) { + public long submitSourceResourceToMdm(IIdType theId, RequestDetails theRequestDetails) { validateSourceType(theId.getResourceType()); IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(theId.getResourceType()); - IBaseResource read = resourceDao.read(theId); + IBaseResource read = resourceDao.read(theId, theRequestDetails); myMdmChannelSubmitterSvc.submitResourceToMdmChannel(read); return 1; } diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/CandidateSearcher.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/CandidateSearcher.java index a98d8b4175c..866590cfad4 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/CandidateSearcher.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/CandidateSearcher.java @@ -20,9 +20,11 @@ package ca.uhn.fhir.jpa.mdm.svc.candidate; * #L% */ +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.mdm.svc.MdmSearchParamSvc; +import ca.uhn.fhir.jpa.partition.SystemRequestDetails; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.mdm.api.IMdmSettings; import ca.uhn.fhir.rest.api.server.IBundleProvider; @@ -50,20 +52,35 @@ public class CandidateSearcher { * * @param theResourceType the type of resources searched on * @param theResourceCriteria the criteria used to search for the candidates + * @param partitionId the partition for the search * @return Optional.empty() if >= IMdmSettings.getCandidateSearchLimit() candidates are found, otherwise * return the bundle provider for the search results. */ - public Optional search(String theResourceType, String theResourceCriteria) { + public Optional search(String theResourceType, String theResourceCriteria, RequestPartitionId partitionId) { SearchParameterMap searchParameterMap = myMdmSearchParamSvc.mapFromCriteria(theResourceType, theResourceCriteria); searchParameterMap.setLoadSynchronousUpTo(myMdmSettings.getCandidateSearchLimit()); IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(theResourceType); - IBundleProvider retval = resourceDao.search(searchParameterMap); + SystemRequestDetails systemRequestDetails = new SystemRequestDetails(); + systemRequestDetails.setRequestPartitionId(partitionId); + IBundleProvider retval = resourceDao.search(searchParameterMap, systemRequestDetails); if (retval.size() != null && retval.size() >= myMdmSettings.getCandidateSearchLimit()) { return Optional.empty(); } return Optional.of(retval); } + + /** + * Perform a search for mdm candidates. + * + * @param theResourceType the type of resources searched on + * @param theResourceCriteria the criteria used to search for the candidates + * @return Optional.empty() if >= IMdmSettings.getCandidateSearchLimit() candidates are found, otherwise + * return the bundle provider for the search results. + */ + public Optional search(String theResourceType, String theResourceCriteria) { + return this.search(theResourceType, theResourceCriteria, RequestPartitionId.allPartitions()); + } } diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/FindCandidateByEidSvc.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/FindCandidateByEidSvc.java index 8d2f39448cf..9adf4e524c1 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/FindCandidateByEidSvc.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/FindCandidateByEidSvc.java @@ -20,11 +20,13 @@ package ca.uhn.fhir.jpa.mdm.svc.candidate; * #L% */ +import ca.uhn.fhir.interceptor.model.RequestPartitionId; +import ca.uhn.fhir.jpa.mdm.svc.MdmResourceDaoSvc; import ca.uhn.fhir.mdm.api.MdmMatchOutcome; import ca.uhn.fhir.mdm.log.Logs; import ca.uhn.fhir.mdm.model.CanonicalEID; import ca.uhn.fhir.mdm.util.EIDHelper; -import ca.uhn.fhir.jpa.mdm.svc.MdmResourceDaoSvc; +import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId; import org.hl7.fhir.instance.model.api.IAnyResource; import org.slf4j.Logger; @@ -52,7 +54,7 @@ public class FindCandidateByEidSvc extends BaseCandidateFinder { List eidFromResource = myEIDHelper.getExternalEid(theBaseResource); if (!eidFromResource.isEmpty()) { for (CanonicalEID eid : eidFromResource) { - Optional oFoundGoldenResource = myMdmResourceDaoSvc.searchGoldenResourceByEID(eid.getValue(), theBaseResource.getIdElement().getResourceType()); + Optional oFoundGoldenResource = myMdmResourceDaoSvc.searchGoldenResourceByEID(eid.getValue(), theBaseResource.getIdElement().getResourceType(), (RequestPartitionId) theBaseResource.getUserData(Constants.RESOURCE_PARTITION_ID)); if (oFoundGoldenResource.isPresent()) { IAnyResource foundGoldenResource = oFoundGoldenResource.get(); Long pidOrNull = myIdHelperService.getPidOrNull(foundGoldenResource); diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/FindCandidateByExampleSvc.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/FindCandidateByExampleSvc.java index e1ad5eb02aa..e2b590226ee 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/FindCandidateByExampleSvc.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/FindCandidateByExampleSvc.java @@ -21,6 +21,8 @@ package ca.uhn.fhir.jpa.mdm.svc.candidate; */ import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.interceptor.model.RequestPartitionId; +import ca.uhn.fhir.jpa.dao.index.IdHelperService; import ca.uhn.fhir.jpa.dao.index.IJpaIdHelperService; import ca.uhn.fhir.jpa.entity.MdmLink; import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc; @@ -28,6 +30,7 @@ import ca.uhn.fhir.mdm.api.IMdmMatchFinderSvc; import ca.uhn.fhir.mdm.api.MatchedTarget; import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; import ca.uhn.fhir.mdm.log.Logs; +import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -66,7 +69,7 @@ public class FindCandidateByExampleSvc extends BaseCandidateFinder { List goldenResourcePidsToExclude = getNoMatchGoldenResourcePids(theTarget); - List matchedCandidates = myMdmMatchFinderSvc.getMatchedTargets(myFhirContext.getResourceType(theTarget), theTarget); + List matchedCandidates = myMdmMatchFinderSvc.getMatchedTargets(myFhirContext.getResourceType(theTarget), theTarget, (RequestPartitionId) theTarget.getUserData(Constants.RESOURCE_PARTITION_ID)); // Convert all possible match targets to their equivalent Golden Resources by looking up in the MdmLink table, // while ensuring that the matches aren't in our NO_MATCH list. diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/MdmCandidateSearchSvc.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/MdmCandidateSearchSvc.java index 92f2f38f047..369ad4d7c28 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/MdmCandidateSearchSvc.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/MdmCandidateSearchSvc.java @@ -20,6 +20,8 @@ package ca.uhn.fhir.jpa.mdm.svc.candidate; * #L% */ +import ca.uhn.fhir.interceptor.model.RequestPartitionId; +import ca.uhn.fhir.jpa.dao.index.IdHelperService; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.dao.index.IJpaIdHelperService; import ca.uhn.fhir.mdm.api.IMdmSettings; @@ -64,12 +66,14 @@ public class MdmCandidateSearchSvc { /** * Given a source resource, search for all resources that are considered an MDM match based on defined MDM rules. * - * @param theResourceType - * @param theResource the {@link IBaseResource} we are attempting to match. + * @param theResourceType the resource type of the resource being matched + * @param theResource the {@link IBaseResource} we are attempting to match. + * @param theRequestPartitionId the {@link RequestPartitionId} representation of the partitions we are limited to when attempting to match + * * @return the list of candidate {@link IBaseResource} which could be matches to theResource */ @Transactional - public Collection findCandidates(String theResourceType, IAnyResource theResource) { + public Collection findCandidates(String theResourceType, IAnyResource theResource, RequestPartitionId theRequestPartitionId) { Map matchedPidsToResources = new HashMap<>(); List filterSearchParams = myMdmSettings.getMdmRules().getCandidateFilterSearchParams(); List filterCriteria = buildFilterQuery(filterSearchParams, theResourceType); @@ -78,7 +82,7 @@ public class MdmCandidateSearchSvc { //If there are zero MdmResourceSearchParamJson, we end up only making a single search, otherwise we //must perform one search per MdmResourceSearchParamJson. if (candidateSearchParams.isEmpty()) { - searchForIdsAndAddToMap(theResourceType, theResource, matchedPidsToResources, filterCriteria, null); + searchForIdsAndAddToMap(theResourceType, theResource, matchedPidsToResources, filterCriteria, null, theRequestPartitionId); } else { for (MdmResourceSearchParamJson resourceSearchParam : candidateSearchParams) { @@ -86,7 +90,7 @@ public class MdmCandidateSearchSvc { continue; } - searchForIdsAndAddToMap(theResourceType, theResource, matchedPidsToResources, filterCriteria, resourceSearchParam); + searchForIdsAndAddToMap(theResourceType, theResource, matchedPidsToResources, filterCriteria, resourceSearchParam, theRequestPartitionId); } } //Obviously we don't want to consider the freshly added resource as a potential candidate. @@ -113,7 +117,7 @@ public class MdmCandidateSearchSvc { * 4. Store all results in `theMatchedPidsToResources` */ @SuppressWarnings("rawtypes") - private void searchForIdsAndAddToMap(String theResourceType, IAnyResource theResource, Map theMatchedPidsToResources, List theFilterCriteria, MdmResourceSearchParamJson resourceSearchParam) { + private void searchForIdsAndAddToMap(String theResourceType, IAnyResource theResource, Map theMatchedPidsToResources, List theFilterCriteria, MdmResourceSearchParamJson resourceSearchParam, RequestPartitionId theRequestPartitionId) { //1. Optional oResourceCriteria = myMdmCandidateSearchCriteriaBuilderSvc.buildResourceQueryString(theResourceType, theResource, theFilterCriteria, resourceSearchParam); if (!oResourceCriteria.isPresent()) { @@ -123,7 +127,7 @@ public class MdmCandidateSearchSvc { ourLog.debug("Searching for {} candidates with {}", theResourceType, resourceCriteria); //2. - Optional bundleProvider = myCandidateSearcher.search(theResourceType, resourceCriteria); + Optional bundleProvider = myCandidateSearcher.search(theResourceType, resourceCriteria, theRequestPartitionId); if (!bundleProvider.isPresent()) { throw new TooManyCandidatesException(Msg.code(762) + "More than " + myMdmSettings.getCandidateSearchLimit() + " candidate matches found for " + resourceCriteria + ". Aborting mdm matching."); } diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/util/MdmPartitionHelper.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/util/MdmPartitionHelper.java new file mode 100644 index 00000000000..7585813786a --- /dev/null +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/util/MdmPartitionHelper.java @@ -0,0 +1,25 @@ +package ca.uhn.fhir.jpa.mdm.util; + +import ca.uhn.fhir.i18n.Msg; +import ca.uhn.fhir.interceptor.model.RequestPartitionId; +import ca.uhn.fhir.mdm.util.MessageHelper; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import org.hl7.fhir.instance.model.api.IAnyResource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class MdmPartitionHelper { + @Autowired + MessageHelper myMessageHelper; + + public void validateResourcesInSamePartition(IAnyResource theFromResource, IAnyResource theToResource){ + RequestPartitionId fromGoldenResourcePartitionId = (RequestPartitionId) theFromResource.getUserData(Constants.RESOURCE_PARTITION_ID); + RequestPartitionId toGoldenPartitionId = (RequestPartitionId) theToResource.getUserData(Constants.RESOURCE_PARTITION_ID); + if (fromGoldenResourcePartitionId != null && toGoldenPartitionId != null && fromGoldenResourcePartitionId.hasPartitionIds() && toGoldenPartitionId.hasPartitionIds() && + !fromGoldenResourcePartitionId.hasPartitionId(toGoldenPartitionId.getFirstPartitionIdOrNull())) { + throw new InvalidRequestException(Msg.code(2075) + myMessageHelper.getMessageForMismatchPartition(theFromResource, theToResource)); + } + } +} diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/BaseMdmR4Test.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/BaseMdmR4Test.java index d3aab6e9999..fc0dddd805e 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/BaseMdmR4Test.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/BaseMdmR4Test.java @@ -1,12 +1,12 @@ package ca.uhn.fhir.jpa.mdm; import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; +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.api.model.DaoMethodOutcome; import ca.uhn.fhir.jpa.dao.data.IMdmLinkDao; import ca.uhn.fhir.jpa.dao.index.IJpaIdHelperService; -import ca.uhn.fhir.jpa.dao.index.IdHelperService; import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test; import ca.uhn.fhir.jpa.entity.MdmLink; import ca.uhn.fhir.jpa.mdm.config.MdmConsumerConfig; @@ -20,6 +20,9 @@ import ca.uhn.fhir.jpa.mdm.matcher.IsPossibleLinkedTo; import ca.uhn.fhir.jpa.mdm.matcher.IsPossibleMatchWith; import ca.uhn.fhir.jpa.mdm.matcher.IsSameGoldenResourceAs; import ca.uhn.fhir.jpa.mdm.svc.MdmMatchLinkSvc; +import ca.uhn.fhir.jpa.model.config.PartitionSettings; +import ca.uhn.fhir.jpa.partition.IPartitionLookupSvc; +import ca.uhn.fhir.jpa.partition.SystemRequestDetails; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.registry.SearchParamRegistryImpl; import ca.uhn.fhir.jpa.subscription.match.config.SubscriptionProcessorConfig; @@ -32,6 +35,7 @@ import ca.uhn.fhir.mdm.rules.svc.MdmResourceMatcherSvc; import ca.uhn.fhir.mdm.util.EIDHelper; import ca.uhn.fhir.mdm.util.MdmResourceUtil; import ca.uhn.fhir.model.api.TemporalPrecisionEnum; +import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId; import ca.uhn.fhir.rest.param.TokenParam; @@ -74,6 +78,8 @@ import static org.slf4j.LoggerFactory.getLogger; @ContextConfiguration(classes = {MdmSubmitterConfig.class, MdmConsumerConfig.class, TestMdmConfigR4.class, SubscriptionProcessorConfig.class}) abstract public class BaseMdmR4Test extends BaseJpaR4Test { + protected static final String PARTITION_1 = "PART-1"; + protected static final String PARTITION_2 = "PART-2"; public static final String NAME_GIVEN_JANE = "Jane"; public static final String NAME_GIVEN_PAUL = "Paul"; public static final String TEST_NAME_FAMILY = "Doe"; @@ -118,6 +124,10 @@ abstract public class BaseMdmR4Test extends BaseJpaR4Test { private IInterceptorBroadcaster myInterceptorBroadcaster; @Autowired private DaoRegistry myDaoRegistry; + @Autowired + protected PartitionSettings myPartitionSettings; + @Autowired + protected IPartitionLookupSvc myPartitionLookupSvc; @BeforeEach public void beforeSetRequestDetails() { @@ -183,8 +193,40 @@ abstract public class BaseMdmR4Test extends BaseJpaR4Test { } @Nonnull - protected Medication createMedication(Medication theMedication) { + protected Patient createPatientOnPartition(Patient thePatient, boolean theMdmManaged, boolean isRedirect, RequestPartitionId theRequestPartitionId) { + if (theMdmManaged) { + MdmResourceUtil.setMdmManaged(thePatient); + if (isRedirect) { + MdmResourceUtil.setGoldenResourceRedirected(thePatient); + } else { + MdmResourceUtil.setGoldenResource(thePatient); + } + } + + SystemRequestDetails systemRequestDetails = new SystemRequestDetails(); + systemRequestDetails.setRequestPartitionId(theRequestPartitionId); + DaoMethodOutcome outcome = myPatientDao.create(thePatient, systemRequestDetails); + Patient patient = (Patient) outcome.getResource(); + patient.setId(outcome.getId()); + patient.setUserData(Constants.RESOURCE_PARTITION_ID, theRequestPartitionId); + return patient; + } + + @Nonnull + protected Patient createPatientOnPartition(Patient thePatient, RequestPartitionId theRequestPartitionId) { //Note that since our mdm-rules block on active=true, all patients must be active. + thePatient.setActive(true); + + SystemRequestDetails systemRequestDetails = new SystemRequestDetails(); + systemRequestDetails.setRequestPartitionId(theRequestPartitionId); + DaoMethodOutcome outcome = myPatientDao.create(thePatient, systemRequestDetails); + Patient patient = (Patient) outcome.getResource(); + patient.setId(outcome.getId()); + return patient; + } + + @Nonnull + protected Medication createMedication(Medication theMedication) { DaoMethodOutcome outcome = myMedicationDao.create(theMedication); Medication medication = (Medication) outcome.getResource(); medication.setId(outcome.getId()); @@ -307,7 +349,7 @@ abstract public class BaseMdmR4Test extends BaseJpaR4Test { String resourceType = theBaseResource.getIdElement().getResourceType(); IFhirResourceDao relevantDao = myDaoRegistry.getResourceDao(resourceType); - Optional matchedLinkForTargetPid = myMdmLinkDaoSvc.getMatchedLinkForSourcePid(runInTransaction(()->myIdHelperService.getPidOrNull(theBaseResource))); + Optional matchedLinkForTargetPid = myMdmLinkDaoSvc.getMatchedLinkForSourcePid(runInTransaction(() -> myIdHelperService.getPidOrNull(theBaseResource))); if (matchedLinkForTargetPid.isPresent()) { Long goldenResourcePid = matchedLinkForTargetPid.get().getGoldenResourcePid(); return (T) relevantDao.readByPid(new ResourcePersistentId(goldenResourcePid)); @@ -337,6 +379,13 @@ abstract public class BaseMdmR4Test extends BaseJpaR4Test { return thePatient; } + protected Patient createPatientAndUpdateLinksOnPartition(Patient thePatient, RequestPartitionId theRequestPartitionId) { + thePatient = createPatientOnPartition(thePatient, theRequestPartitionId); + thePatient.setUserData(Constants.RESOURCE_PARTITION_ID, theRequestPartitionId); + myMdmMatchLinkSvc.updateMdmLinksForMdmSource(thePatient, createContextForCreate("Patient")); + return thePatient; + } + protected Medication buildMedication(String theManufacturerReference) { Medication medication = new Medication(); medication.setManufacturer(new Reference(theManufacturerReference)); @@ -387,16 +436,27 @@ abstract public class BaseMdmR4Test extends BaseJpaR4Test { return thePractitioner; } + protected Practitioner createPractitionerAndUpdateLinksOnPartition(Practitioner thePractitioner, RequestPartitionId theRequestPartitionId) { + thePractitioner.setActive(true); + SystemRequestDetails systemRequestDetails = new SystemRequestDetails(); + systemRequestDetails.setRequestPartitionId(theRequestPartitionId); + DaoMethodOutcome daoMethodOutcome = myPractitionerDao.create(thePractitioner, systemRequestDetails); + thePractitioner.setId(daoMethodOutcome.getId()); + thePractitioner.setUserData(Constants.RESOURCE_PARTITION_ID, theRequestPartitionId); + myMdmMatchLinkSvc.updateMdmLinksForMdmSource(thePractitioner, createContextForCreate("Practitioner")); + return thePractitioner; + } + private Matcher wrapMatcherInTransaction(Supplier> theFunction) { return new Matcher() { @Override public boolean matches(Object actual) { - return runInTransaction(()->theFunction.get().matches(actual)); + return runInTransaction(() -> theFunction.get().matches(actual)); } @Override public void describeMismatch(Object actual, Description mismatchDescription) { - runInTransaction(()->theFunction.get().describeMismatch(actual, mismatchDescription)); + runInTransaction(() -> theFunction.get().describeMismatch(actual, mismatchDescription)); } @Override @@ -406,33 +466,33 @@ abstract public class BaseMdmR4Test extends BaseJpaR4Test { @Override public void describeTo(Description description) { - runInTransaction(()->theFunction.get().describeTo(description)); + runInTransaction(() -> theFunction.get().describeTo(description)); } }; } protected Matcher sameGoldenResourceAs(IAnyResource... theBaseResource) { - return wrapMatcherInTransaction(()->IsSameGoldenResourceAs.sameGoldenResourceAs(myIdHelperService, myMdmLinkDaoSvc, theBaseResource)); + return wrapMatcherInTransaction(() -> IsSameGoldenResourceAs.sameGoldenResourceAs(myIdHelperService, myMdmLinkDaoSvc, theBaseResource)); } protected Matcher linkedTo(IAnyResource... theBaseResource) { - return wrapMatcherInTransaction(()->IsLinkedTo.linkedTo(myIdHelperService, myMdmLinkDaoSvc, theBaseResource)); + return wrapMatcherInTransaction(() -> IsLinkedTo.linkedTo(myIdHelperService, myMdmLinkDaoSvc, theBaseResource)); } protected Matcher possibleLinkedTo(IAnyResource... theBaseResource) { - return wrapMatcherInTransaction(()->IsPossibleLinkedTo.possibleLinkedTo(myIdHelperService, myMdmLinkDaoSvc, theBaseResource)); + return wrapMatcherInTransaction(() -> IsPossibleLinkedTo.possibleLinkedTo(myIdHelperService, myMdmLinkDaoSvc, theBaseResource)); } protected Matcher possibleMatchWith(IAnyResource... theBaseResource) { - return wrapMatcherInTransaction(()->IsPossibleMatchWith.possibleMatchWith(myIdHelperService, myMdmLinkDaoSvc, theBaseResource)); + return wrapMatcherInTransaction(() -> IsPossibleMatchWith.possibleMatchWith(myIdHelperService, myMdmLinkDaoSvc, theBaseResource)); } protected Matcher possibleDuplicateOf(IAnyResource... theBaseResource) { - return wrapMatcherInTransaction(()->IsPossibleDuplicateOf.possibleDuplicateOf(myIdHelperService, myMdmLinkDaoSvc, theBaseResource)); + return wrapMatcherInTransaction(() -> IsPossibleDuplicateOf.possibleDuplicateOf(myIdHelperService, myMdmLinkDaoSvc, theBaseResource)); } protected Matcher matchedToAGoldenResource() { - return wrapMatcherInTransaction(()->IsMatchedToAGoldenResource.matchedToAGoldenResource(myIdHelperService, myMdmLinkDaoSvc)); + return wrapMatcherInTransaction(() -> IsMatchedToAGoldenResource.matchedToAGoldenResource(myIdHelperService, myMdmLinkDaoSvc)); } protected Patient getOnlyGoldenPatient() { @@ -458,7 +518,8 @@ abstract public class BaseMdmR4Test extends BaseJpaR4Test { map.setLoadSynchronous(true); //TODO GGG ensure that this tag search works effectively. map.add("_tag", new TokenParam(MdmConstants.SYSTEM_GOLDEN_RECORD_STATUS, theCode)); - IBundleProvider bundle = myPatientDao.search(map); + SystemRequestDetails systemRequestDetails = SystemRequestDetails.forAllPartitions(); + IBundleProvider bundle = myPatientDao.search(map, systemRequestDetails); return bundle.getResources(0, 999); } diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/config/MdmSubscriptionLoaderTest.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/config/MdmSubscriptionLoaderTest.java index 5dd7ca74d18..02d7003915d 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/config/MdmSubscriptionLoaderTest.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/config/MdmSubscriptionLoaderTest.java @@ -1,19 +1,40 @@ package ca.uhn.fhir.jpa.mdm.config; +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.subscription.channel.subscription.IChannelNamer; +import ca.uhn.fhir.mdm.api.IMdmSettings; +import ca.uhn.fhir.mdm.rules.json.MdmRulesJson; +import ca.uhn.fhir.model.api.IFhirVersion; +import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import ca.uhn.fhir.util.ExtensionUtil; +import ca.uhn.fhir.util.HapiExtensions; +import org.hl7.fhir.instance.model.api.IBaseExtension; import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r4.model.BooleanType; +import org.hl7.fhir.r4.model.Extension; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Subscription; import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.beans.factory.annotation.Autowired; +import java.util.Arrays; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -24,6 +45,18 @@ class MdmSubscriptionLoaderTest { @Mock IFhirResourceDao mySubscriptionDao; + @Mock + DaoRegistry myDaoRegistry; + + @Mock + IMdmSettings myMdmSettings; + + @Spy + FhirContext myFhirContext = FhirContext.forR4Cached(); + + @Mock + IChannelNamer myChannelNamer; + @InjectMocks MdmSubscriptionLoader mySvc = new MdmSubscriptionLoader(); @@ -37,9 +70,9 @@ class MdmSubscriptionLoaderTest { Subscription subscription = new Subscription(); IdType id = new IdType("2401"); subscription.setIdElement(id); - when(mySubscriptionDao.read(id)).thenThrow(new ResourceGoneException("")); + when(mySubscriptionDao.read(eq(id), any())).thenThrow(new ResourceGoneException("")); mySvc.updateIfNotPresent(subscription); - verify(mySubscriptionDao).update(subscription); + verify(mySubscriptionDao).update(eq(subscription), any(RequestDetails.class)); } @Test @@ -47,9 +80,9 @@ class MdmSubscriptionLoaderTest { Subscription subscription = new Subscription(); IdType id = new IdType("2401"); subscription.setIdElement(id); - when(mySubscriptionDao.read(id)).thenThrow(new ResourceNotFoundException("")); + when(mySubscriptionDao.read(eq(id), any())).thenThrow(new ResourceNotFoundException("")); mySvc.updateIfNotPresent(subscription); - verify(mySubscriptionDao).update(subscription); + verify(mySubscriptionDao).update(eq(subscription), any(RequestDetails.class)); } @Test @@ -57,8 +90,29 @@ class MdmSubscriptionLoaderTest { Subscription subscription = new Subscription(); IdType id = new IdType("2401"); subscription.setIdElement(id); - when(mySubscriptionDao.read(id)).thenReturn(subscription); + when(mySubscriptionDao.read(eq(id), any())).thenReturn(subscription); mySvc.updateIfNotPresent(subscription); - verify(mySubscriptionDao, never()).update(any()); + verify(mySubscriptionDao, never()).update(any(), any(RequestDetails.class)); + } + + @Test + public void testDaoUpdateMdmSubscriptions() { + MdmRulesJson mdmRulesJson = new MdmRulesJson(); + mdmRulesJson.setMdmTypes(Arrays.asList("Patient")); + when(myMdmSettings.getMdmRules()).thenReturn(mdmRulesJson); + when(myChannelNamer.getChannelName(any(), any())).thenReturn("Test"); + when(myDaoRegistry.getResourceDao(eq("Subscription"))).thenReturn(mySubscriptionDao); + when(mySubscriptionDao.read(any(), any())).thenThrow(new ResourceGoneException("")); + + mySvc.daoUpdateMdmSubscriptions(); + + ArgumentCaptor subscriptionCaptor = ArgumentCaptor.forClass(Subscription.class); + verify(mySubscriptionDao).update(subscriptionCaptor.capture(), any(RequestDetails.class)); + + IBaseExtension extension = ExtensionUtil.getExtensionByUrl(subscriptionCaptor.getValue(), HapiExtensions.EXTENSION_SUBSCRIPTION_CROSS_PARTITION); + + assertNotNull(extension); + + assertTrue(((BooleanType)extension.getValue()).booleanValue()); } } diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/BaseLinkR4Test.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/BaseLinkR4Test.java index c1d201cec50..98a53c9ce0d 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/BaseLinkR4Test.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/BaseLinkR4Test.java @@ -2,6 +2,7 @@ package ca.uhn.fhir.jpa.mdm.provider; import ca.uhn.fhir.jpa.api.config.DaoConfig; import ca.uhn.fhir.jpa.entity.MdmLink; +import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; import org.hl7.fhir.instance.model.api.IAnyResource; @@ -59,6 +60,7 @@ public abstract class BaseLinkR4Test extends BaseProviderR4Test { @AfterEach public void after() throws IOException { myDaoConfig.setExpungeEnabled(new DaoConfig().isExpungeEnabled()); + myPartitionSettings.setPartitioningEnabled(new PartitionSettings().isPartitioningEnabled()); super.after(); } diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/BaseProviderR4Test.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/BaseProviderR4Test.java index 6b98fe189c4..1a63e0c9d1a 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/BaseProviderR4Test.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/BaseProviderR4Test.java @@ -27,7 +27,7 @@ import java.util.ArrayList; import java.util.List; public abstract class BaseProviderR4Test extends BaseMdmR4Test { - MdmProviderDstu3Plus myMdmProvider; + protected MdmProviderDstu3Plus myMdmProvider; @Autowired private IMdmControllerSvc myMdmControllerSvc; @Autowired diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderCreateLinkR4Test.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderCreateLinkR4Test.java index c7ac52de974..3a86186cd75 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderCreateLinkR4Test.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderCreateLinkR4Test.java @@ -1,6 +1,9 @@ package ca.uhn.fhir.jpa.mdm.provider; + +import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.entity.MdmLink; +import ca.uhn.fhir.jpa.entity.PartitionEntity; import ca.uhn.fhir.mdm.api.MdmConstants; import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; @@ -17,7 +20,7 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.startsWith; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.fail; public class MdmProviderCreateLinkR4Test extends BaseLinkR4Test { @@ -43,6 +46,53 @@ public class MdmProviderCreateLinkR4Test extends BaseLinkR4Test { assertEquals(MdmMatchResultEnum.MATCH, links.get(0).getMatchResult()); } + @Test + public void testCreateLinkWithMatchResultOnSamePartition() { + myPartitionSettings.setPartitioningEnabled(true); + myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1)); + assertLinkCount(1); + + RequestPartitionId requestPartitionId = RequestPartitionId.fromPartitionId(1); + Patient patient = createPatientOnPartition(buildPatientWithNameAndId("PatientGiven", "ID.PatientGiven.123"), true, false, requestPartitionId); + StringType patientId = new StringType(patient.getIdElement().getValue()); + + Patient sourcePatient = createPatientOnPartition(buildPatientWithNameAndId("SourcePatientGiven", "ID.SourcePatientGiven.123"), true, false, requestPartitionId); + StringType sourcePatientId = new StringType(sourcePatient.getIdElement().getValue()); + + myMdmProvider.createLink(sourcePatientId, patientId, MATCH_RESULT, myRequestDetails); + assertLinkCount(2); + + List links = myMdmLinkDaoSvc.findMdmLinksBySourceResource(patient); + assertEquals(links.size(), 1); + assertEquals(MdmLinkSourceEnum.MANUAL, links.get(0).getLinkSource()); + assertEquals(MdmMatchResultEnum.MATCH, links.get(0).getMatchResult()); + assertNotNull(links.get(0).getPartitionId()); + assertEquals(1, links.get(0).getPartitionId().getPartitionId()); + } + + @Test + public void testCreateLinkWithMatchResultOnDifferentPartitions() { + myPartitionSettings.setPartitioningEnabled(true); + myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1)); + myPartitionLookupSvc.createPartition(new PartitionEntity().setId(2).setName(PARTITION_2)); + assertLinkCount(1); + + RequestPartitionId requestPartitionId1 = RequestPartitionId.fromPartitionId(1); + Patient patient = createPatientOnPartition(buildPatientWithNameAndId("PatientGiven", "ID.PatientGiven.123"), true, false, requestPartitionId1); + StringType patientId = new StringType(patient.getIdElement().getValue()); + + RequestPartitionId requestPartitionId2 = RequestPartitionId.fromPartitionId(2); + Patient sourcePatient = createPatientOnPartition(buildPatientWithNameAndId("SourcePatientGiven", "ID.SourcePatientGiven.123"), true, false, requestPartitionId2); + StringType sourcePatientId = new StringType(sourcePatient.getIdElement().getValue()); + + try { + myMdmProvider.createLink(sourcePatientId, patientId, MATCH_RESULT, myRequestDetails); + fail(); + } catch (InvalidRequestException e) { + assertThat(e.getMessage(), endsWith("This operation is only available for resources on the same partition.")); + } + } + @Test public void testCreateLinkWithNullMatchResult() { assertLinkCount(1); diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderMatchR4Test.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderMatchR4Test.java index 5e45d5260ec..3551facb44a 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderMatchR4Test.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderMatchR4Test.java @@ -1,5 +1,6 @@ package ca.uhn.fhir.jpa.mdm.provider; +import ca.uhn.fhir.jpa.partition.SystemRequestDetails; import ca.uhn.fhir.mdm.api.MdmConstants; import com.google.common.collect.Ordering; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -40,7 +41,7 @@ public class MdmProviderMatchR4Test extends BaseProviderR4Test { Patient createdJane = createPatient(jane); Patient newJane = buildJanePatient(); - Bundle result = (Bundle) myMdmProvider.match(newJane); + Bundle result = (Bundle) myMdmProvider.match(newJane, new SystemRequestDetails()); assertEquals(1, result.getEntry().size()); Bundle.BundleEntryComponent entry0 = result.getEntry().get(0); @@ -64,7 +65,7 @@ public class MdmProviderMatchR4Test extends BaseProviderR4Test { Medication createdMedication = createMedication(medication); Medication newMedication = buildMedication("Organization/mfr"); - Bundle result = (Bundle) myMdmProvider.serverMatch(newMedication, new StringType("Medication")); + Bundle result = (Bundle) myMdmProvider.serverMatch(newMedication, new StringType("Medication"), new SystemRequestDetails()); assertEquals(1, result.getEntry().size()); Bundle.BundleEntryComponent entry0 = result.getEntry().get(0); @@ -89,7 +90,7 @@ public class MdmProviderMatchR4Test extends BaseProviderR4Test { Patient createdJane = createPatient(jane); Patient newJane = buildJanePatient(); - Bundle result = (Bundle) myMdmProvider.serverMatch(newJane, new StringType("Patient")); + Bundle result = (Bundle) myMdmProvider.serverMatch(newJane, new StringType("Patient"), new SystemRequestDetails()); assertEquals(1, result.getEntry().size()); Bundle.BundleEntryComponent entry0 = result.getEntry().get(0); @@ -115,7 +116,7 @@ public class MdmProviderMatchR4Test extends BaseProviderR4Test { Patient newJane = buildJanePatient(); - Bundle result = (Bundle) myMdmProvider.match(newJane); + Bundle result = (Bundle) myMdmProvider.match(newJane, new SystemRequestDetails()); assertEquals(2, result.getEntry().size()); Bundle.BundleEntryComponent entry0 = result.getEntry().get(0); @@ -139,7 +140,7 @@ public class MdmProviderMatchR4Test extends BaseProviderR4Test { Patient paul = buildPaulPatient(); paul.setActive(true); - Bundle result = (Bundle) myMdmProvider.match(paul); + Bundle result = (Bundle) myMdmProvider.match(paul, new SystemRequestDetails()); assertEquals(0, result.getEntry().size()); } @@ -151,7 +152,7 @@ public class MdmProviderMatchR4Test extends BaseProviderR4Test { Patient createdJane = createPatient(jane); Patient newJane = buildJanePatient(); - Bundle result = (Bundle) myMdmProvider.match(newJane); + Bundle result = (Bundle) myMdmProvider.match(newJane, new SystemRequestDetails()); assertEquals(1, result.getEntry().size()); assertEquals(createdJane.getId(), result.getEntryFirstRep().getResource().getId()); } @@ -221,7 +222,7 @@ public class MdmProviderMatchR4Test extends BaseProviderR4Test { "}"; IBaseResource coarseResource = myFhirContext.newJsonParser().parseResource(coarsePatient); - Bundle result = (Bundle) myMdmProvider.match((Patient) coarseResource); + Bundle result = (Bundle) myMdmProvider.match((Patient) coarseResource, new SystemRequestDetails()); assertEquals(1, result.getEntry().size()); } } diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderMergeGoldenResourcesR4Test.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderMergeGoldenResourcesR4Test.java index 60e97c2449e..74ca94ea1f1 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderMergeGoldenResourcesR4Test.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderMergeGoldenResourcesR4Test.java @@ -1,7 +1,10 @@ package ca.uhn.fhir.jpa.mdm.provider; +import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.entity.MdmLink; +import ca.uhn.fhir.jpa.entity.PartitionEntity; +import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; import ca.uhn.fhir.mdm.util.MdmResourceUtil; @@ -10,15 +13,19 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.util.TerserUtil; import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.StringType; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import java.io.IOException; import java.util.List; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -40,6 +47,11 @@ public class MdmProviderMergeGoldenResourcesR4Test extends BaseProviderR4Test { myToGoldenPatientId = new StringType(myToGoldenPatient.getIdElement().getValue()); } + @AfterEach + public void after() throws IOException { + myPartitionSettings.setPartitioningEnabled(new PartitionSettings().isPartitioningEnabled()); + super.after(); + } @Test public void testMergeWithOverride() { @@ -69,7 +81,7 @@ public class MdmProviderMergeGoldenResourcesR4Test extends BaseProviderR4Test { // we do not check setActive anymore - as not all types support that assertTrue(MdmResourceUtil.isGoldenRecord(mergedSourcePatient)); - assertTrue(!MdmResourceUtil.isGoldenRecordRedirected(mergedSourcePatient)); + assertFalse(MdmResourceUtil.isGoldenRecordRedirected(mergedSourcePatient)); assertEquals(myToGoldenPatient.getIdElement(), mergedSourcePatient.getIdElement()); assertThat(mergedSourcePatient, is(sameGoldenResourceAs(myToGoldenPatient))); @@ -78,7 +90,7 @@ public class MdmProviderMergeGoldenResourcesR4Test extends BaseProviderR4Test { Patient fromSourcePatient = myPatientDao.read(myFromGoldenPatient.getIdElement().toUnqualifiedVersionless()); - assertTrue(!MdmResourceUtil.isGoldenRecord(fromSourcePatient)); + assertFalse(MdmResourceUtil.isGoldenRecord(fromSourcePatient)); assertTrue(MdmResourceUtil.isGoldenRecordRedirected(fromSourcePatient)); //TODO GGG eventually this will need to check a redirect... this is a hack which doesnt work @@ -95,6 +107,58 @@ public class MdmProviderMergeGoldenResourcesR4Test extends BaseProviderR4Test { assertEquals(link.getLinkSource(), MdmLinkSourceEnum.MANUAL); } + @Test + public void testMergeOnSamePartition() { + myPartitionSettings.setPartitioningEnabled(true); + myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1)); + RequestPartitionId requestPartitionId = RequestPartitionId.fromPartitionId(1); + Patient fromGoldenPatient = createPatientOnPartition(new Patient(), true, false, requestPartitionId); + StringType fromGoldenPatientId = new StringType(fromGoldenPatient.getIdElement().getValue()); + Patient toGoldenPatient = createPatientOnPartition(new Patient(), true, false, requestPartitionId); + StringType toGoldenPatientId = new StringType(toGoldenPatient.getIdElement().getValue()); + + Patient mergedSourcePatient = (Patient) myMdmProvider.mergeGoldenResources(fromGoldenPatientId, + toGoldenPatientId, null, myRequestDetails); + + assertTrue(MdmResourceUtil.isGoldenRecord(mergedSourcePatient)); + assertFalse(MdmResourceUtil.isGoldenRecordRedirected(mergedSourcePatient)); + + assertEquals(toGoldenPatient.getIdElement(), mergedSourcePatient.getIdElement()); + assertThat(mergedSourcePatient, is(sameGoldenResourceAs(toGoldenPatient))); + assertEquals(1, getAllRedirectedGoldenPatients().size()); + // 2 from the set-up and only one from this test should be golden resource + assertEquals(3, getAllGoldenPatients().size()); + + List links = myMdmLinkDaoSvc.findMdmLinksBySourceResource(fromGoldenPatient); + assertThat(links, hasSize(1)); + + MdmLink link = links.get(0); + assertEquals(link.getSourcePid(), fromGoldenPatient.getIdElement().toUnqualifiedVersionless().getIdPartAsLong()); + assertEquals(link.getGoldenResourcePid(), toGoldenPatient.getIdElement().toUnqualifiedVersionless().getIdPartAsLong()); + assertEquals(link.getMatchResult(), MdmMatchResultEnum.REDIRECT); + assertEquals(link.getLinkSource(), MdmLinkSourceEnum.MANUAL); + } + + @Test + public void testMergeOnDifferentPartitions() { + myPartitionSettings.setPartitioningEnabled(true); + myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1)); + RequestPartitionId requestPartitionId1 = RequestPartitionId.fromPartitionId(1); + myPartitionLookupSvc.createPartition(new PartitionEntity().setId(2).setName(PARTITION_2)); + RequestPartitionId requestPartitionId2 = RequestPartitionId.fromPartitionId(2); + Patient fromGoldenPatient = createPatientOnPartition(new Patient(), true, false, requestPartitionId1); + StringType fromGoldenPatientId = new StringType(fromGoldenPatient.getIdElement().getValue()); + Patient toGoldenPatient = createPatientOnPartition(new Patient(), true, false, requestPartitionId2); + StringType toGoldenPatientId = new StringType(toGoldenPatient.getIdElement().getValue()); + + try { + myMdmProvider.mergeGoldenResources(fromGoldenPatientId, toGoldenPatientId, null, myRequestDetails); + fail(); + } catch (InvalidRequestException e) { + assertThat(e.getMessage(), endsWith("This operation is only available for resources on the same partition.")); + } + } + @Test public void testMergeWithManualOverride() { Patient patient = TerserUtil.clone(myFhirContext, myFromGoldenPatient); @@ -109,7 +173,7 @@ public class MdmProviderMergeGoldenResourcesR4Test extends BaseProviderR4Test { assertEquals(1, getAllGoldenPatients().size()); Patient fromSourcePatient = myPatientDao.read(myFromGoldenPatient.getIdElement().toUnqualifiedVersionless()); - assertTrue(!MdmResourceUtil.isGoldenRecord(fromSourcePatient)); + assertFalse(MdmResourceUtil.isGoldenRecord(fromSourcePatient)); assertTrue(MdmResourceUtil.isGoldenRecordRedirected(fromSourcePatient)); List links = myMdmLinkDaoSvc.findMdmLinksBySourceResource(myFromGoldenPatient); diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderNotDuplicateGoldenResourceR4Test.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderNotDuplicateGoldenResourceR4Test.java new file mode 100644 index 00000000000..1a2c1c65279 --- /dev/null +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderNotDuplicateGoldenResourceR4Test.java @@ -0,0 +1,125 @@ +package ca.uhn.fhir.jpa.mdm.provider; + +import ca.uhn.fhir.interceptor.model.RequestPartitionId; +import ca.uhn.fhir.jpa.entity.MdmLink; +import ca.uhn.fhir.jpa.entity.PartitionEntity; +import ca.uhn.fhir.mdm.api.IMdmLinkSvc; +import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; +import ca.uhn.fhir.mdm.api.MdmMatchOutcome; +import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import org.hl7.fhir.r4.model.Patient; +import org.hl7.fhir.r4.model.StringType; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.io.IOException; +import java.util.List; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.endsWith; +import static org.hamcrest.Matchers.startsWith; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +public class MdmProviderNotDuplicateGoldenResourceR4Test extends BaseProviderR4Test { + @Autowired + IMdmLinkSvc myMdmLinkSvc; + private Patient myGoldenPatient; + private StringType myGoldenPatientId; + private Patient myTargetPatient; + private StringType myTargetPatientId; + + @Override + @BeforeEach + public void before() { + super.before(); + + myGoldenPatient = createGoldenPatient(); + myGoldenPatientId = new StringType(myGoldenPatient.getIdElement().getValue()); + myTargetPatient = createGoldenPatient(); + myTargetPatientId = new StringType(myTargetPatient.getIdElement().getValue()); + } + + @AfterEach + public void after() throws IOException { + myPartitionSettings.setPartitioningEnabled(false); + super.after(); + } + + @Test + public void testNotDuplicateGoldenResource() { + myMdmLinkSvc.updateLink(myGoldenPatient, myTargetPatient, MdmMatchOutcome.POSSIBLE_DUPLICATE, MdmLinkSourceEnum.AUTO, createContextForCreate("Patient")); + assertLinkCount(1); + myMdmProvider.notDuplicate(myGoldenPatientId, myTargetPatientId, myRequestDetails); + assertLinkCount(1); + + List links = myMdmLinkDaoSvc.findMdmLinksBySourceResource(myTargetPatient); + assertEquals(MdmLinkSourceEnum.MANUAL, links.get(0).getLinkSource()); + assertEquals(MdmMatchResultEnum.NO_MATCH, links.get(0).getMatchResult()); + } + + @Test + public void testNotDuplicateGoldenResourceNoLinkBetweenResources() { + try { + myMdmProvider.notDuplicate(myGoldenPatientId, myTargetPatientId, myRequestDetails); + fail(); + } catch (InvalidRequestException e) { + assertThat(e.getMessage(), startsWith("HAPI-0745: No link exists between")); + } + } + + @Test + public void testNotDuplicateGoldenResourceNotPossibleDuplicate() { + myMdmLinkSvc.updateLink(myGoldenPatient, myTargetPatient, MdmMatchOutcome.POSSIBLE_MATCH, MdmLinkSourceEnum.AUTO, createContextForCreate("Patient")); + assertLinkCount(1); + try { + myMdmProvider.notDuplicate(myGoldenPatientId, myTargetPatientId, myRequestDetails); + fail(); + } catch (InvalidRequestException e) { + assertThat(e.getMessage(), endsWith("are not linked as POSSIBLE_DUPLICATE.")); + } + } + + @Test + public void testNotDuplicateGoldenResourceOnSamePartition() { + myPartitionSettings.setPartitioningEnabled(true); + myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1)); + RequestPartitionId requestPartitionId = RequestPartitionId.fromPartitionId(1); + Patient goldenPatient = createPatientOnPartition(new Patient(), true, false, requestPartitionId); + StringType goldenPatientId = new StringType(goldenPatient.getIdElement().getValue()); + Patient targetPatient = createPatientOnPartition(new Patient(), true, false, requestPartitionId); + StringType targetPatientId = new StringType(targetPatient.getIdElement().getValue()); + + myMdmLinkSvc.updateLink(goldenPatient, targetPatient, MdmMatchOutcome.POSSIBLE_DUPLICATE, MdmLinkSourceEnum.AUTO, createContextForCreate("Patient")); + assertLinkCount(1); + myMdmProvider.notDuplicate(goldenPatientId, targetPatientId, myRequestDetails); + assertLinkCount(1); + + List links = myMdmLinkDaoSvc.findMdmLinksBySourceResource(targetPatient); + assertEquals(MdmLinkSourceEnum.MANUAL, links.get(0).getLinkSource()); + assertEquals(MdmMatchResultEnum.NO_MATCH, links.get(0).getMatchResult()); + } + + @Test + public void testNotDuplicateGoldenResourceOnDifferentPartitions() { + myPartitionSettings.setPartitioningEnabled(true); + myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1)); + RequestPartitionId requestPartitionId1 = RequestPartitionId.fromPartitionId(1); + myPartitionLookupSvc.createPartition(new PartitionEntity().setId(2).setName(PARTITION_2)); + RequestPartitionId requestPartitionId2 = RequestPartitionId.fromPartitionId(2); + Patient goldenPatient = createPatientOnPartition(new Patient(), true, false, requestPartitionId1); + StringType goldenPatientId = new StringType(goldenPatient.getIdElement().getValue()); + Patient targetPatient = createPatientOnPartition(new Patient(), true, false, requestPartitionId2); + StringType targetPatientId = new StringType(targetPatient.getIdElement().getValue()); + + try { + myMdmProvider.notDuplicate(goldenPatientId, targetPatientId, myRequestDetails); + fail(); + } catch (InvalidRequestException e) { + assertThat(e.getMessage(), startsWith("HAPI-0745: No link exists between")); + } + } +} diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderUpdateLinkR4Test.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderUpdateLinkR4Test.java index d8496638cb4..91810fa20ec 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderUpdateLinkR4Test.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderUpdateLinkR4Test.java @@ -1,7 +1,9 @@ package ca.uhn.fhir.jpa.mdm.provider; +import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.entity.MdmLink; +import ca.uhn.fhir.jpa.entity.PartitionEntity; import ca.uhn.fhir.mdm.api.MdmConstants; import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; @@ -54,6 +56,52 @@ public class MdmProviderUpdateLinkR4Test extends BaseLinkR4Test { assertEquals(MdmMatchResultEnum.MATCH, links.get(0).getMatchResult()); } + @Test + public void testUpdateLinkMatchOnSamePartition() { + myPartitionSettings.setPartitioningEnabled(true); + myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1)); + RequestPartitionId requestPartitionId = RequestPartitionId.fromPartitionId(1); + Patient patient = createPatientAndUpdateLinksOnPartition(buildFrankPatient(), requestPartitionId); + StringType patientId = new StringType(patient.getIdElement().getValue()); + + Patient sourcePatient = getGoldenResourceFromTargetResource(patient); + StringType sourcePatientId = new StringType(sourcePatient.getIdElement().getValue()); + MdmLink link = myMdmLinkDaoSvc.findMdmLinkBySource(patient).get(); + link.setMatchResult(MdmMatchResultEnum.POSSIBLE_MATCH); + saveLink(link); + assertEquals(MdmLinkSourceEnum.AUTO, link.getLinkSource()); + assertLinkCount(2); + myMdmProvider.updateLink(sourcePatientId, patientId, MATCH_RESULT, myRequestDetails); + assertLinkCount(2); + + List links = myMdmLinkDaoSvc.findMdmLinksBySourceResource(patient); + assertEquals(MdmLinkSourceEnum.MANUAL, links.get(0).getLinkSource()); + assertEquals(MdmMatchResultEnum.MATCH, links.get(0).getMatchResult()); + assertNotNull(links.get(0).getPartitionId()); + assertEquals(1, links.get(0).getPartitionId().getPartitionId()); + } + + @Test + public void testUpdateLinkMatchOnDifferentPartitions() { + myPartitionSettings.setPartitioningEnabled(true); + myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1)); + myPartitionLookupSvc.createPartition(new PartitionEntity().setId(2).setName(PARTITION_2)); + RequestPartitionId requestPartitionId1 = RequestPartitionId.fromPartitionId(1); + RequestPartitionId requestPartitionId2 = RequestPartitionId.fromPartitionId(2); + Patient patient = createPatientOnPartition(buildFrankPatient(), true, false, requestPartitionId1); + StringType patientId = new StringType(patient.getIdElement().getValue()); + + Patient sourcePatient = createPatientOnPartition(buildJanePatient(), true, false, requestPartitionId2); + StringType sourcePatientId = new StringType(sourcePatient.getIdElement().getValue()); + assertLinkCount(1); + try { + myMdmProvider.updateLink(sourcePatientId, patientId, MATCH_RESULT, myRequestDetails); + fail(); + } catch (InvalidRequestException e) { + assertThat(e.getMessage(), endsWith("This operation is only available for resources on the same partition.")); + } + } + @Test public void testUpdateLinkTwiceFailsDueToWrongVersion() { myMdmProvider.updateLink(mySourcePatientId, myPatientId, MATCH_RESULT, myRequestDetails); diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmBatchSvcImplIT.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmBatchSvcImplIT.java index 4486cb3c389..9eef716d031 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmBatchSvcImplIT.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmBatchSvcImplIT.java @@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.mdm.svc; import ca.uhn.fhir.interceptor.api.IInterceptorService; import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test; +import ca.uhn.fhir.jpa.partition.SystemRequestDetails; import ca.uhn.fhir.mdm.api.IMdmSubmitSvc; import ca.uhn.test.concurrency.PointcutLatch; import org.apache.commons.lang3.time.DateUtils; @@ -56,7 +57,7 @@ class MdmBatchSvcImplIT extends BaseMdmR4Test { assertLinkCount(0); //SUT - afterMdmLatch.runWithExpectedCount(30, () -> myMdmSubmitSvc.submitAllSourceTypesToMdm(null)); + afterMdmLatch.runWithExpectedCount(30, () -> myMdmSubmitSvc.submitAllSourceTypesToMdm(null, SystemRequestDetails.forAllPartitions())); assertLinkCount(30); } @@ -72,7 +73,7 @@ class MdmBatchSvcImplIT extends BaseMdmR4Test { //SUT myMdmSubmitSvc.setBufferSize(5); - afterMdmLatch.runWithExpectedCount(10, () -> myMdmSubmitSvc.submitSourceResourceTypeToMdm("Patient", null)); + afterMdmLatch.runWithExpectedCount(10, () -> myMdmSubmitSvc.submitSourceResourceTypeToMdm("Patient", null, SystemRequestDetails.newSystemRequestAllPartitions())); assertLinkCount(10); } @@ -89,7 +90,7 @@ class MdmBatchSvcImplIT extends BaseMdmR4Test { assertLinkCount(0); //SUT - afterMdmLatch.runWithExpectedCount(10, () -> myMdmSubmitSvc.submitSourceResourceTypeToMdm("Medication", null)); + afterMdmLatch.runWithExpectedCount(10, () -> myMdmSubmitSvc.submitSourceResourceTypeToMdm("Medication", null, SystemRequestDetails.newSystemRequestAllPartitions())); assertLinkCount(10); } @@ -104,7 +105,7 @@ class MdmBatchSvcImplIT extends BaseMdmR4Test { assertLinkCount(0); //SUT - afterMdmLatch.runWithExpectedCount(10, () -> myMdmSubmitSvc.submitAllSourceTypesToMdm(null)); + afterMdmLatch.runWithExpectedCount(10, () -> myMdmSubmitSvc.submitAllSourceTypesToMdm(null, SystemRequestDetails.newSystemRequestAllPartitions())); assertLinkCount(10); } @@ -117,7 +118,7 @@ class MdmBatchSvcImplIT extends BaseMdmR4Test { assertLinkCount(0); //SUT - afterMdmLatch.runWithExpectedCount(1, () -> myMdmSubmitSvc.submitSourceResourceTypeToMdm("Patient", "Patient?name=gary")); + afterMdmLatch.runWithExpectedCount(1, () -> myMdmSubmitSvc.submitSourceResourceTypeToMdm("Patient", "Patient?name=gary", SystemRequestDetails.newSystemRequestAllPartitions())); assertLinkCount(1); } diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmCandidateSearchSvcIT.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmCandidateSearchSvcIT.java index 318c427a365..da1f30f0bea 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmCandidateSearchSvcIT.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmCandidateSearchSvcIT.java @@ -1,5 +1,6 @@ package ca.uhn.fhir.jpa.mdm.svc; +import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test; import ca.uhn.fhir.jpa.mdm.svc.candidate.MdmCandidateSearchSvc; import ca.uhn.fhir.jpa.mdm.svc.candidate.TooManyCandidatesException; @@ -38,7 +39,7 @@ public class MdmCandidateSearchSvcIT extends BaseMdmR4Test { createActivePatient(); Patient newJane = buildJanePatient(); - Collection result = myMdmCandidateSearchSvc.findCandidates("Patient", newJane); + Collection result = myMdmCandidateSearchSvc.findCandidates("Patient", newJane, RequestPartitionId.allPartitions()); assertEquals(1, result.size()); } @@ -53,7 +54,7 @@ public class MdmCandidateSearchSvcIT extends BaseMdmR4Test { Patient newJane = buildJaneWithBirthday(today); - Collection result = myMdmCandidateSearchSvc.findCandidates("Patient", newJane); + Collection result = myMdmCandidateSearchSvc.findCandidates("Patient", newJane, RequestPartitionId.allPartitions()); assertEquals(1, result.size()); } @@ -71,7 +72,7 @@ public class MdmCandidateSearchSvcIT extends BaseMdmR4Test { incomingPatient.setActive(true); incomingPatient.setGeneralPractitioner(Collections.singletonList(new Reference(practitionerAndUpdateLinks.getId()))); - Collection patient = myMdmCandidateSearchSvc.findCandidates("Patient", incomingPatient); + Collection patient = myMdmCandidateSearchSvc.findCandidates("Patient", incomingPatient, RequestPartitionId.allPartitions()); assertThat(patient, hasSize(1)); } @@ -82,13 +83,13 @@ public class MdmCandidateSearchSvcIT extends BaseMdmR4Test { Patient newJane = buildJanePatient(); createActivePatient(); - assertEquals(1, runInTransaction(()->myMdmCandidateSearchSvc.findCandidates("Patient", newJane).size())); + assertEquals(1, runInTransaction(()->myMdmCandidateSearchSvc.findCandidates("Patient", newJane, RequestPartitionId.allPartitions()).size())); createActivePatient(); - assertEquals(2, runInTransaction(()->myMdmCandidateSearchSvc.findCandidates("Patient", newJane).size())); + assertEquals(2, runInTransaction(()->myMdmCandidateSearchSvc.findCandidates("Patient", newJane, RequestPartitionId.allPartitions()).size())); try { createActivePatient(); - myMdmCandidateSearchSvc.findCandidates("Patient", newJane); + myMdmCandidateSearchSvc.findCandidates("Patient", newJane, RequestPartitionId.allPartitions()); fail(); } catch (TooManyCandidatesException e) { assertEquals("More than 3 candidate matches found for Patient?identifier=http%3A%2F%2Fa.tv%2F%7CID.JANE.123&active=true. Aborting mdm matching.", e.getMessage()); diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmControllerSvcImplTest.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmControllerSvcImplTest.java new file mode 100644 index 00000000000..7835331bbaf --- /dev/null +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmControllerSvcImplTest.java @@ -0,0 +1,158 @@ +package ca.uhn.fhir.jpa.mdm.svc; + +import ca.uhn.fhir.interceptor.api.IInterceptorService; +import ca.uhn.fhir.interceptor.model.RequestPartitionId; +import ca.uhn.fhir.jpa.entity.MdmLink; +import ca.uhn.fhir.jpa.entity.PartitionEntity; +import ca.uhn.fhir.jpa.mdm.provider.BaseLinkR4Test; +import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc; +import ca.uhn.fhir.jpa.partition.SystemRequestDetails; +import ca.uhn.fhir.mdm.api.IMdmControllerSvc; +import ca.uhn.fhir.mdm.api.MdmLinkJson; +import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; +import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; +import ca.uhn.fhir.mdm.api.paging.MdmPageRequest; +import ca.uhn.fhir.mdm.model.MdmTransactionContext; +import ca.uhn.fhir.rest.server.interceptor.partition.RequestTenantPartitionInterceptor; +import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; +import ca.uhn.fhir.test.utilities.BatchJobHelper; +import org.hl7.fhir.instance.model.api.IBaseParameters; +import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.hl7.fhir.r4.model.DecimalType; +import org.hl7.fhir.r4.model.Parameters; +import org.hl7.fhir.r4.model.Patient; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentMatcher; +import org.mockito.Mockito; +import org.springframework.batch.core.ExitStatus; +import org.springframework.batch.core.JobExecution; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.data.domain.Page; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +import static ca.uhn.fhir.mdm.provider.MdmProviderDstu3Plus.DEFAULT_PAGE_SIZE; +import static ca.uhn.fhir.mdm.provider.MdmProviderDstu3Plus.MAX_PAGE_SIZE; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; + +public class MdmControllerSvcImplTest extends BaseLinkR4Test { + @Autowired + IMdmControllerSvc myMdmControllerSvc; + + @SpyBean + @Autowired + IRequestPartitionHelperSvc myRequestPartitionHelperSvc; + + @Autowired + private IInterceptorService myInterceptorService; + @Autowired + private BatchJobHelper myBatchJobHelper; + + @BeforeEach + public void before() { + super.before(); + myPartitionSettings.setPartitioningEnabled(true); + myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1)); + myPartitionLookupSvc.createPartition(new PartitionEntity().setId(2).setName(PARTITION_2)); + myInterceptorService.registerInterceptor(new RequestTenantPartitionInterceptor()); + } + + @Test + public void testSurvivorshipIsCalledOnMatchingToTheSameGoldenResource() { + assertLinkCount(1); + + RequestPartitionId requestPartitionId = RequestPartitionId.fromPartitionId(1); + + Patient patient = createPatientAndUpdateLinksOnPartition(buildFrankPatient(), requestPartitionId); + + getGoldenResourceFromTargetResource(patient); + + MdmLink link = myMdmLinkDaoSvc.findMdmLinkBySource(patient).get(); + link.setMatchResult(MdmMatchResultEnum.POSSIBLE_MATCH); + saveLink(link); + assertEquals(MdmLinkSourceEnum.AUTO, link.getLinkSource()); + assertLinkCount(2); + + Page resultPage = myMdmControllerSvc.queryLinks(null, myPatientId.getIdElement().getValue(), null, null, + new MdmTransactionContext(MdmTransactionContext.OperationType.QUERY_LINKS), + new MdmPageRequest((Integer) null, null, DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE), + new SystemRequestDetails().setRequestPartitionId(RequestPartitionId.fromPartitionId(1))); + + assertEquals(resultPage.getContent().size(), 1); + + assertEquals(resultPage.getContent().get(0).getSourceId(), patient.getIdElement().getResourceType() + "/" + patient.getIdElement().getIdPart()); + + Mockito.verify(myRequestPartitionHelperSvc, Mockito.atLeastOnce()).validateHasPartitionPermissions(any(), eq("Patient"), argThat(new PartitionIdMatcher(requestPartitionId))); + } + + @Test + public void testMdmDuplicateGoldenResource() { + assertLinkCount(1); + + RequestPartitionId requestPartitionId = RequestPartitionId.fromPartitionId(1); + + Patient patient = createPatientAndUpdateLinksOnPartition(buildFrankPatient(), requestPartitionId); + + getGoldenResourceFromTargetResource(patient); + + MdmLink link = myMdmLinkDaoSvc.findMdmLinkBySource(patient).get(); + link.setMatchResult(MdmMatchResultEnum.POSSIBLE_DUPLICATE); + saveLink(link); + assertEquals(MdmLinkSourceEnum.AUTO, link.getLinkSource()); + assertLinkCount(2); + + Page resultPage = myMdmControllerSvc.getDuplicateGoldenResources(null, + new MdmPageRequest((Integer) null, null, DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE), + new SystemRequestDetails().setRequestPartitionId(RequestPartitionId.fromPartitionId(1))); + + assertEquals(resultPage.getContent().size(), 1); + + assertEquals(resultPage.getContent().get(0).getSourceId(), patient.getIdElement().getResourceType() + "/" + patient.getIdElement().getIdPart()); + + Mockito.verify(myRequestPartitionHelperSvc, Mockito.atLeastOnce()).validateHasPartitionPermissions(any(), eq("Patient"), argThat(new PartitionIdMatcher(requestPartitionId))); + } + + @Test + public void testMdmClearWithProvidedResources() { + assertLinkCount(1); + + RequestPartitionId requestPartitionId1 = RequestPartitionId.fromPartitionId(1); + RequestPartitionId requestPartitionId2 = RequestPartitionId.fromPartitionId(2); + createPractitionerAndUpdateLinksOnPartition(buildJanePractitioner(), requestPartitionId1); + createPractitionerAndUpdateLinksOnPartition(buildJanePractitioner(), requestPartitionId2); + assertLinkCount(3); + + List urls = new ArrayList<>(); + urls.add("Practitioner?"); + IPrimitiveType batchSize = new DecimalType(new BigDecimal(100)); + ServletRequestDetails details = new ServletRequestDetails(); + details.setTenantId(PARTITION_2); + IBaseParameters clearJob = myMdmControllerSvc.submitMdmClearJob(urls, batchSize, details); + Long jobId = Long.valueOf(((DecimalType) ((Parameters) clearJob).getParameter("jobId")).getValueAsString()); + JobExecution jobExecution = myBatchJobHelper.awaitJobExecution(jobId); + assertEquals(ExitStatus.COMPLETED, jobExecution.getExitStatus()); + + assertLinkCount(2); + } + + private class PartitionIdMatcher implements ArgumentMatcher { + private RequestPartitionId myRequestPartitionId; + + PartitionIdMatcher(RequestPartitionId theRequestPartitionId) { + myRequestPartitionId = theRequestPartitionId; + } + + @Override + public boolean matches(RequestPartitionId theRequestPartitionId) { + return myRequestPartitionId.getPartitionIds().equals(theRequestPartitionId.getPartitionIds()); + } + } + +} diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkSvcTest.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkSvcTest.java index 629ae40b2f1..3c3c63e3d0d 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkSvcTest.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkSvcTest.java @@ -1,5 +1,6 @@ package ca.uhn.fhir.jpa.mdm.svc; +import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.dao.expunge.ExpungeEverythingService; import ca.uhn.fhir.jpa.entity.MdmLink; @@ -8,6 +9,7 @@ import ca.uhn.fhir.mdm.api.IMdmLinkSvc; import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; import ca.uhn.fhir.mdm.api.MdmMatchOutcome; import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; +import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import org.hamcrest.Matchers; import org.hl7.fhir.r4.model.IdType; @@ -182,4 +184,19 @@ public class MdmLinkSvcTest extends BaseMdmR4Test { assertThat(actual, Matchers.containsInAnyOrder(expected.toArray())); } + + @Test + public void testMdmLinksHasPartitionIdForResourceOnNonDefaultPartition() { + Patient goldenPatient = createGoldenPatient(buildJanePatient()); + Patient patient1 = createPatient(buildJanePatient()); + RequestPartitionId requestPartitionId = RequestPartitionId.fromPartitionId(1); + patient1.setUserData(Constants.RESOURCE_PARTITION_ID, requestPartitionId); + assertEquals(0, myMdmLinkDao.count()); + + myMdmLinkDaoSvc.createOrUpdateLinkEntity(goldenPatient, patient1, MdmMatchOutcome.NEW_GOLDEN_RESOURCE_MATCH, MdmLinkSourceEnum.MANUAL, createContextForCreate("Patient")); + List targets = myMdmLinkDaoSvc.findMdmLinksByGoldenResource(goldenPatient); + assertFalse(targets.isEmpty()); + assertEquals(1, targets.size()); + assertEquals(requestPartitionId.getFirstPartitionIdOrNull(), targets.get(0).getPartitionId().getPartitionId()); + } } diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvcSurvivorshipTest.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvcSurvivorshipTest.java index 2a1d563d085..6131d911bbd 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvcSurvivorshipTest.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvcSurvivorshipTest.java @@ -1,19 +1,10 @@ package ca.uhn.fhir.jpa.mdm.svc; -import ca.uhn.fhir.jpa.entity.MdmLink; import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test; -import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.mdm.api.IMdmLinkSvc; import ca.uhn.fhir.mdm.api.IMdmSurvivorshipService; -import ca.uhn.fhir.mdm.api.MdmConstants; -import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; -import ca.uhn.fhir.mdm.api.MdmMatchOutcome; import ca.uhn.fhir.mdm.model.MdmTransactionContext; import ca.uhn.fhir.mdm.util.GoldenResourceHelper; -import ca.uhn.fhir.rest.api.server.IBundleProvider; -import ca.uhn.fhir.rest.param.TokenParam; -import org.hl7.fhir.instance.model.api.IAnyResource; -import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Patient; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; @@ -23,11 +14,6 @@ import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.mock.mockito.SpyBean; -import javax.annotation.Nullable; - -import static ca.uhn.fhir.mdm.api.MdmMatchResultEnum.*; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; import static org.mockito.Mockito.times; import static org.slf4j.LoggerFactory.getLogger; diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmResourceDaoSvcTest.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmResourceDaoSvcTest.java index 4dbac482c9b..56601cb5b10 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmResourceDaoSvcTest.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmResourceDaoSvcTest.java @@ -1,16 +1,23 @@ package ca.uhn.fhir.jpa.mdm.svc; +import ca.uhn.fhir.interceptor.model.RequestPartitionId; +import ca.uhn.fhir.jpa.entity.PartitionEntity; import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test; +import ca.uhn.fhir.jpa.model.config.PartitionSettings; +import ca.uhn.fhir.jpa.partition.SystemRequestDetails; import ca.uhn.fhir.mdm.util.MdmResourceUtil; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.r4.model.Patient; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import java.io.IOException; import java.util.Optional; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; public class MdmResourceDaoSvcTest extends BaseMdmR4Test { @@ -18,6 +25,12 @@ public class MdmResourceDaoSvcTest extends BaseMdmR4Test { @Autowired MdmResourceDaoSvc myResourceDaoSvc; + @AfterEach + public void after() throws IOException { + myPartitionSettings.setPartitioningEnabled(new PartitionSettings().isPartitioningEnabled()); + super.after(); + } + @Test public void testSearchPatientByEidExcludesNonGoldenPatients() { Patient goodSourcePatient = addExternalEID(createGoldenPatient(), TEST_EID); @@ -35,7 +48,7 @@ public class MdmResourceDaoSvcTest extends BaseMdmR4Test { } @Test - public void testSearcGoldenResourceByEidExcludesNonMdmManaged() { + public void testSearchGoldenResourceByEidExcludesNonMdmManaged() { Patient goodSourcePatient = addExternalEID(createGoldenPatient(), TEST_EID); myPatientDao.update(goodSourcePatient); @@ -46,4 +59,37 @@ public class MdmResourceDaoSvcTest extends BaseMdmR4Test { assertTrue(foundSourcePatient.isPresent()); assertThat(foundSourcePatient.get().getIdElement().toUnqualifiedVersionless().getValue(), is(goodSourcePatient.getIdElement().toUnqualifiedVersionless().getValue())); } + + @Test + public void testSearchGoldenResourceOnSamePartition() { + myPartitionSettings.setPartitioningEnabled(true); + myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1)); + RequestPartitionId requestPartitionId = RequestPartitionId.fromPartitionId(1); + Patient patientOnPartition = createPatientOnPartition(new Patient(), true, false, requestPartitionId); + Patient goodSourcePatient = addExternalEID(patientOnPartition, TEST_EID); + SystemRequestDetails systemRequestDetails = new SystemRequestDetails(); + systemRequestDetails.setRequestPartitionId(requestPartitionId); + myPatientDao.update(goodSourcePatient, systemRequestDetails); + + Optional foundSourcePatient = myResourceDaoSvc.searchGoldenResourceByEID(TEST_EID, "Patient", requestPartitionId); + assertTrue(foundSourcePatient.isPresent()); + assertThat(foundSourcePatient.get().getIdElement().toUnqualifiedVersionless().getValue(), is(goodSourcePatient.getIdElement().toUnqualifiedVersionless().getValue())); + } + + @Test + public void testSearchGoldenResourceOnDifferentPartitions() { + myPartitionSettings.setPartitioningEnabled(true); + myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1)); + RequestPartitionId requestPartitionId1 = RequestPartitionId.fromPartitionId(1); + myPartitionLookupSvc.createPartition(new PartitionEntity().setId(2).setName(PARTITION_2)); + RequestPartitionId requestPartitionId2 = RequestPartitionId.fromPartitionId(2); + Patient patientOnPartition = createPatientOnPartition(new Patient(), true, false, requestPartitionId1); + Patient goodSourcePatient = addExternalEID(patientOnPartition, TEST_EID); + SystemRequestDetails systemRequestDetails = new SystemRequestDetails(); + systemRequestDetails.setRequestPartitionId(requestPartitionId1); + myPatientDao.update(goodSourcePatient, systemRequestDetails); + + Optional foundSourcePatient = myResourceDaoSvc.searchGoldenResourceByEID(TEST_EID, "Patient", requestPartitionId2); + assertFalse(foundSourcePatient.isPresent()); + } } diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/candidate/CandidateSearcherTest.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/candidate/CandidateSearcherTest.java index 18befab3b40..db4089cb895 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/candidate/CandidateSearcherTest.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/candidate/CandidateSearcherTest.java @@ -20,6 +20,8 @@ import java.util.Optional; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -54,7 +56,7 @@ class CandidateSearcherTest { SimpleBundleProvider bundleProvider = new SimpleBundleProvider(); bundleProvider.setSize(candidateSearchLimit + offset); - when(dao.search(map)).thenReturn(bundleProvider); + when(dao.search(eq(map), any())).thenReturn(bundleProvider); Optional result = myCandidateSearcher.search(resourceType, criteria); diff --git a/hapi-fhir-jpaserver-model/pom.xml b/hapi-fhir-jpaserver-model/pom.xml index 428843dce66..1af24e1adad 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 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-searchparam/pom.xml b/hapi-fhir-jpaserver-searchparam/pom.xml index 663b59cb9a6..fd7db5ee903 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 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-subscription/pom.xml b/hapi-fhir-jpaserver-subscription/pom.xml index e954cd1363e..3b9c200a3a8 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 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/config/SubscriptionProcessorConfig.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/config/SubscriptionProcessorConfig.java index d1fbd8025e2..3ef04335bb1 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/config/SubscriptionProcessorConfig.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/config/SubscriptionProcessorConfig.java @@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.subscription.match.config; * #L% */ +import ca.uhn.fhir.jpa.subscription.channel.api.IChannelFactory; import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionChannelRegistry; import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionDeliveryChannelNamer; import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionDeliveryHandlerFactory; @@ -38,6 +39,7 @@ import ca.uhn.fhir.jpa.subscription.match.matcher.subscriber.SubscriptionRegiste import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionLoader; import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionRegistry; import ca.uhn.fhir.jpa.subscription.model.config.SubscriptionModelConfig; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Primary; @@ -103,8 +105,8 @@ public class SubscriptionProcessorConfig { @Bean @Scope("prototype") - public SubscriptionDeliveringMessageSubscriber subscriptionDeliveringMessageSubscriber() { - return new SubscriptionDeliveringMessageSubscriber(); + public SubscriptionDeliveringMessageSubscriber subscriptionDeliveringMessageSubscriber(IChannelFactory theChannelFactory) { + return new SubscriptionDeliveringMessageSubscriber(theChannelFactory); } @Bean diff --git a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/message/SubscriptionDeliveringMessageSubscriber.java b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/message/SubscriptionDeliveringMessageSubscriber.java index 8f5f575c9a0..e514681d5ec 100644 --- a/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/message/SubscriptionDeliveringMessageSubscriber.java +++ b/hapi-fhir-jpaserver-subscription/src/main/java/ca/uhn/fhir/jpa/subscription/match/deliver/message/SubscriptionDeliveringMessageSubscriber.java @@ -32,6 +32,7 @@ import ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryMessage; import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedJsonMessage; import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage; import ca.uhn.fhir.rest.api.EncodingEnum; +import com.google.common.annotations.VisibleForTesting; import org.hl7.fhir.instance.model.api.IBaseResource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -44,16 +45,16 @@ import java.net.URISyntaxException; @Scope("prototype") public class SubscriptionDeliveringMessageSubscriber extends BaseSubscriptionDeliverySubscriber { - private static Logger ourLog = LoggerFactory.getLogger(SubscriptionDeliveringMessageSubscriber.class); + private static final Logger ourLog = LoggerFactory.getLogger(SubscriptionDeliveringMessageSubscriber.class); - @Autowired - private IChannelFactory myChannelFactory; + private final IChannelFactory myChannelFactory; /** * Constructor */ - public SubscriptionDeliveringMessageSubscriber() { + public SubscriptionDeliveringMessageSubscriber(IChannelFactory theChannelFactory) { super(); + myChannelFactory = theChannelFactory; } protected void deliverPayload(ResourceDeliveryMessage theMsg, CanonicalSubscription theSubscription, IChannelProducer theChannelProducer) { @@ -67,6 +68,7 @@ public class SubscriptionDeliveringMessageSubscriber extends BaseSubscriptionDel ResourceModifiedMessage payload = new ResourceModifiedMessage(myFhirContext, thePayloadResource, theMsg.getOperationType()); payload.setMessageKey(theMsg.getMessageKeyOrNull()); payload.setTransactionId(theMsg.getTransactionId()); + payload.setPartitionId(theMsg.getRequestPartitionId()); ResourceModifiedJsonMessage message = new ResourceModifiedJsonMessage(payload); theChannelProducer.send(message); ourLog.debug("Delivering {} message payload {} for {}", theMsg.getOperationType(), theMsg.getPayloadId(), theSubscription.getIdElement(myFhirContext).toUnqualifiedVersionless().getValue()); 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 3d152f9e650..445022a6a66 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 @@ -118,13 +118,13 @@ public class SubscriptionActivatingSubscriber extends BaseSubscriberForSubscript @SuppressWarnings("unchecked") private boolean activateSubscription(final IBaseResource theSubscription) { IFhirResourceDao subscriptionDao = myDaoRegistry.getSubscriptionDao(); - SystemRequestDetails srd = SystemRequestDetails.forAllPartition(); + SystemRequestDetails srd = SystemRequestDetails.forAllPartitions(); IBaseResource subscription = null; try { // read can throw ResourceGoneException // if this happens, we will treat this as a failure to activate - subscription = subscriptionDao.read(theSubscription.getIdElement(), SystemRequestDetails.forAllPartition()); + subscription = subscriptionDao.read(theSubscription.getIdElement(), SystemRequestDetails.forAllPartitions()); subscription.setId(subscription.getIdElement().toVersionless()); ourLog.info("Activating subscription {} from status {} to {}", subscription.getIdElement().toUnqualified().getValue(), SubscriptionConstants.REQUESTED_STATUS, SubscriptionConstants.ACTIVE_STATUS); 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 4d7ffed2cef..3b8089b2170 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 @@ -90,7 +90,7 @@ public class SubscriptionLoader implements IResourceChangeListener { @PostConstruct public void registerListener() { mySearchParameterMap = getSearchParameterMap(); - mySystemRequestDetails = SystemRequestDetails.forAllPartition(); + mySystemRequestDetails = SystemRequestDetails.forAllPartitions(); IResourceChangeListenerCache subscriptionCache = myResourceChangeListenerRegistry.registerResourceResourceChangeListener("Subscription", mySearchParameterMap, this, REFRESH_INTERVAL); subscriptionCache.forceRefresh(); @@ -252,7 +252,7 @@ public class SubscriptionLoader implements IResourceChangeListener { return; } IFhirResourceDao subscriptionDao = getSubscriptionDao(); - SystemRequestDetails systemRequestDetails = SystemRequestDetails.forAllPartition(); + SystemRequestDetails systemRequestDetails = SystemRequestDetails.forAllPartitions(); List resourceList = theResourceIds.stream().map(n -> subscriptionDao.read(n, systemRequestDetails)).collect(Collectors.toList()); updateSubscriptionRegistry(resourceList); } 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 30b44d0cd51..ff489d29d97 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 @@ -116,7 +116,7 @@ public class SubscriptionTriggeringSvcImpl implements ISubscriptionTriggeringSvc if (!subscriptionId.hasResourceType()) { subscriptionId = subscriptionId.withResourceType(ResourceTypeEnum.SUBSCRIPTION.getCode()); } - subscriptionDao.read(subscriptionId, SystemRequestDetails.forAllPartition()); + subscriptionDao.read(subscriptionId, SystemRequestDetails.forAllPartitions()); } List> resourceIds = ObjectUtils.defaultIfNull(theResourceIds, Collections.emptyList()); @@ -300,7 +300,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, SystemRequestDetails.forAllPartition()); + IBaseResource resourceToTrigger = dao.read(resourceId, SystemRequestDetails.forAllPartitions()); 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 7610e9ca0a4..426f07c91a9 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 @@ -5,11 +5,15 @@ import ca.uhn.fhir.i18n.Msg; 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.channel.api.IChannelFactory; +import ca.uhn.fhir.jpa.subscription.channel.api.IChannelProducer; +import ca.uhn.fhir.jpa.subscription.match.deliver.message.SubscriptionDeliveringMessageSubscriber; 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; import ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryJsonMessage; import ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryMessage; +import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedJsonMessage; import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage; import ca.uhn.fhir.rest.api.EncodingEnum; import ca.uhn.fhir.rest.client.api.IGenericClient; @@ -23,6 +27,7 @@ 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.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.slf4j.Logger; @@ -31,7 +36,9 @@ import org.springframework.messaging.Message; import org.springframework.messaging.MessagingException; import org.springframework.messaging.support.GenericMessage; +import java.net.URISyntaxException; import java.time.LocalDate; +import java.util.List; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -49,12 +56,18 @@ public class BaseSubscriptionDeliverySubscriberTest { private static final Logger ourLog = LoggerFactory.getLogger(BaseSubscriptionDeliverySubscriberTest.class); private SubscriptionDeliveringRestHookSubscriber mySubscriber; + private SubscriptionDeliveringMessageSubscriber myMessageSubscriber; private final FhirContext myCtx = FhirContext.forR4(); @Mock private IInterceptorBroadcaster myInterceptorBroadcaster; @Mock protected SubscriptionRegistry mySubscriptionRegistry; + @Mock + private IChannelFactory myChannelFactory; + @Mock + private IChannelProducer myChannelProducer; + @Mock(answer = Answers.RETURNS_DEEP_STUBS) private IRestfulClientFactory myRestfulClientFactory; @Mock(answer = Answers.RETURNS_DEEP_STUBS) @@ -67,6 +80,11 @@ public class BaseSubscriptionDeliverySubscriberTest { mySubscriber.setInterceptorBroadcasterForUnitTest(myInterceptorBroadcaster); mySubscriber.setSubscriptionRegistryForUnitTest(mySubscriptionRegistry); + myMessageSubscriber = new SubscriptionDeliveringMessageSubscriber(myChannelFactory); + myMessageSubscriber.setFhirContextForUnitTest(myCtx); + myMessageSubscriber.setInterceptorBroadcasterForUnitTest(myInterceptorBroadcaster); + myMessageSubscriber.setSubscriptionRegistryForUnitTest(mySubscriptionRegistry); + myCtx.setRestfulClientFactory(myRestfulClientFactory); when(myRestfulClientFactory.newGenericClient(any())).thenReturn(myGenericClient); } @@ -194,7 +212,6 @@ public class BaseSubscriptionDeliverySubscriberTest { ourLog.info(jsonString); - // Assert that the partitionID is being serialized in JSON assertThat(jsonString, containsString("\"partitionDate\":[2020,1,1]")); assertThat(jsonString, containsString("\"partitionIds\":[123]")); @@ -223,6 +240,30 @@ public class BaseSubscriptionDeliverySubscriberTest { assertThat(jsonString, containsString("\"partitionIds\":[null]")); } + @Test + public void testDeliveryMessageWithPartition() throws URISyntaxException { + RequestPartitionId thePartitionId = RequestPartitionId.fromPartitionId(123, LocalDate.of(2020, 1, 1)); + when(myInterceptorBroadcaster.callHooks(eq(Pointcut.SUBSCRIPTION_BEFORE_MESSAGE_DELIVERY), any())).thenReturn(true); + when(myInterceptorBroadcaster.callHooks(eq(Pointcut.SUBSCRIPTION_AFTER_MESSAGE_DELIVERY), any())).thenReturn(false); + when(myChannelFactory.getOrCreateProducer(any(), any(), any())).thenReturn(myChannelProducer); + + CanonicalSubscription subscription = generateSubscription(); + Patient patient = generatePatient(); + + ResourceDeliveryMessage payload = new ResourceDeliveryMessage(); + payload.setSubscription(subscription); + payload.setPayload(myCtx, patient, EncodingEnum.JSON); + payload.setOperationType(ResourceModifiedMessage.OperationTypeEnum.CREATE); + payload.setPartitionId(thePartitionId); + + myMessageSubscriber.handleMessage(payload); + verify(myChannelFactory).getOrCreateProducer(any(), any(), any()); + ArgumentCaptor captor = ArgumentCaptor.forClass(ResourceModifiedJsonMessage.class); + verify(myChannelProducer).send(captor.capture()); + final List params = captor.getAllValues(); + assertEquals(thePartitionId, params.get(0).getPayload().getPartitionId()); + } + @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}\"}}"; @@ -250,4 +291,5 @@ public class BaseSubscriptionDeliverySubscriberTest { subscription.setPayloadString("application/fhir+json"); return subscription; } + } diff --git a/hapi-fhir-jpaserver-test-utilities/pom.xml b/hapi-fhir-jpaserver-test-utilities/pom.xml index e5e85064d3b..5b627067f66 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 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml index 6e2a4733b10..d14b2441926 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 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-server-mdm/pom.xml b/hapi-fhir-server-mdm/pom.xml index b41c4db1bf6..953e9979ebf 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 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmControllerSvc.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmControllerSvc.java index 46048e15292..cfbd8600725 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmControllerSvc.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmControllerSvc.java @@ -22,6 +22,7 @@ package ca.uhn.fhir.mdm.api; import ca.uhn.fhir.mdm.api.paging.MdmPageRequest; import ca.uhn.fhir.mdm.model.MdmTransactionContext; +import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBaseParameters; @@ -34,10 +35,17 @@ import java.util.List; public interface IMdmControllerSvc { + @Deprecated Page queryLinks(@Nullable String theGoldenResourceId, @Nullable String theSourceResourceId, @Nullable String theMatchResult, @Nullable String theLinkSource, MdmTransactionContext theMdmTransactionContext, MdmPageRequest thePageRequest); + Page queryLinks(@Nullable String theGoldenResourceId, @Nullable String theSourceResourceId, @Nullable String theMatchResult, @Nullable String theLinkSource, MdmTransactionContext theMdmTransactionContext, MdmPageRequest thePageRequest, RequestDetails theRequestDetails); + + Page queryLinksFromPartitionList(@Nullable String theGoldenResourceId, @Nullable String theSourceResourceId, @Nullable String theMatchResult, @Nullable String theLinkSource, MdmTransactionContext theMdmTransactionContext, MdmPageRequest thePageRequest, List thePartitionIds); + Page getDuplicateGoldenResources(MdmTransactionContext theMdmTransactionContext, MdmPageRequest thePageRequest); + Page getDuplicateGoldenResources(MdmTransactionContext theMdmTransactionContext, MdmPageRequest thePageRequest, RequestDetails theRequestDetails); + void notDuplicateGoldenResource(String theGoldenResourceId, String theTargetGoldenResourceId, MdmTransactionContext theMdmTransactionContext); IAnyResource mergeGoldenResources(String theFromGoldenResourceId, String theToGoldenResourceId, IAnyResource theManuallyMergedGoldenResource, MdmTransactionContext theMdmTransactionContext); diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmLinkQuerySvc.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmLinkQuerySvc.java index 6d427ffece0..4147528e0ad 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmLinkQuerySvc.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmLinkQuerySvc.java @@ -25,10 +25,14 @@ import ca.uhn.fhir.mdm.model.MdmTransactionContext; import org.hl7.fhir.instance.model.api.IIdType; import org.springframework.data.domain.Page; +import java.util.List; + /** * This service supports the MDM operation providers for those services that return multiple MDM links. */ public interface IMdmLinkQuerySvc { Page queryLinks(IIdType theGoldenResourceId, IIdType theSourceResourceId, MdmMatchResultEnum theMatchResult, MdmLinkSourceEnum theLinkSource, MdmTransactionContext theMdmContext, MdmPageRequest thePageRequest); + Page queryLinks(IIdType theGoldenResourceId, IIdType theSourceResourceId, MdmMatchResultEnum theMatchResult, MdmLinkSourceEnum theLinkSource, MdmTransactionContext theMdmContext, MdmPageRequest thePageRequest, List thePartitionId); Page getDuplicateGoldenResources(MdmTransactionContext theMdmContext, MdmPageRequest thePageRequest); + Page getDuplicateGoldenResources(MdmTransactionContext theMdmContext, MdmPageRequest thePageRequest, List thePartitionId); } diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmMatchFinderSvc.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmMatchFinderSvc.java index 5c6f450a00f..c5f8a3e4ce5 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmMatchFinderSvc.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmMatchFinderSvc.java @@ -20,6 +20,7 @@ package ca.uhn.fhir.mdm.api; * #L% */ +import ca.uhn.fhir.interceptor.model.RequestPartitionId; import org.hl7.fhir.instance.model.api.IAnyResource; import javax.annotation.Nonnull; @@ -36,5 +37,5 @@ public interface IMdmMatchFinderSvc { * @return a List of {@link MatchedTarget} representing POSSIBLE_MATCH and MATCH outcomes. */ @Nonnull - List getMatchedTargets(String theResourceType, IAnyResource theResource); + List getMatchedTargets(String theResourceType, IAnyResource theResource, RequestPartitionId theRequestPartitionId); } diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmSubmitSvc.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmSubmitSvc.java index 0e9589d1dca..d4e3c77473d 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmSubmitSvc.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmSubmitSvc.java @@ -20,6 +20,7 @@ package ca.uhn.fhir.mdm.api; * #L% */ +import ca.uhn.fhir.rest.api.server.RequestDetails; import org.hl7.fhir.instance.model.api.IIdType; import javax.annotation.Nullable; @@ -37,7 +38,7 @@ public interface IMdmSubmitSvc { * * @return */ - long submitAllSourceTypesToMdm(@Nullable String theCriteria); + long submitAllSourceTypesToMdm(@Nullable String theCriteria, RequestDetails theRequestDetails); /** * Given a type and a search criteria, submit all found resources for MDM processing. @@ -46,7 +47,7 @@ public interface IMdmSubmitSvc { * @param theCriteria The FHIR search critieria for filtering the resources to be submitted for MDM processing.. * @return the number of resources submitted for MDM processing. */ - long submitSourceResourceTypeToMdm(String theSourceResourceType, String theCriteria); + long submitSourceResourceTypeToMdm(String theSourceResourceType, String theCriteria, RequestDetails theRequestDetails); /** * Convenience method that calls {@link #submitSourceResourceTypeToMdm(String, String)} with the type pre-populated. @@ -54,7 +55,7 @@ public interface IMdmSubmitSvc { * @param theCriteria The FHIR search critieria for filtering the resources to be submitted for MDM processing. * @return the number of resources submitted for MDM processing. */ - long submitPractitionerTypeToMdm(String theCriteria); + long submitPractitionerTypeToMdm(String theCriteria, RequestDetails theRequestDetails); /** * Convenience method that calls {@link #submitSourceResourceTypeToMdm(String, String)} with the type pre-populated. @@ -62,7 +63,7 @@ public interface IMdmSubmitSvc { * @param theCriteria The FHIR search critieria for filtering the resources to be submitted for MDM processing. * @return the number of resources submitted for MDM processing. */ - long submitPatientTypeToMdm(String theCriteria); + long submitPatientTypeToMdm(String theCriteria, RequestDetails theRequestDetails); /** * Given an ID and a source resource type valid for MDM, manually submit the given ID for MDM processing. @@ -70,7 +71,7 @@ public interface IMdmSubmitSvc { * @param theId the ID of the resource to process for MDM. * @return the constant `1`, as if this function returns successfully, it will have processed one resource for MDM. */ - long submitSourceResourceToMdm(IIdType theId); + long submitSourceResourceToMdm(IIdType theId, RequestDetails theRequestDetails); /** * This setter exists to allow imported modules to override settings. diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/BaseMdmProvider.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/BaseMdmProvider.java index 886e6145047..58637c6ca38 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/BaseMdmProvider.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/BaseMdmProvider.java @@ -118,7 +118,7 @@ public abstract class BaseMdmProvider { protected IBaseParameters parametersFromMdmLinks(Page theMdmLinkStream, boolean includeResultAndSource, ServletRequestDetails theServletRequestDetails, MdmPageRequest thePageRequest) { IBaseParameters retval = ParametersUtil.newInstance(myFhirContext); addPagingParameters(retval, theMdmLinkStream, theServletRequestDetails, thePageRequest); - theMdmLinkStream.forEach(mdmLink -> { + theMdmLinkStream.getContent().forEach(mdmLink -> { IBase resultPart = ParametersUtil.addParameterToParameters(myFhirContext, retval, "link"); ParametersUtil.addPartString(myFhirContext, resultPart, "goldenResourceId", mdmLink.getGoldenResourceId()); ParametersUtil.addPartString(myFhirContext, resultPart, "sourceResourceId", mdmLink.getSourceId()); diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/MdmControllerHelper.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/MdmControllerHelper.java index f1f48b26080..6f4b067ba2b 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/MdmControllerHelper.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/MdmControllerHelper.java @@ -22,6 +22,8 @@ package ca.uhn.fhir.mdm.provider; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.interceptor.model.RequestPartitionId; +import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc; import ca.uhn.fhir.mdm.api.IMdmMatchFinderSvc; import ca.uhn.fhir.mdm.api.IMdmSettings; import ca.uhn.fhir.mdm.api.MatchedTarget; @@ -29,6 +31,7 @@ import ca.uhn.fhir.mdm.api.MdmConstants; import ca.uhn.fhir.mdm.util.MdmResourceUtil; import ca.uhn.fhir.mdm.util.MessageHelper; import ca.uhn.fhir.model.primitive.IdDt; +import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; import ca.uhn.fhir.rest.server.provider.ProviderConstants; @@ -60,18 +63,21 @@ public class MdmControllerHelper { private final IMdmSettings myMdmSettings; private final MessageHelper myMessageHelper; private final IMdmMatchFinderSvc myMdmMatchFinderSvc; + private final IRequestPartitionHelperSvc myRequestPartitionHelperSvc; @Autowired public MdmControllerHelper(FhirContext theFhirContext, IResourceLoader theResourceLoader, IMdmMatchFinderSvc theMdmMatchFinderSvc, IMdmSettings theMdmSettings, - MessageHelper theMessageHelper) { + MessageHelper theMessageHelper, + IRequestPartitionHelperSvc theRequestPartitionHelperSvc) { myFhirContext = theFhirContext; myResourceLoader = theResourceLoader; myMdmSettings = theMdmSettings; myMdmMatchFinderSvc = theMdmMatchFinderSvc; myMessageHelper = theMessageHelper; + myRequestPartitionHelperSvc = theRequestPartitionHelperSvc; } public void validateSameVersion(IAnyResource theResource, String theResourceId) { @@ -130,8 +136,9 @@ public class MdmControllerHelper { /** * Helper method which will return a bundle of all Matches and Possible Matches. */ - public IBaseBundle getMatchesAndPossibleMatchesForResource(IAnyResource theResource, String theResourceType) { - List matches = myMdmMatchFinderSvc.getMatchedTargets(theResourceType, theResource); + public IBaseBundle getMatchesAndPossibleMatchesForResource(IAnyResource theResource, String theResourceType, RequestDetails theRequestDetails) { + RequestPartitionId requestPartitionId = myRequestPartitionHelperSvc.determineReadPartitionForRequest(theRequestDetails, theResourceType, null); + List matches = myMdmMatchFinderSvc.getMatchedTargets(theResourceType, theResource, requestPartitionId); matches.sort(Comparator.comparing((MatchedTarget m) -> m.getMatchResult().getNormalizedScore()).reversed()); BundleBuilder builder = new BundleBuilder(myFhirContext); diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/MdmProviderDstu3Plus.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/MdmProviderDstu3Plus.java index 64a193eda22..0d28d9300a5 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/MdmProviderDstu3Plus.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/MdmProviderDstu3Plus.java @@ -87,21 +87,23 @@ public class MdmProviderDstu3Plus extends BaseMdmProvider { } @Operation(name = ProviderConstants.EMPI_MATCH, typeName = "Patient") - public IBaseBundle match(@OperationParam(name = ProviderConstants.MDM_MATCH_RESOURCE, min = 1, max = 1, typeName = "Patient") IAnyResource thePatient) { + public IBaseBundle match(@OperationParam(name = ProviderConstants.MDM_MATCH_RESOURCE, min = 1, max = 1, typeName = "Patient") IAnyResource thePatient, + RequestDetails theRequestDetails) { if (thePatient == null) { throw new InvalidRequestException(Msg.code(1498) + "resource may not be null"); } - return myMdmControllerHelper.getMatchesAndPossibleMatchesForResource(thePatient, "Patient"); + return myMdmControllerHelper.getMatchesAndPossibleMatchesForResource(thePatient, "Patient", theRequestDetails); } @Operation(name = ProviderConstants.MDM_MATCH) public IBaseBundle serverMatch(@OperationParam(name = ProviderConstants.MDM_MATCH_RESOURCE, min = 1, max = 1) IAnyResource theResource, - @OperationParam(name = ProviderConstants.MDM_RESOURCE_TYPE, min = 1, max = 1, typeName = "string") IPrimitiveType theResourceType + @OperationParam(name = ProviderConstants.MDM_RESOURCE_TYPE, min = 1, max = 1, typeName = "string") IPrimitiveType theResourceType, + RequestDetails theRequestDetails ) { if (theResource == null) { throw new InvalidRequestException(Msg.code(1499) + "resource may not be null"); } - return myMdmControllerHelper.getMatchesAndPossibleMatchesForResource(theResource, theResourceType.getValueAsString()); + return myMdmControllerHelper.getMatchesAndPossibleMatchesForResource(theResource, theResourceType.getValueAsString(), theRequestDetails); } @Operation(name = ProviderConstants.MDM_MERGE_GOLDEN_RESOURCES) @@ -184,13 +186,13 @@ public class MdmProviderDstu3Plus extends BaseMdmProvider { @Description(formalDefinition = "Results from this method are returned across multiple pages. This parameter controls the size of those pages.") @OperationParam(name = Constants.PARAM_COUNT, min = 0, max = 1, typeName = "integer") IPrimitiveType theCount, - ServletRequestDetails theRequestDetails) { MdmPageRequest mdmPageRequest = new MdmPageRequest(theOffset, theCount, DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE); Page mdmLinkJson = myMdmControllerSvc.queryLinks(extractStringOrNull(theGoldenResourceId), extractStringOrNull(theResourceId), extractStringOrNull(theMatchResult), extractStringOrNull(theLinkSource), createMdmContext(theRequestDetails, MdmTransactionContext.OperationType.QUERY_LINKS, - getResourceType(ProviderConstants.MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID, theGoldenResourceId)), mdmPageRequest); + getResourceType(ProviderConstants.MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID, theGoldenResourceId)), + mdmPageRequest, theRequestDetails); return parametersFromMdmLinks(mdmLinkJson, true, theRequestDetails, mdmPageRequest); } @@ -207,7 +209,7 @@ public class MdmProviderDstu3Plus extends BaseMdmProvider { MdmPageRequest mdmPageRequest = new MdmPageRequest(theOffset, theCount, DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE); - Page possibleDuplicates = myMdmControllerSvc.getDuplicateGoldenResources(createMdmContext(theRequestDetails, MdmTransactionContext.OperationType.DUPLICATE_GOLDEN_RESOURCES, null), mdmPageRequest); + Page possibleDuplicates = myMdmControllerSvc.getDuplicateGoldenResources(createMdmContext(theRequestDetails, MdmTransactionContext.OperationType.DUPLICATE_GOLDEN_RESOURCES, null), mdmPageRequest, theRequestDetails); return parametersFromMdmLinks(possibleDuplicates, false, theRequestDetails, mdmPageRequest); } @@ -239,9 +241,9 @@ public class MdmProviderDstu3Plus extends BaseMdmProvider { String resourceType = convertStringTypeToString(theResourceType); long submittedCount; if (resourceType != null) { - submittedCount = myMdmSubmitSvc.submitSourceResourceTypeToMdm(resourceType, criteria); + submittedCount = myMdmSubmitSvc.submitSourceResourceTypeToMdm(resourceType, criteria, theRequestDetails); } else { - submittedCount = myMdmSubmitSvc.submitAllSourceTypesToMdm(criteria); + submittedCount = myMdmSubmitSvc.submitAllSourceTypesToMdm(criteria, theRequestDetails); } return buildMdmOutParametersWithCount(submittedCount); } @@ -257,7 +259,7 @@ public class MdmProviderDstu3Plus extends BaseMdmProvider { public IBaseParameters mdmBatchPatientInstance( @IdParam IIdType theIdParam, RequestDetails theRequest) { - long submittedCount = myMdmSubmitSvc.submitSourceResourceToMdm(theIdParam); + long submittedCount = myMdmSubmitSvc.submitSourceResourceToMdm(theIdParam, theRequest); return buildMdmOutParametersWithCount(submittedCount); } @@ -268,7 +270,7 @@ public class MdmProviderDstu3Plus extends BaseMdmProvider { @OperationParam(name = ProviderConstants.MDM_BATCH_RUN_CRITERIA, typeName = "string") IPrimitiveType theCriteria, RequestDetails theRequest) { String criteria = convertStringTypeToString(theCriteria); - long submittedCount = myMdmSubmitSvc.submitPatientTypeToMdm(criteria); + long submittedCount = myMdmSubmitSvc.submitPatientTypeToMdm(criteria, theRequest); return buildMdmOutParametersWithCount(submittedCount); } @@ -278,7 +280,7 @@ public class MdmProviderDstu3Plus extends BaseMdmProvider { public IBaseParameters mdmBatchPractitionerInstance( @IdParam IIdType theIdParam, RequestDetails theRequest) { - long submittedCount = myMdmSubmitSvc.submitSourceResourceToMdm(theIdParam); + long submittedCount = myMdmSubmitSvc.submitSourceResourceToMdm(theIdParam, theRequest); return buildMdmOutParametersWithCount(submittedCount); } @@ -289,7 +291,7 @@ public class MdmProviderDstu3Plus extends BaseMdmProvider { @OperationParam(name = ProviderConstants.MDM_BATCH_RUN_CRITERIA, typeName = "string") IPrimitiveType theCriteria, RequestDetails theRequest) { String criteria = convertStringTypeToString(theCriteria); - long submittedCount = myMdmSubmitSvc.submitPractitionerTypeToMdm(criteria); + long submittedCount = myMdmSubmitSvc.submitPractitionerTypeToMdm(criteria, theRequest); return buildMdmOutParametersWithCount(submittedCount); } diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/GoldenResourceHelper.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/GoldenResourceHelper.java index a4d6aa973c6..2fdd66a58f2 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/GoldenResourceHelper.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/GoldenResourceHelper.java @@ -32,6 +32,7 @@ import ca.uhn.fhir.mdm.api.IMdmSurvivorshipService; import ca.uhn.fhir.mdm.log.Logs; import ca.uhn.fhir.mdm.model.CanonicalEID; import ca.uhn.fhir.mdm.model.MdmTransactionContext; +import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.util.FhirTerser; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBase; @@ -98,6 +99,9 @@ public class GoldenResourceHelper { MdmResourceUtil.setMdmManaged(newGoldenResource); MdmResourceUtil.setGoldenResource(newGoldenResource); + // add the partition id to the new resource + newGoldenResource.setUserData(Constants.RESOURCE_PARTITION_ID, theIncomingResource.getUserData(Constants.RESOURCE_PARTITION_ID)); + return (T) newGoldenResource; } diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/MessageHelper.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/MessageHelper.java index a20c4ecaa9d..7f8f05335da 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/MessageHelper.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/MessageHelper.java @@ -21,9 +21,9 @@ package ca.uhn.fhir.mdm.util; */ import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.mdm.api.IMdmSettings; import ca.uhn.fhir.mdm.api.MdmConstants; import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; -import ca.uhn.fhir.mdm.api.IMdmSettings; import ca.uhn.fhir.rest.server.provider.ProviderConstants; import org.hl7.fhir.instance.model.api.IAnyResource; import org.springframework.beans.factory.annotation.Autowired; @@ -111,4 +111,13 @@ public class MessageHelper { public String getMessageForFailedGoldenResourceLoad(String theParamName, String theGoldenResourceId) { return theGoldenResourceId + " used as parameter [" + theParamName + "] could not be loaded as a golden resource, as it appears to be lacking the golden resource meta tags."; } + + public String getMessageForMismatchPartition(IAnyResource theGoldenRecord, IAnyResource theSourceResource) { + return getMessageForMismatchPartition(theGoldenRecord.getIdElement().toVersionless().toString(), + theSourceResource.getIdElement().toVersionless().toString()); + } + + public String getMessageForMismatchPartition(String theGoldenRecord, String theSourceResource) { + return theGoldenRecord + " and " + theSourceResource + " are stored in different partitions. This operation is only available for resources on the same partition."; + } } diff --git a/hapi-fhir-server-openapi/pom.xml b/hapi-fhir-server-openapi/pom.xml index 3b4d7b79fb0..84bd20d691f 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 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server/pom.xml b/hapi-fhir-server/pom.xml index 182be5d0d91..e0fcd4bad9a 100644 --- a/hapi-fhir-server/pom.xml +++ b/hapi-fhir-server/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ProviderConstants.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ProviderConstants.java index 46c11aa6cba..c56013d9264 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ProviderConstants.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ProviderConstants.java @@ -88,6 +88,7 @@ public class ProviderConstants { public static final String MDM_QUERY_LINKS = "$mdm-query-links"; public static final String MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID = "goldenResourceId"; public static final String MDM_QUERY_LINKS_RESOURCE_ID = "resourceId"; + public static final String MDM_QUERY_PARTITION_IDS = "partitionIds"; public static final String MDM_QUERY_LINKS_MATCH_RESULT = "matchResult"; public static final String MDM_QUERY_LINKS_LINK_SOURCE = "linkSource"; 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 5f8e9a6b9e8..156612065f4 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 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-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 c18ba0fe5d1..d7e02a72215 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 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-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 33a06a40522..b958bf16589 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 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-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 e49dcb4b634..8c9d210c4ac 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 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-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 0353eca64d5..495255f0e32 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 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-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 580e8c84eaf..eacf28a7391 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 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/pom.xml b/hapi-fhir-spring-boot/pom.xml index 3c1d2d50cf5..bc2ec54701b 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 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-sql-migrate/pom.xml b/hapi-fhir-sql-migrate/pom.xml index abc281b0919..146e2a10aee 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 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-batch2-jobs/pom.xml b/hapi-fhir-storage-batch2-jobs/pom.xml index cd2f77337b0..f06eeae2dcd 100644 --- a/hapi-fhir-storage-batch2-jobs/pom.xml +++ b/hapi-fhir-storage-batch2-jobs/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../hapi-deployable-pom/pom.xml 4.0.0 diff --git a/hapi-fhir-storage-batch2/pom.xml b/hapi-fhir-storage-batch2/pom.xml index cbf54f816fa..209422bcedb 100644 --- a/hapi-fhir-storage-batch2/pom.xml +++ b/hapi-fhir-storage-batch2/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage/pom.xml b/hapi-fhir-storage/pom.xml index 8613fd361ea..cedcaa104c0 100644 --- a/hapi-fhir-storage/pom.xml +++ b/hapi-fhir-storage/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/partition/IRequestPartitionHelperSvc.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/partition/IRequestPartitionHelperSvc.java index cccc42881a7..d17a004d232 100644 --- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/partition/IRequestPartitionHelperSvc.java +++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/partition/IRequestPartitionHelperSvc.java @@ -35,7 +35,7 @@ import java.util.Set; public interface IRequestPartitionHelperSvc { @Nonnull - RequestPartitionId determineReadPartitionForRequest(@Nullable RequestDetails theRequest, String theResourceType, @Nonnull ReadPartitionIdRequestDetails theDetails); + RequestPartitionId determineReadPartitionForRequest(@Nullable RequestDetails theRequest, String theResourceType, ReadPartitionIdRequestDetails theDetails); @Nonnull default RequestPartitionId determineReadPartitionForRequestForRead(RequestDetails theRequest, String theResourceType, IIdType theId) { @@ -55,6 +55,9 @@ public interface IRequestPartitionHelperSvc { return determineReadPartitionForRequest(theRequest, theResourceType, details); } + @Nonnull + default void validateHasPartitionPermissions(RequestDetails theRequest, String theResourceType, RequestPartitionId theRequestPartitionId){} + @Nonnull RequestPartitionId determineCreatePartitionForRequest(@Nullable RequestDetails theRequest, @Nonnull IBaseResource theResource, @Nonnull String theResourceType); 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 9e27d1dc5b8..0d740335134 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,7 +57,7 @@ public class SystemRequestDetails extends RequestDetails { super(new MyInterceptorBroadcaster()); } - public static SystemRequestDetails forAllPartition(){ + public static SystemRequestDetails forAllPartitions(){ return new SystemRequestDetails().setRequestPartitionId(RequestPartitionId.allPartitions()); } diff --git a/hapi-fhir-structures-dstu2.1/pom.xml b/hapi-fhir-structures-dstu2.1/pom.xml index 55362f80b65..2ffa4be5db1 100644 --- a/hapi-fhir-structures-dstu2.1/pom.xml +++ b/hapi-fhir-structures-dstu2.1/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu2/pom.xml b/hapi-fhir-structures-dstu2/pom.xml index 8c1b0f5b9ba..b4c347d03da 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 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu3/pom.xml b/hapi-fhir-structures-dstu3/pom.xml index 62165125e54..7e515aa94b8 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 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-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 18f8e9b4b7b..7cb8d1bd89f 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 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r4/pom.xml b/hapi-fhir-structures-r4/pom.xml index 80eea6b82f5..d32002b239b 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 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r5/pom.xml b/hapi-fhir-structures-r5/pom.xml index 5d80472cb26..dc06844df85 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 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-test-utilities/pom.xml b/hapi-fhir-test-utilities/pom.xml index 085ba424fa6..f349c3d8867 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 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-testpage-overlay/pom.xml b/hapi-fhir-testpage-overlay/pom.xml index e3a5131c087..0724aa8a34a 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 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-validation-resources-dstu2.1/pom.xml b/hapi-fhir-validation-resources-dstu2.1/pom.xml index 56fcb133bab..a04333f2928 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 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-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 7bfbeaf23ac..40cb90ce17e 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 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-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 7e006905e85..69ef0bca7c2 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 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-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 9e60a65b368..cd548fb3885 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 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-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 2638ce3ea7d..36347eb9d0a 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 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation/pom.xml b/hapi-fhir-validation/pom.xml index 2ea9d1f9bf0..dd952554961 100644 --- a/hapi-fhir-validation/pom.xml +++ b/hapi-fhir-validation/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-tinder-plugin/pom.xml b/hapi-tinder-plugin/pom.xml index 4f87e0e7bfa..32b1648774d 100644 --- a/hapi-tinder-plugin/pom.xml +++ b/hapi-tinder-plugin/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../pom.xml @@ -58,37 +58,37 @@ ca.uhn.hapi.fhir hapi-fhir-structures-dstu3 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-structures-hl7org-dstu2 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-structures-r4 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-structures-r5 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-validation-resources-dstu2 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-validation-resources-dstu3 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ca.uhn.hapi.fhir hapi-fhir-validation-resources-r4 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT org.apache.velocity diff --git a/hapi-tinder-test/pom.xml b/hapi-tinder-test/pom.xml index f7a3a430de8..19ba66d4e44 100644 --- a/hapi-tinder-test/pom.xml +++ b/hapi-tinder-test/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index 920a6629beb..3a8d6d08400 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir pom - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT HAPI-FHIR An open-source implementation of the FHIR specification in Java. https://hapifhir.io @@ -2004,7 +2004,7 @@ ca.uhn.hapi.fhir hapi-fhir-checkstyle - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT diff --git a/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml b/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml index f82171b6b1d..18e5614d0f2 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 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-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 6db480a3e3e..c54b8366b2f 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 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-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 abba9a6d26e..3c393fd4623 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 - 6.0.0-PRE9-SNAPSHOT + 6.0.0-PRE10-SNAPSHOT ../../pom.xml