rebuild golden resource from survivorship on link edit (#5250)
* adding rebuilding option for survivorship * rebuild survivorship resources * cleanup * configuring beans * fixing tests * cleanup * spotless * import fixing * cleaning up * fixing tests * fixing tests * version bump * undo change --------- Co-authored-by: leif stawnyczy <leifstawnyczy@leifs-MacBook-Pro.local> Co-authored-by: leif stawnyczy <leifstawnyczy@leifs-mbp.home>
This commit is contained in:
parent
2da8aafad0
commit
f97eadadc6
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-bom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<packaging>pom</packaging>
|
||||
<name>HAPI FHIR BOM</name>
|
||||
|
@ -12,7 +12,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-cli</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -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.
|
||||
"
|
|
@ -11,7 +11,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -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<JpaPid, MdmLink> mdmLinkDao() {
|
||||
return new MdmLinkDaoJpaImpl();
|
||||
}
|
||||
|
||||
@Bean
|
||||
IMdmLinkImplFactory<MdmLink> mdmLinkImplFactory() {
|
||||
return new JpaMdmLinkImplFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Scope("prototype")
|
||||
public PersistenceContextProvider persistenceContextProvider() {
|
||||
|
|
|
@ -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<JpaPid, MdmLink> mdmLinkDao() {
|
||||
return new MdmLinkDaoJpaImpl();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IMdmLinkImplFactory<MdmLink> mdmLinkImplFactory() {
|
||||
return new JpaMdmLinkImplFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IMdmClearHelperSvc<JpaPid> helperSvc(IDeleteExpungeSvc<JpaPid> theDeleteExpungeSvc) {
|
||||
return new MdmClearHelperSvcImpl(theDeleteExpungeSvc);
|
||||
}
|
||||
}
|
|
@ -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<JpaPid, MdmLink> {
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(MdmLinkDaoJpaImpl.class);
|
||||
|
@ -380,7 +380,15 @@ public class MdmLinkDaoJpaImpl implements IMdmLinkDao<JpaPid, MdmLink> {
|
|||
|
||||
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()) {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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<P extends IResourcePersistentId, M extends IMdmLink<P
|
|||
Example<M> 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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 <T>
|
||||
*/
|
||||
@Override
|
||||
public <T extends IBase> 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<MdmTransactionContext> 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<MdmLink> 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()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))));
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
}
|
|
@ -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<IBaseResource> resources = new ArrayList<>();
|
||||
List<MdmLinkJson> links = new ArrayList<>();
|
||||
List<MdmLinkWithRevisionJson> 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<MdmLinkJson> 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
|||
*/
|
||||
<T extends IBase> 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 <T> - Resource type to apply the survivorship rules to
|
||||
*/
|
||||
<T extends IBase> T rebuildGoldenResourceWithSurvivorshipRules(
|
||||
T theGoldenResource, MdmTransactionContext theMdmTransactionContext);
|
||||
}
|
||||
|
|
|
@ -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<IIdType> myGoldenResourceIds = new ArrayList<>();
|
||||
private List<IIdType> 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<IIdType> getGoldenResourceIds() {
|
||||
|
@ -46,6 +61,10 @@ public class MdmHistorySearchParameters {
|
|||
return mySourceIds;
|
||||
}
|
||||
|
||||
public JoinType getParameterJoinType() {
|
||||
return myParameterJoinType;
|
||||
}
|
||||
|
||||
public MdmHistorySearchParameters setGoldenResourceIds(List<String> 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;
|
|
@ -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;
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 <T extends IBase> 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 extends IBase> 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<IBaseResource> 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<IBaseResource> 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<MdmLinkJson> 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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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 <T> 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 extends IAnyResource> 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.
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<parent>
|
||||
<artifactId>hapi-fhir-serviceloaders</artifactId>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<parent>
|
||||
<artifactId>hapi-fhir-serviceloaders</artifactId>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
@ -21,7 +21,7 @@
|
|||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-caching-api</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<parent>
|
||||
<artifactId>hapi-fhir-serviceloaders</artifactId>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<parent>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hapi-fhir-spring-boot-sample-client-apache</artifactId>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-spring-boot</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
</parent>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-deployable-pom</artifactId>
|
||||
<version>6.9.5-SNAPSHOT</version>
|
||||
<version>6.9.6-SNAPSHOT</version>
|
||||
|
||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||
</parent>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue