EMPI to MDM
This commit is contained in:
parent
03643a7b3c
commit
8a7dc4e80b
|
@ -1710,7 +1710,7 @@ public enum Pointcut {
|
||||||
* Hooks may accept the following parameters:
|
* Hooks may accept the following parameters:
|
||||||
* <ul>
|
* <ul>
|
||||||
* <li>ca.uhn.fhir.rest.server.messaging.ResourceOperationMessage - This parameter should not be modified as processing is complete when this hook is invoked.</li>
|
* <li>ca.uhn.fhir.rest.server.messaging.ResourceOperationMessage - This parameter should not be modified as processing is complete when this hook is invoked.</li>
|
||||||
* <li>ca.uhn.fhir.empi.model.TransactionLogMessages - This parameter is for informational messages provided by the EMPI module during EMPI procesing. .</li>
|
* <li>ca.uhn.fhir.rest.server.TransactionLogMessages - This parameter is for informational messages provided by the MDM module during MDM procesing.</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
* </p>
|
* </p>
|
||||||
* <p>
|
* <p>
|
||||||
|
|
|
@ -28,7 +28,7 @@ This automatic linking is done via configurable matching rules that create links
|
||||||
of the match configured in these rules, the link will be set to either POSSIBLE_MATCH or MATCH.
|
of the match configured in these rules, the link will be set to either POSSIBLE_MATCH or MATCH.
|
||||||
|
|
||||||
It is important to note that before a resource is processed by MDM, it is first checked to ensure that it has at least
|
It is important to note that before a resource is processed by MDM, it is first checked to ensure that it has at least
|
||||||
one attribute that the MDM system cares about, as defined in the `empi-rules.json` file. If the incoming resource has
|
one attribute that the MDM system cares about, as defined in the `mdm-rules.json` file. If the incoming resource has
|
||||||
no such attributes, then MDM processing does not occur on it. In this case, no Golden Resource Patient is created for
|
no such attributes, then MDM processing does not occur on it. In this case, no Golden Resource Patient is created for
|
||||||
them. If in the future that Patient is updated to contain attributes the MDM system does concern itself with, it will
|
them. If in the future that Patient is updated to contain attributes the MDM system does concern itself with, it will
|
||||||
be processed at that time.
|
be processed at that time.
|
||||||
|
@ -110,6 +110,6 @@ no longer consider them as a POSSIBLE_DUPLICATE going forward. POSSIBLE_DUPLICAT
|
||||||
When MDM is enabled, the HAPI FHIR JPA Server does the following things on startup:
|
When MDM is enabled, the HAPI FHIR JPA Server does the following things on startup:
|
||||||
|
|
||||||
1. It enables the MESSAGE subscription type and starts up the internal subscription engine.
|
1. It enables the MESSAGE subscription type and starts up the internal subscription engine.
|
||||||
1. It creates two MESSAGE subscriptions, called 'empi-patient' and 'empi-practitioner' that match all incoming Patient and Practitioner resources and send them to an internal queue called "empi". The JPA Server listens to this queue and links incoming resources to Persons.
|
1. It creates two MESSAGE subscriptions, called 'mdm-patient' and 'mdm-practitioner' that match all incoming MDM managed resources and send them to an internal queue called "mdm". The JPA Server listens to this queue and links incoming resources to the appropriate golden resources.
|
||||||
1. The [MDM Operations](/hapi-fhir/docs/server_jpa_mdm/mdm_operations.html) are registered with the server.
|
1. The [MDM Operations](/hapi-fhir/docs/server_jpa_mdm/mdm_operations.html) are registered with the server.
|
||||||
1. It registers a new dao interceptor that restricts access to MDM managed Golden Patient records.
|
1. It registers a new dao interceptor that restricts access to MDM managed Golden Patient records.
|
||||||
|
|
|
@ -11,6 +11,7 @@ Here is an example of a full HAPI MDM rules json document:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"version": "1",
|
"version": "1",
|
||||||
|
"mdmTypes" : ["Patient", "Practitioner"],
|
||||||
"candidateSearchParams": [
|
"candidateSearchParams": [
|
||||||
{
|
{
|
||||||
"resourceType": "Patient",
|
"resourceType": "Patient",
|
||||||
|
|
|
@ -42,9 +42,11 @@ import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class MdmSubscriptionLoader {
|
public class MdmSubscriptionLoader {
|
||||||
|
|
||||||
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
|
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
|
||||||
|
|
||||||
public static final String MDM_SUBSCIPRION_ID_PREFIX = "empi-";
|
public static final String MDM_SUBSCIPRION_ID_PREFIX = "mdm-";
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public FhirContext myFhirContext;
|
public FhirContext myFhirContext;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
|
|
@ -267,7 +267,7 @@ public class MdmLinkDaoSvc {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Persist an EmpiLink to the database.
|
* Persist an MDM link to the database.
|
||||||
*
|
*
|
||||||
* @param theMdmLink the link to save.
|
* @param theMdmLink the link to save.
|
||||||
* @return the persisted {@link MdmLink} entity.
|
* @return the persisted {@link MdmLink} entity.
|
||||||
|
@ -284,7 +284,7 @@ public class MdmLinkDaoSvc {
|
||||||
/**
|
/**
|
||||||
* Given an example {@link MdmLink}, return all links from the database which match the example.
|
* 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.
|
* @param theExampleLink The MDM link containing the data we would like to search for.
|
||||||
* @return a list of {@link MdmLink} entities which match the example.
|
* @return a list of {@link MdmLink} entities which match the example.
|
||||||
*/
|
*/
|
||||||
public List<MdmLink> findMdmLinkByExample(Example<MdmLink> theExampleLink) {
|
public List<MdmLink> findMdmLinkByExample(Example<MdmLink> theExampleLink) {
|
||||||
|
|
|
@ -43,6 +43,7 @@ import java.util.Optional;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class GoldenResourceMergerSvcImpl implements IGoldenResourceMergerSvc {
|
public class GoldenResourceMergerSvcImpl implements IGoldenResourceMergerSvc {
|
||||||
|
|
||||||
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
|
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -50,7 +51,7 @@ public class GoldenResourceMergerSvcImpl implements IGoldenResourceMergerSvc {
|
||||||
@Autowired
|
@Autowired
|
||||||
MdmLinkDaoSvc myMdmLinkDaoSvc;
|
MdmLinkDaoSvc myMdmLinkDaoSvc;
|
||||||
@Autowired
|
@Autowired
|
||||||
IMdmLinkSvc myEmpiLinkSvc;
|
IMdmLinkSvc myMdmLinkSvc;
|
||||||
@Autowired
|
@Autowired
|
||||||
IdHelperService myIdHelperService;
|
IdHelperService myIdHelperService;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -170,7 +171,7 @@ public class GoldenResourceMergerSvcImpl implements IGoldenResourceMergerSvc {
|
||||||
|
|
||||||
private Optional<MdmLink> findFirstLinkWithMatchingTarget(List<MdmLink> theMdmLinks, MdmLink theLinkWithTargetToMatch) {
|
private Optional<MdmLink> findFirstLinkWithMatchingTarget(List<MdmLink> theMdmLinks, MdmLink theLinkWithTargetToMatch) {
|
||||||
return theMdmLinks.stream()
|
return theMdmLinks.stream()
|
||||||
.filter(empiLink -> empiLink.getTargetPid().equals(theLinkWithTargetToMatch.getTargetPid()))
|
.filter(mdmLink -> mdmLink.getTargetPid().equals(theLinkWithTargetToMatch.getTargetPid()))
|
||||||
.findFirst();
|
.findFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,7 @@ import java.util.Optional;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class MdmEidUpdateService {
|
public class MdmEidUpdateService {
|
||||||
|
|
||||||
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
|
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -129,7 +130,7 @@ public class MdmEidUpdateService {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data class to hold context surrounding an update operation for an EMPI target.
|
* Data class to hold context surrounding an update operation for an MDM target.
|
||||||
*/
|
*/
|
||||||
class MdmUpdateContext {
|
class MdmUpdateContext {
|
||||||
private final boolean myHasEidsInCommon;
|
private final boolean myHasEidsInCommon;
|
||||||
|
|
|
@ -45,8 +45,8 @@ public class MdmLinkQuerySvcImpl implements IMdmLinkQuerySvc {
|
||||||
MdmLinkDaoSvc myMdmLinkDaoSvc;
|
MdmLinkDaoSvc myMdmLinkDaoSvc;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Stream<MdmLinkJson> queryLinks(IIdType thePersonId, IIdType theTargetId, MdmMatchResultEnum theMatchResult, MdmLinkSourceEnum theLinkSource, MdmTransactionContext theMdmTransactionContext) {
|
public Stream<MdmLinkJson> queryLinks(IIdType theGoldenResourceId, IIdType theTargetId, MdmMatchResultEnum theMatchResult, MdmLinkSourceEnum theLinkSource, MdmTransactionContext theMdmContext) {
|
||||||
Example<MdmLink> exampleLink = exampleLinkFromParameters(thePersonId, theTargetId, theMatchResult, theLinkSource);
|
Example<MdmLink> exampleLink = exampleLinkFromParameters(theGoldenResourceId, theTargetId, theMatchResult, theLinkSource);
|
||||||
return myMdmLinkDaoSvc.findMdmLinkByExample(exampleLink).stream()
|
return myMdmLinkDaoSvc.findMdmLinkByExample(exampleLink).stream()
|
||||||
.filter(mdmLink -> mdmLink.getMatchResult() != MdmMatchResultEnum.POSSIBLE_DUPLICATE)
|
.filter(mdmLink -> mdmLink.getMatchResult() != MdmMatchResultEnum.POSSIBLE_DUPLICATE)
|
||||||
.map(this::toJson);
|
.map(this::toJson);
|
||||||
|
|
|
@ -159,7 +159,7 @@ public class MdmLinkUpdaterSvcImpl implements IMdmLinkUpdaterSvc {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!MdmUtil.isMdmManaged(theGoldenResource) || !MdmUtil.isMdmManaged(theTarget)) {
|
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");
|
throw new InvalidRequestException("Only MDM Managed Golden Resources may be updated via this operation. The resource provided is not tagged as managed by hapi-mdm");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,10 +59,10 @@ public class MdmMatchLinkSvc {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given an MDM Target (consisting of either a Patient or a Practitioner), find a suitable Person candidate for them,
|
* 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.
|
* or create one if one does not exist. Performs matching based on rules defined in mdm-rules.json.
|
||||||
* Does nothing if resource is determined to be not managed by MDM.
|
* 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 theResource the incoming MDM target, which can be any supported MDM type.
|
||||||
* @param theMdmTransactionContext
|
* @param theMdmTransactionContext
|
||||||
* @return an {@link TransactionLogMessages} which contains all informational messages related to MDM processing of this resource.
|
* @return an {@link TransactionLogMessages} which contains all informational messages related to MDM processing of this resource.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -48,6 +48,7 @@ import static ca.uhn.fhir.mdm.api.MdmConstants.ALL_RESOURCE_SEARCH_PARAM_TYPE;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class MdmCandidateSearchSvc {
|
public class MdmCandidateSearchSvc {
|
||||||
|
|
||||||
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
|
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -65,7 +66,7 @@ public class MdmCandidateSearchSvc {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a target resource, search for all resources that are considered an EMPI match based on defined EMPI rules.
|
* Given a target resource, search for all resources that are considered an MDM match based on defined MDM rules.
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @param theResourceType
|
* @param theResourceType
|
||||||
|
@ -79,8 +80,8 @@ public class MdmCandidateSearchSvc {
|
||||||
List<String> filterCriteria = buildFilterQuery(filterSearchParams, theResourceType);
|
List<String> filterCriteria = buildFilterQuery(filterSearchParams, theResourceType);
|
||||||
List<MdmResourceSearchParamJson> candidateSearchParams = myMdmSettings.getMdmRules().getCandidateSearchParams();
|
List<MdmResourceSearchParamJson> candidateSearchParams = myMdmSettings.getMdmRules().getCandidateSearchParams();
|
||||||
|
|
||||||
//If there are zero EmpiResourceSearchParamJson, we end up only making a single search, otherwise we
|
//If there are zero MdmResourceSearchParamJson, we end up only making a single search, otherwise we
|
||||||
//must perform one search per EmpiResourceSearchParamJson.
|
//must perform one search per MdmResourceSearchParamJson.
|
||||||
if (candidateSearchParams.isEmpty()) {
|
if (candidateSearchParams.isEmpty()) {
|
||||||
searchForIdsAndAddToMap(theResourceType, theResource, matchedPidsToResources, filterCriteria, null);
|
searchForIdsAndAddToMap(theResourceType, theResource, matchedPidsToResources, filterCriteria, null);
|
||||||
} else {
|
} else {
|
||||||
|
@ -129,7 +130,7 @@ public class MdmCandidateSearchSvc {
|
||||||
|
|
||||||
searchParameterMap.setLoadSynchronous(true);
|
searchParameterMap.setLoadSynchronous(true);
|
||||||
|
|
||||||
//TODO EMPI this will blow up under large scale i think.
|
//TODO MDM this will blow up under large scale i think.
|
||||||
//3.
|
//3.
|
||||||
IFhirResourceDao<?> resourceDao = myDaoRegistry.getResourceDao(theResourceType);
|
IFhirResourceDao<?> resourceDao = myDaoRegistry.getResourceDao(theResourceType);
|
||||||
IBundleProvider search = resourceDao.search(searchParameterMap);
|
IBundleProvider search = resourceDao.search(searchParameterMap);
|
||||||
|
|
|
@ -31,6 +31,7 @@ import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class MdmGoldenResourceFindingSvc {
|
public class MdmGoldenResourceFindingSvc {
|
||||||
|
|
||||||
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
|
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@ -52,8 +53,8 @@ public class MdmGoldenResourceFindingSvc {
|
||||||
* 0. First, check the incoming Resource for an EID. If it is present, and we can find a Person with this EID, it automatically matches.
|
* 0. First, check the incoming Resource for an EID. If it is present, and we can find a Person with this EID, it automatically matches.
|
||||||
* 1. First, check link table for any entries where this baseresource is the target of a person. If found, return.
|
* 1. First, check link table for any entries where this baseresource is the target of a person. If found, return.
|
||||||
* 2. If none are found, attempt to find Person Resources which link to this theResource.
|
* 2. If none are found, attempt to find Person Resources which link to this theResource.
|
||||||
* 3. If none are found, attempt to find Person Resources similar to our incoming resource based on the EMPI rules and field matchers.
|
* 3. If none are found, attempt to find Person Resources similar to our incoming resource based on the MDM rules and field matchers.
|
||||||
* 4. If none are found, attempt to find Persons that are linked to Patients/Practitioners that are similar to our incoming resource based on the EMPI rules and
|
* 4. If none are found, attempt to find Persons that are linked to Patients/Practitioners that are similar to our incoming resource based on the MDM rules and
|
||||||
* field matchers.
|
* field matchers.
|
||||||
*
|
*
|
||||||
* @param theResource the {@link IBaseResource} we are attempting to find matching candidate Persons for.
|
* @param theResource the {@link IBaseResource} we are attempting to find matching candidate Persons for.
|
||||||
|
@ -67,9 +68,8 @@ public class MdmGoldenResourceFindingSvc {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matchedSourceResourceCandidates.isEmpty()) {
|
if (matchedSourceResourceCandidates.isEmpty()) {
|
||||||
//OK, so we have not found any links in the EmpiLink table with us as a target. Next, let's find possible Patient/Practitioner
|
//OK, so we have not found any links in the MdmLink table with us as a target. Next, let's find
|
||||||
//matches by following EMPI rules.
|
//possible Golden Resources matches by following MDM rules.
|
||||||
|
|
||||||
matchedSourceResourceCandidates = myFindCandidateByScoreSvc.findCandidates(theResource);
|
matchedSourceResourceCandidates = myFindCandidateByScoreSvc.findCandidates(theResource);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -139,12 +139,6 @@ abstract public class BaseMdmR4Test extends BaseJpaR4Test {
|
||||||
protected void saveLink(MdmLink theMdmLink) {
|
protected void saveLink(MdmLink theMdmLink) {
|
||||||
myMdmLinkDaoSvc.save(theMdmLink);
|
myMdmLinkDaoSvc.save(theMdmLink);
|
||||||
}
|
}
|
||||||
//
|
|
||||||
// @Nonnull
|
|
||||||
// protected Patient createUnmanagedSourceResource() {
|
|
||||||
// // return createGoldenPatient(new Patient(), false);
|
|
||||||
// return createGoldenPatient(new Patient(), false);
|
|
||||||
// }
|
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
protected Patient createGoldenPatient() {
|
protected Patient createGoldenPatient() {
|
||||||
|
@ -169,7 +163,7 @@ abstract public class BaseMdmR4Test extends BaseJpaR4Test {
|
||||||
@Nonnull
|
@Nonnull
|
||||||
protected Patient createPatient(Patient thePatient, boolean theMdmManaged, boolean isRedirect) {
|
protected Patient createPatient(Patient thePatient, boolean theMdmManaged, boolean isRedirect) {
|
||||||
if (theMdmManaged) {
|
if (theMdmManaged) {
|
||||||
MdmUtil.setEmpiManaged(thePatient);
|
MdmUtil.setMdmManaged(thePatient);
|
||||||
if (isRedirect) {
|
if (isRedirect) {
|
||||||
MdmUtil.setGoldenResourceRedirected(thePatient);
|
MdmUtil.setGoldenResourceRedirected(thePatient);
|
||||||
} else {
|
} else {
|
||||||
|
@ -185,7 +179,7 @@ abstract public class BaseMdmR4Test extends BaseJpaR4Test {
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
protected Patient createPatient(Patient thePatient) {
|
protected Patient createPatient(Patient thePatient) {
|
||||||
//Note that since our empi-rules block on active=true, all patients must be active.
|
//Note that since our mdm-rules block on active=true, all patients must be active.
|
||||||
thePatient.setActive(true);
|
thePatient.setActive(true);
|
||||||
|
|
||||||
DaoMethodOutcome outcome = myPatientDao.create(thePatient);
|
DaoMethodOutcome outcome = myPatientDao.create(thePatient);
|
||||||
|
@ -197,7 +191,7 @@ abstract public class BaseMdmR4Test extends BaseJpaR4Test {
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
protected Medication createMedication(Medication theMedication) {
|
protected Medication createMedication(Medication theMedication) {
|
||||||
//Note that since our empi-rules block on active=true, all patients must be active.
|
//Note that since our mdm-rules block on active=true, all patients must be active.
|
||||||
DaoMethodOutcome outcome = myMedicationDao.create(theMedication);
|
DaoMethodOutcome outcome = myMedicationDao.create(theMedication);
|
||||||
Medication medication = (Medication) outcome.getResource();
|
Medication medication = (Medication) outcome.getResource();
|
||||||
medication.setId(outcome.getId());
|
medication.setId(outcome.getId());
|
||||||
|
@ -206,7 +200,7 @@ abstract public class BaseMdmR4Test extends BaseJpaR4Test {
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
protected Practitioner createPractitioner(Practitioner thePractitioner) {
|
protected Practitioner createPractitioner(Practitioner thePractitioner) {
|
||||||
//Note that since our empi-rules block on active=true, all patients must be active.
|
//Note that since our mdm-rules block on active=true, all patients must be active.
|
||||||
thePractitioner.setActive(true);
|
thePractitioner.setActive(true);
|
||||||
DaoMethodOutcome daoMethodOutcome = myPractitionerDao.create(thePractitioner);
|
DaoMethodOutcome daoMethodOutcome = myPractitionerDao.create(thePractitioner);
|
||||||
thePractitioner.setId(daoMethodOutcome.getId());
|
thePractitioner.setId(daoMethodOutcome.getId());
|
||||||
|
@ -329,7 +323,7 @@ abstract public class BaseMdmR4Test extends BaseJpaR4Test {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected <T extends IBaseResource> T getTargetResourceFromEmpiLink(MdmLink theMdmLink, String theResourceType) {
|
protected <T extends IBaseResource> T getTargetResourceFromMdmLink(MdmLink theMdmLink, String theResourceType) {
|
||||||
IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(theResourceType);
|
IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(theResourceType);
|
||||||
return (T) resourceDao.readByPid(new ResourcePersistentId(theMdmLink.getGoldenResourcePid()));
|
return (T) resourceDao.readByPid(new ResourcePersistentId(theMdmLink.getGoldenResourcePid()));
|
||||||
}
|
}
|
||||||
|
@ -470,7 +464,7 @@ abstract public class BaseMdmR4Test extends BaseJpaR4Test {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void logAllLinks() {
|
protected void logAllLinks() {
|
||||||
ourLog.info("Logging all EMPI Links:");
|
ourLog.info("Logging all MDM Links:");
|
||||||
List<MdmLink> links = myMdmLinkDao.findAll();
|
List<MdmLink> links = myMdmLinkDao.findAll();
|
||||||
for (MdmLink link : links) {
|
for (MdmLink link : links) {
|
||||||
ourLog.info(link.toString());
|
ourLog.info(link.toString());
|
||||||
|
@ -530,8 +524,8 @@ abstract public class BaseMdmR4Test extends BaseJpaR4Test {
|
||||||
|
|
||||||
|
|
||||||
protected void printLinks() {
|
protected void printLinks() {
|
||||||
myMdmLinkDao.findAll().forEach(empiLink -> {
|
myMdmLinkDao.findAll().forEach(mdmLink -> {
|
||||||
ourLog.info(String.valueOf(empiLink));
|
ourLog.info(String.valueOf(mdmLink));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,16 +17,16 @@ import java.io.IOException;
|
||||||
@Configuration
|
@Configuration
|
||||||
public abstract class BaseTestMdmConfig {
|
public abstract class BaseTestMdmConfig {
|
||||||
|
|
||||||
@Value("${empi.prevent_eid_updates:true}")
|
@Value("${mdm.prevent_eid_updates:true}")
|
||||||
boolean myPreventEidUpdates;
|
boolean myPreventEidUpdates;
|
||||||
|
|
||||||
@Value("${empi.prevent_multiple_eids:true}")
|
@Value("${mdm.prevent_multiple_eids:true}")
|
||||||
boolean myPreventMultipleEids;
|
boolean myPreventMultipleEids;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
IMdmSettings mdmSettings(MdmRuleValidator theMdmRuleValidator) throws IOException {
|
IMdmSettings mdmSettings(MdmRuleValidator theMdmRuleValidator) throws IOException {
|
||||||
DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
|
DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
|
||||||
Resource resource = resourceLoader.getResource("empi/empi-rules.json");
|
Resource resource = resourceLoader.getResource("mdm/mdm-rules.json");
|
||||||
String json = IOUtils.toString(resource.getInputStream(), Charsets.UTF_8);
|
String json = IOUtils.toString(resource.getInputStream(), Charsets.UTF_8);
|
||||||
return new MdmSettings(theMdmRuleValidator)
|
return new MdmSettings(theMdmRuleValidator)
|
||||||
.setEnabled(false)
|
.setEnabled(false)
|
||||||
|
|
|
@ -18,10 +18,6 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
public class MdmLinkDaoSvcTest extends BaseMdmR4Test {
|
public class MdmLinkDaoSvcTest extends BaseMdmR4Test {
|
||||||
@Autowired
|
|
||||||
MdmLinkDaoSvc myMdmLinkDaoSvc;
|
|
||||||
@Autowired
|
|
||||||
IMdmSettings myEmpiSettings;
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreate() {
|
public void testCreate() {
|
||||||
|
@ -47,7 +43,7 @@ public class MdmLinkDaoSvcTest extends BaseMdmR4Test {
|
||||||
@Test
|
@Test
|
||||||
public void testNew() {
|
public void testNew() {
|
||||||
MdmLink newLink = myMdmLinkDaoSvc.newMdmLink();
|
MdmLink newLink = myMdmLinkDaoSvc.newMdmLink();
|
||||||
MdmRulesJson rules = myEmpiSettings.getMdmRules();
|
MdmRulesJson rules = myMdmSettings.getMdmRules();
|
||||||
assertEquals("1", rules.getVersion());
|
assertEquals("1", rules.getVersion());
|
||||||
assertEquals(rules.getVersion(), newLink.getVersion());
|
assertEquals(rules.getVersion(), newLink.getVersion());
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,10 +7,10 @@ import org.junit.jupiter.api.Test;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
public class MdmEnumTest {
|
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
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void mdmEnumOrdinals() {
|
||||||
|
// This test is here to enforce that new values in these enums are always added to the end
|
||||||
assertEquals(6, MdmMatchResultEnum.values().length);
|
assertEquals(6, MdmMatchResultEnum.values().length);
|
||||||
assertEquals(MdmMatchResultEnum.REDIRECT, MdmMatchResultEnum.values()[MdmMatchResultEnum.values().length - 1]);
|
assertEquals(MdmMatchResultEnum.REDIRECT, MdmMatchResultEnum.values()[MdmMatchResultEnum.values().length - 1]);
|
||||||
|
|
||||||
|
|
|
@ -27,9 +27,9 @@ public class MdmHelperConfig {
|
||||||
|
|
||||||
@Primary
|
@Primary
|
||||||
@Bean
|
@Bean
|
||||||
IMdmSettings empiSettings(MdmRuleValidator theMdmRuleValidator) throws IOException {
|
IMdmSettings mdmSettings(MdmRuleValidator theMdmRuleValidator) throws IOException {
|
||||||
DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
|
DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
|
||||||
Resource resource = resourceLoader.getResource("empi/empi-rules.json");
|
Resource resource = resourceLoader.getResource("mdm/mdm-rules.json");
|
||||||
String json = IOUtils.toString(resource.getInputStream(), Charsets.UTF_8);
|
String json = IOUtils.toString(resource.getInputStream(), Charsets.UTF_8);
|
||||||
|
|
||||||
// Set Enabled to true, and set strict mode.
|
// Set Enabled to true, and set strict mode.
|
||||||
|
|
|
@ -26,7 +26,7 @@ public class MdmExpungeTest extends BaseMdmR4Test {
|
||||||
@Autowired
|
@Autowired
|
||||||
IInterceptorService myInterceptorService;
|
IInterceptorService myInterceptorService;
|
||||||
@Autowired
|
@Autowired
|
||||||
IMdmStorageInterceptor myEmpiStorageInterceptor;
|
IMdmStorageInterceptor myMdmStorageInterceptor;
|
||||||
@Autowired
|
@Autowired
|
||||||
DaoConfig myDaoConfig;
|
DaoConfig myDaoConfig;
|
||||||
private ResourceTable myTargetEntity;
|
private ResourceTable myTargetEntity;
|
||||||
|
@ -50,7 +50,7 @@ public class MdmExpungeTest extends BaseMdmR4Test {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUninterceptedDeleteRemovesEMPIReference() {
|
public void testUninterceptedDeleteRemovesMdmReference() {
|
||||||
assertEquals(1, myMdmLinkDao.count());
|
assertEquals(1, myMdmLinkDao.count());
|
||||||
myPatientDao.delete(myTargetEntity.getIdDt());
|
myPatientDao.delete(myTargetEntity.getIdDt());
|
||||||
assertEquals(1, myMdmLinkDao.count());
|
assertEquals(1, myMdmLinkDao.count());
|
||||||
|
@ -63,14 +63,14 @@ public class MdmExpungeTest extends BaseMdmR4Test {
|
||||||
assertThat(e.getMessage(), containsString("ViolationException"));
|
assertThat(e.getMessage(), containsString("ViolationException"));
|
||||||
assertThat(e.getMessage(), containsString("FK_EMPI_LINK_TARGET"));
|
assertThat(e.getMessage(), containsString("FK_EMPI_LINK_TARGET"));
|
||||||
}
|
}
|
||||||
myInterceptorService.registerInterceptor(myEmpiStorageInterceptor);
|
myInterceptorService.registerInterceptor(myMdmStorageInterceptor);
|
||||||
myPatientDao.expunge(myTargetId.toVersionless(), expungeOptions, null);
|
myPatientDao.expunge(myTargetId.toVersionless(), expungeOptions, null);
|
||||||
assertEquals(0, myMdmLinkDao.count());
|
assertEquals(0, myMdmLinkDao.count());
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
public void afterUnregisterInterceptor() {
|
public void afterUnregisterInterceptor() {
|
||||||
myInterceptorService.unregisterInterceptor(myEmpiStorageInterceptor);
|
myInterceptorService.unregisterInterceptor(myMdmStorageInterceptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -179,7 +179,7 @@ public class MdmStorageInterceptorIT extends BaseMdmR4Test {
|
||||||
assertNotNull(daoMethodOutcome.getId());
|
assertNotNull(daoMethodOutcome.getId());
|
||||||
|
|
||||||
//Updating that person to set them as MDM 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");
|
person.getMeta().addTag(SYSTEM_MDM_MANAGED, CODE_HAPI_MDM_MANAGED, "User is managed by MDM");
|
||||||
try {
|
try {
|
||||||
myMdmHelper.doUpdateResource(person, true);
|
myMdmHelper.doUpdateResource(person, true);
|
||||||
fail();
|
fail();
|
||||||
|
@ -190,19 +190,19 @@ public class MdmStorageInterceptorIT extends BaseMdmR4Test {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMdmManagedPersonCannotBeModifiedByPersonUpdateRequest() 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.
|
// When MDM is enabled, only the MDM system is allowed to modify Person links of Persons with the MDM-MANAGED tag.
|
||||||
Patient patient = new Patient();
|
Patient patient = new Patient();
|
||||||
IIdType patientId = myMdmHelper.createWithLatch(buildPaulPatient()).getDaoMethodOutcome().getId().toUnqualifiedVersionless();
|
IIdType patientId = myMdmHelper.createWithLatch(buildPaulPatient()).getDaoMethodOutcome().getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
patient.setId(patientId);
|
patient.setId(patientId);
|
||||||
|
|
||||||
//Updating a Person who was created via EMPI should fail.
|
// Updating a Golden Resource Patient who was created via MDM should fail.
|
||||||
MdmLink mdmLink = myMdmLinkDaoSvc.getMatchedLinkForTargetPid(myIdHelperService.getPidOrNull(patient)).get();
|
MdmLink mdmLink = myMdmLinkDaoSvc.getMatchedLinkForTargetPid(myIdHelperService.getPidOrNull(patient)).get();
|
||||||
Long sourcePatientPid = mdmLink.getGoldenResourcePid();
|
Long sourcePatientPid = mdmLink.getGoldenResourcePid();
|
||||||
Patient empiSourcePatient = (Patient) myPatientDao.readByPid(new ResourcePersistentId(sourcePatientPid));
|
Patient goldenResourcePatient = (Patient) myPatientDao.readByPid(new ResourcePersistentId(sourcePatientPid));
|
||||||
empiSourcePatient.setGender(Enumerations.AdministrativeGender.MALE);
|
goldenResourcePatient.setGender(Enumerations.AdministrativeGender.MALE);
|
||||||
try {
|
try {
|
||||||
myMdmHelper.doUpdateResource(empiSourcePatient, true);
|
myMdmHelper.doUpdateResource(goldenResourcePatient, true);
|
||||||
fail();
|
fail();
|
||||||
} catch (ForbiddenOperationException e) {
|
} catch (ForbiddenOperationException e) {
|
||||||
assertThat(e.getMessage(), startsWith("Cannot create or modify Resources that are managed by MDM."));
|
assertThat(e.getMessage(), startsWith("Cannot create or modify Resources that are managed by MDM."));
|
||||||
|
@ -213,12 +213,12 @@ public class MdmStorageInterceptorIT extends BaseMdmR4Test {
|
||||||
public void testMdmPointcutReceivesTransactionLogMessages() throws InterruptedException {
|
public void testMdmPointcutReceivesTransactionLogMessages() throws InterruptedException {
|
||||||
MdmHelperR4.OutcomeAndLogMessageWrapper wrapper = myMdmHelper.createWithLatch(buildJanePatient());
|
MdmHelperR4.OutcomeAndLogMessageWrapper wrapper = myMdmHelper.createWithLatch(buildJanePatient());
|
||||||
|
|
||||||
TransactionLogMessages empiTransactionLogMessages = wrapper.getLogMessages();
|
TransactionLogMessages mdmTransactionLogMessages = wrapper.getLogMessages();
|
||||||
|
|
||||||
//There is no TransactionGuid here as there is no TransactionLog in this context.
|
//There is no TransactionGuid here as there is no TransactionLog in this context.
|
||||||
assertThat(empiTransactionLogMessages.getTransactionGuid(), is(nullValue()));
|
assertThat(mdmTransactionLogMessages.getTransactionGuid(), is(nullValue()));
|
||||||
|
|
||||||
List<String> messages = empiTransactionLogMessages.getValues();
|
List<String> messages = mdmTransactionLogMessages.getValues();
|
||||||
assertThat(messages.isEmpty(), is(false));
|
assertThat(messages.isEmpty(), is(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public abstract class BaseSourceResourceMatcher extends TypeSafeMatcher<IAnyResource> {
|
public abstract class BaseSourceResourceMatcher extends TypeSafeMatcher<IAnyResource> {
|
||||||
|
|
||||||
private static final Logger ourLog = LoggerFactory.getLogger(BaseSourceResourceMatcher.class);
|
private static final Logger ourLog = LoggerFactory.getLogger(BaseSourceResourceMatcher.class);
|
||||||
|
|
||||||
protected IdHelperService myIdHelperService;
|
protected IdHelperService myIdHelperService;
|
||||||
|
@ -39,7 +40,7 @@ public abstract class BaseSourceResourceMatcher extends TypeSafeMatcher<IAnyReso
|
||||||
if (isGoldenRecord) {
|
if (isGoldenRecord) {
|
||||||
return myIdHelperService.getPidOrNull(theResource);
|
return myIdHelperService.getPidOrNull(theResource);
|
||||||
}
|
}
|
||||||
MdmLink matchLink = getMatchedEmpiLink(theResource);
|
MdmLink matchLink = getMatchedMdmLink(theResource);
|
||||||
|
|
||||||
if (matchLink == null) {
|
if (matchLink == null) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -51,11 +52,11 @@ public abstract class BaseSourceResourceMatcher extends TypeSafeMatcher<IAnyReso
|
||||||
}
|
}
|
||||||
|
|
||||||
protected List<Long> getPossibleMatchedSourceResourcePidsFromTarget(IAnyResource theBaseResource) {
|
protected List<Long> getPossibleMatchedSourceResourcePidsFromTarget(IAnyResource theBaseResource) {
|
||||||
return getEmpiLinksForTarget(theBaseResource, MdmMatchResultEnum.POSSIBLE_MATCH).stream().map(MdmLink::getGoldenResourcePid).collect(Collectors.toList());
|
return getMdmLinksForTarget(theBaseResource, MdmMatchResultEnum.POSSIBLE_MATCH).stream().map(MdmLink::getGoldenResourcePid).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected MdmLink getMatchedEmpiLink(IAnyResource thePatientOrPractitionerResource) {
|
protected MdmLink getMatchedMdmLink(IAnyResource thePatientOrPractitionerResource) {
|
||||||
List<MdmLink> mdmLinks = getEmpiLinksForTarget(thePatientOrPractitionerResource, MdmMatchResultEnum.MATCH);
|
List<MdmLink> mdmLinks = getMdmLinksForTarget(thePatientOrPractitionerResource, MdmMatchResultEnum.MATCH);
|
||||||
if (mdmLinks.size() == 0) {
|
if (mdmLinks.size() == 0) {
|
||||||
return null;
|
return null;
|
||||||
} else if (mdmLinks.size() == 1) {
|
} else if (mdmLinks.size() == 1) {
|
||||||
|
@ -65,7 +66,7 @@ public abstract class BaseSourceResourceMatcher extends TypeSafeMatcher<IAnyReso
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected List<MdmLink> getEmpiLinksForTarget(IAnyResource theTargetResource, MdmMatchResultEnum theMatchResult) {
|
protected List<MdmLink> getMdmLinksForTarget(IAnyResource theTargetResource, MdmMatchResultEnum theMatchResult) {
|
||||||
Long pidOrNull = myIdHelperService.getPidOrNull(theTargetResource);
|
Long pidOrNull = myIdHelperService.getPidOrNull(theTargetResource);
|
||||||
List<MdmLink> matchLinkForTarget = myMdmLinkDaoSvc.getMdmLinksByTargetPidAndMatchResult(pidOrNull, theMatchResult);
|
List<MdmLink> matchLinkForTarget = myMdmLinkDaoSvc.getMdmLinksByTargetPidAndMatchResult(pidOrNull, theMatchResult);
|
||||||
if (!matchLinkForTarget.isEmpty()) {
|
if (!matchLinkForTarget.isEmpty()) {
|
||||||
|
|
|
@ -23,7 +23,7 @@ public class IsPossibleMatchWith extends BaseSourceResourceMatcher {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean matchesSafely(IAnyResource theIncomingResource) {
|
protected boolean matchesSafely(IAnyResource theIncomingResource) {
|
||||||
List<MdmLink> mdmLinks = getEmpiLinksForTarget(theIncomingResource, MdmMatchResultEnum.POSSIBLE_MATCH);
|
List<MdmLink> mdmLinks = getMdmLinksForTarget(theIncomingResource, MdmMatchResultEnum.POSSIBLE_MATCH);
|
||||||
|
|
||||||
List<Long> personPidsToMatch = myBaseResources.stream()
|
List<Long> personPidsToMatch = myBaseResources.stream()
|
||||||
.map(this::getMatchedResourcePidFromResource)
|
.map(this::getMatchedResourcePidFromResource)
|
||||||
|
@ -36,8 +36,11 @@ public class IsPossibleMatchWith extends BaseSourceResourceMatcher {
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Long> empiLinkSourcePersonPids = mdmLinks.stream().map(MdmLink::getGoldenResourcePid).collect(Collectors.toList());
|
List<Long> mdmLinkGoldenResourcePids = mdmLinks
|
||||||
return empiLinkSourcePersonPids.containsAll(personPidsToMatch);
|
.stream().map(MdmLink::getGoldenResourcePid)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
return mdmLinkGoldenResourcePids.containsAll(personPidsToMatch);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -48,7 +51,7 @@ public class IsPossibleMatchWith extends BaseSourceResourceMatcher {
|
||||||
@Override
|
@Override
|
||||||
protected void describeMismatchSafely(IAnyResource item, Description mismatchDescription) {
|
protected void describeMismatchSafely(IAnyResource item, Description mismatchDescription) {
|
||||||
super.describeMismatchSafely(item, mismatchDescription);
|
super.describeMismatchSafely(item, mismatchDescription);
|
||||||
mismatchDescription.appendText("No Empi Link With POSSIBLE_MATCH was found");
|
mismatchDescription.appendText("No MDM Link With POSSIBLE_MATCH was found");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Matcher<IAnyResource> possibleMatchWith(IdHelperService theIdHelperService, MdmLinkDaoSvc theMdmLinkDaoSvc, IAnyResource... theBaseResource) {
|
public static Matcher<IAnyResource> possibleMatchWith(IdHelperService theIdHelperService, MdmLinkDaoSvc theMdmLinkDaoSvc, IAnyResource... theBaseResource) {
|
||||||
|
|
|
@ -32,7 +32,7 @@ public abstract class BaseProviderR4Test extends BaseMdmR4Test {
|
||||||
|
|
||||||
private String defaultScript;
|
private String defaultScript;
|
||||||
|
|
||||||
protected void setEmpiRuleJson(String theString) throws IOException {
|
protected void setMdmRuleJson(String theString) throws IOException {
|
||||||
DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
|
DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
|
||||||
Resource resource = resourceLoader.getResource(theString);
|
Resource resource = resourceLoader.getResource(theString);
|
||||||
String json = IOUtils.toString(resource.getInputStream(), Charsets.UTF_8);
|
String json = IOUtils.toString(resource.getInputStream(), Charsets.UTF_8);
|
||||||
|
|
|
@ -73,7 +73,7 @@ public class MdmProviderBatchR4Test extends BaseLinkR4Test {
|
||||||
StringType criteria = null;
|
StringType criteria = null;
|
||||||
myMdmProviderR4.clearMdmLinks(null, myRequestDetails);
|
myMdmProviderR4.clearMdmLinks(null, myRequestDetails);
|
||||||
|
|
||||||
afterMdmLatch.runWithExpectedCount(1, () -> myMdmProviderR4.empiBatchOnAllTargets(new StringType("Medication"), criteria, null));
|
afterMdmLatch.runWithExpectedCount(1, () -> myMdmProviderR4.mdmBatchOnAllTargets(new StringType("Medication"), criteria, null));
|
||||||
assertLinkCount(1);
|
assertLinkCount(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,13 +82,13 @@ public class MdmProviderBatchR4Test extends BaseLinkR4Test {
|
||||||
StringType criteria = null;
|
StringType criteria = null;
|
||||||
myMdmProviderR4.clearMdmLinks(null, myRequestDetails);
|
myMdmProviderR4.clearMdmLinks(null, myRequestDetails);
|
||||||
|
|
||||||
afterMdmLatch.runWithExpectedCount(1, () -> myMdmProviderR4.empiBatchPractitionerType(criteria, null));
|
afterMdmLatch.runWithExpectedCount(1, () -> myMdmProviderR4.mdmBatchPractitionerType(criteria, null));
|
||||||
assertLinkCount(1);
|
assertLinkCount(1);
|
||||||
}
|
}
|
||||||
@Test
|
@Test
|
||||||
public void testBatchRunOnSpecificPractitioner() throws InterruptedException {
|
public void testBatchRunOnSpecificPractitioner() throws InterruptedException {
|
||||||
myMdmProviderR4.clearMdmLinks(null, myRequestDetails);
|
myMdmProviderR4.clearMdmLinks(null, myRequestDetails);
|
||||||
afterMdmLatch.runWithExpectedCount(1, () -> myMdmProviderR4.empiBatchPractitionerInstance(myPractitioner.getIdElement(), null));
|
afterMdmLatch.runWithExpectedCount(1, () -> myMdmProviderR4.mdmBatchPractitionerInstance(myPractitioner.getIdElement(), null));
|
||||||
assertLinkCount(1);
|
assertLinkCount(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,7 +96,7 @@ public class MdmProviderBatchR4Test extends BaseLinkR4Test {
|
||||||
public void testBatchRunOnNonExistentSpecificPractitioner() {
|
public void testBatchRunOnNonExistentSpecificPractitioner() {
|
||||||
myMdmProviderR4.clearMdmLinks(null, myRequestDetails);
|
myMdmProviderR4.clearMdmLinks(null, myRequestDetails);
|
||||||
try {
|
try {
|
||||||
myMdmProviderR4.empiBatchPractitionerInstance(new IdType("Practitioner/999"), null);
|
myMdmProviderR4.mdmBatchPractitionerInstance(new IdType("Practitioner/999"), null);
|
||||||
fail();
|
fail();
|
||||||
} catch (ResourceNotFoundException e){}
|
} catch (ResourceNotFoundException e){}
|
||||||
}
|
}
|
||||||
|
@ -106,7 +106,7 @@ public class MdmProviderBatchR4Test extends BaseLinkR4Test {
|
||||||
assertLinkCount(3);
|
assertLinkCount(3);
|
||||||
StringType criteria = null;
|
StringType criteria = null;
|
||||||
myMdmProviderR4.clearMdmLinks(null, myRequestDetails);
|
myMdmProviderR4.clearMdmLinks(null, myRequestDetails);
|
||||||
afterMdmLatch.runWithExpectedCount(1, () -> myMdmProviderR4.empiBatchPatientType(criteria, null));
|
afterMdmLatch.runWithExpectedCount(1, () -> myMdmProviderR4.mdmBatchPatientType(criteria, null));
|
||||||
assertLinkCount(1);
|
assertLinkCount(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,7 +114,7 @@ public class MdmProviderBatchR4Test extends BaseLinkR4Test {
|
||||||
public void testBatchRunOnSpecificPatient() throws InterruptedException {
|
public void testBatchRunOnSpecificPatient() throws InterruptedException {
|
||||||
assertLinkCount(3);
|
assertLinkCount(3);
|
||||||
myMdmProviderR4.clearMdmLinks(null, myRequestDetails);
|
myMdmProviderR4.clearMdmLinks(null, myRequestDetails);
|
||||||
afterMdmLatch.runWithExpectedCount(1, () -> myMdmProviderR4.empiBatchPatientInstance(myPatient.getIdElement(), null));
|
afterMdmLatch.runWithExpectedCount(1, () -> myMdmProviderR4.mdmBatchPatientInstance(myPatient.getIdElement(), null));
|
||||||
assertLinkCount(1);
|
assertLinkCount(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,7 +123,7 @@ public class MdmProviderBatchR4Test extends BaseLinkR4Test {
|
||||||
assertLinkCount(3);
|
assertLinkCount(3);
|
||||||
myMdmProviderR4.clearMdmLinks(null, myRequestDetails);
|
myMdmProviderR4.clearMdmLinks(null, myRequestDetails);
|
||||||
try {
|
try {
|
||||||
myMdmProviderR4.empiBatchPatientInstance(new IdType("Patient/999"), null);
|
myMdmProviderR4.mdmBatchPatientInstance(new IdType("Patient/999"), null);
|
||||||
fail();
|
fail();
|
||||||
} catch (ResourceNotFoundException e){}
|
} catch (ResourceNotFoundException e){}
|
||||||
}
|
}
|
||||||
|
@ -134,7 +134,7 @@ public class MdmProviderBatchR4Test extends BaseLinkR4Test {
|
||||||
StringType criteria = new StringType("");
|
StringType criteria = new StringType("");
|
||||||
myMdmProviderR4.clearMdmLinks(null, myRequestDetails);
|
myMdmProviderR4.clearMdmLinks(null, myRequestDetails);
|
||||||
afterMdmLatch.runWithExpectedCount(3, () -> {
|
afterMdmLatch.runWithExpectedCount(3, () -> {
|
||||||
myMdmProviderR4.empiBatchOnAllTargets(null, criteria, null);
|
myMdmProviderR4.mdmBatchOnAllTargets(null, criteria, null);
|
||||||
});
|
});
|
||||||
assertLinkCount(3);
|
assertLinkCount(3);
|
||||||
}
|
}
|
||||||
|
@ -146,7 +146,7 @@ public class MdmProviderBatchR4Test extends BaseLinkR4Test {
|
||||||
myMdmProviderR4.clearMdmLinks(null, myRequestDetails);
|
myMdmProviderR4.clearMdmLinks(null, myRequestDetails);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
myMdmProviderR4.empiBatchPractitionerType(criteria, null);
|
myMdmProviderR4.mdmBatchPractitionerType(criteria, null);
|
||||||
fail();
|
fail();
|
||||||
} catch(InvalidRequestException e) {
|
} 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")));
|
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")));
|
||||||
|
|
|
@ -145,7 +145,7 @@ public class MdmProviderMatchR4Test extends BaseProviderR4Test {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMatchWithEmptySearchParamCandidates() throws Exception {
|
public void testMatchWithEmptySearchParamCandidates() throws Exception {
|
||||||
setEmpiRuleJson("empi/empty-candidate-search-params.json");
|
setMdmRuleJson("mdm/empty-candidate-search-params.json");
|
||||||
Patient jane = buildJanePatient();
|
Patient jane = buildJanePatient();
|
||||||
jane.setActive(true);
|
jane.setActive(true);
|
||||||
Patient createdJane = createPatient(jane);
|
Patient createdJane = createPatient(jane);
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
package ca.uhn.fhir.jpa.mdm.provider;
|
package ca.uhn.fhir.jpa.mdm.provider;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.entity.MdmLink;
|
||||||
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
|
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
|
||||||
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
|
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
|
||||||
import ca.uhn.fhir.mdm.util.MdmUtil;
|
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.InvalidRequestException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||||
import org.hl7.fhir.r4.model.Patient;
|
import org.hl7.fhir.r4.model.Patient;
|
||||||
|
@ -14,7 +14,8 @@ import org.junit.jupiter.api.Test;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.*;
|
import static org.hamcrest.Matchers.hasSize;
|
||||||
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
@ -65,11 +66,6 @@ public class MdmProviderMergePersonsR4Test extends BaseProviderR4Test {
|
||||||
assertEquals(link.getGoldenResourcePid(), myToSourcePatient.getIdElement().toUnqualifiedVersionless().getIdPartAsLong());
|
assertEquals(link.getGoldenResourcePid(), myToSourcePatient.getIdElement().toUnqualifiedVersionless().getIdPartAsLong());
|
||||||
assertEquals(link.getMatchResult(), MdmMatchResultEnum.REDIRECT);
|
assertEquals(link.getMatchResult(), MdmMatchResultEnum.REDIRECT);
|
||||||
assertEquals(link.getLinkSource(), MdmLinkSourceEnum.MANUAL);
|
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));
|
|
||||||
//assertThat(links.get(0).getTarget().getReference(), is (myToSourcePatient.getIdElement().toUnqualifiedVersionless().getValue()));
|
|
||||||
//assertThat(links.get(0).getAssurance(), is (AssuranceLevelUtil.getAssuranceLevel(EmpiMatchResultEnum.REDIRECT, EmpiLinkSourceEnum.MANUAL).toR4()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -84,20 +80,6 @@ public class MdmProviderMergePersonsR4Test extends BaseProviderR4Test {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// INVALID ANYMORE - we support merging patients to patients now
|
|
||||||
// @Test
|
|
||||||
// public void testMergePatients() {
|
|
||||||
// try {
|
|
||||||
// StringType patientId = new StringType(createPatient().getIdElement().getValue());
|
|
||||||
// StringType otherPatientId = new StringType(createPatient().getIdElement().getValue());
|
|
||||||
// myEmpiProviderR4.mergeGoldenResources(patientId, otherPatientId, myRequestDetails);
|
|
||||||
// fail();
|
|
||||||
// } catch (InvalidRequestException e) {
|
|
||||||
// assertThat(e.getMessage(), endsWith("must have form Person/<id> where <id> is the id of the person"));
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNullParams() {
|
public void testNullParams() {
|
||||||
try {
|
try {
|
||||||
|
@ -122,24 +104,6 @@ public class MdmProviderMergePersonsR4Test extends BaseProviderR4Test {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBadParams() {
|
public void testBadParams() {
|
||||||
print(myFromSourcePatient);
|
|
||||||
print(myToSourcePatient);
|
|
||||||
|
|
||||||
// TODO NG - THESE ARE NOW INVALID
|
|
||||||
// try {
|
|
||||||
// myEmpiProviderR4.mergeGoldenResources(new StringType("Patient/1"), new StringType("Patient/2"), myRequestDetails);
|
|
||||||
// fail();
|
|
||||||
// } catch (InvalidRequestException e) {
|
|
||||||
// assertThat(e.getMessage(), endsWith(" must have form Person/<id> where <id> is the id of the person"));
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// try {
|
|
||||||
// myEmpiProviderR4.mergeGoldenResources(myFromSourcePatientId, new StringType("Patient/2"), myRequestDetails);
|
|
||||||
// fail();
|
|
||||||
// } catch (InvalidRequestException e) {
|
|
||||||
// assertThat(e.getMessage(), endsWith(" must have form Person/<id> where <id> is the id of the person"));
|
|
||||||
// }
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
myMdmProviderR4.mergeGoldenResources(new StringType("Person/1"), new StringType("Person/1"), myRequestDetails);
|
myMdmProviderR4.mergeGoldenResources(new StringType("Person/1"), new StringType("Person/1"), myRequestDetails);
|
||||||
fail();
|
fail();
|
||||||
|
|
|
@ -160,7 +160,7 @@ public class MdmProviderUpdateLinkR4Test extends BaseLinkR4Test {
|
||||||
myMdmProviderR4.updateLink(mySourcePatientId, new StringType(patient.getIdElement().getValue()), NO_MATCH_RESULT, myRequestDetails);
|
myMdmProviderR4.updateLink(mySourcePatientId, new StringType(patient.getIdElement().getValue()), NO_MATCH_RESULT, myRequestDetails);
|
||||||
fail();
|
fail();
|
||||||
} catch (InvalidRequestException e) {
|
} catch (InvalidRequestException e) {
|
||||||
assertEquals("The target is marked with the " + MdmConstants.CODE_NO_MDM_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 MDM linked.", e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
package ca.uhn.fhir.jpa.mdm.svc;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.mdm.provider.MdmProviderUpdateLinkR4Test;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests for this service are in the test for the provider that wraps this service:
|
|
||||||
* @see MdmProviderUpdateLinkR4Test
|
|
||||||
*/
|
|
||||||
public class EmpiLinkUpdaterSvcImplTest {
|
|
||||||
}
|
|
|
@ -153,7 +153,7 @@ public class MdmMatchLinkSvcMultipleEidModeTest extends BaseMdmR4Test {
|
||||||
.map(myIdHelperService::getPidOrNull)
|
.map(myIdHelperService::getPidOrNull)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
//The two Persons related to the patients should both show up in the only existing POSSIBLE_DUPLICATE EmpiLink.
|
//The two Persons related to the patients should both show up in the only existing POSSIBLE_DUPLICATE MdmLink.
|
||||||
MdmLink mdmLink = possibleDuplicates.get(0);
|
MdmLink mdmLink = possibleDuplicates.get(0);
|
||||||
assertThat(mdmLink.getGoldenResourcePid(), is(in(duplicatePids)));
|
assertThat(mdmLink.getGoldenResourcePid(), is(in(duplicatePids)));
|
||||||
assertThat(mdmLink.getTargetPid(), is(in(duplicatePids)));
|
assertThat(mdmLink.getTargetPid(), is(in(duplicatePids)));
|
||||||
|
|
|
@ -53,15 +53,11 @@ public class MdmMatchLinkSvcTest extends BaseMdmR4Test {
|
||||||
private static final Logger ourLog = getLogger(MdmMatchLinkSvcTest.class);
|
private static final Logger ourLog = getLogger(MdmMatchLinkSvcTest.class);
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
IMdmLinkSvc myEmpiLinkSvc;
|
IMdmLinkSvc myMdmLinkSvc;
|
||||||
@Autowired
|
@Autowired
|
||||||
private EIDHelper myEidHelper;
|
private EIDHelper myEidHelper;
|
||||||
@Autowired
|
@Autowired
|
||||||
private GoldenResourceHelper myGoldenResourceHelper;
|
private GoldenResourceHelper myGoldenResourceHelper;
|
||||||
@Autowired
|
|
||||||
private IMdmLinkDao myEmpiLinkDao;
|
|
||||||
@Autowired
|
|
||||||
private DaoRegistry myDaoRegistry;
|
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void before() {
|
public void before() {
|
||||||
|
@ -123,9 +119,9 @@ public class MdmMatchLinkSvcTest extends BaseMdmR4Test {
|
||||||
|
|
||||||
//Create a manual NO_MATCH between janePerson and unmatchedJane.
|
//Create a manual NO_MATCH between janePerson and unmatchedJane.
|
||||||
Patient unmatchedJane = createPatient(buildJanePatient());
|
Patient unmatchedJane = createPatient(buildJanePatient());
|
||||||
myEmpiLinkSvc.updateLink(janePerson, unmatchedJane, MdmMatchOutcome.NO_MATCH, MdmLinkSourceEnum.MANUAL, createContextForCreate("Patient"));
|
myMdmLinkSvc.updateLink(janePerson, unmatchedJane, MdmMatchOutcome.NO_MATCH, MdmLinkSourceEnum.MANUAL, createContextForCreate("Patient"));
|
||||||
|
|
||||||
//rerun EMPI rules against unmatchedJane.
|
//rerun MDM rules against unmatchedJane.
|
||||||
myMdmMatchLinkSvc.updateMdmLinksForMdmTarget(unmatchedJane, createContextForCreate("Patient"));
|
myMdmMatchLinkSvc.updateMdmLinksForMdmTarget(unmatchedJane, createContextForCreate("Patient"));
|
||||||
|
|
||||||
assertThat(unmatchedJane, is(not(sameSourceResourceAs(janePerson))));
|
assertThat(unmatchedJane, is(not(sameSourceResourceAs(janePerson))));
|
||||||
|
@ -146,7 +142,7 @@ public class MdmMatchLinkSvcTest extends BaseMdmR4Test {
|
||||||
Patient unmatchedPatient = createPatient(buildJanePatient());
|
Patient unmatchedPatient = createPatient(buildJanePatient());
|
||||||
|
|
||||||
// This simulates an admin specifically saying that unmatchedPatient does NOT match janePerson.
|
// This simulates an admin specifically saying that unmatchedPatient does NOT match janePerson.
|
||||||
myEmpiLinkSvc.updateLink(janePerson, unmatchedPatient, MdmMatchOutcome.NO_MATCH, MdmLinkSourceEnum.MANUAL, createContextForCreate("Patient"));
|
myMdmLinkSvc.updateLink(janePerson, unmatchedPatient, MdmMatchOutcome.NO_MATCH, MdmLinkSourceEnum.MANUAL, createContextForCreate("Patient"));
|
||||||
// TODO change this so that it will only partially match.
|
// 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
|
//Now normally, when we run update links, it should link to janePerson. However, this manual NO_MATCH link
|
||||||
|
@ -167,10 +163,10 @@ public class MdmMatchLinkSvcTest extends BaseMdmR4Test {
|
||||||
Patient janePatient = addExternalEID(buildJanePatient(), sampleEID);
|
Patient janePatient = addExternalEID(buildJanePatient(), sampleEID);
|
||||||
janePatient = createPatientAndUpdateLinks(janePatient);
|
janePatient = createPatientAndUpdateLinks(janePatient);
|
||||||
|
|
||||||
Optional<MdmLink> empiLink = myMdmLinkDaoSvc.getMatchedLinkForTargetPid(janePatient.getIdElement().getIdPartAsLong());
|
Optional<MdmLink> mdmLink = myMdmLinkDaoSvc.getMatchedLinkForTargetPid(janePatient.getIdElement().getIdPartAsLong());
|
||||||
assertThat(empiLink.isPresent(), is(true));
|
assertThat(mdmLink.isPresent(), is(true));
|
||||||
|
|
||||||
Patient patient = getTargetResourceFromEmpiLink(empiLink.get(), "Patient");
|
Patient patient = getTargetResourceFromMdmLink(mdmLink.get(), "Patient");
|
||||||
List<CanonicalEID> externalEid = myEidHelper.getExternalEid(patient);
|
List<CanonicalEID> externalEid = myEidHelper.getExternalEid(patient);
|
||||||
|
|
||||||
assertThat(externalEid.get(0).getSystem(), is(equalTo(myMdmSettings.getMdmRules().getEnterpriseEIDSystem())));
|
assertThat(externalEid.get(0).getSystem(), is(equalTo(myMdmSettings.getMdmRules().getEnterpriseEIDSystem())));
|
||||||
|
@ -182,7 +178,7 @@ public class MdmMatchLinkSvcTest extends BaseMdmR4Test {
|
||||||
Patient patient = createPatientAndUpdateLinks(buildJanePatient());
|
Patient patient = createPatientAndUpdateLinks(buildJanePatient());
|
||||||
MdmLink mdmLink = myMdmLinkDaoSvc.getMatchedLinkForTargetPid(patient.getIdElement().getIdPartAsLong()).get();
|
MdmLink mdmLink = myMdmLinkDaoSvc.getMatchedLinkForTargetPid(patient.getIdElement().getIdPartAsLong()).get();
|
||||||
|
|
||||||
Patient targetPatient = getTargetResourceFromEmpiLink(mdmLink, "Patient");
|
Patient targetPatient = getTargetResourceFromMdmLink(mdmLink, "Patient");
|
||||||
Identifier identifierFirstRep = targetPatient.getIdentifierFirstRep();
|
Identifier identifierFirstRep = targetPatient.getIdentifierFirstRep();
|
||||||
assertThat(identifierFirstRep.getSystem(), is(equalTo(MdmConstants.HAPI_ENTERPRISE_IDENTIFIER_SYSTEM)));
|
assertThat(identifierFirstRep.getSystem(), is(equalTo(MdmConstants.HAPI_ENTERPRISE_IDENTIFIER_SYSTEM)));
|
||||||
assertThat(identifierFirstRep.getValue(), not(blankOrNullString()));
|
assertThat(identifierFirstRep.getValue(), not(blankOrNullString()));
|
||||||
|
@ -192,8 +188,8 @@ public class MdmMatchLinkSvcTest extends BaseMdmR4Test {
|
||||||
public void testPatientAttributesAreCopiedOverWhenPersonIsCreatedFromPatient() {
|
public void testPatientAttributesAreCopiedOverWhenPersonIsCreatedFromPatient() {
|
||||||
Patient patient = createPatientAndUpdateLinks(buildPatientWithNameIdAndBirthday("Gary", "GARY_ID", new Date()));
|
Patient patient = createPatientAndUpdateLinks(buildPatientWithNameIdAndBirthday("Gary", "GARY_ID", new Date()));
|
||||||
|
|
||||||
Optional<MdmLink> empiLink = myMdmLinkDaoSvc.getMatchedLinkForTargetPid(patient.getIdElement().getIdPartAsLong());
|
Optional<MdmLink> mdmLink = myMdmLinkDaoSvc.getMatchedLinkForTargetPid(patient.getIdElement().getIdPartAsLong());
|
||||||
Patient read = getTargetResourceFromEmpiLink(empiLink.get(), "Patient");
|
Patient read = getTargetResourceFromMdmLink(mdmLink.get(), "Patient");
|
||||||
|
|
||||||
// TODO NG - rules haven't been determined yet revisit once implemented...
|
// TODO NG - rules haven't been determined yet revisit once implemented...
|
||||||
// assertThat(read.getNameFirstRep().getFamily(), is(equalTo(patient.getNameFirstRep().getFamily())));
|
// assertThat(read.getNameFirstRep().getFamily(), is(equalTo(patient.getNameFirstRep().getFamily())));
|
||||||
|
@ -290,17 +286,17 @@ public class MdmMatchLinkSvcTest extends BaseMdmR4Test {
|
||||||
.map(myIdHelperService::getPidOrNull)
|
.map(myIdHelperService::getPidOrNull)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
//The two Persons related to the patients should both show up in the only existing POSSIBLE_DUPLICATE EmpiLink.
|
//The two Persons related to the patients should both show up in the only existing POSSIBLE_DUPLICATE MdmLink.
|
||||||
MdmLink mdmLink = possibleDuplicates.get(0);
|
MdmLink mdmLink = possibleDuplicates.get(0);
|
||||||
assertThat(mdmLink.getGoldenResourcePid(), is(in(duplicatePids)));
|
assertThat(mdmLink.getGoldenResourcePid(), is(in(duplicatePids)));
|
||||||
assertThat(mdmLink.getTargetPid(), is(in(duplicatePids)));
|
assertThat(mdmLink.getTargetPid(), is(in(duplicatePids)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPatientWithNoEmpiTagIsNotMatched() {
|
public void testPatientWithNoMdmTagIsNotMatched() {
|
||||||
// Patient with "no-empi" tag is not matched
|
// Patient with "no-mdm" tag is not matched
|
||||||
Patient janePatient = buildJanePatient();
|
Patient janePatient = buildJanePatient();
|
||||||
janePatient.getMeta().addTag(MdmConstants.SYSTEM_MDM_MANAGED, MdmConstants.CODE_NO_MDM_MANAGED, "Don't EMPI on me!");
|
janePatient.getMeta().addTag(MdmConstants.SYSTEM_MDM_MANAGED, MdmConstants.CODE_NO_MDM_MANAGED, "Don't MDM on me!");
|
||||||
createPatientAndUpdateLinks(janePatient);
|
createPatientAndUpdateLinks(janePatient);
|
||||||
assertLinkCount(0);
|
assertLinkCount(0);
|
||||||
}
|
}
|
||||||
|
@ -364,7 +360,7 @@ public class MdmMatchLinkSvcTest extends BaseMdmR4Test {
|
||||||
//In a normal situation, janePatient2 would just match to jane patient, but here we need to hack it so they are their
|
//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.
|
//own individual Persons for the purpose of this test.
|
||||||
IAnyResource person = myGoldenResourceHelper.createGoldenResourceFromMdmTarget(janePatient2);
|
IAnyResource person = myGoldenResourceHelper.createGoldenResourceFromMdmTarget(janePatient2);
|
||||||
myEmpiLinkSvc.updateLink(person, janePatient2, MdmMatchOutcome.NEW_PERSON_MATCH, MdmLinkSourceEnum.AUTO, createContextForCreate("Patient"));
|
myMdmLinkSvc.updateLink(person, janePatient2, MdmMatchOutcome.NEW_PERSON_MATCH, MdmLinkSourceEnum.AUTO, createContextForCreate("Patient"));
|
||||||
assertThat(janePatient, is(not(sameSourceResourceAs(janePatient2))));
|
assertThat(janePatient, is(not(sameSourceResourceAs(janePatient2))));
|
||||||
|
|
||||||
//In theory, this will match both Persons!
|
//In theory, this will match both Persons!
|
||||||
|
@ -389,7 +385,7 @@ public class MdmMatchLinkSvcTest extends BaseMdmR4Test {
|
||||||
@Test
|
@Test
|
||||||
public void testWhenAllMatchResultsArePOSSIBLE_MATCHThattheyAreLinkedAndNoSourceResourceIsCreated() {
|
public void testWhenAllMatchResultsArePOSSIBLE_MATCHThattheyAreLinkedAndNoSourceResourceIsCreated() {
|
||||||
/**
|
/**
|
||||||
* CASE 4: Only POSSIBLE_MATCH outcomes -> In this case, empi-link records are created with POSSIBLE_MATCH
|
* CASE 4: Only POSSIBLE_MATCH outcomes -> In this case, mdm-link records are created with POSSIBLE_MATCH
|
||||||
* outcome and await manual assignment to either NO_MATCH or MATCHED. Person link is added.
|
* outcome and await manual assignment to either NO_MATCH or MATCHED. Person link is added.
|
||||||
*/
|
*/
|
||||||
Patient patient = buildJanePatient();
|
Patient patient = buildJanePatient();
|
||||||
|
@ -451,7 +447,7 @@ public class MdmMatchLinkSvcTest extends BaseMdmR4Test {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreateSourceResourceFromEmpiTarget() {
|
public void testCreateSourceResourceFromMdmTarget() {
|
||||||
// Create Use Case #2 - adding patient with no EID
|
// Create Use Case #2 - adding patient with no EID
|
||||||
Patient janePatient = buildJanePatient();
|
Patient janePatient = buildJanePatient();
|
||||||
Patient janeSourceResourcePatient = myGoldenResourceHelper.createGoldenResourceFromMdmTarget(janePatient);
|
Patient janeSourceResourcePatient = myGoldenResourceHelper.createGoldenResourceFromMdmTarget(janePatient);
|
||||||
|
|
|
@ -76,7 +76,7 @@ public class MdmPersonMergerSvcTest extends BaseMdmR4Test {
|
||||||
myTargetPatient2 = createPatient();
|
myTargetPatient2 = createPatient();
|
||||||
myTargetPatient3 = createPatient();
|
myTargetPatient3 = createPatient();
|
||||||
|
|
||||||
// Register the empi storage interceptor after the creates so the delete hook is fired when we merge
|
// Register the mdm storage interceptor after the creates so the delete hook is fired when we merge
|
||||||
myInterceptorService.registerInterceptor(myMdmStorageInterceptor);
|
myInterceptorService.registerInterceptor(myMdmStorageInterceptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,7 +102,7 @@ public class MdmPersonMergerSvcTest extends BaseMdmR4Test {
|
||||||
|
|
||||||
private Patient mergeGoldenPatients() {
|
private Patient mergeGoldenPatients() {
|
||||||
assertEquals(0, redirectLinkCount());
|
assertEquals(0, redirectLinkCount());
|
||||||
Patient retval = (Patient) myGoldenResourceMergerSvc.mergeGoldenResources(myFromGoldenPatient, myToGoldenPatient, createEmpiContext());
|
Patient retval = (Patient) myGoldenResourceMergerSvc.mergeGoldenResources(myFromGoldenPatient, myToGoldenPatient, createMdmContext());
|
||||||
assertEquals(1, redirectLinkCount());
|
assertEquals(1, redirectLinkCount());
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
@ -113,7 +113,7 @@ public class MdmPersonMergerSvcTest extends BaseMdmR4Test {
|
||||||
return myMdmLinkDao.findAll(example).size();
|
return myMdmLinkDao.findAll(example).size();
|
||||||
}
|
}
|
||||||
|
|
||||||
private MdmTransactionContext createEmpiContext() {
|
private MdmTransactionContext createMdmContext() {
|
||||||
MdmTransactionContext mdmTransactionContext = new MdmTransactionContext(TransactionLogMessages.createFromTransactionGuid(UUID.randomUUID().toString()), MdmTransactionContext.OperationType.MERGE_PERSONS);
|
MdmTransactionContext mdmTransactionContext = new MdmTransactionContext(TransactionLogMessages.createFromTransactionGuid(UUID.randomUUID().toString()), MdmTransactionContext.OperationType.MERGE_PERSONS);
|
||||||
mdmTransactionContext.setResourceType("Patient");
|
mdmTransactionContext.setResourceType("Patient");
|
||||||
return mdmTransactionContext;
|
return mdmTransactionContext;
|
||||||
|
|
|
@ -17,8 +17,9 @@ import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
@ExtendWith(SpringExtension.class)
|
@ExtendWith(SpringExtension.class)
|
||||||
class MdmResourceFilteringSvcMockTest {
|
class MdmResourceFilteringSvcMockTest {
|
||||||
|
|
||||||
@MockBean
|
@MockBean
|
||||||
private IMdmSettings myEmpiSettings;
|
private IMdmSettings myMdmSettings;
|
||||||
@MockBean
|
@MockBean
|
||||||
MdmSearchParamSvc myMdmSearchParamSvc;
|
MdmSearchParamSvc myMdmSearchParamSvc;
|
||||||
@MockBean
|
@MockBean
|
||||||
|
@ -29,14 +30,14 @@ class MdmResourceFilteringSvcMockTest {
|
||||||
@Configuration
|
@Configuration
|
||||||
static class SpringConfig {
|
static class SpringConfig {
|
||||||
@Bean
|
@Bean
|
||||||
MdmResourceFilteringSvc empiResourceFilteringSvc() {
|
MdmResourceFilteringSvc mdmResourceFilteringSvc() {
|
||||||
return new MdmResourceFilteringSvc();
|
return new MdmResourceFilteringSvc();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEmptyCriteriaShouldBeProcessed() {
|
public void testEmptyCriteriaShouldBeProcessed() {
|
||||||
when(myEmpiSettings.getMdmRules()).thenReturn(new MdmRulesJson());
|
when(myMdmSettings.getMdmRules()).thenReturn(new MdmRulesJson());
|
||||||
assertTrue(myMdmResourceFilteringSvc.shouldBeProcessed(new Patient()));
|
assertTrue(myMdmResourceFilteringSvc.shouldBeProcessed(new Patient()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ class MdmResourceFilteringSvcTest extends BaseMdmR4Test {
|
||||||
@Test
|
@Test
|
||||||
public void testFilterResourcesWhichHaveNoRelevantAttributes() {
|
public void testFilterResourcesWhichHaveNoRelevantAttributes() {
|
||||||
Patient patient = new Patient();
|
Patient patient = new Patient();
|
||||||
patient.setDeceased(new BooleanType(true)); //EMPI rules defined do not care about the deceased attribute.
|
patient.setDeceased(new BooleanType(true)); // MDM rules defined do not care about the deceased attribute.
|
||||||
|
|
||||||
//SUT
|
//SUT
|
||||||
boolean shouldBeProcessed = myMdmResourceFilteringSvc.shouldBeProcessed(patient);
|
boolean shouldBeProcessed = myMdmResourceFilteringSvc.shouldBeProcessed(patient);
|
||||||
|
@ -27,9 +27,9 @@ class MdmResourceFilteringSvcTest extends BaseMdmR4Test {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDoNotFilterResourcesWithEMPIAttributes() {
|
public void testDoNotFilterResourcesWithMdmAttributes() {
|
||||||
Patient patient = new Patient();
|
Patient patient = new Patient();
|
||||||
patient.addIdentifier().setValue("Hey I'm an ID! rules defined in empi-rules.json care about me!");
|
patient.addIdentifier().setValue("Hey I'm an ID! rules defined in mdm-rules.json care about me!");
|
||||||
|
|
||||||
//SUT
|
//SUT
|
||||||
boolean shouldBeProcessed = myMdmResourceFilteringSvc.shouldBeProcessed(patient);
|
boolean shouldBeProcessed = myMdmResourceFilteringSvc.shouldBeProcessed(patient);
|
||||||
|
|
|
@ -48,15 +48,14 @@
|
||||||
<appender-ref ref="STDOUT" />
|
<appender-ref ref="STDOUT" />
|
||||||
</logger>
|
</logger>
|
||||||
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Configuration for EMPI troubleshooting log
|
Configuration for MDM troubleshooting log
|
||||||
-->
|
-->
|
||||||
<appender name="EMPI_TROUBLESHOOTING" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
<appender name="MDM_TROUBLESHOOTING" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter"><level>DEBUG</level></filter>
|
<filter class="ch.qos.logback.classic.filter.ThresholdFilter"><level>DEBUG</level></filter>
|
||||||
<file>${smile.basedir}/log/empi-troubleshooting.log</file>
|
<file>${smile.basedir}/log/mdm-troubleshooting.log</file>
|
||||||
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
|
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
|
||||||
<fileNamePattern>${smile.basedir}/log/empi-troubleshooting.log.%i.gz</fileNamePattern>
|
<fileNamePattern>${smile.basedir}/log/mdm-troubleshooting.log.%i.gz</fileNamePattern>
|
||||||
<minIndex>1</minIndex>
|
<minIndex>1</minIndex>
|
||||||
<maxIndex>9</maxIndex>
|
<maxIndex>9</maxIndex>
|
||||||
</rollingPolicy>
|
</rollingPolicy>
|
||||||
|
@ -67,12 +66,11 @@
|
||||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||||
</encoder>
|
</encoder>
|
||||||
</appender>
|
</appender>
|
||||||
<logger name="ca.uhn.fhir.log.empi_troubleshooting" level="TRACE">
|
|
||||||
<appender-ref ref="EMPI_TROUBLESHOOTING"/>
|
<logger name="ca.uhn.fhir.log.mdm_troubleshooting" level="TRACE">
|
||||||
|
<appender-ref ref="MDM_TROUBLESHOOTING"/>
|
||||||
</logger>
|
</logger>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<root level="info">
|
<root level="info">
|
||||||
<appender-ref ref="STDOUT" />
|
<appender-ref ref="STDOUT" />
|
||||||
</root>
|
</root>
|
||||||
|
|
|
@ -26,9 +26,9 @@ import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This service supports the EMPI Operation providers for those services that return multiple empi links.
|
* This service supports the MDM operation providers for those services that return multiple MDM links.
|
||||||
*/
|
*/
|
||||||
public interface IMdmLinkQuerySvc {
|
public interface IMdmLinkQuerySvc {
|
||||||
Stream<MdmLinkJson> queryLinks(IIdType thePersonId, IIdType theTargetId, MdmMatchResultEnum theMatchResult, MdmLinkSourceEnum theLinkSource, MdmTransactionContext theEmpiContext);
|
Stream<MdmLinkJson> queryLinks(IIdType theGoldenResourceId, IIdType theTargetId, MdmMatchResultEnum theMatchResult, MdmLinkSourceEnum theLinkSource, MdmTransactionContext theMdmContext);
|
||||||
Stream<MdmLinkJson> getDuplicatePersons(MdmTransactionContext theEmpiContext);
|
Stream<MdmLinkJson> getDuplicatePersons(MdmTransactionContext theMdmContext);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,6 @@ import ca.uhn.fhir.mdm.model.MdmTransactionContext;
|
||||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||||
|
|
||||||
public interface IMdmLinkUpdaterSvc {
|
public interface IMdmLinkUpdaterSvc {
|
||||||
IAnyResource updateLink(IAnyResource thePerson, IAnyResource theTarget, MdmMatchResultEnum theMatchResult, MdmTransactionContext theEmpiContext);
|
IAnyResource updateLink(IAnyResource theGoldenResource, IAnyResource theTarget, MdmMatchResultEnum theMatchResult, MdmTransactionContext theMdmContext);
|
||||||
void notDuplicatePerson(IAnyResource thePerson, IAnyResource theTarget, MdmTransactionContext theEmpiContext);
|
void notDuplicatePerson(IAnyResource theGoldenResource, IAnyResource theTarget, MdmTransactionContext theMdmContext);
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,9 @@ import ca.uhn.fhir.mdm.rules.json.MdmRulesJson;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public interface IMdmSettings {
|
public interface IMdmSettings {
|
||||||
String MDM_CHANNEL_NAME = "empi";
|
|
||||||
|
String MDM_CHANNEL_NAME = "mdm";
|
||||||
|
|
||||||
// Parallel processing of MDM can result in missed matches. Best to single-thread.
|
// Parallel processing of MDM can result in missed matches. Best to single-thread.
|
||||||
int MDM_DEFAULT_CONCURRENT_CONSUMERS = 1;
|
int MDM_DEFAULT_CONCURRENT_CONSUMERS = 1;
|
||||||
|
|
||||||
|
|
|
@ -108,13 +108,13 @@ public final class MdmMatchOutcome {
|
||||||
/**
|
/**
|
||||||
* Sets the number of MDM rules checked for this match outcome
|
* Sets the number of MDM rules checked for this match outcome
|
||||||
*
|
*
|
||||||
* @param theEmpiRuleCount
|
* @param theMdmRuleCount
|
||||||
* Number of MDM rules that were checked for this match outcome
|
* Number of MDM rules that were checked for this match outcome
|
||||||
* @return
|
* @return
|
||||||
* Returns this instance
|
* Returns this instance
|
||||||
*/
|
*/
|
||||||
public MdmMatchOutcome setMdmRuleCount(int theEmpiRuleCount) {
|
public MdmMatchOutcome setMdmRuleCount(int theMdmRuleCount) {
|
||||||
myMdmRuleCount = theEmpiRuleCount;
|
myMdmRuleCount = theMdmRuleCount;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,7 @@ public class CanonicalEID {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the appropriate FHIRPath expression to extract the EID identifier value, regardless of resource type.
|
* Get the appropriate FHIRPath expression to extract the EID identifier value, regardless of resource type.
|
||||||
* e.g. if theBaseResource is a patient, and the EMPI EID system is test-system, this will return
|
* e.g. if theBaseResource is a patient, and the MDM EID system is test-system, this will return
|
||||||
*
|
*
|
||||||
* Patient.identifier.where(system='test-system').value
|
* Patient.identifier.where(system='test-system').value
|
||||||
*
|
*
|
||||||
|
|
|
@ -36,7 +36,7 @@ public class MdmTransactionContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Any EMPI methods may add transaction log messages.
|
* Any MDM methods may add transaction log messages.
|
||||||
*/
|
*/
|
||||||
private TransactionLogMessages myTransactionLogMessages;
|
private TransactionLogMessages myTransactionLogMessages;
|
||||||
|
|
||||||
|
|
|
@ -39,14 +39,14 @@ import org.springframework.stereotype.Service;
|
||||||
public class MdmControllerHelper {
|
public class MdmControllerHelper {
|
||||||
private final FhirContext myFhirContext;
|
private final FhirContext myFhirContext;
|
||||||
private final IResourceLoader myResourceLoader;
|
private final IResourceLoader myResourceLoader;
|
||||||
private final IMdmSettings myEmpiSettings;
|
private final IMdmSettings myMdmSettings;
|
||||||
private final MessageHelper myMessageHelper;
|
private final MessageHelper myMessageHelper;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public MdmControllerHelper(FhirContext theFhirContext, IResourceLoader theResourceLoader, IMdmSettings theEmpiSettings, MessageHelper theMessageHelper) {
|
public MdmControllerHelper(FhirContext theFhirContext, IResourceLoader theResourceLoader, IMdmSettings theMdmSettings, MessageHelper theMessageHelper) {
|
||||||
myFhirContext = theFhirContext;
|
myFhirContext = theFhirContext;
|
||||||
myResourceLoader = theResourceLoader;
|
myResourceLoader = theResourceLoader;
|
||||||
myEmpiSettings = theEmpiSettings;
|
myMdmSettings = theMdmSettings;
|
||||||
myMessageHelper = theMessageHelper;
|
myMessageHelper = theMessageHelper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,17 +78,17 @@ public class MdmControllerHelper {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void validateMergeResources(IAnyResource theFromPerson, IAnyResource theToPerson) {
|
public void validateMergeResources(IAnyResource theFromPerson, IAnyResource theToPerson) {
|
||||||
validateIsEmpiManaged(ProviderConstants.MDM_MERGE_GR_FROM_GOLDEN_RESOURCE_ID, theFromPerson);
|
validateIsMdmManaged(ProviderConstants.MDM_MERGE_GR_FROM_GOLDEN_RESOURCE_ID, theFromPerson);
|
||||||
validateIsEmpiManaged(ProviderConstants.MDM_MERGE_GR_TO_GOLDEN_RESOURCE_ID, theToPerson);
|
validateIsMdmManaged(ProviderConstants.MDM_MERGE_GR_TO_GOLDEN_RESOURCE_ID, theToPerson);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toJson(IAnyResource theAnyResource) {
|
public String toJson(IAnyResource theAnyResource) {
|
||||||
return myFhirContext.newJsonParser().encodeResourceToString(theAnyResource);
|
return myFhirContext.newJsonParser().encodeResourceToString(theAnyResource);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void validateIsEmpiManaged(String theName, IAnyResource theResource) {
|
public void validateIsMdmManaged(String theName, IAnyResource theResource) {
|
||||||
String resourceType = myFhirContext.getResourceType(theResource);
|
String resourceType = myFhirContext.getResourceType(theResource);
|
||||||
if (!myEmpiSettings.isSupportedMdmType(resourceType)) {
|
if (!myMdmSettings.isSupportedMdmType(resourceType)) {
|
||||||
throw new InvalidRequestException(
|
throw new InvalidRequestException(
|
||||||
myMessageHelper.getMessageForUnsupportedResource(theName, resourceType)
|
myMessageHelper.getMessageForUnsupportedResource(theName, resourceType)
|
||||||
);
|
);
|
||||||
|
|
|
@ -141,10 +141,10 @@ public class MdmProviderDstu3 extends BaseMdmProvider {
|
||||||
@OperationParam(name=ProviderConstants.MDM_QUERY_LINKS_MATCH_RESULT, min = 0, max = 1) StringType theLinkSource,
|
@OperationParam(name=ProviderConstants.MDM_QUERY_LINKS_MATCH_RESULT, min = 0, max = 1) StringType theLinkSource,
|
||||||
ServletRequestDetails theRequestDetails) {
|
ServletRequestDetails theRequestDetails) {
|
||||||
|
|
||||||
Stream<MdmLinkJson> empiLinkJson = myMdmControllerSvc.queryLinks(extractStringOrNull(theGoldenResourceId), extractStringOrNull(theTargetResourceId),
|
Stream<MdmLinkJson> mdmLinkJson = myMdmControllerSvc.queryLinks(extractStringOrNull(theGoldenResourceId), extractStringOrNull(theTargetResourceId),
|
||||||
extractStringOrNull(theMatchResult), extractStringOrNull(theLinkSource), createMdmContext(theRequestDetails,
|
extractStringOrNull(theMatchResult), extractStringOrNull(theLinkSource), createMdmContext(theRequestDetails,
|
||||||
MdmTransactionContext.OperationType.QUERY_LINKS, getResourceType(ProviderConstants.MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID, theGoldenResourceId)));
|
MdmTransactionContext.OperationType.QUERY_LINKS, getResourceType(ProviderConstants.MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID, theGoldenResourceId)));
|
||||||
return (Parameters) parametersFromMdmLinks(empiLinkJson, true);
|
return (Parameters) parametersFromMdmLinks(mdmLinkJson, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(name = ProviderConstants.MDM_DUPLICATE_GOLDEN_RESOURCES)
|
@Operation(name = ProviderConstants.MDM_DUPLICATE_GOLDEN_RESOURCES)
|
||||||
|
@ -171,12 +171,12 @@ public class MdmProviderDstu3 extends BaseMdmProvider {
|
||||||
@Operation(name = ProviderConstants.OPERATION_MDM_SUBMIT, idempotent = false, returnParameters = {
|
@Operation(name = ProviderConstants.OPERATION_MDM_SUBMIT, idempotent = false, returnParameters = {
|
||||||
@OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type= DecimalType.class)
|
@OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type= DecimalType.class)
|
||||||
})
|
})
|
||||||
public Parameters empiBatchOnAllTargets(
|
public Parameters mdmBatchOnAllTargets(
|
||||||
@OperationParam(name= ProviderConstants.MDM_BATCH_RUN_CRITERIA,min = 0 , max = 1) StringType theCriteria,
|
@OperationParam(name= ProviderConstants.MDM_BATCH_RUN_CRITERIA,min = 0 , max = 1) StringType theCriteria,
|
||||||
ServletRequestDetails theRequestDetails) {
|
ServletRequestDetails theRequestDetails) {
|
||||||
String criteria = convertCriteriaToString(theCriteria);
|
String criteria = convertCriteriaToString(theCriteria);
|
||||||
long submittedCount = myMdmSubmitSvc.submitAllTargetTypesToMdm(criteria);
|
long submittedCount = myMdmSubmitSvc.submitAllTargetTypesToMdm(criteria);
|
||||||
return buildEmpiOutParametersWithCount(submittedCount);
|
return buildMdmOutParametersWithCount(submittedCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String convertCriteriaToString(StringType theCriteria) {
|
private String convertCriteriaToString(StringType theCriteria) {
|
||||||
|
@ -186,8 +186,8 @@ public class MdmProviderDstu3 extends BaseMdmProvider {
|
||||||
@Operation(name = ProviderConstants.MDM_CLEAR, returnParameters = {
|
@Operation(name = ProviderConstants.MDM_CLEAR, returnParameters = {
|
||||||
@OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type= DecimalType.class)
|
@OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type= DecimalType.class)
|
||||||
})
|
})
|
||||||
public Parameters clearEmpiLinks(@OperationParam(name=ProviderConstants.MDM_CLEAR_TARGET_TYPE, min = 0, max = 1) StringType theTargetType,
|
public Parameters clearMdmLinks(@OperationParam(name=ProviderConstants.MDM_CLEAR_TARGET_TYPE, min = 0, max = 1) StringType theTargetType,
|
||||||
ServletRequestDetails theRequestDetails) {
|
ServletRequestDetails theRequestDetails) {
|
||||||
long resetCount;
|
long resetCount;
|
||||||
if (theTargetType == null || StringUtils.isBlank(theTargetType.getValue())) {
|
if (theTargetType == null || StringUtils.isBlank(theTargetType.getValue())) {
|
||||||
resetCount = myMdmExpungeSvc.expungeAllMdmLinks(theRequestDetails);
|
resetCount = myMdmExpungeSvc.expungeAllMdmLinks(theRequestDetails);
|
||||||
|
@ -203,49 +203,49 @@ public class MdmProviderDstu3 extends BaseMdmProvider {
|
||||||
@Operation(name = ProviderConstants.OPERATION_MDM_SUBMIT, idempotent = false, type = Patient.class, returnParameters = {
|
@Operation(name = ProviderConstants.OPERATION_MDM_SUBMIT, idempotent = false, type = Patient.class, returnParameters = {
|
||||||
@OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type = DecimalType.class)
|
@OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type = DecimalType.class)
|
||||||
})
|
})
|
||||||
public Parameters empiBatchPatientInstance(
|
public Parameters mdmBatchPatientInstance(
|
||||||
@IdParam IIdType theIdParam,
|
@IdParam IIdType theIdParam,
|
||||||
RequestDetails theRequest) {
|
RequestDetails theRequest) {
|
||||||
long submittedCount = myMdmSubmitSvc.submitTargetToMdm(theIdParam);
|
long submittedCount = myMdmSubmitSvc.submitTargetToMdm(theIdParam);
|
||||||
return buildEmpiOutParametersWithCount(submittedCount);
|
return buildMdmOutParametersWithCount(submittedCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(name = ProviderConstants.OPERATION_MDM_SUBMIT, idempotent = false, type = Patient.class, returnParameters = {
|
@Operation(name = ProviderConstants.OPERATION_MDM_SUBMIT, idempotent = false, type = Patient.class, returnParameters = {
|
||||||
@OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type = DecimalType.class)
|
@OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type = DecimalType.class)
|
||||||
})
|
})
|
||||||
public Parameters empiBatchPatientType(
|
public Parameters mdmBatchPatientType(
|
||||||
@OperationParam(name = ProviderConstants.MDM_BATCH_RUN_CRITERIA) StringType theCriteria,
|
@OperationParam(name = ProviderConstants.MDM_BATCH_RUN_CRITERIA) StringType theCriteria,
|
||||||
RequestDetails theRequest) {
|
RequestDetails theRequest) {
|
||||||
String criteria = convertCriteriaToString(theCriteria);
|
String criteria = convertCriteriaToString(theCriteria);
|
||||||
long submittedCount = myMdmSubmitSvc.submitPatientTypeToMdm(criteria);
|
long submittedCount = myMdmSubmitSvc.submitPatientTypeToMdm(criteria);
|
||||||
return buildEmpiOutParametersWithCount(submittedCount);
|
return buildMdmOutParametersWithCount(submittedCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(name = ProviderConstants.OPERATION_MDM_SUBMIT, idempotent = false, type = Practitioner.class, returnParameters = {
|
@Operation(name = ProviderConstants.OPERATION_MDM_SUBMIT, idempotent = false, type = Practitioner.class, returnParameters = {
|
||||||
@OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type = DecimalType.class)
|
@OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type = DecimalType.class)
|
||||||
})
|
})
|
||||||
public Parameters empiBatchPractitionerInstance(
|
public Parameters mdmBatchPractitionerInstance(
|
||||||
@IdParam IIdType theIdParam,
|
@IdParam IIdType theIdParam,
|
||||||
RequestDetails theRequest) {
|
RequestDetails theRequest) {
|
||||||
long submittedCount = myMdmSubmitSvc.submitTargetToMdm(theIdParam);
|
long submittedCount = myMdmSubmitSvc.submitTargetToMdm(theIdParam);
|
||||||
return buildEmpiOutParametersWithCount(submittedCount);
|
return buildMdmOutParametersWithCount(submittedCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(name = ProviderConstants.OPERATION_MDM_SUBMIT, idempotent = false, type = Practitioner.class, returnParameters = {
|
@Operation(name = ProviderConstants.OPERATION_MDM_SUBMIT, idempotent = false, type = Practitioner.class, returnParameters = {
|
||||||
@OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type = DecimalType.class)
|
@OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type = DecimalType.class)
|
||||||
})
|
})
|
||||||
public Parameters empiBatchPractitionerType(
|
public Parameters mdmBatchPractitionerType(
|
||||||
@OperationParam(name = ProviderConstants.MDM_BATCH_RUN_CRITERIA) StringType theCriteria,
|
@OperationParam(name = ProviderConstants.MDM_BATCH_RUN_CRITERIA) StringType theCriteria,
|
||||||
RequestDetails theRequest) {
|
RequestDetails theRequest) {
|
||||||
String criteria = convertCriteriaToString(theCriteria);
|
String criteria = convertCriteriaToString(theCriteria);
|
||||||
long submittedCount = myMdmSubmitSvc.submitPractitionerTypeToMdm(criteria);
|
long submittedCount = myMdmSubmitSvc.submitPractitionerTypeToMdm(criteria);
|
||||||
return buildEmpiOutParametersWithCount(submittedCount);
|
return buildMdmOutParametersWithCount(submittedCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function to build the out-parameters for all batch EMPI operations.
|
* Helper function to build the out-parameters for all batch MDM operations.
|
||||||
*/
|
*/
|
||||||
private Parameters buildEmpiOutParametersWithCount(long theCount) {
|
private Parameters buildMdmOutParametersWithCount(long theCount) {
|
||||||
Parameters parameters = new Parameters();
|
Parameters parameters = new Parameters();
|
||||||
parameters.addParameter()
|
parameters.addParameter()
|
||||||
.setName(ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT)
|
.setName(ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT)
|
||||||
|
|
|
@ -194,12 +194,12 @@ public class MdmProviderR4 extends BaseMdmProvider {
|
||||||
@OperationParam(name=ProviderConstants.MDM_QUERY_LINKS_LINK_SOURCE, min = 0, max = 1) StringType theLinkSource,
|
@OperationParam(name=ProviderConstants.MDM_QUERY_LINKS_LINK_SOURCE, min = 0, max = 1) StringType theLinkSource,
|
||||||
ServletRequestDetails theRequestDetails) {
|
ServletRequestDetails theRequestDetails) {
|
||||||
|
|
||||||
Stream<MdmLinkJson> empiLinkJson = myMdmControllerSvc.queryLinks(extractStringOrNull(theGoldenResourceId),
|
Stream<MdmLinkJson> mdmLinkJson = myMdmControllerSvc.queryLinks(extractStringOrNull(theGoldenResourceId),
|
||||||
extractStringOrNull(theResourceId), extractStringOrNull(theMatchResult), extractStringOrNull(theLinkSource),
|
extractStringOrNull(theResourceId), extractStringOrNull(theMatchResult), extractStringOrNull(theLinkSource),
|
||||||
createMdmContext(theRequestDetails, MdmTransactionContext.OperationType.QUERY_LINKS,
|
createMdmContext(theRequestDetails, MdmTransactionContext.OperationType.QUERY_LINKS,
|
||||||
getResourceType(ProviderConstants.MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID, theGoldenResourceId))
|
getResourceType(ProviderConstants.MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID, theGoldenResourceId))
|
||||||
);
|
);
|
||||||
return (Parameters) parametersFromMdmLinks(empiLinkJson, true);
|
return (Parameters) parametersFromMdmLinks(mdmLinkJson, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(name = ProviderConstants.MDM_DUPLICATE_GOLDEN_RESOURCES, idempotent = true)
|
@Operation(name = ProviderConstants.MDM_DUPLICATE_GOLDEN_RESOURCES, idempotent = true)
|
||||||
|
@ -229,7 +229,7 @@ public class MdmProviderR4 extends BaseMdmProvider {
|
||||||
@Operation(name = ProviderConstants.OPERATION_MDM_SUBMIT, idempotent = false, returnParameters = {
|
@Operation(name = ProviderConstants.OPERATION_MDM_SUBMIT, idempotent = false, returnParameters = {
|
||||||
@OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type= IntegerType.class)
|
@OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type= IntegerType.class)
|
||||||
})
|
})
|
||||||
public Parameters empiBatchOnAllTargets(
|
public Parameters mdmBatchOnAllTargets(
|
||||||
//TODO GGG MDM: also have to take it an optional resourceType here, to clarify which resources should have MDM run on them.
|
//TODO GGG MDM: also have to take it an optional resourceType here, to clarify which resources should have MDM run on them.
|
||||||
@OperationParam(name= ProviderConstants.MDM_BATCH_RUN_RESOURCE_TYPE, min = 0 , max = 1) StringType theResourceType,
|
@OperationParam(name= ProviderConstants.MDM_BATCH_RUN_RESOURCE_TYPE, min = 0 , max = 1) StringType theResourceType,
|
||||||
@OperationParam(name= ProviderConstants.MDM_BATCH_RUN_CRITERIA,min = 0 , max = 1) StringType theCriteria,
|
@OperationParam(name= ProviderConstants.MDM_BATCH_RUN_CRITERIA,min = 0 , max = 1) StringType theCriteria,
|
||||||
|
@ -255,7 +255,7 @@ public class MdmProviderR4 extends BaseMdmProvider {
|
||||||
@Operation(name = ProviderConstants.OPERATION_MDM_SUBMIT, idempotent = false, type = Patient.class, returnParameters = {
|
@Operation(name = ProviderConstants.OPERATION_MDM_SUBMIT, idempotent = false, type = Patient.class, returnParameters = {
|
||||||
@OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type = IntegerType.class)
|
@OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type = IntegerType.class)
|
||||||
})
|
})
|
||||||
public Parameters empiBatchPatientInstance(
|
public Parameters mdmBatchPatientInstance(
|
||||||
@IdParam IIdType theIdParam,
|
@IdParam IIdType theIdParam,
|
||||||
RequestDetails theRequest) {
|
RequestDetails theRequest) {
|
||||||
long submittedCount = myMdmSubmitSvc.submitTargetToMdm(theIdParam);
|
long submittedCount = myMdmSubmitSvc.submitTargetToMdm(theIdParam);
|
||||||
|
@ -265,7 +265,7 @@ public class MdmProviderR4 extends BaseMdmProvider {
|
||||||
@Operation(name = ProviderConstants.OPERATION_MDM_SUBMIT, idempotent = false, type = Patient.class, returnParameters = {
|
@Operation(name = ProviderConstants.OPERATION_MDM_SUBMIT, idempotent = false, type = Patient.class, returnParameters = {
|
||||||
@OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type = IntegerType.class)
|
@OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type = IntegerType.class)
|
||||||
})
|
})
|
||||||
public Parameters empiBatchPatientType(
|
public Parameters mdmBatchPatientType(
|
||||||
@OperationParam(name = ProviderConstants.MDM_BATCH_RUN_CRITERIA) StringType theCriteria,
|
@OperationParam(name = ProviderConstants.MDM_BATCH_RUN_CRITERIA) StringType theCriteria,
|
||||||
RequestDetails theRequest) {
|
RequestDetails theRequest) {
|
||||||
String criteria = convertStringTypeToString(theCriteria);
|
String criteria = convertStringTypeToString(theCriteria);
|
||||||
|
@ -276,7 +276,7 @@ public class MdmProviderR4 extends BaseMdmProvider {
|
||||||
@Operation(name = ProviderConstants.OPERATION_MDM_SUBMIT, idempotent = false, type = Practitioner.class, returnParameters = {
|
@Operation(name = ProviderConstants.OPERATION_MDM_SUBMIT, idempotent = false, type = Practitioner.class, returnParameters = {
|
||||||
@OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type = IntegerType.class)
|
@OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type = IntegerType.class)
|
||||||
})
|
})
|
||||||
public Parameters empiBatchPractitionerInstance(
|
public Parameters mdmBatchPractitionerInstance(
|
||||||
@IdParam IIdType theIdParam,
|
@IdParam IIdType theIdParam,
|
||||||
RequestDetails theRequest) {
|
RequestDetails theRequest) {
|
||||||
long submittedCount = myMdmSubmitSvc.submitTargetToMdm(theIdParam);
|
long submittedCount = myMdmSubmitSvc.submitTargetToMdm(theIdParam);
|
||||||
|
@ -286,7 +286,7 @@ public class MdmProviderR4 extends BaseMdmProvider {
|
||||||
@Operation(name = ProviderConstants.OPERATION_MDM_SUBMIT, idempotent = false, type = Practitioner.class, returnParameters = {
|
@Operation(name = ProviderConstants.OPERATION_MDM_SUBMIT, idempotent = false, type = Practitioner.class, returnParameters = {
|
||||||
@OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type = IntegerType.class)
|
@OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_RUN_OUT_PARAM_SUBMIT_COUNT, type = IntegerType.class)
|
||||||
})
|
})
|
||||||
public Parameters empiBatchPractitionerType(
|
public Parameters mdmBatchPractitionerType(
|
||||||
@OperationParam(name = ProviderConstants.MDM_BATCH_RUN_CRITERIA) StringType theCriteria,
|
@OperationParam(name = ProviderConstants.MDM_BATCH_RUN_CRITERIA) StringType theCriteria,
|
||||||
RequestDetails theRequest) {
|
RequestDetails theRequest) {
|
||||||
String criteria = convertStringTypeToString(theCriteria);
|
String criteria = convertStringTypeToString(theCriteria);
|
||||||
|
@ -295,7 +295,7 @@ public class MdmProviderR4 extends BaseMdmProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function to build the out-parameters for all batch EMPI operations.
|
* Helper function to build the out-parameters for all batch MDM operations.
|
||||||
*/
|
*/
|
||||||
private Parameters buildMdmOutParametersWithCount(long theCount) {
|
private Parameters buildMdmOutParametersWithCount(long theCount) {
|
||||||
Parameters parameters = new Parameters();
|
Parameters parameters = new Parameters();
|
||||||
|
|
|
@ -72,6 +72,8 @@ public class MdmRuleValidator implements IMdmRuleValidator {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void validateMdmTypes(MdmRulesJson theMdmRulesJson) {
|
public void validateMdmTypes(MdmRulesJson theMdmRulesJson) {
|
||||||
|
ourLog.info("Validating MDM types {}", theMdmRulesJson.getMdmTypes());
|
||||||
|
|
||||||
if (theMdmRulesJson.getMdmTypes() == null) {
|
if (theMdmRulesJson.getMdmTypes() == null) {
|
||||||
throw new ConfigurationException("mdmTypes must be set to a list of resource types.");
|
throw new ConfigurationException("mdmTypes must be set to a list of resource types.");
|
||||||
}
|
}
|
||||||
|
@ -87,6 +89,8 @@ public class MdmRuleValidator implements IMdmRuleValidator {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateSearchParams(MdmRulesJson theMdmRulesJson) {
|
private void validateSearchParams(MdmRulesJson theMdmRulesJson) {
|
||||||
|
ourLog.info("Validating search parameters {}", theMdmRulesJson.getCandidateSearchParams());
|
||||||
|
|
||||||
for (MdmResourceSearchParamJson searchParams : theMdmRulesJson.getCandidateSearchParams()) {
|
for (MdmResourceSearchParamJson searchParams : theMdmRulesJson.getCandidateSearchParams()) {
|
||||||
searchParams.iterator().forEachRemaining(
|
searchParams.iterator().forEachRemaining(
|
||||||
searchParam -> validateSearchParam("candidateSearchParams", searchParams.getResourceType(), searchParam));
|
searchParam -> validateSearchParam("candidateSearchParams", searchParams.getResourceType(), searchParam));
|
||||||
|
@ -112,6 +116,8 @@ public class MdmRuleValidator implements IMdmRuleValidator {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateMatchFields(MdmRulesJson theMdmRulesJson) {
|
private void validateMatchFields(MdmRulesJson theMdmRulesJson) {
|
||||||
|
ourLog.info("Validating match fields {}", theMdmRulesJson.getMatchFields());
|
||||||
|
|
||||||
Set<String> names = new HashSet<>();
|
Set<String> names = new HashSet<>();
|
||||||
for (MdmFieldMatchJson fieldMatch : theMdmRulesJson.getMatchFields()) {
|
for (MdmFieldMatchJson fieldMatch : theMdmRulesJson.getMatchFields()) {
|
||||||
if (names.contains(fieldMatch.getName())) {
|
if (names.contains(fieldMatch.getName())) {
|
||||||
|
@ -137,7 +143,6 @@ public class MdmRuleValidator implements IMdmRuleValidator {
|
||||||
private void validatePath(List<String> theMdmTypes, MdmFieldMatchJson theFieldMatch) {
|
private void validatePath(List<String> theMdmTypes, MdmFieldMatchJson theFieldMatch) {
|
||||||
String resourceType = theFieldMatch.getResourceType();
|
String resourceType = theFieldMatch.getResourceType();
|
||||||
|
|
||||||
|
|
||||||
if (MdmConstants.ALL_RESOURCE_SEARCH_PARAM_TYPE.equals(resourceType)) {
|
if (MdmConstants.ALL_RESOURCE_SEARCH_PARAM_TYPE.equals(resourceType)) {
|
||||||
validateFieldPathForAllTypes(theMdmTypes, theFieldMatch);
|
validateFieldPathForAllTypes(theMdmTypes, theFieldMatch);
|
||||||
} else {
|
} else {
|
||||||
|
@ -153,18 +158,20 @@ public class MdmRuleValidator implements IMdmRuleValidator {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateFieldPathForType(String theResourceType, MdmFieldMatchJson theFieldMatch) {
|
private void validateFieldPathForType(String theResourceType, MdmFieldMatchJson theFieldMatch) {
|
||||||
|
ourLog.debug(" validating resource {} for {} ", theResourceType, theFieldMatch.getResourcePath());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
RuntimeResourceDefinition resourceDefinition = myFhirContext.getResourceDefinition(theResourceType);
|
RuntimeResourceDefinition resourceDefinition = myFhirContext.getResourceDefinition(theResourceType);
|
||||||
Class<? extends IBaseResource> implementingClass = resourceDefinition.getImplementingClass();
|
Class<? extends IBaseResource> implementingClass = resourceDefinition.getImplementingClass();
|
||||||
myTerser.getDefinition(implementingClass, theResourceType + "." + theFieldMatch.getResourcePath());
|
String path = theResourceType + "." + theFieldMatch.getResourcePath();
|
||||||
} catch (DataFormatException | ConfigurationException e) {
|
myTerser.getDefinition(implementingClass, path);
|
||||||
|
} catch (DataFormatException | ConfigurationException | ClassCastException e) {
|
||||||
throw new ConfigurationException("MatchField " +
|
throw new ConfigurationException("MatchField " +
|
||||||
theFieldMatch.getName() +
|
theFieldMatch.getName() +
|
||||||
" resourceType " +
|
" resourceType " +
|
||||||
theFieldMatch.getResourceType() +
|
theFieldMatch.getResourceType() +
|
||||||
" has invalid path '" + theFieldMatch.getResourcePath() + "'. " +
|
" has invalid path '" + theFieldMatch.getResourcePath() + "'. " +
|
||||||
e.getMessage());
|
e.getMessage());
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,6 +184,8 @@ public class MdmRuleValidator implements IMdmRuleValidator {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ourLog.info("Validating system URI {}", theMdmRulesJson.getEnterpriseEIDSystem());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
new URI(theMdmRulesJson.getEnterpriseEIDSystem());
|
new URI(theMdmRulesJson.getEnterpriseEIDSystem());
|
||||||
} catch (URISyntaxException e) {
|
} catch (URISyntaxException e) {
|
||||||
|
|
|
@ -40,11 +40,10 @@ public class MdmSettings implements IMdmSettings {
|
||||||
private boolean myPreventEidUpdates;
|
private boolean myPreventEidUpdates;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If disabled, the underlying EMPI system will operate under the following assumptions:
|
* If disabled, the underlying MDM system will operate under the following assumptions:
|
||||||
*
|
|
||||||
* 1. Patients/Practitioners may have more than 1 EID of the same system simultaneously.
|
|
||||||
* 2. During linking, incoming patient EIDs will be merged with existing Person EIDs.
|
|
||||||
*
|
*
|
||||||
|
* 1. Target resource may have more than 1 EID of the same system simultaneously.
|
||||||
|
* 2. During linking, incoming patient EIDs will be merged with existing Golden Resource EIDs.
|
||||||
*/
|
*/
|
||||||
private boolean myPreventMultipleEids;
|
private boolean myPreventMultipleEids;
|
||||||
|
|
||||||
|
|
|
@ -25,8 +25,9 @@ import ca.uhn.fhir.rest.param.TokenParamModifier;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This SearchParamJson, unlike EmpiREsourceSearchParamJson, is responsible for doing inclusions during empi
|
* This class, unlike {@link MdmResourceSearchParamJson}, is responsible for doing inclusions during MDM
|
||||||
* candidate searching. e.g. When doing candidate matching, only consider candidates that match all EmpiFilterSearchParams.
|
* candidate searching. e.g. When doing candidate matching, only consider candidates that match all
|
||||||
|
* MdmFilterSearchParams.
|
||||||
*/
|
*/
|
||||||
public class MdmFilterSearchParamJson implements IModelJson {
|
public class MdmFilterSearchParamJson implements IModelJson {
|
||||||
@JsonProperty(value = "resourceType", required = true)
|
@JsonProperty(value = "resourceType", required = true)
|
||||||
|
|
|
@ -34,8 +34,9 @@ import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@JsonDeserialize(converter = MdmRulesJson.EmpiRulesJsonConverter.class)
|
@JsonDeserialize(converter = MdmRulesJson.MdmRulesJsonConverter.class)
|
||||||
public class MdmRulesJson implements IModelJson {
|
public class MdmRulesJson implements IModelJson {
|
||||||
|
|
||||||
@JsonProperty(value = "version", required = true)
|
@JsonProperty(value = "version", required = true)
|
||||||
String myVersion;
|
String myVersion;
|
||||||
@JsonProperty(value = "candidateSearchParams", required = true)
|
@JsonProperty(value = "candidateSearchParams", required = true)
|
||||||
|
@ -163,12 +164,12 @@ public class MdmRulesJson implements IModelJson {
|
||||||
/**
|
/**
|
||||||
* Ensure the vector map is initialized after we deserialize
|
* Ensure the vector map is initialized after we deserialize
|
||||||
*/
|
*/
|
||||||
static class EmpiRulesJsonConverter extends StdConverter<MdmRulesJson, MdmRulesJson> {
|
static class MdmRulesJsonConverter extends StdConverter<MdmRulesJson, MdmRulesJson> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This empty constructor is required by Jackson
|
* This empty constructor is required by Jackson
|
||||||
*/
|
*/
|
||||||
public EmpiRulesJsonConverter() {
|
public MdmRulesJsonConverter() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -38,7 +38,7 @@ public class VectorMatchResultMap {
|
||||||
|
|
||||||
VectorMatchResultMap(MdmRulesJson theMdmRulesJson) {
|
VectorMatchResultMap(MdmRulesJson theMdmRulesJson) {
|
||||||
myMdmRulesJson = theMdmRulesJson;
|
myMdmRulesJson = theMdmRulesJson;
|
||||||
//no reason to hold the entire empirulesjson here
|
// no reason to hold the entire mdmRulesJson here
|
||||||
initMap();
|
initMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,9 +26,11 @@ import org.hl7.fhir.instance.model.api.IBase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enum for holding all the known FHIR Element matchers that we support in HAPI. The string matchers first
|
* Enum for holding all the known FHIR Element matchers that we support in HAPI. The string matchers first
|
||||||
* encode the string using an Apache Encoder before comparing them. https://commons.apache.org/proper/commons-codec/userguide.html
|
* encode the string using an Apache Encoder before comparing them.
|
||||||
|
* https://commons.apache.org/proper/commons-codec/userguide.html
|
||||||
*/
|
*/
|
||||||
public enum MdmMatcherEnum {
|
public enum MdmMatcherEnum {
|
||||||
|
|
||||||
CAVERPHONE1(new HapiStringMatcher(new PhoneticEncoderMatcher(PhoneticEncoderEnum.CAVERPHONE1))),
|
CAVERPHONE1(new HapiStringMatcher(new PhoneticEncoderMatcher(PhoneticEncoderEnum.CAVERPHONE1))),
|
||||||
CAVERPHONE2(new HapiStringMatcher(new PhoneticEncoderMatcher(PhoneticEncoderEnum.CAVERPHONE2))),
|
CAVERPHONE2(new HapiStringMatcher(new PhoneticEncoderMatcher(PhoneticEncoderEnum.CAVERPHONE2))),
|
||||||
COLOGNE(new HapiStringMatcher(new PhoneticEncoderMatcher(PhoneticEncoderEnum.COLOGNE))),
|
COLOGNE(new HapiStringMatcher(new PhoneticEncoderMatcher(PhoneticEncoderEnum.COLOGNE))),
|
||||||
|
@ -48,22 +50,23 @@ public enum MdmMatcherEnum {
|
||||||
|
|
||||||
IDENTIFIER(new IdentifierMatcher());
|
IDENTIFIER(new IdentifierMatcher());
|
||||||
|
|
||||||
private final IMdmFieldMatcher myEmpiFieldMatcher;
|
private final IMdmFieldMatcher myMdmFieldMatcher;
|
||||||
|
|
||||||
MdmMatcherEnum(IMdmFieldMatcher theEmpiFieldMatcher) {
|
MdmMatcherEnum(IMdmFieldMatcher theMdmFieldMatcher) {
|
||||||
myEmpiFieldMatcher = theEmpiFieldMatcher;
|
myMdmFieldMatcher = theMdmFieldMatcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines whether two FHIR elements match according using the provided IEmpiFieldMatcher
|
* Determines whether two FHIR elements match according using the provided {@link IMdmFieldMatcher}
|
||||||
|
*
|
||||||
* @param theFhirContext
|
* @param theFhirContext
|
||||||
* @param theLeftBase left FHIR element to compare
|
* @param theLeftBase left FHIR element to compare
|
||||||
* @param theRightBase right FHIR element to compare
|
* @param theRightBase right FHIR element to compare
|
||||||
* @param theExact used by String matchers. If "false" then the string is normalized (case, accents) before comparing. If "true" then an exact string comparison is performed.
|
* @param theExact used by String matchers. If "false" then the string is normalized (case, accents) before comparing. If "true" then an exact string comparison is performed.
|
||||||
* @param theIdentifierSystem used optionally by the IDENTIFIER matcher, when present, only matches the identifiers if they belong to this system.
|
* @param theIdentifierSystem used optionally by the IDENTIFIER matcher, when present, only matches the identifiers if they belong to this system.
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public boolean match(FhirContext theFhirContext, IBase theLeftBase, IBase theRightBase, boolean theExact, String theIdentifierSystem) {
|
public boolean match(FhirContext theFhirContext, IBase theLeftBase, IBase theRightBase, boolean theExact, String theIdentifierSystem) {
|
||||||
return myEmpiFieldMatcher.matches(theFhirContext, theLeftBase, theRightBase, theExact, theIdentifierSystem);
|
return myMdmFieldMatcher.matches(theFhirContext, theLeftBase, theRightBase, theExact, theIdentifierSystem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,20 +32,21 @@ import org.hl7.fhir.instance.model.api.IBase;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
public enum MdmSimilarityEnum {
|
public enum MdmSimilarityEnum {
|
||||||
|
|
||||||
JARO_WINKLER(new HapiStringSimilarity(new JaroWinkler())),
|
JARO_WINKLER(new HapiStringSimilarity(new JaroWinkler())),
|
||||||
COSINE(new HapiStringSimilarity(new Cosine())),
|
COSINE(new HapiStringSimilarity(new Cosine())),
|
||||||
JACCARD(new HapiStringSimilarity(new Jaccard())),
|
JACCARD(new HapiStringSimilarity(new Jaccard())),
|
||||||
LEVENSCHTEIN(new HapiStringSimilarity(new NormalizedLevenshtein())),
|
LEVENSCHTEIN(new HapiStringSimilarity(new NormalizedLevenshtein())),
|
||||||
SORENSEN_DICE(new HapiStringSimilarity(new SorensenDice()));
|
SORENSEN_DICE(new HapiStringSimilarity(new SorensenDice()));
|
||||||
|
|
||||||
private final IMdmFieldSimilarity myEmpiFieldSimilarity;
|
private final IMdmFieldSimilarity myMdmFieldSimilarity;
|
||||||
|
|
||||||
MdmSimilarityEnum(IMdmFieldSimilarity theEmpiFieldSimilarity) {
|
MdmSimilarityEnum(IMdmFieldSimilarity theMdmFieldSimilarity) {
|
||||||
myEmpiFieldSimilarity = theEmpiFieldSimilarity;
|
myMdmFieldSimilarity = theMdmFieldSimilarity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MdmMatchEvaluation match(FhirContext theFhirContext, IBase theLeftBase, IBase theRightBase, boolean theExact, @Nullable Double theThreshold) {
|
public MdmMatchEvaluation match(FhirContext theFhirContext, IBase theLeftBase, IBase theRightBase, boolean theExact, @Nullable Double theThreshold) {
|
||||||
return matchBySimilarity(myEmpiFieldSimilarity, theFhirContext, theLeftBase, theRightBase, theExact, theThreshold);
|
return matchBySimilarity(myMdmFieldSimilarity, theFhirContext, theLeftBase, theRightBase, theExact, theThreshold);
|
||||||
}
|
}
|
||||||
|
|
||||||
private MdmMatchEvaluation matchBySimilarity(IMdmFieldSimilarity theSimilarity, FhirContext theFhirContext, IBase theLeftBase, IBase theRightBase, boolean theExact, Double theThreshold) {
|
private MdmMatchEvaluation matchBySimilarity(IMdmFieldSimilarity theSimilarity, FhirContext theFhirContext, IBase theLeftBase, IBase theRightBase, boolean theExact, Double theThreshold) {
|
||||||
|
|
|
@ -38,6 +38,7 @@ import static ca.uhn.fhir.mdm.api.MdmConstants.ALL_RESOURCE_SEARCH_PARAM_TYPE;
|
||||||
* This class is responsible for performing matching between raw-typed values of a left record and a right record.
|
* This class is responsible for performing matching between raw-typed values of a left record and a right record.
|
||||||
*/
|
*/
|
||||||
public class MdmResourceFieldMatcher {
|
public class MdmResourceFieldMatcher {
|
||||||
|
|
||||||
private final FhirContext myFhirContext;
|
private final FhirContext myFhirContext;
|
||||||
private final MdmFieldMatchJson myMdmFieldMatchJson;
|
private final MdmFieldMatchJson myMdmFieldMatchJson;
|
||||||
private final String myResourceType;
|
private final String myResourceType;
|
||||||
|
@ -55,7 +56,8 @@ public class MdmResourceFieldMatcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compares two {@link IBaseResource}s and determines if they match, using the algorithm defined in this object's EmpiFieldMatchJson.
|
* Compares two {@link IBaseResource}s and determines if they match, using the algorithm defined in this object's
|
||||||
|
* {@link MdmFieldMatchJson}.
|
||||||
*
|
*
|
||||||
* In this implementation, it determines whether a given field matches between two resources. Internally this is evaluated using FhirPath. If any of the elements of theLeftResource
|
* In this implementation, it determines whether a given field matches between two resources. Internally this is evaluated using FhirPath. If any of the elements of theLeftResource
|
||||||
* match any of the elements of theRightResource, will return true. Otherwise, false.
|
* match any of the elements of theRightResource, will return true. Otherwise, false.
|
||||||
|
|
|
@ -53,7 +53,7 @@ public final class AssuranceLevelUtil {
|
||||||
case POSSIBLE_DUPLICATE:
|
case POSSIBLE_DUPLICATE:
|
||||||
case NO_MATCH:
|
case NO_MATCH:
|
||||||
default:
|
default:
|
||||||
throw new InvalidRequestException("An AUTO EMPI Link may not have a match result of " + theMatchResult);
|
throw new InvalidRequestException("An AUTO MDM Link may not have a match result of " + theMatchResult);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ public final class AssuranceLevelUtil {
|
||||||
case POSSIBLE_DUPLICATE:
|
case POSSIBLE_DUPLICATE:
|
||||||
case POSSIBLE_MATCH:
|
case POSSIBLE_MATCH:
|
||||||
default:
|
default:
|
||||||
throw new InvalidRequestException("A MANUAL EMPI Link may not have a match result of " + theMatchResult);
|
throw new InvalidRequestException("A MANUAL MDM Link may not have a match result of " + theMatchResult);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,6 +62,7 @@ import static ca.uhn.fhir.context.FhirVersionEnum.R4;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class GoldenResourceHelper {
|
public class GoldenResourceHelper {
|
||||||
|
|
||||||
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
|
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
|
||||||
|
|
||||||
private static final String FIELD_NAME_IDENTIFIER = "identifier";
|
private static final String FIELD_NAME_IDENTIFIER = "identifier";
|
||||||
|
@ -100,7 +101,7 @@ public class GoldenResourceHelper {
|
||||||
|
|
||||||
addHapiEidIfNoExternalEidIsPresent(newSourceResource, sourceResourceIdentifier, theIncomingResource);
|
addHapiEidIfNoExternalEidIsPresent(newSourceResource, sourceResourceIdentifier, theIncomingResource);
|
||||||
|
|
||||||
MdmUtil.setEmpiManaged(newSourceResource);
|
MdmUtil.setMdmManaged(newSourceResource);
|
||||||
MdmUtil.setGoldenResource(newSourceResource);
|
MdmUtil.setGoldenResource(newSourceResource);
|
||||||
|
|
||||||
return (T) newSourceResource;
|
return (T) newSourceResource;
|
||||||
|
@ -192,12 +193,11 @@ public class GoldenResourceHelper {
|
||||||
for (IBase base : sourceResourceIdentifiers) {
|
for (IBase base : sourceResourceIdentifiers) {
|
||||||
Optional<IPrimitiveType> system = fhirPath.evaluateFirst(base, "system", IPrimitiveType.class);
|
Optional<IPrimitiveType> system = fhirPath.evaluateFirst(base, "system", IPrimitiveType.class);
|
||||||
if (system.isPresent()) {
|
if (system.isPresent()) {
|
||||||
String empiSystem = myMdmSettings.getMdmRules().getEnterpriseEIDSystem();
|
String mdmSystem = myMdmSettings.getMdmRules().getEnterpriseEIDSystem();
|
||||||
String baseSystem = system.get().getValueAsString();
|
String baseSystem = system.get().getValueAsString();
|
||||||
if (Objects.equals(baseSystem, empiSystem)) {
|
if (Objects.equals(baseSystem, mdmSystem)) {
|
||||||
cloneEidIntoResource(theSourceResourceIdentifier, base, theNewSourceResource);
|
cloneEidIntoResource(theSourceResourceIdentifier, base, theNewSourceResource);
|
||||||
} else if (ourLog.isDebugEnabled()) {
|
ourLog.debug("System {} differs from system in the MDM rules {}", baseSystem, mdmSystem);
|
||||||
ourLog.debug(String.format("System %s differs from system in the EMPI rules %s", baseSystem, empiSystem));
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ourLog.debug("System is missing, skipping");
|
ourLog.debug("System is missing, skipping");
|
||||||
|
@ -257,12 +257,10 @@ public class GoldenResourceHelper {
|
||||||
for (IBase base : sourceResourceIdentifiers) {
|
for (IBase base : sourceResourceIdentifiers) {
|
||||||
Optional<IPrimitiveType> system = fhirPath.evaluateFirst(base, "system", IPrimitiveType.class);
|
Optional<IPrimitiveType> system = fhirPath.evaluateFirst(base, "system", IPrimitiveType.class);
|
||||||
if (system.isPresent()) {
|
if (system.isPresent()) {
|
||||||
String empiSystem = myMdmSettings.getMdmRules().getEnterpriseEIDSystem();
|
String mdmSystem = myMdmSettings.getMdmRules().getEnterpriseEIDSystem();
|
||||||
String baseSystem = system.get().getValueAsString();
|
String baseSystem = system.get().getValueAsString();
|
||||||
if (Objects.equals(baseSystem, empiSystem)) {
|
if (Objects.equals(baseSystem, mdmSystem)) {
|
||||||
if (ourLog.isDebugEnabled()) {
|
ourLog.debug(String.format("Found EID confirming to MDM rules {}. It should not be copied, skipping", baseSystem));
|
||||||
ourLog.debug(String.format("Found EID confirming to EMPI rules %s. It should not be copied, skipping", baseSystem));
|
|
||||||
}
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,23 +29,26 @@ import javax.annotation.Nonnull;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public final class MdmUtil {
|
public final class MdmUtil {
|
||||||
private MdmUtil() {}
|
|
||||||
|
private MdmUtil() {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the resource is tagged as not managed by empi, return false. Otherwise true.
|
* If the resource is tagged as not managed by MDM, return false. Otherwise true.
|
||||||
* @param theBaseResource The Patient/Practitioner that is potentially managed by EMPI.
|
*
|
||||||
* @return A boolean indicating whether EMPI should manage this resource.
|
* @param theBaseResource The FHIR resource that is potentially managed by MDM.
|
||||||
|
* @return A boolean indicating whether MDM can manage this resource.
|
||||||
*/
|
*/
|
||||||
public static boolean isMdmAllowed(IBaseResource theBaseResource) {
|
public static boolean isMdmAllowed(IBaseResource theBaseResource) {
|
||||||
return theBaseResource.getMeta().getTag(MdmConstants.SYSTEM_MDM_MANAGED, MdmConstants.CODE_NO_MDM_MANAGED) == null;
|
return theBaseResource.getMeta().getTag(MdmConstants.SYSTEM_MDM_MANAGED, MdmConstants.CODE_NO_MDM_MANAGED) == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks for the presence of the EMPI-managed tag, indicating the EMPI system has ownership
|
* Checks for the presence of the MDM-managed tag, indicating the MDM system has ownership
|
||||||
* of this Person's links.
|
* of this Person's links.
|
||||||
*
|
*
|
||||||
* @param theBaseResource the resource to check.
|
* @param theBaseResource the resource to check.
|
||||||
* @return a boolean indicating whether or not EMPI manages this Person.
|
* @return a boolean indicating whether or not MDM manages this FHIR resource.
|
||||||
*/
|
*/
|
||||||
public static boolean isMdmManaged(IBaseResource theBaseResource) {
|
public static boolean isMdmManaged(IBaseResource theBaseResource) {
|
||||||
return resourceHasTag(theBaseResource, MdmConstants.SYSTEM_MDM_MANAGED, MdmConstants.CODE_HAPI_MDM_MANAGED);
|
return resourceHasTag(theBaseResource, MdmConstants.SYSTEM_MDM_MANAGED, MdmConstants.CODE_HAPI_MDM_MANAGED);
|
||||||
|
@ -85,14 +88,13 @@ public final class MdmUtil {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the EMPI-managed tag, indicating the EMPI system has ownership of this
|
* Sets the MDM-managed tag, indicating the MDM system has ownership of this
|
||||||
* Resource. No changes are made if resource is already maanged by EMPI.
|
* Resource. No changes are made if resource is already maanged by MDM.
|
||||||
*
|
*
|
||||||
* @param theBaseResource resource to set the tag for
|
* @param theBaseResource resource to set the tag for
|
||||||
* @return
|
* @return Returns resource with the tag set.
|
||||||
* Returns resource with the tag set.
|
|
||||||
*/
|
*/
|
||||||
public static IBaseResource setEmpiManaged(IBaseResource theBaseResource) {
|
public static IBaseResource setMdmManaged(IBaseResource theBaseResource) {
|
||||||
return setTagOnResource(theBaseResource, MdmConstants.SYSTEM_MDM_MANAGED, MdmConstants.CODE_HAPI_MDM_MANAGED, MdmConstants.DISPLAY_HAPI_MDM_MANAGED);
|
return setTagOnResource(theBaseResource, MdmConstants.SYSTEM_MDM_MANAGED, MdmConstants.CODE_HAPI_MDM_MANAGED, MdmConstants.DISPLAY_HAPI_MDM_MANAGED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,10 +127,4 @@ public final class MdmUtil {
|
||||||
}
|
}
|
||||||
return theGoldenResource;
|
return theGoldenResource;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isEmpiManagedPerson(FhirContext theFhirContext, IBaseResource theResource) {
|
|
||||||
String resourceType = theFhirContext.getResourceType(theResource);
|
|
||||||
|
|
||||||
return "Person".equals(resourceType) && isMdmManaged(theResource);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,13 +33,13 @@ import org.springframework.stereotype.Service;
|
||||||
public class MessageHelper {
|
public class MessageHelper {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private final IMdmSettings myEmpiSettings;
|
private final IMdmSettings myMdmSettings;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private final FhirContext myFhirContext;
|
private final FhirContext myFhirContext;
|
||||||
|
|
||||||
public MessageHelper(IMdmSettings theEmpiSettings, FhirContext theFhirContext) {
|
public MessageHelper(IMdmSettings theMdmSettings, FhirContext theFhirContext) {
|
||||||
myEmpiSettings = theEmpiSettings;
|
myMdmSettings = theMdmSettings;
|
||||||
myFhirContext = theFhirContext;
|
myFhirContext = theFhirContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ public class MessageHelper {
|
||||||
|
|
||||||
public String getMessageForUnsupportedResource(String theName, String theResourceType) {
|
public String getMessageForUnsupportedResource(String theName, String theResourceType) {
|
||||||
return String.format("Only %s resources can be merged. The %s points to a %s",
|
return String.format("Only %s resources can be merged. The %s points to a %s",
|
||||||
myEmpiSettings.getSupportedMdmTypes(), theName, theResourceType);
|
myMdmSettings.getSupportedMdmTypes(), theName, theResourceType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getMessageForUnsupportedMatchResult() {
|
public String getMessageForUnsupportedMatchResult() {
|
||||||
|
@ -64,12 +64,12 @@ public class MessageHelper {
|
||||||
|
|
||||||
public String getMessageForUnsupportedFirstArgumentTypeInUpdate(String goldenRecordType) {
|
public String getMessageForUnsupportedFirstArgumentTypeInUpdate(String goldenRecordType) {
|
||||||
return "First argument to " + ProviderConstants.MDM_UPDATE_LINK + " must be a "
|
return "First argument to " + ProviderConstants.MDM_UPDATE_LINK + " must be a "
|
||||||
+ myEmpiSettings.getSupportedMdmTypes() + ". Was " + goldenRecordType;
|
+ myMdmSettings.getSupportedMdmTypes() + ". Was " + goldenRecordType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getMessageForUnsupportedSecondArgumentTypeInUpdate(String theGoldenRecordType) {
|
public String getMessageForUnsupportedSecondArgumentTypeInUpdate(String theGoldenRecordType) {
|
||||||
return "First argument to " + ProviderConstants.MDM_UPDATE_LINK + " must be a "
|
return "First argument to " + ProviderConstants.MDM_UPDATE_LINK + " must be a "
|
||||||
+ myEmpiSettings.getSupportedMdmTypes() + ". Was " + theGoldenRecordType;
|
+ myMdmSettings.getSupportedMdmTypes() + ". Was " + theGoldenRecordType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getMessageForArgumentTypeMismatchInUpdate(String theGoldenRecordType, String theTargetType) {
|
public String getMessageForArgumentTypeMismatchInUpdate(String theGoldenRecordType, String theTargetType) {
|
||||||
|
@ -79,7 +79,7 @@ public class MessageHelper {
|
||||||
|
|
||||||
public String getMessageForUnsupportedTarget() {
|
public String getMessageForUnsupportedTarget() {
|
||||||
return "The target is marked with the " + MdmConstants.CODE_NO_MDM_MANAGED
|
return "The target is marked with the " + MdmConstants.CODE_NO_MDM_MANAGED
|
||||||
+ " tag which means it may not be EMPI linked.";
|
+ " tag which means it may not be MDM linked.";
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getMessageForNoLink(IAnyResource theGoldenRecord, IAnyResource theTarget) {
|
public String getMessageForNoLink(IAnyResource theGoldenRecord, IAnyResource theTarget) {
|
||||||
|
|
|
@ -27,12 +27,14 @@ public class MdmRuleValidatorTest extends BaseR4Test {
|
||||||
when(mySearchParamRetriever.getActiveSearchParam("Practitioner", "identifier")).thenReturn(mock(RuntimeSearchParam.class));
|
when(mySearchParamRetriever.getActiveSearchParam("Practitioner", "identifier")).thenReturn(mock(RuntimeSearchParam.class));
|
||||||
when(mySearchParamRetriever.getActiveSearchParam("Medication", "identifier")).thenReturn(mock(RuntimeSearchParam.class));
|
when(mySearchParamRetriever.getActiveSearchParam("Medication", "identifier")).thenReturn(mock(RuntimeSearchParam.class));
|
||||||
when(mySearchParamRetriever.getActiveSearchParam("AllergyIntolerance", "identifier")).thenReturn(null);
|
when(mySearchParamRetriever.getActiveSearchParam("AllergyIntolerance", "identifier")).thenReturn(null);
|
||||||
|
when(mySearchParamRetriever.getActiveSearchParam("Organization", "identifier")).thenReturn(mock(RuntimeSearchParam.class));
|
||||||
|
when(mySearchParamRetriever.getActiveSearchParam("Organization", "active")).thenReturn(mock(RuntimeSearchParam.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testValidate() throws IOException {
|
public void testValidate() throws IOException {
|
||||||
try {
|
try {
|
||||||
setEmpiRuleJson("bad-rules-bad-url.json");
|
setMdmRuleJson("bad-rules-bad-url.json");
|
||||||
fail();
|
fail();
|
||||||
} catch (ConfigurationException e){
|
} catch (ConfigurationException e){
|
||||||
assertThat(e.getMessage(), is("Enterprise Identifier System (eidSystem) must be a valid URI"));
|
assertThat(e.getMessage(), is("Enterprise Identifier System (eidSystem) must be a valid URI"));
|
||||||
|
@ -42,7 +44,7 @@ public class MdmRuleValidatorTest extends BaseR4Test {
|
||||||
@Test
|
@Test
|
||||||
public void testNonExistentMatchField() throws IOException {
|
public void testNonExistentMatchField() throws IOException {
|
||||||
try {
|
try {
|
||||||
setEmpiRuleJson("bad-rules-missing-name.json");
|
setMdmRuleJson("bad-rules-missing-name.json");
|
||||||
fail();
|
fail();
|
||||||
} catch (ConfigurationException e) {
|
} catch (ConfigurationException e) {
|
||||||
assertThat(e.getMessage(), is("There is no matchField with name foo"));
|
assertThat(e.getMessage(), is("There is no matchField with name foo"));
|
||||||
|
@ -52,7 +54,7 @@ public class MdmRuleValidatorTest extends BaseR4Test {
|
||||||
@Test
|
@Test
|
||||||
public void testSimilarityHasThreshold() throws IOException {
|
public void testSimilarityHasThreshold() throws IOException {
|
||||||
try {
|
try {
|
||||||
setEmpiRuleJson("bad-rules-missing-threshold.json");
|
setMdmRuleJson("bad-rules-missing-threshold.json");
|
||||||
fail();
|
fail();
|
||||||
} catch (ConfigurationException e) {
|
} catch (ConfigurationException e) {
|
||||||
assertThat(e.getMessage(), is("MatchField given-name similarity COSINE requires a matchThreshold"));
|
assertThat(e.getMessage(), is("MatchField given-name similarity COSINE requires a matchThreshold"));
|
||||||
|
@ -62,7 +64,7 @@ public class MdmRuleValidatorTest extends BaseR4Test {
|
||||||
@Test
|
@Test
|
||||||
public void testMatcherBadPath() throws IOException {
|
public void testMatcherBadPath() throws IOException {
|
||||||
try {
|
try {
|
||||||
setEmpiRuleJson("bad-rules-bad-path.json");
|
setMdmRuleJson("bad-rules-bad-path.json");
|
||||||
fail();
|
fail();
|
||||||
} catch (ConfigurationException e) {
|
} catch (ConfigurationException e) {
|
||||||
assertThat(e.getMessage(), startsWith("MatchField given-name resourceType Patient has invalid path 'name.first'. Unknown child name 'first' in element HumanName"));
|
assertThat(e.getMessage(), startsWith("MatchField given-name resourceType Patient has invalid path 'name.first'. Unknown child name 'first' in element HumanName"));
|
||||||
|
@ -72,7 +74,7 @@ public class MdmRuleValidatorTest extends BaseR4Test {
|
||||||
@Test
|
@Test
|
||||||
public void testMatcherBadSearchParam() throws IOException {
|
public void testMatcherBadSearchParam() throws IOException {
|
||||||
try {
|
try {
|
||||||
setEmpiRuleJson("bad-rules-bad-searchparam.json");
|
setMdmRuleJson("bad-rules-bad-searchparam.json");
|
||||||
fail();
|
fail();
|
||||||
} catch (ConfigurationException e) {
|
} catch (ConfigurationException e) {
|
||||||
assertThat(e.getMessage(), startsWith("Error in candidateSearchParams: Patient does not have a search parameter called 'foo'"));
|
assertThat(e.getMessage(), startsWith("Error in candidateSearchParams: Patient does not have a search parameter called 'foo'"));
|
||||||
|
@ -82,7 +84,7 @@ public class MdmRuleValidatorTest extends BaseR4Test {
|
||||||
@Test
|
@Test
|
||||||
public void testMatcherBadFilter() throws IOException {
|
public void testMatcherBadFilter() throws IOException {
|
||||||
try {
|
try {
|
||||||
setEmpiRuleJson("bad-rules-bad-filter.json");
|
setMdmRuleJson("bad-rules-bad-filter.json");
|
||||||
fail();
|
fail();
|
||||||
} catch (ConfigurationException e) {
|
} catch (ConfigurationException e) {
|
||||||
assertThat(e.getMessage(), startsWith("Error in candidateFilterSearchParams: Patient does not have a search parameter called 'foo'"));
|
assertThat(e.getMessage(), startsWith("Error in candidateFilterSearchParams: Patient does not have a search parameter called 'foo'"));
|
||||||
|
@ -92,7 +94,7 @@ public class MdmRuleValidatorTest extends BaseR4Test {
|
||||||
@Test
|
@Test
|
||||||
public void testInvalidMdmType() throws IOException {
|
public void testInvalidMdmType() throws IOException {
|
||||||
try {
|
try {
|
||||||
setEmpiRuleJson("bad-rules-missing-mdm-types.json");
|
setMdmRuleJson("bad-rules-missing-mdm-types.json");
|
||||||
fail();
|
fail();
|
||||||
} catch (ConfigurationException e) {
|
} catch (ConfigurationException e) {
|
||||||
assertThat(e.getMessage(), startsWith("mdmTypes must be set to a list of resource types."));
|
assertThat(e.getMessage(), startsWith("mdmTypes must be set to a list of resource types."));
|
||||||
|
@ -102,15 +104,24 @@ public class MdmRuleValidatorTest extends BaseR4Test {
|
||||||
@Test
|
@Test
|
||||||
public void testMatcherduplicateName() throws IOException {
|
public void testMatcherduplicateName() throws IOException {
|
||||||
try {
|
try {
|
||||||
setEmpiRuleJson("bad-rules-duplicate-name.json");
|
setMdmRuleJson("bad-rules-duplicate-name.json");
|
||||||
fail();
|
fail();
|
||||||
} catch (ConfigurationException e) {
|
} catch (ConfigurationException e) {
|
||||||
assertThat(e.getMessage(), startsWith("Two MatchFields have the same name 'foo'"));
|
assertThat(e.getMessage(), startsWith("Two MatchFields have the same name 'foo'"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInvalidPath() throws IOException {
|
||||||
|
try {
|
||||||
|
setMdmRuleJson("bad-rules-invalid-path.json");
|
||||||
|
fail();
|
||||||
|
} catch (ConfigurationException e) {
|
||||||
|
assertThat(e.getMessage(), startsWith("MatchField name-prefix resourceType Organization has invalid path"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void setEmpiRuleJson(String theTheS) throws IOException {
|
private void setMdmRuleJson(String theTheS) throws IOException {
|
||||||
MdmRuleValidator mdmRuleValidator = new MdmRuleValidator(ourFhirContext, mySearchParamRetriever);
|
MdmRuleValidator mdmRuleValidator = new MdmRuleValidator(ourFhirContext, mySearchParamRetriever);
|
||||||
MdmSettings mdmSettings = new MdmSettings(mdmRuleValidator);
|
MdmSettings mdmSettings = new MdmSettings(mdmRuleValidator);
|
||||||
DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
|
DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
|
||||||
|
|
|
@ -34,31 +34,31 @@ public class AssuranceLevelUtilTest {
|
||||||
AssuranceLevelUtil.getAssuranceLevel(NO_MATCH, AUTO);
|
AssuranceLevelUtil.getAssuranceLevel(NO_MATCH, AUTO);
|
||||||
fail();
|
fail();
|
||||||
} catch (InvalidRequestException e) {
|
} catch (InvalidRequestException e) {
|
||||||
assertEquals("An AUTO EMPI Link may not have a match result of NO_MATCH", e.getMessage());
|
assertEquals("An AUTO MDM Link may not have a match result of NO_MATCH", e.getMessage());
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
AssuranceLevelUtil.getAssuranceLevel(POSSIBLE_DUPLICATE, AUTO);
|
AssuranceLevelUtil.getAssuranceLevel(POSSIBLE_DUPLICATE, AUTO);
|
||||||
fail();
|
fail();
|
||||||
} catch (InvalidRequestException e) {
|
} catch (InvalidRequestException e) {
|
||||||
assertEquals("An AUTO EMPI Link may not have a match result of POSSIBLE_DUPLICATE", e.getMessage());
|
assertEquals("An AUTO MDM Link may not have a match result of POSSIBLE_DUPLICATE", e.getMessage());
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
AssuranceLevelUtil.getAssuranceLevel(NO_MATCH, MANUAL);
|
AssuranceLevelUtil.getAssuranceLevel(NO_MATCH, MANUAL);
|
||||||
fail();
|
fail();
|
||||||
} catch (InvalidRequestException e) {
|
} catch (InvalidRequestException e) {
|
||||||
assertEquals("A MANUAL EMPI Link may not have a match result of NO_MATCH", e.getMessage());
|
assertEquals("A MANUAL MDM Link may not have a match result of NO_MATCH", e.getMessage());
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
AssuranceLevelUtil.getAssuranceLevel(POSSIBLE_MATCH, MANUAL);
|
AssuranceLevelUtil.getAssuranceLevel(POSSIBLE_MATCH, MANUAL);
|
||||||
fail();
|
fail();
|
||||||
} catch (InvalidRequestException e) {
|
} catch (InvalidRequestException e) {
|
||||||
assertEquals("A MANUAL EMPI Link may not have a match result of POSSIBLE_MATCH", e.getMessage());
|
assertEquals("A MANUAL MDM Link may not have a match result of POSSIBLE_MATCH", e.getMessage());
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
AssuranceLevelUtil.getAssuranceLevel(POSSIBLE_DUPLICATE, MANUAL);
|
AssuranceLevelUtil.getAssuranceLevel(POSSIBLE_DUPLICATE, MANUAL);
|
||||||
fail();
|
fail();
|
||||||
} catch (InvalidRequestException e) {
|
} catch (InvalidRequestException e) {
|
||||||
assertEquals("A MANUAL EMPI Link may not have a match result of POSSIBLE_DUPLICATE", e.getMessage());
|
assertEquals("A MANUAL MDM Link may not have a match result of POSSIBLE_DUPLICATE", e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"version": "1",
|
||||||
|
"mdmTypes": ["Organization"],
|
||||||
|
"candidateSearchParams": [],
|
||||||
|
"candidateFilterSearchParams": [],
|
||||||
|
"matchFields": [
|
||||||
|
{
|
||||||
|
"name": "name-prefix",
|
||||||
|
"resourceType": "Organization",
|
||||||
|
"resourcePath": "name.prefix",
|
||||||
|
"matcher": {
|
||||||
|
"algorithm": "STRING"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"matchResultMap": {}
|
||||||
|
}
|
|
@ -136,8 +136,8 @@ public abstract class BaseResourceMessage implements IResourceMessage, IModelJso
|
||||||
* Adds a transaction ID to this message. This ID can be used for many purposes. For example, performing tracing
|
* Adds a transaction ID to this message. This ID can be used for many purposes. For example, performing tracing
|
||||||
* across asynchronous hooks, tying data together, or downstream logging purposes.
|
* across asynchronous hooks, tying data together, or downstream logging purposes.
|
||||||
*
|
*
|
||||||
* One current internal implementation uses this field to tie back EMPI processing results (which are asynchronous)
|
* One current internal implementation uses this field to tie back MDM processing results (which are asynchronous)
|
||||||
* to the original transaction log that caused the EMPI processing to occur.
|
* to the original transaction log that caused the MDM processing to occur.
|
||||||
*
|
*
|
||||||
* @param theTransactionId An ID representing a transaction of relevance to this message.
|
* @param theTransactionId An ID representing a transaction of relevance to this message.
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue