diff --git a/hapi-deployable-pom/pom.xml b/hapi-deployable-pom/pom.xml index 2ec9cf89a87..d4119a31327 100644 --- a/hapi-deployable-pom/pom.xml +++ b/hapi-deployable-pom/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-android/pom.xml b/hapi-fhir-android/pom.xml index 5692c94439f..442c14360d3 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.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-base/pom.xml b/hapi-fhir-base/pom.xml index a80798ea5b5..9ac7a201527 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.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-bom/pom.xml b/hapi-fhir-bom/pom.xml index e4c38b9e6a6..68f115296da 100644 --- a/hapi-fhir-bom/pom.xml +++ b/hapi-fhir-bom/pom.xml @@ -4,7 +4,7 @@ 4.0.0 ca.uhn.hapi.fhir hapi-fhir-bom - 6.9.5-SNAPSHOT + 6.9.6-SNAPSHOT pom HAPI FHIR BOM @@ -12,7 +12,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-checkstyle/pom.xml b/hapi-fhir-checkstyle/pom.xml index 1cb3d11d571..7887f8a846e 100644 --- a/hapi-fhir-checkstyle/pom.xml +++ b/hapi-fhir-checkstyle/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.9.5-SNAPSHOT + 6.9.6-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 1069994d43d..47ca5a50880 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.9.5-SNAPSHOT + 6.9.6-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 dcdaf3ee40a..110ee60ccc3 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.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-cli/pom.xml b/hapi-fhir-cli/pom.xml index c345fb11dff..8754f52a93f 100644 --- a/hapi-fhir-cli/pom.xml +++ b/hapi-fhir-cli/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-client-okhttp/pom.xml b/hapi-fhir-client-okhttp/pom.xml index 3a9e2ed3eec..26429d5be04 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.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-client/pom.xml b/hapi-fhir-client/pom.xml index d59b12b45d1..6bc6c147294 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.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-converter/pom.xml b/hapi-fhir-converter/pom.xml index e74f5fdff01..1fe94d51def 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.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-dist/pom.xml b/hapi-fhir-dist/pom.xml index 5dcb171b543..c4bfa2038ac 100644 --- a/hapi-fhir-dist/pom.xml +++ b/hapi-fhir-dist/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-docs/pom.xml b/hapi-fhir-docs/pom.xml index 4f57c864872..e8dbbd5ec44 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.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5236-rebuild-golden-resource-after-changing-mdm-link.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5236-rebuild-golden-resource-after-changing-mdm-link.yaml new file mode 100644 index 00000000000..227f9b8a39b --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_0_0/5236-rebuild-golden-resource-after-changing-mdm-link.yaml @@ -0,0 +1,13 @@ +--- +type: add +issue: 5236 +title: "Previously, when updating an MDM link to NO_MATCH, + the golden resource involved would maintain its previous + values, as defined by survivorship service. + This would result in out-of-date golden resources with + data that might not be accurate anymore. + Now, when a link is changed to NO_MATCH, golden resources + will be rebuilt from the ground up using the MDM survivorship + service, and the set of links/source resources available at the + time of update. +" diff --git a/hapi-fhir-jacoco/pom.xml b/hapi-fhir-jacoco/pom.xml index 0cdfa074405..91f789457c5 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.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jaxrsserver-base/pom.xml b/hapi-fhir-jaxrsserver-base/pom.xml index 16c9f38e6df..b0d39c3ee95 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.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpa/pom.xml b/hapi-fhir-jpa/pom.xml index 1205d0dd954..6bd7106f74f 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.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-base/pom.xml b/hapi-fhir-jpaserver-base/pom.xml index 57a35693a7c..5d8acdb9386 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.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaConfig.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaConfig.java index 36a888940a2..2fc257245cd 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaConfig.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/JpaConfig.java @@ -70,14 +70,11 @@ import ca.uhn.fhir.jpa.dao.index.DaoResourceLinkResolver; import ca.uhn.fhir.jpa.dao.index.DaoSearchParamSynchronizer; import ca.uhn.fhir.jpa.dao.index.IdHelperService; import ca.uhn.fhir.jpa.dao.index.SearchParamWithInlineReferencesExtractor; -import ca.uhn.fhir.jpa.dao.mdm.JpaMdmLinkImplFactory; -import ca.uhn.fhir.jpa.dao.mdm.MdmLinkDaoJpaImpl; import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService; import ca.uhn.fhir.jpa.dao.validation.SearchParameterDaoValidator; import ca.uhn.fhir.jpa.delete.DeleteConflictFinderService; import ca.uhn.fhir.jpa.delete.DeleteConflictService; import ca.uhn.fhir.jpa.delete.ThreadSafeResourceDeleterSvc; -import ca.uhn.fhir.jpa.entity.MdmLink; import ca.uhn.fhir.jpa.entity.Search; import ca.uhn.fhir.jpa.esr.ExternallyStoredResourceServiceRegistry; import ca.uhn.fhir.jpa.graphql.DaoRegistryGraphQLStorageServices; @@ -171,9 +168,6 @@ import ca.uhn.fhir.jpa.util.MemoryCacheService; import ca.uhn.fhir.jpa.util.PersistenceContextProvider; import ca.uhn.fhir.jpa.validation.ResourceLoaderImpl; import ca.uhn.fhir.jpa.validation.ValidationSettings; -import ca.uhn.fhir.mdm.dao.IMdmLinkDao; -import ca.uhn.fhir.mdm.dao.IMdmLinkImplFactory; -import ca.uhn.fhir.mdm.svc.MdmLinkExpandSvc; import ca.uhn.fhir.model.api.IPrimitiveDatatype; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.storage.IDeleteExpungeJobSubmitter; @@ -221,7 +215,8 @@ import javax.annotation.Nullable; JpaBulkExportConfig.class, SearchConfig.class, PackageLoaderConfig.class, - EnversAuditConfig.class + EnversAuditConfig.class, + MdmJpaConfig.class }) public class JpaConfig { public static final String JPA_VALIDATION_SUPPORT_CHAIN = "myJpaValidationSupportChain"; @@ -480,11 +475,6 @@ public class JpaConfig { return new RequestTenantPartitionInterceptor(); } - @Bean - public MdmLinkExpandSvc mdmLinkExpandSvc() { - return new MdmLinkExpandSvc(); - } - @Bean @Lazy public TerminologyUploaderProvider terminologyUploaderProvider() { @@ -858,16 +848,6 @@ public class JpaConfig { return new ObservationLastNIndexPersistSvc(); } - @Bean - public IMdmLinkDao mdmLinkDao() { - return new MdmLinkDaoJpaImpl(); - } - - @Bean - IMdmLinkImplFactory mdmLinkImplFactory() { - return new JpaMdmLinkImplFactory(); - } - @Bean @Scope("prototype") public PersistenceContextProvider persistenceContextProvider() { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/MdmJpaConfig.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/MdmJpaConfig.java new file mode 100644 index 00000000000..5d1178a1754 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/MdmJpaConfig.java @@ -0,0 +1,38 @@ +package ca.uhn.fhir.jpa.config; + +import ca.uhn.fhir.jpa.api.svc.IDeleteExpungeSvc; +import ca.uhn.fhir.jpa.api.svc.IMdmClearHelperSvc; +import ca.uhn.fhir.jpa.bulk.mdm.MdmClearHelperSvcImpl; +import ca.uhn.fhir.jpa.dao.mdm.JpaMdmLinkImplFactory; +import ca.uhn.fhir.jpa.dao.mdm.MdmLinkDaoJpaImpl; +import ca.uhn.fhir.jpa.entity.MdmLink; +import ca.uhn.fhir.jpa.model.dao.JpaPid; +import ca.uhn.fhir.mdm.dao.IMdmLinkDao; +import ca.uhn.fhir.mdm.dao.IMdmLinkImplFactory; +import ca.uhn.fhir.mdm.svc.MdmLinkExpandSvc; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class MdmJpaConfig { + + @Bean + public MdmLinkExpandSvc mdmLinkExpandSvc() { + return new MdmLinkExpandSvc(); + } + + @Bean + public IMdmLinkDao mdmLinkDao() { + return new MdmLinkDaoJpaImpl(); + } + + @Bean + public IMdmLinkImplFactory mdmLinkImplFactory() { + return new JpaMdmLinkImplFactory(); + } + + @Bean + public IMdmClearHelperSvc helperSvc(IDeleteExpungeSvc theDeleteExpungeSvc) { + return new MdmClearHelperSvcImpl(theDeleteExpungeSvc); + } +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmLinkDaoJpaImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmLinkDaoJpaImpl.java index 1265733af92..013c06efee5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmLinkDaoJpaImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmLinkDaoJpaImpl.java @@ -28,12 +28,12 @@ import ca.uhn.fhir.jpa.entity.MdmLink; import ca.uhn.fhir.jpa.model.dao.JpaPid; import ca.uhn.fhir.jpa.model.entity.EnversRevision; import ca.uhn.fhir.mdm.api.IMdmLink; -import ca.uhn.fhir.mdm.api.MdmHistorySearchParameters; import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; import ca.uhn.fhir.mdm.api.MdmLinkWithRevision; import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; -import ca.uhn.fhir.mdm.api.MdmQuerySearchParameters; import ca.uhn.fhir.mdm.api.paging.MdmPageRequest; +import ca.uhn.fhir.mdm.api.params.MdmHistorySearchParameters; +import ca.uhn.fhir.mdm.api.params.MdmQuerySearchParameters; import ca.uhn.fhir.mdm.dao.IMdmLinkDao; import ca.uhn.fhir.mdm.model.MdmPidTuple; import ca.uhn.fhir.rest.api.SortOrderEnum; @@ -75,13 +75,13 @@ import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import javax.validation.constraints.NotNull; -import static ca.uhn.fhir.mdm.api.MdmQuerySearchParameters.GOLDEN_RESOURCE_NAME; -import static ca.uhn.fhir.mdm.api.MdmQuerySearchParameters.GOLDEN_RESOURCE_PID_NAME; -import static ca.uhn.fhir.mdm.api.MdmQuerySearchParameters.LINK_SOURCE_NAME; -import static ca.uhn.fhir.mdm.api.MdmQuerySearchParameters.MATCH_RESULT_NAME; -import static ca.uhn.fhir.mdm.api.MdmQuerySearchParameters.PARTITION_ID_NAME; -import static ca.uhn.fhir.mdm.api.MdmQuerySearchParameters.RESOURCE_TYPE_NAME; -import static ca.uhn.fhir.mdm.api.MdmQuerySearchParameters.SOURCE_PID_NAME; +import static ca.uhn.fhir.mdm.api.params.MdmQuerySearchParameters.GOLDEN_RESOURCE_NAME; +import static ca.uhn.fhir.mdm.api.params.MdmQuerySearchParameters.GOLDEN_RESOURCE_PID_NAME; +import static ca.uhn.fhir.mdm.api.params.MdmQuerySearchParameters.LINK_SOURCE_NAME; +import static ca.uhn.fhir.mdm.api.params.MdmQuerySearchParameters.MATCH_RESULT_NAME; +import static ca.uhn.fhir.mdm.api.params.MdmQuerySearchParameters.PARTITION_ID_NAME; +import static ca.uhn.fhir.mdm.api.params.MdmQuerySearchParameters.RESOURCE_TYPE_NAME; +import static ca.uhn.fhir.mdm.api.params.MdmQuerySearchParameters.SOURCE_PID_NAME; public class MdmLinkDaoJpaImpl implements IMdmLinkDao { private static final Logger ourLog = LoggerFactory.getLogger(MdmLinkDaoJpaImpl.class); @@ -380,7 +380,15 @@ public class MdmLinkDaoJpaImpl implements IMdmLinkDao { if (!theMdmHistorySearchParameters.getGoldenResourceIds().isEmpty() && !theMdmHistorySearchParameters.getSourceIds().isEmpty()) { - goldenResourceAndOrResourceIdCriterion = AuditEntity.or(goldenResourceIdCriterion, resourceIdCriterion); + if (theMdmHistorySearchParameters.getParameterJoinType() == MdmHistorySearchParameters.JoinType.AND) { + // 'and' the source and golden ids + goldenResourceAndOrResourceIdCriterion = + AuditEntity.and(goldenResourceIdCriterion, resourceIdCriterion); + } else { + // default is 'or' + goldenResourceAndOrResourceIdCriterion = + AuditEntity.or(goldenResourceIdCriterion, resourceIdCriterion); + } } else if (!theMdmHistorySearchParameters.getGoldenResourceIds().isEmpty()) { goldenResourceAndOrResourceIdCriterion = goldenResourceIdCriterion; } else if (!theMdmHistorySearchParameters.getSourceIds().isEmpty()) { diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml b/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml index 8497dd5e29d..85dca1a2dd2 100644 --- a/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml +++ b/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-hfql/pom.xml b/hapi-fhir-jpaserver-hfql/pom.xml index 123434c329a..89c6b89493a 100644 --- a/hapi-fhir-jpaserver-hfql/pom.xml +++ b/hapi-fhir-jpaserver-hfql/pom.xml @@ -3,7 +3,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-ips/pom.xml b/hapi-fhir-jpaserver-ips/pom.xml index dc56af8a232..3929f01af72 100644 --- a/hapi-fhir-jpaserver-ips/pom.xml +++ b/hapi-fhir-jpaserver-ips/pom.xml @@ -3,7 +3,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-mdm/pom.xml b/hapi-fhir-jpaserver-mdm/pom.xml index 32a4860da5a..8eb952e4f8f 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.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml 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 21ab9fa5488..1d82d9e49a1 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 @@ -41,7 +41,6 @@ import ca.uhn.fhir.jpa.mdm.svc.MdmMatchLinkSvc; import ca.uhn.fhir.jpa.mdm.svc.MdmModelConverterSvcImpl; import ca.uhn.fhir.jpa.mdm.svc.MdmResourceDaoSvc; import ca.uhn.fhir.jpa.mdm.svc.MdmResourceFilteringSvc; -import ca.uhn.fhir.jpa.mdm.svc.MdmSurvivorshipSvcImpl; import ca.uhn.fhir.jpa.mdm.svc.candidate.CandidateSearcher; import ca.uhn.fhir.jpa.mdm.svc.candidate.FindCandidateByEidSvc; import ca.uhn.fhir.jpa.mdm.svc.candidate.FindCandidateByExampleSvc; @@ -59,7 +58,6 @@ import ca.uhn.fhir.mdm.api.IMdmLinkSvc; import ca.uhn.fhir.mdm.api.IMdmLinkUpdaterSvc; import ca.uhn.fhir.mdm.api.IMdmMatchFinderSvc; import ca.uhn.fhir.mdm.api.IMdmSettings; -import ca.uhn.fhir.mdm.api.IMdmSurvivorshipService; import ca.uhn.fhir.mdm.batch2.MdmBatch2Config; import ca.uhn.fhir.mdm.blocklist.svc.IBlockListRuleProvider; import ca.uhn.fhir.mdm.blocklist.svc.IBlockRuleEvaluationSvc; @@ -72,7 +70,6 @@ import ca.uhn.fhir.mdm.provider.MdmControllerHelper; import ca.uhn.fhir.mdm.provider.MdmProviderLoader; import ca.uhn.fhir.mdm.svc.MdmSearchParamSvc; import ca.uhn.fhir.mdm.util.EIDHelper; -import ca.uhn.fhir.mdm.util.GoldenResourceHelper; import ca.uhn.fhir.mdm.util.MdmPartitionHelper; import ca.uhn.fhir.mdm.util.MessageHelper; import ca.uhn.fhir.validation.IResourceLoader; @@ -83,8 +80,9 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @Configuration -@Import({MdmCommonConfig.class, MdmBatch2Config.class}) +@Import({MdmCommonConfig.class, MdmSurvivorshipConfig.class, MdmBatch2Config.class}) public class MdmConsumerConfig { + private static final Logger ourLog = Logs.getMdmTroubleshootingLog(); @Bean @@ -92,11 +90,6 @@ public class MdmConsumerConfig { return new MdmStorageInterceptor(); } - @Bean - IMdmSurvivorshipService mdmSurvivorshipService() { - return new MdmSurvivorshipSvcImpl(); - } - @Bean MdmQueueConsumerLoader mdmQueueConsumerLoader( IChannelFactory theChannelFactory, IMdmSettings theMdmSettings, MdmMessageHandler theMdmMessageHandler) { @@ -139,11 +132,6 @@ public class MdmConsumerConfig { return new MdmLinkSvcImpl(); } - @Bean - GoldenResourceHelper goldenResourceHelper(FhirContext theFhirContext) { - return new GoldenResourceHelper(theFhirContext); - } - @Bean MessageHelper messageHelper(IMdmSettings theMdmSettings, FhirContext theFhirContext) { return new MessageHelper(theMdmSettings, theFhirContext); diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmSurvivorshipConfig.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmSurvivorshipConfig.java new file mode 100644 index 00000000000..9b9083cdbab --- /dev/null +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmSurvivorshipConfig.java @@ -0,0 +1,52 @@ +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.svc.IIdHelperService; +import ca.uhn.fhir.mdm.api.IMdmLinkQuerySvc; +import ca.uhn.fhir.mdm.api.IMdmSettings; +import ca.uhn.fhir.mdm.api.IMdmSurvivorshipService; +import ca.uhn.fhir.mdm.svc.MdmSurvivorshipSvcImpl; +import ca.uhn.fhir.mdm.util.EIDHelper; +import ca.uhn.fhir.mdm.util.GoldenResourceHelper; +import ca.uhn.fhir.mdm.util.MdmPartitionHelper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class MdmSurvivorshipConfig { + + @Autowired + protected FhirContext myFhirContext; + + @Autowired + protected DaoRegistry myDaoRegistry; + + @Autowired + private IMdmSettings myMdmSettings; + + @Autowired + private EIDHelper myEIDHelper; + + @Autowired + private MdmPartitionHelper myMdmPartitionHelper; + + @Autowired + private IMdmLinkQuerySvc myMdmLinkQuerySvc; + + @Autowired + private IIdHelperService myIIdHelperService; + + @Bean + public IMdmSurvivorshipService mdmSurvivorshipService() { + return new MdmSurvivorshipSvcImpl( + myFhirContext, goldenResourceHelper(), myDaoRegistry, myMdmLinkQuerySvc, myIIdHelperService); + } + + @Bean + public GoldenResourceHelper goldenResourceHelper() { + // do not make this depend on IMdmSurvivorshipService + return new GoldenResourceHelper(myFhirContext, myMdmSettings, myEIDHelper, myMdmPartitionHelper); + } +} 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 167dfe55e8f..57ca03f6774 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 @@ -24,12 +24,12 @@ import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.api.svc.IIdHelperService; import ca.uhn.fhir.jpa.model.entity.PartitionablePartitionId; import ca.uhn.fhir.mdm.api.IMdmLink; -import ca.uhn.fhir.mdm.api.MdmHistorySearchParameters; import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; import ca.uhn.fhir.mdm.api.MdmLinkWithRevision; import ca.uhn.fhir.mdm.api.MdmMatchOutcome; import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; -import ca.uhn.fhir.mdm.api.MdmQuerySearchParameters; +import ca.uhn.fhir.mdm.api.params.MdmHistorySearchParameters; +import ca.uhn.fhir.mdm.api.params.MdmQuerySearchParameters; import ca.uhn.fhir.mdm.dao.IMdmLinkDao; import ca.uhn.fhir.mdm.dao.MdmLinkFactory; import ca.uhn.fhir.mdm.log.Logs; @@ -282,6 +282,7 @@ public class MdmLinkDaoSvc

example = Example.of(exampleLink); return myMdmLinkDao.findOne(example); } + /** * Delete a given {@link IMdmLink}. Note that this does not clear out the Golden resource. * It is a simple entity delete. 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 092b9cc4d25..b1e15c91a40 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 @@ -29,6 +29,7 @@ import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc; import ca.uhn.fhir.mdm.api.IGoldenResourceMergerSvc; import ca.uhn.fhir.mdm.api.IMdmLink; import ca.uhn.fhir.mdm.api.IMdmLinkSvc; +import ca.uhn.fhir.mdm.api.IMdmSurvivorshipService; import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; import ca.uhn.fhir.mdm.api.MdmMatchOutcome; import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; @@ -81,6 +82,9 @@ public class GoldenResourceMergerSvcImpl implements IGoldenResourceMergerSvc { @Autowired IInterceptorBroadcaster myInterceptorBroadcaster; + @Autowired + private IMdmSurvivorshipService myMdmSurvivorshipService; + @Override @Transactional public IAnyResource mergeGoldenResources(MdmMergeGoldenResourcesParams theParams) { @@ -105,7 +109,8 @@ public class GoldenResourceMergerSvcImpl implements IGoldenResourceMergerSvc { .getResource(); } else { myGoldenResourceHelper.mergeIndentifierFields(fromGoldenResource, toGoldenResource, mdmTransactionContext); - myGoldenResourceHelper.mergeNonIdentiferFields(fromGoldenResource, toGoldenResource, mdmTransactionContext); + myMdmSurvivorshipService.applySurvivorshipRulesToGoldenResource( + fromGoldenResource, toGoldenResource, mdmTransactionContext); // Save changes to the golden resource myMdmResourceDaoSvc.upsertGoldenResource(toGoldenResource, resourceType); } 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 36a8f201d70..6ea10e08c9f 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 @@ -34,10 +34,10 @@ import ca.uhn.fhir.mdm.api.IMdmControllerSvc; import ca.uhn.fhir.mdm.api.IMdmLinkCreateSvc; import ca.uhn.fhir.mdm.api.IMdmLinkQuerySvc; import ca.uhn.fhir.mdm.api.IMdmLinkUpdaterSvc; -import ca.uhn.fhir.mdm.api.MdmHistorySearchParameters; import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; -import ca.uhn.fhir.mdm.api.MdmQuerySearchParameters; import ca.uhn.fhir.mdm.api.paging.MdmPageRequest; +import ca.uhn.fhir.mdm.api.params.MdmHistorySearchParameters; +import ca.uhn.fhir.mdm.api.params.MdmQuerySearchParameters; import ca.uhn.fhir.mdm.batch2.clear.MdmClearAppCtx; import ca.uhn.fhir.mdm.batch2.clear.MdmClearJobParameters; import ca.uhn.fhir.mdm.batch2.submit.MdmSubmitAppCtx; diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmEidUpdateService.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmEidUpdateService.java index c442a2e3b1e..95e445012d3 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmEidUpdateService.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmEidUpdateService.java @@ -180,8 +180,8 @@ public class MdmEidUpdateService { log( theMdmTransactionContext, "Duplicate detected based on the fact that both resources have different external EIDs."); - IAnyResource newGoldenResource = - myGoldenResourceHelper.createGoldenResourceFromMdmSourceResource(theResource, theMdmTransactionContext); + IAnyResource newGoldenResource = myGoldenResourceHelper.createGoldenResourceFromMdmSourceResource( + theResource, theMdmTransactionContext, myMdmSurvivorshipService); myMdmLinkSvc.updateLink( newGoldenResource, 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 740cf046356..c638fbb5b35 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 @@ -22,12 +22,12 @@ package ca.uhn.fhir.jpa.mdm.svc; import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc; import ca.uhn.fhir.mdm.api.IMdmLink; import ca.uhn.fhir.mdm.api.IMdmLinkQuerySvc; -import ca.uhn.fhir.mdm.api.MdmHistorySearchParameters; import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; import ca.uhn.fhir.mdm.api.MdmLinkWithRevision; import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; -import ca.uhn.fhir.mdm.api.MdmQuerySearchParameters; import ca.uhn.fhir.mdm.api.paging.MdmPageRequest; +import ca.uhn.fhir.mdm.api.params.MdmHistorySearchParameters; +import ca.uhn.fhir.mdm.api.params.MdmQuerySearchParameters; import ca.uhn.fhir.mdm.model.MdmTransactionContext; import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkJson; import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkWithRevisionJson; 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 8c721bf0b6c..96892d37965 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 @@ -141,16 +141,7 @@ public class MdmLinkUpdaterSvcImpl implements IMdmLinkUpdaterSvc { mdmLink.setLinkSource(MdmLinkSourceEnum.MANUAL); // Add partition for the mdm link if it doesn't exist - RequestPartitionId goldenResourcePartitionId = - (RequestPartitionId) goldenResource.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())); - } + addPartitioninfoForLinkIfNecessary(goldenResource, mdmLink); myMdmLinkDaoSvc.save(mdmLink); if (matchResult == MdmMatchResultEnum.MATCH) { @@ -158,15 +149,38 @@ public class MdmLinkUpdaterSvcImpl implements IMdmLinkUpdaterSvc { myMdmSurvivorshipService.applySurvivorshipRulesToGoldenResource(sourceResource, goldenResource, mdmContext); } + /** + * We use the versionless id + * because we call update on the goldenResource in 2 places: + * here and below where we rebuild goldenresources if we have set + * a link to NO_MATCH. + * + * This can be a problem when a source resource is deleted. + * then {@link MdmStorageInterceptor} will update all links + * connecting to any golden resource that was connected to the now deleted + * source resource to NO_MATCH before deleting orphaned golden resources. + */ + goldenResource.setId(goldenResource.getIdElement().toVersionless()); myMdmResourceDaoSvc.upsertGoldenResource(goldenResource, mdmContext.getResourceType()); if (matchResult == MdmMatchResultEnum.NO_MATCH) { - // We need to return no match for when a Golden Resource has already been found elsewhere - if (myMdmLinkDaoSvc - .getMdmLinksBySourcePidAndMatchResult(sourceResourceId, MdmMatchResultEnum.MATCH) - .isEmpty()) { - // Need to find a new Golden Resource to link this target to + /* + * link is broken. We need to do 2 things: + * * update links for the source resource (if no other golden resources exist, for instance) + * * rebuild the golden resource from scratch, using current survivorship rules + * and the current set of links + */ + List links = + myMdmLinkDaoSvc.getMdmLinksBySourcePidAndMatchResult(sourceResourceId, MdmMatchResultEnum.MATCH); + if (links.isEmpty()) { + // No more links to source; Find a new Golden Resource to link this target to myMdmMatchLinkSvc.updateMdmLinksForMdmSource(sourceResource, mdmContext); } + + // with the link broken, the golden resource has delta info from a resource + // that is no longer matched to it; we need to remove this delta. But it's + // easier to just rebuild the resource from scratch using survivorship rules/current links + goldenResource = + myMdmSurvivorshipService.rebuildGoldenResourceWithSurvivorshipRules(goldenResource, mdmContext); } if (myInterceptorBroadcaster.hasHooks(Pointcut.MDM_POST_UPDATE_LINK)) { @@ -181,6 +195,19 @@ public class MdmLinkUpdaterSvcImpl implements IMdmLinkUpdaterSvc { return goldenResource; } + private static void addPartitioninfoForLinkIfNecessary(IAnyResource goldenResource, IMdmLink mdmLink) { + RequestPartitionId goldenResourcePartitionId = + (RequestPartitionId) goldenResource.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())); + } + } + /** * When updating POSSIBLE_MATCH link to a MATCH we need to validate that a MATCH to a different golden resource * doesn't exist, because a resource mustn't be a MATCH to more than one golden resource 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 8dd8abca747..b7fba3ab877 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 @@ -25,6 +25,7 @@ import ca.uhn.fhir.jpa.mdm.svc.candidate.CandidateStrategyEnum; 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.IMdmSurvivorshipService; import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; import ca.uhn.fhir.mdm.api.MdmMatchOutcome; import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; @@ -69,6 +70,9 @@ public class MdmMatchLinkSvc { @Autowired private IBlockRuleEvaluationSvc myBlockRuleEvaluationSvc; + @Autowired + private IMdmSurvivorshipService myMdmSurvivorshipService; + /** * Given an MDM source (consisting of any supported MDM type), find a suitable Golden Resource candidate for them, * or create one if one does not exist. Performs matching based on rules defined in mdm-rules.json. @@ -187,8 +191,8 @@ public class MdmMatchLinkSvc { String.format( "There were no matched candidates for MDM, creating a new %s Golden Resource.", theResource.getIdElement().getResourceType())); - IAnyResource newGoldenResource = - myGoldenResourceHelper.createGoldenResourceFromMdmSourceResource(theResource, theMdmTransactionContext); + IAnyResource newGoldenResource = myGoldenResourceHelper.createGoldenResourceFromMdmSourceResource( + theResource, theMdmTransactionContext, myMdmSurvivorshipService); // TODO GGG :) // 1. Get the right helper // 2. Create source resource for the MDM source @@ -214,7 +218,7 @@ public class MdmMatchLinkSvc { theMdmTransactionContext, "Duplicate detected based on the fact that both resources have different external EIDs."); IAnyResource newGoldenResource = myGoldenResourceHelper.createGoldenResourceFromMdmSourceResource( - theTargetResource, theMdmTransactionContext); + theTargetResource, theMdmTransactionContext, myMdmSurvivorshipService); myMdmLinkSvc.updateLink( newGoldenResource, diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmSurvivorshipSvcImpl.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmSurvivorshipSvcImpl.java deleted file mode 100644 index e1624d7999e..00000000000 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmSurvivorshipSvcImpl.java +++ /dev/null @@ -1,65 +0,0 @@ -/*- - * #%L - * HAPI FHIR JPA Server - Master Data Management - * %% - * Copyright (C) 2014 - 2023 Smile CDR, Inc. - * %% - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * #L% - */ -package ca.uhn.fhir.jpa.mdm.svc; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.mdm.api.IMdmSurvivorshipService; -import ca.uhn.fhir.mdm.model.MdmTransactionContext; -import ca.uhn.fhir.util.TerserUtil; -import org.hl7.fhir.instance.model.api.IBase; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.springframework.beans.factory.annotation.Autowired; - -public class MdmSurvivorshipSvcImpl implements IMdmSurvivorshipService { - - @Autowired - private FhirContext myFhirContext; - - /** - * Merges two golden resources by overwriting all field values on theGoldenResource param for CREATE_RESOURCE, - * UPDATE_RESOURCE, SUBMIT_RESOURCE_TO_MDM, UPDATE_LINK (when setting to MATCH) and MANUAL_MERGE_GOLDEN_RESOURCES. - * PID, identifiers and meta values are not affected by this operation. - * - * @param theTargetResource Target resource to retrieve fields from - * @param theGoldenResource Golden resource to merge fields into - * @param theMdmTransactionContext Current transaction context - * @param - */ - @Override - public void applySurvivorshipRulesToGoldenResource( - T theTargetResource, T theGoldenResource, MdmTransactionContext theMdmTransactionContext) { - switch (theMdmTransactionContext.getRestOperation()) { - case MERGE_GOLDEN_RESOURCES: - TerserUtil.mergeFields( - myFhirContext, - (IBaseResource) theTargetResource, - (IBaseResource) theGoldenResource, - TerserUtil.EXCLUDE_IDS_AND_META); - break; - default: - TerserUtil.replaceFields( - myFhirContext, - (IBaseResource) theTargetResource, - (IBaseResource) theGoldenResource, - TerserUtil.EXCLUDE_IDS_AND_META); - break; - } - } -} 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 385e14d2fba..d1f1fc1608f 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 @@ -11,6 +11,7 @@ import ca.uhn.fhir.jpa.mdm.config.MdmConsumerConfig; import ca.uhn.fhir.jpa.mdm.config.MdmSubmitterConfig; import ca.uhn.fhir.jpa.mdm.config.TestMdmConfigR4; import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc; +import ca.uhn.fhir.jpa.mdm.helper.MdmLinkHelper; import ca.uhn.fhir.jpa.mdm.matcher.IsLinkedTo; import ca.uhn.fhir.jpa.mdm.matcher.IsMatchedToAGoldenResource; import ca.uhn.fhir.jpa.mdm.matcher.IsPossibleDuplicateOf; @@ -26,6 +27,7 @@ import ca.uhn.fhir.jpa.searchparam.registry.SearchParamRegistryImpl; import ca.uhn.fhir.jpa.subscription.match.config.SubscriptionProcessorConfig; import ca.uhn.fhir.jpa.test.BaseJpaR4Test; import ca.uhn.fhir.mdm.api.IMdmLink; +import ca.uhn.fhir.mdm.api.IMdmLinkUpdaterSvc; import ca.uhn.fhir.mdm.api.MdmConstants; import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; @@ -134,6 +136,11 @@ abstract public class BaseMdmR4Test extends BaseJpaR4Test { @Autowired protected IPartitionLookupSvc myPartitionLookupSvc; + @Autowired + protected IMdmLinkUpdaterSvc myMdmLinkUpdaterSvc; + @Autowired + protected MdmLinkHelper myLinkHelper; + @BeforeEach public void beforeSetRequestDetails() { myRequestDetails = new ServletRequestDetails(myInterceptorBroadcaster); diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/dao/MdmLinkDaoSvcTest.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/dao/MdmLinkDaoSvcTest.java index 5af53d34392..27abb67ece0 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/dao/MdmLinkDaoSvcTest.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/dao/MdmLinkDaoSvcTest.java @@ -7,7 +7,7 @@ import ca.uhn.fhir.jpa.model.dao.JpaPid; import ca.uhn.fhir.jpa.model.entity.EnversRevision; import ca.uhn.fhir.jpa.util.TestUtil; import ca.uhn.fhir.mdm.api.IMdmLink; -import ca.uhn.fhir.mdm.api.MdmHistorySearchParameters; +import ca.uhn.fhir.mdm.api.params.MdmHistorySearchParameters; import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; import ca.uhn.fhir.mdm.api.MdmLinkWithRevision; import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; 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 1fc960c0863..7860c5d33d3 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 @@ -4,10 +4,10 @@ import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.entity.MdmLink; import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.mdm.api.IMdmLink; -import ca.uhn.fhir.mdm.api.MdmHistorySearchParameters; import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; import ca.uhn.fhir.mdm.api.MdmLinkWithRevision; import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; +import ca.uhn.fhir.mdm.api.params.MdmHistorySearchParameters; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.StringType; 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 55a5c9a42c6..8d44e7f1be1 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,7 @@ package ca.uhn.fhir.jpa.mdm.provider; -import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.i18n.Msg; +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.IMdmSettings; @@ -11,6 +11,7 @@ import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; import ca.uhn.fhir.mdm.util.MessageHelper; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; +import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.StringType; import org.junit.jupiter.api.AfterEach; @@ -147,10 +148,16 @@ public class MdmProviderUpdateLinkR4Test extends BaseLinkR4Test { } @Test - public void testUnlinkLink() { - myMdmProvider.updateLink(mySourcePatientId, myPatientId, NO_MATCH_RESULT, myRequestDetails); + public void testUnlinkLink_usingOutOfDateResourceId_throwsResourceVersionConflict() { + IBaseResource resultantPatient = myMdmProvider.updateLink(mySourcePatientId, myPatientId, NO_MATCH_RESULT, myRequestDetails); - materiallyChangeGoldenPatient(); + /* + * updatating a link to NO_MATCH reruns survivorship rules + * (thus rebuilding the golden resource; + * which in this case is mySourcePatient). + * Thus we don't have to update the patient again to get the + * version out of sync + */ try { myMdmProvider.updateLink(mySourcePatientId, myPatientId, MATCH_RESULT, myRequestDetails); 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 index 2ad12f37e86..8babd3d137a 100644 --- 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 @@ -12,7 +12,7 @@ import ca.uhn.fhir.mdm.api.IMdmControllerSvc; import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkJson; import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; -import ca.uhn.fhir.mdm.api.MdmQuerySearchParameters; +import ca.uhn.fhir.mdm.api.params.MdmQuerySearchParameters; import ca.uhn.fhir.mdm.api.paging.MdmPageRequest; import ca.uhn.fhir.mdm.batch2.clear.MdmClearStep; import ca.uhn.fhir.mdm.model.MdmTransactionContext; diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkQuerySvcImplSvcTest.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkQuerySvcImplSvcTest.java index 07776a47f2c..b43f43bb513 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkQuerySvcImplSvcTest.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkQuerySvcImplSvcTest.java @@ -4,7 +4,7 @@ import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.jpa.entity.MdmLink; import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test; import ca.uhn.fhir.mdm.api.IMdmLinkQuerySvc; -import ca.uhn.fhir.mdm.api.MdmHistorySearchParameters; +import ca.uhn.fhir.mdm.api.params.MdmHistorySearchParameters; import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkWithRevisionJson; 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 6131d911bbd..5000aa411be 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,11 +1,19 @@ 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.model.dao.JpaPid; import ca.uhn.fhir.mdm.api.IMdmLinkSvc; import ca.uhn.fhir.mdm.api.IMdmSurvivorshipService; +import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; +import ca.uhn.fhir.mdm.model.MdmCreateOrUpdateParams; import ca.uhn.fhir.mdm.model.MdmTransactionContext; import ca.uhn.fhir.mdm.util.GoldenResourceHelper; +import ca.uhn.fhir.parser.IParser; +import org.hl7.fhir.r4.model.Address; +import org.hl7.fhir.r4.model.ContactPoint; import org.hl7.fhir.r4.model.Patient; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.Captor; @@ -14,6 +22,13 @@ import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.mock.mockito.SpyBean; +import java.util.Arrays; +import java.util.Date; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.times; import static org.slf4j.LoggerFactory.getLogger; @@ -35,6 +50,15 @@ public class MdmMatchLinkSvcSurvivorshipTest extends BaseMdmR4Test { @Captor ArgumentCaptor myContext; + private IParser myParser; + + @BeforeEach + public void before() throws Exception { + super.before(); + + myParser = myFhirContext.newJsonParser(); + } + @Test public void testSurvivorshipIsCalledOnMatchingToTheSameGoldenResource() { // no candidates @@ -50,7 +74,87 @@ public class MdmMatchLinkSvcSurvivorshipTest extends BaseMdmR4Test { verifySurvivorshipCalled(3); } + @Test + public void testUpdateLinkToNoMatch_rebuildsGoldenResource() { + // setup + int resourceCount = 5; + + Address address = new Address(); + address.setCity("Toronto"); + address.addLine("123 fake st"); + + ContactPoint tele = new ContactPoint(); + tele.setSystem(ContactPoint.ContactPointSystem.PHONE); + tele.setValue("555-555-5555"); + + Date birthdate = new Date(); + + Patient r = null; + for (int i = 0; i < resourceCount; i++) { + // new patient with name + Patient p = buildJanePatient(); + p.addName() + .addGiven("Jane") + .setFamily("Doe"); + p.setTelecom(null); + p.setAddress(null); + p.setBirthDate(null); + + /* + * We will add some different values to + * the final resource we create. + * + * We do this only on the final one, because + * the default IMdmSurvivorship rules is to replace + * all of the current values in a golden resource + * with all of the new values from the newly added + * one (ie, overwriting non-null values with null). + */ + if (i == resourceCount - 1) { + p.setTelecom(Arrays.asList(tele)); + p.setAddress(Arrays.asList(address)); + p.setBirthDate(birthdate); + } + + r = createPatientAndUpdateLinks(p); + } + + Optional linkop = myMdmLinkDaoSvc.findMdmLinkBySource(r); + assertTrue(linkop.isPresent()); + MdmLink link = linkop.get(); + JpaPid gpid = link.getGoldenResourcePersistenceId(); + + Patient golden = myPatientDao.readByPid(gpid); + + // we should have a link for each resource all linked + // to the same golden resource + assertEquals(resourceCount, myMdmLinkDaoSvc.findMdmLinksByGoldenResource(golden).size()); + assertEquals(1, golden.getAddress().size()); + assertEquals(1, golden.getTelecom().size()); + assertEquals(r.getTelecom().get(0).getValue(), golden.getTelecom().get(0).getValue()); + + // test + // unmatch final link + MdmCreateOrUpdateParams params = new MdmCreateOrUpdateParams(); + params.setGoldenResourceId(golden.getId()); + params.setGoldenResource(golden); + params.setResourceId(r.getId()); + params.setSourceResource(r); + params.setMdmContext(createContextForCreate("Patient")); + params.setMatchResult(MdmMatchResultEnum.NO_MATCH); + golden = (Patient) myMdmLinkUpdaterSvc.updateLink(params); + + // verify + assertTrue(golden.getTelecom() == null || golden.getTelecom().isEmpty()); + assertNull(golden.getBirthDate()); + } + private void verifySurvivorshipCalled(int theNumberOfTimes) { - Mockito.verify(myMdmSurvivorshipService, times(theNumberOfTimes)).applySurvivorshipRulesToGoldenResource(myPatientCaptor.capture(), myPatientCaptor.capture(), myContext.capture()); + Mockito.verify(myMdmSurvivorshipService, times(theNumberOfTimes)) + .applySurvivorshipRulesToGoldenResource( + myPatientCaptor.capture(), + myPatientCaptor.capture(), + myContext.capture() + ); } } diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvcTest.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvcTest.java index 888a6ad91df..967ccf4969c 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvcTest.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvcTest.java @@ -7,13 +7,13 @@ import ca.uhn.fhir.jpa.entity.MdmLink; import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test; import ca.uhn.fhir.jpa.mdm.config.BaseTestMdmConfig; import ca.uhn.fhir.jpa.mdm.config.BlockListConfig; -import ca.uhn.fhir.jpa.mdm.helper.MdmLinkHelper; import ca.uhn.fhir.jpa.mdm.helper.testmodels.MDMState; import ca.uhn.fhir.jpa.model.dao.JpaPid; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.mdm.api.IMdmLink; import ca.uhn.fhir.mdm.api.IMdmLinkSvc; import ca.uhn.fhir.mdm.api.IMdmLinkUpdaterSvc; +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; @@ -91,10 +91,9 @@ public class MdmMatchLinkSvcTest { @Autowired private GoldenResourceHelper myGoldenResourceHelper; @Autowired - private IMdmLinkUpdaterSvc myMdmLinkUpdaterSvc; - + private IMdmSurvivorshipService myMdmSurvivorshipService; @Autowired - private MdmLinkHelper myLinkHelper; + private IMdmLinkUpdaterSvc myMdmLinkUpdaterSvc; @Test public void testAddPatientLinksToNewGoldenResourceIfNoneFound() { @@ -479,7 +478,11 @@ public class MdmMatchLinkSvcTest { //In a normal situation, janePatient2 would just match to jane patient, but here we need to hack it so they are their //own individual GoldenResource for the purpose of this test. - IAnyResource goldenResource = myGoldenResourceHelper.createGoldenResourceFromMdmSourceResource(janePatient2, new MdmTransactionContext(MdmTransactionContext.OperationType.CREATE_RESOURCE)); + IAnyResource goldenResource = myGoldenResourceHelper.createGoldenResourceFromMdmSourceResource( + janePatient2, + new MdmTransactionContext(MdmTransactionContext.OperationType.CREATE_RESOURCE), + myMdmSurvivorshipService + ); myMdmLinkSvc.updateLink(goldenResource, janePatient2, MdmMatchOutcome.NEW_GOLDEN_RESOURCE_MATCH, MdmLinkSourceEnum.AUTO, createContextForCreate("Patient")); assertThat(janePatient, is(not(sameGoldenResourceAs(janePatient2)))); @@ -593,7 +596,11 @@ public class MdmMatchLinkSvcTest { public void testCreateGoldenResourceFromMdmTarget() { // Create Use Case #2 - adding patient with no EID Patient janePatient = buildJanePatient(); - Patient janeGoldenResourcePatient = myGoldenResourceHelper.createGoldenResourceFromMdmSourceResource(janePatient, new MdmTransactionContext(MdmTransactionContext.OperationType.CREATE_RESOURCE)); + Patient janeGoldenResourcePatient = myGoldenResourceHelper.createGoldenResourceFromMdmSourceResource( + janePatient, + new MdmTransactionContext(MdmTransactionContext.OperationType.CREATE_RESOURCE), + myMdmSurvivorshipService + ); // golden record now contains HAPI-generated EID and HAPI tag assertTrue(MdmResourceUtil.isMdmManaged(janeGoldenResourcePatient)); @@ -769,8 +776,11 @@ public class MdmMatchLinkSvcTest { //In a normal situation, janePatient2 would just match to jane patient, but here we need to hack it so they are their //own individual GoldenResource for the purpose of this test. - IAnyResource goldenResource = myGoldenResourceHelper.createGoldenResourceFromMdmSourceResource(janePatient2, - new MdmTransactionContext(MdmTransactionContext.OperationType.CREATE_RESOURCE)); + IAnyResource goldenResource = myGoldenResourceHelper.createGoldenResourceFromMdmSourceResource( + janePatient2, + new MdmTransactionContext(MdmTransactionContext.OperationType.CREATE_RESOURCE), + myMdmSurvivorshipService + ); myMdmLinkSvc.updateLink(goldenResource, janePatient2, MdmMatchOutcome.NEW_GOLDEN_RESOURCE_MATCH, MdmLinkSourceEnum.AUTO, createContextForCreate("Patient")); assertThat(janePatient, is(not(sameGoldenResourceAs(janePatient2)))); diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmSurvivorshipSvcImplIT.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmSurvivorshipSvcImplIT.java new file mode 100644 index 00000000000..febcaf4c6e9 --- /dev/null +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmSurvivorshipSvcImplIT.java @@ -0,0 +1,58 @@ +package ca.uhn.fhir.jpa.mdm.svc; + +import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test; +import ca.uhn.fhir.mdm.api.IMdmSurvivorshipService; +import ca.uhn.fhir.mdm.model.MdmTransactionContext; +import org.hl7.fhir.r4.model.Patient; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class MdmSurvivorshipSvcImplIT extends BaseMdmR4Test { + + @Autowired + private IMdmSurvivorshipService myMdmSurvivorshipService; + + @Test + public void testRulesOnCreate() { + Patient p1 = buildFrankPatient(); + Patient p2 = new Patient(); + + myMdmSurvivorshipService.applySurvivorshipRulesToGoldenResource(p1, p2, new MdmTransactionContext(MdmTransactionContext.OperationType.CREATE_RESOURCE)); + + assertFalse(p2.hasIdElement()); + assertTrue(p2.getIdentifier().isEmpty()); + assertTrue(p2.getMeta().isEmpty()); + + assertTrue(p1.getNameFirstRep().equalsDeep(p2.getNameFirstRep())); + assertNull(p2.getBirthDate()); + assertEquals(p1.getTelecom().size(), p2.getTelecom().size()); + assertTrue(p2.getTelecomFirstRep().equalsDeep(p1.getTelecomFirstRep())); + } + + @Test + public void testRulesOnMerge() { + Patient p1 = buildFrankPatient(); + String p1Name = p1.getNameFirstRep().getNameAsSingleString(); + Patient p2 = buildPaulPatient(); + String p2Name = p2.getNameFirstRep().getNameAsSingleString(); + + myMdmSurvivorshipService.applySurvivorshipRulesToGoldenResource(p1, p2, new MdmTransactionContext(MdmTransactionContext.OperationType.MERGE_GOLDEN_RESOURCES)); + + assertFalse(p2.hasIdElement()); + assertFalse(p2.getIdentifier().isEmpty()); + assertTrue(p2.getMeta().isEmpty()); + + assertEquals(2, p2.getName().size()); + assertEquals(p2Name, p2.getName().get(0).getNameAsSingleString()); + assertEquals(p1Name, p2.getName().get(1).getNameAsSingleString()); + assertNull(p2.getBirthDate()); + + assertEquals(p1.getTelecom().size(), p1.getTelecom().size()); + assertTrue(p2.getTelecomFirstRep().equalsDeep(p1.getTelecomFirstRep())); + } +} diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmSurvivorshipSvcImplTest.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmSurvivorshipSvcImplTest.java index 1722a3e0b22..a6502687415 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmSurvivorshipSvcImplTest.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmSurvivorshipSvcImplTest.java @@ -1,59 +1,216 @@ package ca.uhn.fhir.jpa.mdm.svc; -import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test; -import ca.uhn.fhir.mdm.api.IMdmSurvivorshipService; +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.api.svc.IIdHelperService; +import ca.uhn.fhir.jpa.dao.index.IdHelperService; +import ca.uhn.fhir.jpa.entity.MdmLink; +import ca.uhn.fhir.jpa.model.dao.JpaPid; +import ca.uhn.fhir.mdm.api.IMdmLinkQuerySvc; +import ca.uhn.fhir.mdm.api.IMdmSettings; +import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; +import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; +import ca.uhn.fhir.mdm.model.CanonicalEID; import ca.uhn.fhir.mdm.model.MdmTransactionContext; +import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkJson; +import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkWithRevisionJson; +import ca.uhn.fhir.mdm.rules.json.MdmRulesJson; +import ca.uhn.fhir.mdm.svc.MdmSurvivorshipSvcImpl; +import ca.uhn.fhir.mdm.util.EIDHelper; +import ca.uhn.fhir.mdm.util.GoldenResourceHelper; +import ca.uhn.fhir.mdm.util.MdmPartitionHelper; +import ca.uhn.fhir.mdm.util.MdmResourceUtil; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.r4.model.Patient; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.data.domain.Page; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNull; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.jupiter.api.Assertions.assertNotEquals; +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.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; -class MdmSurvivorshipSvcImplTest extends BaseMdmR4Test { +@ExtendWith(MockitoExtension.class) +public class MdmSurvivorshipSvcImplTest { - @Autowired - private IMdmSurvivorshipService myMdmSurvivorshipService; + @Spy + private FhirContext myFhirContext = FhirContext.forR4Cached(); - @Test - public void testRulesOnCreate() { - Patient p1 = buildFrankPatient(); - Patient p2 = new Patient(); + @Mock + private DaoRegistry myDaoRegistry; - myMdmSurvivorshipService.applySurvivorshipRulesToGoldenResource(p1, p2, new MdmTransactionContext(MdmTransactionContext.OperationType.CREATE_RESOURCE)); + private GoldenResourceHelper myGoldenResourceHelper; - assertFalse(p2.hasIdElement()); - assertTrue(p2.getIdentifier().isEmpty()); - assertTrue(p2.getMeta().isEmpty()); + // mocks for our GoldenResourceHelper + @Mock + private IMdmSettings myMdmSettings; + @Mock + private EIDHelper myEIDHelper; + @Mock + private MdmPartitionHelper myMdmPartitionHelper; - assertTrue(p1.getNameFirstRep().equalsDeep(p2.getNameFirstRep())); - assertNull(p2.getBirthDate()); - assertEquals(p1.getTelecom().size(), p2.getTelecom().size()); - assertTrue(p2.getTelecomFirstRep().equalsDeep(p1.getTelecomFirstRep())); + @Spy + private IIdHelperService myIIdHelperService = new IdHelperService(); + + @Mock + private IMdmLinkQuerySvc myMdmLinkQuerySvc; + + private MdmSurvivorshipSvcImpl mySvc; + + @BeforeEach + public void before() { + myGoldenResourceHelper = spy(new GoldenResourceHelper( + myFhirContext, + myMdmSettings, + myEIDHelper, + myMdmPartitionHelper + )); + + mySvc = new MdmSurvivorshipSvcImpl( + myFhirContext, + myGoldenResourceHelper, + myDaoRegistry, + myMdmLinkQuerySvc, + myIIdHelperService + ); } + @SuppressWarnings({"rawtypes", "unchecked"}) @Test - public void testRulesOnMerge() { - Patient p1 = buildFrankPatient(); - String p1Name = p1.getNameFirstRep().getNameAsSingleString(); - Patient p2 = buildPaulPatient(); - String p2Name = p2.getNameFirstRep().getNameAsSingleString(); + public void rebuildGoldenResourceCurrentLinksUsingSurvivorshipRules_withManyLinks_rebuildsInUpdateOrder() { + // setup + // create resources + Patient goldenPatient = new Patient(); + goldenPatient.addAddress() + .setCity("Toronto") + .addLine("200 fake st"); + goldenPatient.addName() + .setFamily("Doe") + .addGiven("Jane"); + goldenPatient.setId("Patient/777"); + MdmResourceUtil.setMdmManaged(goldenPatient); + MdmResourceUtil.setGoldenResource(goldenPatient); - myMdmSurvivorshipService.applySurvivorshipRulesToGoldenResource(p1, p2, new MdmTransactionContext(MdmTransactionContext.OperationType.MERGE_GOLDEN_RESOURCES)); + List resources = new ArrayList<>(); + List links = new ArrayList<>(); + List linksWithRevisions = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + // we want our resources to be slightly different + Patient patient = new Patient(); + patient.addName() + .setFamily("Doe") + .addGiven("John" + i); + patient.addAddress() + .setCity("Toronto") + .addLine(String.format("11%d fake st", i)); + patient.addIdentifier() + .setSystem("http://example.com") + .setValue("Value" + i); + patient.setId("Patient/" + i); + resources.add(patient); - assertFalse(p2.hasIdElement()); - assertFalse(p2.getIdentifier().isEmpty()); - assertTrue(p2.getMeta().isEmpty()); + MdmLinkJson link = createLinkJson( + patient, + goldenPatient + ); + links.add(link); + linksWithRevisions.add(new MdmLinkWithRevisionJson( + link, + 1L, + Date.from( + Instant.now().minus(i, ChronoUnit.HOURS) + ) + )); + } - assertEquals(2, p2.getName().size()); - assertEquals(p2Name, p2.getName().get(0).getNameAsSingleString()); - assertEquals(p1Name, p2.getName().get(1).getNameAsSingleString()); - assertNull(p2.getBirthDate()); + IFhirResourceDao resourceDao = mock(IFhirResourceDao.class); - assertEquals(p1.getTelecom().size(), p1.getTelecom().size()); - assertTrue(p2.getTelecomFirstRep().equalsDeep(p1.getTelecomFirstRep())); + // when + when(myDaoRegistry.getResourceDao(eq("Patient"))) + .thenReturn(resourceDao); + AtomicInteger counter = new AtomicInteger(); + when(resourceDao.readByPid(any())) + .thenAnswer(params -> resources.get(counter.getAndIncrement())); + Page linkPage = mock(Page.class); + when(myMdmLinkQuerySvc.queryLinks(any(), any())) + .thenReturn(linkPage); + when(linkPage.get()) + .thenReturn(links.stream()); + when(myMdmSettings.getMdmRules()) + .thenReturn(new MdmRulesJson()); + // we will return a non-empty list to reduce mocking + when(myEIDHelper.getExternalEid(any())) + .thenReturn(Collections.singletonList(new CanonicalEID("example", "value", "use"))); + + // test + Patient goldenPatientRebuilt = mySvc.rebuildGoldenResourceWithSurvivorshipRules( + goldenPatient, + createTransactionContext() + ); + + // verify + assertNotNull(goldenPatientRebuilt); + // make sure it doesn't match the previous golden resource + assertNotEquals(goldenPatient, goldenPatientRebuilt); + assertNotEquals(goldenPatient.getName().get(0).getGiven(), goldenPatientRebuilt.getName().get(0).getGiven()); + assertNotEquals(goldenPatient.getAddress().get(0).getLine().get(0), goldenPatientRebuilt.getAddress().get(0).getLine().get(0)); + // make sure it's still a golden resource + assertTrue(MdmResourceUtil.isGoldenRecord(goldenPatientRebuilt)); + + verify(resourceDao) + .update(eq(goldenPatientRebuilt), any(RequestDetails.class)); } + private MdmLink createLinkWithoutUpdateDate(Patient theSource, Patient theGoldenResource) { + MdmLink link = new MdmLink(); + link.setCreated(Date.from( + Instant.now().minus(2, ChronoUnit.DAYS) + )); + link.setLinkSource(MdmLinkSourceEnum.AUTO); + link.setMatchResult(MdmMatchResultEnum.MATCH); + link.setSourcePersistenceId(JpaPid.fromId(theSource.getIdElement().getIdPartAsLong())); + link.setGoldenResourcePersistenceId(JpaPid.fromId(theGoldenResource.getIdElement().getIdPartAsLong())); + + return link; + } + + private MdmTransactionContext createTransactionContext() { + MdmTransactionContext context = new MdmTransactionContext(); + context.setRestOperation(MdmTransactionContext.OperationType.UPDATE_LINK); + context.setResourceType("Patient"); + return context; + } + + private MdmLinkJson createLinkJson( + IBaseResource theSource, + IBaseResource theGolden + ) { + MdmLinkJson linkJson = new MdmLinkJson(); + linkJson.setLinkSource(MdmLinkSourceEnum.AUTO); + linkJson.setGoldenResourceId(theGolden.getIdElement().getValueAsString()); + linkJson.setSourceId(theSource.getIdElement().getValueAsString()); + linkJson.setMatchResult(MdmMatchResultEnum.MATCH); + + return linkJson; + } } diff --git a/hapi-fhir-jpaserver-model/pom.xml b/hapi-fhir-jpaserver-model/pom.xml index c60443005ea..a724c6bebcd 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.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-searchparam/pom.xml b/hapi-fhir-jpaserver-searchparam/pom.xml index 1b3c46570c1..f56dfa29880 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.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-subscription/pom.xml b/hapi-fhir-jpaserver-subscription/pom.xml index de8c1b193e1..4e5963dff28 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.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-dstu2/pom.xml b/hapi-fhir-jpaserver-test-dstu2/pom.xml index 8152257189c..03e21455977 100644 --- a/hapi-fhir-jpaserver-test-dstu2/pom.xml +++ b/hapi-fhir-jpaserver-test-dstu2/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-dstu3/pom.xml b/hapi-fhir-jpaserver-test-dstu3/pom.xml index 68704330e01..207fc24c869 100644 --- a/hapi-fhir-jpaserver-test-dstu3/pom.xml +++ b/hapi-fhir-jpaserver-test-dstu3/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-r4/pom.xml b/hapi-fhir-jpaserver-test-r4/pom.xml index 1a4f04a10d0..bcab6510d7c 100644 --- a/hapi-fhir-jpaserver-test-r4/pom.xml +++ b/hapi-fhir-jpaserver-test-r4/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-r4b/pom.xml b/hapi-fhir-jpaserver-test-r4b/pom.xml index 85910c07036..d10ffebffab 100644 --- a/hapi-fhir-jpaserver-test-r4b/pom.xml +++ b/hapi-fhir-jpaserver-test-r4b/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-r5/pom.xml b/hapi-fhir-jpaserver-test-r5/pom.xml index 911cbf9ba1d..d95a57e0c26 100644 --- a/hapi-fhir-jpaserver-test-r5/pom.xml +++ b/hapi-fhir-jpaserver-test-r5/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-utilities/pom.xml b/hapi-fhir-jpaserver-test-utilities/pom.xml index ce0fc4e678b..6c8616951a7 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.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml index a5db4b565c9..cf521cd6e29 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.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-server-cds-hooks/pom.xml b/hapi-fhir-server-cds-hooks/pom.xml index e159337c7bf..be1e670e44f 100644 --- a/hapi-fhir-server-cds-hooks/pom.xml +++ b/hapi-fhir-server-cds-hooks/pom.xml @@ -7,7 +7,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server-mdm/pom.xml b/hapi-fhir-server-mdm/pom.xml index d79fb225fb5..7b6e763b18e 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.9.5-SNAPSHOT + 6.9.6-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 c76b6fa584c..dc6051daa13 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 @@ -20,6 +20,8 @@ package ca.uhn.fhir.mdm.api; import ca.uhn.fhir.mdm.api.paging.MdmPageRequest; +import ca.uhn.fhir.mdm.api.params.MdmHistorySearchParameters; +import ca.uhn.fhir.mdm.api.params.MdmQuerySearchParameters; import ca.uhn.fhir.mdm.model.MdmCreateOrUpdateParams; import ca.uhn.fhir.mdm.model.MdmMergeGoldenResourcesParams; import ca.uhn.fhir.mdm.model.MdmTransactionContext; 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 759e305f47e..e34df5f487c 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 @@ -20,6 +20,8 @@ package ca.uhn.fhir.mdm.api; import ca.uhn.fhir.mdm.api.paging.MdmPageRequest; +import ca.uhn.fhir.mdm.api.params.MdmHistorySearchParameters; +import ca.uhn.fhir.mdm.api.params.MdmQuerySearchParameters; import ca.uhn.fhir.mdm.model.MdmTransactionContext; import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkJson; import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkWithRevisionJson; diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmSurvivorshipService.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmSurvivorshipService.java index a0e23fd68b8..7168c9b9b57 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmSurvivorshipService.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmSurvivorshipService.java @@ -28,6 +28,10 @@ import org.hl7.fhir.instance.model.api.IBase; public interface IMdmSurvivorshipService { /** + * Merges two golden resources by overwriting all field values on theGoldenResource param for CREATE_RESOURCE, + * UPDATE_RESOURCE, SUBMIT_RESOURCE_TO_MDM, UPDATE_LINK (when setting to MATCH) and MANUAL_MERGE_GOLDEN_RESOURCES. + * PID, identifiers and meta values are not affected by this operation. + * * Applies survivorship rules to merge fields from the specified target resource to the golden resource. Survivorship * rules may include, but not limited to the following data consolidation methods: * @@ -60,4 +64,19 @@ public interface IMdmSurvivorshipService { */ void applySurvivorshipRulesToGoldenResource( T theTargetResource, T theGoldenResource, MdmTransactionContext theMdmTransactionContext); + + /** + * GoldenResources can have non-empty field data created from changes to the various + * resources that are matched to it (using some pre-defined survivorship rules). + * + * If a match link between a source and golden resource is broken, this method + * will rebuild/repopulate the GoldenResource based on the current links + * and current survivorship rules. + * + * @param theGoldenResource - the golden resource to rebuild + * @param theMdmTransactionContext - the transaction context + * @param - Resource type to apply the survivorship rules to + */ + T rebuildGoldenResourceWithSurvivorshipRules( + T theGoldenResource, MdmTransactionContext theMdmTransactionContext); } diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmHistorySearchParameters.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/MdmHistorySearchParameters.java similarity index 83% rename from hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmHistorySearchParameters.java rename to hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/MdmHistorySearchParameters.java index af1f8949779..43f4f59113e 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmHistorySearchParameters.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/MdmHistorySearchParameters.java @@ -1,4 +1,4 @@ -package ca.uhn.fhir.mdm.api; +package ca.uhn.fhir.mdm.api.params; /*- * #%L @@ -33,9 +33,24 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; public class MdmHistorySearchParameters { + + public enum JoinType { + AND, + OR + } + private List myGoldenResourceIds = new ArrayList<>(); private List mySourceIds = new ArrayList<>(); + /** + * When both myGoldenResourceIds and mySourceIds are provided, + * this parameter determines whether to 'and' or 'or' the query + * that fetches links by these values. + * + * Default (legacy) functionality is 'or'. + */ + private JoinType myParameterJoinType = JoinType.OR; + public MdmHistorySearchParameters() {} public List getGoldenResourceIds() { @@ -46,6 +61,10 @@ public class MdmHistorySearchParameters { return mySourceIds; } + public JoinType getParameterJoinType() { + return myParameterJoinType; + } + public MdmHistorySearchParameters setGoldenResourceIds(List theGoldenResourceIds) { myGoldenResourceIds = extractId(theGoldenResourceIds); return this; @@ -56,6 +75,11 @@ public class MdmHistorySearchParameters { return this; } + public MdmHistorySearchParameters setJoinType(JoinType theJoinType) { + myParameterJoinType = theJoinType; + return this; + } + @Override public boolean equals(Object theO) { if (this == theO) return true; diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmQuerySearchParameters.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/MdmQuerySearchParameters.java similarity index 98% rename from hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmQuerySearchParameters.java rename to hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/MdmQuerySearchParameters.java index 719c9ff3eb7..6deeb0c2d46 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmQuerySearchParameters.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/params/MdmQuerySearchParameters.java @@ -17,9 +17,11 @@ * limitations under the License. * #L% */ -package ca.uhn.fhir.mdm.api; +package ca.uhn.fhir.mdm.api.params; import ca.uhn.fhir.i18n.Msg; +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.provider.MdmControllerUtil; import ca.uhn.fhir.rest.api.SortOrderEnum; diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/dao/IMdmLinkDao.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/dao/IMdmLinkDao.java index 74741f47b28..b5a64aac33b 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/dao/IMdmLinkDao.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/dao/IMdmLinkDao.java @@ -21,12 +21,12 @@ package ca.uhn.fhir.mdm.dao; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.mdm.api.IMdmLink; -import ca.uhn.fhir.mdm.api.MdmHistorySearchParameters; import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; import ca.uhn.fhir.mdm.api.MdmLinkWithRevision; import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; -import ca.uhn.fhir.mdm.api.MdmQuerySearchParameters; import ca.uhn.fhir.mdm.api.paging.MdmPageRequest; +import ca.uhn.fhir.mdm.api.params.MdmHistorySearchParameters; +import ca.uhn.fhir.mdm.api.params.MdmQuerySearchParameters; import ca.uhn.fhir.mdm.model.MdmPidTuple; import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/MdmLinkHistoryProviderDstu3Plus.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/MdmLinkHistoryProviderDstu3Plus.java index eac89e903d0..6670e1334a5 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/MdmLinkHistoryProviderDstu3Plus.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/MdmLinkHistoryProviderDstu3Plus.java @@ -25,7 +25,7 @@ import ca.uhn.fhir.interceptor.api.HookParams; import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.mdm.api.IMdmControllerSvc; -import ca.uhn.fhir.mdm.api.MdmHistorySearchParameters; +import ca.uhn.fhir.mdm.api.params.MdmHistorySearchParameters; import ca.uhn.fhir.mdm.model.mdmevents.MdmHistoryEvent; import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkWithRevisionJson; import ca.uhn.fhir.rest.annotation.Operation; 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 dc07039fae2..f62427e46d2 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 @@ -28,8 +28,8 @@ import ca.uhn.fhir.mdm.api.IMdmControllerSvc; import ca.uhn.fhir.mdm.api.IMdmSettings; import ca.uhn.fhir.mdm.api.IMdmSubmitSvc; import ca.uhn.fhir.mdm.api.MdmConstants; -import ca.uhn.fhir.mdm.api.MdmQuerySearchParameters; import ca.uhn.fhir.mdm.api.paging.MdmPageRequest; +import ca.uhn.fhir.mdm.api.params.MdmQuerySearchParameters; import ca.uhn.fhir.mdm.model.MdmCreateOrUpdateParams; import ca.uhn.fhir.mdm.model.MdmMergeGoldenResourcesParams; import ca.uhn.fhir.mdm.model.MdmTransactionContext; diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/svc/MdmSurvivorshipSvcImpl.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/svc/MdmSurvivorshipSvcImpl.java new file mode 100644 index 00000000000..23c299736f3 --- /dev/null +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/svc/MdmSurvivorshipSvcImpl.java @@ -0,0 +1,148 @@ +/*- + * #%L + * HAPI FHIR JPA Server - Master Data Management + * %% + * Copyright (C) 2014 - 2023 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.mdm.svc; + +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.api.svc.IIdHelperService; +import ca.uhn.fhir.mdm.api.IMdmLinkQuerySvc; +import ca.uhn.fhir.mdm.api.IMdmSurvivorshipService; +import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; +import ca.uhn.fhir.mdm.api.paging.MdmPageRequest; +import ca.uhn.fhir.mdm.api.params.MdmQuerySearchParameters; +import ca.uhn.fhir.mdm.model.MdmTransactionContext; +import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkJson; +import ca.uhn.fhir.mdm.util.GoldenResourceHelper; +import ca.uhn.fhir.rest.api.server.SystemRequestDetails; +import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; +import ca.uhn.fhir.util.TerserUtil; +import org.hl7.fhir.instance.model.api.IAnyResource; +import org.hl7.fhir.instance.model.api.IBase; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.springframework.data.domain.Page; + +import java.util.stream.Stream; + +public class MdmSurvivorshipSvcImpl implements IMdmSurvivorshipService { + + protected final FhirContext myFhirContext; + + private final GoldenResourceHelper myGoldenResourceHelper; + + private final DaoRegistry myDaoRegistry; + private final IMdmLinkQuerySvc myMdmLinkQuerySvc; + + private final IIdHelperService myIIdHelperService; + + public MdmSurvivorshipSvcImpl( + FhirContext theFhirContext, + GoldenResourceHelper theResourceHelper, + DaoRegistry theDaoRegistry, + IMdmLinkQuerySvc theLinkQuerySvc, + IIdHelperService theIIdHelperService) { + myFhirContext = theFhirContext; + myGoldenResourceHelper = theResourceHelper; + myDaoRegistry = theDaoRegistry; + myMdmLinkQuerySvc = theLinkQuerySvc; + myIIdHelperService = theIIdHelperService; + } + + // this logic is custom in smile vs hapi + @Override + public void applySurvivorshipRulesToGoldenResource( + T theTargetResource, T theGoldenResource, MdmTransactionContext theMdmTransactionContext) { + switch (theMdmTransactionContext.getRestOperation()) { + case MERGE_GOLDEN_RESOURCES: + TerserUtil.mergeFields( + myFhirContext, + (IBaseResource) theTargetResource, + (IBaseResource) theGoldenResource, + TerserUtil.EXCLUDE_IDS_AND_META); + break; + default: + TerserUtil.replaceFields( + myFhirContext, + (IBaseResource) theTargetResource, + (IBaseResource) theGoldenResource, + TerserUtil.EXCLUDE_IDS_AND_META); + break; + } + } + + // This logic is the same for all implementations (including jpa or mongo) + @SuppressWarnings({"rawtypes", "unchecked"}) + @Override + public T rebuildGoldenResourceWithSurvivorshipRules( + T theGoldenResourceBase, MdmTransactionContext theMdmTransactionContext) { + IBaseResource goldenResource = (IBaseResource) theGoldenResourceBase; + + // we want a list of source ids linked to this + // golden resource id; sorted and filtered for only MATCH results + Stream sourceResources = + getMatchedSourceIdsByLinkUpdateDate(goldenResource, theMdmTransactionContext); + + IBaseResource toSave = myGoldenResourceHelper.createGoldenResourceFromMdmSourceResource( + (IAnyResource) goldenResource, + theMdmTransactionContext, + null // we don't want to apply survivorship - just create a new GoldenResource + ); + + toSave.setId(goldenResource.getIdElement().toUnqualifiedVersionless()); + + sourceResources.forEach(source -> { + applySurvivorshipRulesToGoldenResource(source, toSave, theMdmTransactionContext); + }); + + // save it + IFhirResourceDao dao = myDaoRegistry.getResourceDao(goldenResource.fhirType()); + dao.update(toSave, new SystemRequestDetails()); + + return (T) toSave; + } + + private Stream getMatchedSourceIdsByLinkUpdateDate( + IBaseResource theGoldenResource, MdmTransactionContext theMdmTransactionContext) { + String resourceType = theGoldenResource.fhirType(); + IFhirResourceDao dao = myDaoRegistry.getResourceDao(resourceType); + + MdmQuerySearchParameters searchParameters = new MdmQuerySearchParameters(new MdmPageRequest(0, 50, 50, 50)); + searchParameters.setGoldenResourceId(theGoldenResource.getIdElement()); + searchParameters.setSort("myUpdated"); + searchParameters.setMatchResult(MdmMatchResultEnum.MATCH); + Page linksQuery = myMdmLinkQuerySvc.queryLinks(searchParameters, theMdmTransactionContext); + + return linksQuery.get().map(link -> { + String sourceId = link.getSourceId(); + + // +1 because of "/" in id: "ResourceType/Id" + IResourcePersistentId pid = getResourcePID(sourceId.substring(resourceType.length() + 1), resourceType); + + // this might be a bit unperformant + // but it depends how many links there are + // per golden resource (unlikely to be thousands) + return dao.readByPid(pid); + }); + } + + private IResourcePersistentId getResourcePID(String theId, String theResourceType) { + return myIIdHelperService.newPidFromStringIdAndResourceName(theId, theResourceType); + } +} diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/EIDHelper.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/EIDHelper.java index 0e8a7e7c0c8..fd03392b9ec 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/EIDHelper.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/EIDHelper.java @@ -34,7 +34,7 @@ import java.util.UUID; import java.util.stream.Collectors; @Service -public final class EIDHelper { +public class EIDHelper { private final FhirContext myFhirContext; private final IMdmSettings myMdmSettings; 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 3c228ba7830..d133a7a9605 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 @@ -58,23 +58,24 @@ public class GoldenResourceHelper { static final String FIELD_NAME_IDENTIFIER = "identifier"; - @Autowired - private IMdmSettings myMdmSettings; + private final IMdmSettings myMdmSettings; - @Autowired - private EIDHelper myEIDHelper; + private final EIDHelper myEIDHelper; - @Autowired - private IMdmSurvivorshipService myMdmSurvivorshipService; - - @Autowired - private MdmPartitionHelper myMdmPartitionHelper; + private final MdmPartitionHelper myMdmPartitionHelper; private final FhirContext myFhirContext; @Autowired - public GoldenResourceHelper(FhirContext theFhirContext) { + public GoldenResourceHelper( + FhirContext theFhirContext, + IMdmSettings theMdmSettings, + EIDHelper theEIDHelper, + MdmPartitionHelper theMdmPartitionHelper) { myFhirContext = theFhirContext; + myMdmSettings = theMdmSettings; + myEIDHelper = theEIDHelper; + myMdmPartitionHelper = theMdmPartitionHelper; } /** @@ -82,20 +83,28 @@ public class GoldenResourceHelper { * a randomly generated UUID EID will be created. * * @param Supported MDM resource type (e.g. Patient, Practitioner) - * @param theIncomingResource The resource that will be used as the starting point for the MDM linking. - * @param theMdmTransactionContext + * @param theIncomingResource The resource to build the golden resource off of. + * Could be the source resource or another golden resource. + * If a golden resource, do not provide an IMdmSurvivorshipService + * @param theMdmTransactionContext The mdm transaction context + * @param theMdmSurvivorshipService IMdmSurvivorshipSvc. Provide only if survivorshipskills are desired + * to be applied. Provide null otherwise. */ @Nonnull public T createGoldenResourceFromMdmSourceResource( - T theIncomingResource, MdmTransactionContext theMdmTransactionContext) { + T theIncomingResource, + MdmTransactionContext theMdmTransactionContext, + IMdmSurvivorshipService theMdmSurvivorshipService) { validateContextSupported(); // get a ref to the actual ID Field RuntimeResourceDefinition resourceDefinition = myFhirContext.getResourceDefinition(theIncomingResource); IBaseResource newGoldenResource = resourceDefinition.newInstance(); - myMdmSurvivorshipService.applySurvivorshipRulesToGoldenResource( - theIncomingResource, newGoldenResource, theMdmTransactionContext); + if (theMdmSurvivorshipService != null) { + theMdmSurvivorshipService.applySurvivorshipRulesToGoldenResource( + theIncomingResource, newGoldenResource, theMdmTransactionContext); + } // hapi has 2 metamodels: for children and types BaseRuntimeChildDefinition goldenResourceIdentifier = resourceDefinition.getChildByName(FIELD_NAME_IDENTIFIER); @@ -322,14 +331,6 @@ public class GoldenResourceHelper { myFhirContext, theFromGoldenResource, theToGoldenResource, FIELD_NAME_IDENTIFIER); } - public void mergeNonIdentiferFields( - IBaseResource theFromGoldenResource, - IBaseResource theToGoldenResource, - MdmTransactionContext theMdmTransactionContext) { - myMdmSurvivorshipService.applySurvivorshipRulesToGoldenResource( - theFromGoldenResource, theToGoldenResource, theMdmTransactionContext); - } - /** * An incoming resource is a potential duplicate if it matches a source that has a golden resource with an official * EID, but the incoming resource also has an EID that does not match. diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/MdmResourceUtil.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/MdmResourceUtil.java index 99931f58e57..44f34ea094f 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/MdmResourceUtil.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/util/MdmResourceUtil.java @@ -118,6 +118,12 @@ public final class MdmResourceUtil { MdmConstants.DISPLAY_GOLDEN_RECORD); } + /** + * Sets the provided resource as 'redirected' golden resource. + * This is done when a Golden Resource has been deprecated + * and is no longer the primary golden resource (for example, + * after a merge of 2 golden resources). + */ public static IBaseResource setGoldenResourceRedirected(IBaseResource theBaseResource) { return setTagOnResource( theBaseResource, diff --git a/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/api/MdmQuerySearchParametersTest.java b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/api/MdmQuerySearchParametersTest.java index cd9083a96f5..301011c9b5c 100644 --- a/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/api/MdmQuerySearchParametersTest.java +++ b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/api/MdmQuerySearchParametersTest.java @@ -1,6 +1,7 @@ package ca.uhn.fhir.mdm.api; import ca.uhn.fhir.mdm.api.paging.MdmPageRequest; +import ca.uhn.fhir.mdm.api.params.MdmQuerySearchParameters; import org.junit.jupiter.api.Test; import java.util.Collections; diff --git a/hapi-fhir-server-openapi/pom.xml b/hapi-fhir-server-openapi/pom.xml index f74d97185dc..786cb0cbc08 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.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server/pom.xml b/hapi-fhir-server/pom.xml index 0172c2e355b..548f1c89344 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.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml index 3d816c267e3..1c4ba4f7abb 100644 --- a/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml +++ b/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml @@ -7,7 +7,7 @@ hapi-fhir-serviceloaders ca.uhn.hapi.fhir - 6.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-caffeine/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-caffeine/pom.xml index 0b688dca3ca..e12e860112c 100644 --- a/hapi-fhir-serviceloaders/hapi-fhir-caching-caffeine/pom.xml +++ b/hapi-fhir-serviceloaders/hapi-fhir-caching-caffeine/pom.xml @@ -7,7 +7,7 @@ hapi-fhir-serviceloaders ca.uhn.hapi.fhir - 6.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../pom.xml @@ -21,7 +21,7 @@ ca.uhn.hapi.fhir hapi-fhir-caching-api - 6.9.5-SNAPSHOT + 6.9.6-SNAPSHOT diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml index efc25c7736f..1fecff814ed 100644 --- a/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml +++ b/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml @@ -7,7 +7,7 @@ hapi-fhir-serviceloaders ca.uhn.hapi.fhir - 6.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-testing/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-testing/pom.xml index 8f5f87754c6..943dd0a0ae8 100644 --- a/hapi-fhir-serviceloaders/hapi-fhir-caching-testing/pom.xml +++ b/hapi-fhir-serviceloaders/hapi-fhir-caching-testing/pom.xml @@ -7,7 +7,7 @@ hapi-fhir ca.uhn.hapi.fhir - 6.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../../pom.xml diff --git a/hapi-fhir-serviceloaders/pom.xml b/hapi-fhir-serviceloaders/pom.xml index 41b115a65ef..b565b77d00a 100644 --- a/hapi-fhir-serviceloaders/pom.xml +++ b/hapi-fhir-serviceloaders/pom.xml @@ -5,7 +5,7 @@ hapi-deployable-pom ca.uhn.hapi.fhir - 6.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml index 5fc19f89b07..108aff5c5ea 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.9.5-SNAPSHOT + 6.9.6-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 fa9ccc40e2d..c1970bb0492 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.9.5-SNAPSHOT + 6.9.6-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 30525c48f24..50522df7acf 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.9.5-SNAPSHOT + 6.9.6-SNAPSHOT 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 a127e93848f..a1e20c2b17b 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.9.5-SNAPSHOT + 6.9.6-SNAPSHOT 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 ccab356f3e7..70522e2b08d 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.9.5-SNAPSHOT + 6.9.6-SNAPSHOT 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 fd564849a6b..a2c349642c0 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.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/pom.xml b/hapi-fhir-spring-boot/pom.xml index b83d25dee59..28d995ffe78 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.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-sql-migrate/pom.xml b/hapi-fhir-sql-migrate/pom.xml index 88c2390c2b3..29063cd2a8b 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.9.5-SNAPSHOT + 6.9.6-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 370936524fb..4acd856c343 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.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-batch2-test-utilities/pom.xml b/hapi-fhir-storage-batch2-test-utilities/pom.xml index 2505145eed9..e80aa46e9e2 100644 --- a/hapi-fhir-storage-batch2-test-utilities/pom.xml +++ b/hapi-fhir-storage-batch2-test-utilities/pom.xml @@ -7,7 +7,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-batch2/pom.xml b/hapi-fhir-storage-batch2/pom.xml index c73b869ffbe..f9f63f922d2 100644 --- a/hapi-fhir-storage-batch2/pom.xml +++ b/hapi-fhir-storage-batch2/pom.xml @@ -7,7 +7,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-cr/pom.xml b/hapi-fhir-storage-cr/pom.xml index d548d96cc2d..e1b375c2d43 100644 --- a/hapi-fhir-storage-cr/pom.xml +++ b/hapi-fhir-storage-cr/pom.xml @@ -7,7 +7,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-mdm/pom.xml b/hapi-fhir-storage-mdm/pom.xml index a0fde1d87a8..5a2675f3c52 100644 --- a/hapi-fhir-storage-mdm/pom.xml +++ b/hapi-fhir-storage-mdm/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-test-utilities/pom.xml b/hapi-fhir-storage-test-utilities/pom.xml index d949b23a66c..1e179fa7b02 100644 --- a/hapi-fhir-storage-test-utilities/pom.xml +++ b/hapi-fhir-storage-test-utilities/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage/pom.xml b/hapi-fhir-storage/pom.xml index 6d57ead552a..059018d56d8 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.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu2.1/pom.xml b/hapi-fhir-structures-dstu2.1/pom.xml index 79339021e37..f858d6fed23 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.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu2/pom.xml b/hapi-fhir-structures-dstu2/pom.xml index df4bb882f1b..e2c99577549 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.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu3/pom.xml b/hapi-fhir-structures-dstu3/pom.xml index 4c470f8d8ea..83d18205f1c 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.9.5-SNAPSHOT + 6.9.6-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 05ea90a1c52..bf1ab0c9c87 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.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r4/pom.xml b/hapi-fhir-structures-r4/pom.xml index da28c5ab85a..1d2311afaaa 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.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r4b/pom.xml b/hapi-fhir-structures-r4b/pom.xml index a002e1602d6..6a9896c2d27 100644 --- a/hapi-fhir-structures-r4b/pom.xml +++ b/hapi-fhir-structures-r4b/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r5/pom.xml b/hapi-fhir-structures-r5/pom.xml index f08634de45b..1af7a34ad04 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.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-test-utilities/pom.xml b/hapi-fhir-test-utilities/pom.xml index 5fcb91ba439..0e5897fc632 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.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/models/TestResource.java b/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/models/TestResource.java new file mode 100644 index 00000000000..46b8604951a --- /dev/null +++ b/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/models/TestResource.java @@ -0,0 +1,44 @@ +package ca.uhn.fhir.models; + +import org.springframework.core.io.AbstractResource; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.core.io.Resource; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.nio.charset.StandardCharsets; + +/** + * This is a test object that can be used to wrap a string + * that could be injected as a spring-resource at bootup. + */ +public class TestResource extends AbstractResource { + + public static Resource createFromString(String theTxt) { + TestResource resource = new TestResource(new ByteArrayResource(theTxt.getBytes(StandardCharsets.UTF_8))); + + return resource; + } + + private Resource myResource; + + private TestResource(Resource theResource) { + myResource = theResource; + } + + @Override + public String getDescription() { + return getClass().getName(); + } + + @Override + public InputStream getInputStream() throws IOException { + return myResource.getInputStream(); + } + + @Override + public URL getURL() throws IOException { + return new URL("http://example.com/test"); + } +} diff --git a/hapi-fhir-testpage-overlay/pom.xml b/hapi-fhir-testpage-overlay/pom.xml index cbb4afd86cd..474d44c436e 100644 --- a/hapi-fhir-testpage-overlay/pom.xml +++ b/hapi-fhir-testpage-overlay/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-validation-resources-dstu2.1/pom.xml b/hapi-fhir-validation-resources-dstu2.1/pom.xml index 1cf5f440658..a2515ca2af1 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.9.5-SNAPSHOT + 6.9.6-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 7e96eabc21a..e96002a6247 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.9.5-SNAPSHOT + 6.9.6-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 9e049600d1d..42462bbf8c9 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.9.5-SNAPSHOT + 6.9.6-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 f0c135351f3..339d81d41d5 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.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-r4b/pom.xml b/hapi-fhir-validation-resources-r4b/pom.xml index 7fd8a0ca6af..c3d48c138e7 100644 --- a/hapi-fhir-validation-resources-r4b/pom.xml +++ b/hapi-fhir-validation-resources-r4b/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 6.9.5-SNAPSHOT + 6.9.6-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 e7c700380d0..ec955eb83ac 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.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation/pom.xml b/hapi-fhir-validation/pom.xml index 69141b45152..c7123aee26d 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.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-tinder-plugin/pom.xml b/hapi-tinder-plugin/pom.xml index 547437e33a4..429bb73c6f1 100644 --- a/hapi-tinder-plugin/pom.xml +++ b/hapi-tinder-plugin/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../pom.xml diff --git a/hapi-tinder-test/pom.xml b/hapi-tinder-test/pom.xml index 9d738b88142..fc398f2f2f0 100644 --- a/hapi-tinder-test/pom.xml +++ b/hapi-tinder-test/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index cf8e3681a0d..3761f1615d0 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ ca.uhn.hapi.fhir hapi-fhir pom - 6.9.5-SNAPSHOT + 6.9.6-SNAPSHOT HAPI-FHIR An open-source implementation of the FHIR specification in Java. diff --git a/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml b/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml index 1af96dead48..6d2b6555908 100644 --- a/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml +++ b/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml @@ -7,7 +7,7 @@ ca.uhn.hapi.fhir hapi-fhir - 6.9.5-SNAPSHOT + 6.9.6-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 a5e51321d18..6b2482d9327 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.9.5-SNAPSHOT + 6.9.6-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 09648a0442a..3202d94f5ba 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.9.5-SNAPSHOT + 6.9.6-SNAPSHOT ../../pom.xml