remove references to EMPI

This commit is contained in:
Tadgh 2020-11-18 19:50:00 -05:00
parent 805d5b1d6c
commit 75c0c25d7b
194 changed files with 3987 additions and 4070 deletions

View File

@ -1703,9 +1703,9 @@ public enum Pointcut {
),
/**
* <b>EMPI Hook:</b>
* Invoked whenever a persisted Patient/Practitioner resource (a resource that has just been stored in the
* database via a create/update/patch/etc.) has been matched against related resources and EMPI links have been updated.
* <b>MDM(EMPI) Hook:</b>
* Invoked whenever a persisted resource (a resource that has just been stored in the
* database via a create/update/patch/etc.) has been matched against related resources and MDM links have been updated.
* <p>
* Hooks may accept the following parameters:
* <ul>
@ -1717,7 +1717,7 @@ public enum Pointcut {
* Hooks should return <code>void</code>.
* </p>
*/
EMPI_AFTER_PERSISTED_RESOURCE_CHECKED(void.class, "ca.uhn.fhir.rest.server.messaging.ResourceOperationMessage", "ca.uhn.fhir.rest.server.TransactionLogMessages"),
MDM_AFTER_PERSISTED_RESOURCE_CHECKED(void.class, "ca.uhn.fhir.rest.server.messaging.ResourceOperationMessage", "ca.uhn.fhir.rest.server.TransactionLogMessages"),
/**
* <b>Performance Tracing Hook:</b>

View File

@ -38,7 +38,7 @@
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>hapi-fhir-server-empi</artifactId>
<artifactId>hapi-fhir-server-mdm</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
@ -103,7 +103,7 @@
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>hapi-fhir-jpaserver-empi</artifactId>
<artifactId>hapi-fhir-jpaserver-mdm</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>

View File

@ -30,6 +30,6 @@ HAPI EMPI keeps track of which links were automatically established vs manually
Follow these steps to enable EMPI on the server:
The [EmpiSettings](/hapi-fhir/apidocs/hapi-fhir-server-empi/ca/uhn/fhir/empi/rules/config/EmpiSettings.html) bean contains configuration settings related to EMPI within the server. To enable EMPI, the [setEnabled(boolean)](/hapi-fhir/apidocs/hapi-fhir-server-empi/ca/uhn/fhir/empi/rules/config/EmpiSettings.html#setEnabled(boolean)) property should be enabled.
The [EmpiSettings](/hapi-fhir/apidocs/hapi-fhir-server-mdm/ca/uhn/fhir/empi/rules/config/EmpiSettings.html) bean contains configuration settings related to EMPI within the server. To enable EMPI, the [setEnabled(boolean)](/hapi-fhir/apidocs/hapi-fhir-server-mdm/ca/uhn/fhir/empi/rules/config/EmpiSettings.html#setEnabled(boolean)) property should be enabled.
See [EMPI EID Settings](/hapi-fhir/docs/server_jpa_empi/empi_eid.html#empi-eid-settings) for a description of the EID-related settings.

View File

@ -4,11 +4,11 @@ An Enterprise Identifier(EID) is a unique identifier that can be attached to Pat
## EMPI EID Settings
The [EmpiSettings](/hapi-fhir/apidocs/hapi-fhir-server-empi/ca/uhn/fhir/empi/rules/config/EmpiSettings.html) bean contains two EID related settings. Both are enabled by default.
The [EmpiSettings](/hapi-fhir/apidocs/hapi-fhir-server-mdm/ca/uhn/fhir/empi/rules/config/EmpiSettings.html) bean contains two EID related settings. Both are enabled by default.
* **Prevent EID Updates** ([JavaDoc](/hapi-fhir/apidocs/hapi-fhir-server-empi/ca/uhn/fhir/empi/rules/config/EmpiSettings.html#setPreventEidUpdates(boolean))): If this is enabled, then once an EID is set on a resource, it cannot be changed. If disabled, patients may have their EID updated.
* **Prevent EID Updates** ([JavaDoc](/hapi-fhir/apidocs/hapi-fhir-server-mdm/ca/uhn/fhir/empi/rules/config/EmpiSettings.html#setPreventEidUpdates(boolean))): If this is enabled, then once an EID is set on a resource, it cannot be changed. If disabled, patients may have their EID updated.
* **Prevent multiple EIDs**: ([JavaDoc](/hapi-fhir/apidocs/hapi-fhir-server-empi/ca/uhn/fhir/empi/rules/config/EmpiSettings.html#setPreventMultipleEids(boolean))): If this is enabled, then a resource cannot have more than one EID, and incoming resources that break this rule will be rejected.
* **Prevent multiple EIDs**: ([JavaDoc](/hapi-fhir/apidocs/hapi-fhir-server-mdm/ca/uhn/fhir/empi/rules/config/EmpiSettings.html#setPreventMultipleEids(boolean))): If this is enabled, then a resource cannot have more than one EID, and incoming resources that break this rule will be rejected.
## EMPI EID Scenarios

View File

@ -1,6 +1,6 @@
# EMPI Operations
EMPI links are managed by EMPI Operations. These operations are supplied by a [plain provider](/docs/server_plain/resource_providers.html#plain-providers) called [EmpiProvider](/hapi-fhir/apidocs/hapi-fhir-server-empi/ca/uhn/fhir/empi/provider/EmpiProviderR4.html).
EMPI links are managed by EMPI Operations. These operations are supplied by a [plain provider](/docs/server_plain/resource_providers.html#plain-providers) called [EmpiProvider](/hapi-fhir/apidocs/hapi-fhir-server-mdm/ca/uhn/fhir/empi/provider/EmpiProviderR4.html).
In cases where the operation changes data, if a resource id parameter contains a version (e.g. `Person/123/_history/1`), then the operation will fail with a 409 CONFLICT if that is not the latest version of that resource. This feature can be used to prevent update conflicts in an environment where multiple users are working on the same set of empi links.

View File

@ -33,7 +33,7 @@
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-server-empi</artifactId>
<artifactId>hapi-fhir-server-mdm</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
@ -108,7 +108,7 @@
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jpaserver-empi</artifactId>
<artifactId>hapi-fhir-jpaserver-mdm</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>

View File

@ -77,7 +77,7 @@
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-server-empi</artifactId>
<artifactId>hapi-fhir-server-mdm</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>

View File

@ -88,9 +88,8 @@ import ca.uhn.fhir.rest.server.interceptor.consent.IConsentContextServices;
import ca.uhn.fhir.rest.server.interceptor.partition.RequestTenantPartitionInterceptor;
import org.hibernate.jpa.HibernatePersistenceProvider;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.utilities.npm.BasePackageCacheManager;
import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager;
import org.hl7.fhir.utilities.graphql.IGraphQLStorageServices;
import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager;
import org.springframework.batch.core.configuration.annotation.BatchConfigurer;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.beans.factory.annotation.Autowired;
@ -147,7 +146,7 @@ import java.util.Date;
@ComponentScan.Filter(type = FilterType.REGEX, pattern = ".*Test.*"),
@ComponentScan.Filter(type = FilterType.REGEX, pattern = "ca.uhn.fhir.jpa.subscription.*"),
@ComponentScan.Filter(type = FilterType.REGEX, pattern = "ca.uhn.fhir.jpa.searchparam.*"),
@ComponentScan.Filter(type = FilterType.REGEX, pattern = "ca.uhn.fhir.jpa.empi.*"),
@ComponentScan.Filter(type = FilterType.REGEX, pattern = "ca.uhn.fhir.jpa.mdm.*"),
@ComponentScan.Filter(type = FilterType.REGEX, pattern = "ca.uhn.fhir.jpa.starter.*"),
@ComponentScan.Filter(type = FilterType.REGEX, pattern = "ca.uhn.fhir.jpa.batch.*")
})

View File

@ -20,8 +20,8 @@ package ca.uhn.fhir.jpa.dao.data;
* #L%
*/
import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
import ca.uhn.fhir.jpa.entity.EmpiLink;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.jpa.entity.MdmLink;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
@ -29,12 +29,12 @@ import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
@Repository
public interface IEmpiLinkDao extends JpaRepository<EmpiLink, Long> {
public interface IMdmLinkDao extends JpaRepository<MdmLink, Long> {
@Modifying
@Query("DELETE FROM EmpiLink f WHERE myPersonPid = :pid OR myTargetPid = :pid")
@Query("DELETE FROM MdmLink f WHERE myPersonPid = :pid OR myTargetPid = :pid")
int deleteWithAnyReferenceToPid(@Param("pid") Long thePid);
@Modifying
@Query("DELETE FROM EmpiLink f WHERE (myPersonPid = :pid OR myTargetPid = :pid) AND myMatchResult <> :matchResult")
int deleteWithAnyReferenceToPidAndMatchResultNot(@Param("pid") Long thePid, @Param("matchResult")EmpiMatchResultEnum theMatchResult);
@Query("DELETE FROM MdmLink f WHERE (myPersonPid = :pid OR myTargetPid = :pid) AND myMatchResult <> :matchResult")
int deleteWithAnyReferenceToPidAndMatchResultNot(@Param("pid") Long thePid, @Param("matchResult") MdmMatchResultEnum theMatchResult);
}

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.jpa.dao.empi;
package ca.uhn.fhir.jpa.dao.mdm;
/*-
* #%L
@ -20,8 +20,9 @@ package ca.uhn.fhir.jpa.dao.empi;
* #L%
*/
import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
import ca.uhn.fhir.jpa.dao.data.IEmpiLinkDao;
import ca.uhn.fhir.jpa.entity.MdmLink;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.jpa.dao.data.IMdmLinkDao;
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.slf4j.Logger;
@ -30,33 +31,33 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class EmpiLinkDeleteSvc {
private static final Logger ourLog = LoggerFactory.getLogger(EmpiLinkDeleteSvc.class);
public class MdmLinkDeleteSvc {
private static final Logger ourLog = LoggerFactory.getLogger(MdmLinkDeleteSvc.class);
@Autowired
private IEmpiLinkDao myEmpiLinkDao;
private IMdmLinkDao myMdmLinkDao;
@Autowired
private IdHelperService myIdHelperService;
/**
* Delete all EmpiLink records with any reference to this resource. (Used by Expunge.)
* Delete all {@link MdmLink} records with any reference to this resource. (Used by Expunge.)
* @param theResource
* @return the number of records deleted
*/
public int deleteWithAnyReferenceTo(IBaseResource theResource) {
Long pid = myIdHelperService.getPidOrThrowException(theResource.getIdElement(), null);
int removed = myEmpiLinkDao.deleteWithAnyReferenceToPid(pid);
int removed = myMdmLinkDao.deleteWithAnyReferenceToPid(pid);
if (removed > 0) {
ourLog.info("Removed {} EMPI links with references to {}", removed, theResource.getIdElement().toVersionless());
ourLog.info("Removed {} MDM links with references to {}", removed, theResource.getIdElement().toVersionless());
}
return removed;
}
public int deleteNonRedirectWithWithAnyReferenceTo(IBaseResource theResource) {
Long pid = myIdHelperService.getPidOrThrowException(theResource.getIdElement(), null);
int removed = myEmpiLinkDao.deleteWithAnyReferenceToPidAndMatchResultNot(pid, EmpiMatchResultEnum.REDIRECT);
int removed = myMdmLinkDao.deleteWithAnyReferenceToPidAndMatchResultNot(pid, MdmMatchResultEnum.REDIRECT);
if (removed > 0) {
ourLog.info("Removed {} non-redirect EMPI links with references to {}", removed, theResource.getIdElement().toVersionless());
ourLog.info("Removed {} non-redirect MDM links with references to {}", removed, theResource.getIdElement().toVersionless());
}
return removed;
}

View File

@ -20,8 +20,8 @@ package ca.uhn.fhir.jpa.entity;
* #L%
*/
import ca.uhn.fhir.empi.api.EmpiLinkSourceEnum;
import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import org.apache.commons.lang3.builder.ToStringBuilder;
@ -47,7 +47,7 @@ import java.util.Date;
@Table(name = "MPI_LINK", uniqueConstraints = {
@UniqueConstraint(name = "IDX_EMPI_PERSON_TGT", columnNames = {"PERSON_PID", "TARGET_PID"}),
})
public class EmpiLink {
public class MdmLink {
public static final int VERSION_LENGTH = 16;
private static final int MATCH_RESULT_LENGTH = 16;
private static final int LINK_SOURCE_LENGTH = 16;
@ -61,16 +61,18 @@ public class EmpiLink {
private Long myId;
@ManyToOne(optional = false, fetch = FetchType.LAZY, cascade = {})
@JoinColumn(name = "SOURCE_RESOURCE_PID", referencedColumnName = "RES_ID", foreignKey = @ForeignKey(name = "FK_EMPI_LINK_SOURCE_RESOURCE"), insertable=false, updatable=false, nullable=false)
private ResourceTable mySourceResource;
@JoinColumn(name = "GOLDEN_RESOURCE_PID", referencedColumnName = "RES_ID", foreignKey = @ForeignKey(name = "FK_EMPI_LINK_GOLDEN_RESOURCE"), insertable=false, updatable=false, nullable=false)
private ResourceTable myGoldenResource;
@Column(name = "SOURCE_RESOURCE_PID", nullable=false)
private Long mySourceResourcePid;
@Column(name = "GOLDEN_RESOURCE_PID", nullable=false)
private Long myGoldenResourcePid;
@Deprecated
@ManyToOne(optional = false, fetch = FetchType.LAZY, cascade = {})
@JoinColumn(name = "PERSON_PID", referencedColumnName = "RES_ID", foreignKey = @ForeignKey(name = "FK_EMPI_LINK_PERSON"), insertable=false, updatable=false, nullable=false)
private ResourceTable myPerson;
@Deprecated
@Column(name = "PERSON_PID", nullable=false)
private Long myPersonPid;
@ -83,11 +85,11 @@ public class EmpiLink {
@Column(name = "MATCH_RESULT", nullable = false)
@Enumerated(EnumType.ORDINAL)
private EmpiMatchResultEnum myMatchResult;
private MdmMatchResultEnum myMatchResult;
@Column(name = "LINK_SOURCE", nullable = false)
@Enumerated(EnumType.ORDINAL)
private EmpiLinkSourceEnum myLinkSource;
private MdmLinkSourceEnum myLinkSource;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "CREATED", nullable = false)
@ -118,47 +120,47 @@ public class EmpiLink {
@Column(name = "RULE_COUNT")
private Long myRuleCount;
public EmpiLink() {}
public MdmLink() {}
public EmpiLink(String theVersion) {
public MdmLink(String theVersion) {
myVersion = theVersion;
}
@Column(name = "TARGET_TYPE", nullable = true, length = TARGET_TYPE_LENGTH)
private String myEmpiTargetType;
private String myMdmTargetType;
public Long getId() {
return myId;
}
public EmpiLink setId(Long theId) {
public MdmLink setId(Long theId) {
myId = theId;
return this;
}
public ResourceTable getSourceResource() {
return mySourceResource;
public ResourceTable getGoldenResource() {
return myGoldenResource;
}
public EmpiLink setSourceResource(ResourceTable theSourceResource) {
mySourceResource = theSourceResource;
mySourceResourcePid = theSourceResource.getId();
public MdmLink setGoldenResource(ResourceTable theGoldenResource) {
myGoldenResource = theGoldenResource;
myGoldenResourcePid = theGoldenResource.getId();
return this;
}
public Long getSourceResourcePid() {
return mySourceResourcePid;
public Long getGoldenResourcePid() {
return myGoldenResourcePid;
}
public EmpiLink setPersonPid(Long thePersonPid) {
public MdmLink setPersonPid(Long thePersonPid) {
myPersonPid = thePersonPid;
return this;
}
public EmpiLink setGoldenResourcePid(Long theSourceResourcePid) {
public MdmLink setGoldenResourcePid(Long theSourceResourcePid) {
setPersonPid(theSourceResourcePid);
mySourceResourcePid = theSourceResourcePid;
myGoldenResourcePid = theSourceResourcePid;
return this;
}
@ -166,7 +168,7 @@ public class EmpiLink {
return myTarget;
}
public EmpiLink setTarget(ResourceTable theTarget) {
public MdmLink setTarget(ResourceTable theTarget) {
myTarget = theTarget;
myTargetPid = theTarget.getId();
return this;
@ -176,62 +178,62 @@ public class EmpiLink {
return myTargetPid;
}
public EmpiLink setTargetPid(Long theTargetPid) {
public MdmLink setTargetPid(Long theTargetPid) {
myTargetPid = theTargetPid;
return this;
}
public EmpiMatchResultEnum getMatchResult() {
public MdmMatchResultEnum getMatchResult() {
return myMatchResult;
}
public EmpiLink setMatchResult(EmpiMatchResultEnum theMatchResult) {
public MdmLink setMatchResult(MdmMatchResultEnum theMatchResult) {
myMatchResult = theMatchResult;
return this;
}
public boolean isNoMatch() {
return myMatchResult == EmpiMatchResultEnum.NO_MATCH;
return myMatchResult == MdmMatchResultEnum.NO_MATCH;
}
public boolean isMatch() {
return myMatchResult == EmpiMatchResultEnum.MATCH;
return myMatchResult == MdmMatchResultEnum.MATCH;
}
public boolean isPossibleMatch() {
return myMatchResult == EmpiMatchResultEnum.POSSIBLE_MATCH;
return myMatchResult == MdmMatchResultEnum.POSSIBLE_MATCH;
}
public boolean isRedirect() {
return myMatchResult == EmpiMatchResultEnum.REDIRECT;
return myMatchResult == MdmMatchResultEnum.REDIRECT;
}
public boolean isPossibleDuplicate() {
return myMatchResult == EmpiMatchResultEnum.POSSIBLE_DUPLICATE;
return myMatchResult == MdmMatchResultEnum.POSSIBLE_DUPLICATE;
}
public EmpiLinkSourceEnum getLinkSource() {
public MdmLinkSourceEnum getLinkSource() {
return myLinkSource;
}
public EmpiLink setLinkSource(EmpiLinkSourceEnum theLinkSource) {
public MdmLink setLinkSource(MdmLinkSourceEnum theLinkSource) {
myLinkSource = theLinkSource;
return this;
}
public boolean isAuto() {
return myLinkSource == EmpiLinkSourceEnum.AUTO;
return myLinkSource == MdmLinkSourceEnum.AUTO;
}
public boolean isManual() {
return myLinkSource == EmpiLinkSourceEnum.MANUAL;
return myLinkSource == MdmLinkSourceEnum.MANUAL;
}
public Date getCreated() {
return myCreated;
}
public EmpiLink setCreated(Date theCreated) {
public MdmLink setCreated(Date theCreated) {
myCreated = theCreated;
return this;
}
@ -240,7 +242,7 @@ public class EmpiLink {
return myUpdated;
}
public EmpiLink setUpdated(Date theUpdated) {
public MdmLink setUpdated(Date theUpdated) {
myUpdated = theUpdated;
return this;
}
@ -249,7 +251,7 @@ public class EmpiLink {
return myVersion;
}
public EmpiLink setVersion(String theVersion) {
public MdmLink setVersion(String theVersion) {
myVersion = theVersion;
return this;
}
@ -258,7 +260,7 @@ public class EmpiLink {
return myVector;
}
public EmpiLink setVector(Long theVector) {
public MdmLink setVector(Long theVector) {
myVector = theVector;
return this;
}
@ -267,7 +269,7 @@ public class EmpiLink {
return myScore;
}
public EmpiLink setScore(Double theScore) {
public MdmLink setScore(Double theScore) {
myScore = theScore;
return this;
}
@ -280,7 +282,7 @@ public class EmpiLink {
return myEidMatch != null && myEidMatch;
}
public EmpiLink setEidMatch(Boolean theEidMatch) {
public MdmLink setEidMatch(Boolean theEidMatch) {
myEidMatch = theEidMatch;
return this;
}
@ -289,13 +291,13 @@ public class EmpiLink {
return myHadToCreateNewResource != null && myHadToCreateNewResource;
}
public EmpiLink setHadToCreateNewResource(Boolean theHadToCreateNewResource) {
public MdmLink setHadToCreateNewResource(Boolean theHadToCreateNewResource) {
myHadToCreateNewResource = theHadToCreateNewResource;
return this;
}
public EmpiLink setEmpiTargetType(String theEmpiTargetType) {
myEmpiTargetType = theEmpiTargetType;
public MdmLink setMdmTargetType(String mdmTargetType) {
myMdmTargetType = mdmTargetType;
return this;
}
@ -303,19 +305,20 @@ public class EmpiLink {
public String toString() {
return new ToStringBuilder(this)
.append("myId", myId)
.append("myPersonPid", mySourceResourcePid)
.append("myGoldenResource", myGoldenResourcePid)
.append("myTargetPid", myTargetPid)
.append("myEmpiTargetType", myEmpiTargetType)
.append("myMdmTargetType", myMdmTargetType)
.append("myMatchResult", myMatchResult)
.append("myLinkSource", myLinkSource)
.append("myEidMatch", myEidMatch)
.append("myNewPerson", myHadToCreateNewResource)
.append("myHadToCreateNewResource", myHadToCreateNewResource)
.append("myScore", myScore)
.append("myRuleCount", myRuleCount)
.toString();
}
public String getEmpiTargetType() {
return myEmpiTargetType;
public String getMdmTargetType() {
return myMdmTargetType;
}
public Long getRuleCount() {

View File

@ -10,7 +10,7 @@
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>
<artifactId>hapi-fhir-jpaserver-empi</artifactId>
<artifactId>hapi-fhir-jpaserver-mdm</artifactId>
<packaging>jar</packaging>
<name>HAPI FHIR JPA Server - Enterprise Master Patient Index</name>
@ -18,7 +18,7 @@
<dependencies>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-server-empi</artifactId>
<artifactId>hapi-fhir-server-mdm</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>

View File

@ -1,26 +0,0 @@
@startuml
abstract class AbstractList
abstract AbstractCollection
interface List
interface Collection
List <|-- AbstractList
Collection <|-- AbstractCollection
Collection <|- List
AbstractCollection <|- AbstractList
AbstractList <|-- ArrayList
class ArrayList {
Object[] elementData
size()
}
enum TimeUnit {
DAYS
HOURS
MINUTES
}
@enduml

View File

@ -1,240 +0,0 @@
package ca.uhn.fhir.jpa.empi.config;
/*-
* #%L
* HAPI FHIR JPA Server - Enterprise Master Patient Index
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
* 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%
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.empi.api.IEmpiControllerSvc;
import ca.uhn.fhir.empi.api.IEmpiExpungeSvc;
import ca.uhn.fhir.empi.api.IEmpiLinkQuerySvc;
import ca.uhn.fhir.empi.api.IEmpiLinkSvc;
import ca.uhn.fhir.empi.api.IEmpiLinkUpdaterSvc;
import ca.uhn.fhir.empi.api.IEmpiMatchFinderSvc;
import ca.uhn.fhir.empi.api.IGoldenResourceMergerSvc;
import ca.uhn.fhir.empi.api.IEmpiSettings;
import ca.uhn.fhir.empi.log.Logs;
import ca.uhn.fhir.empi.provider.EmpiControllerHelper;
import ca.uhn.fhir.empi.provider.EmpiProviderLoader;
import ca.uhn.fhir.empi.rules.config.EmpiRuleValidator;
import ca.uhn.fhir.empi.rules.svc.EmpiResourceMatcherSvc;
import ca.uhn.fhir.empi.util.EIDHelper;
import ca.uhn.fhir.empi.util.MessageHelper;
import ca.uhn.fhir.empi.util.PersonHelper;
import ca.uhn.fhir.jpa.dao.empi.EmpiLinkDeleteSvc;
import ca.uhn.fhir.jpa.empi.broker.EmpiMessageHandler;
import ca.uhn.fhir.jpa.empi.broker.EmpiQueueConsumerLoader;
import ca.uhn.fhir.jpa.empi.dao.EmpiLinkDaoSvc;
import ca.uhn.fhir.jpa.empi.dao.EmpiLinkFactory;
import ca.uhn.fhir.jpa.empi.interceptor.EmpiStorageInterceptor;
import ca.uhn.fhir.jpa.empi.interceptor.IEmpiStorageInterceptor;
import ca.uhn.fhir.jpa.empi.svc.EmpiClearSvcImpl;
import ca.uhn.fhir.jpa.empi.svc.EmpiControllerSvcImpl;
import ca.uhn.fhir.jpa.empi.svc.EmpiEidUpdateService;
import ca.uhn.fhir.jpa.empi.svc.EmpiLinkQuerySvcImpl;
import ca.uhn.fhir.jpa.empi.svc.EmpiLinkSvcImpl;
import ca.uhn.fhir.jpa.empi.svc.EmpiLinkUpdaterSvcImpl;
import ca.uhn.fhir.jpa.empi.svc.EmpiMatchFinderSvcImpl;
import ca.uhn.fhir.jpa.empi.svc.EmpiMatchLinkSvc;
import ca.uhn.fhir.jpa.empi.svc.EmpiPersonDeletingSvc;
import ca.uhn.fhir.jpa.empi.svc.GoldenResourceMergerSvcImpl;
import ca.uhn.fhir.jpa.empi.svc.EmpiResourceDaoSvc;
import ca.uhn.fhir.jpa.empi.svc.EmpiResourceFilteringSvc;
import ca.uhn.fhir.jpa.empi.svc.candidate.EmpiCandidateSearchCriteriaBuilderSvc;
import ca.uhn.fhir.jpa.empi.svc.candidate.EmpiCandidateSearchSvc;
import ca.uhn.fhir.jpa.empi.svc.candidate.EmpiSourceResourceFindingSvc;
import ca.uhn.fhir.jpa.empi.svc.candidate.FindCandidateByEidSvc;
import ca.uhn.fhir.jpa.empi.svc.candidate.FindCandidateByLinkSvc;
import ca.uhn.fhir.jpa.empi.svc.candidate.FindCandidateByScoreSvc;
import ca.uhn.fhir.rest.server.util.ISearchParamRetriever;
import ca.uhn.fhir.validation.IResourceLoader;
import org.slf4j.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class EmpiConsumerConfig {
private static final Logger ourLog = Logs.getEmpiTroubleshootingLog();
@Bean
IEmpiStorageInterceptor empiStorageInterceptor() {
return new EmpiStorageInterceptor();
}
@Bean
EmpiQueueConsumerLoader empiQueueConsumerLoader() {
return new EmpiQueueConsumerLoader();
}
@Bean
EmpiMessageHandler empiMessageHandler() {
return new EmpiMessageHandler();
}
@Bean
EmpiMatchLinkSvc empiMatchLinkSvc() {
return new EmpiMatchLinkSvc();
}
@Bean
EmpiEidUpdateService eidUpdateService() {
return new EmpiEidUpdateService();
}
@Bean
EmpiResourceDaoSvc empiResourceDaoSvc() {
return new EmpiResourceDaoSvc();
}
@Bean
IEmpiLinkSvc empiLinkSvc() {
return new EmpiLinkSvcImpl();
}
@Bean
PersonHelper personHelper(FhirContext theFhirContext) {
return new PersonHelper(theFhirContext);
}
@Bean
MessageHelper messageHelper(IEmpiSettings theEmpiSettings, FhirContext theFhirContext) {
return new MessageHelper(theEmpiSettings, theFhirContext);
}
@Bean
EmpiSubscriptionLoader empiSubscriptionLoader() {
return new EmpiSubscriptionLoader();
}
@Bean
EmpiSearchParameterLoader empiSearchParameterLoader() {
return new EmpiSearchParameterLoader();
}
@Bean
EmpiSourceResourceFindingSvc empiPersonFindingSvc() {
return new EmpiSourceResourceFindingSvc();
}
@Bean
FindCandidateByEidSvc findCandidateByEidSvc() {
return new FindCandidateByEidSvc();
}
@Bean
FindCandidateByLinkSvc findCandidateByLinkSvc() {
return new FindCandidateByLinkSvc();
}
@Bean
FindCandidateByScoreSvc findCandidateByScoreSvc() {
return new FindCandidateByScoreSvc();
}
@Bean
EmpiProviderLoader empiProviderLoader() {
return new EmpiProviderLoader();
}
@Bean
EmpiRuleValidator empiRuleValidator(FhirContext theFhirContext, ISearchParamRetriever theSearchParamRetriever) {
return new EmpiRuleValidator(theFhirContext, theSearchParamRetriever);
}
@Bean
IEmpiMatchFinderSvc empiMatchFinderSvc() {
return new EmpiMatchFinderSvcImpl();
}
@Bean
IGoldenResourceMergerSvc empiPersonMergerSvc() {
return new GoldenResourceMergerSvcImpl();
}
@Bean
IEmpiLinkQuerySvc empiLinkQuerySvc() {
return new EmpiLinkQuerySvcImpl();
}
@Bean
IEmpiExpungeSvc empiResetSvc(EmpiLinkDaoSvc theEmpiLinkDaoSvc, EmpiPersonDeletingSvc theEmpiPersonDeletingSvcImpl, IEmpiSettings theIEmpiSettings) {
return new EmpiClearSvcImpl(theEmpiLinkDaoSvc, theEmpiPersonDeletingSvcImpl, theIEmpiSettings);
}
@Bean
EmpiCandidateSearchSvc empiCandidateSearchSvc() {
return new EmpiCandidateSearchSvc();
}
@Bean
EmpiCandidateSearchCriteriaBuilderSvc empiCriteriaBuilderSvc() {
return new EmpiCandidateSearchCriteriaBuilderSvc();
}
@Bean
EmpiResourceMatcherSvc empiResourceComparatorSvc(FhirContext theFhirContext, IEmpiSettings theEmpiConfig) {
return new EmpiResourceMatcherSvc(theFhirContext, theEmpiConfig);
}
@Bean
EIDHelper eidHelper(FhirContext theFhirContext, IEmpiSettings theEmpiConfig) {
return new EIDHelper(theFhirContext, theEmpiConfig);
}
@Bean
EmpiLinkDaoSvc empiLinkDaoSvc() {
return new EmpiLinkDaoSvc();
}
@Bean
EmpiLinkFactory empiLinkFactory(IEmpiSettings theEmpiSettings) {
return new EmpiLinkFactory(theEmpiSettings);
}
@Bean
IEmpiLinkUpdaterSvc manualLinkUpdaterSvc() {
return new EmpiLinkUpdaterSvcImpl();
}
@Bean
EmpiLoader empiLoader() {
return new EmpiLoader();
}
@Bean
EmpiLinkDeleteSvc empiLinkDeleteSvc() {
return new EmpiLinkDeleteSvc();
}
@Bean
EmpiResourceFilteringSvc empiResourceFilteringSvc() {
return new EmpiResourceFilteringSvc();
}
@Bean
EmpiControllerHelper empiProviderHelper(FhirContext theFhirContext, IResourceLoader theResourceLoader, IEmpiSettings theEmpiSettings, MessageHelper messageHelper) {
return new EmpiControllerHelper(theFhirContext, theResourceLoader, theEmpiSettings, messageHelper);
}
@Bean
IEmpiControllerSvc empiControllerSvc() {
return new EmpiControllerSvcImpl();
}
}

View File

@ -1,76 +0,0 @@
package ca.uhn.fhir.jpa.empi.config;
/*-
* #%L
* HAPI FHIR JPA Server - Enterprise Master Patient Index
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
* 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%
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.empi.api.IEmpiChannelSubmitterSvc;
import ca.uhn.fhir.empi.api.IEmpiSubmitSvc;
import ca.uhn.fhir.empi.rules.config.EmpiRuleValidator;
import ca.uhn.fhir.jpa.dao.empi.EmpiLinkDeleteSvc;
import ca.uhn.fhir.jpa.empi.interceptor.EmpiSubmitterInterceptorLoader;
import ca.uhn.fhir.jpa.empi.svc.EmpiChannelSubmitterSvcImpl;
import ca.uhn.fhir.jpa.empi.svc.EmpiPersonDeletingSvc;
import ca.uhn.fhir.jpa.empi.svc.EmpiSearchParamSvc;
import ca.uhn.fhir.jpa.empi.svc.EmpiSubmitSvcImpl;
import ca.uhn.fhir.jpa.subscription.channel.api.IChannelFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
@Configuration
public class EmpiSubmitterConfig {
@Bean
EmpiSubmitterInterceptorLoader empiSubmitterInterceptorLoader() {
return new EmpiSubmitterInterceptorLoader();
}
@Bean
EmpiSearchParamSvc empiSearchParamSvc() {
return new EmpiSearchParamSvc();
}
@Bean
EmpiRuleValidator empiRuleValidator(FhirContext theFhirContext) {
return new EmpiRuleValidator(theFhirContext, empiSearchParamSvc());
}
@Bean
EmpiLinkDeleteSvc empiLinkDeleteSvc() {
return new EmpiLinkDeleteSvc();
}
@Bean
EmpiPersonDeletingSvc empiPersonDeletingSvc() {
return new EmpiPersonDeletingSvc();
}
@Bean
@Lazy
IEmpiChannelSubmitterSvc empiChannelSubmitterSvc(FhirContext theFhirContext, IChannelFactory theChannelFactory) {
return new EmpiChannelSubmitterSvcImpl(theFhirContext, theChannelFactory);
}
@Bean
IEmpiSubmitSvc empiBatchService() {
return new EmpiSubmitSvcImpl();
}
}

View File

@ -1,362 +0,0 @@
package ca.uhn.fhir.jpa.empi.dao;
/*-
* #%L
* HAPI FHIR JPA Server - Enterprise Master Patient Index
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
* 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%
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.empi.api.EmpiLinkSourceEnum;
import ca.uhn.fhir.empi.api.EmpiMatchOutcome;
import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
import ca.uhn.fhir.empi.log.Logs;
import ca.uhn.fhir.empi.model.MdmTransactionContext;
import ca.uhn.fhir.jpa.dao.data.IEmpiLinkDao;
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
import ca.uhn.fhir.jpa.entity.EmpiLink;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
public class EmpiLinkDaoSvc {
private static final Logger ourLog = Logs.getEmpiTroubleshootingLog();
@Autowired
private IEmpiLinkDao myEmpiLinkDao;
@Autowired
private EmpiLinkFactory myEmpiLinkFactory;
@Autowired
private IdHelperService myIdHelperService;
@Autowired
private FhirContext myFhirContext;
@Transactional
public EmpiLink createOrUpdateLinkEntity(IBaseResource theSourceResource, IBaseResource theTargetResource, EmpiMatchOutcome theMatchOutcome, EmpiLinkSourceEnum theLinkSource, @Nullable MdmTransactionContext theMdmTransactionContext) {
Long sourceResourcePid = myIdHelperService.getPidOrNull(theSourceResource);
Long targetResourcePid = myIdHelperService.getPidOrNull(theTargetResource);
EmpiLink empiLink = getOrCreateEmpiLinkBySourceResourcePidAndTargetResourcePid(sourceResourcePid, targetResourcePid);
empiLink.setLinkSource(theLinkSource);
empiLink.setMatchResult(theMatchOutcome.getMatchResultEnum());
// Preserve these flags for link updates
empiLink.setEidMatch(theMatchOutcome.isEidMatch() | empiLink.isEidMatch());
empiLink.setHadToCreateNewResource(theMatchOutcome.isNewPerson() | empiLink.getHadToCreateNewResource());
empiLink.setEmpiTargetType(myFhirContext.getResourceType(theTargetResource));
if (empiLink.getScore() != null) {
empiLink.setScore(Math.max(theMatchOutcome.score, empiLink.getScore()));
} else {
empiLink.setScore(theMatchOutcome.score);
}
String message = String.format("Creating EmpiLink from %s to %s -> %s", theSourceResource.getIdElement().toUnqualifiedVersionless(), theTargetResource.getIdElement().toUnqualifiedVersionless(), theMatchOutcome);
theMdmTransactionContext.addTransactionLogMessage(message);
ourLog.debug(message);
save(empiLink);
return empiLink;
}
@Nonnull
public EmpiLink getOrCreateEmpiLinkBySourceResourcePidAndTargetResourcePid(Long theSourceResourcePid, Long theTargetResourcePid) {
Optional<EmpiLink> oExisting = getLinkBySourceResourcePidAndTargetResourcePid(theSourceResourcePid, theTargetResourcePid);
if (oExisting.isPresent()) {
return oExisting.get();
} else {
EmpiLink newLink = myEmpiLinkFactory.newEmpiLink();
newLink.setGoldenResourcePid(theSourceResourcePid);
newLink.setPersonPid(theSourceResourcePid);
newLink.setTargetPid(theTargetResourcePid);
return newLink;
}
}
public Optional<EmpiLink> getLinkBySourceResourcePidAndTargetResourcePid(Long theSourceResourcePid, Long theTargetResourcePid) {
if (theTargetResourcePid == null || theSourceResourcePid == null) {
return Optional.empty();
}
EmpiLink link = myEmpiLinkFactory.newEmpiLink();
link.setTargetPid(theTargetResourcePid);
link.setGoldenResourcePid(theSourceResourcePid);
Example<EmpiLink> example = Example.of(link);
return myEmpiLinkDao.findOne(example);
}
/**
* Given a Target Pid, and a match result, return all links that match these criteria.
*
* @param theTargetPid the target of the relationship.
* @param theMatchResult the Match Result of the relationship
* @return a list of {@link EmpiLink} entities matching these criteria.
*/
public List<EmpiLink> getEmpiLinksByTargetPidAndMatchResult(Long theTargetPid, EmpiMatchResultEnum theMatchResult) {
EmpiLink exampleLink = myEmpiLinkFactory.newEmpiLink();
exampleLink.setTargetPid(theTargetPid);
exampleLink.setMatchResult(theMatchResult);
Example<EmpiLink> example = Example.of(exampleLink);
return myEmpiLinkDao.findAll(example);
}
/**
* Given a target Pid, return its Matched EmpiLink. There can only ever be at most one of these, but its possible
* the target has no matches, and may return an empty optional.
*
* @param theTargetPid The Pid of the target you wish to find the matching link for.
* @return the {@link EmpiLink} that contains the Match information for the target.
*/
public Optional<EmpiLink> getMatchedLinkForTargetPid(Long theTargetPid) {
EmpiLink exampleLink = myEmpiLinkFactory.newEmpiLink();
exampleLink.setTargetPid(theTargetPid);
exampleLink.setMatchResult(EmpiMatchResultEnum.MATCH);
Example<EmpiLink> example = Example.of(exampleLink);
return myEmpiLinkDao.findOne(example);
}
/**
* Given an IBaseResource, return its Matched EmpiLink. There can only ever be at most one of these, but its possible
* the target has no matches, and may return an empty optional.
*
* @param theTarget The IBaseResource representing the target you wish to find the matching link for.
* @return the {@link EmpiLink} that contains the Match information for the target.
*/
public Optional<EmpiLink> getMatchedLinkForTarget(IBaseResource theTarget) {
Long pid = myIdHelperService.getPidOrNull(theTarget);
if (pid == null) {
return Optional.empty();
}
EmpiLink exampleLink = myEmpiLinkFactory.newEmpiLink();
exampleLink.setTargetPid(pid);
exampleLink.setMatchResult(EmpiMatchResultEnum.MATCH);
Example<EmpiLink> example = Example.of(exampleLink);
return myEmpiLinkDao.findOne(example);
}
/**
* Given a person a target and a match result, return the matching EmpiLink, if it exists.
*
* @param thePersonPid The Pid of the Person in the relationship
* @param theTargetPid The Pid of the target in the relationship
* @param theMatchResult The MatchResult you are looking for.
* @return an Optional {@link EmpiLink} containing the matched link if it exists.
*/
public Optional<EmpiLink> getEmpiLinksByPersonPidTargetPidAndMatchResult(Long thePersonPid, Long theTargetPid, EmpiMatchResultEnum theMatchResult) {
EmpiLink exampleLink = myEmpiLinkFactory.newEmpiLink();
exampleLink.setGoldenResourcePid(thePersonPid);
exampleLink.setTargetPid(theTargetPid);
exampleLink.setMatchResult(theMatchResult);
Example<EmpiLink> example = Example.of(exampleLink);
return myEmpiLinkDao.findOne(example);
}
/**
* Get all {@link EmpiLink} which have {@link EmpiMatchResultEnum#POSSIBLE_DUPLICATE} as their match result.
*
* @return A list of EmpiLinks that hold potential duplicate persons.
*/
public List<EmpiLink> getPossibleDuplicates() {
EmpiLink exampleLink = myEmpiLinkFactory.newEmpiLink();
exampleLink.setMatchResult(EmpiMatchResultEnum.POSSIBLE_DUPLICATE);
Example<EmpiLink> example = Example.of(exampleLink);
return myEmpiLinkDao.findAll(example);
}
public Optional<EmpiLink> findEmpiLinkByTarget(IBaseResource theTargetResource) {
@Nullable Long pid = myIdHelperService.getPidOrNull(theTargetResource);
if (pid == null) {
return Optional.empty();
}
EmpiLink exampleLink = myEmpiLinkFactory.newEmpiLink().setTargetPid(pid);
Example<EmpiLink> example = Example.of(exampleLink);
return myEmpiLinkDao.findOne(example);
}
/**
* Delete a given EmpiLink. Note that this does not clear out the Person, or the Person's related links.
* It is a simple entity delete.
*
* @param theEmpiLink the EmpiLink to delete.
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void deleteLink(EmpiLink theEmpiLink) {
myEmpiLinkDao.delete(theEmpiLink);
}
/**
* Given a Golden Resource , return all links in which they are the source Person of the {@link EmpiLink}
*
* @param theGoldenResource The {@link IBaseResource} Person who's links you would like to retrieve.
* @return A list of all {@link EmpiLink} entities in which theGoldenResource is the source Person.
*/
public List<EmpiLink> findEmpiLinksByGoldenResource(IBaseResource theGoldenResource) {
Long pid = myIdHelperService.getPidOrNull(theGoldenResource);
if (pid == null) {
return Collections.emptyList();
}
EmpiLink exampleLink = myEmpiLinkFactory.newEmpiLink().setGoldenResourcePid(pid);
Example<EmpiLink> example = Example.of(exampleLink);
return myEmpiLinkDao.findAll(example);
}
/**
* Delete all {@link EmpiLink} entities, and return all resource PIDs from the source of the relationship.
*
* @return A list of Long representing the related Person Pids.
*/
@Transactional
public List<Long> deleteAllEmpiLinksAndReturnGoldenResourcePids() {
List<EmpiLink> all = myEmpiLinkDao.findAll();
return deleteEmpiLinksAndReturnGoldenResourcePids(all);
}
private List<Long> deleteEmpiLinksAndReturnGoldenResourcePids(List<EmpiLink> theLinks) {
Set<Long> persons = theLinks.stream().map(EmpiLink::getSourceResourcePid).collect(Collectors.toSet());
//TODO GGG this is probably invalid... we are essentially looking for GOLDEN -> GOLDEN links, which are either POSSIBLE_DUPLICATE
//and REDIRECT
//persons.addAll(theLinks.stream().filter(link -> "Person".equals(link.getEmpiTargetType())).map(EmpiLink::getTargetPid).collect(Collectors.toSet()));
persons.addAll(theLinks.stream()
.filter(link -> link.getMatchResult().equals(EmpiMatchResultEnum.REDIRECT)
|| link.getMatchResult().equals(EmpiMatchResultEnum.POSSIBLE_DUPLICATE))
.map(EmpiLink::getTargetPid).collect(Collectors.toSet()));
ourLog.info("Deleting {} EMPI link records...", theLinks.size());
myEmpiLinkDao.deleteAll(theLinks);
ourLog.info("{} EMPI link records deleted", theLinks.size());
return new ArrayList<>(persons);
}
/**
* Given a valid {@link String}, delete all {@link EmpiLink} entities for that type, and get the Pids
* for the Person resources which were the sources of the links.
*
* @param theTargetType the type of relationship you would like to delete.
* @return A list of longs representing the Pids of the Person resources used as the sources of the relationships that were deleted.
*/
public List<Long> deleteAllEmpiLinksOfTypeAndReturnPersonPids(String theTargetType) {
EmpiLink link = new EmpiLink();
link.setEmpiTargetType(theTargetType);
Example<EmpiLink> exampleLink = Example.of(link);
List<EmpiLink> allOfType = myEmpiLinkDao.findAll(exampleLink);
return deleteEmpiLinksAndReturnGoldenResourcePids(allOfType);
}
/**
* Persist an EmpiLink to the database.
*
* @param theEmpiLink the link to save.
* @return the persisted {@link EmpiLink} entity.
*/
public EmpiLink save(EmpiLink theEmpiLink) {
if (theEmpiLink.getCreated() == null) {
theEmpiLink.setCreated(new Date());
}
theEmpiLink.setUpdated(new Date());
return myEmpiLinkDao.save(theEmpiLink);
}
/**
* Given an example {@link EmpiLink}, return all links from the database which match the example.
*
* @param theExampleLink The EmpiLink containing the data we would like to search for.
* @return a list of {@link EmpiLink} entities which match the example.
*/
public List<EmpiLink> findEmpiLinkByExample(Example<EmpiLink> theExampleLink) {
return myEmpiLinkDao.findAll(theExampleLink);
}
/**
* Given a target {@link IBaseResource}, return all {@link EmpiLink} entities in which this target is the target
* of the relationship. This will show you all links for a given Patient/Practitioner.
*
* @param theTargetResource the target resource to find links for.
* @return all links for the target.
*/
public List<EmpiLink> findEmpiLinksByTarget(IBaseResource theTargetResource) {
Long pid = myIdHelperService.getPidOrNull(theTargetResource);
if (pid == null) {
return Collections.emptyList();
}
EmpiLink exampleLink = myEmpiLinkFactory.newEmpiLink().setTargetPid(pid);
Example<EmpiLink> example = Example.of(exampleLink);
return myEmpiLinkDao.findAll(example);
}
/**
* Finds all links pointing from the target resource to the source resource.
*
* @param theTargetResource Resource referencing the source resource
* @param theSourceResource Resource being referenced by the source resource
*
* @return
* Returns all EMPI links pointing to the source from target resource
*/
public List<EmpiLink> findEmpiLinksByTargetAndSource(IBaseResource theTargetResource, IBaseResource theSourceResource) {
Long targetPid = myIdHelperService.getPidOrNull(theTargetResource);
if (targetPid == null) {
return Collections.emptyList();
}
Long sourcePid = myIdHelperService.getPidOrNull(theSourceResource);
if (sourcePid == null) {
return Collections.emptyList();
}
EmpiLink exampleLink = myEmpiLinkFactory.newEmpiLink().setTargetPid(targetPid).setGoldenResourcePid(sourcePid);
Example<EmpiLink> example = Example.of(exampleLink);
return myEmpiLinkDao.findAll(example);
}
/**
* Finds all {@link EmpiLink} entities in which theSourceResource's PID is the source
* of the relationship.
*
* @param theSourceResource the source resource to find links for.
* @return all links for the source.
*/
public List<EmpiLink> findEmpiMatchLinksBySource(IBaseResource theSourceResource) {
Long pid = myIdHelperService.getPidOrNull(theSourceResource);
if (pid == null) {
return Collections.emptyList();
}
EmpiLink exampleLink = myEmpiLinkFactory.newEmpiLink().setGoldenResourcePid(pid);
exampleLink.setMatchResult(EmpiMatchResultEnum.MATCH);
Example<EmpiLink> example = Example.of(exampleLink);
return myEmpiLinkDao.findAll(example);
}
/**
* Factory delegation method, whenever you need a new EmpiLink, use this factory method.
* //TODO Should we make the constructor private for EmpiLink? or work out some way to ensure they can only be instantiated via factory.
*
* @return A new {@link EmpiLink}.
*/
public EmpiLink newEmpiLink() {
return myEmpiLinkFactory.newEmpiLink();
}
}

View File

@ -1,79 +0,0 @@
package ca.uhn.fhir.jpa.empi.svc;
/*-
* #%L
* HAPI FHIR JPA Server - Enterprise Master Patient Index
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
* 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%
*/
import ca.uhn.fhir.empi.api.IEmpiExpungeSvc;
import ca.uhn.fhir.empi.api.IEmpiSettings;
import ca.uhn.fhir.empi.log.Logs;
import ca.uhn.fhir.jpa.api.model.DeleteMethodOutcome;
import ca.uhn.fhir.jpa.empi.dao.EmpiLinkDaoSvc;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.provider.ProviderConstants;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
/**
* This class is responsible for clearing out existing EMPI links, as well as deleting all persons related to those EMPI Links.
*
*/
public class EmpiClearSvcImpl implements IEmpiExpungeSvc {
private static final Logger ourLog = Logs.getEmpiTroubleshootingLog();
final EmpiLinkDaoSvc myEmpiLinkDaoSvc;
final EmpiPersonDeletingSvc myEmpiPersonDeletingSvcImpl;
final IEmpiSettings myEmpiSettings;
@Autowired
public EmpiClearSvcImpl(EmpiLinkDaoSvc theEmpiLinkDaoSvc, EmpiPersonDeletingSvc theEmpiPersonDeletingSvcImpl, IEmpiSettings theIEmpiSettings) {
myEmpiLinkDaoSvc = theEmpiLinkDaoSvc;
myEmpiPersonDeletingSvcImpl = theEmpiPersonDeletingSvcImpl;
myEmpiSettings = theIEmpiSettings;
}
@Override
public long expungeAllMdmLinksOfTargetType(String theResourceType, ServletRequestDetails theRequestDetails) {
throwExceptionIfInvalidTargetType(theResourceType);
ourLog.info("Clearing all EMPI Links for resource type {}...", theResourceType);
List<Long> personPids = myEmpiLinkDaoSvc.deleteAllEmpiLinksOfTypeAndReturnPersonPids(theResourceType);
DeleteMethodOutcome deleteOutcome = myEmpiPersonDeletingSvcImpl.expungePersonPids(personPids, theRequestDetails);
ourLog.info("EMPI clear operation complete. Removed {} EMPI links and {} Person resources.", personPids.size(), deleteOutcome.getExpungedResourcesCount());
return personPids.size();
}
private void throwExceptionIfInvalidTargetType(String theResourceType) {
if (!myEmpiSettings.isSupportedMdmType(theResourceType)) {
throw new InvalidRequestException(ProviderConstants.MDM_CLEAR + " does not support resource type: " + theResourceType);
}
}
@Override
public long expungeAllEmpiLinks(ServletRequestDetails theRequestDetails) {
ourLog.info("Clearing all EMPI Links...");
List<Long> personPids = myEmpiLinkDaoSvc.deleteAllEmpiLinksAndReturnGoldenResourcePids();
DeleteMethodOutcome deleteOutcome = myEmpiPersonDeletingSvcImpl.expungePersonPids(personPids, theRequestDetails);
ourLog.info("EMPI clear operation complete. Removed {} EMPI links and expunged {} Person resources.", personPids.size(), deleteOutcome.getExpungedResourcesCount());
return personPids.size();
}
}

View File

@ -1,100 +0,0 @@
package ca.uhn.fhir.jpa.empi.svc;
/*-
* #%L
* HAPI FHIR JPA Server - Enterprise Master Patient Index
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
* 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%
*/
import ca.uhn.fhir.empi.api.EmpiLinkJson;
import ca.uhn.fhir.empi.api.EmpiLinkSourceEnum;
import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
import ca.uhn.fhir.empi.api.IEmpiControllerSvc;
import ca.uhn.fhir.empi.api.IEmpiLinkQuerySvc;
import ca.uhn.fhir.empi.api.IEmpiLinkUpdaterSvc;
import ca.uhn.fhir.empi.api.IGoldenResourceMergerSvc;
import ca.uhn.fhir.empi.model.MdmTransactionContext;
import ca.uhn.fhir.empi.provider.EmpiControllerHelper;
import ca.uhn.fhir.empi.provider.EmpiControllerUtil;
import ca.uhn.fhir.rest.server.provider.ProviderConstants;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.stream.Stream;
/**
* This class acts as a layer between EmpiProviders and EMPI services to support a REST API that's not a FHIR Operation API.
*/
@Service
public class EmpiControllerSvcImpl implements IEmpiControllerSvc {
@Autowired
EmpiControllerHelper myEmpiControllerHelper;
@Autowired
IGoldenResourceMergerSvc myEmpiPersonMergerSvc;
@Autowired
IEmpiLinkQuerySvc myEmpiLinkQuerySvc;
@Autowired
IEmpiLinkUpdaterSvc myIEmpiLinkUpdaterSvc;
@Override
public IAnyResource mergeGoldenResources(String theFromPersonId, String theToPersonId, MdmTransactionContext theMdmTransactionContext) {
IAnyResource fromPerson = myEmpiControllerHelper.getLatestGoldenResourceFromIdOrThrowException(ProviderConstants.MDM_MERGE_GR_FROM_GOLDEN_RESOURCE_ID, theFromPersonId);
IAnyResource toPerson = myEmpiControllerHelper.getLatestGoldenResourceFromIdOrThrowException(ProviderConstants.MDM_MERGE_GR_TO_GOLDEN_RESOURCE_ID, theToPersonId);
myEmpiControllerHelper.validateMergeResources(fromPerson, toPerson);
myEmpiControllerHelper.validateSameVersion(fromPerson, theFromPersonId);
myEmpiControllerHelper.validateSameVersion(toPerson, theToPersonId);
return myEmpiPersonMergerSvc.mergeGoldenResources(fromPerson, toPerson, theMdmTransactionContext);
}
@Override
public Stream<EmpiLinkJson> queryLinks(@Nullable String thePersonId, @Nullable String theTargetId, @Nullable String theMatchResult, @Nullable String theLinkSource, MdmTransactionContext theEmpiContext) {
IIdType personId = EmpiControllerUtil.extractPersonIdDtOrNull(ProviderConstants.MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID, thePersonId);
IIdType targetId = EmpiControllerUtil.extractTargetIdDtOrNull(ProviderConstants.MDM_QUERY_LINKS_RESOURCE_ID, theTargetId);
EmpiMatchResultEnum matchResult = EmpiControllerUtil.extractMatchResultOrNull(theMatchResult);
EmpiLinkSourceEnum linkSource = EmpiControllerUtil.extractLinkSourceOrNull(theLinkSource);
return myEmpiLinkQuerySvc.queryLinks(personId, targetId, matchResult, linkSource, theEmpiContext);
}
@Override
public Stream<EmpiLinkJson> getDuplicateGoldenResources(MdmTransactionContext theEmpiContext) {
return myEmpiLinkQuerySvc.getDuplicatePersons(theEmpiContext);
}
@Override
public IAnyResource updateLink(String theGoldenResourceId, String theTargetId, String theMatchResult, MdmTransactionContext theEmpiContext) {
EmpiMatchResultEnum matchResult = EmpiControllerUtil.extractMatchResultOrNull(theMatchResult);
IAnyResource person = myEmpiControllerHelper.getLatestGoldenResourceFromIdOrThrowException(ProviderConstants.MDM_UPDATE_LINK_GOLDEN_RESOURCE_ID, theGoldenResourceId);
IAnyResource target = myEmpiControllerHelper.getLatestTargetFromIdOrThrowException(ProviderConstants.MDM_UPDATE_LINK_RESOURCE_ID, theTargetId);
myEmpiControllerHelper.validateSameVersion(person, theGoldenResourceId);
myEmpiControllerHelper.validateSameVersion(target, theTargetId);
return myIEmpiLinkUpdaterSvc.updateLink(person, target, matchResult, theEmpiContext);
}
@Override
public void notDuplicateGoldenResource(String thePersonId, String theTargetPersonId, MdmTransactionContext theEmpiContext) {
IAnyResource person = myEmpiControllerHelper.getLatestGoldenResourceFromIdOrThrowException(ProviderConstants.MDM_UPDATE_LINK_GOLDEN_RESOURCE_ID, thePersonId);
IAnyResource target = myEmpiControllerHelper.getLatestGoldenResourceFromIdOrThrowException(ProviderConstants.MDM_UPDATE_LINK_RESOURCE_ID, theTargetPersonId);
myIEmpiLinkUpdaterSvc.notDuplicatePerson(person, target, theEmpiContext);
}
}

View File

@ -1,191 +0,0 @@
package ca.uhn.fhir.jpa.empi.svc;
/*-
* #%L
* HAPI FHIR JPA Server - Enterprise Master Patient Index
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
* 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%
*/
import ca.uhn.fhir.empi.api.EmpiLinkSourceEnum;
import ca.uhn.fhir.empi.api.EmpiMatchOutcome;
import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
import ca.uhn.fhir.empi.api.IEmpiLinkSvc;
import ca.uhn.fhir.empi.log.Logs;
import ca.uhn.fhir.empi.model.CanonicalIdentityAssuranceLevel;
import ca.uhn.fhir.empi.model.MdmTransactionContext;
import ca.uhn.fhir.empi.util.AssuranceLevelUtil;
import ca.uhn.fhir.empi.util.PersonHelper;
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
import ca.uhn.fhir.jpa.empi.dao.EmpiLinkDaoSvc;
import ca.uhn.fhir.jpa.entity.EmpiLink;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseBackboneElement;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
import java.util.Optional;
/**
* This class is in charge of managing EmpiLinks between Persons and target resources
*/
@Service
public class EmpiLinkSvcImpl implements IEmpiLinkSvc {
private static final Logger ourLog = Logs.getEmpiTroubleshootingLog();
@Autowired
private EmpiResourceDaoSvc myEmpiResourceDaoSvc;
@Autowired
private EmpiLinkDaoSvc myEmpiLinkDaoSvc;
@Autowired
private PersonHelper myPersonHelper;
@Autowired
private IdHelperService myIdHelperService;
@Override
@Transactional
public void updateLink(IAnyResource thePerson, IAnyResource theTarget, EmpiMatchOutcome theMatchOutcome, EmpiLinkSourceEnum theLinkSource, MdmTransactionContext theMdmTransactionContext) {
IIdType resourceId = theTarget.getIdElement().toUnqualifiedVersionless();
if (theMatchOutcome.isPossibleDuplicate() && personsLinkedAsNoMatch(thePerson, theTarget)) {
log(theMdmTransactionContext, thePerson.getIdElement().toUnqualifiedVersionless() +
" is linked as NO_MATCH with " +
theTarget.getIdElement().toUnqualifiedVersionless() +
" not linking as POSSIBLE_DUPLICATE.");
return;
}
EmpiMatchResultEnum matchResultEnum = theMatchOutcome.getMatchResultEnum();
validateRequestIsLegal(thePerson, theTarget, matchResultEnum, theLinkSource);
// switch (matchResultEnum) {
// case MATCH:
// myPersonHelper.addOrUpdateLink(thePerson, resourceId, AssuranceLevelUtil.getAssuranceLevel(matchResultEnum, theLinkSource), theEmpiTransactionContext);
// myEmpiResourceDaoSvc.updatePerson(thePerson);
// break;
// case POSSIBLE_MATCH:
// myPersonHelper.addOrUpdateLink(thePerson, resourceId, AssuranceLevelUtil.getAssuranceLevel(matchResultEnum, theLinkSource), theEmpiTransactionContext);
// break;
// case NO_MATCH:
// myPersonHelper.removeLink(thePerson, resourceId, theEmpiTransactionContext);
// break;
// case POSSIBLE_DUPLICATE:
// break;
// }
myEmpiResourceDaoSvc.upsertSourceResource(thePerson, theMdmTransactionContext.getResourceType());
createOrUpdateLinkEntity(thePerson, theTarget, theMatchOutcome, theLinkSource, theMdmTransactionContext);
}
private boolean personsLinkedAsNoMatch(IAnyResource thePerson, IAnyResource theTarget) {
Long personId = myIdHelperService.getPidOrThrowException(thePerson);
Long targetId = myIdHelperService.getPidOrThrowException(theTarget);
// TODO perf collapse into one query
return myEmpiLinkDaoSvc.getEmpiLinksByPersonPidTargetPidAndMatchResult(personId, targetId, EmpiMatchResultEnum.NO_MATCH).isPresent() ||
myEmpiLinkDaoSvc.getEmpiLinksByPersonPidTargetPidAndMatchResult(targetId, personId, EmpiMatchResultEnum.NO_MATCH).isPresent();
}
// @Override
// @Transactional
// public void syncEmpiLinksToPersonLinks(IAnyResource thePersonResource, EmpiTransactionContext theEmpiTransactionContext) {
// // int origLinkCount = myPersonHelper.getLinkCount(thePersonResource);
// int origLinkCount = myEmpiLinkDaoSvc.findEmpiMatchLinksBySource(thePersonResource).size();
//
// List<EmpiLink> empiLinks = myEmpiLinkDaoSvc.findEmpiLinksBySourceResource(thePersonResource);
//
// List<IBaseBackboneElement> newLinks = empiLinks.stream()
// .filter(link -> link.isMatch() || link.isPossibleMatch() || link.isRedirect())
// .map(this::personLinkFromEmpiLink)
// .collect(Collectors.toList());
// myPersonHelper.setLinks(thePersonResource, newLinks);
// if (newLinks.size() > origLinkCount) {
// log(theEmpiTransactionContext, thePersonResource.getIdElement().toVersionless() + " links increased from " + origLinkCount + " to " + newLinks.size());
// } else if (newLinks.size() < origLinkCount) {
// log(theEmpiTransactionContext, thePersonResource.getIdElement().toVersionless() + " links decreased from " + origLinkCount + " to " + newLinks.size());
// }
//
// }
@Override
public void deleteLink(IAnyResource theSourceResource, IAnyResource theTargetResource, MdmTransactionContext theMdmTransactionContext) {
// myPersonHelper.removeLink(theExistingPerson, theResource.getIdElement(), theEmpiTransactionContext);
// myEmpiLinkDaoSvc.deleteEmpiLinks(theSourceResource, theTargetResource);
Optional<EmpiLink> oEmpiLink = getEmpiLinkForPersonTargetPair(theSourceResource, theTargetResource);
if (oEmpiLink.isPresent()) {
EmpiLink empiLink = oEmpiLink.get();
log(theMdmTransactionContext, "Deleting EmpiLink [" + theSourceResource.getIdElement().toVersionless() + " -> " + theTargetResource.getIdElement().toVersionless() + "] with result: " + empiLink.getMatchResult());
myEmpiLinkDaoSvc.deleteLink(empiLink);
}
}
private IBaseBackboneElement personLinkFromEmpiLink(EmpiLink empiLink) {
IIdType resourceId = myIdHelperService.resourceIdFromPidOrThrowException(empiLink.getTargetPid());
CanonicalIdentityAssuranceLevel assuranceLevel = AssuranceLevelUtil.getAssuranceLevel(empiLink.getMatchResult(), empiLink.getLinkSource());
return myPersonHelper.newPersonLink(resourceId, assuranceLevel);
}
/**
* Helper function which runs various business rules about what types of requests are allowed.
*/
private void validateRequestIsLegal(IAnyResource thePerson, IAnyResource theResource, EmpiMatchResultEnum theMatchResult, EmpiLinkSourceEnum theLinkSource) {
Optional<EmpiLink> oExistingLink = getEmpiLinkForPersonTargetPair(thePerson, theResource);
if (oExistingLink.isPresent() && systemIsAttemptingToModifyManualLink(theLinkSource, oExistingLink.get())) {
throw new InternalErrorException("EMPI system is not allowed to modify links on manually created links");
}
if (systemIsAttemptingToAddNoMatch(theLinkSource, theMatchResult)) {
throw new InternalErrorException("EMPI system is not allowed to automatically NO_MATCH a resource");
}
}
/**
* Helper function which detects when the EMPI system is attempting to add a NO_MATCH link, which is not allowed.
*/
private boolean systemIsAttemptingToAddNoMatch(EmpiLinkSourceEnum theLinkSource, EmpiMatchResultEnum theMatchResult) {
return theLinkSource == EmpiLinkSourceEnum.AUTO && theMatchResult == EmpiMatchResultEnum.NO_MATCH;
}
/**
* Helper function to let us catch when System EMPI rules are attempting to override a manually defined link.
*/
private boolean systemIsAttemptingToModifyManualLink(EmpiLinkSourceEnum theIncomingSource, EmpiLink theExistingSource) {
return theIncomingSource == EmpiLinkSourceEnum.AUTO && theExistingSource.isManual();
}
private Optional<EmpiLink> getEmpiLinkForPersonTargetPair(IAnyResource thePerson, IAnyResource theCandidate) {
if (thePerson.getIdElement().getIdPart() == null || theCandidate.getIdElement().getIdPart() == null) {
return Optional.empty();
} else {
return myEmpiLinkDaoSvc.getLinkBySourceResourcePidAndTargetResourcePid(
myIdHelperService.getPidOrNull(thePerson),
myIdHelperService.getPidOrNull(theCandidate)
);
}
}
private void createOrUpdateLinkEntity(IBaseResource theSourceResource, IBaseResource theTargetResource, EmpiMatchOutcome theMatchOutcome, EmpiLinkSourceEnum theLinkSource, MdmTransactionContext theMdmTransactionContext) {
myEmpiLinkDaoSvc.createOrUpdateLinkEntity(theSourceResource, theTargetResource, theMatchOutcome, theLinkSource, theMdmTransactionContext);
}
private void log(MdmTransactionContext theMdmTransactionContext, String theMessage) {
theMdmTransactionContext.addTransactionLogMessage(theMessage);
ourLog.debug(theMessage);
}
}

View File

@ -1,165 +0,0 @@
package ca.uhn.fhir.jpa.empi.svc;
/*-
* #%L
* HAPI FHIR JPA Server - Enterprise Master Patient Index
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
* 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%
*/
import ca.uhn.fhir.empi.api.EmpiLinkSourceEnum;
import ca.uhn.fhir.empi.api.EmpiMatchOutcome;
import ca.uhn.fhir.empi.api.IEmpiLinkSvc;
import ca.uhn.fhir.empi.log.Logs;
import ca.uhn.fhir.empi.model.MdmTransactionContext;
import ca.uhn.fhir.empi.util.EmpiUtil;
import ca.uhn.fhir.empi.util.PersonHelper;
import ca.uhn.fhir.jpa.empi.svc.candidate.CandidateList;
import ca.uhn.fhir.jpa.empi.svc.candidate.EmpiSourceResourceFindingSvc;
import ca.uhn.fhir.jpa.empi.svc.candidate.MatchedSourceResourceCandidate;
import ca.uhn.fhir.rest.server.TransactionLogMessages;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* EmpiMatchLinkSvc is the entrypoint for HAPI's EMPI system. An incoming resource can call
* updateEmpiLinksForEmpiTarget and the underlying EMPI system will take care of matching it to a person, or creating a
* new Person if a suitable one was not found.
*/
@Service
public class EmpiMatchLinkSvc {
private static final Logger ourLog = Logs.getEmpiTroubleshootingLog();
@Autowired
private IEmpiLinkSvc myEmpiLinkSvc;
@Autowired
private EmpiSourceResourceFindingSvc myEmpiSourceResourceFindingSvc;
@Autowired
private PersonHelper myGoldenResourceHelper;
@Autowired
private EmpiEidUpdateService myEidUpdateService;
/**
* Given an Empi Target (consisting of either a Patient or a Practitioner), find a suitable Person candidate for them,
* or create one if one does not exist. Performs matching based on rules defined in empi-rules.json.
* Does nothing if resource is determined to be not managed by EMPI.
*
* @param theResource the incoming EMPI target, which is either a Patient or Practitioner.
* @param theMdmTransactionContext
* @return an {@link TransactionLogMessages} which contains all informational messages related to EMPI processing of this resource.
*/
public MdmTransactionContext updateEmpiLinksForEmpiTarget(IAnyResource theResource, MdmTransactionContext theMdmTransactionContext) {
if (EmpiUtil.isEmpiAccessible(theResource)) {
return doEmpiUpdate(theResource, theMdmTransactionContext);
} else {
return null;
}
}
private MdmTransactionContext doEmpiUpdate(IAnyResource theResource, MdmTransactionContext theMdmTransactionContext) {
CandidateList candidateList = myEmpiSourceResourceFindingSvc.findSourceResourceCandidates(theResource);
if (candidateList.isEmpty()) {
handleEmpiWithNoCandidates(theResource, theMdmTransactionContext);
} else if (candidateList.exactlyOneMatch()) {
handleEmpiWithSingleCandidate(theResource, candidateList.getOnlyMatch(), theMdmTransactionContext);
} else {
handleEmpiWithMultipleCandidates(theResource, candidateList, theMdmTransactionContext);
}
return theMdmTransactionContext;
}
private void handleEmpiWithMultipleCandidates(IAnyResource theResource, CandidateList theCandidateList, MdmTransactionContext theMdmTransactionContext) {
MatchedSourceResourceCandidate firstMatch = theCandidateList.getFirstMatch();
Long samplePersonPid = firstMatch.getCandidatePersonPid().getIdAsLong();
boolean allSamePerson = theCandidateList.stream()
.allMatch(candidate -> candidate.getCandidatePersonPid().getIdAsLong().equals(samplePersonPid));
if (allSamePerson) {
log(theMdmTransactionContext, "EMPI received multiple match candidates, but they are all linked to the same person.");
handleEmpiWithSingleCandidate(theResource, firstMatch, theMdmTransactionContext);
} else {
log(theMdmTransactionContext, "EMPI received multiple match candidates, that were linked to different Persons. Setting POSSIBLE_DUPLICATES and POSSIBLE_MATCHES.");
//Set them all as POSSIBLE_MATCH
List<IAnyResource> persons = new ArrayList<>();
for (MatchedSourceResourceCandidate matchedSourceResourceCandidate : theCandidateList.getCandidates()) {
IAnyResource person = myEmpiSourceResourceFindingSvc
.getSourceResourceFromMatchedSourceResourceCandidate(matchedSourceResourceCandidate, theMdmTransactionContext.getResourceType());
EmpiMatchOutcome outcome = EmpiMatchOutcome.POSSIBLE_MATCH;
outcome.setEidMatch(theCandidateList.isEidMatch());
myEmpiLinkSvc.updateLink(person, theResource, outcome, EmpiLinkSourceEnum.AUTO, theMdmTransactionContext);
persons.add(person);
}
//Set all Persons as POSSIBLE_DUPLICATE of the last person.
IAnyResource firstPerson = persons.get(0);
persons.subList(1, persons.size())
.forEach(possibleDuplicatePerson -> {
EmpiMatchOutcome outcome = EmpiMatchOutcome.POSSIBLE_DUPLICATE;
outcome.setEidMatch(theCandidateList.isEidMatch());
myEmpiLinkSvc.updateLink(firstPerson, possibleDuplicatePerson, outcome, EmpiLinkSourceEnum.AUTO, theMdmTransactionContext);
});
}
}
private void handleEmpiWithNoCandidates(IAnyResource theResource, MdmTransactionContext theMdmTransactionContext) {
log(theMdmTransactionContext, String.format("There were no matched candidates for EMPI, creating a new %s.", theResource.getIdElement().getResourceType()));
IAnyResource newPerson = myGoldenResourceHelper.createGoldenResourceFromMdmTarget(theResource);
// TODO GGG :)
// 1. Get the right helper
// 2. Create source resoruce for the EMPI target
// 3. UPDATE EMPI LINK TABLE
myEmpiLinkSvc.updateLink(newPerson, theResource, EmpiMatchOutcome.NEW_PERSON_MATCH, EmpiLinkSourceEnum.AUTO, theMdmTransactionContext);
}
private void handleEmpiCreate(IAnyResource theTargetResource, MatchedSourceResourceCandidate thePersonCandidate, MdmTransactionContext theMdmTransactionContext) {
log(theMdmTransactionContext, "EMPI has narrowed down to one candidate for matching.");
IAnyResource sourceResource = myEmpiSourceResourceFindingSvc.getSourceResourceFromMatchedSourceResourceCandidate(thePersonCandidate, theMdmTransactionContext.getResourceType());
if (myGoldenResourceHelper.isPotentialDuplicate(sourceResource, theTargetResource)) {
log(theMdmTransactionContext, "Duplicate detected based on the fact that both resources have different external EIDs.");
IAnyResource newSourceResource = myGoldenResourceHelper.createGoldenResourceFromMdmTarget(theTargetResource);
myEmpiLinkSvc.updateLink(newSourceResource, theTargetResource, EmpiMatchOutcome.NEW_PERSON_MATCH, EmpiLinkSourceEnum.AUTO, theMdmTransactionContext);
myEmpiLinkSvc.updateLink(newSourceResource, sourceResource, EmpiMatchOutcome.POSSIBLE_DUPLICATE, EmpiLinkSourceEnum.AUTO, theMdmTransactionContext);
} else {
if (thePersonCandidate.isMatch()) {
myGoldenResourceHelper.handleExternalEidAddition(sourceResource, theTargetResource, theMdmTransactionContext);
// myPersonHelper.updatePersonFromNewlyCreatedEmpiTarget(person, theResource, theEmpiTransactionContext);
}
myEmpiLinkSvc.updateLink(sourceResource, theTargetResource, thePersonCandidate.getMatchResult(), EmpiLinkSourceEnum.AUTO, theMdmTransactionContext);
}
}
private void handleEmpiWithSingleCandidate(IAnyResource theResource, MatchedSourceResourceCandidate thePersonCandidate, MdmTransactionContext theMdmTransactionContext) {
log(theMdmTransactionContext, "EMPI has narrowed down to one candidate for matching.");
if (theMdmTransactionContext.getRestOperation().equals(MdmTransactionContext.OperationType.UPDATE_RESOURCE)) {
myEidUpdateService.handleEmpiUpdate(theResource, thePersonCandidate, theMdmTransactionContext);
} else {
handleEmpiCreate(theResource, thePersonCandidate, theMdmTransactionContext);
}
}
private void log(MdmTransactionContext theMdmTransactionContext, String theMessage) {
theMdmTransactionContext.addTransactionLogMessage(theMessage);
ourLog.debug(theMessage);
}
}

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.jpa.empi.broker;
package ca.uhn.fhir.jpa.mdm.broker;
/*-
* #%L
@ -21,14 +21,14 @@ package ca.uhn.fhir.jpa.empi.broker;
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.empi.api.IEmpiSettings;
import ca.uhn.fhir.empi.log.Logs;
import ca.uhn.fhir.empi.model.MdmTransactionContext;
import ca.uhn.fhir.mdm.api.IMdmSettings;
import ca.uhn.fhir.mdm.log.Logs;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
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.jpa.empi.svc.EmpiMatchLinkSvc;
import ca.uhn.fhir.jpa.empi.svc.EmpiResourceFilteringSvc;
import ca.uhn.fhir.jpa.mdm.svc.MdmMatchLinkSvc;
import ca.uhn.fhir.jpa.mdm.svc.MdmResourceFilteringSvc;
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedJsonMessage;
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage;
import ca.uhn.fhir.rest.server.TransactionLogMessages;
@ -43,19 +43,19 @@ import org.springframework.messaging.MessagingException;
import org.springframework.stereotype.Service;
@Service
public class EmpiMessageHandler implements MessageHandler {
private static final Logger ourLog = Logs.getEmpiTroubleshootingLog();
public class MdmMessageHandler implements MessageHandler {
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
@Autowired
private EmpiMatchLinkSvc myEmpiMatchLinkSvc;
private MdmMatchLinkSvc myMdmMatchLinkSvc;
@Autowired
private IInterceptorBroadcaster myInterceptorBroadcaster;
@Autowired
private FhirContext myFhirContext;
@Autowired
private EmpiResourceFilteringSvc myEmpiResourceFilteringSvc;
private MdmResourceFilteringSvc myMdmResourceFilteringSvc;
@Autowired
private IEmpiSettings myEmpiSettings;
private IMdmSettings myMdmSettings;
@Override
public void handleMessage(Message<?> theMessage) throws MessagingException {
@ -68,75 +68,75 @@ public class EmpiMessageHandler implements MessageHandler {
ResourceModifiedMessage msg = ((ResourceModifiedJsonMessage) theMessage).getPayload();
try {
if (myEmpiResourceFilteringSvc.shouldBeProcessed(getResourceFromPayload(msg))) {
matchEmpiAndUpdateLinks(msg);
if (myMdmResourceFilteringSvc.shouldBeProcessed(getResourceFromPayload(msg))) {
matchMdmAndUpdateLinks(msg);
}
} catch (Exception e) {
ourLog.error("Failed to handle EMPI Matching Resource:", e);
ourLog.error("Failed to handle MDM Matching Resource:", e);
throw e;
}
}
public void matchEmpiAndUpdateLinks(ResourceModifiedMessage theMsg) {
public void matchMdmAndUpdateLinks(ResourceModifiedMessage theMsg) {
String resourceType = theMsg.getId(myFhirContext).getResourceType();
validateResourceType(resourceType);
MdmTransactionContext empiContext = createEmpiContext(theMsg, resourceType);
MdmTransactionContext mdmContext = createMdmContext(theMsg, resourceType);
try {
switch (theMsg.getOperationType()) {
case CREATE:
handleCreatePatientOrPractitioner(theMsg, empiContext);
handleCreatePatientOrPractitioner(theMsg, mdmContext);
break;
case UPDATE:
case MANUALLY_TRIGGERED:
handleUpdatePatientOrPractitioner(theMsg, empiContext);
handleUpdatePatientOrPractitioner(theMsg, mdmContext);
break;
case DELETE:
default:
ourLog.trace("Not processing modified message for {}", theMsg.getOperationType());
}
}catch (Exception e) {
log(empiContext, "Failure during EMPI processing: " + e.getMessage(), e);
log(mdmContext, "Failure during MDM processing: " + e.getMessage(), e);
} finally {
// Interceptor call: EMPI_AFTER_PERSISTED_RESOURCE_CHECKED
// Interceptor call: MDM_AFTER_PERSISTED_RESOURCE_CHECKED
ResourceOperationMessage outgoingMsg = new ResourceOperationMessage(myFhirContext, theMsg.getPayload(myFhirContext), theMsg.getOperationType());
outgoingMsg.setTransactionId(theMsg.getTransactionId());
HookParams params = new HookParams()
.add(ResourceOperationMessage.class, outgoingMsg)
.add(TransactionLogMessages.class, empiContext.getTransactionLogMessages());
myInterceptorBroadcaster.callHooks(Pointcut.EMPI_AFTER_PERSISTED_RESOURCE_CHECKED, params);
.add(TransactionLogMessages.class, mdmContext.getTransactionLogMessages());
myInterceptorBroadcaster.callHooks(Pointcut.MDM_AFTER_PERSISTED_RESOURCE_CHECKED, params);
}
}
private MdmTransactionContext createEmpiContext(ResourceModifiedMessage theMsg, String theResourceType) {
private MdmTransactionContext createMdmContext(ResourceModifiedMessage theMsg, String theResourceType) {
TransactionLogMessages transactionLogMessages = TransactionLogMessages.createFromTransactionGuid(theMsg.getTransactionId());
MdmTransactionContext.OperationType empiOperation;
MdmTransactionContext.OperationType mdmOperation;
switch (theMsg.getOperationType()) {
case CREATE:
empiOperation = MdmTransactionContext.OperationType.CREATE_RESOURCE;
mdmOperation = MdmTransactionContext.OperationType.CREATE_RESOURCE;
break;
case UPDATE:
empiOperation = MdmTransactionContext.OperationType.UPDATE_RESOURCE;
mdmOperation = MdmTransactionContext.OperationType.UPDATE_RESOURCE;
break;
case MANUALLY_TRIGGERED:
empiOperation = MdmTransactionContext.OperationType.SUBMIT_RESOURCE_TO_EMPI;
mdmOperation = MdmTransactionContext.OperationType.SUBMIT_RESOURCE_TO_MDM;
break;
case DELETE:
default:
ourLog.trace("Not creating an EmpiTransactionContext for {}", theMsg.getOperationType());
throw new InvalidRequestException("We can't handle non-update/create operations in EMPI");
ourLog.trace("Not creating an MdmTransactionContext for {}", theMsg.getOperationType());
throw new InvalidRequestException("We can't handle non-update/create operations in MDM");
}
return new MdmTransactionContext(transactionLogMessages, empiOperation, theResourceType);
return new MdmTransactionContext(transactionLogMessages, mdmOperation, theResourceType);
}
private void validateResourceType(String theResourceType) {
if (!myEmpiSettings.isSupportedMdmType(theResourceType)) {
throw new IllegalStateException("Unsupported resource type submitted to EMPI matching queue: " + theResourceType);
if (!myMdmSettings.isSupportedMdmType(theResourceType)) {
throw new IllegalStateException("Unsupported resource type submitted to MDM matching queue: " + theResourceType);
}
}
private void handleCreatePatientOrPractitioner(ResourceModifiedMessage theMsg, MdmTransactionContext theMdmTransactionContext) {
myEmpiMatchLinkSvc.updateEmpiLinksForEmpiTarget(getResourceFromPayload(theMsg), theMdmTransactionContext);
myMdmMatchLinkSvc.updateMdmLinksForMdmTarget(getResourceFromPayload(theMsg), theMdmTransactionContext);
}
private IAnyResource getResourceFromPayload(ResourceModifiedMessage theMsg) {
@ -144,16 +144,16 @@ public class EmpiMessageHandler implements MessageHandler {
}
private void handleUpdatePatientOrPractitioner(ResourceModifiedMessage theMsg, MdmTransactionContext theMdmTransactionContext) {
myEmpiMatchLinkSvc.updateEmpiLinksForEmpiTarget(getResourceFromPayload(theMsg), theMdmTransactionContext);
myMdmMatchLinkSvc.updateMdmLinksForMdmTarget(getResourceFromPayload(theMsg), theMdmTransactionContext);
}
private void log(MdmTransactionContext theEmpiContext, String theMessage) {
theEmpiContext.addTransactionLogMessage(theMessage);
private void log(MdmTransactionContext theMdmContext, String theMessage) {
theMdmContext.addTransactionLogMessage(theMessage);
ourLog.debug(theMessage);
}
private void log(MdmTransactionContext theEmpiContext, String theMessage, Exception theException) {
theEmpiContext.addTransactionLogMessage(theMessage);
private void log(MdmTransactionContext theMdmContext, String theMessage, Exception theException) {
theMdmContext.addTransactionLogMessage(theMessage);
ourLog.error(theMessage, theException);
}
}

View File

@ -1,7 +1,7 @@
package ca.uhn.fhir.jpa.empi.broker;
package ca.uhn.fhir.jpa.mdm.broker;
import ca.uhn.fhir.empi.api.IEmpiSettings;
import ca.uhn.fhir.empi.log.Logs;
import ca.uhn.fhir.mdm.api.IMdmSettings;
import ca.uhn.fhir.mdm.log.Logs;
import ca.uhn.fhir.jpa.subscription.channel.api.ChannelConsumerSettings;
import ca.uhn.fhir.jpa.subscription.channel.api.IChannelFactory;
import ca.uhn.fhir.jpa.subscription.channel.api.IChannelReceiver;
@ -35,29 +35,29 @@ import javax.annotation.PreDestroy;
*/
@Service
public class EmpiQueueConsumerLoader {
private static final Logger ourLog = Logs.getEmpiTroubleshootingLog();
public class MdmQueueConsumerLoader {
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
@Autowired
private EmpiMessageHandler myEmpiMessageHandler;
private MdmMessageHandler myMdmMessageHandler;
@Autowired
private IChannelFactory myChannelFactory;
@Autowired
private IEmpiSettings myEmpiSettings;
private IMdmSettings myMdmSettings;
protected IChannelReceiver myEmpiChannel;
protected IChannelReceiver myMdmChannel;
@PostConstruct
public void startListeningToEmpiChannel() {
if (myEmpiChannel == null) {
public void startListeningToMdmChannel() {
if (myMdmChannel == null) {
ChannelConsumerSettings config = new ChannelConsumerSettings();
config.setConcurrentConsumers(myEmpiSettings.getConcurrentConsumers());
myEmpiChannel = myChannelFactory.getOrCreateReceiver(IEmpiSettings.EMPI_CHANNEL_NAME, ResourceModifiedJsonMessage.class, config);
if (myEmpiChannel == null) {
ourLog.error("Unable to create receiver for {}", IEmpiSettings.EMPI_CHANNEL_NAME);
config.setConcurrentConsumers(myMdmSettings.getConcurrentConsumers());
myMdmChannel = myChannelFactory.getOrCreateReceiver(IMdmSettings.MDM_CHANNEL_NAME, ResourceModifiedJsonMessage.class, config);
if (myMdmChannel == null) {
ourLog.error("Unable to create receiver for {}", IMdmSettings.MDM_CHANNEL_NAME);
} else {
myEmpiChannel.subscribe(myEmpiMessageHandler);
ourLog.info("EMPI Matching Consumer subscribed to Matching Channel {} with name {}", myEmpiChannel.getClass().getName(), myEmpiChannel.getName());
myMdmChannel.subscribe(myMdmMessageHandler);
ourLog.info("MDM Matching Consumer subscribed to Matching Channel {} with name {}", myMdmChannel.getClass().getName(), myMdmChannel.getName());
}
}
}
@ -65,14 +65,14 @@ public class EmpiQueueConsumerLoader {
@SuppressWarnings("unused")
@PreDestroy
public void stop() {
if (myEmpiChannel != null) {
myEmpiChannel.unsubscribe(myEmpiMessageHandler);
ourLog.info("EMPI Matching Consumer unsubscribed from Matching Channel {} with name {}", myEmpiChannel.getClass().getName(), myEmpiChannel.getName());
if (myMdmChannel != null) {
myMdmChannel.unsubscribe(myMdmMessageHandler);
ourLog.info("MDM Matching Consumer unsubscribed from Matching Channel {} with name {}", myMdmChannel.getClass().getName(), myMdmChannel.getName());
}
}
@VisibleForTesting
public IChannelReceiver getEmpiChannelForUnitTest() {
return myEmpiChannel;
public IChannelReceiver getMdmChannelForUnitTest() {
return myMdmChannel;
}
}

View File

@ -0,0 +1,240 @@
package ca.uhn.fhir.jpa.mdm.config;
/*-
* #%L
* HAPI FHIR JPA Server - Enterprise Master Patient Index
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
* 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%
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.mdm.api.IMdmControllerSvc;
import ca.uhn.fhir.mdm.api.IMdmExpungeSvc;
import ca.uhn.fhir.mdm.api.IMdmLinkQuerySvc;
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.IGoldenResourceMergerSvc;
import ca.uhn.fhir.mdm.api.IMdmSettings;
import ca.uhn.fhir.mdm.log.Logs;
import ca.uhn.fhir.mdm.provider.MdmControllerHelper;
import ca.uhn.fhir.mdm.provider.MdmProviderLoader;
import ca.uhn.fhir.mdm.rules.config.MdmRuleValidator;
import ca.uhn.fhir.mdm.rules.svc.MdmResourceMatcherSvc;
import ca.uhn.fhir.mdm.util.EIDHelper;
import ca.uhn.fhir.mdm.util.MessageHelper;
import ca.uhn.fhir.mdm.util.GoldenResourceHelper;
import ca.uhn.fhir.jpa.dao.mdm.MdmLinkDeleteSvc;
import ca.uhn.fhir.jpa.mdm.broker.MdmMessageHandler;
import ca.uhn.fhir.jpa.mdm.broker.MdmQueueConsumerLoader;
import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc;
import ca.uhn.fhir.jpa.mdm.dao.MdmLinkFactory;
import ca.uhn.fhir.jpa.mdm.interceptor.MdmStorageInterceptor;
import ca.uhn.fhir.jpa.mdm.interceptor.IMdmStorageInterceptor;
import ca.uhn.fhir.jpa.mdm.svc.MdmClearSvcImpl;
import ca.uhn.fhir.jpa.mdm.svc.MdmControllerSvcImpl;
import ca.uhn.fhir.jpa.mdm.svc.MdmEidUpdateService;
import ca.uhn.fhir.jpa.mdm.svc.MdmLinkQuerySvcImpl;
import ca.uhn.fhir.jpa.mdm.svc.MdmLinkSvcImpl;
import ca.uhn.fhir.jpa.mdm.svc.MdmLinkUpdaterSvcImpl;
import ca.uhn.fhir.jpa.mdm.svc.MdmMatchFinderSvcImpl;
import ca.uhn.fhir.jpa.mdm.svc.MdmMatchLinkSvc;
import ca.uhn.fhir.jpa.mdm.svc.MdmGoldenResourceDeletingSvc;
import ca.uhn.fhir.jpa.mdm.svc.GoldenResourceMergerSvcImpl;
import ca.uhn.fhir.jpa.mdm.svc.MdmResourceDaoSvc;
import ca.uhn.fhir.jpa.mdm.svc.MdmResourceFilteringSvc;
import ca.uhn.fhir.jpa.mdm.svc.candidate.MdmCandidateSearchCriteriaBuilderSvc;
import ca.uhn.fhir.jpa.mdm.svc.candidate.MdmCandidateSearchSvc;
import ca.uhn.fhir.jpa.mdm.svc.candidate.MdmGoldenResourceFindingSvc;
import ca.uhn.fhir.jpa.mdm.svc.candidate.FindCandidateByEidSvc;
import ca.uhn.fhir.jpa.mdm.svc.candidate.FindCandidateByLinkSvc;
import ca.uhn.fhir.jpa.mdm.svc.candidate.FindCandidateByScoreSvc;
import ca.uhn.fhir.rest.server.util.ISearchParamRetriever;
import ca.uhn.fhir.validation.IResourceLoader;
import org.slf4j.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MdmConsumerConfig {
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
@Bean
IMdmStorageInterceptor mdmStorageInterceptor() {
return new MdmStorageInterceptor();
}
@Bean
MdmQueueConsumerLoader mdmQueueConsumerLoader() {
return new MdmQueueConsumerLoader();
}
@Bean
MdmMessageHandler mdmMessageHandler() {
return new MdmMessageHandler();
}
@Bean
MdmMatchLinkSvc mdmMatchLinkSvc() {
return new MdmMatchLinkSvc();
}
@Bean
MdmEidUpdateService eidUpdateService() {
return new MdmEidUpdateService();
}
@Bean
MdmResourceDaoSvc mdmResourceDaoSvc() {
return new MdmResourceDaoSvc();
}
@Bean
IMdmLinkSvc mdmLinkSvc() {
return new MdmLinkSvcImpl();
}
@Bean
GoldenResourceHelper goldenResourceHelper(FhirContext theFhirContext) {
return new GoldenResourceHelper(theFhirContext);
}
@Bean
MessageHelper messageHelper(IMdmSettings theMdmSettings, FhirContext theFhirContext) {
return new MessageHelper(theMdmSettings, theFhirContext);
}
@Bean
MdmSubscriptionLoader mdmSubscriptionLoader() {
return new MdmSubscriptionLoader();
}
@Bean
MdmSearchParameterLoader mdmSearchParameterLoader() {
return new MdmSearchParameterLoader();
}
@Bean
MdmGoldenResourceFindingSvc mdmPersonFindingSvc() {
return new MdmGoldenResourceFindingSvc();
}
@Bean
FindCandidateByEidSvc findCandidateByEidSvc() {
return new FindCandidateByEidSvc();
}
@Bean
FindCandidateByLinkSvc findCandidateByLinkSvc() {
return new FindCandidateByLinkSvc();
}
@Bean
FindCandidateByScoreSvc findCandidateByScoreSvc() {
return new FindCandidateByScoreSvc();
}
@Bean
MdmProviderLoader mdmProviderLoader() {
return new MdmProviderLoader();
}
@Bean
MdmRuleValidator mdmRuleValidator(FhirContext theFhirContext, ISearchParamRetriever theSearchParamRetriever) {
return new MdmRuleValidator(theFhirContext, theSearchParamRetriever);
}
@Bean
IMdmMatchFinderSvc mdmMatchFinderSvc() {
return new MdmMatchFinderSvcImpl();
}
@Bean
IGoldenResourceMergerSvc mdmPersonMergerSvc() {
return new GoldenResourceMergerSvcImpl();
}
@Bean
IMdmLinkQuerySvc mdmLinkQuerySvc() {
return new MdmLinkQuerySvcImpl();
}
@Bean
IMdmExpungeSvc mdmResetSvc(MdmLinkDaoSvc theMdmLinkDaoSvc, MdmGoldenResourceDeletingSvc theDeletingSvc, IMdmSettings theIMdmSettings) {
return new MdmClearSvcImpl(theMdmLinkDaoSvc, theDeletingSvc, theIMdmSettings);
}
@Bean
MdmCandidateSearchSvc mdmCandidateSearchSvc() {
return new MdmCandidateSearchSvc();
}
@Bean
MdmCandidateSearchCriteriaBuilderSvc mdmCriteriaBuilderSvc() {
return new MdmCandidateSearchCriteriaBuilderSvc();
}
@Bean
MdmResourceMatcherSvc mdmResourceComparatorSvc(FhirContext theFhirContext, IMdmSettings theMdmSettings) {
return new MdmResourceMatcherSvc(theFhirContext, theMdmSettings);
}
@Bean
EIDHelper eidHelper(FhirContext theFhirContext, IMdmSettings theMdmSettings) {
return new EIDHelper(theFhirContext, theMdmSettings);
}
@Bean
MdmLinkDaoSvc mdmLinkDaoSvc() {
return new MdmLinkDaoSvc();
}
@Bean
MdmLinkFactory mdmLinkFactory(IMdmSettings theMdmSettings) {
return new MdmLinkFactory(theMdmSettings);
}
@Bean
IMdmLinkUpdaterSvc mdmLinkUpdaterSvc() {
return new MdmLinkUpdaterSvcImpl();
}
@Bean
MdmLoader mdmLoader() {
return new MdmLoader();
}
@Bean
MdmLinkDeleteSvc mdmLinkDeleteSvc() {
return new MdmLinkDeleteSvc();
}
@Bean
MdmResourceFilteringSvc mdmResourceFilteringSvc() {
return new MdmResourceFilteringSvc();
}
@Bean
MdmControllerHelper mdmProviderHelper(FhirContext theFhirContext, IResourceLoader theResourceLoader, IMdmSettings theMdmSettings, MessageHelper messageHelper) {
return new MdmControllerHelper(theFhirContext, theResourceLoader, theMdmSettings, messageHelper);
}
@Bean
IMdmControllerSvc mdmControllerSvc() {
return new MdmControllerSvcImpl();
}
}

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.jpa.empi.config;
package ca.uhn.fhir.jpa.mdm.config;
/*-
* #%L
@ -20,8 +20,8 @@ package ca.uhn.fhir.jpa.empi.config;
* #L%
*/
import ca.uhn.fhir.empi.api.IEmpiSettings;
import ca.uhn.fhir.empi.provider.EmpiProviderLoader;
import ca.uhn.fhir.mdm.api.IMdmSettings;
import ca.uhn.fhir.mdm.provider.MdmProviderLoader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@ -31,34 +31,35 @@ import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;
@Service
public class EmpiLoader {
private static final Logger ourLog = LoggerFactory.getLogger(EmpiLoader.class);
public class MdmLoader {
private static final Logger ourLog = LoggerFactory.getLogger(MdmLoader.class);
@Autowired
IEmpiSettings myEmpiProperties;
IMdmSettings myMdmSettings;
@Autowired
EmpiProviderLoader myEmpiProviderLoader;
MdmProviderLoader myMdmProviderLoader;
@Autowired
EmpiSubscriptionLoader myEmpiSubscriptionLoader;
MdmSubscriptionLoader myMdmSubscriptionLoader;
@Autowired
EmpiSearchParameterLoader myEmpiSearchParameterLoader;
MdmSearchParameterLoader myMdmSearchParameterLoader;
@EventListener(classes = {ContextRefreshedEvent.class})
// This @Order is here to ensure that MatchingQueueSubscriberLoader has initialized before we initialize this.
// Otherwise the EMPI subscriptions won't get loaded into the SubscriptionRegistry
// Otherwise the MDM subscriptions won't get loaded into the SubscriptionRegistry
@Order
public void updateSubscriptions() {
if (!myEmpiProperties.isEnabled()) {
if (!myMdmSettings.isEnabled()) {
return;
}
myEmpiProviderLoader.loadProvider();
ourLog.info("EMPI provider registered");
myMdmProviderLoader.loadProvider();
ourLog.info("MDM provider registered");
myEmpiSubscriptionLoader.daoUpdateEmpiSubscriptions();
ourLog.info("EMPI subscriptions updated");
myMdmSubscriptionLoader.daoUpdateMdmSubscriptions();
ourLog.info("MDM subscriptions updated");
myEmpiSearchParameterLoader.daoUpdateEmpiSearchParameters();
ourLog.info("EMPI search parameters updated");
//TODO GGG MDM: Do we need these search parameters, or equivalent, anymore? Don't think so... ask @fil512
myMdmSearchParameterLoader.daoUpdateMdmSearchParameters();
ourLog.info("MDM search parameters updated");
}
}

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.jpa.empi.config;
package ca.uhn.fhir.jpa.mdm.config;
/*-
* #%L
@ -22,7 +22,7 @@ package ca.uhn.fhir.jpa.empi.config;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.empi.api.EmpiConstants;
import ca.uhn.fhir.mdm.api.MdmConstants;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import org.hl7.fhir.instance.model.api.IBaseResource;
@ -32,28 +32,28 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class EmpiSearchParameterLoader {
public static final String EMPI_PERSON_ASSURANCE_SEARCH_PARAMETER_ID = "person-assurance";
public static final String EMPI_PERSON_ACTIVE_SEARCH_PARAMETER_ID = "person-active";
public class MdmSearchParameterLoader {
public static final String MDM_PERSON_ASSURANCE_SEARCH_PARAMETER_ID = "person-assurance";
public static final String MDM_PERSON_ACTIVE_SEARCH_PARAMETER_ID = "person-active";
@Autowired
public FhirContext myFhirContext;
@Autowired
public DaoRegistry myDaoRegistry;
synchronized public void daoUpdateEmpiSearchParameters() {
synchronized public void daoUpdateMdmSearchParameters() {
IBaseResource personAssurance;
IBaseResource personActive;
switch (myFhirContext.getVersion().getVersion()) {
case DSTU3:
personAssurance = buildAssuranceEmpiSearchParameterDstu3();
personActive = buildActiveEmpiSearchParameterDstu3();
personAssurance = buildAssuranceMdmSearchParameterDstu3();
personActive = buildActiveMdmSearchParameterDstu3();
break;
case R4:
personAssurance = buildAssuranceEmpiSearchParameterR4();
personActive = buildActiveEmpiSearchParameterR4();
personAssurance = buildAssuranceMdmSearchParameterR4();
personActive = buildActiveMdmSearchParameterR4();
break;
default:
throw new ConfigurationException("EMPI not supported for FHIR version " + myFhirContext.getVersion().getVersion());
throw new ConfigurationException("MDM not supported for FHIR version " + myFhirContext.getVersion().getVersion());
}
IFhirResourceDao<IBaseResource> searchParameterDao = myDaoRegistry.getResourceDao("SearchParameter");
@ -61,11 +61,11 @@ public class EmpiSearchParameterLoader {
searchParameterDao.update(personActive);
}
private org.hl7.fhir.dstu3.model.SearchParameter buildAssuranceEmpiSearchParameterDstu3() {
private org.hl7.fhir.dstu3.model.SearchParameter buildAssuranceMdmSearchParameterDstu3() {
org.hl7.fhir.dstu3.model.SearchParameter retval = new org.hl7.fhir.dstu3.model.SearchParameter();
retval.setId(EMPI_PERSON_ASSURANCE_SEARCH_PARAMETER_ID);
retval.setId(MDM_PERSON_ASSURANCE_SEARCH_PARAMETER_ID);
retval.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);
retval.getMeta().addTag().setSystem(EmpiConstants.SYSTEM_MDM_MANAGED).setCode(EmpiConstants.CODE_HAPI_MDM_MANAGED);
retval.getMeta().addTag().setSystem(MdmConstants.SYSTEM_MDM_MANAGED).setCode(MdmConstants.CODE_HAPI_MDM_MANAGED);
retval.setCode("assurance");
retval.addBase("Person");
retval.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
@ -74,11 +74,11 @@ public class EmpiSearchParameterLoader {
return retval;
}
private SearchParameter buildAssuranceEmpiSearchParameterR4() {
private SearchParameter buildAssuranceMdmSearchParameterR4() {
SearchParameter retval = new SearchParameter();
retval.setId(EMPI_PERSON_ASSURANCE_SEARCH_PARAMETER_ID);
retval.setId(MDM_PERSON_ASSURANCE_SEARCH_PARAMETER_ID);
retval.setStatus(Enumerations.PublicationStatus.ACTIVE);
retval.getMeta().addTag().setSystem(EmpiConstants.SYSTEM_MDM_MANAGED).setCode(EmpiConstants.CODE_HAPI_MDM_MANAGED);
retval.getMeta().addTag().setSystem(MdmConstants.SYSTEM_MDM_MANAGED).setCode(MdmConstants.CODE_HAPI_MDM_MANAGED);
retval.setCode("assurance");
retval.addBase("Person");
retval.setType(Enumerations.SearchParamType.TOKEN);
@ -87,11 +87,11 @@ public class EmpiSearchParameterLoader {
return retval;
}
private org.hl7.fhir.dstu3.model.SearchParameter buildActiveEmpiSearchParameterDstu3() {
private org.hl7.fhir.dstu3.model.SearchParameter buildActiveMdmSearchParameterDstu3() {
org.hl7.fhir.dstu3.model.SearchParameter retval = new org.hl7.fhir.dstu3.model.SearchParameter();
retval.setId(EMPI_PERSON_ACTIVE_SEARCH_PARAMETER_ID);
retval.setId(MDM_PERSON_ACTIVE_SEARCH_PARAMETER_ID);
retval.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);
retval.getMeta().addTag().setSystem(EmpiConstants.SYSTEM_MDM_MANAGED).setCode(EmpiConstants.CODE_HAPI_MDM_MANAGED);
retval.getMeta().addTag().setSystem(MdmConstants.SYSTEM_MDM_MANAGED).setCode(MdmConstants.CODE_HAPI_MDM_MANAGED);
retval.setCode("active");
retval.addBase("Person");
retval.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
@ -100,11 +100,11 @@ public class EmpiSearchParameterLoader {
return retval;
}
private SearchParameter buildActiveEmpiSearchParameterR4() {
private SearchParameter buildActiveMdmSearchParameterR4() {
SearchParameter retval = new SearchParameter();
retval.setId(EMPI_PERSON_ACTIVE_SEARCH_PARAMETER_ID);
retval.setId(MDM_PERSON_ACTIVE_SEARCH_PARAMETER_ID);
retval.setStatus(Enumerations.PublicationStatus.ACTIVE);
retval.getMeta().addTag().setSystem(EmpiConstants.SYSTEM_MDM_MANAGED).setCode(EmpiConstants.CODE_HAPI_MDM_MANAGED);
retval.getMeta().addTag().setSystem(MdmConstants.SYSTEM_MDM_MANAGED).setCode(MdmConstants.CODE_HAPI_MDM_MANAGED);
retval.setCode("active");
retval.addBase("Person");
retval.setType(Enumerations.SearchParamType.TOKEN);

View File

@ -0,0 +1,76 @@
package ca.uhn.fhir.jpa.mdm.config;
/*-
* #%L
* HAPI FHIR JPA Server - Enterprise Master Patient Index
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
* 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%
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.mdm.api.IMdmChannelSubmitterSvc;
import ca.uhn.fhir.mdm.api.IMdmSubmitSvc;
import ca.uhn.fhir.mdm.rules.config.MdmRuleValidator;
import ca.uhn.fhir.jpa.dao.mdm.MdmLinkDeleteSvc;
import ca.uhn.fhir.jpa.mdm.interceptor.MdmSubmitterInterceptorLoader;
import ca.uhn.fhir.jpa.mdm.svc.MdmChannelSubmitterSvcImpl;
import ca.uhn.fhir.jpa.mdm.svc.MdmGoldenResourceDeletingSvc;
import ca.uhn.fhir.jpa.mdm.svc.MdmSearchParamSvc;
import ca.uhn.fhir.jpa.mdm.svc.MdmSubmitSvcImpl;
import ca.uhn.fhir.jpa.subscription.channel.api.IChannelFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
@Configuration
public class MdmSubmitterConfig {
@Bean
MdmSubmitterInterceptorLoader mdmSubmitterInterceptorLoader() {
return new MdmSubmitterInterceptorLoader();
}
@Bean
MdmSearchParamSvc mdmSearchParamSvc() {
return new MdmSearchParamSvc();
}
@Bean
MdmRuleValidator mdmRuleValidator(FhirContext theFhirContext) {
return new MdmRuleValidator(theFhirContext, mdmSearchParamSvc());
}
@Bean
MdmLinkDeleteSvc mdmLinkDeleteSvc() {
return new MdmLinkDeleteSvc();
}
@Bean
MdmGoldenResourceDeletingSvc mdmGoldenResourceDeletingSvc() {
return new MdmGoldenResourceDeletingSvc();
}
@Bean
@Lazy
IMdmChannelSubmitterSvc mdmChannelSubmitterSvc(FhirContext theFhirContext, IChannelFactory theChannelFactory) {
return new MdmChannelSubmitterSvcImpl(theFhirContext, theChannelFactory);
}
@Bean
IMdmSubmitSvc mdmBatchService() {
return new MdmSubmitSvcImpl();
}
}

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.jpa.empi.config;
package ca.uhn.fhir.jpa.mdm.config;
/*-
* #%L
@ -22,9 +22,9 @@ package ca.uhn.fhir.jpa.empi.config;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.empi.api.EmpiConstants;
import ca.uhn.fhir.empi.api.IEmpiSettings;
import ca.uhn.fhir.empi.log.Logs;
import ca.uhn.fhir.mdm.api.MdmConstants;
import ca.uhn.fhir.mdm.api.IMdmSettings;
import ca.uhn.fhir.mdm.log.Logs;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
@ -37,12 +37,14 @@ import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class EmpiSubscriptionLoader {
private static final Logger ourLog = Logs.getEmpiTroubleshootingLog();
import java.util.List;
import java.util.stream.Collectors;
public static final String EMPI_PATIENT_SUBSCRIPTION_ID = "empi-patient";
public static final String EMPI_PRACTITIONER_SUBSCRIPTION_ID = "empi-practitioner";
@Service
public class MdmSubscriptionLoader {
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
public static final String MDM_SUBSCIPRION_ID_PREFIX = "empi-";
@Autowired
public FhirContext myFhirContext;
@Autowired
@ -51,27 +53,35 @@ public class EmpiSubscriptionLoader {
public IdHelperService myIdHelperService;
@Autowired
IChannelNamer myChannelNamer;
@Autowired
private IMdmSettings myMdmSettings;
private IFhirResourceDao<IBaseResource> mySubscriptionDao;
synchronized public void daoUpdateEmpiSubscriptions() {
IBaseResource patientSub;
IBaseResource practitionerSub;
synchronized public void daoUpdateMdmSubscriptions() {
List<IBaseResource> subscriptions;
List<String> mdmResourceTypes = myMdmSettings.getMdmRules().getMdmTypes();
switch (myFhirContext.getVersion().getVersion()) {
case DSTU3:
patientSub = buildEmpiSubscriptionDstu3(EMPI_PATIENT_SUBSCRIPTION_ID, "Patient?");
practitionerSub = buildEmpiSubscriptionDstu3(EMPI_PRACTITIONER_SUBSCRIPTION_ID, "Practitioner?");
subscriptions = mdmResourceTypes
.stream()
.map(resourceType -> buildMdmSubscriptionDstu3(MDM_SUBSCIPRION_ID_PREFIX + resourceType, resourceType+"?"))
.collect(Collectors.toList());
break;
case R4:
patientSub = buildEmpiSubscriptionR4(EMPI_PATIENT_SUBSCRIPTION_ID, "Patient?");
practitionerSub = buildEmpiSubscriptionR4(EMPI_PRACTITIONER_SUBSCRIPTION_ID, "Practitioner?");
subscriptions = mdmResourceTypes
.stream()
.map(resourceType -> buildMdmSubscriptionR4(MDM_SUBSCIPRION_ID_PREFIX + resourceType, resourceType+"?"))
.collect(Collectors.toList());
break;
default:
throw new ConfigurationException("EMPI not supported for FHIR version " + myFhirContext.getVersion().getVersion());
throw new ConfigurationException("MDM not supported for FHIR version " + myFhirContext.getVersion().getVersion());
}
mySubscriptionDao = myDaoRegistry.getResourceDao("Subscription");
updateIfNotPresent(patientSub);
updateIfNotPresent(practitionerSub);
for (IBaseResource subscription : subscriptions) {
updateIfNotPresent(subscription);
}
}
private synchronized void updateIfNotPresent(IBaseResource theSubscription) {
@ -83,30 +93,30 @@ public class EmpiSubscriptionLoader {
}
}
private org.hl7.fhir.dstu3.model.Subscription buildEmpiSubscriptionDstu3(String theId, String theCriteria) {
private org.hl7.fhir.dstu3.model.Subscription buildMdmSubscriptionDstu3(String theId, String theCriteria) {
org.hl7.fhir.dstu3.model.Subscription retval = new org.hl7.fhir.dstu3.model.Subscription();
retval.setId(theId);
retval.setReason("EMPI");
retval.setReason("MDM");
retval.setStatus(org.hl7.fhir.dstu3.model.Subscription.SubscriptionStatus.REQUESTED);
retval.setCriteria(theCriteria);
retval.getMeta().addTag().setSystem(EmpiConstants.SYSTEM_MDM_MANAGED).setCode(EmpiConstants.CODE_HAPI_MDM_MANAGED);
retval.getMeta().addTag().setSystem(MdmConstants.SYSTEM_MDM_MANAGED).setCode(MdmConstants.CODE_HAPI_MDM_MANAGED);
org.hl7.fhir.dstu3.model.Subscription.SubscriptionChannelComponent channel = retval.getChannel();
channel.setType(org.hl7.fhir.dstu3.model.Subscription.SubscriptionChannelType.MESSAGE);
channel.setEndpoint("channel:" + myChannelNamer.getChannelName(IEmpiSettings.EMPI_CHANNEL_NAME, new ChannelProducerSettings()));
channel.setEndpoint("channel:" + myChannelNamer.getChannelName(IMdmSettings.MDM_CHANNEL_NAME, new ChannelProducerSettings()));
channel.setPayload("application/json");
return retval;
}
private Subscription buildEmpiSubscriptionR4(String theId, String theCriteria) {
private Subscription buildMdmSubscriptionR4(String theId, String theCriteria) {
Subscription retval = new Subscription();
retval.setId(theId);
retval.setReason("EMPI");
retval.setReason("MDM");
retval.setStatus(Subscription.SubscriptionStatus.REQUESTED);
retval.setCriteria(theCriteria);
retval.getMeta().addTag().setSystem(EmpiConstants.SYSTEM_MDM_MANAGED).setCode(EmpiConstants.CODE_HAPI_MDM_MANAGED);
retval.getMeta().addTag().setSystem(MdmConstants.SYSTEM_MDM_MANAGED).setCode(MdmConstants.CODE_HAPI_MDM_MANAGED);
Subscription.SubscriptionChannelComponent channel = retval.getChannel();
channel.setType(Subscription.SubscriptionChannelType.MESSAGE);
channel.setEndpoint("channel:" + myChannelNamer.getChannelName(IEmpiSettings.EMPI_CHANNEL_NAME, new ChannelProducerSettings()));
channel.setEndpoint("channel:" + myChannelNamer.getChannelName(IMdmSettings.MDM_CHANNEL_NAME, new ChannelProducerSettings()));
channel.setPayload("application/json");
return retval;
}

View File

@ -0,0 +1,339 @@
package ca.uhn.fhir.jpa.mdm.dao;
/*-
* #%L
* HAPI FHIR JPA Server - Enterprise Master Patient Index
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
* 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%
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchOutcome;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.mdm.log.Logs;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.jpa.dao.data.IMdmLinkDao;
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
import ca.uhn.fhir.jpa.entity.MdmLink;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
public class MdmLinkDaoSvc {
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
@Autowired
private IMdmLinkDao myMdmLinkDao;
@Autowired
private MdmLinkFactory myMdmLinkFactory;
@Autowired
private IdHelperService myIdHelperService;
@Autowired
private FhirContext myFhirContext;
@Transactional
public MdmLink createOrUpdateLinkEntity(IBaseResource theSourceResource, IBaseResource theTargetResource, MdmMatchOutcome theMatchOutcome, MdmLinkSourceEnum theLinkSource, @Nullable MdmTransactionContext theMdmTransactionContext) {
Long sourceResourcePid = myIdHelperService.getPidOrNull(theSourceResource);
Long targetResourcePid = myIdHelperService.getPidOrNull(theTargetResource);
MdmLink mdmLink = getOrCreateMdmLinkBySourceResourcePidAndTargetResourcePid(sourceResourcePid, targetResourcePid);
mdmLink.setLinkSource(theLinkSource);
mdmLink.setMatchResult(theMatchOutcome.getMatchResultEnum());
// Preserve these flags for link updates
mdmLink.setEidMatch(theMatchOutcome.isEidMatch() | mdmLink.isEidMatch());
mdmLink.setHadToCreateNewResource(theMatchOutcome.isCreatedNewResource() | mdmLink.getHadToCreateNewResource());
mdmLink.setMdmTargetType(myFhirContext.getResourceType(theTargetResource));
if (mdmLink.getScore() != null) {
mdmLink.setScore(Math.max(theMatchOutcome.score, mdmLink.getScore()));
} else {
mdmLink.setScore(theMatchOutcome.score);
}
String message = String.format("Creating MdmLink from %s to %s -> %s", theSourceResource.getIdElement().toUnqualifiedVersionless(), theTargetResource.getIdElement().toUnqualifiedVersionless(), theMatchOutcome);
theMdmTransactionContext.addTransactionLogMessage(message);
ourLog.debug(message);
save(mdmLink);
return mdmLink;
}
@Nonnull
public MdmLink getOrCreateMdmLinkBySourceResourcePidAndTargetResourcePid(Long theSourceResourcePid, Long theTargetResourcePid) {
Optional<MdmLink> oExisting = getLinkBySourceResourcePidAndTargetResourcePid(theSourceResourcePid, theTargetResourcePid);
if (oExisting.isPresent()) {
return oExisting.get();
} else {
MdmLink newLink = myMdmLinkFactory.newMdmLink();
newLink.setGoldenResourcePid(theSourceResourcePid);
newLink.setPersonPid(theSourceResourcePid);
newLink.setTargetPid(theTargetResourcePid);
return newLink;
}
}
public Optional<MdmLink> getLinkBySourceResourcePidAndTargetResourcePid(Long theSourceResourcePid, Long theTargetResourcePid) {
if (theTargetResourcePid == null || theSourceResourcePid == null) {
return Optional.empty();
}
MdmLink link = myMdmLinkFactory.newMdmLink();
link.setTargetPid(theTargetResourcePid);
link.setGoldenResourcePid(theSourceResourcePid);
Example<MdmLink> example = Example.of(link);
return myMdmLinkDao.findOne(example);
}
/**
* Given a Target Pid, and a match result, return all links that match these criteria.
*
* @param theTargetPid the target of the relationship.
* @param theMatchResult the Match Result of the relationship
* @return a list of {@link MdmLink} entities matching these criteria.
*/
public List<MdmLink> getMdmLinksByTargetPidAndMatchResult(Long theTargetPid, MdmMatchResultEnum theMatchResult) {
MdmLink exampleLink = myMdmLinkFactory.newMdmLink();
exampleLink.setTargetPid(theTargetPid);
exampleLink.setMatchResult(theMatchResult);
Example<MdmLink> example = Example.of(exampleLink);
return myMdmLinkDao.findAll(example);
}
/**
* Given a target Pid, return its Matched {@link MdmLink}. There can only ever be at most one of these, but its possible
* the target has no matches, and may return an empty optional.
*
* @param theTargetPid The Pid of the target you wish to find the matching link for.
* @return the {@link MdmLink} that contains the Match information for the target.
*/
public Optional<MdmLink> getMatchedLinkForTargetPid(Long theTargetPid) {
MdmLink exampleLink = myMdmLinkFactory.newMdmLink();
exampleLink.setTargetPid(theTargetPid);
exampleLink.setMatchResult(MdmMatchResultEnum.MATCH);
Example<MdmLink> example = Example.of(exampleLink);
return myMdmLinkDao.findOne(example);
}
/**
* Given an IBaseResource, return its Matched {@link MdmLink}. There can only ever be at most one of these, but its possible
* the target has no matches, and may return an empty optional.
*
* @param theTarget The IBaseResource representing the target you wish to find the matching link for.
* @return the {@link MdmLink} that contains the Match information for the target.
*/
public Optional<MdmLink> getMatchedLinkForTarget(IBaseResource theTarget) {
Long pid = myIdHelperService.getPidOrNull(theTarget);
if (pid == null) {
return Optional.empty();
}
MdmLink exampleLink = myMdmLinkFactory.newMdmLink();
exampleLink.setTargetPid(pid);
exampleLink.setMatchResult(MdmMatchResultEnum.MATCH);
Example<MdmLink> example = Example.of(exampleLink);
return myMdmLinkDao.findOne(example);
}
/**
* Given a person a target and a match result, return the matching {@link MdmLink}, if it exists.
*
* @param thePersonPid The Pid of the Person in the relationship
* @param theTargetPid The Pid of the target in the relationship
* @param theMatchResult The MatchResult you are looking for.
* @return an Optional {@link MdmLink} containing the matched link if it exists.
*/
public Optional<MdmLink> getMdmLinksByPersonPidTargetPidAndMatchResult(Long thePersonPid, Long theTargetPid, MdmMatchResultEnum theMatchResult) {
MdmLink exampleLink = myMdmLinkFactory.newMdmLink();
exampleLink.setGoldenResourcePid(thePersonPid);
exampleLink.setTargetPid(theTargetPid);
exampleLink.setMatchResult(theMatchResult);
Example<MdmLink> example = Example.of(exampleLink);
return myMdmLinkDao.findOne(example);
}
/**
* Get all {@link MdmLink} which have {@link MdmMatchResultEnum#POSSIBLE_DUPLICATE} as their match result.
*
* @return A list of {@link MdmLink} that hold potential duplicate persons.
*/
public List<MdmLink> getPossibleDuplicates() {
MdmLink exampleLink = myMdmLinkFactory.newMdmLink();
exampleLink.setMatchResult(MdmMatchResultEnum.POSSIBLE_DUPLICATE);
Example<MdmLink> example = Example.of(exampleLink);
return myMdmLinkDao.findAll(example);
}
public Optional<MdmLink> findMdmLinkByTarget(IBaseResource theTargetResource) {
@Nullable Long pid = myIdHelperService.getPidOrNull(theTargetResource);
if (pid == null) {
return Optional.empty();
}
MdmLink exampleLink = myMdmLinkFactory.newMdmLink().setTargetPid(pid);
Example<MdmLink> example = Example.of(exampleLink);
return myMdmLinkDao.findOne(example);
}
/**
* Delete a given {@link MdmLink}. Note that this does not clear out the Golden resource.
* It is a simple entity delete.
*
* @param theMdmLink the {@link MdmLink} to delete.
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void deleteLink(MdmLink theMdmLink) {
myMdmLinkDao.delete(theMdmLink);
}
/**
* Given a Golden Resource , return all links in which they are the source Person of the {@link MdmLink}
*
* @param theGoldenResource The {@link IBaseResource} Person who's links you would like to retrieve.
* @return A list of all {@link MdmLink} entities in which theGoldenResource is the source Person.
*/
public List<MdmLink> findMdmLinksByGoldenResource(IBaseResource theGoldenResource) {
Long pid = myIdHelperService.getPidOrNull(theGoldenResource);
if (pid == null) {
return Collections.emptyList();
}
MdmLink exampleLink = myMdmLinkFactory.newMdmLink().setGoldenResourcePid(pid);
Example<MdmLink> example = Example.of(exampleLink);
return myMdmLinkDao.findAll(example);
}
/**
* Delete all {@link MdmLink} entities, and return all resource PIDs from the source of the relationship.
*
* @return A list of Long representing the related Person Pids.
*/
@Transactional
public List<Long> deleteAllMdmLinksAndReturnGoldenResourcePids() {
List<MdmLink> all = myMdmLinkDao.findAll();
return deleteMdmLinksAndReturnGoldenResourcePids(all);
}
private List<Long> deleteMdmLinksAndReturnGoldenResourcePids(List<MdmLink> theLinks) {
Set<Long> persons = theLinks.stream().map(MdmLink::getGoldenResourcePid).collect(Collectors.toSet());
//TODO GGG this is probably invalid... we are essentially looking for GOLDEN -> GOLDEN links, which are either POSSIBLE_DUPLICATE
//and REDIRECT
//persons.addAll(theLinks.stream().filter(link -> "Person".equals(link.getEmpiTargetType())).map(EmpiLink::getTargetPid).collect(Collectors.toSet()));
persons.addAll(theLinks.stream()
.filter(link -> link.getMatchResult().equals(MdmMatchResultEnum.REDIRECT)
|| link.getMatchResult().equals(MdmMatchResultEnum.POSSIBLE_DUPLICATE))
.map(MdmLink::getTargetPid).collect(Collectors.toSet()));
ourLog.info("Deleting {} MDM link records...", theLinks.size());
myMdmLinkDao.deleteAll(theLinks);
ourLog.info("{} MDM link records deleted", theLinks.size());
return new ArrayList<>(persons);
}
/**
* Given a valid {@link String}, delete all {@link MdmLink} entities for that type, and get the Pids
* for the Person resources which were the sources of the links.
*
* @param theTargetType the type of relationship you would like to delete.
* @return A list of longs representing the Pids of the Person resources used as the sources of the relationships that were deleted.
*/
public List<Long> deleteAllMdmLinksOfTypeAndReturnGoldenResourcePids(String theTargetType) {
MdmLink link = new MdmLink();
link.setMdmTargetType(theTargetType);
Example<MdmLink> exampleLink = Example.of(link);
List<MdmLink> allOfType = myMdmLinkDao.findAll(exampleLink);
return deleteMdmLinksAndReturnGoldenResourcePids(allOfType);
}
/**
* Persist an EmpiLink to the database.
*
* @param theMdmLink the link to save.
* @return the persisted {@link MdmLink} entity.
*/
public MdmLink save(MdmLink theMdmLink) {
if (theMdmLink.getCreated() == null) {
theMdmLink.setCreated(new Date());
}
theMdmLink.setUpdated(new Date());
return myMdmLinkDao.save(theMdmLink);
}
/**
* Given an example {@link MdmLink}, return all links from the database which match the example.
*
* @param theExampleLink The EmpiLink containing the data we would like to search for.
* @return a list of {@link MdmLink} entities which match the example.
*/
public List<MdmLink> findMdmLinkByExample(Example<MdmLink> theExampleLink) {
return myMdmLinkDao.findAll(theExampleLink);
}
/**
* Given a target {@link IBaseResource}, return all {@link MdmLink} entities in which this target is the target
* of the relationship. This will show you all links for a given Patient/Practitioner.
*
* @param theTargetResource the target resource to find links for.
* @return all links for the target.
*/
public List<MdmLink> findMdmLinksByTarget(IBaseResource theTargetResource) {
Long pid = myIdHelperService.getPidOrNull(theTargetResource);
if (pid == null) {
return Collections.emptyList();
}
MdmLink exampleLink = myMdmLinkFactory.newMdmLink().setTargetPid(pid);
Example<MdmLink> example = Example.of(exampleLink);
return myMdmLinkDao.findAll(example);
}
/**
* Finds all {@link MdmLink} entities in which theSourceResource's PID is the source
* of the relationship.
*
* @param theSourceResource the source resource to find links for.
* @return all links for the source.
*/
public List<MdmLink> findMdmMatchLinksBySource(IBaseResource theSourceResource) {
Long pid = myIdHelperService.getPidOrNull(theSourceResource);
if (pid == null) {
return Collections.emptyList();
}
MdmLink exampleLink = myMdmLinkFactory.newMdmLink().setGoldenResourcePid(pid);
exampleLink.setMatchResult(MdmMatchResultEnum.MATCH);
Example<MdmLink> example = Example.of(exampleLink);
return myMdmLinkDao.findAll(example);
}
/**
* Factory delegation method, whenever you need a new MdmLink, use this factory method.
* //TODO Should we make the constructor private for MdmLink? or work out some way to ensure they can only be instantiated via factory.
*
* @return A new {@link MdmLink}.
*/
public MdmLink newMdmLink() {
return myMdmLinkFactory.newMdmLink();
}
}

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.jpa.empi.dao;
package ca.uhn.fhir.jpa.mdm.dao;
/*-
* #%L
@ -20,24 +20,24 @@ package ca.uhn.fhir.jpa.empi.dao;
* #L%
*/
import ca.uhn.fhir.empi.api.IEmpiSettings;
import ca.uhn.fhir.jpa.entity.EmpiLink;
import ca.uhn.fhir.mdm.api.IMdmSettings;
import ca.uhn.fhir.jpa.entity.MdmLink;
import org.springframework.beans.factory.annotation.Autowired;
public class EmpiLinkFactory {
private final IEmpiSettings myEmpiSettings;
public class MdmLinkFactory {
private final IMdmSettings myMdmSettings;
@Autowired
public EmpiLinkFactory(IEmpiSettings theEmpiSettings) {
myEmpiSettings = theEmpiSettings;
public MdmLinkFactory(IMdmSettings theMdmSettings) {
myMdmSettings = theMdmSettings;
}
/**
* Create a new EmpiLink, populating it with the version of the ruleset used to create it.
* Create a new {@link MdmLink}, populating it with the version of the ruleset used to create it.
*
* @return the new {@link EmpiLink}
* @return the new {@link MdmLink}
*/
public EmpiLink newEmpiLink() {
return new EmpiLink(myEmpiSettings.getRuleVersion());
public MdmLink newMdmLink() {
return new MdmLink(myMdmSettings.getRuleVersion());
}
}

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.jpa.empi.interceptor;
package ca.uhn.fhir.jpa.mdm.interceptor;
/*-
* #%L
@ -20,5 +20,5 @@ package ca.uhn.fhir.jpa.empi.interceptor;
* #L%
*/
public interface IEmpiStorageInterceptor {
public interface IMdmStorageInterceptor {
}

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.jpa.empi.interceptor;
package ca.uhn.fhir.jpa.mdm.interceptor;
/*-
* #%L
@ -21,17 +21,17 @@ package ca.uhn.fhir.jpa.empi.interceptor;
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.empi.api.EmpiConstants;
import ca.uhn.fhir.empi.api.IEmpiSettings;
import ca.uhn.fhir.empi.model.CanonicalEID;
import ca.uhn.fhir.empi.util.EIDHelper;
import ca.uhn.fhir.empi.util.EmpiUtil;
import ca.uhn.fhir.empi.util.PersonHelper;
import ca.uhn.fhir.mdm.api.MdmConstants;
import ca.uhn.fhir.mdm.api.IMdmSettings;
import ca.uhn.fhir.mdm.model.CanonicalEID;
import ca.uhn.fhir.mdm.util.EIDHelper;
import ca.uhn.fhir.mdm.util.MdmUtil;
import ca.uhn.fhir.mdm.util.GoldenResourceHelper;
import ca.uhn.fhir.interceptor.api.Hook;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.dao.empi.EmpiLinkDeleteSvc;
import ca.uhn.fhir.jpa.dao.mdm.MdmLinkDeleteSvc;
import ca.uhn.fhir.jpa.dao.expunge.ExpungeEverythingService;
import ca.uhn.fhir.jpa.entity.EmpiLink;
import ca.uhn.fhir.jpa.entity.MdmLink;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
@ -45,72 +45,72 @@ import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@Service
public class EmpiStorageInterceptor implements IEmpiStorageInterceptor {
private static final Logger ourLog = LoggerFactory.getLogger(EmpiStorageInterceptor.class);
public class MdmStorageInterceptor implements IMdmStorageInterceptor {
private static final Logger ourLog = LoggerFactory.getLogger(MdmStorageInterceptor.class);
@Autowired
private ExpungeEverythingService myExpungeEverythingService;
@Autowired
private EmpiLinkDeleteSvc myEmpiLinkDeleteSvc;
private MdmLinkDeleteSvc myMdmLinkDeleteSvc;
@Autowired
private FhirContext myFhirContext;
@Autowired
private EIDHelper myEIDHelper;
@Autowired
private IEmpiSettings myEmpiSettings;
private IMdmSettings myMdmSettings;
@Autowired
private PersonHelper myPersonHelper;
private GoldenResourceHelper myGoldenResourceHelper;
@Hook(Pointcut.STORAGE_PRESTORAGE_RESOURCE_CREATED)
public void blockManualPersonManipulationOnCreate(IBaseResource theBaseResource, RequestDetails theRequestDetails, ServletRequestDetails theServletRequestDetails) {
public void blockManualResourceManipulationOnCreate(IBaseResource theBaseResource, RequestDetails theRequestDetails, ServletRequestDetails theServletRequestDetails) {
//If running in single EID mode, forbid multiple eids.
if (myEmpiSettings.isPreventMultipleEids()) {
if (myMdmSettings.isPreventMultipleEids()) {
forbidIfHasMultipleEids(theBaseResource);
}
// TODO EMPI find a better way to identify EMPI calls
// TODO GGG MDM find a better way to identify i nternal calls?
if (isInternalRequest(theRequestDetails)) {
return;
}
forbidIfEmpiManagedTagIsPresent(theBaseResource);
forbidIfMdmManagedTagIsPresent(theBaseResource);
}
@Hook(Pointcut.STORAGE_PRESTORAGE_RESOURCE_UPDATED)
public void blockManualPersonManipulationOnUpdate(IBaseResource theOldResource, IBaseResource theNewResource, RequestDetails theRequestDetails, ServletRequestDetails theServletRequestDetails) {
public void blockManualPersonManipulationOnUpdate(IBaseResource theOldResource, IBaseResource theUpdatedResource, RequestDetails theRequestDetails, ServletRequestDetails theServletRequestDetails) {
//If running in single EID mode, forbid multiple eids.
if (myEmpiSettings.isPreventMultipleEids()) {
forbidIfHasMultipleEids(theNewResource);
if (myMdmSettings.isPreventMultipleEids()) {
forbidIfHasMultipleEids(theUpdatedResource);
}
if (EmpiUtil.isEmpiManagedPerson(myFhirContext, theNewResource) &&
myPersonHelper.isDeactivated(theNewResource)) {
ourLog.debug("Deleting empi links to deactivated Person {}", theNewResource.getIdElement().toUnqualifiedVersionless());
int deleted = myEmpiLinkDeleteSvc.deleteNonRedirectWithWithAnyReferenceTo(theNewResource);
//TODO GGG MDM: Check if this is actually handled already in mdm update code or not.
if (myGoldenResourceHelper.isDeactivated(theUpdatedResource)) {
ourLog.debug("Deleting MDM links to deactivated Person {}", theUpdatedResource.getIdElement().toUnqualifiedVersionless());
int deleted = myMdmLinkDeleteSvc.deleteNonRedirectWithWithAnyReferenceTo(theUpdatedResource);
if (deleted > 0) {
ourLog.debug("Deleted {} empi links", deleted);
ourLog.debug("Deleted {} MDM links", deleted);
}
}
if (isInternalRequest(theRequestDetails)) {
return;
}
forbidIfEmpiManagedTagIsPresent(theOldResource);
forbidModifyingEmpiTag(theNewResource, theOldResource);
forbidIfMdmManagedTagIsPresent(theOldResource);
forbidModifyingMdmTag(theUpdatedResource, theOldResource);
if (myEmpiSettings.isPreventEidUpdates()) {
forbidIfModifyingExternalEidOnTarget(theNewResource, theOldResource);
if (myMdmSettings.isPreventEidUpdates()) {
forbidIfModifyingExternalEidOnTarget(theUpdatedResource, theOldResource);
}
}
@Hook(Pointcut.STORAGE_PRESTORAGE_RESOURCE_DELETED)
public void deleteEmpiLinks(RequestDetails theRequest, IBaseResource theResource) {
if (!EmpiUtil.isEmpiResourceType(myFhirContext, theResource)) {
public void deleteMdmLinks(RequestDetails theRequest, IBaseResource theResource) {
if (!MdmUtil.isMdmResourceType(myFhirContext, theResource)) {
return;
}
myEmpiLinkDeleteSvc.deleteWithAnyReferenceTo(theResource);
myMdmLinkDeleteSvc.deleteWithAnyReferenceTo(theResource);
}
private void forbidIfModifyingExternalEidOnTarget(IBaseResource theNewResource, IBaseResource theOldResource) {
@ -130,11 +130,11 @@ public class EmpiStorageInterceptor implements IEmpiStorageInterceptor {
}
/*
* Will throw a forbidden error if a request attempts to add/remove the EMPI tag on a Person.
* Will throw a forbidden error if a request attempts to add/remove the MDM tag on a Resource.
*/
private void forbidModifyingEmpiTag(IBaseResource theNewResource, IBaseResource theOldResource) {
if (EmpiUtil.isEmpiManaged(theNewResource) != EmpiUtil.isEmpiManaged(theOldResource)) {
throwBlockEmpiManagedTagChange();
private void forbidModifyingMdmTag(IBaseResource theNewResource, IBaseResource theOldResource) {
if (MdmUtil.isMdmManaged(theNewResource) != MdmUtil.isMdmManaged(theOldResource)) {
throwBlockMdmManagedTagChange();
}
}
@ -154,18 +154,21 @@ public class EmpiStorageInterceptor implements IEmpiStorageInterceptor {
return theRequestDetails == null;
}
private void forbidIfEmpiManagedTagIsPresent(IBaseResource theResource) {
if (EmpiUtil.isEmpiManaged(theResource)) {
throwModificationBlockedByEmpi();
private void forbidIfMdmManagedTagIsPresent(IBaseResource theResource) {
if (MdmUtil.isMdmManaged(theResource)) {
throwModificationBlockedByMdm();
}
if (MdmUtil.hasGoldenRecordSystemTag(theResource)) {
throwModificationBlockedByMdm();
}
}
private void throwBlockEmpiManagedTagChange() {
throw new ForbiddenOperationException("The " + EmpiConstants.CODE_HAPI_MDM_MANAGED + " tag on a resource may not be changed once created.");
private void throwBlockMdmManagedTagChange() {
throw new ForbiddenOperationException("The " + MdmConstants.CODE_HAPI_MDM_MANAGED + " tag on a resource may not be changed once created.");
}
private void throwModificationBlockedByEmpi() {
throw new ForbiddenOperationException("Cannot create or modify Resources that are managed by EMPI.");
private void throwModificationBlockedByMdm() {
throw new ForbiddenOperationException("Cannot create or modify Resources that are managed by MDM. This resource contains a tag with one of these systems: " + MdmConstants.SYSTEM_GOLDEN_RECORD_STATUS + " or " + MdmConstants.SYSTEM_MDM_MANAGED);
}
private void throwBlockMultipleEids() {
@ -177,14 +180,14 @@ public class EmpiStorageInterceptor implements IEmpiStorageInterceptor {
}
@Hook(Pointcut.STORAGE_PRESTORAGE_EXPUNGE_EVERYTHING)
public void expungeAllEmpiLinks(AtomicInteger theCounter) {
ourLog.debug("Expunging all EmpiLink records");
theCounter.addAndGet(myExpungeEverythingService.expungeEverythingByType(EmpiLink.class));
public void expungeAllMdmLinks(AtomicInteger theCounter) {
ourLog.debug("Expunging all MdmLink records");
theCounter.addAndGet(myExpungeEverythingService.expungeEverythingByType(MdmLink.class));
}
@Hook(Pointcut.STORAGE_PRESTORAGE_EXPUNGE_RESOURCE)
public void expungeAllMatchedEmpiLinks(AtomicInteger theCounter, IBaseResource theResource) {
ourLog.debug("Expunging EmpiLink records with reference to {}", theResource.getIdElement());
theCounter.addAndGet(myEmpiLinkDeleteSvc.deleteWithAnyReferenceTo(theResource));
public void expungeAllMatchedMdmLinks(AtomicInteger theCounter, IBaseResource theResource) {
ourLog.debug("Expunging MdmLink records with reference to {}", theResource.getIdElement());
theCounter.addAndGet(myMdmLinkDeleteSvc.deleteWithAnyReferenceTo(theResource));
}
}

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.jpa.empi.interceptor;
package ca.uhn.fhir.jpa.mdm.interceptor;
/*-
* #%L
@ -20,8 +20,8 @@ package ca.uhn.fhir.jpa.empi.interceptor;
* #L%
*/
import ca.uhn.fhir.empi.api.IEmpiSettings;
import ca.uhn.fhir.empi.log.Logs;
import ca.uhn.fhir.mdm.api.IMdmSettings;
import ca.uhn.fhir.mdm.log.Logs;
import ca.uhn.fhir.interceptor.api.IInterceptorService;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.subscription.submit.interceptor.SubscriptionSubmitInterceptorLoader;
@ -31,15 +31,15 @@ import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.PostConstruct;
public class EmpiSubmitterInterceptorLoader {
private static final Logger ourLog = Logs.getEmpiTroubleshootingLog();
public class MdmSubmitterInterceptorLoader {
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
@Autowired
private IEmpiSettings myEmpiProperties;
private IMdmSettings myMdmSettings;
@Autowired
DaoConfig myDaoConfig;
@Autowired
private IEmpiStorageInterceptor myIEmpiStorageInterceptor;
private IMdmStorageInterceptor myIMdmStorageInterceptor;
@Autowired
private IInterceptorService myInterceptorService;
@Autowired
@ -47,13 +47,13 @@ public class EmpiSubmitterInterceptorLoader {
@PostConstruct
public void start() {
if (!myEmpiProperties.isEnabled()) {
if (!myMdmSettings.isEnabled()) {
return;
}
myDaoConfig.addSupportedSubscriptionType(Subscription.SubscriptionChannelType.MESSAGE);
myInterceptorService.registerInterceptor(myIEmpiStorageInterceptor);
ourLog.info("EMPI interceptor registered");
myInterceptorService.registerInterceptor(myIMdmStorageInterceptor);
ourLog.info("MDM interceptor registered");
// We need to call SubscriptionSubmitInterceptorLoader.start() again in case there were no subscription types the first time it was called.
mySubscriptionSubmitInterceptorLoader.start();
}

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.jpa.empi.svc;
package ca.uhn.fhir.jpa.mdm.svc;
/*-
* #%L
@ -20,16 +20,16 @@ package ca.uhn.fhir.jpa.empi.svc;
* #L%
*/
import ca.uhn.fhir.empi.api.EmpiLinkSourceEnum;
import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
import ca.uhn.fhir.empi.api.IEmpiLinkSvc;
import ca.uhn.fhir.empi.api.IGoldenResourceMergerSvc;
import ca.uhn.fhir.empi.log.Logs;
import ca.uhn.fhir.empi.model.MdmTransactionContext;
import ca.uhn.fhir.empi.util.PersonHelper;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.mdm.api.IMdmLinkSvc;
import ca.uhn.fhir.mdm.api.IGoldenResourceMergerSvc;
import ca.uhn.fhir.mdm.log.Logs;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.mdm.util.GoldenResourceHelper;
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
import ca.uhn.fhir.jpa.empi.dao.EmpiLinkDaoSvc;
import ca.uhn.fhir.jpa.entity.EmpiLink;
import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc;
import ca.uhn.fhir.jpa.entity.MdmLink;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.slf4j.Logger;
@ -43,18 +43,18 @@ import java.util.Optional;
@Service
public class GoldenResourceMergerSvcImpl implements IGoldenResourceMergerSvc {
private static final Logger ourLog = Logs.getEmpiTroubleshootingLog();
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
@Autowired
PersonHelper myPersonHelper;
GoldenResourceHelper myGoldenResourceHelper;
@Autowired
EmpiLinkDaoSvc myEmpiLinkDaoSvc;
MdmLinkDaoSvc myMdmLinkDaoSvc;
@Autowired
IEmpiLinkSvc myEmpiLinkSvc;
IMdmLinkSvc myEmpiLinkSvc;
@Autowired
IdHelperService myIdHelperService;
@Autowired
EmpiResourceDaoSvc myEmpiResourceDaoSvc;
MdmResourceDaoSvc myMdmResourceDaoSvc;
@Override
@Transactional
@ -64,7 +64,7 @@ public class GoldenResourceMergerSvcImpl implements IGoldenResourceMergerSvc {
String resourceType = theMdmTransactionContext.getResourceType();
//Merge attributes, to be determined when survivorship is solved.
myPersonHelper.mergeFields(theFromGoldenResource, theToGoldenResource);
myGoldenResourceHelper.mergeFields(theFromGoldenResource, theToGoldenResource);
//Merge the links from the FROM to the TO resource. Clean up dangling links.
mergeGoldenResourceLinks(theFromGoldenResource, theToGoldenResource, toGoldenResourcePid, theMdmTransactionContext);
@ -73,13 +73,13 @@ public class GoldenResourceMergerSvcImpl implements IGoldenResourceMergerSvc {
addMergeLink(toGoldenResourcePid, fromGoldenResourcePid, resourceType);
//Strip the golden resource tag from the now-deprecated resource.
myEmpiResourceDaoSvc.removeGoldenResourceTag(theFromGoldenResource, resourceType);
myMdmResourceDaoSvc.removeGoldenResourceTag(theFromGoldenResource, resourceType);
//Add the REDIRECT tag to that same deprecated resource.
myPersonHelper.deactivateResource(theFromGoldenResource);
myGoldenResourceHelper.deactivateResource(theFromGoldenResource);
//Save the deprecated resource.
myEmpiResourceDaoSvc.upsertSourceResource(theFromGoldenResource, resourceType);
myMdmResourceDaoSvc.upsertSourceResource(theFromGoldenResource, resourceType);
log(theMdmTransactionContext, "Merged " + theFromGoldenResource.getIdElement().toVersionless() + " into " + theToGoldenResource.getIdElement().toVersionless());
return theToGoldenResource;
@ -93,26 +93,26 @@ public class GoldenResourceMergerSvcImpl implements IGoldenResourceMergerSvc {
* @param theMdmTransactionContext Context to keep track of the deletions
*/
private void removeTargetLinks(IAnyResource theFrom, IAnyResource theTo, MdmTransactionContext theMdmTransactionContext) {
List<EmpiLink> allLinksWithTheFromAsTarget = myEmpiLinkDaoSvc.findEmpiLinksByGoldenResource(theFrom);
List<MdmLink> allLinksWithTheFromAsTarget = myMdmLinkDaoSvc.findMdmLinksByGoldenResource(theFrom);
allLinksWithTheFromAsTarget
.stream()
//TODO GGG NG MDM: Why are we keeping manual links? Haven't we already copied those over in the previous merge step?
.filter(EmpiLink::isAuto) // only keep manual links
.filter(MdmLink::isAuto) // only keep manual links
.forEach(l -> {
theMdmTransactionContext.addTransactionLogMessage(String.format("Deleting link %s", l));
myEmpiLinkDaoSvc.deleteLink(l);
myMdmLinkDaoSvc.deleteLink(l);
});
}
private void addMergeLink(Long theSourceResourcePidAkaActive, Long theTargetResourcePidAkaDeactivated, String theResourceType) {
EmpiLink empiLink = myEmpiLinkDaoSvc
.getOrCreateEmpiLinkBySourceResourcePidAndTargetResourcePid(theSourceResourcePidAkaActive, theTargetResourcePidAkaDeactivated);
MdmLink mdmLink = myMdmLinkDaoSvc
.getOrCreateMdmLinkBySourceResourcePidAndTargetResourcePid(theSourceResourcePidAkaActive, theTargetResourcePidAkaDeactivated);
empiLink
.setEmpiTargetType(theResourceType)
.setMatchResult(EmpiMatchResultEnum.REDIRECT)
.setLinkSource(EmpiLinkSourceEnum.MANUAL);
myEmpiLinkDaoSvc.save(empiLink);
mdmLink
.setMdmTargetType(theResourceType)
.setMatchResult(MdmMatchResultEnum.REDIRECT)
.setLinkSource(MdmLinkSourceEnum.MANUAL);
myMdmLinkDaoSvc.save(mdmLink);
}
@ -131,22 +131,22 @@ public class GoldenResourceMergerSvcImpl implements IGoldenResourceMergerSvc {
* @param theMdmTransactionContext
*/
private void mergeGoldenResourceLinks(IAnyResource theFromResource, IAnyResource theToResource, Long theToResourcePid, MdmTransactionContext theMdmTransactionContext) {
List<EmpiLink> fromLinks = myEmpiLinkDaoSvc.findEmpiLinksByGoldenResource(theFromResource); // fromLinks - links going to theFromResource
List<EmpiLink> toLinks = myEmpiLinkDaoSvc.findEmpiLinksByGoldenResource(theToResource); // toLinks - links going to theToResource
List<EmpiLink> toDelete = new ArrayList<>();
List<MdmLink> fromLinks = myMdmLinkDaoSvc.findMdmLinksByGoldenResource(theFromResource); // fromLinks - links going to theFromResource
List<MdmLink> toLinks = myMdmLinkDaoSvc.findMdmLinksByGoldenResource(theToResource); // toLinks - links going to theToResource
List<MdmLink> toDelete = new ArrayList<>();
for (EmpiLink fromLink : fromLinks) {
Optional<EmpiLink> optionalToLink = findFirstLinkWithMatchingTarget(toLinks, fromLink);
for (MdmLink fromLink : fromLinks) {
Optional<MdmLink> optionalToLink = findFirstLinkWithMatchingTarget(toLinks, fromLink);
if (optionalToLink.isPresent()) {
// The original links already contain this target, so move it over to the toResource
EmpiLink toLink = optionalToLink.get();
MdmLink toLink = optionalToLink.get();
if (fromLink.isManual()) {
switch (toLink.getLinkSource()) {
case AUTO:
//3
log(theMdmTransactionContext, String.format("MANUAL overrides AUT0. Deleting link %s", toLink.toString()));
myEmpiLinkDaoSvc.deleteLink(toLink);
myMdmLinkDaoSvc.deleteLink(toLink);
break;
case MANUAL:
if (fromLink.getMatchResult() != toLink.getMatchResult()) {
@ -162,14 +162,14 @@ public class GoldenResourceMergerSvcImpl implements IGoldenResourceMergerSvc {
//2 The original TO links didn't contain this target, so move it over to the toGoldenResource
fromLink.setGoldenResourcePid(theToResourcePid);
ourLog.trace("Saving link {}", fromLink);
myEmpiLinkDaoSvc.save(fromLink);
myMdmLinkDaoSvc.save(fromLink);
}
//1 Delete dangling links
toDelete.forEach(link -> myEmpiLinkDaoSvc.deleteLink(link));
toDelete.forEach(link -> myMdmLinkDaoSvc.deleteLink(link));
}
private Optional<EmpiLink> findFirstLinkWithMatchingTarget(List<EmpiLink> theEmpiLinks, EmpiLink theLinkWithTargetToMatch) {
return theEmpiLinks.stream()
private Optional<MdmLink> findFirstLinkWithMatchingTarget(List<MdmLink> theMdmLinks, MdmLink theLinkWithTargetToMatch) {
return theMdmLinks.stream()
.filter(empiLink -> empiLink.getTargetPid().equals(theLinkWithTargetToMatch.getTargetPid()))
.findFirst();
}

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.jpa.empi.svc;
package ca.uhn.fhir.jpa.mdm.svc;
/*-
* #%L
@ -21,8 +21,8 @@ package ca.uhn.fhir.jpa.empi.svc;
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.empi.api.IEmpiChannelSubmitterSvc;
import ca.uhn.fhir.empi.log.Logs;
import ca.uhn.fhir.mdm.api.IMdmChannelSubmitterSvc;
import ca.uhn.fhir.mdm.log.Logs;
import ca.uhn.fhir.jpa.subscription.channel.api.ChannelProducerSettings;
import ca.uhn.fhir.jpa.subscription.channel.api.IChannelFactory;
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedJsonMessage;
@ -33,47 +33,47 @@ import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.MessageChannel;
import static ca.uhn.fhir.empi.api.IEmpiSettings.EMPI_CHANNEL_NAME;
import static ca.uhn.fhir.mdm.api.IMdmSettings.MDM_CHANNEL_NAME;
/**
* This class is responsible for manual submissions of {@link IAnyResource} resources onto the Empi Channel.
* This class is responsible for manual submissions of {@link IAnyResource} resources onto the MDM Channel.
*/
public class EmpiChannelSubmitterSvcImpl implements IEmpiChannelSubmitterSvc {
private static final Logger ourLog = Logs.getEmpiTroubleshootingLog();
public class MdmChannelSubmitterSvcImpl implements IMdmChannelSubmitterSvc {
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
private MessageChannel myEmpiChannelProducer;
private MessageChannel myMdmChannelProducer;
private FhirContext myFhirContext;
private IChannelFactory myChannelFactory;
@Override
public void submitResourceToEmpiChannel(IBaseResource theResource) {
public void submitResourceToMdmChannel(IBaseResource theResource) {
ResourceModifiedJsonMessage resourceModifiedJsonMessage = new ResourceModifiedJsonMessage();
ResourceModifiedMessage resourceModifiedMessage = new ResourceModifiedMessage(myFhirContext, theResource, ResourceModifiedMessage.OperationTypeEnum.MANUALLY_TRIGGERED);
resourceModifiedMessage.setOperationType(ResourceModifiedMessage.OperationTypeEnum.MANUALLY_TRIGGERED);
resourceModifiedJsonMessage.setPayload(resourceModifiedMessage);
boolean success = getEmpiChannelProducer().send(resourceModifiedJsonMessage);
boolean success = getMdmChannelProducer().send(resourceModifiedJsonMessage);
if (!success) {
ourLog.error("Failed to submit {} to EMPI Channel.", resourceModifiedMessage.getPayloadId());
ourLog.error("Failed to submit {} to MDM Channel.", resourceModifiedMessage.getPayloadId());
}
}
@Autowired
public EmpiChannelSubmitterSvcImpl(FhirContext theFhirContext, IChannelFactory theIChannelFactory) {
public MdmChannelSubmitterSvcImpl(FhirContext theFhirContext, IChannelFactory theIChannelFactory) {
myFhirContext = theFhirContext;
myChannelFactory = theIChannelFactory;
}
private void init() {
ChannelProducerSettings channelSettings = new ChannelProducerSettings();
myEmpiChannelProducer= myChannelFactory.getOrCreateProducer(EMPI_CHANNEL_NAME, ResourceModifiedJsonMessage.class, channelSettings);
myMdmChannelProducer = myChannelFactory.getOrCreateProducer(MDM_CHANNEL_NAME, ResourceModifiedJsonMessage.class, channelSettings);
}
private MessageChannel getEmpiChannelProducer() {
if (myEmpiChannelProducer == null) {
private MessageChannel getMdmChannelProducer() {
if (myMdmChannelProducer == null) {
init();
}
return myEmpiChannelProducer;
return myMdmChannelProducer;
}
}

View File

@ -0,0 +1,79 @@
package ca.uhn.fhir.jpa.mdm.svc;
/*-
* #%L
* HAPI FHIR JPA Server - Enterprise Master Patient Index
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
* 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%
*/
import ca.uhn.fhir.mdm.api.IMdmExpungeSvc;
import ca.uhn.fhir.mdm.api.IMdmSettings;
import ca.uhn.fhir.mdm.log.Logs;
import ca.uhn.fhir.jpa.api.model.DeleteMethodOutcome;
import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.provider.ProviderConstants;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
/**
* This class is responsible for clearing out existing MDM links, as well as deleting all persons related to those MDM Links.
*
*/
public class MdmClearSvcImpl implements IMdmExpungeSvc {
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
final MdmLinkDaoSvc myMdmLinkDaoSvc;
final MdmGoldenResourceDeletingSvc myMdmGoldenResourceDeletingSvcImpl;
final IMdmSettings myMdmSettings;
@Autowired
public MdmClearSvcImpl(MdmLinkDaoSvc theMdmLinkDaoSvc, MdmGoldenResourceDeletingSvc theMdmGoldenResourceDeletingSvcImpl, IMdmSettings theIMdmSettings) {
myMdmLinkDaoSvc = theMdmLinkDaoSvc;
myMdmGoldenResourceDeletingSvcImpl = theMdmGoldenResourceDeletingSvcImpl;
myMdmSettings = theIMdmSettings;
}
@Override
public long expungeAllMdmLinksOfTargetType(String theResourceType, ServletRequestDetails theRequestDetails) {
throwExceptionIfInvalidTargetType(theResourceType);
ourLog.info("Clearing all MDM Links for resource type {}...", theResourceType);
List<Long> personPids = myMdmLinkDaoSvc.deleteAllMdmLinksOfTypeAndReturnGoldenResourcePids(theResourceType);
DeleteMethodOutcome deleteOutcome = myMdmGoldenResourceDeletingSvcImpl.expungeGoldenResourcePids(personPids, theRequestDetails);
ourLog.info("MDM clear operation complete. Removed {} MDM links and {} Person resources.", personPids.size(), deleteOutcome.getExpungedResourcesCount());
return personPids.size();
}
private void throwExceptionIfInvalidTargetType(String theResourceType) {
if (!myMdmSettings.isSupportedMdmType(theResourceType)) {
throw new InvalidRequestException(ProviderConstants.MDM_CLEAR + " does not support resource type: " + theResourceType);
}
}
@Override
public long expungeAllMdmLinks(ServletRequestDetails theRequestDetails) {
ourLog.info("Clearing all MDM Links...");
List<Long> goldenResourcePids = myMdmLinkDaoSvc.deleteAllMdmLinksAndReturnGoldenResourcePids();
DeleteMethodOutcome deleteOutcome = myMdmGoldenResourceDeletingSvcImpl.expungeGoldenResourcePids(goldenResourcePids, theRequestDetails);
ourLog.info("MDM clear operation complete. Removed {} MDM links and expunged {} Golden resources.", goldenResourcePids.size(), deleteOutcome.getExpungedResourcesCount());
return goldenResourcePids.size();
}
}

View File

@ -0,0 +1,100 @@
package ca.uhn.fhir.jpa.mdm.svc;
/*-
* #%L
* HAPI FHIR JPA Server - Enterprise Master Patient Index
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
* 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%
*/
import ca.uhn.fhir.mdm.api.MdmLinkJson;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.mdm.api.IMdmControllerSvc;
import ca.uhn.fhir.mdm.api.IMdmLinkQuerySvc;
import ca.uhn.fhir.mdm.api.IMdmLinkUpdaterSvc;
import ca.uhn.fhir.mdm.api.IGoldenResourceMergerSvc;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.mdm.provider.MdmControllerHelper;
import ca.uhn.fhir.mdm.provider.MdmControllerUtil;
import ca.uhn.fhir.rest.server.provider.ProviderConstants;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.stream.Stream;
/**
* This class acts as a layer between MdmProviders and MDM services to support a REST API that's not a FHIR Operation API.
*/
@Service
public class MdmControllerSvcImpl implements IMdmControllerSvc {
@Autowired
MdmControllerHelper myMdmControllerHelper;
@Autowired
IGoldenResourceMergerSvc myGoldenResourceMergerSvc;
@Autowired
IMdmLinkQuerySvc myMdmLinkQuerySvc;
@Autowired
IMdmLinkUpdaterSvc myIMdmLinkUpdaterSvc;
@Override
public IAnyResource mergeGoldenResources(String theFromPersonId, String theToPersonId, MdmTransactionContext theMdmTransactionContext) {
IAnyResource fromPerson = myMdmControllerHelper.getLatestGoldenResourceFromIdOrThrowException(ProviderConstants.MDM_MERGE_GR_FROM_GOLDEN_RESOURCE_ID, theFromPersonId);
IAnyResource toPerson = myMdmControllerHelper.getLatestGoldenResourceFromIdOrThrowException(ProviderConstants.MDM_MERGE_GR_TO_GOLDEN_RESOURCE_ID, theToPersonId);
myMdmControllerHelper.validateMergeResources(fromPerson, toPerson);
myMdmControllerHelper.validateSameVersion(fromPerson, theFromPersonId);
myMdmControllerHelper.validateSameVersion(toPerson, theToPersonId);
return myGoldenResourceMergerSvc.mergeGoldenResources(fromPerson, toPerson, theMdmTransactionContext);
}
@Override
public Stream<MdmLinkJson> queryLinks(@Nullable String thePersonId, @Nullable String theTargetId, @Nullable String theMatchResult, @Nullable String theLinkSource, MdmTransactionContext theMdmTransactionContext) {
IIdType personId = MdmControllerUtil.extractPersonIdDtOrNull(ProviderConstants.MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID, thePersonId);
IIdType targetId = MdmControllerUtil.extractTargetIdDtOrNull(ProviderConstants.MDM_QUERY_LINKS_RESOURCE_ID, theTargetId);
MdmMatchResultEnum matchResult = MdmControllerUtil.extractMatchResultOrNull(theMatchResult);
MdmLinkSourceEnum linkSource = MdmControllerUtil.extractLinkSourceOrNull(theLinkSource);
return myMdmLinkQuerySvc.queryLinks(personId, targetId, matchResult, linkSource, theMdmTransactionContext);
}
@Override
public Stream<MdmLinkJson> getDuplicateGoldenResources(MdmTransactionContext theMdmTransactionContext) {
return myMdmLinkQuerySvc.getDuplicatePersons(theMdmTransactionContext);
}
@Override
public IAnyResource updateLink(String theGoldenResourceId, String theTargetId, String theMatchResult, MdmTransactionContext theMdmTransactionContext) {
MdmMatchResultEnum matchResult = MdmControllerUtil.extractMatchResultOrNull(theMatchResult);
IAnyResource person = myMdmControllerHelper.getLatestGoldenResourceFromIdOrThrowException(ProviderConstants.MDM_UPDATE_LINK_GOLDEN_RESOURCE_ID, theGoldenResourceId);
IAnyResource target = myMdmControllerHelper.getLatestTargetFromIdOrThrowException(ProviderConstants.MDM_UPDATE_LINK_RESOURCE_ID, theTargetId);
myMdmControllerHelper.validateSameVersion(person, theGoldenResourceId);
myMdmControllerHelper.validateSameVersion(target, theTargetId);
return myIMdmLinkUpdaterSvc.updateLink(person, target, matchResult, theMdmTransactionContext);
}
@Override
public void notDuplicateGoldenResource(String thePersonId, String theTargetPersonId, MdmTransactionContext theMdmTransactionContext) {
IAnyResource person = myMdmControllerHelper.getLatestGoldenResourceFromIdOrThrowException(ProviderConstants.MDM_UPDATE_LINK_GOLDEN_RESOURCE_ID, thePersonId);
IAnyResource target = myMdmControllerHelper.getLatestGoldenResourceFromIdOrThrowException(ProviderConstants.MDM_UPDATE_LINK_RESOURCE_ID, theTargetPersonId);
myIMdmLinkUpdaterSvc.notDuplicatePerson(person, target, theMdmTransactionContext);
}
}

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.jpa.empi.svc;
package ca.uhn.fhir.jpa.mdm.svc;
/*-
* #%L
@ -20,19 +20,19 @@ package ca.uhn.fhir.jpa.empi.svc;
* #L%
*/
import ca.uhn.fhir.empi.api.EmpiLinkSourceEnum;
import ca.uhn.fhir.empi.api.EmpiMatchOutcome;
import ca.uhn.fhir.empi.api.IEmpiLinkSvc;
import ca.uhn.fhir.empi.api.IEmpiSettings;
import ca.uhn.fhir.empi.log.Logs;
import ca.uhn.fhir.empi.model.CanonicalEID;
import ca.uhn.fhir.empi.model.MdmTransactionContext;
import ca.uhn.fhir.empi.util.EIDHelper;
import ca.uhn.fhir.empi.util.PersonHelper;
import ca.uhn.fhir.jpa.empi.dao.EmpiLinkDaoSvc;
import ca.uhn.fhir.jpa.empi.svc.candidate.EmpiSourceResourceFindingSvc;
import ca.uhn.fhir.jpa.empi.svc.candidate.MatchedSourceResourceCandidate;
import ca.uhn.fhir.jpa.entity.EmpiLink;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchOutcome;
import ca.uhn.fhir.mdm.api.IMdmLinkSvc;
import ca.uhn.fhir.mdm.api.IMdmSettings;
import ca.uhn.fhir.mdm.log.Logs;
import ca.uhn.fhir.mdm.model.CanonicalEID;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.mdm.util.EIDHelper;
import ca.uhn.fhir.mdm.util.GoldenResourceHelper;
import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc;
import ca.uhn.fhir.jpa.mdm.svc.candidate.MdmGoldenResourceFindingSvc;
import ca.uhn.fhir.jpa.mdm.svc.candidate.MatchedSourceResourceCandidate;
import ca.uhn.fhir.jpa.entity.MdmLink;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.slf4j.Logger;
@ -43,33 +43,33 @@ import java.util.List;
import java.util.Optional;
@Service
public class EmpiEidUpdateService {
private static final Logger ourLog = Logs.getEmpiTroubleshootingLog();
public class MdmEidUpdateService {
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
@Autowired
private EmpiResourceDaoSvc myEmpiResourceDaoSvc;
private MdmResourceDaoSvc myMdmResourceDaoSvc;
@Autowired
private IEmpiLinkSvc myEmpiLinkSvc;
private IMdmLinkSvc myMdmLinkSvc;
@Autowired
private EmpiSourceResourceFindingSvc myEmpiSourceResourceFindingSvc;
private MdmGoldenResourceFindingSvc myMdmGoldenResourceFindingSvc;
@Autowired
private PersonHelper myPersonHelper;
private GoldenResourceHelper myGoldenResourceHelper;
@Autowired
private EIDHelper myEIDHelper;
@Autowired
private EmpiLinkDaoSvc myEmpiLinkDaoSvc;
private MdmLinkDaoSvc myMdmLinkDaoSvc;
@Autowired
private IEmpiSettings myEmpiSettings;
private IMdmSettings myMdmSettings;
void handleEmpiUpdate(IAnyResource theResource, MatchedSourceResourceCandidate theMatchedSourceResourceCandidate, MdmTransactionContext theMdmTransactionContext) {
EmpiUpdateContext updateContext = new EmpiUpdateContext(theMatchedSourceResourceCandidate, theResource);
void handleMdmUpdate(IAnyResource theResource, MatchedSourceResourceCandidate theMatchedSourceResourceCandidate, MdmTransactionContext theMdmTransactionContext) {
MdmUpdateContext updateContext = new MdmUpdateContext(theMatchedSourceResourceCandidate, theResource);
if (updateContext.isRemainsMatchedToSamePerson()) {
// Copy over any new external EIDs which don't already exist.
// TODO NG - Eventually this call will use terser to clone data in, once the surviorship rules for copying data will be confirmed
// myPersonHelper.updatePersonFromUpdatedEmpiTarget(updateContext.getMatchedPerson(), theResource, theEmpiTransactionContext);
if (!updateContext.isIncomingResourceHasAnEid() || updateContext.isHasEidsInCommon()) {
//update to patient that uses internal EIDs only.
myEmpiLinkSvc.updateLink(updateContext.getMatchedSourceResource(), theResource, theMatchedSourceResourceCandidate.getMatchResult(), EmpiLinkSourceEnum.AUTO, theMdmTransactionContext);
myMdmLinkSvc.updateLink(updateContext.getMatchedSourceResource(), theResource, theMatchedSourceResourceCandidate.getMatchResult(), MdmLinkSourceEnum.AUTO, theMdmTransactionContext);
} else if (!updateContext.isHasEidsInCommon()) {
handleNoEidsInCommon(theResource, theMatchedSourceResourceCandidate, theMdmTransactionContext, updateContext);
}
@ -80,47 +80,47 @@ public class EmpiEidUpdateService {
}
}
private void handleNoEidsInCommon(IAnyResource theResource, MatchedSourceResourceCandidate theMatchedSourceResourceCandidate, MdmTransactionContext theMdmTransactionContext, EmpiUpdateContext theUpdateContext) {
private void handleNoEidsInCommon(IAnyResource theResource, MatchedSourceResourceCandidate theMatchedSourceResourceCandidate, MdmTransactionContext theMdmTransactionContext, MdmUpdateContext theUpdateContext) {
// the user is simply updating their EID. We propagate this change to the Person.
//overwrite. No EIDS in common, but still same person.
if (myEmpiSettings.isPreventMultipleEids()) {
if (myEmpiLinkDaoSvc.findEmpiMatchLinksBySource(theUpdateContext.getMatchedSourceResource()).size() <= 1) { // If there is only 0/1 link on the person, we can safely overwrite the EID.
if (myMdmSettings.isPreventMultipleEids()) {
if (myMdmLinkDaoSvc.findMdmMatchLinksBySource(theUpdateContext.getMatchedSourceResource()).size() <= 1) { // If there is only 0/1 link on the person, we can safely overwrite the EID.
// if (myPersonHelper.getLinkCount(theUpdateContext.getMatchedSourceResource()) <= 1) { // If there is only 0/1 link on the person, we can safely overwrite the EID.
handleExternalEidOverwrite(theUpdateContext.getMatchedSourceResource(), theResource, theMdmTransactionContext);
} else { // If the person has multiple patients tied to it, we can't just overwrite the EID, so we split the person.
createNewPersonAndFlagAsDuplicate(theResource, theMdmTransactionContext, theUpdateContext.getExistingPerson());
}
} else {
myPersonHelper.handleExternalEidAddition(theUpdateContext.getMatchedSourceResource(), theResource, theMdmTransactionContext);
myGoldenResourceHelper.handleExternalEidAddition(theUpdateContext.getMatchedSourceResource(), theResource, theMdmTransactionContext);
}
myEmpiLinkSvc.updateLink(theUpdateContext.getMatchedSourceResource(), theResource, theMatchedSourceResourceCandidate.getMatchResult(), EmpiLinkSourceEnum.AUTO, theMdmTransactionContext);
myMdmLinkSvc.updateLink(theUpdateContext.getMatchedSourceResource(), theResource, theMatchedSourceResourceCandidate.getMatchResult(), MdmLinkSourceEnum.AUTO, theMdmTransactionContext);
}
private void handleExternalEidOverwrite(IAnyResource thePerson, IAnyResource theResource, MdmTransactionContext theMdmTransactionContext) {
List<CanonicalEID> eidFromResource = myEIDHelper.getExternalEid(theResource);
if (!eidFromResource.isEmpty()) {
myPersonHelper.overwriteExternalEids(thePerson, eidFromResource);
myGoldenResourceHelper.overwriteExternalEids(thePerson, eidFromResource);
}
}
private boolean candidateIsSameAsEmpiLinkPerson(EmpiLink theExistingMatchLink, MatchedSourceResourceCandidate thePersonCandidate) {
return theExistingMatchLink.getSourceResourcePid().equals(thePersonCandidate.getCandidatePersonPid().getIdAsLong());
private boolean candidateIsSameAsMdmLinkPerson(MdmLink theExistingMatchLink, MatchedSourceResourceCandidate thePersonCandidate) {
return theExistingMatchLink.getGoldenResourcePid().equals(thePersonCandidate.getCandidatePersonPid().getIdAsLong());
}
private void createNewPersonAndFlagAsDuplicate(IAnyResource theResource, MdmTransactionContext theMdmTransactionContext, IAnyResource theOldPerson) {
log(theMdmTransactionContext, "Duplicate detected based on the fact that both resources have different external EIDs.");
IAnyResource newPerson = myPersonHelper.createGoldenResourceFromMdmTarget(theResource);
IAnyResource newPerson = myGoldenResourceHelper.createGoldenResourceFromMdmTarget(theResource);
myEmpiLinkSvc.updateLink(newPerson, theResource, EmpiMatchOutcome.NEW_PERSON_MATCH, EmpiLinkSourceEnum.AUTO, theMdmTransactionContext);
myEmpiLinkSvc.updateLink(newPerson, theOldPerson, EmpiMatchOutcome.POSSIBLE_DUPLICATE, EmpiLinkSourceEnum.AUTO, theMdmTransactionContext);
myMdmLinkSvc.updateLink(newPerson, theResource, MdmMatchOutcome.NEW_PERSON_MATCH, MdmLinkSourceEnum.AUTO, theMdmTransactionContext);
myMdmLinkSvc.updateLink(newPerson, theOldPerson, MdmMatchOutcome.POSSIBLE_DUPLICATE, MdmLinkSourceEnum.AUTO, theMdmTransactionContext);
}
private void linkToNewPersonAndFlagAsDuplicate(IAnyResource theResource, IAnyResource theOldPerson, IAnyResource theNewPerson, MdmTransactionContext theMdmTransactionContext) {
log(theMdmTransactionContext, "Changing a match link!");
myEmpiLinkSvc.deleteLink(theOldPerson, theResource, theMdmTransactionContext);
myEmpiLinkSvc.updateLink(theNewPerson, theResource, EmpiMatchOutcome.NEW_PERSON_MATCH, EmpiLinkSourceEnum.AUTO, theMdmTransactionContext);
myMdmLinkSvc.deleteLink(theOldPerson, theResource, theMdmTransactionContext);
myMdmLinkSvc.updateLink(theNewPerson, theResource, MdmMatchOutcome.NEW_PERSON_MATCH, MdmLinkSourceEnum.AUTO, theMdmTransactionContext);
log(theMdmTransactionContext, "Duplicate detected based on the fact that both resources have different external EIDs.");
myEmpiLinkSvc.updateLink(theNewPerson, theOldPerson, EmpiMatchOutcome.POSSIBLE_DUPLICATE, EmpiLinkSourceEnum.AUTO, theMdmTransactionContext);
myMdmLinkSvc.updateLink(theNewPerson, theOldPerson, MdmMatchOutcome.POSSIBLE_DUPLICATE, MdmLinkSourceEnum.AUTO, theMdmTransactionContext);
}
private void log(MdmTransactionContext theMdmTransactionContext, String theMessage) {
@ -131,7 +131,7 @@ public class EmpiEidUpdateService {
/**
* Data class to hold context surrounding an update operation for an EMPI target.
*/
class EmpiUpdateContext {
class MdmUpdateContext {
private final boolean myHasEidsInCommon;
private final boolean myIncomingResourceHasAnEid;
private IAnyResource myExistingPerson;
@ -143,21 +143,21 @@ public class EmpiEidUpdateService {
private final IAnyResource myMatchedSourceResource;
EmpiUpdateContext(MatchedSourceResourceCandidate theMatchedSourceResourceCandidate, IAnyResource theResource) {
MdmUpdateContext(MatchedSourceResourceCandidate theMatchedSourceResourceCandidate, IAnyResource theResource) {
final String resourceType = theResource.getIdElement().getResourceType();
myMatchedSourceResource = myEmpiSourceResourceFindingSvc.getSourceResourceFromMatchedSourceResourceCandidate(theMatchedSourceResourceCandidate, resourceType);
myMatchedSourceResource = myMdmGoldenResourceFindingSvc.getSourceResourceFromMatchedSourceResourceCandidate(theMatchedSourceResourceCandidate, resourceType);
myHasEidsInCommon = myEIDHelper.hasEidOverlap(myMatchedSourceResource, theResource);
myIncomingResourceHasAnEid = !myEIDHelper.getExternalEid(theResource).isEmpty();
Optional<EmpiLink> theExistingMatchLink = myEmpiLinkDaoSvc.getMatchedLinkForTarget(theResource);
Optional<MdmLink> theExistingMatchLink = myMdmLinkDaoSvc.getMatchedLinkForTarget(theResource);
myExistingPerson = null;
if (theExistingMatchLink.isPresent()) {
EmpiLink empiLink = theExistingMatchLink.get();
Long existingPersonPid = empiLink.getSourceResourcePid();
myExistingPerson = myEmpiResourceDaoSvc.readSourceResourceByPid(new ResourcePersistentId(existingPersonPid), resourceType);
myRemainsMatchedToSamePerson = candidateIsSameAsEmpiLinkPerson(empiLink, theMatchedSourceResourceCandidate);
MdmLink mdmLink = theExistingMatchLink.get();
Long existingPersonPid = mdmLink.getGoldenResourcePid();
myExistingPerson = myMdmResourceDaoSvc.readSourceResourceByPid(new ResourcePersistentId(existingPersonPid), resourceType);
myRemainsMatchedToSamePerson = candidateIsSameAsMdmLinkPerson(mdmLink, theMatchedSourceResourceCandidate);
} else {
myRemainsMatchedToSamePerson = false;
}

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.jpa.empi.svc;
package ca.uhn.fhir.jpa.mdm.svc;
/*-
* #%L
@ -20,7 +20,7 @@ package ca.uhn.fhir.jpa.empi.svc;
* #L%
*/
import ca.uhn.fhir.empi.log.Logs;
import ca.uhn.fhir.mdm.log.Logs;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.model.DeleteMethodOutcome;
import ca.uhn.fhir.jpa.dao.expunge.DeleteExpungeService;
@ -35,8 +35,8 @@ import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class EmpiPersonDeletingSvc {
private static final Logger ourLog = Logs.getEmpiTroubleshootingLog();
public class MdmGoldenResourceDeletingSvc {
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
/**
* This is here for the case of possible infinite loops. Technically batch conflict deletion should handle this, but this is an escape hatch.
@ -50,7 +50,7 @@ public class EmpiPersonDeletingSvc {
@Autowired
DeleteExpungeService myDeleteExpungeService;
public DeleteMethodOutcome expungePersonPids(List<Long> thePersonPids, ServletRequestDetails theRequestDetails) {
public DeleteMethodOutcome expungeGoldenResourcePids(List<Long> thePersonPids, ServletRequestDetails theRequestDetails) {
return myDeleteExpungeService.expungeByResourcePids(ProviderConstants.MDM_CLEAR, "Person", new SliceImpl<>(thePersonPids), theRequestDetails);
}
}

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.jpa.empi.svc;
package ca.uhn.fhir.jpa.mdm.svc;
/*-
* #%L
@ -20,14 +20,14 @@ package ca.uhn.fhir.jpa.empi.svc;
* #L%
*/
import ca.uhn.fhir.empi.api.EmpiLinkJson;
import ca.uhn.fhir.empi.api.EmpiLinkSourceEnum;
import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
import ca.uhn.fhir.empi.api.IEmpiLinkQuerySvc;
import ca.uhn.fhir.empi.model.MdmTransactionContext;
import ca.uhn.fhir.mdm.api.MdmLinkJson;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.mdm.api.IMdmLinkQuerySvc;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
import ca.uhn.fhir.jpa.empi.dao.EmpiLinkDaoSvc;
import ca.uhn.fhir.jpa.entity.EmpiLink;
import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc;
import ca.uhn.fhir.jpa.entity.MdmLink;
import org.hl7.fhir.instance.model.api.IIdType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -36,33 +36,33 @@ import org.springframework.data.domain.Example;
import java.util.stream.Stream;
public class EmpiLinkQuerySvcImpl implements IEmpiLinkQuerySvc {
private static final Logger ourLog = LoggerFactory.getLogger(EmpiLinkQuerySvcImpl.class);
public class MdmLinkQuerySvcImpl implements IMdmLinkQuerySvc {
private static final Logger ourLog = LoggerFactory.getLogger(MdmLinkQuerySvcImpl.class);
@Autowired
IdHelperService myIdHelperService;
@Autowired
EmpiLinkDaoSvc myEmpiLinkDaoSvc;
MdmLinkDaoSvc myMdmLinkDaoSvc;
@Override
public Stream<EmpiLinkJson> queryLinks(IIdType thePersonId, IIdType theTargetId, EmpiMatchResultEnum theMatchResult, EmpiLinkSourceEnum theLinkSource, MdmTransactionContext theEmpiContext) {
Example<EmpiLink> exampleLink = exampleLinkFromParameters(thePersonId, theTargetId, theMatchResult, theLinkSource);
return myEmpiLinkDaoSvc.findEmpiLinkByExample(exampleLink).stream()
.filter(empiLink -> empiLink.getMatchResult() != EmpiMatchResultEnum.POSSIBLE_DUPLICATE)
public Stream<MdmLinkJson> queryLinks(IIdType thePersonId, IIdType theTargetId, MdmMatchResultEnum theMatchResult, MdmLinkSourceEnum theLinkSource, MdmTransactionContext theMdmTransactionContext) {
Example<MdmLink> exampleLink = exampleLinkFromParameters(thePersonId, theTargetId, theMatchResult, theLinkSource);
return myMdmLinkDaoSvc.findMdmLinkByExample(exampleLink).stream()
.filter(mdmLink -> mdmLink.getMatchResult() != MdmMatchResultEnum.POSSIBLE_DUPLICATE)
.map(this::toJson);
}
@Override
public Stream<EmpiLinkJson> getDuplicatePersons(MdmTransactionContext theEmpiContext) {
Example<EmpiLink> exampleLink = exampleLinkFromParameters(null, null, EmpiMatchResultEnum.POSSIBLE_DUPLICATE, null);
return myEmpiLinkDaoSvc.findEmpiLinkByExample(exampleLink).stream().map(this::toJson);
public Stream<MdmLinkJson> getDuplicatePersons(MdmTransactionContext theMdmContext) {
Example<MdmLink> exampleLink = exampleLinkFromParameters(null, null, MdmMatchResultEnum.POSSIBLE_DUPLICATE, null);
return myMdmLinkDaoSvc.findMdmLinkByExample(exampleLink).stream().map(this::toJson);
}
private EmpiLinkJson toJson(EmpiLink theLink) {
EmpiLinkJson retval = new EmpiLinkJson();
private MdmLinkJson toJson(MdmLink theLink) {
MdmLinkJson retval = new MdmLinkJson();
String targetId = myIdHelperService.resourceIdFromPidOrThrowException(theLink.getTargetPid()).toVersionless().getValue();
retval.setTargetId(targetId);
String goldenResourceId = myIdHelperService.resourceIdFromPidOrThrowException(theLink.getSourceResourcePid()).toVersionless().getValue();
String goldenResourceId = myIdHelperService.resourceIdFromPidOrThrowException(theLink.getGoldenResourcePid()).toVersionless().getValue();
retval.setGoldenResourceId(goldenResourceId);
retval.setCreated(theLink.getCreated());
retval.setEidMatch(theLink.getEidMatch());
@ -76,20 +76,20 @@ public class EmpiLinkQuerySvcImpl implements IEmpiLinkQuerySvc {
return retval;
}
private Example<EmpiLink> exampleLinkFromParameters(IIdType thePersonId, IIdType theTargetId, EmpiMatchResultEnum theMatchResult, EmpiLinkSourceEnum theLinkSource) {
EmpiLink empiLink = myEmpiLinkDaoSvc.newEmpiLink();
private Example<MdmLink> exampleLinkFromParameters(IIdType thePersonId, IIdType theTargetId, MdmMatchResultEnum theMatchResult, MdmLinkSourceEnum theLinkSource) {
MdmLink mdmLink = myMdmLinkDaoSvc.newMdmLink();
if (thePersonId != null) {
empiLink.setGoldenResourcePid(myIdHelperService.getPidOrThrowException(thePersonId));
mdmLink.setGoldenResourcePid(myIdHelperService.getPidOrThrowException(thePersonId));
}
if (theTargetId != null) {
empiLink.setTargetPid(myIdHelperService.getPidOrThrowException(theTargetId));
mdmLink.setTargetPid(myIdHelperService.getPidOrThrowException(theTargetId));
}
if (theMatchResult != null) {
empiLink.setMatchResult(theMatchResult);
mdmLink.setMatchResult(theMatchResult);
}
if (theLinkSource != null) {
empiLink.setLinkSource(theLinkSource);
mdmLink.setLinkSource(theLinkSource);
}
return Example.of(empiLink);
return Example.of(mdmLink);
}
}

View File

@ -0,0 +1,143 @@
package ca.uhn.fhir.jpa.mdm.svc;
/*-
* #%L
* HAPI FHIR JPA Server - Enterprise Master Patient Index
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
* 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%
*/
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
import ca.uhn.fhir.jpa.entity.MdmLink;
import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc;
import ca.uhn.fhir.mdm.api.IMdmLinkSvc;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchOutcome;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.mdm.log.Logs;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.mdm.util.GoldenResourceHelper;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.transaction.Transactional;
import java.util.Optional;
/**
* This class is in charge of managing MdmLinks between Golden Resources and target resources
*/
@Service
public class MdmLinkSvcImpl implements IMdmLinkSvc {
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
@Autowired
private MdmResourceDaoSvc myMdmResourceDaoSvc;
@Autowired
private MdmLinkDaoSvc myMdmLinkDaoSvc;
@Autowired
private GoldenResourceHelper myGoldenResourceHelper;
@Autowired
private IdHelperService myIdHelperService;
@Override
@Transactional
public void updateLink(IAnyResource thePerson, IAnyResource theTarget, MdmMatchOutcome theMatchOutcome, MdmLinkSourceEnum theLinkSource, MdmTransactionContext theMdmTransactionContext) {
if (theMatchOutcome.isPossibleDuplicate() && personsLinkedAsNoMatch(thePerson, theTarget)) {
log(theMdmTransactionContext, thePerson.getIdElement().toUnqualifiedVersionless() +
" is linked as NO_MATCH with " +
theTarget.getIdElement().toUnqualifiedVersionless() +
" not linking as POSSIBLE_DUPLICATE.");
return;
}
MdmMatchResultEnum matchResultEnum = theMatchOutcome.getMatchResultEnum();
validateRequestIsLegal(thePerson, theTarget, matchResultEnum, theLinkSource);
myMdmResourceDaoSvc.upsertSourceResource(thePerson, theMdmTransactionContext.getResourceType());
createOrUpdateLinkEntity(thePerson, theTarget, theMatchOutcome, theLinkSource, theMdmTransactionContext);
}
private boolean personsLinkedAsNoMatch(IAnyResource thePerson, IAnyResource theTarget) {
Long personId = myIdHelperService.getPidOrThrowException(thePerson);
Long targetId = myIdHelperService.getPidOrThrowException(theTarget);
// TODO perf collapse into one query
return myMdmLinkDaoSvc.getMdmLinksByPersonPidTargetPidAndMatchResult(personId, targetId, MdmMatchResultEnum.NO_MATCH).isPresent() ||
myMdmLinkDaoSvc.getMdmLinksByPersonPidTargetPidAndMatchResult(targetId, personId, MdmMatchResultEnum.NO_MATCH).isPresent();
}
@Override
public void deleteLink(IAnyResource theSourceResource, IAnyResource theTargetResource, MdmTransactionContext theMdmTransactionContext) {
Optional<MdmLink> optionalMdmLink = getMdmLinkForGoldenResourceTargetPair(theSourceResource, theTargetResource);
if (optionalMdmLink.isPresent()) {
MdmLink mdmLink = optionalMdmLink.get();
log(theMdmTransactionContext, "Deleting MdmLink [" + theSourceResource.getIdElement().toVersionless() + " -> " + theTargetResource.getIdElement().toVersionless() + "] with result: " + mdmLink.getMatchResult());
myMdmLinkDaoSvc.deleteLink(mdmLink);
}
}
/**
* Helper function which runs various business rules about what types of requests are allowed.
*/
private void validateRequestIsLegal(IAnyResource theGoldenResource, IAnyResource theResource, MdmMatchResultEnum theMatchResult, MdmLinkSourceEnum theLinkSource) {
Optional<MdmLink> oExistingLink = getMdmLinkForGoldenResourceTargetPair(theGoldenResource, theResource);
if (oExistingLink.isPresent() && systemIsAttemptingToModifyManualLink(theLinkSource, oExistingLink.get())) {
throw new InternalErrorException("MDM system is not allowed to modify links on manually created links");
}
if (systemIsAttemptingToAddNoMatch(theLinkSource, theMatchResult)) {
throw new InternalErrorException("MDM system is not allowed to automatically NO_MATCH a resource");
}
}
/**
* Helper function which detects when the MDM system is attempting to add a NO_MATCH link, which is not allowed.
*/
private boolean systemIsAttemptingToAddNoMatch(MdmLinkSourceEnum theLinkSource, MdmMatchResultEnum theMatchResult) {
return theLinkSource == MdmLinkSourceEnum.AUTO && theMatchResult == MdmMatchResultEnum.NO_MATCH;
}
/**
* Helper function to let us catch when System MDM rules are attempting to override a manually defined link.
*/
private boolean systemIsAttemptingToModifyManualLink(MdmLinkSourceEnum theIncomingSource, MdmLink theExistingSource) {
return theIncomingSource == MdmLinkSourceEnum.AUTO && theExistingSource.isManual();
}
private Optional<MdmLink> getMdmLinkForGoldenResourceTargetPair(IAnyResource thePerson, IAnyResource theCandidate) {
if (thePerson.getIdElement().getIdPart() == null || theCandidate.getIdElement().getIdPart() == null) {
return Optional.empty();
} else {
return myMdmLinkDaoSvc.getLinkBySourceResourcePidAndTargetResourcePid(
myIdHelperService.getPidOrNull(thePerson),
myIdHelperService.getPidOrNull(theCandidate)
);
}
}
private void createOrUpdateLinkEntity(IBaseResource theSourceResource, IBaseResource theTargetResource, MdmMatchOutcome theMatchOutcome, MdmLinkSourceEnum theLinkSource, MdmTransactionContext theMdmTransactionContext) {
myMdmLinkDaoSvc.createOrUpdateLinkEntity(theSourceResource, theTargetResource, theMatchOutcome, theLinkSource, theMdmTransactionContext);
}
private void log(MdmTransactionContext theMdmTransactionContext, String theMessage) {
theMdmTransactionContext.addTransactionLogMessage(theMessage);
ourLog.debug(theMessage);
}
}

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.jpa.empi.svc;
package ca.uhn.fhir.jpa.mdm.svc;
/*-
* #%L
@ -21,20 +21,18 @@ package ca.uhn.fhir.jpa.empi.svc;
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.empi.api.EmpiConstants;
import ca.uhn.fhir.empi.api.EmpiLinkSourceEnum;
import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
import ca.uhn.fhir.empi.api.IEmpiLinkSvc;
import ca.uhn.fhir.empi.api.IEmpiLinkUpdaterSvc;
import ca.uhn.fhir.empi.api.IEmpiSettings;
import ca.uhn.fhir.empi.log.Logs;
import ca.uhn.fhir.empi.model.MdmTransactionContext;
import ca.uhn.fhir.empi.rules.config.EmpiSettings;
import ca.uhn.fhir.empi.util.EmpiUtil;
import ca.uhn.fhir.empi.util.MessageHelper;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.mdm.api.IMdmLinkSvc;
import ca.uhn.fhir.mdm.api.IMdmLinkUpdaterSvc;
import ca.uhn.fhir.mdm.api.IMdmSettings;
import ca.uhn.fhir.mdm.log.Logs;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.mdm.util.MdmUtil;
import ca.uhn.fhir.mdm.util.MessageHelper;
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
import ca.uhn.fhir.jpa.empi.dao.EmpiLinkDaoSvc;
import ca.uhn.fhir.jpa.entity.EmpiLink;
import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc;
import ca.uhn.fhir.jpa.entity.MdmLink;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.provider.ProviderConstants;
import org.hl7.fhir.instance.model.api.IAnyResource;
@ -45,30 +43,30 @@ import org.springframework.transaction.annotation.Transactional;
import java.util.Objects;
import java.util.Optional;
public class EmpiLinkUpdaterSvcImpl implements IEmpiLinkUpdaterSvc {
public class MdmLinkUpdaterSvcImpl implements IMdmLinkUpdaterSvc {
private static final Logger ourLog = Logs.getEmpiTroubleshootingLog();
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
@Autowired
FhirContext myFhirContext;
@Autowired
IdHelperService myIdHelperService;
@Autowired
EmpiLinkDaoSvc myEmpiLinkDaoSvc;
MdmLinkDaoSvc myMdmLinkDaoSvc;
@Autowired
IEmpiLinkSvc myEmpiLinkSvc;
IMdmLinkSvc myMdmLinkSvc;
@Autowired
EmpiResourceDaoSvc myEmpiResourceDaoSvc;
MdmResourceDaoSvc myMdmResourceDaoSvc;
@Autowired
EmpiMatchLinkSvc myEmpiMatchLinkSvc;
MdmMatchLinkSvc myMdmMatchLinkSvc;
@Autowired
IEmpiSettings myEmpiSettings;
IMdmSettings myMdmSettings;
@Autowired
MessageHelper myMessageHelper;
@Transactional
@Override
public IAnyResource updateLink(IAnyResource theGoldenResource, IAnyResource theTarget, EmpiMatchResultEnum theMatchResult, MdmTransactionContext theEmpiContext) {
public IAnyResource updateLink(IAnyResource theGoldenResource, IAnyResource theTarget, MdmMatchResultEnum theMatchResult, MdmTransactionContext theMdmContext) {
String targetType = myFhirContext.getResourceType(theTarget);
validateUpdateLinkRequest(theGoldenResource, theTarget, theMatchResult, targetType);
@ -76,43 +74,42 @@ public class EmpiLinkUpdaterSvcImpl implements IEmpiLinkUpdaterSvc {
Long goldenResourceId = myIdHelperService.getPidOrThrowException(theGoldenResource);
Long targetId = myIdHelperService.getPidOrThrowException(theTarget);
Optional<EmpiLink> oEmpiLink = myEmpiLinkDaoSvc.getLinkBySourceResourcePidAndTargetResourcePid(goldenResourceId, targetId);
if (!oEmpiLink.isPresent()) {
Optional<MdmLink> optionalMdmLink = myMdmLinkDaoSvc.getLinkBySourceResourcePidAndTargetResourcePid(goldenResourceId, targetId);
if (!optionalMdmLink.isPresent()) {
throw new InvalidRequestException(myMessageHelper.getMessageForNoLink(theGoldenResource, theTarget));
}
EmpiLink empiLink = oEmpiLink.get();
if (empiLink.getMatchResult() == theMatchResult) {
MdmLink mdmLink = optionalMdmLink.get();
if (mdmLink.getMatchResult() == theMatchResult) {
ourLog.warn("MDM Link for " + theGoldenResource.getIdElement().toVersionless() + ", " + theTarget.getIdElement().toVersionless() + " already has value " + theMatchResult + ". Nothing to do.");
return theGoldenResource;
}
ourLog.info("Manually updating MDM Link for " + theGoldenResource.getIdElement().toVersionless() + ", " + theTarget.getIdElement().toVersionless() + " from " + empiLink.getMatchResult() + " to " + theMatchResult + ".");
empiLink.setMatchResult(theMatchResult);
empiLink.setLinkSource(EmpiLinkSourceEnum.MANUAL);
myEmpiLinkDaoSvc.save(empiLink);
// myEmpiLinkSvc.syncEmpiLinksToPersonLinks(theGoldenResource, theEmpiContext);
myEmpiResourceDaoSvc.upsertSourceResource(theGoldenResource, theEmpiContext.getResourceType());
if (theMatchResult == EmpiMatchResultEnum.NO_MATCH) {
ourLog.info("Manually updating MDM Link for " + theGoldenResource.getIdElement().toVersionless() + ", " + theTarget.getIdElement().toVersionless() + " from " + mdmLink.getMatchResult() + " to " + theMatchResult + ".");
mdmLink.setMatchResult(theMatchResult);
mdmLink.setLinkSource(MdmLinkSourceEnum.MANUAL);
myMdmLinkDaoSvc.save(mdmLink);
myMdmResourceDaoSvc.upsertSourceResource(theGoldenResource, theMdmContext.getResourceType());
if (theMatchResult == MdmMatchResultEnum.NO_MATCH) {
// Need to find a new Person to link this target to
myEmpiMatchLinkSvc.updateEmpiLinksForEmpiTarget(theTarget, theEmpiContext);
myMdmMatchLinkSvc.updateMdmLinksForMdmTarget(theTarget, theMdmContext);
}
return theGoldenResource;
}
private void validateUpdateLinkRequest(IAnyResource theGoldenRecord, IAnyResource theTarget, EmpiMatchResultEnum theMatchResult, String theTargetType) {
private void validateUpdateLinkRequest(IAnyResource theGoldenRecord, IAnyResource theTarget, MdmMatchResultEnum theMatchResult, String theTargetType) {
String goldenRecordType = myFhirContext.getResourceType(theGoldenRecord);
if (theMatchResult != EmpiMatchResultEnum.NO_MATCH &&
theMatchResult != EmpiMatchResultEnum.MATCH) {
if (theMatchResult != MdmMatchResultEnum.NO_MATCH &&
theMatchResult != MdmMatchResultEnum.MATCH) {
throw new InvalidRequestException(myMessageHelper.getMessageForUnsupportedMatchResult());
}
if (!myEmpiSettings.isSupportedMdmType(goldenRecordType)) {
if (!myMdmSettings.isSupportedMdmType(goldenRecordType)) {
throw new InvalidRequestException(myMessageHelper.getMessageForUnsupportedFirstArgumentTypeInUpdate(goldenRecordType));
}
if (!myEmpiSettings.isSupportedMdmType(theTargetType)) {
if (!myMdmSettings.isSupportedMdmType(theTargetType)) {
throw new InvalidRequestException(myMessageHelper.getMessageForUnsupportedSecondArgumentTypeInUpdate(theTargetType));
}
@ -120,39 +117,39 @@ public class EmpiLinkUpdaterSvcImpl implements IEmpiLinkUpdaterSvc {
throw new InvalidRequestException(myMessageHelper.getMessageForArgumentTypeMismatchInUpdate(goldenRecordType, theTargetType));
}
if (!EmpiUtil.isEmpiManaged(theGoldenRecord)) {
if (!MdmUtil.isMdmManaged(theGoldenRecord)) {
throw new InvalidRequestException(myMessageHelper.getMessageForUnmanagedResource());
}
if (!EmpiUtil.isEmpiAccessible(theTarget)) {
if (!MdmUtil.isMdmAllowed(theTarget)) {
throw new InvalidRequestException(myMessageHelper.getMessageForUnsupportedTarget());
}
}
@Transactional
@Override
public void notDuplicatePerson(IAnyResource thePerson, IAnyResource theTarget, MdmTransactionContext theEmpiContext) {
public void notDuplicatePerson(IAnyResource thePerson, IAnyResource theTarget, MdmTransactionContext theMdmContext) {
validateNotDuplicatePersonRequest(thePerson, theTarget);
Long personId = myIdHelperService.getPidOrThrowException(thePerson);
Long targetId = myIdHelperService.getPidOrThrowException(theTarget);
Optional<EmpiLink> oEmpiLink = myEmpiLinkDaoSvc.getLinkBySourceResourcePidAndTargetResourcePid(personId, targetId);
if (!oEmpiLink.isPresent()) {
Optional<MdmLink> oMdmLink = myMdmLinkDaoSvc.getLinkBySourceResourcePidAndTargetResourcePid(personId, targetId);
if (!oMdmLink.isPresent()) {
throw new InvalidRequestException("No link exists between " + thePerson.getIdElement().toVersionless() + " and " + theTarget.getIdElement().toVersionless());
}
EmpiLink empiLink = oEmpiLink.get();
if (!empiLink.isPossibleDuplicate()) {
MdmLink mdmLink = oMdmLink.get();
if (!mdmLink.isPossibleDuplicate()) {
throw new InvalidRequestException(thePerson.getIdElement().toVersionless() + " and " + theTarget.getIdElement().toVersionless() + " are not linked as POSSIBLE_DUPLICATE.");
}
empiLink.setMatchResult(EmpiMatchResultEnum.NO_MATCH);
empiLink.setLinkSource(EmpiLinkSourceEnum.MANUAL);
myEmpiLinkDaoSvc.save(empiLink);
mdmLink.setMatchResult(MdmMatchResultEnum.NO_MATCH);
mdmLink.setLinkSource(MdmLinkSourceEnum.MANUAL);
myMdmLinkDaoSvc.save(mdmLink);
}
/**
* Ensure that the two resources are of the same type and both are managed by HAPI-EMPI.
* Ensure that the two resources are of the same type and both are managed by HAPI-MDM
*/
private void validateNotDuplicatePersonRequest(IAnyResource theGoldenResource, IAnyResource theTarget) {
String goldenResourceType = myFhirContext.getResourceType(theGoldenResource);
@ -161,8 +158,8 @@ public class EmpiLinkUpdaterSvcImpl implements IEmpiLinkUpdaterSvc {
throw new InvalidRequestException("First argument to " + ProviderConstants.MDM_UPDATE_LINK + " must be the same resource type as the second argument. Was " + goldenResourceType + "/" + targetType);
}
if (!EmpiUtil.isEmpiManaged(theGoldenResource) || !EmpiUtil.isEmpiManaged(theTarget)) {
throw new InvalidRequestException("Only EMPI Managed Golden Resources may be updated via this operation. The resource provided is not tagged as managed by hapi-empi");
if (!MdmUtil.isMdmManaged(theGoldenResource) || !MdmUtil.isMdmManaged(theTarget)) {
throw new InvalidRequestException("Only MDM Managed Golden Resources may be updated via this operation. The resource provided is not tagged as managed by hapi-empi");
}
}
}

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.jpa.empi.svc;
package ca.uhn.fhir.jpa.mdm.svc;
/*-
* #%L
@ -20,10 +20,10 @@ package ca.uhn.fhir.jpa.empi.svc;
* #L%
*/
import ca.uhn.fhir.empi.api.IEmpiMatchFinderSvc;
import ca.uhn.fhir.empi.api.MatchedTarget;
import ca.uhn.fhir.empi.rules.svc.EmpiResourceMatcherSvc;
import ca.uhn.fhir.jpa.empi.svc.candidate.EmpiCandidateSearchSvc;
import ca.uhn.fhir.mdm.api.IMdmMatchFinderSvc;
import ca.uhn.fhir.mdm.api.MatchedTarget;
import ca.uhn.fhir.mdm.rules.svc.MdmResourceMatcherSvc;
import ca.uhn.fhir.jpa.mdm.svc.candidate.MdmCandidateSearchSvc;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@ -34,20 +34,20 @@ import java.util.List;
import java.util.stream.Collectors;
@Service
public class EmpiMatchFinderSvcImpl implements IEmpiMatchFinderSvc {
public class MdmMatchFinderSvcImpl implements IMdmMatchFinderSvc {
@Autowired
private EmpiCandidateSearchSvc myEmpiCandidateSearchSvc;
private MdmCandidateSearchSvc myMdmCandidateSearchSvc;
@Autowired
private EmpiResourceMatcherSvc myEmpiResourceMatcherSvc;
private MdmResourceMatcherSvc myMdmResourceMatcherSvc;
@Override
@Nonnull
public List<MatchedTarget> getMatchedTargets(String theResourceType, IAnyResource theResource) {
Collection<IAnyResource> targetCandidates = myEmpiCandidateSearchSvc.findCandidates(theResourceType, theResource);
Collection<IAnyResource> targetCandidates = myMdmCandidateSearchSvc.findCandidates(theResourceType, theResource);
return targetCandidates.stream()
.map(candidate -> new MatchedTarget(candidate, myEmpiResourceMatcherSvc.getMatchResult(theResource, candidate)))
.map(candidate -> new MatchedTarget(candidate, myMdmResourceMatcherSvc.getMatchResult(theResource, candidate)))
.collect(Collectors.toList());
}

View File

@ -0,0 +1,165 @@
package ca.uhn.fhir.jpa.mdm.svc;
/*-
* #%L
* HAPI FHIR JPA Server - Enterprise Master Patient Index
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
* 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%
*/
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchOutcome;
import ca.uhn.fhir.mdm.api.IMdmLinkSvc;
import ca.uhn.fhir.mdm.log.Logs;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.mdm.util.MdmUtil;
import ca.uhn.fhir.mdm.util.GoldenResourceHelper;
import ca.uhn.fhir.jpa.mdm.svc.candidate.CandidateList;
import ca.uhn.fhir.jpa.mdm.svc.candidate.MdmGoldenResourceFindingSvc;
import ca.uhn.fhir.jpa.mdm.svc.candidate.MatchedSourceResourceCandidate;
import ca.uhn.fhir.rest.server.TransactionLogMessages;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* MdmMatchLinkSvc is the entrypoint for HAPI's MDM system. An incoming resource can call
* updateMdmLinksForMdmTarget and the underlying MDM system will take care of matching it to a person, or creating a
* new Person if a suitable one was not found.
*/
@Service
public class MdmMatchLinkSvc {
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
@Autowired
private IMdmLinkSvc myMdmLinkSvc;
@Autowired
private MdmGoldenResourceFindingSvc myMdmGoldenResourceFindingSvc;
@Autowired
private GoldenResourceHelper myGoldenResourceHelper;
@Autowired
private MdmEidUpdateService myEidUpdateService;
/**
* Given an MDM Target (consisting of either a Patient or a Practitioner), find a suitable Person candidate for them,
* or create one if one does not exist. Performs matching based on rules defined in empi-rules.json.
* Does nothing if resource is determined to be not managed by MDM.
*
* @param theResource the incoming MDM target, which is either a Patient or Practitioner.
* @param theMdmTransactionContext
* @return an {@link TransactionLogMessages} which contains all informational messages related to MDM processing of this resource.
*/
public MdmTransactionContext updateMdmLinksForMdmTarget(IAnyResource theResource, MdmTransactionContext theMdmTransactionContext) {
if (MdmUtil.isMdmAllowed(theResource)) {
return doMdmUpdate(theResource, theMdmTransactionContext);
} else {
return null;
}
}
private MdmTransactionContext doMdmUpdate(IAnyResource theResource, MdmTransactionContext theMdmTransactionContext) {
CandidateList candidateList = myMdmGoldenResourceFindingSvc.findSourceResourceCandidates(theResource);
if (candidateList.isEmpty()) {
handleMdmWithNoCandidates(theResource, theMdmTransactionContext);
} else if (candidateList.exactlyOneMatch()) {
handleMdmWithSingleCandidate(theResource, candidateList.getOnlyMatch(), theMdmTransactionContext);
} else {
handleMdmWithMultipleCandidates(theResource, candidateList, theMdmTransactionContext);
}
return theMdmTransactionContext;
}
private void handleMdmWithMultipleCandidates(IAnyResource theResource, CandidateList theCandidateList, MdmTransactionContext theMdmTransactionContext) {
MatchedSourceResourceCandidate firstMatch = theCandidateList.getFirstMatch();
Long samplePersonPid = firstMatch.getCandidatePersonPid().getIdAsLong();
boolean allSamePerson = theCandidateList.stream()
.allMatch(candidate -> candidate.getCandidatePersonPid().getIdAsLong().equals(samplePersonPid));
if (allSamePerson) {
log(theMdmTransactionContext, "MDM received multiple match candidates, but they are all linked to the same person.");
handleMdmWithSingleCandidate(theResource, firstMatch, theMdmTransactionContext);
} else {
log(theMdmTransactionContext, "MDM received multiple match candidates, that were linked to different Persons. Setting POSSIBLE_DUPLICATES and POSSIBLE_MATCHES.");
//Set them all as POSSIBLE_MATCH
List<IAnyResource> persons = new ArrayList<>();
for (MatchedSourceResourceCandidate matchedSourceResourceCandidate : theCandidateList.getCandidates()) {
IAnyResource person = myMdmGoldenResourceFindingSvc
.getSourceResourceFromMatchedSourceResourceCandidate(matchedSourceResourceCandidate, theMdmTransactionContext.getResourceType());
MdmMatchOutcome outcome = MdmMatchOutcome.POSSIBLE_MATCH;
outcome.setEidMatch(theCandidateList.isEidMatch());
myMdmLinkSvc.updateLink(person, theResource, outcome, MdmLinkSourceEnum.AUTO, theMdmTransactionContext);
persons.add(person);
}
//Set all Persons as POSSIBLE_DUPLICATE of the last person.
IAnyResource firstPerson = persons.get(0);
persons.subList(1, persons.size())
.forEach(possibleDuplicatePerson -> {
MdmMatchOutcome outcome = MdmMatchOutcome.POSSIBLE_DUPLICATE;
outcome.setEidMatch(theCandidateList.isEidMatch());
myMdmLinkSvc.updateLink(firstPerson, possibleDuplicatePerson, outcome, MdmLinkSourceEnum.AUTO, theMdmTransactionContext);
});
}
}
private void handleMdmWithNoCandidates(IAnyResource theResource, MdmTransactionContext theMdmTransactionContext) {
log(theMdmTransactionContext, String.format("There were no matched candidates for MDM, creating a new %s.", theResource.getIdElement().getResourceType()));
IAnyResource newGoldenResource = myGoldenResourceHelper.createGoldenResourceFromMdmTarget(theResource);
// TODO GGG :)
// 1. Get the right helper
// 2. Create source resoruce for the MDM target
// 3. UPDATE MDM LINK TABLE
myMdmLinkSvc.updateLink(newGoldenResource, theResource, MdmMatchOutcome.NEW_PERSON_MATCH, MdmLinkSourceEnum.AUTO, theMdmTransactionContext);
}
private void handleMdmCreate(IAnyResource theTargetResource, MatchedSourceResourceCandidate thePersonCandidate, MdmTransactionContext theMdmTransactionContext) {
log(theMdmTransactionContext, "MDM has narrowed down to one candidate for matching.");
IAnyResource sourceResource = myMdmGoldenResourceFindingSvc.getSourceResourceFromMatchedSourceResourceCandidate(thePersonCandidate, theMdmTransactionContext.getResourceType());
if (myGoldenResourceHelper.isPotentialDuplicate(sourceResource, theTargetResource)) {
log(theMdmTransactionContext, "Duplicate detected based on the fact that both resources have different external EIDs.");
IAnyResource newSourceResource = myGoldenResourceHelper.createGoldenResourceFromMdmTarget(theTargetResource);
myMdmLinkSvc.updateLink(newSourceResource, theTargetResource, MdmMatchOutcome.NEW_PERSON_MATCH, MdmLinkSourceEnum.AUTO, theMdmTransactionContext);
myMdmLinkSvc.updateLink(newSourceResource, sourceResource, MdmMatchOutcome.POSSIBLE_DUPLICATE, MdmLinkSourceEnum.AUTO, theMdmTransactionContext);
} else {
if (thePersonCandidate.isMatch()) {
myGoldenResourceHelper.handleExternalEidAddition(sourceResource, theTargetResource, theMdmTransactionContext);
//TODO MDM GGG/NG: eventually we need to add survivorship rules of attributes here. Currently no data is copied over except EIDs.
}
myMdmLinkSvc.updateLink(sourceResource, theTargetResource, thePersonCandidate.getMatchResult(), MdmLinkSourceEnum.AUTO, theMdmTransactionContext);
}
}
private void handleMdmWithSingleCandidate(IAnyResource theResource, MatchedSourceResourceCandidate thePersonCandidate, MdmTransactionContext theMdmTransactionContext) {
log(theMdmTransactionContext, "MDM has narrowed down to one candidate for matching.");
if (theMdmTransactionContext.getRestOperation().equals(MdmTransactionContext.OperationType.UPDATE_RESOURCE)) {
myEidUpdateService.handleMdmUpdate(theResource, thePersonCandidate, theMdmTransactionContext);
} else {
handleMdmCreate(theResource, thePersonCandidate, theMdmTransactionContext);
}
}
private void log(MdmTransactionContext theMdmTransactionContext, String theMessage) {
theMdmTransactionContext.addTransactionLogMessage(theMessage);
ourLog.debug(theMessage);
}
}

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.jpa.empi.svc;
package ca.uhn.fhir.jpa.mdm.svc;
/*-
* #%L
@ -20,8 +20,8 @@ package ca.uhn.fhir.jpa.empi.svc;
* #L%
*/
import ca.uhn.fhir.empi.api.EmpiConstants;
import ca.uhn.fhir.empi.api.IEmpiSettings;
import ca.uhn.fhir.mdm.api.MdmConstants;
import ca.uhn.fhir.mdm.api.IMdmSettings;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome;
@ -41,14 +41,14 @@ import java.util.List;
import java.util.Optional;
@Service
public class EmpiResourceDaoSvc {
public class MdmResourceDaoSvc {
private static final int MAX_MATCHING_PERSONS = 1000;
@Autowired
DaoRegistry myDaoRegistry;
@Autowired
IEmpiSettings myEmpiConfig;
IMdmSettings myMdmSettings;
public DaoMethodOutcome upsertSourceResource(IAnyResource theSourceResource, String theResourceType) {
IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(theResourceType);
@ -66,7 +66,7 @@ public class EmpiResourceDaoSvc {
*/
public void removeGoldenResourceTag(IAnyResource theGoldenResource, String theResourcetype) {
IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(theResourcetype);
resourceDao.removeTag(theGoldenResource.getIdElement(), TagTypeEnum.TAG, EmpiConstants.SYSTEM_GOLDEN_RECORD_STATUS, EmpiConstants.CODE_GOLDEN_RECORD);
resourceDao.removeTag(theGoldenResource.getIdElement(), TagTypeEnum.TAG, MdmConstants.SYSTEM_GOLDEN_RECORD_STATUS, MdmConstants.CODE_GOLDEN_RECORD);
}
public IAnyResource readSourceResourceByPid(ResourcePersistentId theSourceResourcePid, String theResourceType) {
@ -86,7 +86,7 @@ public class EmpiResourceDaoSvc {
return Optional.empty();
} else if (resources.size() > 1) {
throw new InternalErrorException("Found more than one active " +
EmpiConstants.CODE_HAPI_MDM_MANAGED +
MdmConstants.CODE_HAPI_MDM_MANAGED +
" Person with EID " +
theEid +
": " +
@ -103,8 +103,8 @@ public class EmpiResourceDaoSvc {
private SearchParameterMap buildEidSearchParameterMap(String theTheEid) {
SearchParameterMap map = new SearchParameterMap();
map.setLoadSynchronous(true);
map.add("identifier", new TokenParam(myEmpiConfig.getEmpiRules().getEnterpriseEIDSystem(), theTheEid));
map.add("_tag", new TokenParam(EmpiConstants.SYSTEM_GOLDEN_RECORD_STATUS, EmpiConstants.CODE_GOLDEN_RECORD));
map.add("identifier", new TokenParam(myMdmSettings.getMdmRules().getEnterpriseEIDSystem(), theTheEid));
map.add("_tag", new TokenParam(MdmConstants.SYSTEM_GOLDEN_RECORD_STATUS, MdmConstants.CODE_GOLDEN_RECORD));
return map;
}
}

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.jpa.empi.svc;
package ca.uhn.fhir.jpa.mdm.svc;
/*-
* #%L
@ -21,9 +21,10 @@ package ca.uhn.fhir.jpa.empi.svc;
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.empi.api.IEmpiSettings;
import ca.uhn.fhir.empi.log.Logs;
import ca.uhn.fhir.empi.rules.json.EmpiResourceSearchParamJson;
import ca.uhn.fhir.mdm.api.IMdmSettings;
import ca.uhn.fhir.mdm.log.Logs;
import ca.uhn.fhir.mdm.rules.json.MdmResourceSearchParamJson;
import ca.uhn.fhir.mdm.util.MdmUtil;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
@ -32,43 +33,48 @@ import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class EmpiResourceFilteringSvc {
private static final Logger ourLog = Logs.getEmpiTroubleshootingLog();
public class MdmResourceFilteringSvc {
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
@Autowired
private IEmpiSettings myEmpiSettings;
private IMdmSettings myMdmSettings;
@Autowired
EmpiSearchParamSvc myEmpiSearchParamSvc;
MdmSearchParamSvc myMdmSearchParamSvc;
@Autowired
FhirContext myFhirContext;
/**
* Given a resource from the EMPI Channel, determine whether or not EMPI processing should occur on it.
* Given a resource from the MDM Channel, determine whether or not MDM processing should occur on it.
*
* EMPI processing should occur if for any {@link EmpiResourceSearchParamJson) Search Param, the resource contains a value.
* MDM processing should occur if for any {@link MdmResourceSearchParamJson ) Search Param, the resource contains a value.
*
* If the resource has no attributes that appear in the candidate search params, processing should be skipped, as there is not
* sufficient information to perform meaningful EMPI processing. (For example, how can EMPI processing occur on a patient that has _no_ attributes?)
* sufficient information to perform meaningful MDM processing. (For example, how can MDM processing occur on a patient that has _no_ attributes?)
*
* @param theResource the resource that you wish to check against EMPI rules.
* @param theResource the resource that you wish to check against MDM rules.
*
* @return whether or not EMPI processing should proceed
* @return whether or not MDM processing should proceed
*/
public boolean shouldBeProcessed(IAnyResource theResource) {
//TODO GGG ask KHS: Skip the infinite loop, whoops. Better way to do this? tighter subscription criteria?
if (MdmUtil.isMdmManaged(theResource)) {
ourLog.debug("MDM Message handler is dropping [{}] as it is MDM-managed.");
return false;
}
String resourceType = myFhirContext.getResourceType(theResource);
List<EmpiResourceSearchParamJson> candidateSearchParams = myEmpiSettings.getEmpiRules().getCandidateSearchParams();
List<MdmResourceSearchParamJson> candidateSearchParams = myMdmSettings.getMdmRules().getCandidateSearchParams();
if (candidateSearchParams.isEmpty()) {
return true;
}
boolean containsValueForSomeSearchParam = candidateSearchParams.stream()
.filter(csp -> myEmpiSearchParamSvc.searchParamTypeIsValidForResourceType(csp.getResourceType(), resourceType))
.filter(csp -> myMdmSearchParamSvc.searchParamTypeIsValidForResourceType(csp.getResourceType(), resourceType))
.flatMap(csp -> csp.getSearchParams().stream())
.map(searchParam -> myEmpiSearchParamSvc.getValueFromResourceForSearchParam(theResource, searchParam))
.map(searchParam -> myMdmSearchParamSvc.getValueFromResourceForSearchParam(theResource, searchParam))
.anyMatch(valueList -> !valueList.isEmpty());
ourLog.trace("Is {} suitable for EMPI processing? : {}", theResource.getId(), containsValueForSomeSearchParam);
ourLog.trace("Is {} suitable for MDM processing? : {}", theResource.getId(), containsValueForSomeSearchParam);
return containsValueForSomeSearchParam;
}
}

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.jpa.empi.svc;
package ca.uhn.fhir.jpa.mdm.svc;
/*-
* #%L
@ -41,7 +41,7 @@ import javax.annotation.Nullable;
import java.util.List;
@Service
public class EmpiSearchParamSvc implements ISearchParamRetriever {
public class MdmSearchParamSvc implements ISearchParamRetriever {
@Autowired
FhirContext myFhirContext;
@Autowired

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.jpa.empi.svc;
package ca.uhn.fhir.jpa.mdm.svc;
/*-
* #%L
@ -20,10 +20,10 @@ package ca.uhn.fhir.jpa.empi.svc;
* #L%
*/
import ca.uhn.fhir.empi.api.IEmpiChannelSubmitterSvc;
import ca.uhn.fhir.empi.api.IEmpiSettings;
import ca.uhn.fhir.empi.api.IEmpiSubmitSvc;
import ca.uhn.fhir.empi.log.Logs;
import ca.uhn.fhir.mdm.api.IMdmChannelSubmitterSvc;
import ca.uhn.fhir.mdm.api.IMdmSettings;
import ca.uhn.fhir.mdm.api.IMdmSubmitSvc;
import ca.uhn.fhir.mdm.log.Logs;
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
@ -49,28 +49,28 @@ import java.util.Collections;
import java.util.List;
import java.util.UUID;
public class EmpiSubmitSvcImpl implements IEmpiSubmitSvc {
private static final Logger ourLog = Logs.getEmpiTroubleshootingLog();
public class MdmSubmitSvcImpl implements IMdmSubmitSvc {
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
@Autowired
private DaoRegistry myDaoRegistry;
@Autowired
private EmpiSearchParamSvc myEmpiSearchParamSvc;
private MdmSearchParamSvc myMdmSearchParamSvc;
@Autowired
private IEmpiChannelSubmitterSvc myEmpiChannelSubmitterSvc;
private IMdmChannelSubmitterSvc myMdmChannelSubmitterSvc;
@Autowired
private IEmpiSettings myEmpiSettings;
private IMdmSettings myMdmSettings;
private static final int BUFFER_SIZE = 100;
@Override
@Transactional
public long submitAllTargetTypesToEmpi(@Nullable String theCriteria) {
long submittedCount = myEmpiSettings.getEmpiRules().getMdmTypes().stream()
.mapToLong(targetType -> submitTargetTypeToEmpi(targetType, theCriteria))
public long submitAllTargetTypesToMdm(@Nullable String theCriteria) {
long submittedCount = myMdmSettings.getMdmRules().getMdmTypes().stream()
.mapToLong(targetType -> submitTargetTypeToMdm(targetType, theCriteria))
.sum();
return submittedCount;
@ -78,64 +78,64 @@ public class EmpiSubmitSvcImpl implements IEmpiSubmitSvc {
@Override
@Transactional
public long submitTargetTypeToEmpi(String theTargetType, @Nullable String theCriteria) {
public long submitTargetTypeToMdm(String theTargetType, @Nullable String theCriteria) {
if (theCriteria == null) {
ourLog.info("Submitting all resources of type {} to EMPI", theTargetType);
ourLog.info("Submitting all resources of type {} to MDM", theTargetType);
} else {
ourLog.info("Submitting resources of type {} with criteria {} to EMPI", theTargetType, theCriteria);
ourLog.info("Submitting resources of type {} with criteria {} to MDM", theTargetType, theCriteria);
}
validateTargetType(theTargetType);
SearchParameterMap spMap = myEmpiSearchParamSvc.getSearchParameterMapFromCriteria(theTargetType, theCriteria);
SearchParameterMap spMap = myMdmSearchParamSvc.getSearchParameterMapFromCriteria(theTargetType, theCriteria);
spMap.setLoadSynchronousUpTo(BUFFER_SIZE);
ISearchBuilder searchBuilder = myEmpiSearchParamSvc.generateSearchBuilderForType(theTargetType);
return submitAllMatchingResourcesToEmpiChannel(spMap, searchBuilder);
ISearchBuilder searchBuilder = myMdmSearchParamSvc.generateSearchBuilderForType(theTargetType);
return submitAllMatchingResourcesToMdmChannel(spMap, searchBuilder);
}
private long submitAllMatchingResourcesToEmpiChannel(SearchParameterMap theSpMap, ISearchBuilder theSearchBuilder) {
private long submitAllMatchingResourcesToMdmChannel(SearchParameterMap theSpMap, ISearchBuilder theSearchBuilder) {
SearchRuntimeDetails searchRuntimeDetails = new SearchRuntimeDetails(null, UUID.randomUUID().toString());
long total = 0;
try (IResultIterator query = theSearchBuilder.createQuery(theSpMap, searchRuntimeDetails, null, RequestPartitionId.defaultPartition())) {
Collection<ResourcePersistentId> pidBatch;
do {
pidBatch = query.getNextResultBatch(BUFFER_SIZE);
total += loadPidsAndSubmitToEmpiChannel(theSearchBuilder, pidBatch);
total += loadPidsAndSubmitToMdmChannel(theSearchBuilder, pidBatch);
} while (query.hasNext());
} catch (IOException theE) {
throw new InternalErrorException("Failure while attempting to query resources for " + ProviderConstants.OPERATION_MDM_SUBMIT, theE);
}
ourLog.info("EMPI Submit complete. Submitted a total of {} resources.", total);
ourLog.info("MDM Submit complete. Submitted a total of {} resources.", total);
return total;
}
/**
* Given a collection of ResourcePersistentId objects, and a search builder, load the IBaseResources and submit them to
* the EMPI channel for processing.
* the MDM channel for processing.
*
* @param theSearchBuilder the related DAO search builder.
* @param thePidsToSubmit The collection of PIDs whos resources you want to submit for EMPI processing.
* @param thePidsToSubmit The collection of PIDs whos resources you want to submit for MDM processing.
*
* @return The total count of submitted resources.
*/
private long loadPidsAndSubmitToEmpiChannel(ISearchBuilder theSearchBuilder, Collection<ResourcePersistentId> thePidsToSubmit) {
private long loadPidsAndSubmitToMdmChannel(ISearchBuilder theSearchBuilder, Collection<ResourcePersistentId> thePidsToSubmit) {
List<IBaseResource> resourcesToSubmit = new ArrayList<>();
theSearchBuilder.loadResourcesByPid(thePidsToSubmit, Collections.emptyList(), resourcesToSubmit, false, null);
ourLog.info("Submitting {} resources to EMPI", resourcesToSubmit.size());
ourLog.info("Submitting {} resources to MDM", resourcesToSubmit.size());
resourcesToSubmit
.forEach(resource -> myEmpiChannelSubmitterSvc.submitResourceToEmpiChannel(resource));
.forEach(resource -> myMdmChannelSubmitterSvc.submitResourceToMdmChannel(resource));
return resourcesToSubmit.size();
}
@Override
@Transactional
public long submitPractitionerTypeToMdm(@Nullable String theCriteria) {
return submitTargetTypeToEmpi("Practitioner", theCriteria);
return submitTargetTypeToMdm("Practitioner", theCriteria);
}
@Override
@Transactional
public long submitPatientTypeToMdm(@Nullable String theCriteria) {
return submitTargetTypeToEmpi("Patient", theCriteria);
return submitTargetTypeToMdm("Patient", theCriteria);
}
@Override
@ -144,12 +144,12 @@ public class EmpiSubmitSvcImpl implements IEmpiSubmitSvc {
validateTargetType(theId.getResourceType());
IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(theId.getResourceType());
IBaseResource read = resourceDao.read(theId);
myEmpiChannelSubmitterSvc.submitResourceToEmpiChannel(read);
myMdmChannelSubmitterSvc.submitResourceToMdmChannel(read);
return 1;
}
private void validateTargetType(String theResourceType) {
if(!myEmpiSettings.getEmpiRules().getMdmTypes().contains(theResourceType)) {
if(!myMdmSettings.getMdmRules().getMdmTypes().contains(theResourceType)) {
throw new InvalidRequestException(ProviderConstants.OPERATION_MDM_SUBMIT + " does not support resource type: " + theResourceType);
}
}

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.jpa.empi.svc.candidate;
package ca.uhn.fhir.jpa.mdm.svc.candidate;
/*-
* #%L
@ -21,7 +21,7 @@ package ca.uhn.fhir.jpa.empi.svc.candidate;
*/
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
import ca.uhn.fhir.jpa.empi.dao.EmpiLinkDaoSvc;
import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.springframework.beans.factory.annotation.Autowired;
@ -31,7 +31,7 @@ public abstract class BaseCandidateFinder {
@Autowired
IdHelperService myIdHelperService;
@Autowired
EmpiLinkDaoSvc myEmpiLinkDaoSvc;
MdmLinkDaoSvc myMdmLinkDaoSvc;
CandidateList findCandidates(IAnyResource theTarget) {
CandidateList candidateList = new CandidateList(getStrategy());

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.jpa.empi.svc.candidate;
package ca.uhn.fhir.jpa.mdm.svc.candidate;
/*-
* #%L

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.jpa.empi.svc.candidate;
package ca.uhn.fhir.jpa.mdm.svc.candidate;
/*-
* #%L
@ -21,11 +21,11 @@ package ca.uhn.fhir.jpa.empi.svc.candidate;
*/
public enum CandidateStrategyEnum {
/** Find Person candidates based on matching EID */
/** Find Golden Resource candidates based on matching EID */
EID,
/** Find Person candidates based on a link already existing for the target resource */
/** Find Golden Resource candidates based on a link already existing for the target resource */
LINK,
/** Find Person candidates based on other targets that match the incoming target using the EMPI Matching rules */
/** Find Golden Resource candidates based on other targets that match the incoming target using the MDM Matching rules */
SCORE;
public boolean isEidMatch() {

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.jpa.empi.svc.candidate;
package ca.uhn.fhir.jpa.mdm.svc.candidate;
/*-
* #%L
@ -20,11 +20,11 @@ package ca.uhn.fhir.jpa.empi.svc.candidate;
* #L%
*/
import ca.uhn.fhir.empi.api.EmpiMatchOutcome;
import ca.uhn.fhir.empi.log.Logs;
import ca.uhn.fhir.empi.model.CanonicalEID;
import ca.uhn.fhir.empi.util.EIDHelper;
import ca.uhn.fhir.jpa.empi.svc.EmpiResourceDaoSvc;
import ca.uhn.fhir.mdm.api.MdmMatchOutcome;
import ca.uhn.fhir.mdm.log.Logs;
import ca.uhn.fhir.mdm.model.CanonicalEID;
import ca.uhn.fhir.mdm.util.EIDHelper;
import ca.uhn.fhir.jpa.mdm.svc.MdmResourceDaoSvc;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.slf4j.Logger;
@ -38,12 +38,12 @@ import java.util.Optional;
@Service
public class FindCandidateByEidSvc extends BaseCandidateFinder {
private static final Logger ourLog = Logs.getEmpiTroubleshootingLog();
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
@Autowired
private EIDHelper myEIDHelper;
@Autowired
private EmpiResourceDaoSvc myEmpiResourceDaoSvc;
private MdmResourceDaoSvc myMdmResourceDaoSvc;
protected List<MatchedSourceResourceCandidate> findMatchSourceResourceCandidates(IAnyResource theBaseResource) {
List<MatchedSourceResourceCandidate> retval = new ArrayList<>();
@ -51,11 +51,11 @@ public class FindCandidateByEidSvc extends BaseCandidateFinder {
List<CanonicalEID> eidFromResource = myEIDHelper.getExternalEid(theBaseResource);
if (!eidFromResource.isEmpty()) {
for (CanonicalEID eid : eidFromResource) {
Optional<IAnyResource> oFoundPerson = myEmpiResourceDaoSvc.searchGoldenResourceByEID(eid.getValue(), theBaseResource.getIdElement().getResourceType());
Optional<IAnyResource> oFoundPerson = myMdmResourceDaoSvc.searchGoldenResourceByEID(eid.getValue(), theBaseResource.getIdElement().getResourceType());
if (oFoundPerson.isPresent()) {
IAnyResource foundPerson = oFoundPerson.get();
Long pidOrNull = myIdHelperService.getPidOrNull(foundPerson);
MatchedSourceResourceCandidate mpc = new MatchedSourceResourceCandidate(new ResourcePersistentId(pidOrNull), EmpiMatchOutcome.EID_MATCH);
MatchedSourceResourceCandidate mpc = new MatchedSourceResourceCandidate(new ResourcePersistentId(pidOrNull), MdmMatchOutcome.EID_MATCH);
ourLog.debug("Matched {} by EID {}", foundPerson.getIdElement(), eid);
retval.add(mpc);
}

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.jpa.empi.svc.candidate;
package ca.uhn.fhir.jpa.mdm.svc.candidate;
/*-
* #%L
@ -20,8 +20,8 @@ package ca.uhn.fhir.jpa.empi.svc.candidate;
* #L%
*/
import ca.uhn.fhir.empi.log.Logs;
import ca.uhn.fhir.jpa.entity.EmpiLink;
import ca.uhn.fhir.mdm.log.Logs;
import ca.uhn.fhir.jpa.entity.MdmLink;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.slf4j.Logger;
@ -33,10 +33,10 @@ import java.util.Optional;
@Service
public class FindCandidateByLinkSvc extends BaseCandidateFinder {
private static final Logger ourLog = Logs.getEmpiTroubleshootingLog();
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
/**
* Attempt to find a currently matching Person, based on the presence of an {@link EmpiLink} entity.
* Attempt to find a currently matching Person, based on the presence of an {@link MdmLink} entity.
*
* @param theTarget the {@link IAnyResource} that we want to find candidate Persons for.
* @return an Optional list of {@link MatchedSourceResourceCandidate} indicating matches.
@ -47,9 +47,9 @@ public class FindCandidateByLinkSvc extends BaseCandidateFinder {
Long targetPid = myIdHelperService.getPidOrNull(theTarget);
if (targetPid != null) {
Optional<EmpiLink> oLink = myEmpiLinkDaoSvc.getMatchedLinkForTargetPid(targetPid);
Optional<MdmLink> oLink = myMdmLinkDaoSvc.getMatchedLinkForTargetPid(targetPid);
if (oLink.isPresent()) {
ResourcePersistentId personPid = new ResourcePersistentId(oLink.get().getSourceResourcePid());
ResourcePersistentId personPid = new ResourcePersistentId(oLink.get().getGoldenResourcePid());
ourLog.debug("Resource previously linked. Using existing link.");
retval.add(new MatchedSourceResourceCandidate(personPid, oLink.get()));
}

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.jpa.empi.svc.candidate;
package ca.uhn.fhir.jpa.mdm.svc.candidate;
/*-
* #%L
@ -21,13 +21,13 @@ package ca.uhn.fhir.jpa.empi.svc.candidate;
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
import ca.uhn.fhir.empi.api.IEmpiMatchFinderSvc;
import ca.uhn.fhir.empi.api.MatchedTarget;
import ca.uhn.fhir.empi.log.Logs;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.mdm.api.IMdmMatchFinderSvc;
import ca.uhn.fhir.mdm.api.MatchedTarget;
import ca.uhn.fhir.mdm.log.Logs;
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
import ca.uhn.fhir.jpa.empi.dao.EmpiLinkDaoSvc;
import ca.uhn.fhir.jpa.entity.EmpiLink;
import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc;
import ca.uhn.fhir.jpa.entity.MdmLink;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource;
@ -42,21 +42,21 @@ import java.util.stream.Collectors;
@Service
public class FindCandidateByScoreSvc extends BaseCandidateFinder {
private static final Logger ourLog = Logs.getEmpiTroubleshootingLog();
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
@Autowired
private FhirContext myFhirContext;
@Autowired
IdHelperService myIdHelperService;
@Autowired
private EmpiLinkDaoSvc myEmpiLinkDaoSvc;
private MdmLinkDaoSvc myMdmLinkDaoSvc;
@Autowired
private IEmpiMatchFinderSvc myEmpiMatchFinderSvc;
private IMdmMatchFinderSvc myMdmMatchFinderSvc;
/**
* Attempt to find matching Persons by resolving them from similar Matching target resources, where target resource
* can be either Patient or Practitioner. Runs EMPI logic over the existing Patient/Practitioners, then finds their
* entries in the EmpiLink table, and returns all the matches found therein.
* can be either Patient or Practitioner. Runs MDM logic over the existing Patient/Practitioners, then finds their
* entries in the MdmLink table, and returns all the matches found therein.
*
* @param theTarget the {@link IBaseResource} which we want to find candidate Persons for.
* @return an Optional list of {@link MatchedSourceResourceCandidate} indicating matches.
@ -67,27 +67,26 @@ public class FindCandidateByScoreSvc extends BaseCandidateFinder {
List<Long> personPidsToExclude = getNoMatchPersonPids(theTarget);
List<MatchedTarget> matchedCandidates = myMdmMatchFinderSvc.getMatchedTargets(myFhirContext.getResourceType(theTarget), theTarget);
List<MatchedTarget> matchedCandidates = myEmpiMatchFinderSvc.getMatchedTargets(myFhirContext.getResourceType(theTarget), theTarget);
//Convert all possible match targets to their equivalent Persons by looking up in the EmpiLink table,
//Convert all possible match targets to their equivalent Persons by looking up in the MdmLink table,
//while ensuring that the matches aren't in our NO_MATCH list.
// The data flow is as follows ->
// MatchedTargetCandidate -> Person -> EmpiLink -> MatchedPersonCandidate
// MatchedTargetCandidate -> Person -> MdmLink -> MatchedPersonCandidate
matchedCandidates = matchedCandidates.stream().filter(mc -> mc.isMatch() || mc.isPossibleMatch()).collect(Collectors.toList());
for (MatchedTarget match : matchedCandidates) {
Optional<EmpiLink> optMatchEmpiLink = myEmpiLinkDaoSvc.getMatchedLinkForTargetPid(myIdHelperService.getPidOrNull(match.getTarget()));
if (!optMatchEmpiLink.isPresent()) {
Optional<MdmLink> optionalMdmLink = myMdmLinkDaoSvc.getMatchedLinkForTargetPid(myIdHelperService.getPidOrNull(match.getTarget()));
if (!optionalMdmLink.isPresent()) {
continue;
}
EmpiLink matchEmpiLink = optMatchEmpiLink.get();
if (personPidsToExclude.contains(matchEmpiLink.getSourceResourcePid())) {
ourLog.info("Skipping EMPI on candidate person with PID {} due to manual NO_MATCH", matchEmpiLink.getSourceResourcePid());
MdmLink matchMdmLink = optionalMdmLink.get();
if (personPidsToExclude.contains(matchMdmLink.getGoldenResourcePid())) {
ourLog.info("Skipping MDM on candidate person with PID {} due to manual NO_MATCH", matchMdmLink.getGoldenResourcePid());
continue;
}
MatchedSourceResourceCandidate candidate = new MatchedSourceResourceCandidate(getResourcePersistentId(matchEmpiLink.getSourceResourcePid()), match.getMatchResult());
MatchedSourceResourceCandidate candidate = new MatchedSourceResourceCandidate(getResourcePersistentId(matchMdmLink.getGoldenResourcePid()), match.getMatchResult());
retval.add(candidate);
}
return retval;
@ -95,9 +94,9 @@ public class FindCandidateByScoreSvc extends BaseCandidateFinder {
private List<Long> getNoMatchPersonPids(IBaseResource theBaseResource) {
Long targetPid = myIdHelperService.getPidOrNull(theBaseResource);
return myEmpiLinkDaoSvc.getEmpiLinksByTargetPidAndMatchResult(targetPid, EmpiMatchResultEnum.NO_MATCH)
return myMdmLinkDaoSvc.getMdmLinksByTargetPidAndMatchResult(targetPid, MdmMatchResultEnum.NO_MATCH)
.stream()
.map(EmpiLink::getSourceResourcePid)
.map(MdmLink::getGoldenResourcePid)
.collect(Collectors.toList());
}

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.jpa.empi.svc.candidate;
package ca.uhn.fhir.jpa.mdm.svc.candidate;
/*-
* #%L
@ -20,34 +20,34 @@ package ca.uhn.fhir.jpa.empi.svc.candidate;
* #L%
*/
import ca.uhn.fhir.empi.api.EmpiMatchOutcome;
import ca.uhn.fhir.jpa.entity.EmpiLink;
import ca.uhn.fhir.mdm.api.MdmMatchOutcome;
import ca.uhn.fhir.jpa.entity.MdmLink;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
public class MatchedSourceResourceCandidate {
private final ResourcePersistentId myCandidateSourceResourcePid;
private final EmpiMatchOutcome myEmpiMatchOutcome;
private final MdmMatchOutcome myMdmMatchOutcome;
public MatchedSourceResourceCandidate(ResourcePersistentId theCandidate, EmpiMatchOutcome theEmpiMatchOutcome) {
public MatchedSourceResourceCandidate(ResourcePersistentId theCandidate, MdmMatchOutcome theMdmMatchOutcome) {
myCandidateSourceResourcePid = theCandidate;
myEmpiMatchOutcome = theEmpiMatchOutcome;
myMdmMatchOutcome = theMdmMatchOutcome;
}
public MatchedSourceResourceCandidate(ResourcePersistentId thePersonPid, EmpiLink theEmpiLink) {
public MatchedSourceResourceCandidate(ResourcePersistentId thePersonPid, MdmLink theMdmLink) {
myCandidateSourceResourcePid = thePersonPid;
myEmpiMatchOutcome = new EmpiMatchOutcome(theEmpiLink.getVector(), theEmpiLink.getScore()).setMatchResultEnum(theEmpiLink.getMatchResult());
myMdmMatchOutcome = new MdmMatchOutcome(theMdmLink.getVector(), theMdmLink.getScore()).setMatchResultEnum(theMdmLink.getMatchResult());
}
public ResourcePersistentId getCandidatePersonPid() {
return myCandidateSourceResourcePid;
}
public EmpiMatchOutcome getMatchResult() {
return myEmpiMatchOutcome;
public MdmMatchOutcome getMatchResult() {
return myMdmMatchOutcome;
}
public boolean isMatch() {
return myEmpiMatchOutcome.isMatch();
return myMdmMatchOutcome.isMatch();
}
}

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.jpa.empi.svc.candidate;
package ca.uhn.fhir.jpa.mdm.svc.candidate;
/*-
* #%L
@ -20,8 +20,8 @@ package ca.uhn.fhir.jpa.empi.svc.candidate;
* #L%
*/
import ca.uhn.fhir.empi.rules.json.EmpiResourceSearchParamJson;
import ca.uhn.fhir.jpa.empi.svc.EmpiSearchParamSvc;
import ca.uhn.fhir.mdm.rules.json.MdmResourceSearchParamJson;
import ca.uhn.fhir.jpa.mdm.svc.MdmSearchParamSvc;
import ca.uhn.fhir.util.UrlUtil;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.springframework.beans.factory.annotation.Autowired;
@ -35,9 +35,9 @@ import java.util.Optional;
import java.util.stream.Collectors;
@Service
public class EmpiCandidateSearchCriteriaBuilderSvc {
public class MdmCandidateSearchCriteriaBuilderSvc {
@Autowired
private EmpiSearchParamSvc myEmpiSearchParamSvc;
private MdmSearchParamSvc myMdmSearchParamSvc;
/*
* Given a list of criteria upon which to block, a resource search parameter, and a list of values for that given search parameter,
@ -46,14 +46,14 @@ public class EmpiCandidateSearchCriteriaBuilderSvc {
* Patient?active=true&name.given=Gary,Grant&name.family=Graham
*/
@Nonnull
public Optional<String> buildResourceQueryString(String theResourceType, IAnyResource theResource, List<String> theFilterCriteria, @Nullable EmpiResourceSearchParamJson resourceSearchParam) {
public Optional<String> buildResourceQueryString(String theResourceType, IAnyResource theResource, List<String> theFilterCriteria, @Nullable MdmResourceSearchParamJson resourceSearchParam) {
List<String> criteria = new ArrayList<>();
// If there are candidate search params, then make use of them, otherwise, search with only the filters.
if (resourceSearchParam != null) {
resourceSearchParam.iterator().forEachRemaining(searchParam -> {
//to compare it to all known PERSON objects, using the overlapping search parameters that they have.
List<String> valuesFromResourceForSearchParam = myEmpiSearchParamSvc.getValueFromResourceForSearchParam(theResource, searchParam);
List<String> valuesFromResourceForSearchParam = myMdmSearchParamSvc.getValueFromResourceForSearchParam(theResource, searchParam);
if (!valuesFromResourceForSearchParam.isEmpty()) {
criteria.add(buildResourceMatchQuery(searchParam, valuesFromResourceForSearchParam));
}

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.jpa.empi.svc.candidate;
package ca.uhn.fhir.jpa.mdm.svc.candidate;
/*-
* #%L
@ -20,14 +20,14 @@ package ca.uhn.fhir.jpa.empi.svc.candidate;
* #L%
*/
import ca.uhn.fhir.empi.api.IEmpiSettings;
import ca.uhn.fhir.empi.log.Logs;
import ca.uhn.fhir.empi.rules.json.EmpiFilterSearchParamJson;
import ca.uhn.fhir.empi.rules.json.EmpiResourceSearchParamJson;
import ca.uhn.fhir.mdm.api.IMdmSettings;
import ca.uhn.fhir.mdm.log.Logs;
import ca.uhn.fhir.mdm.rules.json.MdmFilterSearchParamJson;
import ca.uhn.fhir.mdm.rules.json.MdmResourceSearchParamJson;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
import ca.uhn.fhir.jpa.empi.svc.EmpiSearchParamSvc;
import ca.uhn.fhir.jpa.mdm.svc.MdmSearchParamSvc;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import org.hl7.fhir.instance.model.api.IAnyResource;
@ -44,24 +44,24 @@ import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import static ca.uhn.fhir.empi.api.EmpiConstants.ALL_RESOURCE_SEARCH_PARAM_TYPE;
import static ca.uhn.fhir.mdm.api.MdmConstants.ALL_RESOURCE_SEARCH_PARAM_TYPE;
@Service
public class EmpiCandidateSearchSvc {
private static final Logger ourLog = Logs.getEmpiTroubleshootingLog();
public class MdmCandidateSearchSvc {
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
@Autowired
private IEmpiSettings myEmpiConfig;
private IMdmSettings myMdmSettings;
@Autowired
private EmpiSearchParamSvc myEmpiSearchParamSvc;
private MdmSearchParamSvc myMdmSearchParamSvc;
@Autowired
private DaoRegistry myDaoRegistry;
@Autowired
private IdHelperService myIdHelperService;
@Autowired
private EmpiCandidateSearchCriteriaBuilderSvc myEmpiCandidateSearchCriteriaBuilderSvc;
private MdmCandidateSearchCriteriaBuilderSvc myMdmCandidateSearchCriteriaBuilderSvc;
public EmpiCandidateSearchSvc() {
public MdmCandidateSearchSvc() {
}
/**
@ -75,16 +75,16 @@ public class EmpiCandidateSearchSvc {
*/
public Collection<IAnyResource> findCandidates(String theResourceType, IAnyResource theResource) {
Map<Long, IAnyResource> matchedPidsToResources = new HashMap<>();
List<EmpiFilterSearchParamJson> filterSearchParams = myEmpiConfig.getEmpiRules().getCandidateFilterSearchParams();
List<MdmFilterSearchParamJson> filterSearchParams = myMdmSettings.getMdmRules().getCandidateFilterSearchParams();
List<String> filterCriteria = buildFilterQuery(filterSearchParams, theResourceType);
List<EmpiResourceSearchParamJson> candidateSearchParams = myEmpiConfig.getEmpiRules().getCandidateSearchParams();
List<MdmResourceSearchParamJson> candidateSearchParams = myMdmSettings.getMdmRules().getCandidateSearchParams();
//If there are zero EmpiResourceSearchParamJson, we end up only making a single search, otherwise we
//must perform one search per EmpiResourceSearchParamJson.
if (candidateSearchParams.isEmpty()) {
searchForIdsAndAddToMap(theResourceType, theResource, matchedPidsToResources, filterCriteria, null);
} else {
for (EmpiResourceSearchParamJson resourceSearchParam : candidateSearchParams) {
for (MdmResourceSearchParamJson resourceSearchParam : candidateSearchParams) {
if (!isSearchParamForResource(theResourceType, resourceSearchParam)) {
continue;
@ -102,7 +102,7 @@ public class EmpiCandidateSearchSvc {
return matchedPidsToResources.values();
}
private boolean isSearchParamForResource(String theResourceType, EmpiResourceSearchParamJson resourceSearchParam) {
private boolean isSearchParamForResource(String theResourceType, MdmResourceSearchParamJson resourceSearchParam) {
String resourceType = resourceSearchParam.getResourceType();
return resourceType.equals(theResourceType) || resourceType.equalsIgnoreCase(ALL_RESOURCE_SEARCH_PARAM_TYPE);
}
@ -115,9 +115,9 @@ public class EmpiCandidateSearchSvc {
* 4. Store all results in `theMatchedPidsToResources`
*/
@SuppressWarnings("rawtypes")
private void searchForIdsAndAddToMap(String theResourceType, IAnyResource theResource, Map<Long, IAnyResource> theMatchedPidsToResources, List<String> theFilterCriteria, EmpiResourceSearchParamJson resourceSearchParam) {
private void searchForIdsAndAddToMap(String theResourceType, IAnyResource theResource, Map<Long, IAnyResource> theMatchedPidsToResources, List<String> theFilterCriteria, MdmResourceSearchParamJson resourceSearchParam) {
//1.
Optional<String> oResourceCriteria = myEmpiCandidateSearchCriteriaBuilderSvc.buildResourceQueryString(theResourceType, theResource, theFilterCriteria, resourceSearchParam);
Optional<String> oResourceCriteria = myMdmCandidateSearchCriteriaBuilderSvc.buildResourceQueryString(theResourceType, theResource, theFilterCriteria, resourceSearchParam);
if (!oResourceCriteria.isPresent()) {
return;
}
@ -125,7 +125,7 @@ public class EmpiCandidateSearchSvc {
ourLog.debug("Searching for {} candidates with {}", theResourceType, resourceCriteria);
//2.
SearchParameterMap searchParameterMap = myEmpiSearchParamSvc.mapFromCriteria(theResourceType, resourceCriteria);
SearchParameterMap searchParameterMap = myMdmSearchParamSvc.mapFromCriteria(theResourceType, resourceCriteria);
searchParameterMap.setLoadSynchronous(true);
@ -147,18 +147,18 @@ public class EmpiCandidateSearchSvc {
}
}
private List<String> buildFilterQuery(List<EmpiFilterSearchParamJson> theFilterSearchParams, String theResourceType) {
private List<String> buildFilterQuery(List<MdmFilterSearchParamJson> theFilterSearchParams, String theResourceType) {
return Collections.unmodifiableList(theFilterSearchParams.stream()
.filter(spFilterJson -> paramIsOnCorrectType(theResourceType, spFilterJson))
.map(this::convertToQueryString)
.collect(Collectors.toList()));
}
private boolean paramIsOnCorrectType(String theResourceType, EmpiFilterSearchParamJson spFilterJson) {
private boolean paramIsOnCorrectType(String theResourceType, MdmFilterSearchParamJson spFilterJson) {
return spFilterJson.getResourceType().equals(theResourceType) || spFilterJson.getResourceType().equalsIgnoreCase(ALL_RESOURCE_SEARCH_PARAM_TYPE);
}
private String convertToQueryString(EmpiFilterSearchParamJson theSpFilterJson) {
private String convertToQueryString(MdmFilterSearchParamJson theSpFilterJson) {
String qualifier = theSpFilterJson.getTokenParamModifierAsString();
return theSpFilterJson.getSearchParam() + qualifier + "=" + theSpFilterJson.getFixedValue();
}

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.jpa.empi.svc.candidate;
package ca.uhn.fhir.jpa.mdm.svc.candidate;
/*-
* #%L
@ -20,8 +20,8 @@ package ca.uhn.fhir.jpa.empi.svc.candidate;
* #L%
*/
import ca.uhn.fhir.empi.log.Logs;
import ca.uhn.fhir.jpa.empi.svc.EmpiResourceDaoSvc;
import ca.uhn.fhir.mdm.log.Logs;
import ca.uhn.fhir.jpa.mdm.svc.MdmResourceDaoSvc;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource;
@ -30,11 +30,11 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class EmpiSourceResourceFindingSvc {
private static final Logger ourLog = Logs.getEmpiTroubleshootingLog();
public class MdmGoldenResourceFindingSvc {
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
@Autowired
private EmpiResourceDaoSvc myEmpiResourceDaoSvc;
private MdmResourceDaoSvc myMdmResourceDaoSvc;
@Autowired
private FindCandidateByEidSvc myFindCandidateByEidSvc;
@ -78,6 +78,6 @@ public class EmpiSourceResourceFindingSvc {
public IAnyResource getSourceResourceFromMatchedSourceResourceCandidate(MatchedSourceResourceCandidate theMatchedSourceResourceCandidate, String theResourceType) {
ResourcePersistentId personPid = theMatchedSourceResourceCandidate.getCandidatePersonPid();
return myEmpiResourceDaoSvc.readSourceResourceByPid(personPid, theResourceType);
return myMdmResourceDaoSvc.readSourceResourceByPid(personPid, theResourceType);
}
}

View File

@ -1,55 +0,0 @@
package ca.uhn.fhir.jpa.empi.dao;
import ca.uhn.fhir.empi.api.EmpiLinkSourceEnum;
import ca.uhn.fhir.empi.api.IEmpiSettings;
import ca.uhn.fhir.empi.rules.json.EmpiRulesJson;
import ca.uhn.fhir.jpa.empi.BaseEmpiR4Test;
import ca.uhn.fhir.jpa.entity.EmpiLink;
import ca.uhn.fhir.jpa.util.TestUtil;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class EmpiLinkDaoSvcTest extends BaseEmpiR4Test {
@Autowired
EmpiLinkDaoSvc myEmpiLinkDaoSvc;
@Autowired
IEmpiSettings myEmpiSettings;
@Test
public void testCreate() {
EmpiLink empiLink = createResourcesAndBuildTestEmpiLink();
assertThat(empiLink.getCreated(), is(nullValue()));
assertThat(empiLink.getUpdated(), is(nullValue()));
myEmpiLinkDaoSvc.save(empiLink);
assertThat(empiLink.getCreated(), is(notNullValue()));
assertThat(empiLink.getUpdated(), is(notNullValue()));
assertTrue(empiLink.getUpdated().getTime() - empiLink.getCreated().getTime() < 1000);
}
@Test
public void testUpdate() {
EmpiLink createdLink = myEmpiLinkDaoSvc.save(createResourcesAndBuildTestEmpiLink());
assertThat(createdLink.getLinkSource(), is(EmpiLinkSourceEnum.MANUAL));
TestUtil.sleepOneClick();
createdLink.setLinkSource(EmpiLinkSourceEnum.AUTO);
EmpiLink updatedLink = myEmpiLinkDaoSvc.save(createdLink);
assertNotEquals(updatedLink.getCreated(), updatedLink.getUpdated());
}
@Test
public void testNew() {
EmpiLink newLink = myEmpiLinkDaoSvc.newEmpiLink();
EmpiRulesJson rules = myEmpiSettings.getEmpiRules();
assertEquals("1", rules.getVersion());
assertEquals(rules.getVersion(), newLink.getVersion());
}
}

View File

@ -1,20 +0,0 @@
package ca.uhn.fhir.jpa.empi.entity;
import ca.uhn.fhir.empi.api.EmpiLinkSourceEnum;
import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class EmpiEnumTest {
@Test
public void empiEnumOrdinals() {
// This test is here to enforce that new values in these enums are always added to the end
assertEquals(6, EmpiMatchResultEnum.values().length);
assertEquals(EmpiMatchResultEnum.REDIRECT, EmpiMatchResultEnum.values()[EmpiMatchResultEnum.values().length - 1]);
assertEquals(2, EmpiLinkSourceEnum.values().length);
assertEquals(EmpiLinkSourceEnum.MANUAL, EmpiLinkSourceEnum.values()[EmpiLinkSourceEnum.values().length - 1]);
}
}

View File

@ -1,31 +0,0 @@
package ca.uhn.fhir.jpa.empi.helper;
import ca.uhn.fhir.jpa.dao.data.IEmpiLinkDao;
import ca.uhn.fhir.jpa.entity.EmpiLink;
import ca.uhn.fhir.model.primitive.IdDt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class EmpiLinkHelper {
private static final Logger ourLog = LoggerFactory.getLogger(EmpiLinkHelper.class);
@Autowired
IEmpiLinkDao myEmpiLinkDao;
@Transactional
public void logEmpiLinks() {
List<EmpiLink> links = myEmpiLinkDao.findAll();
ourLog.info("All EMPI Links:");
for (EmpiLink link : links) {
IdDt personId = link.getSourceResource().getIdDt().toVersionless();
IdDt targetId = link.getTarget().getIdDt().toVersionless();
ourLog.info("{}: {}, {}, {}, {}", link.getId(), personId, targetId, link.getMatchResult(), link.getLinkSource());
}
}
}

View File

@ -1,53 +0,0 @@
package ca.uhn.fhir.jpa.empi.provider;
import ca.uhn.fhir.empi.api.IEmpiControllerSvc;
import ca.uhn.fhir.empi.api.IEmpiExpungeSvc;
import ca.uhn.fhir.empi.api.IEmpiMatchFinderSvc;
import ca.uhn.fhir.empi.api.IEmpiSubmitSvc;
import ca.uhn.fhir.empi.provider.EmpiProviderR4;
import ca.uhn.fhir.empi.rules.config.EmpiSettings;
import ca.uhn.fhir.jpa.empi.BaseEmpiR4Test;
import com.google.common.base.Charsets;
import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import java.io.IOException;
public abstract class BaseProviderR4Test extends BaseEmpiR4Test {
EmpiProviderR4 myEmpiProviderR4;
@Autowired
private IEmpiMatchFinderSvc myEmpiMatchFinderSvc;
@Autowired
private IEmpiControllerSvc myEmpiControllerSvc;
@Autowired
private IEmpiExpungeSvc myEmpiResetSvc;
@Autowired
private IEmpiSubmitSvc myEmpiBatchSvc;
@Autowired
private EmpiSettings myEmpiSettings;
private String defaultScript;
protected void setEmpiRuleJson(String theString) throws IOException {
DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
Resource resource = resourceLoader.getResource(theString);
String json = IOUtils.toString(resource.getInputStream(), Charsets.UTF_8);
myEmpiSettings.setScriptText(json);
}
@BeforeEach
public void before() {
myEmpiProviderR4 = new EmpiProviderR4(myFhirContext, myEmpiControllerSvc, myEmpiMatchFinderSvc, myEmpiResetSvc, myEmpiBatchSvc);
defaultScript = myEmpiSettings.getScriptText();
}
@AfterEach
public void after() throws IOException {
super.after();
myEmpiSettings.setScriptText(defaultScript);
}
}

View File

@ -1,33 +1,33 @@
package ca.uhn.fhir.jpa.empi;
package ca.uhn.fhir.jpa.mdm;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.empi.api.EmpiConstants;
import ca.uhn.fhir.empi.api.EmpiLinkSourceEnum;
import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
import ca.uhn.fhir.empi.api.IEmpiSettings;
import ca.uhn.fhir.empi.model.MdmTransactionContext;
import ca.uhn.fhir.empi.rules.svc.EmpiResourceMatcherSvc;
import ca.uhn.fhir.empi.util.EIDHelper;
import ca.uhn.fhir.empi.util.EmpiUtil;
import ca.uhn.fhir.mdm.api.MdmConstants;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.mdm.api.IMdmSettings;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.mdm.rules.svc.MdmResourceMatcherSvc;
import ca.uhn.fhir.mdm.util.EIDHelper;
import ca.uhn.fhir.mdm.util.MdmUtil;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome;
import ca.uhn.fhir.jpa.dao.data.IEmpiLinkDao;
import ca.uhn.fhir.jpa.dao.data.IMdmLinkDao;
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
import ca.uhn.fhir.jpa.empi.config.EmpiConsumerConfig;
import ca.uhn.fhir.jpa.empi.config.EmpiSearchParameterLoader;
import ca.uhn.fhir.jpa.empi.config.EmpiSubmitterConfig;
import ca.uhn.fhir.jpa.empi.config.TestEmpiConfigR4;
import ca.uhn.fhir.jpa.empi.dao.EmpiLinkDaoSvc;
import ca.uhn.fhir.jpa.empi.matcher.IsLinkedTo;
import ca.uhn.fhir.jpa.empi.matcher.IsMatchedToAPerson;
import ca.uhn.fhir.jpa.empi.matcher.IsPossibleDuplicateOf;
import ca.uhn.fhir.jpa.empi.matcher.IsPossibleLinkedTo;
import ca.uhn.fhir.jpa.empi.matcher.IsPossibleMatchWith;
import ca.uhn.fhir.jpa.empi.matcher.IsSameSourceResourceAs;
import ca.uhn.fhir.jpa.empi.svc.EmpiMatchLinkSvc;
import ca.uhn.fhir.jpa.entity.EmpiLink;
import ca.uhn.fhir.jpa.mdm.config.MdmConsumerConfig;
import ca.uhn.fhir.jpa.mdm.config.MdmSearchParameterLoader;
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.matcher.IsLinkedTo;
import ca.uhn.fhir.jpa.mdm.matcher.IsMatchedToAPerson;
import ca.uhn.fhir.jpa.mdm.matcher.IsPossibleDuplicateOf;
import ca.uhn.fhir.jpa.mdm.matcher.IsPossibleLinkedTo;
import ca.uhn.fhir.jpa.mdm.matcher.IsPossibleMatchWith;
import ca.uhn.fhir.jpa.mdm.matcher.IsSameSourceResourceAs;
import ca.uhn.fhir.jpa.mdm.svc.MdmMatchLinkSvc;
import ca.uhn.fhir.jpa.entity.MdmLink;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.searchparam.registry.SearchParamRegistryImpl;
import ca.uhn.fhir.jpa.subscription.match.config.SubscriptionProcessorConfig;
@ -70,9 +70,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.slf4j.LoggerFactory.getLogger;
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = {EmpiSubmitterConfig.class, EmpiConsumerConfig.class, TestEmpiConfigR4.class, SubscriptionProcessorConfig.class})
abstract public class BaseEmpiR4Test extends BaseJpaR4Test {
private static final Logger ourLog = getLogger(BaseEmpiR4Test.class);
@ContextConfiguration(classes = {MdmSubmitterConfig.class, MdmConsumerConfig.class, TestMdmConfigR4.class, SubscriptionProcessorConfig.class})
abstract public class BaseMdmR4Test extends BaseJpaR4Test {
private static final Logger ourLog = getLogger(BaseMdmR4Test.class);
public static final String NAME_GIVEN_JANE = "Jane";
public static final String NAME_GIVEN_PAUL = "Paul";
@ -98,21 +98,21 @@ abstract public class BaseEmpiR4Test extends BaseJpaR4Test {
@Autowired
protected IFhirResourceDao<Practitioner> myPractitionerDao;
@Autowired
protected EmpiResourceMatcherSvc myEmpiResourceMatcherSvc;
protected MdmResourceMatcherSvc myMdmResourceMatcherSvc;
@Autowired
protected IEmpiLinkDao myEmpiLinkDao;
protected IMdmLinkDao myMdmLinkDao;
@Autowired
protected EmpiLinkDaoSvc myEmpiLinkDaoSvc;
protected MdmLinkDaoSvc myMdmLinkDaoSvc;
@Autowired
protected IdHelperService myIdHelperService;
@Autowired
protected IEmpiSettings myEmpiConfig;
protected IMdmSettings myMdmSettings;
@Autowired
protected EmpiMatchLinkSvc myEmpiMatchLinkSvc;
protected MdmMatchLinkSvc myMdmMatchLinkSvc;
@Autowired
protected EIDHelper myEIDHelper;
@Autowired
EmpiSearchParameterLoader myEmpiSearchParameterLoader;
MdmSearchParameterLoader myMdmSearchParameterLoader;
@Autowired
SearchParamRegistryImpl mySearchParamRegistry;
@Autowired
@ -131,13 +131,13 @@ abstract public class BaseEmpiR4Test extends BaseJpaR4Test {
@Override
@AfterEach
public void after() throws IOException {
myEmpiLinkDao.deleteAll();
assertEquals(0, myEmpiLinkDao.count());
myMdmLinkDao.deleteAll();
assertEquals(0, myMdmLinkDao.count());
super.after();
}
protected void saveLink(EmpiLink theEmpiLink) {
myEmpiLinkDaoSvc.save(theEmpiLink);
protected void saveLink(MdmLink theMdmLink) {
myMdmLinkDaoSvc.save(theMdmLink);
}
//
// @Nonnull
@ -167,13 +167,13 @@ abstract public class BaseEmpiR4Test extends BaseJpaR4Test {
}
@Nonnull
protected Patient createPatient(Patient thePatient, boolean theEmpiManaged, boolean isRedirect) {
if (theEmpiManaged) {
EmpiUtil.setEmpiManaged(thePatient);
protected Patient createPatient(Patient thePatient, boolean theMdmManaged, boolean isRedirect) {
if (theMdmManaged) {
MdmUtil.setEmpiManaged(thePatient);
if (isRedirect) {
EmpiUtil.setGoldenResourceRedirected(thePatient);
MdmUtil.setGoldenResourceRedirected(thePatient);
} else {
EmpiUtil.setGoldenResource(thePatient);
MdmUtil.setGoldenResource(thePatient);
}
}
@ -313,40 +313,40 @@ abstract public class BaseEmpiR4Test extends BaseJpaR4Test {
}
protected void assertLinkCount(long theExpectedCount) {
assertEquals(theExpectedCount, myEmpiLinkDao.count());
assertEquals(theExpectedCount, myMdmLinkDao.count());
}
protected IAnyResource getGoldenResourceFromTargetResource(IAnyResource theBaseResource) {
String resourceType = theBaseResource.getIdElement().getResourceType();
IFhirResourceDao relevantDao = myDaoRegistry.getResourceDao(resourceType);
Optional<EmpiLink> matchedLinkForTargetPid = myEmpiLinkDaoSvc.getMatchedLinkForTargetPid(myIdHelperService.getPidOrNull(theBaseResource));
Optional<MdmLink> matchedLinkForTargetPid = myMdmLinkDaoSvc.getMatchedLinkForTargetPid(myIdHelperService.getPidOrNull(theBaseResource));
if (matchedLinkForTargetPid.isPresent()) {
Long sourceResourcePid = matchedLinkForTargetPid.get().getSourceResourcePid();
Long sourceResourcePid = matchedLinkForTargetPid.get().getGoldenResourcePid();
return (IAnyResource) relevantDao.readByPid(new ResourcePersistentId(sourceResourcePid));
} else {
return null;
}
}
protected <T extends IBaseResource> T getTargetResourceFromEmpiLink(EmpiLink theEmpiLink, String theResourceType) {
protected <T extends IBaseResource> T getTargetResourceFromEmpiLink(MdmLink theMdmLink, String theResourceType) {
IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(theResourceType);
return (T) resourceDao.readByPid(new ResourcePersistentId(theEmpiLink.getSourceResourcePid()));
return (T) resourceDao.readByPid(new ResourcePersistentId(theMdmLink.getGoldenResourcePid()));
}
protected Patient addExternalEID(Patient thePatient, String theEID) {
thePatient.addIdentifier().setSystem(myEmpiConfig.getEmpiRules().getEnterpriseEIDSystem()).setValue(theEID);
thePatient.addIdentifier().setSystem(myMdmSettings.getMdmRules().getEnterpriseEIDSystem()).setValue(theEID);
return thePatient;
}
protected Patient clearExternalEIDs(Patient thePatient) {
thePatient.getIdentifier().removeIf(theIdentifier -> theIdentifier.getSystem().equalsIgnoreCase(myEmpiConfig.getEmpiRules().getEnterpriseEIDSystem()));
thePatient.getIdentifier().removeIf(theIdentifier -> theIdentifier.getSystem().equalsIgnoreCase(myMdmSettings.getMdmRules().getEnterpriseEIDSystem()));
return thePatient;
}
protected Patient createPatientAndUpdateLinks(Patient thePatient) {
thePatient = createPatient(thePatient);
myEmpiMatchLinkSvc.updateEmpiLinksForEmpiTarget(thePatient, createContextForCreate("Patient"));
myMdmMatchLinkSvc.updateMdmLinksForMdmTarget(thePatient, createContextForCreate("Patient"));
return thePatient;
}
@ -365,7 +365,7 @@ abstract public class BaseEmpiR4Test extends BaseJpaR4Test {
protected Medication createMedicationAndUpdateLinks(Medication theMedication) {
theMedication = createMedication(theMedication);
myEmpiMatchLinkSvc.updateEmpiLinksForEmpiTarget(theMedication, createContextForCreate("Medication"));
myMdmMatchLinkSvc.updateMdmLinksForMdmTarget(theMedication, createContextForCreate("Medication"));
return theMedication;
}
@ -387,7 +387,7 @@ abstract public class BaseEmpiR4Test extends BaseJpaR4Test {
protected Patient updatePatientAndUpdateLinks(Patient thePatient) {
thePatient = (Patient) myPatientDao.update(thePatient).getResource();
myEmpiMatchLinkSvc.updateEmpiLinksForEmpiTarget(thePatient, createContextForUpdate(thePatient.getIdElement().getResourceType()));
myMdmMatchLinkSvc.updateMdmLinksForMdmTarget(thePatient, createContextForUpdate(thePatient.getIdElement().getResourceType()));
return thePatient;
}
@ -395,32 +395,32 @@ abstract public class BaseEmpiR4Test extends BaseJpaR4Test {
thePractitioner.setActive(true);
DaoMethodOutcome daoMethodOutcome = myPractitionerDao.create(thePractitioner);
thePractitioner.setId(daoMethodOutcome.getId());
myEmpiMatchLinkSvc.updateEmpiLinksForEmpiTarget(thePractitioner, createContextForCreate("Practitioner"));
myMdmMatchLinkSvc.updateMdmLinksForMdmTarget(thePractitioner, createContextForCreate("Practitioner"));
return thePractitioner;
}
protected Matcher<IAnyResource> sameSourceResourceAs(IAnyResource... theBaseResource) {
return IsSameSourceResourceAs.sameSourceResourceAs(myIdHelperService, myEmpiLinkDaoSvc, theBaseResource);
return IsSameSourceResourceAs.sameSourceResourceAs(myIdHelperService, myMdmLinkDaoSvc, theBaseResource);
}
protected Matcher<IAnyResource> linkedTo(IAnyResource... theBaseResource) {
return IsLinkedTo.linkedTo(myIdHelperService, myEmpiLinkDaoSvc, theBaseResource);
return IsLinkedTo.linkedTo(myIdHelperService, myMdmLinkDaoSvc, theBaseResource);
}
protected Matcher<IAnyResource> possibleLinkedTo(IAnyResource... theBaseResource) {
return IsPossibleLinkedTo.possibleLinkedTo(myIdHelperService, myEmpiLinkDaoSvc, theBaseResource);
return IsPossibleLinkedTo.possibleLinkedTo(myIdHelperService, myMdmLinkDaoSvc, theBaseResource);
}
protected Matcher<IAnyResource> possibleMatchWith(IAnyResource... theBaseResource) {
return IsPossibleMatchWith.possibleMatchWith(myIdHelperService, myEmpiLinkDaoSvc, theBaseResource);
return IsPossibleMatchWith.possibleMatchWith(myIdHelperService, myMdmLinkDaoSvc, theBaseResource);
}
protected Matcher<IAnyResource> possibleDuplicateOf(IAnyResource... theBaseResource) {
return IsPossibleDuplicateOf.possibleDuplicateOf(myIdHelperService, myEmpiLinkDaoSvc, theBaseResource);
return IsPossibleDuplicateOf.possibleDuplicateOf(myIdHelperService, myMdmLinkDaoSvc, theBaseResource);
}
protected Matcher<IAnyResource> matchedToAPerson() {
return IsMatchedToAPerson.matchedToAPerson(myIdHelperService, myEmpiLinkDaoSvc);
protected Matcher<IAnyResource> matchedToAGoldenResource() {
return IsMatchedToAPerson.matchedToAGoldenResource(myIdHelperService, myMdmLinkDaoSvc);
}
protected Patient getOnlyGoldenPatient() {
@ -432,12 +432,12 @@ abstract public class BaseEmpiR4Test extends BaseJpaR4Test {
@Nonnull
protected List<IBaseResource> getAllGoldenPatients() {
return getPatientsByTag(EmpiConstants.CODE_GOLDEN_RECORD);
return getPatientsByTag(MdmConstants.CODE_GOLDEN_RECORD);
}
@Nonnull
protected List<IBaseResource> getAllRedirectedGoldenPatients() {
return getPatientsByTag(EmpiConstants.CODE_GOLDEN_RECORD_REDIRECTED);
return getPatientsByTag(MdmConstants.CODE_GOLDEN_RECORD_REDIRECTED);
}
@NotNull
@ -445,52 +445,58 @@ abstract public class BaseEmpiR4Test extends BaseJpaR4Test {
SearchParameterMap map = new SearchParameterMap();
map.setLoadSynchronous(true);
//TODO GGG ensure that this tag search works effectively.
map.add("_tag", new TokenParam(EmpiConstants.SYSTEM_GOLDEN_RECORD_STATUS, theCode));
map.add("_tag", new TokenParam(MdmConstants.SYSTEM_GOLDEN_RECORD_STATUS, theCode));
IBundleProvider bundle = myPatientDao.search(map);
return bundle.getResources(0, 999);
}
@Nonnull
protected EmpiLink createResourcesAndBuildTestEmpiLink() {
protected MdmLink createResourcesAndBuildTestMDMLink() {
Patient sourcePatient = createGoldenPatient();
Patient patient = createPatient();
EmpiLink empiLink = myEmpiLinkDaoSvc.newEmpiLink();
empiLink.setLinkSource(EmpiLinkSourceEnum.MANUAL);
empiLink.setMatchResult(EmpiMatchResultEnum.MATCH);
empiLink.setGoldenResourcePid(myIdHelperService.getPidOrNull(sourcePatient));
empiLink.setTargetPid(myIdHelperService.getPidOrNull(patient));
return empiLink;
MdmLink mdmLink = myMdmLinkDaoSvc.newMdmLink();
mdmLink.setLinkSource(MdmLinkSourceEnum.MANUAL);
mdmLink.setMatchResult(MdmMatchResultEnum.MATCH);
mdmLink.setGoldenResourcePid(myIdHelperService.getPidOrNull(sourcePatient));
mdmLink.setTargetPid(myIdHelperService.getPidOrNull(patient));
return mdmLink;
}
protected void loadEmpiSearchParameters() {
myEmpiSearchParameterLoader.daoUpdateEmpiSearchParameters();
protected void loadMdmSearchParameters() {
myMdmSearchParameterLoader.daoUpdateMdmSearchParameters();
mySearchParamRegistry.forceRefresh();
}
protected void logAllLinks() {
ourLog.info("Logging all EMPI Links:");
List<EmpiLink> links = myEmpiLinkDao.findAll();
for (EmpiLink link : links) {
List<MdmLink> links = myMdmLinkDao.findAll();
for (MdmLink link : links) {
ourLog.info(link.toString());
}
}
protected void assertLinksMatchResult(EmpiMatchResultEnum... theExpectedValues) {
assertFields(EmpiLink::getMatchResult, theExpectedValues);
protected void assertLinksMatchResult(MdmMatchResultEnum... theExpectedValues) {
assertFields(MdmLink::getMatchResult, theExpectedValues);
}
protected void assertLinksCreatedNewResource(Boolean... theExpectedValues) {
assertFields(EmpiLink::getHadToCreateNewResource, theExpectedValues);
assertFields(MdmLink::getHadToCreateNewResource, theExpectedValues);
}
protected void assertLinksMatchedByEid(Boolean... theExpectedValues) {
assertFields(EmpiLink::getEidMatch, theExpectedValues);
assertFields(MdmLink::getEidMatch, theExpectedValues);
}
public SearchParameterMap buildGoldenResourceSearchParameterMap() {
SearchParameterMap spMap = new SearchParameterMap();
spMap.setLoadSynchronous(true);
spMap.add("_tag", new TokenParam(MdmConstants.SYSTEM_GOLDEN_RECORD_STATUS, MdmConstants.CODE_GOLDEN_RECORD));
return spMap;
}
private <T> void assertFields(Function<EmpiLink, T> theAccessor, T... theExpectedValues) {
List<EmpiLink> links = myEmpiLinkDao.findAll();
private <T> void assertFields(Function<MdmLink, T> theAccessor, T... theExpectedValues) {
List<MdmLink> links = myMdmLinkDao.findAll();
assertEquals(theExpectedValues.length, links.size());
for (int i = 0; i < links.size(); ++i) {
assertEquals(theExpectedValues[i], theAccessor.apply(links.get(i)), "Value at index " + i + " was not equal");
@ -524,7 +530,7 @@ abstract public class BaseEmpiR4Test extends BaseJpaR4Test {
protected void printLinks() {
myEmpiLinkDao.findAll().forEach(empiLink -> {
myMdmLinkDao.findAll().forEach(empiLink -> {
ourLog.info(String.valueOf(empiLink));
});
}

View File

@ -1,9 +1,9 @@
package ca.uhn.fhir.jpa.empi.config;
package ca.uhn.fhir.jpa.mdm.config;
import ca.uhn.fhir.empi.api.IEmpiSettings;
import ca.uhn.fhir.empi.rules.config.EmpiRuleValidator;
import ca.uhn.fhir.empi.rules.config.EmpiSettings;
import ca.uhn.fhir.jpa.empi.helper.EmpiLinkHelper;
import ca.uhn.fhir.mdm.api.IMdmSettings;
import ca.uhn.fhir.mdm.rules.config.MdmRuleValidator;
import ca.uhn.fhir.mdm.rules.config.MdmSettings;
import ca.uhn.fhir.jpa.mdm.helper.MdmLinkHelper;
import com.google.common.base.Charsets;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Value;
@ -15,7 +15,7 @@ import org.springframework.core.io.Resource;
import java.io.IOException;
@Configuration
public abstract class BaseTestEmpiConfig {
public abstract class BaseTestMdmConfig {
@Value("${empi.prevent_eid_updates:true}")
boolean myPreventEidUpdates;
@ -24,11 +24,11 @@ public abstract class BaseTestEmpiConfig {
boolean myPreventMultipleEids;
@Bean
IEmpiSettings empiSettings(EmpiRuleValidator theEmpiRuleValidator) throws IOException {
IMdmSettings mdmSettings(MdmRuleValidator theMdmRuleValidator) throws IOException {
DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
Resource resource = resourceLoader.getResource("empi/empi-rules.json");
String json = IOUtils.toString(resource.getInputStream(), Charsets.UTF_8);
return new EmpiSettings(theEmpiRuleValidator)
return new MdmSettings(theMdmRuleValidator)
.setEnabled(false)
.setScriptText(json)
.setPreventEidUpdates(myPreventEidUpdates)
@ -36,7 +36,7 @@ public abstract class BaseTestEmpiConfig {
}
@Bean
EmpiLinkHelper empiLinkHelper() {
return new EmpiLinkHelper();
MdmLinkHelper mdmLinkHelper() {
return new MdmLinkHelper();
}
}

View File

@ -1,9 +1,9 @@
package ca.uhn.fhir.jpa.empi.config;
package ca.uhn.fhir.jpa.mdm.config;
import ca.uhn.fhir.jpa.subscription.channel.config.SubscriptionChannelConfig;
import ca.uhn.fhir.jpa.subscription.submit.config.SubscriptionSubmitterConfig;
import org.springframework.context.annotation.Import;
@Import({SubscriptionSubmitterConfig.class, SubscriptionChannelConfig.class})
public class TestEmpiConfigR4 extends BaseTestEmpiConfig {
public class TestMdmConfigR4 extends BaseTestMdmConfig {
}

View File

@ -0,0 +1,55 @@
package ca.uhn.fhir.jpa.mdm.dao;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.IMdmSettings;
import ca.uhn.fhir.mdm.rules.json.MdmRulesJson;
import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
import ca.uhn.fhir.jpa.entity.MdmLink;
import ca.uhn.fhir.jpa.util.TestUtil;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class MdmLinkDaoSvcTest extends BaseMdmR4Test {
@Autowired
MdmLinkDaoSvc myMdmLinkDaoSvc;
@Autowired
IMdmSettings myEmpiSettings;
@Test
public void testCreate() {
MdmLink mdmLink = createResourcesAndBuildTestMDMLink();
assertThat(mdmLink.getCreated(), is(nullValue()));
assertThat(mdmLink.getUpdated(), is(nullValue()));
myMdmLinkDaoSvc.save(mdmLink);
assertThat(mdmLink.getCreated(), is(notNullValue()));
assertThat(mdmLink.getUpdated(), is(notNullValue()));
assertTrue(mdmLink.getUpdated().getTime() - mdmLink.getCreated().getTime() < 1000);
}
@Test
public void testUpdate() {
MdmLink createdLink = myMdmLinkDaoSvc.save(createResourcesAndBuildTestMDMLink());
assertThat(createdLink.getLinkSource(), is(MdmLinkSourceEnum.MANUAL));
TestUtil.sleepOneClick();
createdLink.setLinkSource(MdmLinkSourceEnum.AUTO);
MdmLink updatedLink = myMdmLinkDaoSvc.save(createdLink);
assertNotEquals(updatedLink.getCreated(), updatedLink.getUpdated());
}
@Test
public void testNew() {
MdmLink newLink = myMdmLinkDaoSvc.newMdmLink();
MdmRulesJson rules = myEmpiSettings.getMdmRules();
assertEquals("1", rules.getVersion());
assertEquals(rules.getVersion(), newLink.getVersion());
}
}

View File

@ -0,0 +1,20 @@
package ca.uhn.fhir.jpa.mdm.entity;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class MdmEnumTest {
@Test
public void empiEnumOrdinals() {
// This test is here to enforce that new values in these enums are always added to the end
assertEquals(6, MdmMatchResultEnum.values().length);
assertEquals(MdmMatchResultEnum.REDIRECT, MdmMatchResultEnum.values()[MdmMatchResultEnum.values().length - 1]);
assertEquals(2, MdmLinkSourceEnum.values().length);
assertEquals(MdmLinkSourceEnum.MANUAL, MdmLinkSourceEnum.values()[MdmLinkSourceEnum.values().length - 1]);
}
}

View File

@ -1,11 +1,11 @@
package ca.uhn.fhir.jpa.empi.helper;
package ca.uhn.fhir.jpa.mdm.helper;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.interceptor.api.IInterceptorService;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.empi.broker.EmpiQueueConsumerLoader;
import ca.uhn.fhir.jpa.empi.config.EmpiSubscriptionLoader;
import ca.uhn.fhir.jpa.mdm.broker.MdmQueueConsumerLoader;
import ca.uhn.fhir.jpa.mdm.config.MdmSubscriptionLoader;
import ca.uhn.fhir.jpa.subscription.channel.impl.LinkedBlockingChannel;
import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionLoader;
import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionRegistry;
@ -27,21 +27,21 @@ import static org.mockito.Mockito.when;
/**
* How to use this Rule:
* <p>
* This rule is to be used whenever you want to have the EmpiInterceptor loaded, and be able
* to execute creates/updates/deletes while being assured that all EMPI work has been done before exiting.
* This rule is to be used whenever you want to have the MdmInterceptor loaded, and be able
* to execute creates/updates/deletes while being assured that all MDM work has been done before exiting.
* Provides two types of method:
* <p>
* 1. doUpdate/doCreate. These methods do not wait for Asynchronous EMPI work to be done. Use these when you are expecting
* 1. doUpdate/doCreate. These methods do not wait for Asynchronous MDM work to be done. Use these when you are expecting
* the calls to fail, as those hooks will never be called.
* <p>
* 2. createWithLatch/updateWithLatch. These methods will await the EMPI hooks, which are only triggered post-EMPI processing
* You should use these when you are expecting successful processing of the resource, and need to wait for async EMPI linking
* 2. createWithLatch/updateWithLatch. These methods will await the MDM hooks, which are only triggered post-MDM processing
* You should use these when you are expecting successful processing of the resource, and need to wait for async MDM linking
* work to be done.
* <p>
* Note: all create/update functions take an optional isExternalHttpRequest boolean, to make it appear as though the request's
* origin is an HTTP request.
*/
public abstract class BaseEmpiHelper implements BeforeEachCallback, AfterEachCallback {
public abstract class BaseMdmHelper implements BeforeEachCallback, AfterEachCallback {
@Mock
protected ServletRequestDetails myMockSrd;
@Mock
@ -50,15 +50,15 @@ public abstract class BaseEmpiHelper implements BeforeEachCallback, AfterEachCal
protected RestfulServer myMockRestfulServer;
@Mock
protected FhirContext myMockFhirContext;
protected PointcutLatch myAfterEmpiLatch = new PointcutLatch(Pointcut.EMPI_AFTER_PERSISTED_RESOURCE_CHECKED);
protected PointcutLatch myAfterMdmLatch = new PointcutLatch(Pointcut.MDM_AFTER_PERSISTED_RESOURCE_CHECKED);
@Autowired
EmpiQueueConsumerLoader myEmpiQueueConsumerLoader;
MdmQueueConsumerLoader myMdmQueueConsumerLoader;
@Autowired
SubscriptionRegistry mySubscriptionRegistry;
@Autowired
SubscriptionLoader mySubscriptionLoader;
@Autowired
EmpiSubscriptionLoader myEmpiSubscriptionLoader;
MdmSubscriptionLoader myMdmSubscriptionLoader;
@Mock
private IInterceptorBroadcaster myMockInterceptorBroadcaster;
@Autowired
@ -66,9 +66,9 @@ public abstract class BaseEmpiHelper implements BeforeEachCallback, AfterEachCal
@Override
public void afterEach(ExtensionContext context) throws Exception {
myInterceptorService.unregisterInterceptor(myAfterEmpiLatch);
myAfterEmpiLatch.clear();
waitUntilEmpiQueueIsEmpty();
myInterceptorService.unregisterInterceptor(myAfterMdmLatch);
myAfterMdmLatch.clear();
waitUntilMdmQueueIsEmpty();
}
@Override
@ -83,11 +83,11 @@ public abstract class BaseEmpiHelper implements BeforeEachCallback, AfterEachCal
when(myMockRestfulServer.getFhirContext()).thenReturn(myMockFhirContext);
//This sets up our basic interceptor, and also attached the latch so we can await the hook calls.
myInterceptorService.registerAnonymousInterceptor(Pointcut.EMPI_AFTER_PERSISTED_RESOURCE_CHECKED, myAfterEmpiLatch);
myInterceptorService.registerAnonymousInterceptor(Pointcut.MDM_AFTER_PERSISTED_RESOURCE_CHECKED, myAfterMdmLatch);
// We need to call this because subscriptions will get deleted in @After cleanup
waitForActivatedSubscriptionCount(0);
myEmpiSubscriptionLoader.daoUpdateEmpiSubscriptions();
myMdmSubscriptionLoader.daoUpdateMdmSubscriptions();
mySubscriptionLoader.syncSubscriptions();
waitForActivatedSubscriptionCount(2);
}
@ -97,12 +97,12 @@ public abstract class BaseEmpiHelper implements BeforeEachCallback, AfterEachCal
await("Active Subscription Count has reached " + theSize).until(() -> mySubscriptionRegistry.size() >= theSize);
}
private void waitUntilEmpiQueueIsEmpty() {
private void waitUntilMdmQueueIsEmpty() {
await().until(() -> getExecutorQueueSize() == 0);
}
public int getExecutorQueueSize() {
LinkedBlockingChannel channel = (LinkedBlockingChannel) myEmpiQueueConsumerLoader.getEmpiChannelForUnitTest();
LinkedBlockingChannel channel = (LinkedBlockingChannel) myMdmQueueConsumerLoader.getMdmChannelForUnitTest();
return channel.getQueueSizeForUnitTest();
}

View File

@ -1,8 +1,8 @@
package ca.uhn.fhir.jpa.empi.helper;
package ca.uhn.fhir.jpa.mdm.helper;
import ca.uhn.fhir.empi.api.IEmpiSettings;
import ca.uhn.fhir.empi.rules.config.EmpiRuleValidator;
import ca.uhn.fhir.empi.rules.config.EmpiSettings;
import ca.uhn.fhir.mdm.api.IMdmSettings;
import ca.uhn.fhir.mdm.rules.config.MdmRuleValidator;
import ca.uhn.fhir.mdm.rules.config.MdmSettings;
import com.google.common.base.Charsets;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Value;
@ -13,27 +13,27 @@ import org.springframework.core.io.Resource;
import java.io.IOException;
public class EmpiHelperConfig {
public class MdmHelperConfig {
@Bean
public EmpiHelperR4 empiHelperR4() {
return new EmpiHelperR4();
public MdmHelperR4 mdmHelperR4() {
return new MdmHelperR4();
}
@Value("${empi.prevent_eid_updates:false}")
@Value("${mdm.prevent_eid_updates:false}")
boolean myPreventEidUpdates;
@Value("${empi.prevent_multiple_eids:true}")
@Value("${mdm.prevent_multiple_eids:true}")
boolean myPreventMultipleEids;
@Primary
@Bean
IEmpiSettings empiSettings(EmpiRuleValidator theEmpiRuleValidator) throws IOException {
IMdmSettings empiSettings(MdmRuleValidator theMdmRuleValidator) throws IOException {
DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
Resource resource = resourceLoader.getResource("empi/empi-rules.json");
String json = IOUtils.toString(resource.getInputStream(), Charsets.UTF_8);
// Set Enabled to true, and set strict mode.
return new EmpiSettings(theEmpiRuleValidator)
return new MdmSettings(theMdmRuleValidator)
.setEnabled(true)
.setScriptText(json)
.setPreventEidUpdates(myPreventEidUpdates)

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.jpa.empi.helper;
package ca.uhn.fhir.jpa.mdm.helper;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
@ -8,7 +8,7 @@ import ca.uhn.fhir.rest.server.TransactionLogMessages;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.springframework.beans.factory.annotation.Autowired;
public class EmpiHelperR4 extends BaseEmpiHelper {
public class MdmHelperR4 extends BaseMdmHelper {
@Autowired
private FhirContext myFhirContext;
@Autowired
@ -19,10 +19,10 @@ public class EmpiHelperR4 extends BaseEmpiHelper {
}
public OutcomeAndLogMessageWrapper createWithLatch(IBaseResource theBaseResource, boolean isExternalHttpRequest) throws InterruptedException {
myAfterEmpiLatch.setExpectedCount(1);
myAfterMdmLatch.setExpectedCount(1);
DaoMethodOutcome daoMethodOutcome = doCreateResource(theBaseResource, isExternalHttpRequest);
myAfterEmpiLatch.awaitExpected();
return new OutcomeAndLogMessageWrapper(daoMethodOutcome, myAfterEmpiLatch.getLatchInvocationParameterOfType(TransactionLogMessages.class));
myAfterMdmLatch.awaitExpected();
return new OutcomeAndLogMessageWrapper(daoMethodOutcome, myAfterMdmLatch.getLatchInvocationParameterOfType(TransactionLogMessages.class));
}
public OutcomeAndLogMessageWrapper updateWithLatch(IBaseResource theIBaseResource) throws InterruptedException {
@ -30,10 +30,10 @@ public class EmpiHelperR4 extends BaseEmpiHelper {
}
public OutcomeAndLogMessageWrapper updateWithLatch(IBaseResource theIBaseResource, boolean isExternalHttpRequest) throws InterruptedException {
myAfterEmpiLatch.setExpectedCount(1);
myAfterMdmLatch.setExpectedCount(1);
DaoMethodOutcome daoMethodOutcome = doUpdateResource(theIBaseResource, isExternalHttpRequest);
myAfterEmpiLatch.awaitExpected();
return new OutcomeAndLogMessageWrapper(daoMethodOutcome, myAfterEmpiLatch.getLatchInvocationParameterOfType(TransactionLogMessages.class));
myAfterMdmLatch.awaitExpected();
return new OutcomeAndLogMessageWrapper(daoMethodOutcome, myAfterMdmLatch.getLatchInvocationParameterOfType(TransactionLogMessages.class));
}
public DaoMethodOutcome doCreateResource(IBaseResource theResource, boolean isExternalHttpRequest) {
@ -52,7 +52,7 @@ public class EmpiHelperR4 extends BaseEmpiHelper {
/**
* OutcomeAndLogMessageWrapper is a simple wrapper class which is _excellent_. It allows us to skip the fact that java doesn't allow
* multiple returns, and wraps both the Method Outcome of the DAO, _and_ the TransactionLogMessages that were passed to the pointcut
* by the EMPI module.
* by the MDM module.
*/
public class OutcomeAndLogMessageWrapper {
DaoMethodOutcome myDaoMethodOutcome;

View File

@ -0,0 +1,31 @@
package ca.uhn.fhir.jpa.mdm.helper;
import ca.uhn.fhir.jpa.dao.data.IMdmLinkDao;
import ca.uhn.fhir.jpa.entity.MdmLink;
import ca.uhn.fhir.model.primitive.IdDt;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class MdmLinkHelper {
private static final Logger ourLog = LoggerFactory.getLogger(MdmLinkHelper.class);
@Autowired
IMdmLinkDao myMdmLinkDao;
@Transactional
public void logMdmLinks() {
List<MdmLink> links = myMdmLinkDao.findAll();
ourLog.info("All MDM Links:");
for (MdmLink link : links) {
IdDt goldenResourceId = link.getGoldenResource().getIdDt().toVersionless();
IdDt targetId = link.getTarget().getIdDt().toVersionless();
ourLog.info("{}: {}, {}, {}, {}", link.getId(), goldenResourceId, targetId, link.getMatchResult(), link.getLinkSource());
}
}
}

View File

@ -1,14 +1,13 @@
package ca.uhn.fhir.jpa.empi.interceptor;
package ca.uhn.fhir.jpa.mdm.interceptor;
import ca.uhn.fhir.empi.api.EmpiLinkSourceEnum;
import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
import ca.uhn.fhir.interceptor.api.IInterceptorService;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.api.model.ExpungeOptions;
import ca.uhn.fhir.jpa.dao.data.IEmpiLinkDao;
import ca.uhn.fhir.jpa.empi.BaseEmpiR4Test;
import ca.uhn.fhir.jpa.entity.EmpiLink;
import ca.uhn.fhir.jpa.entity.MdmLink;
import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import org.hl7.fhir.r4.model.Patient;
@ -22,16 +21,14 @@ import static org.hamcrest.core.StringContains.containsString;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
public class EmpiExpungeTest extends BaseEmpiR4Test {
public class MdmExpungeTest extends BaseMdmR4Test {
@Autowired
IInterceptorService myInterceptorService;
@Autowired
IEmpiStorageInterceptor myEmpiStorageInterceptor;
IMdmStorageInterceptor myEmpiStorageInterceptor;
@Autowired
DaoConfig myDaoConfig;
@Autowired
IEmpiLinkDao myEmpiLinkDao;
private ResourceTable myTargetEntity;
private ResourceTable mySourceEntity;
private IdDt myTargetId;
@ -44,19 +41,19 @@ public class EmpiExpungeTest extends BaseEmpiR4Test {
myTargetId = myTargetEntity.getIdDt().toVersionless();
mySourceEntity = (ResourceTable) myPatientDao.create(new Patient()).getEntity();
EmpiLink empiLink = myEmpiLinkDaoSvc.newEmpiLink();
empiLink.setLinkSource(EmpiLinkSourceEnum.MANUAL);
empiLink.setMatchResult(EmpiMatchResultEnum.MATCH);
empiLink.setGoldenResourcePid(mySourceEntity.getId());
empiLink.setTargetPid(myTargetEntity.getId());
saveLink(empiLink);
MdmLink mdmLink = myMdmLinkDaoSvc.newMdmLink();
mdmLink.setLinkSource(MdmLinkSourceEnum.MANUAL);
mdmLink.setMatchResult(MdmMatchResultEnum.MATCH);
mdmLink.setGoldenResourcePid(mySourceEntity.getId());
mdmLink.setTargetPid(myTargetEntity.getId());
saveLink(mdmLink);
}
@Test
public void testUninterceptedDeleteRemovesEMPIReference() {
assertEquals(1, myEmpiLinkDao.count());
assertEquals(1, myMdmLinkDao.count());
myPatientDao.delete(myTargetEntity.getIdDt());
assertEquals(1, myEmpiLinkDao.count());
assertEquals(1, myMdmLinkDao.count());
ExpungeOptions expungeOptions = new ExpungeOptions();
expungeOptions.setExpungeDeletedResources(true);
try {
@ -68,7 +65,7 @@ public class EmpiExpungeTest extends BaseEmpiR4Test {
}
myInterceptorService.registerInterceptor(myEmpiStorageInterceptor);
myPatientDao.expunge(myTargetId.toVersionless(), expungeOptions, null);
assertEquals(0, myEmpiLinkDao.count());
assertEquals(0, myMdmLinkDao.count());
}
@AfterEach

View File

@ -1,14 +1,13 @@
package ca.uhn.fhir.jpa.empi.interceptor;
package ca.uhn.fhir.jpa.mdm.interceptor;
import ca.uhn.fhir.empi.model.CanonicalEID;
import ca.uhn.fhir.empi.rules.config.EmpiSettings;
import ca.uhn.fhir.mdm.model.CanonicalEID;
import ca.uhn.fhir.mdm.rules.config.MdmSettings;
import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome;
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
import ca.uhn.fhir.jpa.empi.BaseEmpiR4Test;
import ca.uhn.fhir.jpa.empi.helper.EmpiHelperConfig;
import ca.uhn.fhir.jpa.empi.helper.EmpiHelperR4;
import ca.uhn.fhir.jpa.entity.EmpiLink;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
import ca.uhn.fhir.jpa.mdm.helper.MdmHelperConfig;
import ca.uhn.fhir.jpa.mdm.helper.MdmHelperR4;
import ca.uhn.fhir.jpa.entity.MdmLink;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.rest.server.TransactionLogMessages;
@ -17,6 +16,7 @@ import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.Enumerations;
import org.hl7.fhir.r4.model.Medication;
import org.hl7.fhir.r4.model.Organization;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.Person;
@ -32,11 +32,15 @@ import org.springframework.test.context.ContextConfiguration;
import java.util.Date;
import java.util.List;
import static ca.uhn.fhir.empi.api.EmpiConstants.CODE_HAPI_MDM_MANAGED;
import static ca.uhn.fhir.empi.api.EmpiConstants.SYSTEM_MDM_MANAGED;
import static ca.uhn.fhir.mdm.api.MdmConstants.CODE_GOLDEN_RECORD;
import static ca.uhn.fhir.mdm.api.MdmConstants.CODE_GOLDEN_RECORD_REDIRECTED;
import static ca.uhn.fhir.mdm.api.MdmConstants.CODE_HAPI_MDM_MANAGED;
import static ca.uhn.fhir.mdm.api.MdmConstants.SYSTEM_GOLDEN_RECORD_STATUS;
import static ca.uhn.fhir.mdm.api.MdmConstants.SYSTEM_MDM_MANAGED;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.startsWith;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.nullValue;
@ -46,31 +50,31 @@ import static org.junit.jupiter.api.Assertions.fail;
import static org.slf4j.LoggerFactory.getLogger;
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
@ContextConfiguration(classes = {EmpiHelperConfig.class})
public class EmpiStorageInterceptorIT extends BaseEmpiR4Test {
@ContextConfiguration(classes = {MdmHelperConfig.class})
public class MdmStorageInterceptorIT extends BaseMdmR4Test {
private static final Logger ourLog = getLogger(EmpiStorageInterceptorIT.class);
private static final Logger ourLog = getLogger(MdmStorageInterceptorIT.class);
@RegisterExtension
@Autowired
public EmpiHelperR4 myEmpiHelper;
public MdmHelperR4 myMdmHelper;
@Autowired
private IdHelperService myIdHelperService;
@BeforeEach
public void before() {
super.loadEmpiSearchParameters();
super.loadMdmSearchParameters();
}
@Test
public void testCreatePractitioner() throws InterruptedException {
myEmpiHelper.createWithLatch(buildPractitionerWithNameAndId("somename", "some_id"));
myMdmHelper.createWithLatch(buildPractitionerWithNameAndId("somename", "some_id"));
assertLinkCount(1);
}
@Test
public void testDeletePersonDeletesLinks() throws InterruptedException {
myEmpiHelper.createWithLatch(buildPaulPatient());
myMdmHelper.createWithLatch(buildPaulPatient());
assertLinkCount(1);
Patient sourcePatient = getOnlyGoldenPatient();
myPatientDao.delete(sourcePatient.getIdElement());
@ -78,64 +82,88 @@ public class EmpiStorageInterceptorIT extends BaseEmpiR4Test {
}
@Test
public void testCreatePersonWithEmpiTagForbidden() throws InterruptedException {
//Creating a person with the EMPI-MANAGED tag should fail
Person person = new Person();
person.getMeta().addTag(SYSTEM_MDM_MANAGED, CODE_HAPI_MDM_MANAGED, "User is managed by EMPI");
public void testCreatePatientWithMdmTagForbidden() throws InterruptedException {
//Creating a person with the MDM-MANAGED tag should fail
Patient patient = new Patient();
patient.getMeta().addTag(SYSTEM_MDM_MANAGED, CODE_HAPI_MDM_MANAGED, "User is managed by MDM");
try {
myEmpiHelper.doCreateResource(person, true);
myMdmHelper.doCreateResource(patient, true);
fail();
} catch (ForbiddenOperationException e) {
assertEquals("Cannot create or modify Resources that are managed by EMPI.", e.getMessage());
assertThat(e.getMessage(), startsWith("Cannot create or modify Resources that are managed by MDM."));
}
}
@Test
public void testCreatingPersonWithInsufficentEMPIAttributesIsNotEMPIProcessed() throws InterruptedException {
myEmpiHelper.doCreateResource(new Patient(), true);
public void testCreatePatientWithGoldenRecordTagForbidden() throws InterruptedException {
Patient patient = new Patient();
patient.getMeta().addTag(SYSTEM_GOLDEN_RECORD_STATUS, CODE_GOLDEN_RECORD, "Golden Record");
try {
myMdmHelper.doCreateResource(patient, true);
fail();
} catch (ForbiddenOperationException e) {
assertThat(e.getMessage(), startsWith("Cannot create or modify Resources that are managed by MDM."));
}
}
@Test
public void testCreateMedicationWithGoldenRecordRedirectTagForbidden() throws InterruptedException {
Medication medication = new Medication();
medication.getMeta().addTag(SYSTEM_GOLDEN_RECORD_STATUS, CODE_GOLDEN_RECORD_REDIRECTED, "Golden Record");
try {
myMdmHelper.doCreateResource(medication, true);
fail();
} catch (ForbiddenOperationException e) {
assertThat(e.getMessage(), startsWith("Cannot create or modify Resources that are managed by MDM."));
}
}
@Test
public void testCreatingPersonWithInsufficentMDMAttributesIsNotMDMProcessed() throws InterruptedException {
myMdmHelper.doCreateResource(new Patient(), true);
assertLinkCount(0);
}
@Test
public void testCreatingPatientWithOneOrMoreMatchingAttributesIsEMPIProcessed() throws InterruptedException {
myEmpiHelper.createWithLatch(buildPaulPatient());
public void testCreatingPatientWithOneOrMoreMatchingAttributesIsMDMProcessed() throws InterruptedException {
myMdmHelper.createWithLatch(buildPaulPatient());
assertLinkCount(1);
}
@Test
public void testCreateOrganizationWithEmpiTagForbidden() throws InterruptedException {
//Creating a organization with the EMPI-MANAGED tag should fail
public void testCreateOrganizationWithMdmTagForbidden() throws InterruptedException {
//Creating a organization with the MDM-MANAGED tag should fail
Organization organization = new Organization();
organization.getMeta().addTag(SYSTEM_MDM_MANAGED, CODE_HAPI_MDM_MANAGED, "User is managed by EMPI");
organization.getMeta().addTag(SYSTEM_MDM_MANAGED, CODE_HAPI_MDM_MANAGED, "User is managed by MDM");
try {
myEmpiHelper.doCreateResource(organization, true);
myMdmHelper.doCreateResource(organization, true);
fail();
} catch (ForbiddenOperationException e) {
assertEquals("Cannot create or modify Resources that are managed by EMPI.", e.getMessage());
assertThat(e.getMessage(), startsWith("Cannot create or modify Resources that are managed by MDM."));
}
}
@Test
public void testUpdateOrganizationWithEmpiTagForbidden() throws InterruptedException {
//Creating a organization with the EMPI-MANAGED tag should fail
public void testUpdateOrganizationWithMdmTagForbidden() throws InterruptedException {
//Creating a organization with the MDM-MANAGED tag should fail
Organization organization = new Organization();
myEmpiHelper.doCreateResource(organization, true);
organization.getMeta().addTag(SYSTEM_MDM_MANAGED, CODE_HAPI_MDM_MANAGED, "User is managed by EMPI");
myMdmHelper.doCreateResource(organization, true);
organization.getMeta().addTag(SYSTEM_MDM_MANAGED, CODE_HAPI_MDM_MANAGED, "User is managed by MDM");
try {
myEmpiHelper.doUpdateResource(organization, true);
myMdmHelper.doUpdateResource(organization, true);
fail();
} catch (ForbiddenOperationException e) {
assertEquals("The HAPI-EMPI tag on a resource may not be changed once created.", e.getMessage());
assertEquals("The HAPI-MDM tag on a resource may not be changed once created.", e.getMessage());
}
}
@Test
public void testPersonRecordsManagedByEmpiAllShareSameTag() throws InterruptedException {
myEmpiHelper.createWithLatch(buildJanePatient());
myEmpiHelper.createWithLatch(buildPaulPatient());
public void testPersonRecordsManagedByMdmAllShareSameTag() throws InterruptedException {
myMdmHelper.createWithLatch(buildJanePatient());
myMdmHelper.createWithLatch(buildPaulPatient());
//TODO GGG MDM: this test is out of date, since we now are using golden record Patients
IBundleProvider search = myPatientDao.search(new SearchParameterMap().setLoadSynchronous(true));
IBundleProvider search = myPatientDao.search(buildGoldenResourceSearchParameterMap());
List<IBaseResource> resources = search.getResources(0, search.size());
for (IBaseResource person : resources) {
@ -144,46 +172,46 @@ public class EmpiStorageInterceptorIT extends BaseEmpiR4Test {
}
@Test
public void testNonEmpiManagedPersonCannotHaveEmpiManagedTagAddedToThem() {
public void testNonMdmManagedPersonCannotHaveMdmManagedTagAddedToThem() {
//Person created manually.
Person person = new Person();
DaoMethodOutcome daoMethodOutcome = myEmpiHelper.doCreateResource(person, true);
DaoMethodOutcome daoMethodOutcome = myMdmHelper.doCreateResource(person, true);
assertNotNull(daoMethodOutcome.getId());
//Updating that person to set them as EMPI managed is not allowed.
//Updating that person to set them as MDM managed is not allowed.
person.getMeta().addTag(SYSTEM_MDM_MANAGED, CODE_HAPI_MDM_MANAGED, "User is managed by EMPI");
try {
myEmpiHelper.doUpdateResource(person, true);
myMdmHelper.doUpdateResource(person, true);
fail();
} catch (ForbiddenOperationException e) {
assertEquals("The HAPI-EMPI tag on a resource may not be changed once created.", e.getMessage());
assertEquals("The HAPI-MDM tag on a resource may not be changed once created.", e.getMessage());
}
}
@Test
public void testEmpiManagedPersonCannotBeModifiedByPersonUpdateRequest() throws InterruptedException {
public void testMdmManagedPersonCannotBeModifiedByPersonUpdateRequest() throws InterruptedException {
// When EMPI is enabled, only the EMPI system is allowed to modify Person links of Persons with the EMPI-MANAGED tag.
Patient patient = new Patient();
IIdType patientId = myEmpiHelper.createWithLatch(buildPaulPatient()).getDaoMethodOutcome().getId().toUnqualifiedVersionless();
IIdType patientId = myMdmHelper.createWithLatch(buildPaulPatient()).getDaoMethodOutcome().getId().toUnqualifiedVersionless();
patient.setId(patientId);
//Updating a Person who was created via EMPI should fail.
EmpiLink empiLink = myEmpiLinkDaoSvc.getMatchedLinkForTargetPid(myIdHelperService.getPidOrNull(patient)).get();
Long sourcePatientPid = empiLink.getSourceResourcePid();
MdmLink mdmLink = myMdmLinkDaoSvc.getMatchedLinkForTargetPid(myIdHelperService.getPidOrNull(patient)).get();
Long sourcePatientPid = mdmLink.getGoldenResourcePid();
Patient empiSourcePatient = (Patient) myPatientDao.readByPid(new ResourcePersistentId(sourcePatientPid));
empiSourcePatient.setGender(Enumerations.AdministrativeGender.MALE);
try {
myEmpiHelper.doUpdateResource(empiSourcePatient, true);
myMdmHelper.doUpdateResource(empiSourcePatient, true);
fail();
} catch (ForbiddenOperationException e) {
assertEquals("Cannot create or modify Resources that are managed by EMPI.", e.getMessage());
assertThat(e.getMessage(), startsWith("Cannot create or modify Resources that are managed by MDM."));
}
}
@Test
public void testEmpiPointcutReceivesTransactionLogMessages() throws InterruptedException {
EmpiHelperR4.OutcomeAndLogMessageWrapper wrapper = myEmpiHelper.createWithLatch(buildJanePatient());
public void testMdmPointcutReceivesTransactionLogMessages() throws InterruptedException {
MdmHelperR4.OutcomeAndLogMessageWrapper wrapper = myMdmHelper.createWithLatch(buildJanePatient());
TransactionLogMessages empiTransactionLogMessages = wrapper.getLogMessages();
@ -197,12 +225,12 @@ public class EmpiStorageInterceptorIT extends BaseEmpiR4Test {
@Test
public void testWhenASingularPatientUpdatesExternalEidThatPersonEidIsUpdated() throws InterruptedException {
Patient jane = addExternalEID(buildJanePatient(), "some_eid");
EmpiHelperR4.OutcomeAndLogMessageWrapper latch = myEmpiHelper.createWithLatch(jane);
MdmHelperR4.OutcomeAndLogMessageWrapper latch = myMdmHelper.createWithLatch(jane);
jane.setId(latch.getDaoMethodOutcome().getId());
clearExternalEIDs(jane);
jane = addExternalEID(jane, "some_new_eid");
EmpiHelperR4.OutcomeAndLogMessageWrapper outcomeWrapper = myEmpiHelper.updateWithLatch(jane);
MdmHelperR4.OutcomeAndLogMessageWrapper outcomeWrapper = myMdmHelper.updateWithLatch(jane);
IAnyResource person = getGoldenResourceFromTargetResource(jane);
List<CanonicalEID> externalEids = myEIDHelper.getExternalEid(person);
assertThat(externalEids, hasSize(1));
@ -213,12 +241,12 @@ public class EmpiStorageInterceptorIT extends BaseEmpiR4Test {
public void testWhenEidUpdatesAreDisabledForbidsUpdatesToEidsOnTargets() throws InterruptedException {
setPreventEidUpdates(true);
Patient jane = addExternalEID(buildJanePatient(), "some_eid");
EmpiHelperR4.OutcomeAndLogMessageWrapper latch = myEmpiHelper.createWithLatch(jane);
MdmHelperR4.OutcomeAndLogMessageWrapper latch = myMdmHelper.createWithLatch(jane);
jane.setId(latch.getDaoMethodOutcome().getId());
clearExternalEIDs(jane);
jane = addExternalEID(jane, "some_new_eid");
try {
myEmpiHelper.doUpdateResource(jane, true);
myMdmHelper.doUpdateResource(jane, true);
fail();
} catch (ForbiddenOperationException e) {
assertThat(e.getMessage(), is(equalTo("While running with EID updates disabled, EIDs may not be updated on Patient/Practitioner resources")));
@ -233,7 +261,7 @@ public class EmpiStorageInterceptorIT extends BaseEmpiR4Test {
addExternalEID(patient, "123");
addExternalEID(patient, "456");
try {
myEmpiHelper.doCreateResource(patient, true);
myMdmHelper.doCreateResource(patient, true);
fail();
} catch (ForbiddenOperationException e) {
assertThat(e.getMessage(), is(equalTo("While running with multiple EIDs disabled, Patient/Practitioner resources may have at most one EID.")));
@ -243,7 +271,7 @@ public class EmpiStorageInterceptorIT extends BaseEmpiR4Test {
}
@Test
public void testInterceptorHandlesNonEmpiResources() {
public void testInterceptorHandlesNonMdmResources() {
setPreventEidUpdates(true);
//Create some arbitrary resource.
@ -256,20 +284,20 @@ public class EmpiStorageInterceptorIT extends BaseEmpiR4Test {
fooSp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE);
myEmpiHelper.doCreateResource(fooSp, true);
myMdmHelper.doCreateResource(fooSp, true);
fooSp.setXpathUsage(SearchParameter.XPathUsageType.PHONETIC);
myEmpiHelper.doUpdateResource(fooSp, true);
myMdmHelper.doUpdateResource(fooSp, true);
}
@Test
public void testPatientsWithNoEIDCanBeUpdated() throws InterruptedException {
setPreventEidUpdates(true);
Patient p = buildPaulPatient();
EmpiHelperR4.OutcomeAndLogMessageWrapper wrapper = myEmpiHelper.createWithLatch(p);
MdmHelperR4.OutcomeAndLogMessageWrapper wrapper = myMdmHelper.createWithLatch(p);
p.setId(wrapper.getDaoMethodOutcome().getId());
p.setBirthDate(new Date());
myEmpiHelper.updateWithLatch(p);
myMdmHelper.updateWithLatch(p);
setPreventEidUpdates(false);
}
@ -277,19 +305,19 @@ public class EmpiStorageInterceptorIT extends BaseEmpiR4Test {
public void testPatientsCanHaveEIDAddedInStrictMode() throws InterruptedException {
setPreventEidUpdates(true);
Patient p = buildPaulPatient();
EmpiHelperR4.OutcomeAndLogMessageWrapper messageWrapper = myEmpiHelper.createWithLatch(p);
MdmHelperR4.OutcomeAndLogMessageWrapper messageWrapper = myMdmHelper.createWithLatch(p);
p.setId(messageWrapper.getDaoMethodOutcome().getId());
addExternalEID(p, "external eid");
myEmpiHelper.updateWithLatch(p);
myMdmHelper.updateWithLatch(p);
setPreventEidUpdates(false);
}
private void setPreventEidUpdates(boolean thePrevent) {
((EmpiSettings) myEmpiConfig).setPreventEidUpdates(thePrevent);
((MdmSettings) myMdmSettings).setPreventEidUpdates(thePrevent);
}
private void setPreventMultipleEids(boolean thePrevent) {
((EmpiSettings) myEmpiConfig).setPreventMultipleEids(thePrevent);
((MdmSettings) myMdmSettings).setPreventMultipleEids(thePrevent);
}
}

View File

@ -1,11 +1,10 @@
package ca.uhn.fhir.jpa.empi.matcher;
package ca.uhn.fhir.jpa.mdm.matcher;
import ca.uhn.fhir.empi.api.EmpiConstants;
import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
import ca.uhn.fhir.empi.util.EmpiUtil;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.mdm.util.MdmUtil;
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
import ca.uhn.fhir.jpa.empi.dao.EmpiLinkDaoSvc;
import ca.uhn.fhir.jpa.entity.EmpiLink;
import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc;
import ca.uhn.fhir.jpa.entity.MdmLink;
import org.hamcrest.TypeSafeMatcher;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.slf4j.Logger;
@ -22,13 +21,13 @@ public abstract class BaseSourceResourceMatcher extends TypeSafeMatcher<IAnyReso
private static final Logger ourLog = LoggerFactory.getLogger(BaseSourceResourceMatcher.class);
protected IdHelperService myIdHelperService;
protected EmpiLinkDaoSvc myEmpiLinkDaoSvc;
protected MdmLinkDaoSvc myMdmLinkDaoSvc;
protected Collection<IAnyResource> myBaseResources;
protected String myTargetType;
protected BaseSourceResourceMatcher(IdHelperService theIdHelperService, EmpiLinkDaoSvc theEmpiLinkDaoSvc, IAnyResource... theBaseResource) {
protected BaseSourceResourceMatcher(IdHelperService theIdHelperService, MdmLinkDaoSvc theMdmLinkDaoSvc, IAnyResource... theBaseResource) {
myIdHelperService = theIdHelperService;
myEmpiLinkDaoSvc = theEmpiLinkDaoSvc;
myMdmLinkDaoSvc = theMdmLinkDaoSvc;
myBaseResources = Arrays.stream(theBaseResource).collect(Collectors.toList());
}
@ -36,39 +35,39 @@ public abstract class BaseSourceResourceMatcher extends TypeSafeMatcher<IAnyReso
protected Long getMatchedResourcePidFromResource(IAnyResource theResource) {
Long retval;
boolean isGoldenRecord = EmpiUtil.isEmpiManaged(theResource);
boolean isGoldenRecord = MdmUtil.isMdmManaged(theResource);
if (isGoldenRecord) {
return myIdHelperService.getPidOrNull(theResource);
}
EmpiLink matchLink = getMatchedEmpiLink(theResource);
MdmLink matchLink = getMatchedEmpiLink(theResource);
if (matchLink == null) {
return null;
} else {
retval = matchLink.getSourceResourcePid();
myTargetType = matchLink.getEmpiTargetType();
retval = matchLink.getGoldenResourcePid();
myTargetType = matchLink.getMdmTargetType();
}
return retval;
}
protected List<Long> getPossibleMatchedSourceResourcePidsFromTarget(IAnyResource theBaseResource) {
return getEmpiLinksForTarget(theBaseResource, EmpiMatchResultEnum.POSSIBLE_MATCH).stream().map(EmpiLink::getSourceResourcePid).collect(Collectors.toList());
return getEmpiLinksForTarget(theBaseResource, MdmMatchResultEnum.POSSIBLE_MATCH).stream().map(MdmLink::getGoldenResourcePid).collect(Collectors.toList());
}
protected EmpiLink getMatchedEmpiLink(IAnyResource thePatientOrPractitionerResource) {
List<EmpiLink> empiLinks = getEmpiLinksForTarget(thePatientOrPractitionerResource, EmpiMatchResultEnum.MATCH);
if (empiLinks.size() == 0) {
protected MdmLink getMatchedEmpiLink(IAnyResource thePatientOrPractitionerResource) {
List<MdmLink> mdmLinks = getEmpiLinksForTarget(thePatientOrPractitionerResource, MdmMatchResultEnum.MATCH);
if (mdmLinks.size() == 0) {
return null;
} else if (empiLinks.size() == 1) {
return empiLinks.get(0);
} else if (mdmLinks.size() == 1) {
return mdmLinks.get(0);
} else {
throw new IllegalStateException("Its illegal to have more than 1 match for a given target! we found " + empiLinks.size() + " for resource with id: " + thePatientOrPractitionerResource.getIdElement().toUnqualifiedVersionless());
throw new IllegalStateException("Its illegal to have more than 1 match for a given target! we found " + mdmLinks.size() + " for resource with id: " + thePatientOrPractitionerResource.getIdElement().toUnqualifiedVersionless());
}
}
protected List<EmpiLink> getEmpiLinksForTarget(IAnyResource theTargetResource, EmpiMatchResultEnum theMatchResult) {
protected List<MdmLink> getEmpiLinksForTarget(IAnyResource theTargetResource, MdmMatchResultEnum theMatchResult) {
Long pidOrNull = myIdHelperService.getPidOrNull(theTargetResource);
List<EmpiLink> matchLinkForTarget = myEmpiLinkDaoSvc.getEmpiLinksByTargetPidAndMatchResult(pidOrNull, theMatchResult);
List<MdmLink> matchLinkForTarget = myMdmLinkDaoSvc.getMdmLinksByTargetPidAndMatchResult(pidOrNull, theMatchResult);
if (!matchLinkForTarget.isEmpty()) {
return matchLinkForTarget;
} else {

View File

@ -1,7 +1,7 @@
package ca.uhn.fhir.jpa.empi.matcher;
package ca.uhn.fhir.jpa.mdm.matcher;
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
import ca.uhn.fhir.jpa.empi.dao.EmpiLinkDaoSvc;
import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hl7.fhir.instance.model.api.IAnyResource;
@ -19,8 +19,8 @@ public class IsLinkedTo extends BaseSourceResourceMatcher {
private List<Long> baseResourcePersonPids;
private Long incomingResourcePersonPid;
protected IsLinkedTo(IdHelperService theIdHelperService, EmpiLinkDaoSvc theEmpiLinkDaoSvc, IAnyResource... theBaseResource) {
super(theIdHelperService, theEmpiLinkDaoSvc, theBaseResource);
protected IsLinkedTo(IdHelperService theIdHelperService, MdmLinkDaoSvc theMdmLinkDaoSvc, IAnyResource... theBaseResource) {
super(theIdHelperService, theMdmLinkDaoSvc, theBaseResource);
}
@ -42,7 +42,7 @@ public class IsLinkedTo extends BaseSourceResourceMatcher {
public void describeTo(Description theDescription) {
}
public static Matcher<IAnyResource> linkedTo(IdHelperService theIdHelperService, EmpiLinkDaoSvc theEmpiLinkDaoSvc, IAnyResource... theBaseResource) {
return new IsLinkedTo(theIdHelperService, theEmpiLinkDaoSvc, theBaseResource);
public static Matcher<IAnyResource> linkedTo(IdHelperService theIdHelperService, MdmLinkDaoSvc theMdmLinkDaoSvc, IAnyResource... theBaseResource) {
return new IsLinkedTo(theIdHelperService, theMdmLinkDaoSvc, theBaseResource);
}
}

View File

@ -1,8 +1,8 @@
package ca.uhn.fhir.jpa.empi.matcher;
package ca.uhn.fhir.jpa.mdm.matcher;
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
import ca.uhn.fhir.jpa.empi.dao.EmpiLinkDaoSvc;
import ca.uhn.fhir.jpa.entity.EmpiLink;
import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc;
import ca.uhn.fhir.jpa.entity.MdmLink;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
@ -13,16 +13,16 @@ import java.util.Optional;
public class IsMatchedToAPerson extends TypeSafeMatcher<IAnyResource> {
private final IdHelperService myIdHelperService;
private final EmpiLinkDaoSvc myEmpiLinkDaoSvc;
private final MdmLinkDaoSvc myMdmLinkDaoSvc;
public IsMatchedToAPerson(IdHelperService theIdHelperService, EmpiLinkDaoSvc theEmpiLinkDaoSvc) {
public IsMatchedToAPerson(IdHelperService theIdHelperService, MdmLinkDaoSvc theMdmLinkDaoSvc) {
myIdHelperService = theIdHelperService;
myEmpiLinkDaoSvc = theEmpiLinkDaoSvc;
myMdmLinkDaoSvc = theMdmLinkDaoSvc;
}
@Override
protected boolean matchesSafely(IAnyResource theIncomingResource) {
Optional<EmpiLink> matchedLinkForTargetPid = myEmpiLinkDaoSvc.getMatchedLinkForTargetPid(myIdHelperService.getPidOrNull(theIncomingResource));
Optional<MdmLink> matchedLinkForTargetPid = myMdmLinkDaoSvc.getMatchedLinkForTargetPid(myIdHelperService.getPidOrNull(theIncomingResource));
return matchedLinkForTargetPid.isPresent();
}
@ -31,7 +31,7 @@ public class IsMatchedToAPerson extends TypeSafeMatcher<IAnyResource> {
theDescription.appendText("patient/practitioner was not linked to a Person.");
}
public static Matcher<IAnyResource> matchedToAPerson(IdHelperService theIdHelperService, EmpiLinkDaoSvc theEmpiLinkDaoSvc) {
return new IsMatchedToAPerson(theIdHelperService, theEmpiLinkDaoSvc);
public static Matcher<IAnyResource> matchedToAGoldenResource(IdHelperService theIdHelperService, MdmLinkDaoSvc theMdmLinkDaoSvc) {
return new IsMatchedToAPerson(theIdHelperService, theMdmLinkDaoSvc);
}
}

View File

@ -1,9 +1,9 @@
package ca.uhn.fhir.jpa.empi.matcher;
package ca.uhn.fhir.jpa.mdm.matcher;
import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
import ca.uhn.fhir.jpa.empi.dao.EmpiLinkDaoSvc;
import ca.uhn.fhir.jpa.entity.EmpiLink;
import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc;
import ca.uhn.fhir.jpa.entity.MdmLink;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hl7.fhir.instance.model.api.IAnyResource;
@ -14,13 +14,13 @@ import java.util.stream.Collectors;
public class IsPossibleDuplicateOf extends BaseSourceResourceMatcher {
/**
* Matcher with tells us if there is an EmpiLink with between these two resources that are considered POSSIBLE DUPLICATE.
* Matcher with tells us if there is an MdmLink with between these two resources that are considered POSSIBLE DUPLICATE.
* For use only on persons.
*/
private Long incomingPersonPid;
protected IsPossibleDuplicateOf(IdHelperService theIdHelperService, EmpiLinkDaoSvc theEmpiLinkDaoSvc, IAnyResource... theBaseResource) {
super(theIdHelperService, theEmpiLinkDaoSvc, theBaseResource);
protected IsPossibleDuplicateOf(IdHelperService theIdHelperService, MdmLinkDaoSvc theMdmLinkDaoSvc, IAnyResource... theBaseResource) {
super(theIdHelperService, theMdmLinkDaoSvc, theBaseResource);
}
@Override
@ -36,9 +36,9 @@ public class IsPossibleDuplicateOf extends BaseSourceResourceMatcher {
//Returns true if there is a POSSIBLE_DUPLICATE between the incoming resource, and all of the resources passed in via the constructor.
return personPidsToMatch.stream()
.map(baseResourcePid -> {
Optional<EmpiLink> duplicateLink = myEmpiLinkDaoSvc.getEmpiLinksByPersonPidTargetPidAndMatchResult(baseResourcePid, incomingPersonPid, EmpiMatchResultEnum.POSSIBLE_DUPLICATE);
Optional<MdmLink> duplicateLink = myMdmLinkDaoSvc.getMdmLinksByPersonPidTargetPidAndMatchResult(baseResourcePid, incomingPersonPid, MdmMatchResultEnum.POSSIBLE_DUPLICATE);
if (!duplicateLink.isPresent()) {
duplicateLink = myEmpiLinkDaoSvc.getEmpiLinksByPersonPidTargetPidAndMatchResult(incomingPersonPid, baseResourcePid, EmpiMatchResultEnum.POSSIBLE_DUPLICATE);
duplicateLink = myMdmLinkDaoSvc.getMdmLinksByPersonPidTargetPidAndMatchResult(incomingPersonPid, baseResourcePid, MdmMatchResultEnum.POSSIBLE_DUPLICATE);
}
return duplicateLink;
}).allMatch(Optional::isPresent);
@ -46,16 +46,16 @@ public class IsPossibleDuplicateOf extends BaseSourceResourceMatcher {
@Override
public void describeTo(Description theDescription) {
theDescription.appendText("Person was not duplicate of Person/" + incomingPersonPid);
theDescription.appendText("Resource was not duplicate of Resource/" + incomingPersonPid);
}
@Override
protected void describeMismatchSafely(IAnyResource item, Description mismatchDescription) {
super.describeMismatchSafely(item, mismatchDescription);
mismatchDescription.appendText("No Empi Link With POSSIBLE_DUPLICATE was found");
mismatchDescription.appendText("No MdmLink With POSSIBLE_DUPLICATE was found");
}
public static Matcher<IAnyResource> possibleDuplicateOf(IdHelperService theIdHelperService, EmpiLinkDaoSvc theEmpiLinkDaoSvc, IAnyResource... theBaseResource) {
return new IsPossibleDuplicateOf(theIdHelperService, theEmpiLinkDaoSvc, theBaseResource);
public static Matcher<IAnyResource> possibleDuplicateOf(IdHelperService theIdHelperService, MdmLinkDaoSvc theMdmLinkDaoSvc, IAnyResource... theBaseResource) {
return new IsPossibleDuplicateOf(theIdHelperService, theMdmLinkDaoSvc, theBaseResource);
}
}

View File

@ -1,7 +1,7 @@
package ca.uhn.fhir.jpa.empi.matcher;
package ca.uhn.fhir.jpa.mdm.matcher;
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
import ca.uhn.fhir.jpa.empi.dao.EmpiLinkDaoSvc;
import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hl7.fhir.instance.model.api.IAnyResource;
@ -19,8 +19,8 @@ public class IsPossibleLinkedTo extends BaseSourceResourceMatcher {
private List<Long> baseResourcePersonPids;
private Long incomingResourcePersonPid;
protected IsPossibleLinkedTo(IdHelperService theIdHelperService, EmpiLinkDaoSvc theEmpiLinkDaoSvc, IAnyResource... theTargetResources) {
super(theIdHelperService, theEmpiLinkDaoSvc, theTargetResources);
protected IsPossibleLinkedTo(IdHelperService theIdHelperService, MdmLinkDaoSvc theMdmLinkDaoSvc, IAnyResource... theTargetResources) {
super(theIdHelperService, theMdmLinkDaoSvc, theTargetResources);
}
@Override
@ -41,7 +41,7 @@ public class IsPossibleLinkedTo extends BaseSourceResourceMatcher {
public void describeTo(Description theDescription) {
}
public static Matcher<IAnyResource> possibleLinkedTo(IdHelperService theIdHelperService, EmpiLinkDaoSvc theEmpiLinkDaoSvc, IAnyResource... theBaseResource) {
return new IsPossibleLinkedTo(theIdHelperService, theEmpiLinkDaoSvc, theBaseResource);
public static Matcher<IAnyResource> possibleLinkedTo(IdHelperService theIdHelperService, MdmLinkDaoSvc theMdmLinkDaoSvc, IAnyResource... theBaseResource) {
return new IsPossibleLinkedTo(theIdHelperService, theMdmLinkDaoSvc, theBaseResource);
}
}

View File

@ -1,9 +1,9 @@
package ca.uhn.fhir.jpa.empi.matcher;
package ca.uhn.fhir.jpa.mdm.matcher;
import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
import ca.uhn.fhir.jpa.empi.dao.EmpiLinkDaoSvc;
import ca.uhn.fhir.jpa.entity.EmpiLink;
import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc;
import ca.uhn.fhir.jpa.entity.MdmLink;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hl7.fhir.instance.model.api.IAnyResource;
@ -13,17 +13,17 @@ import java.util.Objects;
import java.util.stream.Collectors;
/**
* Matcher with tells us if there is an EmpiLink with between these two resources that are considered POSSIBLE_MATCH
* Matcher with tells us if there is an MdmLink with between these two resources that are considered POSSIBLE_MATCH
*/
public class IsPossibleMatchWith extends BaseSourceResourceMatcher {
protected IsPossibleMatchWith(IdHelperService theIdHelperService, EmpiLinkDaoSvc theEmpiLinkDaoSvc, IAnyResource... theBaseResource) {
super(theIdHelperService, theEmpiLinkDaoSvc, theBaseResource);
protected IsPossibleMatchWith(IdHelperService theIdHelperService, MdmLinkDaoSvc theMdmLinkDaoSvc, IAnyResource... theBaseResource) {
super(theIdHelperService, theMdmLinkDaoSvc, theBaseResource);
}
@Override
protected boolean matchesSafely(IAnyResource theIncomingResource) {
List<EmpiLink> empiLinks = getEmpiLinksForTarget(theIncomingResource, EmpiMatchResultEnum.POSSIBLE_MATCH);
List<MdmLink> mdmLinks = getEmpiLinksForTarget(theIncomingResource, MdmMatchResultEnum.POSSIBLE_MATCH);
List<Long> personPidsToMatch = myBaseResources.stream()
.map(this::getMatchedResourcePidFromResource)
@ -36,7 +36,7 @@ public class IsPossibleMatchWith extends BaseSourceResourceMatcher {
.collect(Collectors.toList());
}
List<Long> empiLinkSourcePersonPids = empiLinks.stream().map(EmpiLink::getSourceResourcePid).collect(Collectors.toList());
List<Long> empiLinkSourcePersonPids = mdmLinks.stream().map(MdmLink::getGoldenResourcePid).collect(Collectors.toList());
return empiLinkSourcePersonPids.containsAll(personPidsToMatch);
}
@ -51,7 +51,7 @@ public class IsPossibleMatchWith extends BaseSourceResourceMatcher {
mismatchDescription.appendText("No Empi Link With POSSIBLE_MATCH was found");
}
public static Matcher<IAnyResource> possibleMatchWith(IdHelperService theIdHelperService, EmpiLinkDaoSvc theEmpiLinkDaoSvc, IAnyResource... theBaseResource) {
return new IsPossibleMatchWith(theIdHelperService, theEmpiLinkDaoSvc, theBaseResource);
public static Matcher<IAnyResource> possibleMatchWith(IdHelperService theIdHelperService, MdmLinkDaoSvc theMdmLinkDaoSvc, IAnyResource... theBaseResource) {
return new IsPossibleMatchWith(theIdHelperService, theMdmLinkDaoSvc, theBaseResource);
}
}

View File

@ -1,7 +1,7 @@
package ca.uhn.fhir.jpa.empi.matcher;
package ca.uhn.fhir.jpa.mdm.matcher;
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
import ca.uhn.fhir.jpa.empi.dao.EmpiLinkDaoSvc;
import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hl7.fhir.instance.model.api.IAnyResource;
@ -14,8 +14,8 @@ public class IsSameSourceResourceAs extends BaseSourceResourceMatcher {
private List<Long> sourceResourcePidsToMatch;
private Long incomingSourceResourcePid;
public IsSameSourceResourceAs(IdHelperService theIdHelperService, EmpiLinkDaoSvc theEmpiLinkDaoSvc, IAnyResource... theBaseResource) {
super(theIdHelperService, theEmpiLinkDaoSvc, theBaseResource);
public IsSameSourceResourceAs(IdHelperService theIdHelperService, MdmLinkDaoSvc theMdmLinkDaoSvc, IAnyResource... theBaseResource) {
super(theIdHelperService, theMdmLinkDaoSvc, theBaseResource);
}
@Override
@ -40,7 +40,7 @@ public class IsSameSourceResourceAs extends BaseSourceResourceMatcher {
mismatchDescription.appendText(String.format(" was actually linked to %s/%s", myTargetType, incomingSourceResourcePid));
}
public static Matcher<IAnyResource> sameSourceResourceAs(IdHelperService theIdHelperService, EmpiLinkDaoSvc theEmpiLinkDaoSvc, IAnyResource... theBaseResource) {
return new IsSameSourceResourceAs(theIdHelperService, theEmpiLinkDaoSvc, theBaseResource);
public static Matcher<IAnyResource> sameSourceResourceAs(IdHelperService theIdHelperService, MdmLinkDaoSvc theMdmLinkDaoSvc, IAnyResource... theBaseResource) {
return new IsSameSourceResourceAs(theIdHelperService, theMdmLinkDaoSvc, theBaseResource);
}
}

View File

@ -1,9 +1,9 @@
package ca.uhn.fhir.jpa.empi.provider;
package ca.uhn.fhir.jpa.mdm.provider;
import ca.uhn.fhir.empi.api.EmpiLinkSourceEnum;
import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.entity.EmpiLink;
import ca.uhn.fhir.jpa.entity.MdmLink;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.StringType;
@ -18,17 +18,17 @@ import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
public abstract class BaseLinkR4Test extends BaseProviderR4Test {
protected static final StringType NO_MATCH_RESULT = new StringType(EmpiMatchResultEnum.NO_MATCH.name());
protected static final StringType MATCH_RESULT = new StringType(EmpiMatchResultEnum.MATCH.name());
protected static final StringType POSSIBLE_MATCH_RESULT = new StringType(EmpiMatchResultEnum.POSSIBLE_MATCH.name());
protected static final StringType POSSIBLE_DUPLICATE_RESULT = new StringType(EmpiMatchResultEnum.POSSIBLE_DUPLICATE.name());
protected static final StringType NO_MATCH_RESULT = new StringType(MdmMatchResultEnum.NO_MATCH.name());
protected static final StringType MATCH_RESULT = new StringType(MdmMatchResultEnum.MATCH.name());
protected static final StringType POSSIBLE_MATCH_RESULT = new StringType(MdmMatchResultEnum.POSSIBLE_MATCH.name());
protected static final StringType POSSIBLE_DUPLICATE_RESULT = new StringType(MdmMatchResultEnum.POSSIBLE_DUPLICATE.name());
@Autowired
DaoConfig myDaoConfig;
protected Patient myPatient;
protected IAnyResource mySourcePatient;
protected EmpiLink myLink;
protected MdmLink myLink;
protected StringType myPatientId;
protected StringType mySourcePatientId;
protected StringType myVersionlessGodlenResourceId;
@ -47,9 +47,9 @@ public abstract class BaseLinkR4Test extends BaseProviderR4Test {
myLink = getOnlyPatientLink();
// Tests require our initial link to be a POSSIBLE_MATCH
myLink.setMatchResult(EmpiMatchResultEnum.POSSIBLE_MATCH);
myLink.setMatchResult(MdmMatchResultEnum.POSSIBLE_MATCH);
saveLink(myLink);
assertEquals(EmpiLinkSourceEnum.AUTO, myLink.getLinkSource());
assertEquals(MdmLinkSourceEnum.AUTO, myLink.getLinkSource());
myDaoConfig.setExpungeEnabled(true);
}
@ -60,12 +60,12 @@ public abstract class BaseLinkR4Test extends BaseProviderR4Test {
}
@Nonnull
protected EmpiLink getOnlyPatientLink() {
return myEmpiLinkDaoSvc.findEmpiLinkByTarget(myPatient).get();
protected MdmLink getOnlyPatientLink() {
return myMdmLinkDaoSvc.findMdmLinkByTarget(myPatient).get();
}
@Nonnull
protected List<EmpiLink> getPatientLinks() {
return myEmpiLinkDaoSvc.findEmpiLinksByTarget(myPatient);
protected List<MdmLink> getPatientLinks() {
return myMdmLinkDaoSvc.findMdmLinksByTarget(myPatient);
}
}

View File

@ -0,0 +1,53 @@
package ca.uhn.fhir.jpa.mdm.provider;
import ca.uhn.fhir.mdm.api.IMdmControllerSvc;
import ca.uhn.fhir.mdm.api.IMdmExpungeSvc;
import ca.uhn.fhir.mdm.api.IMdmMatchFinderSvc;
import ca.uhn.fhir.mdm.api.IMdmSubmitSvc;
import ca.uhn.fhir.mdm.provider.MdmProviderR4;
import ca.uhn.fhir.mdm.rules.config.MdmSettings;
import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
import com.google.common.base.Charsets;
import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import java.io.IOException;
public abstract class BaseProviderR4Test extends BaseMdmR4Test {
MdmProviderR4 myMdmProviderR4;
@Autowired
private IMdmMatchFinderSvc myMdmMatchFinderSvc;
@Autowired
private IMdmControllerSvc myMdmControllerSvc;
@Autowired
private IMdmExpungeSvc myMdmExpungeSvc;
@Autowired
private IMdmSubmitSvc myMdmSubmitSvc;
@Autowired
private MdmSettings myMdmSettings;
private String defaultScript;
protected void setEmpiRuleJson(String theString) throws IOException {
DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
Resource resource = resourceLoader.getResource(theString);
String json = IOUtils.toString(resource.getInputStream(), Charsets.UTF_8);
myMdmSettings.setScriptText(json);
}
@BeforeEach
public void before() {
myMdmProviderR4 = new MdmProviderR4(myFhirContext, myMdmControllerSvc, myMdmMatchFinderSvc, myMdmExpungeSvc, myMdmSubmitSvc);
defaultScript = myMdmSettings.getScriptText();
}
@AfterEach
public void after() throws IOException {
super.after();
myMdmSettings.setScriptText(defaultScript);
}
}

View File

@ -1,4 +1,4 @@
package ca.uhn.fhir.jpa.empi.provider;
package ca.uhn.fhir.jpa.mdm.provider;
import ca.uhn.fhir.interceptor.api.IInterceptorService;
import ca.uhn.fhir.interceptor.api.Pointcut;
@ -23,7 +23,7 @@ import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.fail;
public class EmpiProviderBatchR4Test extends BaseLinkR4Test {
public class MdmProviderBatchR4Test extends BaseLinkR4Test {
public static final String ORGANIZATION_DUMMY = "Organization/dummy";
protected Practitioner myPractitioner;
@ -38,7 +38,7 @@ public class EmpiProviderBatchR4Test extends BaseLinkR4Test {
@Autowired
IInterceptorService myInterceptorService;
PointcutLatch afterEmpiLatch = new PointcutLatch(Pointcut.EMPI_AFTER_PERSISTED_RESOURCE_CHECKED);
PointcutLatch afterMdmLatch = new PointcutLatch(Pointcut.MDM_AFTER_PERSISTED_RESOURCE_CHECKED);
@BeforeEach
@ -59,44 +59,44 @@ public class EmpiProviderBatchR4Test extends BaseLinkR4Test {
myGoldenMedicationId = new StringType(myGoldenMedication.getIdElement().getValue());
myInterceptorService.registerAnonymousInterceptor(Pointcut.EMPI_AFTER_PERSISTED_RESOURCE_CHECKED, afterEmpiLatch);
myInterceptorService.registerAnonymousInterceptor(Pointcut.MDM_AFTER_PERSISTED_RESOURCE_CHECKED, afterMdmLatch);
}
@AfterEach
public void after() throws IOException {
myInterceptorService.unregisterInterceptor(afterEmpiLatch);
myInterceptorService.unregisterInterceptor(afterMdmLatch);
super.after();
}
@Test
public void testBatchRunOnAllMedications() throws InterruptedException {
StringType criteria = null;
myEmpiProviderR4.clearEmpiLinks(null, myRequestDetails);
myMdmProviderR4.clearMdmLinks(null, myRequestDetails);
afterEmpiLatch.runWithExpectedCount(1, () -> myEmpiProviderR4.empiBatchOnAllTargets(new StringType("Medication"), criteria, null));
afterMdmLatch.runWithExpectedCount(1, () -> myMdmProviderR4.empiBatchOnAllTargets(new StringType("Medication"), criteria, null));
assertLinkCount(1);
}
@Test
public void testBatchRunOnAllPractitioners() throws InterruptedException {
StringType criteria = null;
myEmpiProviderR4.clearEmpiLinks(null, myRequestDetails);
myMdmProviderR4.clearMdmLinks(null, myRequestDetails);
afterEmpiLatch.runWithExpectedCount(1, () -> myEmpiProviderR4.empiBatchPractitionerType(criteria, null));
afterMdmLatch.runWithExpectedCount(1, () -> myMdmProviderR4.empiBatchPractitionerType(criteria, null));
assertLinkCount(1);
}
@Test
public void testBatchRunOnSpecificPractitioner() throws InterruptedException {
myEmpiProviderR4.clearEmpiLinks(null, myRequestDetails);
afterEmpiLatch.runWithExpectedCount(1, () -> myEmpiProviderR4.empiBatchPractitionerInstance(myPractitioner.getIdElement(), null));
myMdmProviderR4.clearMdmLinks(null, myRequestDetails);
afterMdmLatch.runWithExpectedCount(1, () -> myMdmProviderR4.empiBatchPractitionerInstance(myPractitioner.getIdElement(), null));
assertLinkCount(1);
}
@Test
public void testBatchRunOnNonExistentSpecificPractitioner() {
myEmpiProviderR4.clearEmpiLinks(null, myRequestDetails);
myMdmProviderR4.clearMdmLinks(null, myRequestDetails);
try {
myEmpiProviderR4.empiBatchPractitionerInstance(new IdType("Practitioner/999"), null);
myMdmProviderR4.empiBatchPractitionerInstance(new IdType("Practitioner/999"), null);
fail();
} catch (ResourceNotFoundException e){}
}
@ -105,25 +105,25 @@ public class EmpiProviderBatchR4Test extends BaseLinkR4Test {
public void testBatchRunOnAllPatients() throws InterruptedException {
assertLinkCount(3);
StringType criteria = null;
myEmpiProviderR4.clearEmpiLinks(null, myRequestDetails);
afterEmpiLatch.runWithExpectedCount(1, () -> myEmpiProviderR4.empiBatchPatientType(criteria, null));
myMdmProviderR4.clearMdmLinks(null, myRequestDetails);
afterMdmLatch.runWithExpectedCount(1, () -> myMdmProviderR4.empiBatchPatientType(criteria, null));
assertLinkCount(1);
}
@Test
public void testBatchRunOnSpecificPatient() throws InterruptedException {
assertLinkCount(3);
myEmpiProviderR4.clearEmpiLinks(null, myRequestDetails);
afterEmpiLatch.runWithExpectedCount(1, () -> myEmpiProviderR4.empiBatchPatientInstance(myPatient.getIdElement(), null));
myMdmProviderR4.clearMdmLinks(null, myRequestDetails);
afterMdmLatch.runWithExpectedCount(1, () -> myMdmProviderR4.empiBatchPatientInstance(myPatient.getIdElement(), null));
assertLinkCount(1);
}
@Test
public void testBatchRunOnNonExistentSpecificPatient() {
assertLinkCount(3);
myEmpiProviderR4.clearEmpiLinks(null, myRequestDetails);
myMdmProviderR4.clearMdmLinks(null, myRequestDetails);
try {
myEmpiProviderR4.empiBatchPatientInstance(new IdType("Patient/999"), null);
myMdmProviderR4.empiBatchPatientInstance(new IdType("Patient/999"), null);
fail();
} catch (ResourceNotFoundException e){}
}
@ -132,9 +132,9 @@ public class EmpiProviderBatchR4Test extends BaseLinkR4Test {
public void testBatchRunOnAllTypes() throws InterruptedException {
assertLinkCount(3);
StringType criteria = new StringType("");
myEmpiProviderR4.clearEmpiLinks(null, myRequestDetails);
afterEmpiLatch.runWithExpectedCount(3, () -> {
myEmpiProviderR4.empiBatchOnAllTargets(null, criteria, null);
myMdmProviderR4.clearMdmLinks(null, myRequestDetails);
afterMdmLatch.runWithExpectedCount(3, () -> {
myMdmProviderR4.empiBatchOnAllTargets(null, criteria, null);
});
assertLinkCount(3);
}
@ -143,10 +143,10 @@ public class EmpiProviderBatchR4Test extends BaseLinkR4Test {
public void testBatchRunOnAllTypesWithInvalidCriteria() {
assertLinkCount(3);
StringType criteria = new StringType("death-date=2020-06-01");
myEmpiProviderR4.clearEmpiLinks(null, myRequestDetails);
myMdmProviderR4.clearMdmLinks(null, myRequestDetails);
try {
myEmpiProviderR4.empiBatchPractitionerType(criteria, null);
myMdmProviderR4.empiBatchPractitionerType(criteria, null);
fail();
} catch(InvalidRequestException e) {
assertThat(e.getMessage(), is(equalTo("Failed to parse match URL[death-date=2020-06-01] - Resource type Practitioner does not have a parameter with name: death-date")));

View File

@ -1,8 +1,8 @@
package ca.uhn.fhir.jpa.empi.provider;
package ca.uhn.fhir.jpa.mdm.provider;
import ca.uhn.fhir.empi.api.EmpiConstants;
import ca.uhn.fhir.empi.api.EmpiLinkSourceEnum;
import ca.uhn.fhir.jpa.entity.EmpiLink;
import ca.uhn.fhir.mdm.api.MdmConstants;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.jpa.entity.MdmLink;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
@ -20,7 +20,7 @@ import org.junit.jupiter.api.Test;
import javax.annotation.Nonnull;
import java.util.List;
import static ca.uhn.fhir.empi.api.EmpiMatchOutcome.POSSIBLE_MATCH;
import static ca.uhn.fhir.mdm.api.MdmMatchOutcome.POSSIBLE_MATCH;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
@ -29,7 +29,7 @@ import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.jupiter.api.Assertions.fail;
public class EmpiProviderClearLinkR4Test extends BaseLinkR4Test {
public class MdmProviderClearLinkR4Test extends BaseLinkR4Test {
protected Practitioner myPractitioner;
protected StringType myPractitionerId;
protected IAnyResource myPractitionerSourceResource;
@ -47,7 +47,7 @@ public class EmpiProviderClearLinkR4Test extends BaseLinkR4Test {
@Test
public void testClearAllLinks() {
assertLinkCount(2);
myEmpiProviderR4.clearEmpiLinks(null, myRequestDetails);
myMdmProviderR4.clearMdmLinks(null, myRequestDetails);
assertNoLinksExist();
}
@ -69,7 +69,7 @@ public class EmpiProviderClearLinkR4Test extends BaseLinkR4Test {
assertLinkCount(2);
Patient read = myPatientDao.read(new IdDt(mySourcePatientId.getValueAsString()).toVersionless());
assertThat(read, is(notNullValue()));
myEmpiProviderR4.clearEmpiLinks(new StringType("Patient"), myRequestDetails);
myMdmProviderR4.clearMdmLinks(new StringType("Patient"), myRequestDetails);
assertNoPatientLinksExist();
try {
myPatientDao.read(new IdDt(mySourcePatientId.getValueAsString()).toVersionless());
@ -86,7 +86,7 @@ public class EmpiProviderClearLinkR4Test extends BaseLinkR4Test {
Patient patientAndUpdateLinks = createPatientAndUpdateLinks(buildJanePatient());
IAnyResource person = getGoldenResourceFromTargetResource(patientAndUpdateLinks);
assertThat(person, is(notNullValue()));
myEmpiProviderR4.clearEmpiLinks(null, myRequestDetails);
myMdmProviderR4.clearMdmLinks(null, myRequestDetails);
assertNoPatientLinksExist();
person = getGoldenResourceFromTargetResource(patientAndUpdateLinks);
assertThat(person, is(nullValue()));
@ -103,7 +103,7 @@ public class EmpiProviderClearLinkR4Test extends BaseLinkR4Test {
linkPersons(personFromTarget, personFromTarget2);
//SUT
myEmpiProviderR4.clearEmpiLinks(null, myRequestDetails);
myMdmProviderR4.clearMdmLinks(null, myRequestDetails);
assertNoPatientLinksExist();
IBundleProvider search = myPatientDao.search(buildSourceResourceParameterMap());
@ -115,7 +115,7 @@ public class EmpiProviderClearLinkR4Test extends BaseLinkR4Test {
* @return
*/
private SearchParameterMap buildSourceResourceParameterMap() {
return new SearchParameterMap().setLoadSynchronous(true).add("_tag", new TokenParam(EmpiConstants.SYSTEM_MDM_MANAGED, EmpiConstants.CODE_HAPI_MDM_MANAGED));
return new SearchParameterMap().setLoadSynchronous(true).add("_tag", new TokenParam(MdmConstants.SYSTEM_MDM_MANAGED, MdmConstants.CODE_HAPI_MDM_MANAGED));
}
@Test
@ -134,7 +134,7 @@ public class EmpiProviderClearLinkR4Test extends BaseLinkR4Test {
linkPersons(personFromTarget2, personFromTarget);
//SUT
Parameters parameters = myEmpiProviderR4.clearEmpiLinks(null, myRequestDetails);
Parameters parameters = myMdmProviderR4.clearMdmLinks(null, myRequestDetails);
printLinks();
@ -148,7 +148,7 @@ public class EmpiProviderClearLinkR4Test extends BaseLinkR4Test {
private void linkPersons(IAnyResource theGoldenResource, IAnyResource theTargetResource) {
// TODO NG - Should be ok to leave this - not really
// throw new UnsupportedOperationException("We need to fix this!");
myEmpiLinkDaoSvc.createOrUpdateLinkEntity(theGoldenResource, theTargetResource, POSSIBLE_MATCH, EmpiLinkSourceEnum.AUTO, createContextForCreate("Patient"));
myMdmLinkDaoSvc.createOrUpdateLinkEntity(theGoldenResource, theTargetResource, POSSIBLE_MATCH, MdmLinkSourceEnum.AUTO, createContextForCreate("Patient"));
}
@Test
@ -156,7 +156,7 @@ public class EmpiProviderClearLinkR4Test extends BaseLinkR4Test {
assertLinkCount(2);
Practitioner read = myPractitionerDao.read(new IdDt(myPractitionerSourceResourceId.getValueAsString()).toVersionless());
assertThat(read, is(notNullValue()));
myEmpiProviderR4.clearEmpiLinks(new StringType("Practitioner"), myRequestDetails);
myMdmProviderR4.clearMdmLinks(new StringType("Practitioner"), myRequestDetails);
assertNoPractitionerLinksExist();
try {
myPractitionerDao.read(new IdDt(myPractitionerSourceResourceId.getValueAsString()).toVersionless());
@ -167,7 +167,7 @@ public class EmpiProviderClearLinkR4Test extends BaseLinkR4Test {
@Test
public void testClearInvalidTargetType() {
try {
myEmpiProviderR4.clearEmpiLinks(new StringType("Observation"), myRequestDetails);
myMdmProviderR4.clearMdmLinks(new StringType("Observation"), myRequestDetails);
fail();
} catch (InvalidRequestException e) {
assertThat(e.getMessage(), is(equalTo("$mdm-clear does not support resource type: Observation")));
@ -175,7 +175,7 @@ public class EmpiProviderClearLinkR4Test extends BaseLinkR4Test {
}
@Nonnull
protected List<EmpiLink> getPractitionerLinks() {
return myEmpiLinkDaoSvc.findEmpiLinksByTarget(myPractitioner);
protected List<MdmLink> getPractitionerLinks() {
return myMdmLinkDaoSvc.findMdmLinksByTarget(myPractitioner);
}
}

View File

@ -1,11 +1,10 @@
package ca.uhn.fhir.jpa.empi.provider;
package ca.uhn.fhir.jpa.mdm.provider;
import ca.uhn.fhir.empi.api.EmpiConstants;
import ca.uhn.fhir.mdm.api.MdmConstants;
import com.google.common.collect.Ordering;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.Extension;
import org.hl7.fhir.r4.model.Medication;
import org.hl7.fhir.r4.model.Organization;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.StringType;
import org.hl7.fhir.r4.model.codesystems.MatchGrade;
@ -21,9 +20,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class EmpiProviderMatchR4Test extends BaseProviderR4Test {
public class MdmProviderMatchR4Test extends BaseProviderR4Test {
private static final Logger ourLog = LoggerFactory.getLogger(EmpiProviderMatchR4Test.class);
private static final Logger ourLog = LoggerFactory.getLogger(MdmProviderMatchR4Test.class);
public static final String NAME_GIVEN_JANET = NAME_GIVEN_JANE + "t";
@ -31,7 +30,7 @@ public class EmpiProviderMatchR4Test extends BaseProviderR4Test {
@BeforeEach
public void before() {
super.before();
super.loadEmpiSearchParameters();
super.loadMdmSearchParameters();
}
@Test
@ -41,7 +40,7 @@ public class EmpiProviderMatchR4Test extends BaseProviderR4Test {
Patient createdJane = createPatient(jane);
Patient newJane = buildJanePatient();
Bundle result = myEmpiProviderR4.match(newJane);
Bundle result = myMdmProviderR4.match(newJane);
assertEquals(1, result.getEntry().size());
Bundle.BundleEntryComponent entry0 = result.getEntry().get(0);
@ -51,7 +50,7 @@ public class EmpiProviderMatchR4Test extends BaseProviderR4Test {
assertEquals(Bundle.SearchEntryMode.MATCH, searchComponent.getMode());
assertEquals(2.0 / 3.0, searchComponent.getScore().doubleValue(), 0.01);
Extension matchGradeExtension = searchComponent.getExtensionByUrl(EmpiConstants.FIHR_STRUCTURE_DEF_MATCH_GRADE_URL_NAMESPACE);
Extension matchGradeExtension = searchComponent.getExtensionByUrl(MdmConstants.FIHR_STRUCTURE_DEF_MATCH_GRADE_URL_NAMESPACE);
assertNotNull(matchGradeExtension);
assertEquals(MatchGrade.CERTAIN.toCode(), matchGradeExtension.getValue().toString());
}
@ -65,7 +64,7 @@ public class EmpiProviderMatchR4Test extends BaseProviderR4Test {
Medication createdMedication = createMedication(medication);
Medication newMedication = buildMedication("Organization/mfr");
Bundle result = myEmpiProviderR4.serverMatch(newMedication, new StringType("Medication"));
Bundle result = myMdmProviderR4.serverMatch(newMedication, new StringType("Medication"));
assertEquals(1, result.getEntry().size());
Bundle.BundleEntryComponent entry0 = result.getEntry().get(0);
@ -76,7 +75,7 @@ public class EmpiProviderMatchR4Test extends BaseProviderR4Test {
//Since there is only
assertEquals(1.0 / 1.0, searchComponent.getScore().doubleValue(), 0.01);
Extension matchGradeExtension = searchComponent.getExtensionByUrl(EmpiConstants.FIHR_STRUCTURE_DEF_MATCH_GRADE_URL_NAMESPACE);
Extension matchGradeExtension = searchComponent.getExtensionByUrl(MdmConstants.FIHR_STRUCTURE_DEF_MATCH_GRADE_URL_NAMESPACE);
assertNotNull(matchGradeExtension);
assertEquals(MatchGrade.CERTAIN.toCode(), matchGradeExtension.getValue().toString());
@ -90,7 +89,7 @@ public class EmpiProviderMatchR4Test extends BaseProviderR4Test {
Patient createdJane = createPatient(jane);
Patient newJane = buildJanePatient();
Bundle result = myEmpiProviderR4.serverMatch(newJane, new StringType("Patient"));
Bundle result = myMdmProviderR4.serverMatch(newJane, new StringType("Patient"));
assertEquals(1, result.getEntry().size());
Bundle.BundleEntryComponent entry0 = result.getEntry().get(0);
@ -100,7 +99,7 @@ public class EmpiProviderMatchR4Test extends BaseProviderR4Test {
assertEquals(Bundle.SearchEntryMode.MATCH, searchComponent.getMode());
assertEquals(2.0 / 3.0, searchComponent.getScore().doubleValue(), 0.01);
Extension matchGradeExtension = searchComponent.getExtensionByUrl(EmpiConstants.FIHR_STRUCTURE_DEF_MATCH_GRADE_URL_NAMESPACE);
Extension matchGradeExtension = searchComponent.getExtensionByUrl(MdmConstants.FIHR_STRUCTURE_DEF_MATCH_GRADE_URL_NAMESPACE);
assertNotNull(matchGradeExtension);
assertEquals(MatchGrade.CERTAIN.toCode(), matchGradeExtension.getValue().toString());
}
@ -116,7 +115,7 @@ public class EmpiProviderMatchR4Test extends BaseProviderR4Test {
Patient newJane = buildJanePatient();
Bundle result = myEmpiProviderR4.match(newJane);
Bundle result = myMdmProviderR4.match(newJane);
assertEquals(2, result.getEntry().size());
Bundle.BundleEntryComponent entry0 = result.getEntry().get(0);
@ -140,7 +139,7 @@ public class EmpiProviderMatchR4Test extends BaseProviderR4Test {
Patient paul = buildPaulPatient();
paul.setActive(true);
Bundle result = myEmpiProviderR4.match(paul);
Bundle result = myMdmProviderR4.match(paul);
assertEquals(0, result.getEntry().size());
}
@ -152,7 +151,7 @@ public class EmpiProviderMatchR4Test extends BaseProviderR4Test {
Patient createdJane = createPatient(jane);
Patient newJane = buildJanePatient();
Bundle result = myEmpiProviderR4.match(newJane);
Bundle result = myMdmProviderR4.match(newJane);
assertEquals(1, result.getEntry().size());
assertEquals(createdJane.getId(), result.getEntryFirstRep().getResource().getId());
}

View File

@ -1,22 +1,17 @@
package ca.uhn.fhir.jpa.empi.provider;
package ca.uhn.fhir.jpa.mdm.provider;
import ca.uhn.fhir.empi.api.EmpiLinkSourceEnum;
import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
import ca.uhn.fhir.empi.util.AssuranceLevelUtil;
import ca.uhn.fhir.empi.util.EmpiUtil;
import ca.uhn.fhir.jpa.entity.EmpiLink;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.mdm.util.MdmUtil;
import ca.uhn.fhir.jpa.entity.MdmLink;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.Identifier;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.StringType;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Optional;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
@ -24,7 +19,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
public class EmpiProviderMergePersonsR4Test extends BaseProviderR4Test {
public class MdmProviderMergePersonsR4Test extends BaseProviderR4Test {
private Patient myFromSourcePatient;
private StringType myFromSourcePatientId;
@ -35,7 +30,7 @@ public class EmpiProviderMergePersonsR4Test extends BaseProviderR4Test {
@BeforeEach
public void before() {
super.before();
super.loadEmpiSearchParameters();
super.loadMdmSearchParameters();
myFromSourcePatient = createGoldenPatient();
myFromSourcePatientId = new StringType(myFromSourcePatient.getIdElement().getValue());
@ -45,10 +40,10 @@ public class EmpiProviderMergePersonsR4Test extends BaseProviderR4Test {
@Test
public void testMerge() {
Patient mergedSourcePatient = (Patient) myEmpiProviderR4.mergeGoldenResources(myFromSourcePatientId,
Patient mergedSourcePatient = (Patient) myMdmProviderR4.mergeGoldenResources(myFromSourcePatientId,
myToSourcePatientId, myRequestDetails);
assertTrue(EmpiUtil.isGoldenRecord(myFromSourcePatient));
assertTrue(MdmUtil.isGoldenRecord(myFromSourcePatient));
assertEquals(myToSourcePatient.getIdElement(), mergedSourcePatient.getIdElement());
assertThat(mergedSourcePatient, is(sameSourceResourceAs(myToSourcePatient)));
assertEquals(1, getAllRedirectedGoldenPatients().size());
@ -56,20 +51,20 @@ public class EmpiProviderMergePersonsR4Test extends BaseProviderR4Test {
Patient fromSourcePatient = myPatientDao.read(myFromSourcePatient.getIdElement().toUnqualifiedVersionless());
assertThat(fromSourcePatient.getActive(), is(false));
assertTrue(EmpiUtil.isGoldenRecordRedirected(fromSourcePatient));
assertTrue(MdmUtil.isGoldenRecordRedirected(fromSourcePatient));
//TODO GGG eventually this will need to check a redirect... this is a hack which doesnt work
// Optional<Identifier> redirect = fromSourcePatient.getIdentifier().stream().filter(theIdentifier -> theIdentifier.getSystem().equals("REDIRECT")).findFirst();
// assertThat(redirect.get().getValue(), is(equalTo(myToSourcePatient.getIdElement().toUnqualified().getValue())));
List<EmpiLink> links = myEmpiLinkDaoSvc.findEmpiLinksByTarget(myFromSourcePatient);
List<MdmLink> links = myMdmLinkDaoSvc.findMdmLinksByTarget(myFromSourcePatient);
assertThat(links, hasSize(1));
EmpiLink link = links.get(0);
MdmLink link = links.get(0);
assertEquals(link.getTargetPid(), myFromSourcePatient.getIdElement().toUnqualifiedVersionless().getIdPartAsLong());
assertEquals(link.getSourceResourcePid(), myToSourcePatient.getIdElement().toUnqualifiedVersionless().getIdPartAsLong());
assertEquals(link.getMatchResult(), EmpiMatchResultEnum.REDIRECT);
assertEquals(link.getLinkSource(), EmpiLinkSourceEnum.MANUAL);
assertEquals(link.getGoldenResourcePid(), myToSourcePatient.getIdElement().toUnqualifiedVersionless().getIdPartAsLong());
assertEquals(link.getMatchResult(), MdmMatchResultEnum.REDIRECT);
assertEquals(link.getLinkSource(), MdmLinkSourceEnum.MANUAL);
// assertThat(links.get(0).getAssurance(), is (AssuranceLevelUtil.getAssuranceLevel(EmpiMatchResultEnum.REDIRECT, EmpiLinkSourceEnum.MANUAL).toR4()));
//List<Person.PersonLinkComponent> links = fromSourcePatient.getLink();
//assertThat(links, hasSize(1));
@ -82,7 +77,7 @@ public class EmpiProviderMergePersonsR4Test extends BaseProviderR4Test {
StringType fromPersonId = new StringType(createPatient().getIdElement().getValue());
StringType toPersonId = new StringType(createPatient().getIdElement().getValue());
try {
myEmpiProviderR4.mergeGoldenResources(fromPersonId, toPersonId, myRequestDetails);
myMdmProviderR4.mergeGoldenResources(fromPersonId, toPersonId, myRequestDetails);
fail();
} catch (InvalidRequestException e) {
assertEquals("Only MDM managed resources can be merged. MDM managed resources must have the HAPI-MDM tag.", e.getMessage());
@ -106,19 +101,19 @@ public class EmpiProviderMergePersonsR4Test extends BaseProviderR4Test {
@Test
public void testNullParams() {
try {
myEmpiProviderR4.mergeGoldenResources(null, null, myRequestDetails);
myMdmProviderR4.mergeGoldenResources(null, null, myRequestDetails);
fail();
} catch (InvalidRequestException e) {
assertEquals("fromGoldenResourceId cannot be null", e.getMessage());
}
try {
myEmpiProviderR4.mergeGoldenResources(null, myToSourcePatientId, myRequestDetails);
myMdmProviderR4.mergeGoldenResources(null, myToSourcePatientId, myRequestDetails);
fail();
} catch (InvalidRequestException e) {
assertEquals("fromGoldenResourceId cannot be null", e.getMessage());
}
try {
myEmpiProviderR4.mergeGoldenResources(myFromSourcePatientId, null, myRequestDetails);
myMdmProviderR4.mergeGoldenResources(myFromSourcePatientId, null, myRequestDetails);
fail();
} catch (InvalidRequestException e) {
assertEquals("toGoldenResourceId cannot be null", e.getMessage());
@ -146,21 +141,21 @@ public class EmpiProviderMergePersonsR4Test extends BaseProviderR4Test {
// }
try {
myEmpiProviderR4.mergeGoldenResources(new StringType("Person/1"), new StringType("Person/1"), myRequestDetails);
myMdmProviderR4.mergeGoldenResources(new StringType("Person/1"), new StringType("Person/1"), myRequestDetails);
fail();
} catch (InvalidRequestException e) {
assertEquals("fromPersonId must be different from toPersonId", e.getMessage());
}
try {
myEmpiProviderR4.mergeGoldenResources(new StringType("Person/abc"), myToSourcePatientId, myRequestDetails);
myMdmProviderR4.mergeGoldenResources(new StringType("Person/abc"), myToSourcePatientId, myRequestDetails);
fail();
} catch (ResourceNotFoundException e) {
assertEquals("Resource Person/abc is not known", e.getMessage());
}
try {
myEmpiProviderR4.mergeGoldenResources(myFromSourcePatientId, new StringType("Person/abc"), myRequestDetails);
myMdmProviderR4.mergeGoldenResources(myFromSourcePatientId, new StringType("Person/abc"), myRequestDetails);
fail();
} catch (ResourceNotFoundException e) {
assertEquals("Resource Person/abc is not known", e.getMessage());

View File

@ -1,8 +1,8 @@
package ca.uhn.fhir.jpa.empi.provider;
package ca.uhn.fhir.jpa.mdm.provider;
import ca.uhn.fhir.empi.api.EmpiLinkSourceEnum;
import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
import ca.uhn.fhir.jpa.entity.EmpiLink;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.jpa.entity.MdmLink;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import org.hl7.fhir.instance.model.api.IAnyResource;
@ -26,8 +26,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
public class EmpiProviderQueryLinkR4Test extends BaseLinkR4Test {
private static final Logger ourLog = LoggerFactory.getLogger(EmpiProviderQueryLinkR4Test.class);
public class MdmProviderQueryLinkR4Test extends BaseLinkR4Test {
private static final Logger ourLog = LoggerFactory.getLogger(MdmProviderQueryLinkR4Test.class);
private StringType myLinkSource;
private StringType myPerson1Id;
private StringType myPerson2Id;
@ -41,7 +41,7 @@ public class EmpiProviderQueryLinkR4Test extends BaseLinkR4Test {
createPatientAndUpdateLinks(buildJanePatient());
// Add a possible duplicate
myLinkSource = new StringType(EmpiLinkSourceEnum.AUTO.name());
myLinkSource = new StringType(MdmLinkSourceEnum.AUTO.name());
Patient sourcePatient1 = createGoldenPatient();
myPerson1Id = new StringType(sourcePatient1.getIdElement().toVersionless().getValue());
Long sourcePatient1Pid = myIdHelperService.getPidOrNull(sourcePatient1);
@ -49,19 +49,19 @@ public class EmpiProviderQueryLinkR4Test extends BaseLinkR4Test {
myPerson2Id = new StringType(sourcePatient2.getIdElement().toVersionless().getValue());
Long sourcePatient2Pid = myIdHelperService.getPidOrNull(sourcePatient2);
EmpiLink possibleDuplicateEmpiLink = myEmpiLinkDaoSvc.newEmpiLink().setGoldenResourcePid(sourcePatient1Pid).setTargetPid(sourcePatient2Pid).setMatchResult(EmpiMatchResultEnum.POSSIBLE_DUPLICATE).setLinkSource(EmpiLinkSourceEnum.AUTO);
saveLink(possibleDuplicateEmpiLink);
MdmLink possibleDuplicateMdmLink = myMdmLinkDaoSvc.newMdmLink().setGoldenResourcePid(sourcePatient1Pid).setTargetPid(sourcePatient2Pid).setMatchResult(MdmMatchResultEnum.POSSIBLE_DUPLICATE).setLinkSource(MdmLinkSourceEnum.AUTO);
saveLink(possibleDuplicateMdmLink);
}
@Test
public void testQueryLinkOneMatch() {
Parameters result = myEmpiProviderR4.queryLinks(mySourcePatientId, myPatientId, null, null, myRequestDetails);
Parameters result = myMdmProviderR4.queryLinks(mySourcePatientId, myPatientId, null, null, myRequestDetails);
ourLog.info(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(result));
List<Parameters.ParametersParameterComponent> list = result.getParameter();
assertThat(list, hasSize(1));
List<Parameters.ParametersParameterComponent> part = list.get(0).getPart();
assertEmpiLink(7, part, mySourcePatientId.getValue(), myPatientId.getValue(), EmpiMatchResultEnum.POSSIBLE_MATCH, "false", "true", null);
assertMdmLink(7, part, mySourcePatientId.getValue(), myPatientId.getValue(), MdmMatchResultEnum.POSSIBLE_MATCH, "false", "true", null);
}
@Test
@ -72,38 +72,38 @@ public class EmpiProviderQueryLinkR4Test extends BaseLinkR4Test {
IAnyResource person = getGoldenResourceFromTargetResource(patient);
IIdType personId = person.getIdElement().toVersionless();
Parameters result = myEmpiProviderR4.queryLinks(null, null, null, myLinkSource, myRequestDetails);
Parameters result = myMdmProviderR4.queryLinks(null, null, null, myLinkSource, myRequestDetails);
ourLog.info(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(result));
List<Parameters.ParametersParameterComponent> list = result.getParameter();
assertThat(list, hasSize(3));
List<Parameters.ParametersParameterComponent> part = list.get(2).getPart();
assertEmpiLink(7, part, personId.getValue(), patientId.getValue(), EmpiMatchResultEnum.MATCH, "false", "false", "2");
assertMdmLink(7, part, personId.getValue(), patientId.getValue(), MdmMatchResultEnum.MATCH, "false", "false", "2");
}
@Test
public void testQueryPossibleDuplicates() {
Parameters result = myEmpiProviderR4.getDuplicateGoldenResources(myRequestDetails);
Parameters result = myMdmProviderR4.getDuplicateGoldenResources(myRequestDetails);
ourLog.info(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(result));
List<Parameters.ParametersParameterComponent> list = result.getParameter();
assertThat(list, hasSize(1));
List<Parameters.ParametersParameterComponent> part = list.get(0).getPart();
assertEmpiLink(2, part, myPerson1Id.getValue(), myPerson2Id.getValue(), EmpiMatchResultEnum.POSSIBLE_DUPLICATE, "false", "false", null);
assertMdmLink(2, part, myPerson1Id.getValue(), myPerson2Id.getValue(), MdmMatchResultEnum.POSSIBLE_DUPLICATE, "false", "false", null);
}
@Test
public void testNotDuplicate() {
{
Parameters result = myEmpiProviderR4.getDuplicateGoldenResources(myRequestDetails);
Parameters result = myMdmProviderR4.getDuplicateGoldenResources(myRequestDetails);
List<Parameters.ParametersParameterComponent> list = result.getParameter();
assertThat(list, hasSize(1));
}
{
Parameters result = myEmpiProviderR4.notDuplicate(myPerson1Id, myPerson2Id, myRequestDetails);
Parameters result = myMdmProviderR4.notDuplicate(myPerson1Id, myPerson2Id, myRequestDetails);
ourLog.info(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(result));
assertEquals("success", result.getParameterFirstRep().getName());
assertTrue(((BooleanType) (result.getParameterFirstRep().getValue())).booleanValue());
}
Parameters result = myEmpiProviderR4.getDuplicateGoldenResources(myRequestDetails);
Parameters result = myMdmProviderR4.getDuplicateGoldenResources(myRequestDetails);
List<Parameters.ParametersParameterComponent> list = result.getParameter();
assertThat(list, hasSize(0));
}
@ -111,14 +111,14 @@ public class EmpiProviderQueryLinkR4Test extends BaseLinkR4Test {
@Test
public void testNotDuplicateBadId() {
try {
myEmpiProviderR4.notDuplicate(myPerson1Id, new StringType("Person/notAnId123"), myRequestDetails);
myMdmProviderR4.notDuplicate(myPerson1Id, new StringType("Person/notAnId123"), myRequestDetails);
fail();
} catch (ResourceNotFoundException e) {
assertEquals("Resource Person/notAnId123 is not known", e.getMessage());
}
}
private void assertEmpiLink(int theExpectedSize, List<Parameters.ParametersParameterComponent> thePart, String thePersonId, String theTargetId, EmpiMatchResultEnum theMatchResult, String theEidMatch, String theNewPerson, String theScore) {
private void assertMdmLink(int theExpectedSize, List<Parameters.ParametersParameterComponent> thePart, String thePersonId, String theTargetId, MdmMatchResultEnum theMatchResult, String theEidMatch, String theNewPerson, String theScore) {
assertThat(thePart, hasSize(theExpectedSize));
assertThat(thePart.get(0).getName(), is("goldenResourceId"));
assertThat(thePart.get(0).getValue().toString(), is(removeVersion(thePersonId)));

View File

@ -1,10 +1,10 @@
package ca.uhn.fhir.jpa.empi.provider;
package ca.uhn.fhir.jpa.mdm.provider;
import ca.uhn.fhir.empi.api.EmpiConstants;
import ca.uhn.fhir.empi.api.EmpiLinkSourceEnum;
import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
import ca.uhn.fhir.empi.util.MessageHelper;
import ca.uhn.fhir.jpa.entity.EmpiLink;
import ca.uhn.fhir.mdm.api.MdmConstants;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.mdm.util.MessageHelper;
import ca.uhn.fhir.jpa.entity.MdmLink;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
import org.hl7.fhir.r4.model.Patient;
@ -18,7 +18,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.jupiter.api.Assertions.*;
public class EmpiProviderUpdateLinkR4Test extends BaseLinkR4Test {
public class MdmProviderUpdateLinkR4Test extends BaseLinkR4Test {
@Autowired
private MessageHelper myMessageHelper;
@ -26,36 +26,36 @@ public class EmpiProviderUpdateLinkR4Test extends BaseLinkR4Test {
@Test
public void testUpdateLinkNoMatch() {
assertLinkCount(1);
myEmpiProviderR4.updateLink(mySourcePatientId, myPatientId, NO_MATCH_RESULT, myRequestDetails);
myMdmProviderR4.updateLink(mySourcePatientId, myPatientId, NO_MATCH_RESULT, myRequestDetails);
assertLinkCount(2);
List<EmpiLink> links = getPatientLinks();
assertEquals(EmpiLinkSourceEnum.MANUAL, links.get(0).getLinkSource());
assertEquals(EmpiMatchResultEnum.NO_MATCH, links.get(0).getMatchResult());
assertEquals(EmpiLinkSourceEnum.AUTO, links.get(1).getLinkSource());
assertEquals(EmpiMatchResultEnum.MATCH, links.get(1).getMatchResult());
assertNotEquals(links.get(0).getSourceResourcePid(), links.get(1).getSourceResourcePid());
List<MdmLink> links = getPatientLinks();
assertEquals(MdmLinkSourceEnum.MANUAL, links.get(0).getLinkSource());
assertEquals(MdmMatchResultEnum.NO_MATCH, links.get(0).getMatchResult());
assertEquals(MdmLinkSourceEnum.AUTO, links.get(1).getLinkSource());
assertEquals(MdmMatchResultEnum.MATCH, links.get(1).getMatchResult());
assertNotEquals(links.get(0).getGoldenResourcePid(), links.get(1).getGoldenResourcePid());
}
@Test
public void testUpdateLinkMatch() {
assertLinkCount(1);
myEmpiProviderR4.updateLink(mySourcePatientId, myPatientId, MATCH_RESULT, myRequestDetails);
myMdmProviderR4.updateLink(mySourcePatientId, myPatientId, MATCH_RESULT, myRequestDetails);
assertLinkCount(1);
List<EmpiLink> links = getPatientLinks();
assertEquals(EmpiLinkSourceEnum.MANUAL, links.get(0).getLinkSource());
assertEquals(EmpiMatchResultEnum.MATCH, links.get(0).getMatchResult());
List<MdmLink> links = getPatientLinks();
assertEquals(MdmLinkSourceEnum.MANUAL, links.get(0).getLinkSource());
assertEquals(MdmMatchResultEnum.MATCH, links.get(0).getMatchResult());
}
@Test
public void testUpdateLinkTwiceFailsDueToWrongVersion() {
myEmpiProviderR4.updateLink(mySourcePatientId, myPatientId, MATCH_RESULT, myRequestDetails);
myMdmProviderR4.updateLink(mySourcePatientId, myPatientId, MATCH_RESULT, myRequestDetails);
materiallyChangeGoldenPatient();
try {
myEmpiProviderR4.updateLink(mySourcePatientId, myPatientId, NO_MATCH_RESULT, myRequestDetails);
myMdmProviderR4.updateLink(mySourcePatientId, myPatientId, NO_MATCH_RESULT, myRequestDetails);
fail();
} catch (ResourceVersionConflictException e) {
assertThat(e.getMessage(), matchesPattern("Requested resource Patient/\\d+/_history/1 is not the latest version. Latest version is Patient/\\d+/_history/2"));
@ -70,19 +70,19 @@ public class EmpiProviderUpdateLinkR4Test extends BaseLinkR4Test {
@Test
public void testUpdateLinkTwiceDoesNotThrowValidationErrorWhenNoVersionIsProvided() {
myEmpiProviderR4.updateLink(mySourcePatientId, myPatientId, MATCH_RESULT, myRequestDetails);
Patient patient = (Patient) myEmpiProviderR4.updateLink(myVersionlessGodlenResourceId, myPatientId, NO_MATCH_RESULT, myRequestDetails);
myMdmProviderR4.updateLink(mySourcePatientId, myPatientId, MATCH_RESULT, myRequestDetails);
Patient patient = (Patient) myMdmProviderR4.updateLink(myVersionlessGodlenResourceId, myPatientId, NO_MATCH_RESULT, myRequestDetails);
assertNotNull(patient); // if this wasn't allowed - a validation exception would be thrown
}
@Test
public void testUnlinkLink() {
myEmpiProviderR4.updateLink(mySourcePatientId, myPatientId, NO_MATCH_RESULT, myRequestDetails);
myMdmProviderR4.updateLink(mySourcePatientId, myPatientId, NO_MATCH_RESULT, myRequestDetails);
materiallyChangeGoldenPatient();
try {
myEmpiProviderR4.updateLink(mySourcePatientId, myPatientId, MATCH_RESULT, myRequestDetails);
myMdmProviderR4.updateLink(mySourcePatientId, myPatientId, MATCH_RESULT, myRequestDetails);
fail();
} catch (ResourceVersionConflictException e) {
assertThat(e.getMessage(), matchesPattern("Requested resource Patient/\\d+/_history/1 is not the latest version. Latest version is Patient/\\d+/_history/2"));
@ -92,7 +92,7 @@ public class EmpiProviderUpdateLinkR4Test extends BaseLinkR4Test {
@Test
public void testUpdateIllegalResultForPossibleMatch() {
try {
myEmpiProviderR4.updateLink(mySourcePatientId, myPatientId, POSSIBLE_MATCH_RESULT, myRequestDetails);
myMdmProviderR4.updateLink(mySourcePatientId, myPatientId, POSSIBLE_MATCH_RESULT, myRequestDetails);
fail();
} catch (InvalidRequestException e) {
assertEquals("$mdm-update-link illegal matchResult value 'POSSIBLE_MATCH'. Must be NO_MATCH or MATCH", e.getMessage());
@ -102,7 +102,7 @@ public class EmpiProviderUpdateLinkR4Test extends BaseLinkR4Test {
@Test
public void testUpdateIllegalResultPD() {
try {
myEmpiProviderR4.updateLink(mySourcePatientId, myPatientId, POSSIBLE_DUPLICATE_RESULT, myRequestDetails);
myMdmProviderR4.updateLink(mySourcePatientId, myPatientId, POSSIBLE_DUPLICATE_RESULT, myRequestDetails);
fail();
} catch (InvalidRequestException e) {
assertEquals("$mdm-update-link illegal matchResult value 'POSSIBLE_DUPLICATE'. Must be NO_MATCH or MATCH", e.getMessage());
@ -112,7 +112,7 @@ public class EmpiProviderUpdateLinkR4Test extends BaseLinkR4Test {
@Test
public void testUpdateIllegalSecondArg() {
try {
myEmpiProviderR4.updateLink(myPatientId, new StringType(""), NO_MATCH_RESULT, myRequestDetails);
myMdmProviderR4.updateLink(myPatientId, new StringType(""), NO_MATCH_RESULT, myRequestDetails);
fail();
} catch (InvalidRequestException e) {
assertThat(e.getMessage(), endsWith(" must have form <resourceType>/<id> where <id> is the id of the resource and <resourceType> is the type of the resource"));
@ -122,7 +122,7 @@ public class EmpiProviderUpdateLinkR4Test extends BaseLinkR4Test {
@Test
public void testUpdateIllegalFirstArg() {
try {
myEmpiProviderR4.updateLink(new StringType(""), myPatientId, NO_MATCH_RESULT, myRequestDetails);
myMdmProviderR4.updateLink(new StringType(""), myPatientId, NO_MATCH_RESULT, myRequestDetails);
fail();
} catch (InvalidRequestException e) {
assertThat(e.getMessage(), endsWith(" must have form <resourceType>/<id> where <id> is the id of the resource"));
@ -132,7 +132,7 @@ public class EmpiProviderUpdateLinkR4Test extends BaseLinkR4Test {
@Test
public void testAttemptingToModifyANonExistentLinkFails() {
try {
myEmpiProviderR4.updateLink(mySourcePatientId, mySourcePatientId, NO_MATCH_RESULT, myRequestDetails);
myMdmProviderR4.updateLink(mySourcePatientId, mySourcePatientId, NO_MATCH_RESULT, myRequestDetails);
fail();
} catch (InvalidRequestException e) {
assertThat(e.getMessage(), startsWith("No link"));
@ -143,7 +143,7 @@ public class EmpiProviderUpdateLinkR4Test extends BaseLinkR4Test {
public void testUpdateStrangePerson() {
Patient person = createPatient();
try {
myEmpiProviderR4.updateLink(new StringType(person.getIdElement().getValue()), myPatientId, NO_MATCH_RESULT, myRequestDetails);
myMdmProviderR4.updateLink(new StringType(person.getIdElement().getValue()), myPatientId, NO_MATCH_RESULT, myRequestDetails);
fail();
} catch (InvalidRequestException e) {
String expectedMessage = myMessageHelper.getMessageForUnmanagedResource();
@ -154,13 +154,13 @@ public class EmpiProviderUpdateLinkR4Test extends BaseLinkR4Test {
@Test
public void testExcludedPerson() {
Patient patient = new Patient();
patient.getMeta().addTag().setSystem(EmpiConstants.SYSTEM_MDM_MANAGED).setCode(EmpiConstants.CODE_NO_EMPI_MANAGED);
patient.getMeta().addTag().setSystem(MdmConstants.SYSTEM_MDM_MANAGED).setCode(MdmConstants.CODE_NO_MDM_MANAGED);
createPatient(patient);
try {
myEmpiProviderR4.updateLink(mySourcePatientId, new StringType(patient.getIdElement().getValue()), NO_MATCH_RESULT, myRequestDetails);
myMdmProviderR4.updateLink(mySourcePatientId, new StringType(patient.getIdElement().getValue()), NO_MATCH_RESULT, myRequestDetails);
fail();
} catch (InvalidRequestException e) {
assertEquals("The target is marked with the " + EmpiConstants.CODE_NO_EMPI_MANAGED + " tag which means it may not be EMPI linked.", e.getMessage());
assertEquals("The target is marked with the " + MdmConstants.CODE_NO_MDM_MANAGED + " tag which means it may not be EMPI linked.", e.getMessage());
}
}
}

View File

@ -1,16 +1,16 @@
package ca.uhn.fhir.jpa.empi.searchparam;
package ca.uhn.fhir.jpa.mdm.searchparam;
import ca.uhn.fhir.jpa.empi.BaseEmpiR4Test;
import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
import org.junit.jupiter.api.BeforeEach;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SearchParameterTest extends BaseEmpiR4Test {
public class SearchParameterTest extends BaseMdmR4Test {
private static final Logger ourLog = LoggerFactory.getLogger(SearchParameterTest.class);
@BeforeEach
public void before() {
super.loadEmpiSearchParameters();
super.loadMdmSearchParameters();
}
/**

View File

@ -1,10 +1,10 @@
package ca.uhn.fhir.jpa.empi.svc;
package ca.uhn.fhir.jpa.mdm.svc;
import ca.uhn.fhir.jpa.empi.provider.EmpiProviderUpdateLinkR4Test;
import ca.uhn.fhir.jpa.mdm.provider.MdmProviderUpdateLinkR4Test;
/**
* Tests for this service are in the test for the provider that wraps this service:
* @see EmpiProviderUpdateLinkR4Test
* @see MdmProviderUpdateLinkR4Test
*/
public class EmpiLinkUpdaterSvcImplTest {
}

View File

@ -1,9 +1,9 @@
package ca.uhn.fhir.jpa.empi.svc;
package ca.uhn.fhir.jpa.mdm.svc;
import ca.uhn.fhir.empi.api.IEmpiSubmitSvc;
import ca.uhn.fhir.mdm.api.IMdmSubmitSvc;
import ca.uhn.fhir.interceptor.api.IInterceptorService;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.empi.BaseEmpiR4Test;
import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
import ca.uhn.test.concurrency.PointcutLatch;
import org.apache.commons.lang3.time.DateUtils;
import org.junit.jupiter.api.AfterEach;
@ -14,29 +14,29 @@ import org.springframework.beans.factory.annotation.Autowired;
import java.io.IOException;
import java.util.Date;
class EmpiBatchSvcImplTest extends BaseEmpiR4Test {
class MdmBatchSvcImplTest extends BaseMdmR4Test {
@Autowired
IEmpiSubmitSvc myEmpiSubmitSvc;
IMdmSubmitSvc myMdmSubmitSvc;
@Autowired
IInterceptorService myInterceptorService;
PointcutLatch afterEmpiLatch = new PointcutLatch(Pointcut.EMPI_AFTER_PERSISTED_RESOURCE_CHECKED);
PointcutLatch afterMdmLatch = new PointcutLatch(Pointcut.MDM_AFTER_PERSISTED_RESOURCE_CHECKED);
@BeforeEach
public void before() {
myInterceptorService.registerAnonymousInterceptor(Pointcut.EMPI_AFTER_PERSISTED_RESOURCE_CHECKED, afterEmpiLatch);
myInterceptorService.registerAnonymousInterceptor(Pointcut.MDM_AFTER_PERSISTED_RESOURCE_CHECKED, afterMdmLatch);
}
@AfterEach
public void after() throws IOException {
myInterceptorService.unregisterInterceptor(afterEmpiLatch);
afterEmpiLatch.clear();
myInterceptorService.unregisterInterceptor(afterMdmLatch);
afterMdmLatch.clear();
super.after();
}
@Test
public void testEmpiBatchRunWorksOverMultipleTargetTypes() throws InterruptedException {
public void testMdmBatchRunWorksOverMultipleTargetTypes() throws InterruptedException {
for (int i =0; i < 10; i++) {
createPatient(buildJanePatient());
@ -54,13 +54,13 @@ class EmpiBatchSvcImplTest extends BaseEmpiR4Test {
assertLinkCount(0);
//SUT
afterEmpiLatch.runWithExpectedCount(30, () -> myEmpiSubmitSvc.submitAllTargetTypesToEmpi(null));
afterMdmLatch.runWithExpectedCount(30, () -> myMdmSubmitSvc.submitAllTargetTypesToMdm(null));
assertLinkCount(30);
}
@Test
public void testEmpiBatchOnPatientType() throws Exception {
public void testMdmBatchOnPatientType() throws Exception {
for (int i =0; i < 10; i++) {
createPatient(buildPatientWithNameAndId("test", "id"));
@ -69,13 +69,13 @@ class EmpiBatchSvcImplTest extends BaseEmpiR4Test {
assertLinkCount(0);
//SUT
afterEmpiLatch.runWithExpectedCount(10, () -> myEmpiSubmitSvc.submitTargetTypeToEmpi("Patient", null));
afterMdmLatch.runWithExpectedCount(10, () -> myMdmSubmitSvc.submitTargetTypeToMdm("Patient", null));
assertLinkCount(10);
}
@Test
public void testEmpiBatchOnMedicationType() throws Exception {
public void testMdmBatchOnMedicationType() throws Exception {
createDummyOrganization();
@ -86,13 +86,13 @@ class EmpiBatchSvcImplTest extends BaseEmpiR4Test {
assertLinkCount(0);
//SUT
afterEmpiLatch.runWithExpectedCount(10, () -> myEmpiSubmitSvc.submitTargetTypeToEmpi("Medication", null));
afterMdmLatch.runWithExpectedCount(10, () -> myMdmSubmitSvc.submitTargetTypeToMdm("Medication", null));
assertLinkCount(10);
}
@Test
public void testEmpiBatchOnPractitionerType() throws Exception {
public void testMdmBatchOnPractitionerType() throws Exception {
for (int i =0; i < 10; i++) {
createPractitioner(buildPractitionerWithNameAndId("test", "id"));
@ -101,20 +101,20 @@ class EmpiBatchSvcImplTest extends BaseEmpiR4Test {
assertLinkCount(0);
//SUT
afterEmpiLatch.runWithExpectedCount(10, () -> myEmpiSubmitSvc.submitAllTargetTypesToEmpi(null));
afterMdmLatch.runWithExpectedCount(10, () -> myMdmSubmitSvc.submitAllTargetTypesToMdm(null));
assertLinkCount(10);
}
@Test
public void testEmpiOnTargetTypeWithCriteria() throws InterruptedException {
public void testMdmOnTargetTypeWithCriteria() throws InterruptedException {
createPatient(buildPatientWithNameIdAndBirthday("gary", "gary_id", new Date()));
createPatient(buildPatientWithNameIdAndBirthday("john", "john_id", DateUtils.addDays(new Date(), -300)));
assertLinkCount(0);
//SUT
afterEmpiLatch.runWithExpectedCount(1, () -> myEmpiSubmitSvc.submitTargetTypeToEmpi("Patient", "Patient?name=gary"));
afterMdmLatch.runWithExpectedCount(1, () -> myMdmSubmitSvc.submitTargetTypeToMdm("Patient", "Patient?name=gary"));
assertLinkCount(1);
}

View File

@ -1,8 +1,8 @@
package ca.uhn.fhir.jpa.empi.svc;
package ca.uhn.fhir.jpa.mdm.svc;
import ca.uhn.fhir.empi.rules.json.EmpiResourceSearchParamJson;
import ca.uhn.fhir.jpa.empi.BaseEmpiR4Test;
import ca.uhn.fhir.jpa.empi.svc.candidate.EmpiCandidateSearchCriteriaBuilderSvc;
import ca.uhn.fhir.mdm.rules.json.MdmResourceSearchParamJson;
import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
import ca.uhn.fhir.jpa.mdm.svc.candidate.MdmCandidateSearchCriteriaBuilderSvc;
import org.hl7.fhir.r4.model.HumanName;
import org.hl7.fhir.r4.model.Patient;
import org.junit.jupiter.api.Test;
@ -20,16 +20,16 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class EmpiCandidateSearchCriteriaBuilderSvcTest extends BaseEmpiR4Test {
public class MdmCandidateSearchCriteriaBuilderSvcTest extends BaseMdmR4Test {
@Autowired
EmpiCandidateSearchCriteriaBuilderSvc myEmpiCandidateSearchCriteriaBuilderSvc;
MdmCandidateSearchCriteriaBuilderSvc myMdmCandidateSearchCriteriaBuilderSvc;
@Test
public void testEmptyCase() {
Patient patient = new Patient();
EmpiResourceSearchParamJson searchParamJson = new EmpiResourceSearchParamJson();
MdmResourceSearchParamJson searchParamJson = new MdmResourceSearchParamJson();
searchParamJson.addSearchParam("family");
Optional<String> result = myEmpiCandidateSearchCriteriaBuilderSvc.buildResourceQueryString("Patient", patient, Collections.emptyList(), searchParamJson);
Optional<String> result = myMdmCandidateSearchCriteriaBuilderSvc.buildResourceQueryString("Patient", patient, Collections.emptyList(), searchParamJson);
assertFalse(result.isPresent());
}
@ -37,9 +37,9 @@ public class EmpiCandidateSearchCriteriaBuilderSvcTest extends BaseEmpiR4Test {
public void testSimpleCase() {
Patient patient = new Patient();
patient.addName().setFamily("Fernandez");
EmpiResourceSearchParamJson searchParamJson = new EmpiResourceSearchParamJson();
MdmResourceSearchParamJson searchParamJson = new MdmResourceSearchParamJson();
searchParamJson.addSearchParam("family");
Optional<String> result = myEmpiCandidateSearchCriteriaBuilderSvc.buildResourceQueryString("Patient", patient, Collections.emptyList(), searchParamJson);
Optional<String> result = myMdmCandidateSearchCriteriaBuilderSvc.buildResourceQueryString("Patient", patient, Collections.emptyList(), searchParamJson);
assertTrue(result.isPresent());
assertEquals("Patient?family=Fernandez", result.get());
}
@ -51,10 +51,10 @@ public class EmpiCandidateSearchCriteriaBuilderSvcTest extends BaseEmpiR4Test {
humanName.addGiven("Jose");
humanName.addGiven("Martin");
humanName.setFamily("Fernandez");
EmpiResourceSearchParamJson searchParamJson = new EmpiResourceSearchParamJson();
MdmResourceSearchParamJson searchParamJson = new MdmResourceSearchParamJson();
searchParamJson.addSearchParam("given");
searchParamJson.addSearchParam("family");
Optional<String> result = myEmpiCandidateSearchCriteriaBuilderSvc.buildResourceQueryString("Patient", patient, Collections.emptyList(), searchParamJson);
Optional<String> result = myMdmCandidateSearchCriteriaBuilderSvc.buildResourceQueryString("Patient", patient, Collections.emptyList(), searchParamJson);
assertTrue(result.isPresent());
assertThat(result.get(), anyOf(equalTo("Patient?given=Jose,Martin&family=Fernandez"), equalTo("Patient?given=Martin,Jose&family=Fernandez")));
}
@ -63,9 +63,9 @@ public class EmpiCandidateSearchCriteriaBuilderSvcTest extends BaseEmpiR4Test {
public void testIdentifier() {
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:oid:1.2.36.146.595.217.0.1").setValue("12345");
EmpiResourceSearchParamJson searchParamJson = new EmpiResourceSearchParamJson();
MdmResourceSearchParamJson searchParamJson = new MdmResourceSearchParamJson();
searchParamJson.addSearchParam("identifier");
Optional<String> result = myEmpiCandidateSearchCriteriaBuilderSvc.buildResourceQueryString("Patient", patient, Collections.emptyList(), searchParamJson);
Optional<String> result = myMdmCandidateSearchCriteriaBuilderSvc.buildResourceQueryString("Patient", patient, Collections.emptyList(), searchParamJson);
assertTrue(result.isPresent());
assertEquals(result.get(), "Patient?identifier=urn%3Aoid%3A1.2.36.146.595.217.0.1%7C12345");
}
@ -74,9 +74,9 @@ public class EmpiCandidateSearchCriteriaBuilderSvcTest extends BaseEmpiR4Test {
public void testIdentifierSpaceIsEscaped() {
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:oid:1.2.36.146.595.217.0.1").setValue("abc def");
EmpiResourceSearchParamJson searchParamJson = new EmpiResourceSearchParamJson();
MdmResourceSearchParamJson searchParamJson = new MdmResourceSearchParamJson();
searchParamJson.addSearchParam("identifier");
Optional<String> result = myEmpiCandidateSearchCriteriaBuilderSvc.buildResourceQueryString("Patient", patient, Collections.emptyList(), searchParamJson);
Optional<String> result = myMdmCandidateSearchCriteriaBuilderSvc.buildResourceQueryString("Patient", patient, Collections.emptyList(), searchParamJson);
assertTrue(result.isPresent());
assertEquals("Patient?identifier=urn%3Aoid%3A1.2.36.146.595.217.0.1%7Cabc%20def", result.get());
}
@ -84,7 +84,7 @@ public class EmpiCandidateSearchCriteriaBuilderSvcTest extends BaseEmpiR4Test {
@Test
public void testOmittingCandidateSearchParamsIsAllowed() {
Patient patient = new Patient();
Optional<String> result = myEmpiCandidateSearchCriteriaBuilderSvc.buildResourceQueryString("Patient", patient, Collections.emptyList(), null);
Optional<String> result = myMdmCandidateSearchCriteriaBuilderSvc.buildResourceQueryString("Patient", patient, Collections.emptyList(), null);
assertThat(result.isPresent(), is(true));
assertThat(result.get(), is(equalTo("Patient?")));
}
@ -93,7 +93,7 @@ public class EmpiCandidateSearchCriteriaBuilderSvcTest extends BaseEmpiR4Test {
public void testEmptyCandidateSearchParamsWorksInConjunctionWithFilterParams() {
Patient patient = new Patient();
List<String> filterParams = Collections.singletonList("active=true");
Optional<String> result = myEmpiCandidateSearchCriteriaBuilderSvc.buildResourceQueryString("Patient", patient, filterParams, null);
Optional<String> result = myMdmCandidateSearchCriteriaBuilderSvc.buildResourceQueryString("Patient", patient, filterParams, null);
assertThat(result.isPresent(), is(true));
assertThat(result.get(), is(equalTo("Patient?active=true")));
}

View File

@ -1,7 +1,7 @@
package ca.uhn.fhir.jpa.empi.svc;
package ca.uhn.fhir.jpa.mdm.svc;
import ca.uhn.fhir.jpa.empi.BaseEmpiR4Test;
import ca.uhn.fhir.jpa.empi.svc.candidate.EmpiCandidateSearchSvc;
import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
import ca.uhn.fhir.jpa.mdm.svc.candidate.MdmCandidateSearchSvc;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.Practitioner;
@ -17,10 +17,10 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class EmpiCandidateSearchSvcTest extends BaseEmpiR4Test {
public class MdmCandidateSearchSvcTest extends BaseMdmR4Test {
@Autowired
EmpiCandidateSearchSvc myEmpiCandidateSearchSvc;
MdmCandidateSearchSvc myMdmCandidateSearchSvc;
@Test
public void testFindCandidates() {
@ -29,7 +29,7 @@ public class EmpiCandidateSearchSvcTest extends BaseEmpiR4Test {
createPatient(jane);
Patient newJane = buildJanePatient();
Collection<IAnyResource> result = myEmpiCandidateSearchSvc.findCandidates("Patient", newJane);
Collection<IAnyResource> result = myMdmCandidateSearchSvc.findCandidates("Patient", newJane);
assertEquals(1, result.size());
}
@ -44,7 +44,7 @@ public class EmpiCandidateSearchSvcTest extends BaseEmpiR4Test {
Patient newJane = buildJaneWithBirthday(today);
Collection<IAnyResource> result = myEmpiCandidateSearchSvc.findCandidates("Patient", newJane);
Collection<IAnyResource> result = myMdmCandidateSearchSvc.findCandidates("Patient", newJane);
assertEquals(1, result.size());
}
@ -62,7 +62,7 @@ public class EmpiCandidateSearchSvcTest extends BaseEmpiR4Test {
incomingPatient.setActive(true);
incomingPatient.setGeneralPractitioner(Collections.singletonList(new Reference(practitionerAndUpdateLinks.getId())));
Collection<IAnyResource> patient = myEmpiCandidateSearchSvc.findCandidates("Patient", incomingPatient);
Collection<IAnyResource> patient = myMdmCandidateSearchSvc.findCandidates("Patient", incomingPatient);
assertThat(patient, hasSize(1));
}
}

View File

@ -1,16 +1,13 @@
package ca.uhn.fhir.jpa.empi.svc;
package ca.uhn.fhir.jpa.mdm.svc;
import ca.uhn.fhir.empi.api.EmpiLinkSourceEnum;
import ca.uhn.fhir.empi.api.EmpiMatchOutcome;
import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
import ca.uhn.fhir.empi.api.IEmpiLinkSvc;
import ca.uhn.fhir.empi.util.EIDHelper;
import ca.uhn.fhir.jpa.empi.BaseEmpiR4Test;
import ca.uhn.fhir.jpa.entity.EmpiLink;
import ca.uhn.fhir.jpa.entity.MdmLink;
import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
import ca.uhn.fhir.mdm.api.IMdmLinkSvc;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchOutcome;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import org.assertj.core.util.Lists;
import org.hamcrest.Matchers;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.Patient;
import org.junit.jupiter.api.AfterEach;
@ -27,18 +24,17 @@ import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
public class EmpiLinkSvcTest extends BaseEmpiR4Test {
private static final EmpiMatchOutcome POSSIBLE_MATCH = new EmpiMatchOutcome(null, null).setMatchResultEnum(EmpiMatchResultEnum.POSSIBLE_MATCH);
public class MdmLinkSvcTest extends BaseMdmR4Test {
private static final MdmMatchOutcome POSSIBLE_MATCH = new MdmMatchOutcome(null, null).setMatchResultEnum(MdmMatchResultEnum.POSSIBLE_MATCH);
@Autowired
IEmpiLinkSvc myEmpiLinkSvc;
IMdmLinkSvc myMdmLinkSvc;
@Override
@AfterEach
public void after() throws IOException {
myExpungeEverythingService.expungeEverythingByType(EmpiLink.class);
myExpungeEverythingService.expungeEverythingByType(MdmLink.class);
super.after();
}
@ -46,8 +42,8 @@ public class EmpiLinkSvcTest extends BaseEmpiR4Test {
public void compareEmptyPatients() {
Patient patient = new Patient();
patient.setId("Patient/1");
EmpiMatchResultEnum result = myEmpiResourceMatcherSvc.getMatchResult(patient, patient).getMatchResultEnum();
assertEquals(EmpiMatchResultEnum.NO_MATCH, result);
MdmMatchResultEnum result = myMdmResourceMatcherSvc.getMatchResult(patient, patient).getMatchResultEnum();
assertEquals(MdmMatchResultEnum.NO_MATCH, result);
}
@Test
@ -59,7 +55,7 @@ public class EmpiLinkSvcTest extends BaseEmpiR4Test {
Patient patient = createPatient();
{
myEmpiLinkSvc.updateLink(goldenPatient, patient, POSSIBLE_MATCH, EmpiLinkSourceEnum.AUTO, createContextForCreate("Patient"));
myMdmLinkSvc.updateLink(goldenPatient, patient, POSSIBLE_MATCH, MdmLinkSourceEnum.AUTO, createContextForCreate("Patient"));
assertLinkCount(1);
// TODO NG should be ok to remove
// Patient newSourcePatient = myPatientDao.read(sourcePatientId);
@ -67,7 +63,7 @@ public class EmpiLinkSvcTest extends BaseEmpiR4Test {
}
{
myEmpiLinkSvc.updateLink(goldenPatient, patient, EmpiMatchOutcome.NO_MATCH, EmpiLinkSourceEnum.MANUAL, createContextForCreate("Patient"));
myMdmLinkSvc.updateLink(goldenPatient, patient, MdmMatchOutcome.NO_MATCH, MdmLinkSourceEnum.MANUAL, createContextForCreate("Patient"));
assertLinkCount(1);
// TODO NG should be ok to remove
// Patient newSourcePatient = myPatientDao.read(sourcePatientId);
@ -82,7 +78,7 @@ public class EmpiLinkSvcTest extends BaseEmpiR4Test {
Patient goldenPatient1 = createGoldenPatient();
Patient goldenPatient2 = createGoldenPatient();
// TODO GGG MDM NOT VALID
myEmpiLinkSvc.updateLink(goldenPatient1, goldenPatient2, EmpiMatchOutcome.POSSIBLE_DUPLICATE, EmpiLinkSourceEnum.AUTO, createContextForCreate("Patient"));
myMdmLinkSvc.updateLink(goldenPatient1, goldenPatient2, MdmMatchOutcome.POSSIBLE_DUPLICATE, MdmLinkSourceEnum.AUTO, createContextForCreate("Patient"));
assertLinkCount(1);
}
@ -94,13 +90,13 @@ public class EmpiLinkSvcTest extends BaseEmpiR4Test {
Long goldenPatient1Pid = myIdHelperService.getPidOrNull(goldenPatient1);
Long goldenPatient2Pid = myIdHelperService.getPidOrNull(goldenPatient2);
assertFalse(myEmpiLinkDaoSvc.getLinkBySourceResourcePidAndTargetResourcePid(goldenPatient1Pid, goldenPatient2Pid).isPresent());
assertFalse(myEmpiLinkDaoSvc.getLinkBySourceResourcePidAndTargetResourcePid(goldenPatient2Pid, goldenPatient1Pid).isPresent());
assertFalse(myMdmLinkDaoSvc.getLinkBySourceResourcePidAndTargetResourcePid(goldenPatient1Pid, goldenPatient2Pid).isPresent());
assertFalse(myMdmLinkDaoSvc.getLinkBySourceResourcePidAndTargetResourcePid(goldenPatient2Pid, goldenPatient1Pid).isPresent());
saveNoMatchLink(goldenPatient1Pid, goldenPatient2Pid);
myEmpiLinkSvc.updateLink(goldenPatient1, goldenPatient2, EmpiMatchOutcome.POSSIBLE_DUPLICATE, EmpiLinkSourceEnum.AUTO, createContextForCreate("Person"));
assertFalse(myEmpiLinkDaoSvc.getEmpiLinksByPersonPidTargetPidAndMatchResult(goldenPatient1Pid, goldenPatient2Pid, EmpiMatchResultEnum.POSSIBLE_DUPLICATE).isPresent());
myMdmLinkSvc.updateLink(goldenPatient1, goldenPatient2, MdmMatchOutcome.POSSIBLE_DUPLICATE, MdmLinkSourceEnum.AUTO, createContextForCreate("Person"));
assertFalse(myMdmLinkDaoSvc.getMdmLinksByPersonPidTargetPidAndMatchResult(goldenPatient1Pid, goldenPatient2Pid, MdmMatchResultEnum.POSSIBLE_DUPLICATE).isPresent());
assertLinkCount(1);
}
@ -112,52 +108,52 @@ public class EmpiLinkSvcTest extends BaseEmpiR4Test {
Long goldenPatient1Pid = myIdHelperService.getPidOrNull(goldenPatient1);
Long goldenPatient2Pid = myIdHelperService.getPidOrNull(goldenPatient2);
assertFalse(myEmpiLinkDaoSvc.getLinkBySourceResourcePidAndTargetResourcePid(goldenPatient1Pid, goldenPatient2Pid).isPresent());
assertFalse(myEmpiLinkDaoSvc.getLinkBySourceResourcePidAndTargetResourcePid(goldenPatient2Pid, goldenPatient1Pid).isPresent());
assertFalse(myMdmLinkDaoSvc.getLinkBySourceResourcePidAndTargetResourcePid(goldenPatient1Pid, goldenPatient2Pid).isPresent());
assertFalse(myMdmLinkDaoSvc.getLinkBySourceResourcePidAndTargetResourcePid(goldenPatient2Pid, goldenPatient1Pid).isPresent());
saveNoMatchLink(goldenPatient2Pid, goldenPatient1Pid);
myEmpiLinkSvc.updateLink(goldenPatient1, goldenPatient2, EmpiMatchOutcome.POSSIBLE_DUPLICATE, EmpiLinkSourceEnum.AUTO, createContextForCreate("Person"));
assertFalse(myEmpiLinkDaoSvc.getEmpiLinksByPersonPidTargetPidAndMatchResult(goldenPatient1Pid, goldenPatient2Pid, EmpiMatchResultEnum.POSSIBLE_DUPLICATE).isPresent());
myMdmLinkSvc.updateLink(goldenPatient1, goldenPatient2, MdmMatchOutcome.POSSIBLE_DUPLICATE, MdmLinkSourceEnum.AUTO, createContextForCreate("Person"));
assertFalse(myMdmLinkDaoSvc.getMdmLinksByPersonPidTargetPidAndMatchResult(goldenPatient1Pid, goldenPatient2Pid, MdmMatchResultEnum.POSSIBLE_DUPLICATE).isPresent());
assertLinkCount(1);
}
private void saveNoMatchLink(Long theGoldenResourcePid, Long theTargetPid) {
EmpiLink noMatchLink = myEmpiLinkDaoSvc.newEmpiLink()
MdmLink noMatchLink = myMdmLinkDaoSvc.newMdmLink()
.setGoldenResourcePid(theGoldenResourcePid)
.setTargetPid(theTargetPid)
.setLinkSource(EmpiLinkSourceEnum.MANUAL)
.setMatchResult(EmpiMatchResultEnum.NO_MATCH);
.setLinkSource(MdmLinkSourceEnum.MANUAL)
.setMatchResult(MdmMatchResultEnum.NO_MATCH);
saveLink(noMatchLink);
}
@Test
public void testManualEmpiLinksCannotBeModifiedBySystem() {
public void testManualMdmLinksCannotBeModifiedBySystem() {
// Patient goldenPatient = createGoldenPatient(buildJaneSourcePatient());
Patient goldenPatient = createGoldenPatient(buildJanePatient());
Patient patient = createPatient(buildJanePatient());
myEmpiLinkSvc.updateLink(goldenPatient, patient, EmpiMatchOutcome.NO_MATCH, EmpiLinkSourceEnum.MANUAL, createContextForCreate("Patient"));
myMdmLinkSvc.updateLink(goldenPatient, patient, MdmMatchOutcome.NO_MATCH, MdmLinkSourceEnum.MANUAL, createContextForCreate("Patient"));
try {
myEmpiLinkSvc.updateLink(goldenPatient, patient, EmpiMatchOutcome.NEW_PERSON_MATCH, EmpiLinkSourceEnum.AUTO, null);
myMdmLinkSvc.updateLink(goldenPatient, patient, MdmMatchOutcome.NEW_PERSON_MATCH, MdmLinkSourceEnum.AUTO, null);
fail();
} catch (InternalErrorException e) {
assertThat(e.getMessage(), is(equalTo("EMPI system is not allowed to modify links on manually created links")));
assertThat(e.getMessage(), is(equalTo("MDM system is not allowed to modify links on manually created links")));
}
}
@Test
public void testAutomaticallyAddedNO_MATCHEmpiLinksAreNotAllowed() {
public void testAutomaticallyAddedNO_MATCHMdmLinksAreNotAllowed() {
// Patient goldenPatient = createGoldenPatient(buildJaneSourcePatient());
Patient goldenPatient = createGoldenPatient(buildJanePatient());
Patient patient = createPatient(buildJanePatient());
// Test: it should be impossible to have a AUTO NO_MATCH record. The only NO_MATCH records in the system must be MANUAL.
try {
myEmpiLinkSvc.updateLink(goldenPatient, patient, EmpiMatchOutcome.NO_MATCH, EmpiLinkSourceEnum.AUTO, createContextForUpdate("Patient"));
myMdmLinkSvc.updateLink(goldenPatient, patient, MdmMatchOutcome.NO_MATCH, MdmLinkSourceEnum.AUTO, createContextForUpdate("Patient"));
fail();
} catch (InternalErrorException e) {
assertThat(e.getMessage(), is(equalTo("EMPI system is not allowed to automatically NO_MATCH a resource")));
assertThat(e.getMessage(), is(equalTo("MDM system is not allowed to automatically NO_MATCH a resource")));
}
}
@ -167,13 +163,12 @@ public class EmpiLinkSvcTest extends BaseEmpiR4Test {
Patient goldenPatient = createGoldenPatient(buildJanePatient());
Patient patient1 = createPatient(buildJanePatient());
Patient patient2 = createPatient(buildJanePatient());
assertEquals(0, myEmpiLinkDao.count());
assertEquals(0, myMdmLinkDao.count());
myEmpiLinkDaoSvc.createOrUpdateLinkEntity(goldenPatient, patient1, EmpiMatchOutcome.NEW_PERSON_MATCH, EmpiLinkSourceEnum.MANUAL, createContextForCreate("Patient"));
myEmpiLinkDaoSvc.createOrUpdateLinkEntity(goldenPatient, patient2, EmpiMatchOutcome.NO_MATCH, EmpiLinkSourceEnum.MANUAL, createContextForCreate("Patient"));
// myEmpiLinkSvc.syncEmpiLinksToPersonLinks(sourcePatient, createContextForCreate("Patient"));
myMdmLinkDaoSvc.createOrUpdateLinkEntity(goldenPatient, patient1, MdmMatchOutcome.NEW_PERSON_MATCH, MdmLinkSourceEnum.MANUAL, createContextForCreate("Patient"));
myMdmLinkDaoSvc.createOrUpdateLinkEntity(goldenPatient, patient2, MdmMatchOutcome.NO_MATCH, MdmLinkSourceEnum.MANUAL, createContextForCreate("Patient"));
List<EmpiLink> targets = myEmpiLinkDaoSvc.findEmpiLinksByGoldenResource(goldenPatient);
List<MdmLink> targets = myMdmLinkDaoSvc.findMdmLinksByGoldenResource(goldenPatient);
assertFalse(targets.isEmpty());
assertEquals(2, targets.size());
// TODO NG - OK? original assertTrue(goldenPatient.hasLink());

View File

@ -1,10 +1,10 @@
package ca.uhn.fhir.jpa.empi.svc;
package ca.uhn.fhir.jpa.mdm.svc;
import ca.uhn.fhir.empi.api.EmpiConstants;
import ca.uhn.fhir.empi.model.CanonicalEID;
import ca.uhn.fhir.empi.util.EIDHelper;
import ca.uhn.fhir.jpa.empi.BaseEmpiR4Test;
import ca.uhn.fhir.jpa.entity.EmpiLink;
import ca.uhn.fhir.mdm.api.MdmConstants;
import ca.uhn.fhir.mdm.model.CanonicalEID;
import ca.uhn.fhir.mdm.util.EIDHelper;
import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
import ca.uhn.fhir.jpa.entity.MdmLink;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.r4.model.Identifier;
import org.hl7.fhir.r4.model.Patient;
@ -18,9 +18,9 @@ import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static ca.uhn.fhir.empi.api.EmpiMatchResultEnum.MATCH;
import static ca.uhn.fhir.empi.api.EmpiMatchResultEnum.POSSIBLE_DUPLICATE;
import static ca.uhn.fhir.empi.api.EmpiMatchResultEnum.POSSIBLE_MATCH;
import static ca.uhn.fhir.mdm.api.MdmMatchResultEnum.MATCH;
import static ca.uhn.fhir.mdm.api.MdmMatchResultEnum.POSSIBLE_DUPLICATE;
import static ca.uhn.fhir.mdm.api.MdmMatchResultEnum.POSSIBLE_MATCH;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
@ -30,16 +30,16 @@ import static org.hamcrest.Matchers.not;
import static org.slf4j.LoggerFactory.getLogger;
@TestPropertySource(properties = {
"empi.prevent_multiple_eids=false"
"mdm.prevent_multiple_eids=false"
})
public class EmpiMatchLinkSvcMultipleEidModeTest extends BaseEmpiR4Test {
private static final Logger ourLog = getLogger(EmpiMatchLinkSvcMultipleEidModeTest.class);
public class MdmMatchLinkSvcMultipleEidModeTest extends BaseMdmR4Test {
private static final Logger ourLog = getLogger(MdmMatchLinkSvcMultipleEidModeTest.class);
@Autowired
private EIDHelper myEidHelper;
@BeforeEach
public void before() {
super.loadEmpiSearchParameters();
super.loadMdmSearchParameters();
}
@Test
@ -71,16 +71,16 @@ public class EmpiMatchLinkSvcMultipleEidModeTest extends BaseEmpiR4Test {
//The collision should have kept the old identifier
Identifier firstIdentifier = identifier.get(0);
assertThat(firstIdentifier.getSystem(), is(equalTo(EmpiConstants.HAPI_ENTERPRISE_IDENTIFIER_SYSTEM)));
assertThat(firstIdentifier.getSystem(), is(equalTo(MdmConstants.HAPI_ENTERPRISE_IDENTIFIER_SYSTEM)));
assertThat(firstIdentifier.getValue(), is(equalTo(foundHapiEid)));
//The collision should have added a new identifier with the external system.
Identifier secondIdentifier = identifier.get(1);
assertThat(secondIdentifier.getSystem(), is(equalTo(myEmpiConfig.getEmpiRules().getEnterpriseEIDSystem())));
assertThat(secondIdentifier.getSystem(), is(equalTo(myMdmSettings.getMdmRules().getEnterpriseEIDSystem())));
assertThat(secondIdentifier.getValue(), is(equalTo("12345")));
Identifier thirdIdentifier = identifier.get(2);
assertThat(thirdIdentifier.getSystem(), is(equalTo(myEmpiConfig.getEmpiRules().getEnterpriseEIDSystem())));
assertThat(thirdIdentifier.getSystem(), is(equalTo(myMdmSettings.getMdmRules().getEnterpriseEIDSystem())));
assertThat(thirdIdentifier.getValue(), is(equalTo("67890")));
}
@ -145,7 +145,7 @@ public class EmpiMatchLinkSvcMultipleEidModeTest extends BaseEmpiR4Test {
assertLinksCreatedNewResource(true, true, false);
assertLinksMatchedByEid(false, false, true);
List<EmpiLink> possibleDuplicates = myEmpiLinkDaoSvc.getPossibleDuplicates();
List<MdmLink> possibleDuplicates = myMdmLinkDaoSvc.getPossibleDuplicates();
assertThat(possibleDuplicates, hasSize(1));
List<Long> duplicatePids = Stream.of(patient1, patient2)
@ -154,9 +154,9 @@ public class EmpiMatchLinkSvcMultipleEidModeTest extends BaseEmpiR4Test {
.collect(Collectors.toList());
//The two Persons related to the patients should both show up in the only existing POSSIBLE_DUPLICATE EmpiLink.
EmpiLink empiLink = possibleDuplicates.get(0);
assertThat(empiLink.getSourceResourcePid(), is(in(duplicatePids)));
assertThat(empiLink.getTargetPid(), is(in(duplicatePids)));
MdmLink mdmLink = possibleDuplicates.get(0);
assertThat(mdmLink.getGoldenResourcePid(), is(in(duplicatePids)));
assertThat(mdmLink.getTargetPid(), is(in(duplicatePids)));
}
@Test
@ -200,11 +200,11 @@ public class EmpiMatchLinkSvcMultipleEidModeTest extends BaseEmpiR4Test {
assertLinksCreatedNewResource(true, true, false, false, false);
assertLinksMatchedByEid(false, true, true, true, true);
assertThat(patient2, is(not(matchedToAPerson())));
assertThat(patient2, is(not(matchedToAGoldenResource())));
assertThat(patient2, is(possibleMatchWith(patient1)));
assertThat(patient2, is(possibleMatchWith(patient3)));
List<EmpiLink> possibleDuplicates = myEmpiLinkDaoSvc.getPossibleDuplicates();
List<MdmLink> possibleDuplicates = myMdmLinkDaoSvc.getPossibleDuplicates();
assertThat(possibleDuplicates, hasSize(1));
assertThat(patient3, is(possibleDuplicateOf(patient1)));
}

View File

@ -1,17 +1,17 @@
package ca.uhn.fhir.jpa.empi.svc;
package ca.uhn.fhir.jpa.mdm.svc;
import ca.uhn.fhir.empi.api.EmpiConstants;
import ca.uhn.fhir.empi.api.EmpiLinkSourceEnum;
import ca.uhn.fhir.empi.api.EmpiMatchOutcome;
import ca.uhn.fhir.empi.api.IEmpiLinkSvc;
import ca.uhn.fhir.empi.model.CanonicalEID;
import ca.uhn.fhir.empi.util.EIDHelper;
import ca.uhn.fhir.empi.util.EmpiUtil;
import ca.uhn.fhir.empi.util.PersonHelper;
import ca.uhn.fhir.mdm.api.MdmConstants;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchOutcome;
import ca.uhn.fhir.mdm.api.IMdmLinkSvc;
import ca.uhn.fhir.mdm.model.CanonicalEID;
import ca.uhn.fhir.mdm.util.EIDHelper;
import ca.uhn.fhir.mdm.util.MdmUtil;
import ca.uhn.fhir.mdm.util.GoldenResourceHelper;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.dao.data.IEmpiLinkDao;
import ca.uhn.fhir.jpa.empi.BaseEmpiR4Test;
import ca.uhn.fhir.jpa.entity.EmpiLink;
import ca.uhn.fhir.jpa.dao.data.IMdmLinkDao;
import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
import ca.uhn.fhir.jpa.entity.MdmLink;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.TokenParam;
@ -32,10 +32,10 @@ import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static ca.uhn.fhir.empi.api.EmpiMatchResultEnum.MATCH;
import static ca.uhn.fhir.empi.api.EmpiMatchResultEnum.NO_MATCH;
import static ca.uhn.fhir.empi.api.EmpiMatchResultEnum.POSSIBLE_DUPLICATE;
import static ca.uhn.fhir.empi.api.EmpiMatchResultEnum.POSSIBLE_MATCH;
import static ca.uhn.fhir.mdm.api.MdmMatchResultEnum.MATCH;
import static ca.uhn.fhir.mdm.api.MdmMatchResultEnum.NO_MATCH;
import static ca.uhn.fhir.mdm.api.MdmMatchResultEnum.POSSIBLE_DUPLICATE;
import static ca.uhn.fhir.mdm.api.MdmMatchResultEnum.POSSIBLE_MATCH;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.blankOrNullString;
import static org.hamcrest.Matchers.equalTo;
@ -48,24 +48,24 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.slf4j.LoggerFactory.getLogger;
public class EmpiMatchLinkSvcTest extends BaseEmpiR4Test {
public class MdmMatchLinkSvcTest extends BaseMdmR4Test {
private static final Logger ourLog = getLogger(EmpiMatchLinkSvcTest.class);
private static final Logger ourLog = getLogger(MdmMatchLinkSvcTest.class);
@Autowired
IEmpiLinkSvc myEmpiLinkSvc;
IMdmLinkSvc myEmpiLinkSvc;
@Autowired
private EIDHelper myEidHelper;
@Autowired
private PersonHelper myPersonHelper;
private GoldenResourceHelper myGoldenResourceHelper;
@Autowired
private IEmpiLinkDao myEmpiLinkDao;
private IMdmLinkDao myEmpiLinkDao;
@Autowired
private DaoRegistry myDaoRegistry;
@BeforeEach
public void before() {
super.loadEmpiSearchParameters();
super.loadMdmSearchParameters();
}
@Test
@ -123,10 +123,10 @@ public class EmpiMatchLinkSvcTest extends BaseEmpiR4Test {
//Create a manual NO_MATCH between janePerson and unmatchedJane.
Patient unmatchedJane = createPatient(buildJanePatient());
myEmpiLinkSvc.updateLink(janePerson, unmatchedJane, EmpiMatchOutcome.NO_MATCH, EmpiLinkSourceEnum.MANUAL, createContextForCreate("Patient"));
myEmpiLinkSvc.updateLink(janePerson, unmatchedJane, MdmMatchOutcome.NO_MATCH, MdmLinkSourceEnum.MANUAL, createContextForCreate("Patient"));
//rerun EMPI rules against unmatchedJane.
myEmpiMatchLinkSvc.updateEmpiLinksForEmpiTarget(unmatchedJane, createContextForCreate("Patient"));
myMdmMatchLinkSvc.updateMdmLinksForMdmTarget(unmatchedJane, createContextForCreate("Patient"));
assertThat(unmatchedJane, is(not(sameSourceResourceAs(janePerson))));
assertThat(unmatchedJane, is(not(linkedTo(originalJane))));
@ -146,12 +146,12 @@ public class EmpiMatchLinkSvcTest extends BaseEmpiR4Test {
Patient unmatchedPatient = createPatient(buildJanePatient());
// This simulates an admin specifically saying that unmatchedPatient does NOT match janePerson.
myEmpiLinkSvc.updateLink(janePerson, unmatchedPatient, EmpiMatchOutcome.NO_MATCH, EmpiLinkSourceEnum.MANUAL, createContextForCreate("Patient"));
myEmpiLinkSvc.updateLink(janePerson, unmatchedPatient, MdmMatchOutcome.NO_MATCH, MdmLinkSourceEnum.MANUAL, createContextForCreate("Patient"));
// TODO change this so that it will only partially match.
//Now normally, when we run update links, it should link to janePerson. However, this manual NO_MATCH link
//should cause a whole new Person to be created.
myEmpiMatchLinkSvc.updateEmpiLinksForEmpiTarget(unmatchedPatient, createContextForCreate("Patient"));
myMdmMatchLinkSvc.updateMdmLinksForMdmTarget(unmatchedPatient, createContextForCreate("Patient"));
assertThat(unmatchedPatient, is(not(sameSourceResourceAs(janePerson))));
assertThat(unmatchedPatient, is(not(linkedTo(originalJane))));
@ -167,24 +167,24 @@ public class EmpiMatchLinkSvcTest extends BaseEmpiR4Test {
Patient janePatient = addExternalEID(buildJanePatient(), sampleEID);
janePatient = createPatientAndUpdateLinks(janePatient);
Optional<EmpiLink> empiLink = myEmpiLinkDaoSvc.getMatchedLinkForTargetPid(janePatient.getIdElement().getIdPartAsLong());
Optional<MdmLink> empiLink = myMdmLinkDaoSvc.getMatchedLinkForTargetPid(janePatient.getIdElement().getIdPartAsLong());
assertThat(empiLink.isPresent(), is(true));
Patient patient = getTargetResourceFromEmpiLink(empiLink.get(), "Patient");
List<CanonicalEID> externalEid = myEidHelper.getExternalEid(patient);
assertThat(externalEid.get(0).getSystem(), is(equalTo(myEmpiConfig.getEmpiRules().getEnterpriseEIDSystem())));
assertThat(externalEid.get(0).getSystem(), is(equalTo(myMdmSettings.getMdmRules().getEnterpriseEIDSystem())));
assertThat(externalEid.get(0).getValue(), is(equalTo(sampleEID)));
}
@Test
public void testWhenPatientIsCreatedWithoutAnEIDThePersonGetsAutomaticallyAssignedOne() {
Patient patient = createPatientAndUpdateLinks(buildJanePatient());
EmpiLink empiLink = myEmpiLinkDaoSvc.getMatchedLinkForTargetPid(patient.getIdElement().getIdPartAsLong()).get();
MdmLink mdmLink = myMdmLinkDaoSvc.getMatchedLinkForTargetPid(patient.getIdElement().getIdPartAsLong()).get();
Patient targetPatient = getTargetResourceFromEmpiLink(empiLink, "Patient");
Patient targetPatient = getTargetResourceFromEmpiLink(mdmLink, "Patient");
Identifier identifierFirstRep = targetPatient.getIdentifierFirstRep();
assertThat(identifierFirstRep.getSystem(), is(equalTo(EmpiConstants.HAPI_ENTERPRISE_IDENTIFIER_SYSTEM)));
assertThat(identifierFirstRep.getSystem(), is(equalTo(MdmConstants.HAPI_ENTERPRISE_IDENTIFIER_SYSTEM)));
assertThat(identifierFirstRep.getValue(), not(blankOrNullString()));
}
@ -192,7 +192,7 @@ public class EmpiMatchLinkSvcTest extends BaseEmpiR4Test {
public void testPatientAttributesAreCopiedOverWhenPersonIsCreatedFromPatient() {
Patient patient = createPatientAndUpdateLinks(buildPatientWithNameIdAndBirthday("Gary", "GARY_ID", new Date()));
Optional<EmpiLink> empiLink = myEmpiLinkDaoSvc.getMatchedLinkForTargetPid(patient.getIdElement().getIdPartAsLong());
Optional<MdmLink> empiLink = myMdmLinkDaoSvc.getMatchedLinkForTargetPid(patient.getIdElement().getIdPartAsLong());
Patient read = getTargetResourceFromEmpiLink(empiLink.get(), "Patient");
// TODO NG - rules haven't been determined yet revisit once implemented...
@ -232,12 +232,12 @@ public class EmpiMatchLinkSvcTest extends BaseEmpiR4Test {
//The collision should have kept the old identifier
Identifier firstIdentifier = identifier.get(0);
assertThat(firstIdentifier.getSystem(), is(equalTo(EmpiConstants.HAPI_ENTERPRISE_IDENTIFIER_SYSTEM)));
assertThat(firstIdentifier.getSystem(), is(equalTo(MdmConstants.HAPI_ENTERPRISE_IDENTIFIER_SYSTEM)));
assertThat(firstIdentifier.getValue(), is(equalTo(foundHapiEid)));
//The collision should have added a new identifier with the external system.
Identifier secondIdentifier = identifier.get(1);
assertThat(secondIdentifier.getSystem(), is(equalTo(myEmpiConfig.getEmpiRules().getEnterpriseEIDSystem())));
assertThat(secondIdentifier.getSystem(), is(equalTo(myMdmSettings.getMdmRules().getEnterpriseEIDSystem())));
assertThat(secondIdentifier.getValue(), is(equalTo("12345")));
}
@ -281,7 +281,7 @@ public class EmpiMatchLinkSvcTest extends BaseEmpiR4Test {
Patient patient2 = addExternalEID(buildJanePatient(), "eid-2");
patient2 = createPatientAndUpdateLinks(patient2);
List<EmpiLink> possibleDuplicates = myEmpiLinkDaoSvc.getPossibleDuplicates();
List<MdmLink> possibleDuplicates = myMdmLinkDaoSvc.getPossibleDuplicates();
assertThat(possibleDuplicates, hasSize(1));
@ -291,16 +291,16 @@ public class EmpiMatchLinkSvcTest extends BaseEmpiR4Test {
.collect(Collectors.toList());
//The two Persons related to the patients should both show up in the only existing POSSIBLE_DUPLICATE EmpiLink.
EmpiLink empiLink = possibleDuplicates.get(0);
assertThat(empiLink.getSourceResourcePid(), is(in(duplicatePids)));
assertThat(empiLink.getTargetPid(), is(in(duplicatePids)));
MdmLink mdmLink = possibleDuplicates.get(0);
assertThat(mdmLink.getGoldenResourcePid(), is(in(duplicatePids)));
assertThat(mdmLink.getTargetPid(), is(in(duplicatePids)));
}
@Test
public void testPatientWithNoEmpiTagIsNotMatched() {
// Patient with "no-empi" tag is not matched
Patient janePatient = buildJanePatient();
janePatient.getMeta().addTag(EmpiConstants.SYSTEM_MDM_MANAGED, EmpiConstants.CODE_NO_EMPI_MANAGED, "Don't EMPI on me!");
janePatient.getMeta().addTag(MdmConstants.SYSTEM_MDM_MANAGED, MdmConstants.CODE_NO_MDM_MANAGED, "Don't EMPI on me!");
createPatientAndUpdateLinks(janePatient);
assertLinkCount(0);
}
@ -332,7 +332,7 @@ public class EmpiMatchLinkSvcTest extends BaseEmpiR4Test {
assertLinkCount(0);
Patient janePatient = createPatientAndUpdateLinks(buildJanePatient());
assertLinkCount(1);
assertThat(janePatient, is(matchedToAPerson()));
assertThat(janePatient, is(matchedToAGoldenResource()));
}
@Test
@ -363,8 +363,8 @@ public class EmpiMatchLinkSvcTest extends BaseEmpiR4Test {
//In a normal situation, janePatient2 would just match to jane patient, but here we need to hack it so they are their
//own individual Persons for the purpose of this test.
IAnyResource person = myPersonHelper.createGoldenResourceFromMdmTarget(janePatient2);
myEmpiLinkSvc.updateLink(person, janePatient2, EmpiMatchOutcome.NEW_PERSON_MATCH, EmpiLinkSourceEnum.AUTO, createContextForCreate("Patient"));
IAnyResource person = myGoldenResourceHelper.createGoldenResourceFromMdmTarget(janePatient2);
myEmpiLinkSvc.updateLink(person, janePatient2, MdmMatchOutcome.NEW_PERSON_MATCH, MdmLinkSourceEnum.AUTO, createContextForCreate("Patient"));
assertThat(janePatient, is(not(sameSourceResourceAs(janePatient2))));
//In theory, this will match both Persons!
@ -377,7 +377,7 @@ public class EmpiMatchLinkSvcTest extends BaseEmpiR4Test {
assertThat(incomingJanePatient, is(possibleMatchWith(janePatient, janePatient2)));
//Ensure there is no successful MATCH links for incomingJanePatient
Optional<EmpiLink> matchedLinkForTargetPid = myEmpiLinkDaoSvc.getMatchedLinkForTargetPid(myIdHelperService.getPidOrNull(incomingJanePatient));
Optional<MdmLink> matchedLinkForTargetPid = myMdmLinkDaoSvc.getMatchedLinkForTargetPid(myIdHelperService.getPidOrNull(incomingJanePatient));
assertThat(matchedLinkForTargetPid.isPresent(), is(false));
logAllLinks();
@ -426,7 +426,7 @@ public class EmpiMatchLinkSvcTest extends BaseEmpiR4Test {
private SearchParameterMap buildGoldenRecordSearchParameterMap() {
SearchParameterMap searchParameterMap = new SearchParameterMap();
searchParameterMap.setLoadSynchronous(true);
searchParameterMap.add("_tag", new TokenParam(EmpiConstants.SYSTEM_MDM_MANAGED, EmpiConstants.CODE_HAPI_MDM_MANAGED));
searchParameterMap.add("_tag", new TokenParam(MdmConstants.SYSTEM_MDM_MANAGED, MdmConstants.CODE_HAPI_MDM_MANAGED));
return searchParameterMap;
}
@ -454,10 +454,10 @@ public class EmpiMatchLinkSvcTest extends BaseEmpiR4Test {
public void testCreateSourceResourceFromEmpiTarget() {
// Create Use Case #2 - adding patient with no EID
Patient janePatient = buildJanePatient();
Patient janeSourceResourcePatient = myPersonHelper.createGoldenResourceFromMdmTarget(janePatient);
Patient janeSourceResourcePatient = myGoldenResourceHelper.createGoldenResourceFromMdmTarget(janePatient);
// golden record now contains HAPI-generated EID and HAPI tag
assertTrue(EmpiUtil.isEmpiManaged(janeSourceResourcePatient));
assertTrue(MdmUtil.isMdmManaged(janeSourceResourcePatient));
assertFalse(myEidHelper.getHapiEid(janeSourceResourcePatient).isEmpty());
// original checks - verifies that EIDs are assigned
@ -586,7 +586,7 @@ public class EmpiMatchLinkSvcTest extends BaseEmpiR4Test {
}
private void assertNoDuplicates() {
List<EmpiLink> possibleDuplicates = myEmpiLinkDaoSvc.getPossibleDuplicates();
List<MdmLink> possibleDuplicates = myMdmLinkDaoSvc.getPossibleDuplicates();
assertThat(possibleDuplicates, hasSize(0));
}
@ -621,7 +621,7 @@ public class EmpiMatchLinkSvcTest extends BaseEmpiR4Test {
assertThat(patient2, is(sameSourceResourceAs(patient1)));
List<EmpiLink> possibleDuplicates = myEmpiLinkDaoSvc.getPossibleDuplicates();
List<MdmLink> possibleDuplicates = myMdmLinkDaoSvc.getPossibleDuplicates();
assertThat(possibleDuplicates, hasSize(1));
assertThat(patient3, is(possibleDuplicateOf(patient1)));

View File

@ -1,15 +1,15 @@
package ca.uhn.fhir.jpa.empi.svc;
package ca.uhn.fhir.jpa.mdm.svc;
import ca.uhn.fhir.empi.api.EmpiLinkSourceEnum;
import ca.uhn.fhir.empi.api.EmpiMatchOutcome;
import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
import ca.uhn.fhir.empi.api.IGoldenResourceMergerSvc;
import ca.uhn.fhir.empi.model.MdmTransactionContext;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchOutcome;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.mdm.api.IGoldenResourceMergerSvc;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.interceptor.api.IInterceptorService;
import ca.uhn.fhir.jpa.empi.BaseEmpiR4Test;
import ca.uhn.fhir.jpa.empi.helper.EmpiLinkHelper;
import ca.uhn.fhir.jpa.empi.interceptor.IEmpiStorageInterceptor;
import ca.uhn.fhir.jpa.entity.EmpiLink;
import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
import ca.uhn.fhir.jpa.mdm.helper.MdmLinkHelper;
import ca.uhn.fhir.jpa.mdm.interceptor.IMdmStorageInterceptor;
import ca.uhn.fhir.jpa.entity.MdmLink;
import ca.uhn.fhir.rest.server.TransactionLogMessages;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import org.hl7.fhir.instance.model.api.IBaseResource;
@ -37,19 +37,19 @@ import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
public class EmpiPersonMergerSvcTest extends BaseEmpiR4Test {
public class MdmPersonMergerSvcTest extends BaseMdmR4Test {
public static final String GIVEN_NAME = "Jenn";
public static final String FAMILY_NAME = "Chan";
public static final String POSTAL_CODE = "M6G 1B4";
private static final String BAD_GIVEN_NAME = "Bob";
private static final EmpiMatchOutcome POSSIBLE_MATCH = new EmpiMatchOutcome(null, null).setMatchResultEnum(EmpiMatchResultEnum.POSSIBLE_MATCH);
private static final MdmMatchOutcome POSSIBLE_MATCH = new MdmMatchOutcome(null, null).setMatchResultEnum(MdmMatchResultEnum.POSSIBLE_MATCH);
@Autowired
IGoldenResourceMergerSvc myEmpiPersonMergerSvc;
IGoldenResourceMergerSvc myGoldenResourceMergerSvc;
@Autowired
EmpiLinkHelper myEmpiLinkHelper;
MdmLinkHelper myMdmLinkHelper;
@Autowired
IEmpiStorageInterceptor myEmpiStorageInterceptor;
IMdmStorageInterceptor myMdmStorageInterceptor;
@Autowired
IInterceptorService myInterceptorService;
@ -63,7 +63,7 @@ public class EmpiPersonMergerSvcTest extends BaseEmpiR4Test {
@BeforeEach
public void before() {
super.loadEmpiSearchParameters();
super.loadMdmSearchParameters();
myFromGoldenPatient = createGoldenPatient();
IdType fromSourcePatientId = myFromGoldenPatient.getIdElement().toUnqualifiedVersionless();
@ -77,13 +77,13 @@ public class EmpiPersonMergerSvcTest extends BaseEmpiR4Test {
myTargetPatient3 = createPatient();
// Register the empi storage interceptor after the creates so the delete hook is fired when we merge
myInterceptorService.registerInterceptor(myEmpiStorageInterceptor);
myInterceptorService.registerInterceptor(myMdmStorageInterceptor);
}
@Override
@AfterEach
public void after() throws IOException {
myInterceptorService.unregisterInterceptor(myEmpiStorageInterceptor);
myInterceptorService.unregisterInterceptor(myMdmStorageInterceptor);
super.after();
}
@ -102,15 +102,15 @@ public class EmpiPersonMergerSvcTest extends BaseEmpiR4Test {
private Patient mergeGoldenPatients() {
assertEquals(0, redirectLinkCount());
Patient retval = (Patient) myEmpiPersonMergerSvc.mergeGoldenResources(myFromGoldenPatient, myToGoldenPatient, createEmpiContext());
Patient retval = (Patient) myGoldenResourceMergerSvc.mergeGoldenResources(myFromGoldenPatient, myToGoldenPatient, createEmpiContext());
assertEquals(1, redirectLinkCount());
return retval;
}
private int redirectLinkCount() {
EmpiLink empiLink = new EmpiLink().setMatchResult(EmpiMatchResultEnum.REDIRECT);
Example<EmpiLink> example = Example.of(empiLink);
return myEmpiLinkDao.findAll(example).size();
MdmLink mdmLink = new MdmLink().setMatchResult(MdmMatchResultEnum.REDIRECT);
Example<MdmLink> example = Example.of(mdmLink);
return myMdmLinkDao.findAll(example).size();
}
private MdmTransactionContext createEmpiContext() {
@ -121,29 +121,29 @@ public class EmpiPersonMergerSvcTest extends BaseEmpiR4Test {
@Test
public void mergeRemovesPossibleDuplicatesLink() {
EmpiLink empiLink = myEmpiLinkDaoSvc.newEmpiLink()
MdmLink mdmLink = myMdmLinkDaoSvc.newMdmLink()
.setGoldenResourcePid(myToGoldenPatientPid)
.setTargetPid(myFromGoldenPatientPid)
.setEmpiTargetType("Patient")
.setMatchResult(EmpiMatchResultEnum.POSSIBLE_DUPLICATE)
.setLinkSource(EmpiLinkSourceEnum.AUTO);
.setMdmTargetType("Patient")
.setMatchResult(MdmMatchResultEnum.POSSIBLE_DUPLICATE)
.setLinkSource(MdmLinkSourceEnum.AUTO);
saveLink(empiLink);
saveLink(mdmLink);
{
List<EmpiLink> foundLinks = myEmpiLinkDao.findAll();
List<MdmLink> foundLinks = myMdmLinkDao.findAll();
assertEquals(1, foundLinks.size());
assertEquals(EmpiMatchResultEnum.POSSIBLE_DUPLICATE, foundLinks.get(0).getMatchResult());
assertEquals(MdmMatchResultEnum.POSSIBLE_DUPLICATE, foundLinks.get(0).getMatchResult());
}
myEmpiLinkHelper.logEmpiLinks();
myMdmLinkHelper.logMdmLinks();
mergeGoldenPatients();
{
List<EmpiLink> foundLinks = myEmpiLinkDao.findAll();
List<MdmLink> foundLinks = myMdmLinkDao.findAll();
assertEquals(1, foundLinks.size());
assertEquals(EmpiMatchResultEnum.REDIRECT, foundLinks.get(0).getMatchResult());
assertEquals(MdmMatchResultEnum.REDIRECT, foundLinks.get(0).getMatchResult());
}
}
@ -173,88 +173,88 @@ public class EmpiPersonMergerSvcTest extends BaseEmpiR4Test {
@Test
public void fromLinkToNoLink() {
createEmpiLink(myFromGoldenPatient, myTargetPatient1);
createMdmLink(myFromGoldenPatient, myTargetPatient1);
Patient mergedGoldenPatient = mergeGoldenPatients();
List<EmpiLink> links = getNonRedirectLinksByPerson(mergedGoldenPatient);
List<MdmLink> links = getNonRedirectLinksByPerson(mergedGoldenPatient);
assertEquals(1, links.size());
assertThat(mergedGoldenPatient, is(possibleLinkedTo(myTargetPatient1)));
}
@Test
public void fromNoLinkToLink() {
createEmpiLink(myToGoldenPatient, myTargetPatient1);
createMdmLink(myToGoldenPatient, myTargetPatient1);
Patient mergedSourcePatient = mergeGoldenPatients();
List<EmpiLink> links = getNonRedirectLinksByPerson(mergedSourcePatient);
List<MdmLink> links = getNonRedirectLinksByPerson(mergedSourcePatient);
assertEquals(1, links.size());
assertThat(mergedSourcePatient, is(possibleLinkedTo(myTargetPatient1)));
}
@Test
public void fromManualLinkOverridesAutoToLink() {
EmpiLink fromLink = createEmpiLink(myFromGoldenPatient, myTargetPatient1);
fromLink.setLinkSource(EmpiLinkSourceEnum.MANUAL);
fromLink.setMatchResult(EmpiMatchResultEnum.MATCH);
MdmLink fromLink = createMdmLink(myFromGoldenPatient, myTargetPatient1);
fromLink.setLinkSource(MdmLinkSourceEnum.MANUAL);
fromLink.setMatchResult(MdmMatchResultEnum.MATCH);
saveLink(fromLink);
createEmpiLink(myToGoldenPatient, myTargetPatient1);
createMdmLink(myToGoldenPatient, myTargetPatient1);
mergeGoldenPatients();
List<EmpiLink> links = getNonRedirectLinksByPerson(myToGoldenPatient);
List<MdmLink> links = getNonRedirectLinksByPerson(myToGoldenPatient);
assertEquals(1, links.size());
assertEquals(EmpiLinkSourceEnum.MANUAL, links.get(0).getLinkSource());
assertEquals(MdmLinkSourceEnum.MANUAL, links.get(0).getLinkSource());
}
private List<EmpiLink> getNonRedirectLinksByPerson(Patient theGoldenPatient) {
return myEmpiLinkDaoSvc.findEmpiLinksByGoldenResource(theGoldenPatient).stream()
private List<MdmLink> getNonRedirectLinksByPerson(Patient theGoldenPatient) {
return myMdmLinkDaoSvc.findMdmLinksByGoldenResource(theGoldenPatient).stream()
.filter(link -> !link.isRedirect())
.collect(Collectors.toList());
}
@Test
public void fromManualNoMatchLinkOverridesAutoToLink() {
EmpiLink fromLink = createEmpiLink(myFromGoldenPatient, myTargetPatient1);
fromLink.setLinkSource(EmpiLinkSourceEnum.MANUAL);
fromLink.setMatchResult(EmpiMatchResultEnum.NO_MATCH);
MdmLink fromLink = createMdmLink(myFromGoldenPatient, myTargetPatient1);
fromLink.setLinkSource(MdmLinkSourceEnum.MANUAL);
fromLink.setMatchResult(MdmMatchResultEnum.NO_MATCH);
saveLink(fromLink);
createEmpiLink(myToGoldenPatient, myTargetPatient1);
createMdmLink(myToGoldenPatient, myTargetPatient1);
mergeGoldenPatients();
List<EmpiLink> links = getNonRedirectLinksByPerson(myToGoldenPatient);
List<MdmLink> links = getNonRedirectLinksByPerson(myToGoldenPatient);
assertEquals(1, links.size());
assertEquals(EmpiLinkSourceEnum.MANUAL, links.get(0).getLinkSource());
assertEquals(EmpiMatchResultEnum.NO_MATCH, links.get(0).getMatchResult());
assertEquals(MdmLinkSourceEnum.MANUAL, links.get(0).getLinkSource());
assertEquals(MdmMatchResultEnum.NO_MATCH, links.get(0).getMatchResult());
}
@Test
public void fromManualAutoMatchLinkNoOverridesManualToLink() {
createEmpiLink(myFromGoldenPatient, myTargetPatient1);
createMdmLink(myFromGoldenPatient, myTargetPatient1);
EmpiLink toLink = createEmpiLink(myToGoldenPatient, myTargetPatient1);
toLink.setLinkSource(EmpiLinkSourceEnum.MANUAL);
toLink.setMatchResult(EmpiMatchResultEnum.NO_MATCH);
MdmLink toLink = createMdmLink(myToGoldenPatient, myTargetPatient1);
toLink.setLinkSource(MdmLinkSourceEnum.MANUAL);
toLink.setMatchResult(MdmMatchResultEnum.NO_MATCH);
saveLink(toLink);
mergeGoldenPatients();
List<EmpiLink> links = getNonRedirectLinksByPerson(myToGoldenPatient);
List<MdmLink> links = getNonRedirectLinksByPerson(myToGoldenPatient);
assertEquals(1, links.size());
assertEquals(EmpiLinkSourceEnum.MANUAL, links.get(0).getLinkSource());
assertEquals(EmpiMatchResultEnum.NO_MATCH, links.get(0).getMatchResult());
assertEquals(MdmLinkSourceEnum.MANUAL, links.get(0).getLinkSource());
assertEquals(MdmMatchResultEnum.NO_MATCH, links.get(0).getMatchResult());
}
@Test
public void fromNoMatchMergeToManualMatchIsError() {
EmpiLink fromLink = createEmpiLink(myFromGoldenPatient, myTargetPatient1);
fromLink.setLinkSource(EmpiLinkSourceEnum.MANUAL);
fromLink.setMatchResult(EmpiMatchResultEnum.NO_MATCH);
MdmLink fromLink = createMdmLink(myFromGoldenPatient, myTargetPatient1);
fromLink.setLinkSource(MdmLinkSourceEnum.MANUAL);
fromLink.setMatchResult(MdmMatchResultEnum.NO_MATCH);
saveLink(fromLink);
EmpiLink toLink = createEmpiLink(myToGoldenPatient, myTargetPatient1);
toLink.setLinkSource(EmpiLinkSourceEnum.MANUAL);
toLink.setMatchResult(EmpiMatchResultEnum.MATCH);
MdmLink toLink = createMdmLink(myToGoldenPatient, myTargetPatient1);
toLink.setLinkSource(MdmLinkSourceEnum.MANUAL);
toLink.setMatchResult(MdmMatchResultEnum.MATCH);
saveLink(toLink);
try {
@ -267,14 +267,14 @@ public class EmpiPersonMergerSvcTest extends BaseEmpiR4Test {
@Test
public void fromMatchMergeToManualNoMatchIsError() {
EmpiLink fromLink = createEmpiLink(myFromGoldenPatient, myTargetPatient1);
fromLink.setLinkSource(EmpiLinkSourceEnum.MANUAL);
fromLink.setMatchResult(EmpiMatchResultEnum.MATCH);
MdmLink fromLink = createMdmLink(myFromGoldenPatient, myTargetPatient1);
fromLink.setLinkSource(MdmLinkSourceEnum.MANUAL);
fromLink.setMatchResult(MdmMatchResultEnum.MATCH);
saveLink(fromLink);
EmpiLink toLink = createEmpiLink(myToGoldenPatient, myTargetPatient1);
toLink.setLinkSource(EmpiLinkSourceEnum.MANUAL);
toLink.setMatchResult(EmpiMatchResultEnum.NO_MATCH);
MdmLink toLink = createMdmLink(myToGoldenPatient, myTargetPatient1);
toLink.setLinkSource(MdmLinkSourceEnum.MANUAL);
toLink.setMatchResult(MdmMatchResultEnum.NO_MATCH);
saveLink(toLink);
try {
@ -287,14 +287,14 @@ public class EmpiPersonMergerSvcTest extends BaseEmpiR4Test {
@Test
public void fromNoMatchMergeToManualMatchDifferentPatientIsOk() {
EmpiLink fromLink = createEmpiLink(myFromGoldenPatient, myTargetPatient1);
fromLink.setLinkSource(EmpiLinkSourceEnum.MANUAL);
fromLink.setMatchResult(EmpiMatchResultEnum.NO_MATCH);
MdmLink fromLink = createMdmLink(myFromGoldenPatient, myTargetPatient1);
fromLink.setLinkSource(MdmLinkSourceEnum.MANUAL);
fromLink.setMatchResult(MdmMatchResultEnum.NO_MATCH);
saveLink(fromLink);
EmpiLink toLink = createEmpiLink(myToGoldenPatient, myTargetPatient2);
toLink.setLinkSource(EmpiLinkSourceEnum.MANUAL);
toLink.setMatchResult(EmpiMatchResultEnum.MATCH);
MdmLink toLink = createMdmLink(myToGoldenPatient, myTargetPatient2);
toLink.setLinkSource(MdmLinkSourceEnum.MANUAL);
toLink.setMatchResult(MdmMatchResultEnum.MATCH);
saveLink(toLink);
mergeGoldenPatients();
@ -302,18 +302,18 @@ public class EmpiPersonMergerSvcTest extends BaseEmpiR4Test {
assertResourceHasLinkCount(myToGoldenPatient, 3);
assertResourceHasLinkCount(myFromGoldenPatient, 0);
// TODO ENSURE PROPER LINK TYPES
assertEquals(3, myEmpiLinkDao.count());
assertEquals(3, myMdmLinkDao.count());
}
@Test
public void from123To1() {
createEmpiLink(myFromGoldenPatient, myTargetPatient1);
createEmpiLink(myFromGoldenPatient, myTargetPatient2);
createEmpiLink(myFromGoldenPatient, myTargetPatient3);
createEmpiLink(myToGoldenPatient, myTargetPatient1);
createMdmLink(myFromGoldenPatient, myTargetPatient1);
createMdmLink(myFromGoldenPatient, myTargetPatient2);
createMdmLink(myFromGoldenPatient, myTargetPatient3);
createMdmLink(myToGoldenPatient, myTargetPatient1);
mergeGoldenPatients();
myEmpiLinkHelper.logEmpiLinks();
myMdmLinkHelper.logMdmLinks();
assertThat(myToGoldenPatient, is(possibleLinkedTo(myTargetPatient1, myTargetPatient2, myTargetPatient3)));
assertResourceHasAutoLinkCount(myToGoldenPatient, 3);
@ -321,37 +321,37 @@ public class EmpiPersonMergerSvcTest extends BaseEmpiR4Test {
private void assertResourceHasLinkCount(IBaseResource theResource, int theCount) {
List<EmpiLink> links = myEmpiLinkDaoSvc.findEmpiLinksByGoldenResource(theResource);
List<MdmLink> links = myMdmLinkDaoSvc.findMdmLinksByGoldenResource(theResource);
assertEquals(theCount, links.size());
}
@Test
public void from1To123() {
createEmpiLink(myFromGoldenPatient, myTargetPatient1);
createEmpiLink(myToGoldenPatient, myTargetPatient1);
createEmpiLink(myToGoldenPatient, myTargetPatient2);
createEmpiLink(myToGoldenPatient, myTargetPatient3);
createMdmLink(myFromGoldenPatient, myTargetPatient1);
createMdmLink(myToGoldenPatient, myTargetPatient1);
createMdmLink(myToGoldenPatient, myTargetPatient2);
createMdmLink(myToGoldenPatient, myTargetPatient3);
mergeGoldenPatients();
myEmpiLinkHelper.logEmpiLinks();
myMdmLinkHelper.logMdmLinks();
assertThat(myToGoldenPatient, is(possibleLinkedTo(myTargetPatient1, myTargetPatient2, myTargetPatient3)));
assertResourceHasAutoLinkCount(myToGoldenPatient, 3);
}
private void assertResourceHasAutoLinkCount(Patient myToGoldenPatient, int theCount) {
List<EmpiLink> links = myEmpiLinkDaoSvc.findEmpiLinksByGoldenResource(myToGoldenPatient);
assertEquals(theCount, links.stream().filter(EmpiLink::isAuto).count());
List<MdmLink> links = myMdmLinkDaoSvc.findMdmLinksByGoldenResource(myToGoldenPatient);
assertEquals(theCount, links.stream().filter(MdmLink::isAuto).count());
}
@Test
public void from123To123() {
createEmpiLink(myFromGoldenPatient, myTargetPatient1);
createEmpiLink(myFromGoldenPatient, myTargetPatient2);
createEmpiLink(myFromGoldenPatient, myTargetPatient3);
createEmpiLink(myToGoldenPatient, myTargetPatient1);
createEmpiLink(myToGoldenPatient, myTargetPatient2);
createEmpiLink(myToGoldenPatient, myTargetPatient3);
createMdmLink(myFromGoldenPatient, myTargetPatient1);
createMdmLink(myFromGoldenPatient, myTargetPatient2);
createMdmLink(myFromGoldenPatient, myTargetPatient3);
createMdmLink(myToGoldenPatient, myTargetPatient1);
createMdmLink(myToGoldenPatient, myTargetPatient2);
createMdmLink(myToGoldenPatient, myTargetPatient3);
mergeGoldenPatients();
@ -362,13 +362,13 @@ public class EmpiPersonMergerSvcTest extends BaseEmpiR4Test {
@Test
public void from12To23() {
createEmpiLink(myFromGoldenPatient, myTargetPatient1);
createEmpiLink(myFromGoldenPatient, myTargetPatient2);
createEmpiLink(myToGoldenPatient, myTargetPatient2);
createEmpiLink(myToGoldenPatient, myTargetPatient3);
createMdmLink(myFromGoldenPatient, myTargetPatient1);
createMdmLink(myFromGoldenPatient, myTargetPatient2);
createMdmLink(myToGoldenPatient, myTargetPatient2);
createMdmLink(myToGoldenPatient, myTargetPatient3);
mergeGoldenPatients();
myEmpiLinkHelper.logEmpiLinks();
myMdmLinkHelper.logMdmLinks();
assertThat(myToGoldenPatient, is(possibleLinkedTo(myTargetPatient1, myTargetPatient2, myTargetPatient3)));
@ -428,10 +428,10 @@ public class EmpiPersonMergerSvcTest extends BaseEmpiR4Test {
assertThat(myToGoldenPatient.getIdentifier(), hasSize(4));
}
private EmpiLink createEmpiLink(Patient theSourcePatient, Patient theTargetPatient) {
private MdmLink createMdmLink(Patient theSourcePatient, Patient theTargetPatient) {
//TODO GGG Ensure theis comment can be safely removed
//theSourcePatient.addLink().setTarget(new Reference(theTargetPatient));
return myEmpiLinkDaoSvc.createOrUpdateLinkEntity(theSourcePatient, theTargetPatient, POSSIBLE_MATCH, EmpiLinkSourceEnum.AUTO, createContextForCreate("Patient"));
return myMdmLinkDaoSvc.createOrUpdateLinkEntity(theSourcePatient, theTargetPatient, POSSIBLE_MATCH, MdmLinkSourceEnum.AUTO, createContextForCreate("Patient"));
}
private void populatePerson(Patient theSourcePatient) {

View File

@ -1,7 +1,7 @@
package ca.uhn.fhir.jpa.empi.svc;
package ca.uhn.fhir.jpa.mdm.svc;
import ca.uhn.fhir.empi.util.EmpiUtil;
import ca.uhn.fhir.jpa.empi.BaseEmpiR4Test;
import ca.uhn.fhir.mdm.util.MdmUtil;
import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.r4.model.Patient;
import org.junit.jupiter.api.BeforeEach;
@ -14,14 +14,14 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class EmpiResourceDaoSvcTest extends BaseEmpiR4Test {
public class MdmResourceDaoSvcTest extends BaseMdmR4Test {
private static final String TEST_EID = "TEST_EID";
@Autowired
EmpiResourceDaoSvc myResourceDaoSvc;
MdmResourceDaoSvc myResourceDaoSvc;
@BeforeEach
public void before() {
super.loadEmpiSearchParameters();
super.loadMdmSearchParameters();
}
@Test
@ -32,7 +32,7 @@ public class EmpiResourceDaoSvcTest extends BaseEmpiR4Test {
Patient badSourcePatient = addExternalEID(createRedirectedGoldenPatient(new Patient()), TEST_EID);
EmpiUtil.setGoldenResourceRedirected(badSourcePatient);
MdmUtil.setGoldenResourceRedirected(badSourcePatient);
myPatientDao.update(badSourcePatient);
Optional<IAnyResource> foundPerson = myResourceDaoSvc.searchGoldenResourceByEID(TEST_EID, "Patient");
@ -41,7 +41,7 @@ public class EmpiResourceDaoSvcTest extends BaseEmpiR4Test {
}
@Test
public void testSearchPersonByEidExcludesNonEmpiManaged() {
public void testSearchPersonByEidExcludesNonMdmManaged() {
Patient goodSourcePatient = addExternalEID(createGoldenPatient(), TEST_EID);
myPatientDao.update(goodSourcePatient);

View File

@ -1,8 +1,8 @@
package ca.uhn.fhir.jpa.empi.svc;
package ca.uhn.fhir.jpa.mdm.svc;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.empi.api.IEmpiSettings;
import ca.uhn.fhir.empi.rules.json.EmpiRulesJson;
import ca.uhn.fhir.mdm.api.IMdmSettings;
import ca.uhn.fhir.mdm.rules.json.MdmRulesJson;
import org.hl7.fhir.r4.model.Patient;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@ -16,26 +16,27 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.when;
@ExtendWith(SpringExtension.class)
class EmpiResourceFilteringSvcMockTest {
class MdmResourceFilteringSvcMockTest {
@MockBean
private IEmpiSettings myEmpiSettings;
private IMdmSettings myEmpiSettings;
@MockBean
EmpiSearchParamSvc myEmpiSearchParamSvc;
MdmSearchParamSvc myMdmSearchParamSvc;
@MockBean
FhirContext myFhirContext;
@Autowired
private EmpiResourceFilteringSvc myEmpiResourceFilteringSvc;
private MdmResourceFilteringSvc myMdmResourceFilteringSvc;
@Configuration
static class SpringConfig {
@Bean EmpiResourceFilteringSvc empiResourceFilteringSvc() {
return new EmpiResourceFilteringSvc();
@Bean
MdmResourceFilteringSvc empiResourceFilteringSvc() {
return new MdmResourceFilteringSvc();
}
}
@Test
public void testEmptyCriteriaShouldBeProcessed() {
when(myEmpiSettings.getEmpiRules()).thenReturn(new EmpiRulesJson());
assertTrue(myEmpiResourceFilteringSvc.shouldBeProcessed(new Patient()));
when(myEmpiSettings.getMdmRules()).thenReturn(new MdmRulesJson());
assertTrue(myMdmResourceFilteringSvc.shouldBeProcessed(new Patient()));
}
}

View File

@ -1,6 +1,6 @@
package ca.uhn.fhir.jpa.empi.svc;
package ca.uhn.fhir.jpa.mdm.svc;
import ca.uhn.fhir.jpa.empi.BaseEmpiR4Test;
import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
import org.hl7.fhir.r4.model.BooleanType;
import org.hl7.fhir.r4.model.Patient;
import org.junit.jupiter.api.Test;
@ -10,10 +10,10 @@ import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
class EmpiResourceFilteringSvcTest extends BaseEmpiR4Test {
class MdmResourceFilteringSvcTest extends BaseMdmR4Test {
@Autowired
private EmpiResourceFilteringSvc myEmpiResourceFilteringSvc;
private MdmResourceFilteringSvc myMdmResourceFilteringSvc;
@Test
public void testFilterResourcesWhichHaveNoRelevantAttributes() {
@ -21,7 +21,7 @@ class EmpiResourceFilteringSvcTest extends BaseEmpiR4Test {
patient.setDeceased(new BooleanType(true)); //EMPI rules defined do not care about the deceased attribute.
//SUT
boolean shouldBeProcessed = myEmpiResourceFilteringSvc.shouldBeProcessed(patient);
boolean shouldBeProcessed = myMdmResourceFilteringSvc.shouldBeProcessed(patient);
assertThat(shouldBeProcessed, is(equalTo(false)));
}
@ -32,7 +32,7 @@ class EmpiResourceFilteringSvcTest extends BaseEmpiR4Test {
patient.addIdentifier().setValue("Hey I'm an ID! rules defined in empi-rules.json care about me!");
//SUT
boolean shouldBeProcessed = myEmpiResourceFilteringSvc.shouldBeProcessed(patient);
boolean shouldBeProcessed = myMdmResourceFilteringSvc.shouldBeProcessed(patient);
assertThat(shouldBeProcessed, is(equalTo(true)));
}

View File

@ -20,10 +20,6 @@ package ca.uhn.fhir.jpa.migrate.tasks;
* #L%
*/
import ca.uhn.fhir.jpa.entity.EmpiLink;
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
import ca.uhn.fhir.jpa.entity.TermConceptMap;
import ca.uhn.fhir.jpa.entity.TermValueSet;
import ca.uhn.fhir.jpa.migrate.DriverTypeEnum;
import ca.uhn.fhir.jpa.migrate.taskdef.ArbitrarySqlTask;
import ca.uhn.fhir.jpa.migrate.taskdef.CalculateHashesTask;
@ -140,6 +136,7 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
empiLink.addColumn("20200715.4", "VECTOR").nullable().type(ColumnTypeEnum.LONG);
empiLink.addColumn("20200715.5", "SCORE").nullable().type(ColumnTypeEnum.FLOAT);
init510_20200725();
//EMPI Target Type

Some files were not shown because too many files have changed in this diff Show More