theFilterSearchParams, String theResourceType) {
return Collections.unmodifiableList(theFilterSearchParams.stream()
.filter(spFilterJson -> paramIsOnCorrectType(theResourceType, spFilterJson))
.map(this::convertToQueryString)
.collect(Collectors.toList()));
}
- private boolean paramIsOnCorrectType(String theResourceType, MdmFilterSearchParamJson spFilterJson) {
+ private boolean paramIsOnCorrectType(String theResourceType, EmpiFilterSearchParamJson spFilterJson) {
return spFilterJson.getResourceType().equals(theResourceType) || spFilterJson.getResourceType().equalsIgnoreCase(ALL_RESOURCE_SEARCH_PARAM_TYPE);
}
- private String convertToQueryString(MdmFilterSearchParamJson theSpFilterJson) {
+ private String convertToQueryString(EmpiFilterSearchParamJson theSpFilterJson) {
String qualifier = theSpFilterJson.getTokenParamModifierAsString();
return theSpFilterJson.getSearchParam() + qualifier + "=" + theSpFilterJson.getFixedValue();
}
diff --git a/hapi-fhir-jpaserver-empi/src/main/java/ca/uhn/fhir/jpa/empi/svc/candidate/EmpiPersonFindingSvc.java b/hapi-fhir-jpaserver-empi/src/main/java/ca/uhn/fhir/jpa/empi/svc/candidate/EmpiPersonFindingSvc.java
new file mode 100644
index 00000000000..b106496be6f
--- /dev/null
+++ b/hapi-fhir-jpaserver-empi/src/main/java/ca/uhn/fhir/jpa/empi/svc/candidate/EmpiPersonFindingSvc.java
@@ -0,0 +1,79 @@
+package ca.uhn.fhir.jpa.empi.svc.candidate;
+
+/*-
+ * #%L
+ * HAPI FHIR JPA Server - Enterprise Master Patient Index
+ * %%
+ * Copyright (C) 2014 - 2020 University Health Network
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+
+import ca.uhn.fhir.empi.log.Logs;
+import ca.uhn.fhir.jpa.empi.svc.EmpiResourceDaoSvc;
+import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
+import org.hl7.fhir.instance.model.api.IAnyResource;
+import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class EmpiPersonFindingSvc {
+ private static final Logger ourLog = Logs.getEmpiTroubleshootingLog();
+
+ @Autowired
+ private EmpiResourceDaoSvc myEmpiResourceDaoSvc;
+
+ @Autowired
+ private FindCandidateByEidSvc myFindCandidateByEidSvc;
+ @Autowired
+ private FindCandidateByLinkSvc myFindCandidateByLinkSvc;
+ @Autowired
+ private FindCandidateByScoreSvc myFindCandidateByScoreSvc;
+
+ /**
+ * Given an incoming IBaseResource, limited to Patient/Practitioner, return a list of {@link MatchedPersonCandidate}
+ * indicating possible candidates for a matching Person. Uses several separate methods for finding candidates:
+ *
+ * 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.
+ * 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.
+ * 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
+ * field matchers.
+ *
+ * @param theResource the {@link IBaseResource} we are attempting to find matching candidate Persons for.
+ * @return A list of {@link MatchedPersonCandidate} indicating all potential Person matches.
+ */
+ public CandidateList findPersonCandidates(IAnyResource theResource) {
+ CandidateList matchedPersonCandidates = myFindCandidateByEidSvc.findCandidates(theResource);
+
+ if (matchedPersonCandidates.isEmpty()) {
+ matchedPersonCandidates = myFindCandidateByLinkSvc.findCandidates(theResource);
+ }
+ if (matchedPersonCandidates.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
+ //matches by following EMPI rules.
+
+ matchedPersonCandidates = myFindCandidateByScoreSvc.findCandidates(theResource);
+ }
+ return matchedPersonCandidates;
+ }
+
+ public IAnyResource getPersonFromMatchedPersonCandidate(MatchedPersonCandidate theMatchedPersonCandidate) {
+ ResourcePersistentId personPid = theMatchedPersonCandidate.getCandidatePersonPid();
+ return myEmpiResourceDaoSvc.readPersonByPid(personPid);
+ }
+}
diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/FindCandidateByEidSvc.java b/hapi-fhir-jpaserver-empi/src/main/java/ca/uhn/fhir/jpa/empi/svc/candidate/FindCandidateByEidSvc.java
similarity index 55%
rename from hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/FindCandidateByEidSvc.java
rename to hapi-fhir-jpaserver-empi/src/main/java/ca/uhn/fhir/jpa/empi/svc/candidate/FindCandidateByEidSvc.java
index b599e393c45..4581ba4641b 100644
--- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/FindCandidateByEidSvc.java
+++ b/hapi-fhir-jpaserver-empi/src/main/java/ca/uhn/fhir/jpa/empi/svc/candidate/FindCandidateByEidSvc.java
@@ -1,8 +1,8 @@
-package ca.uhn.fhir.jpa.mdm.svc.candidate;
+package ca.uhn.fhir.jpa.empi.svc.candidate;
/*-
* #%L
- * HAPI FHIR JPA Server - Master Data Management
+ * HAPI FHIR JPA Server - Enterprise Master Patient Index
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
@@ -20,11 +20,11 @@ package ca.uhn.fhir.jpa.mdm.svc.candidate;
* #L%
*/
-import ca.uhn.fhir.mdm.api.MdmMatchOutcome;
-import ca.uhn.fhir.mdm.log.Logs;
-import ca.uhn.fhir.mdm.model.CanonicalEID;
-import ca.uhn.fhir.mdm.util.EIDHelper;
-import ca.uhn.fhir.jpa.mdm.svc.MdmResourceDaoSvc;
+import ca.uhn.fhir.empi.api.EmpiMatchOutcome;
+import ca.uhn.fhir.empi.log.Logs;
+import ca.uhn.fhir.empi.model.CanonicalEID;
+import ca.uhn.fhir.empi.util.EIDHelper;
+import ca.uhn.fhir.jpa.empi.svc.EmpiResourceDaoSvc;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.slf4j.Logger;
@@ -37,26 +37,25 @@ import java.util.Optional;
@Service
public class FindCandidateByEidSvc extends BaseCandidateFinder {
-
- private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
+ private static final Logger ourLog = Logs.getEmpiTroubleshootingLog();
@Autowired
private EIDHelper myEIDHelper;
@Autowired
- private MdmResourceDaoSvc myMdmResourceDaoSvc;
+ private EmpiResourceDaoSvc myEmpiResourceDaoSvc;
- protected List findMatchGoldenResourceCandidates(IAnyResource theBaseResource) {
- List retval = new ArrayList<>();
+ protected List findMatchPersonCandidates(IAnyResource theBaseResource) {
+ List retval = new ArrayList<>();
List eidFromResource = myEIDHelper.getExternalEid(theBaseResource);
if (!eidFromResource.isEmpty()) {
for (CanonicalEID eid : eidFromResource) {
- Optional oFoundGoldenResource = myMdmResourceDaoSvc.searchGoldenResourceByEID(eid.getValue(), theBaseResource.getIdElement().getResourceType());
- if (oFoundGoldenResource.isPresent()) {
- IAnyResource foundGoldenResource = oFoundGoldenResource.get();
- Long pidOrNull = myIdHelperService.getPidOrNull(foundGoldenResource);
- MatchedGoldenResourceCandidate mpc = new MatchedGoldenResourceCandidate(new ResourcePersistentId(pidOrNull), MdmMatchOutcome.EID_MATCH);
- ourLog.debug("Matched {} by EID {}", foundGoldenResource.getIdElement(), eid);
+ Optional oFoundPerson = myEmpiResourceDaoSvc.searchPersonByEid(eid.getValue());
+ if (oFoundPerson.isPresent()) {
+ IAnyResource foundPerson = oFoundPerson.get();
+ Long pidOrNull = myIdHelperService.getPidOrNull(foundPerson);
+ MatchedPersonCandidate mpc = new MatchedPersonCandidate(new ResourcePersistentId(pidOrNull), EmpiMatchOutcome.EID_MATCH);
+ ourLog.debug("Matched {} by EID {}", foundPerson.getIdElement(), eid);
retval.add(mpc);
}
}
diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/FindCandidateByLinkSvc.java b/hapi-fhir-jpaserver-empi/src/main/java/ca/uhn/fhir/jpa/empi/svc/candidate/FindCandidateByLinkSvc.java
similarity index 60%
rename from hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/FindCandidateByLinkSvc.java
rename to hapi-fhir-jpaserver-empi/src/main/java/ca/uhn/fhir/jpa/empi/svc/candidate/FindCandidateByLinkSvc.java
index 6eb8ba5e7cb..4de065c0708 100644
--- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/FindCandidateByLinkSvc.java
+++ b/hapi-fhir-jpaserver-empi/src/main/java/ca/uhn/fhir/jpa/empi/svc/candidate/FindCandidateByLinkSvc.java
@@ -1,8 +1,8 @@
-package ca.uhn.fhir.jpa.mdm.svc.candidate;
+package ca.uhn.fhir.jpa.empi.svc.candidate;
/*-
* #%L
- * HAPI FHIR JPA Server - Master Data Management
+ * HAPI FHIR JPA Server - Enterprise Master Patient Index
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
@@ -20,8 +20,8 @@ package ca.uhn.fhir.jpa.mdm.svc.candidate;
* #L%
*/
-import ca.uhn.fhir.mdm.log.Logs;
-import ca.uhn.fhir.jpa.entity.MdmLink;
+import ca.uhn.fhir.empi.log.Logs;
+import ca.uhn.fhir.jpa.entity.EmpiLink;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.slf4j.Logger;
@@ -33,25 +33,25 @@ import java.util.Optional;
@Service
public class FindCandidateByLinkSvc extends BaseCandidateFinder {
- private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
+ private static final Logger ourLog = Logs.getEmpiTroubleshootingLog();
/**
- * Attempt to find a currently matching Golden Resource, based on the presence of an {@link MdmLink} entity.
+ * Attempt to find a currently matching Person, based on the presence of an {@link EmpiLink} entity.
*
- * @param theTarget the {@link IAnyResource} that we want to find candidate Golden Resources for.
- * @return an Optional list of {@link MatchedGoldenResourceCandidate} indicating matches.
+ * @param theTarget the {@link IAnyResource} that we want to find candidate Persons for.
+ * @return an Optional list of {@link MatchedPersonCandidate} indicating matches.
*/
@Override
- protected List findMatchGoldenResourceCandidates(IAnyResource theTarget) {
- List retval = new ArrayList<>();
+ protected List findMatchPersonCandidates(IAnyResource theTarget) {
+ List retval = new ArrayList<>();
Long targetPid = myIdHelperService.getPidOrNull(theTarget);
if (targetPid != null) {
- Optional oLink = myMdmLinkDaoSvc.getMatchedLinkForSourcePid(targetPid);
+ Optional oLink = myEmpiLinkDaoSvc.getMatchedLinkForTargetPid(targetPid);
if (oLink.isPresent()) {
- ResourcePersistentId goldenResourcePid = new ResourcePersistentId(oLink.get().getGoldenResourcePid());
+ ResourcePersistentId personPid = new ResourcePersistentId(oLink.get().getPersonPid());
ourLog.debug("Resource previously linked. Using existing link.");
- retval.add(new MatchedGoldenResourceCandidate(goldenResourcePid, oLink.get()));
+ retval.add(new MatchedPersonCandidate(personPid, oLink.get()));
}
}
return retval;
diff --git a/hapi-fhir-jpaserver-empi/src/main/java/ca/uhn/fhir/jpa/empi/svc/candidate/FindCandidateByScoreSvc.java b/hapi-fhir-jpaserver-empi/src/main/java/ca/uhn/fhir/jpa/empi/svc/candidate/FindCandidateByScoreSvc.java
new file mode 100644
index 00000000000..9391e4182de
--- /dev/null
+++ b/hapi-fhir-jpaserver-empi/src/main/java/ca/uhn/fhir/jpa/empi/svc/candidate/FindCandidateByScoreSvc.java
@@ -0,0 +1,110 @@
+package ca.uhn.fhir.jpa.empi.svc.candidate;
+
+/*-
+ * #%L
+ * HAPI FHIR JPA Server - Enterprise Master Patient Index
+ * %%
+ * Copyright (C) 2014 - 2020 University Health Network
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
+import ca.uhn.fhir.empi.api.IEmpiMatchFinderSvc;
+import ca.uhn.fhir.empi.api.MatchedTarget;
+import ca.uhn.fhir.empi.log.Logs;
+import ca.uhn.fhir.jpa.dao.index.IdHelperService;
+import ca.uhn.fhir.jpa.empi.dao.EmpiLinkDaoSvc;
+import ca.uhn.fhir.jpa.entity.EmpiLink;
+import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
+import org.hl7.fhir.instance.model.api.IAnyResource;
+import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+@Service
+public class FindCandidateByScoreSvc extends BaseCandidateFinder {
+ private static final Logger ourLog = Logs.getEmpiTroubleshootingLog();
+
+ @Autowired
+ private FhirContext myFhirContext;
+ @Autowired
+ IdHelperService myIdHelperService;
+ @Autowired
+ private EmpiLinkDaoSvc myEmpiLinkDaoSvc;
+ @Autowired
+ private IEmpiMatchFinderSvc myEmpiMatchFinderSvc;
+
+ /**
+ * Attempt to find matching Persons by resolving them from similar Matching target resources, where target resource
+ * can be either Patient or Practitioner. Runs EMPI logic over the existing Patient/Practitioners, then finds their
+ * entries in the EmpiLink table, and returns all the matches found therein.
+ *
+ * @param theTarget the {@link IBaseResource} which we want to find candidate Persons for.
+ * @return an Optional list of {@link MatchedPersonCandidate} indicating matches.
+ */
+ @Override
+ protected List findMatchPersonCandidates(IAnyResource theTarget) {
+ List retval = new ArrayList<>();
+
+ List personPidsToExclude = getNoMatchPersonPids(theTarget);
+ List matchedCandidates = myEmpiMatchFinderSvc.getMatchedTargets(myFhirContext.getResourceType(theTarget), theTarget);
+
+ //Convert all possible match targets to their equivalent Persons by looking up in the EmpiLink table,
+ //while ensuring that the matches aren't in our NO_MATCH list.
+ // The data flow is as follows ->
+ // MatchedTargetCandidate -> Person -> EmpiLink -> MatchedPersonCandidate
+ matchedCandidates = matchedCandidates.stream().filter(mc -> mc.isMatch() || mc.isPossibleMatch()).collect(Collectors.toList());
+ for (MatchedTarget match : matchedCandidates) {
+ Optional optMatchEmpiLink = myEmpiLinkDaoSvc.getMatchedLinkForTargetPid(myIdHelperService.getPidOrNull(match.getTarget()));
+ if (!optMatchEmpiLink.isPresent()) {
+ continue;
+ }
+
+ EmpiLink matchEmpiLink = optMatchEmpiLink.get();
+ if (personPidsToExclude.contains(matchEmpiLink.getPersonPid())) {
+ ourLog.info("Skipping EMPI on candidate person with PID {} due to manual NO_MATCH", matchEmpiLink.getPersonPid());
+ continue;
+ }
+
+ MatchedPersonCandidate candidate = new MatchedPersonCandidate(getResourcePersistentId(matchEmpiLink.getPersonPid()), match.getMatchResult());
+ retval.add(candidate);
+ }
+ return retval;
+ }
+
+ private List getNoMatchPersonPids(IBaseResource theBaseResource) {
+ Long targetPid = myIdHelperService.getPidOrNull(theBaseResource);
+ return myEmpiLinkDaoSvc.getEmpiLinksByTargetPidAndMatchResult(targetPid, EmpiMatchResultEnum.NO_MATCH)
+ .stream()
+ .map(EmpiLink::getPersonPid)
+ .collect(Collectors.toList());
+ }
+
+ private ResourcePersistentId getResourcePersistentId(Long thePersonPid) {
+ return new ResourcePersistentId(thePersonPid);
+ }
+
+ @Override
+ protected CandidateStrategyEnum getStrategy() {
+ return CandidateStrategyEnum.SCORE;
+ }
+}
diff --git a/hapi-fhir-jpaserver-empi/src/main/java/ca/uhn/fhir/jpa/empi/svc/candidate/MatchedPersonCandidate.java b/hapi-fhir-jpaserver-empi/src/main/java/ca/uhn/fhir/jpa/empi/svc/candidate/MatchedPersonCandidate.java
new file mode 100644
index 00000000000..dadd064633c
--- /dev/null
+++ b/hapi-fhir-jpaserver-empi/src/main/java/ca/uhn/fhir/jpa/empi/svc/candidate/MatchedPersonCandidate.java
@@ -0,0 +1,52 @@
+package ca.uhn.fhir.jpa.empi.svc.candidate;
+
+/*-
+ * #%L
+ * HAPI FHIR JPA Server - Enterprise Master Patient Index
+ * %%
+ * Copyright (C) 2014 - 2020 University Health Network
+ * %%
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * #L%
+ */
+
+import ca.uhn.fhir.empi.api.EmpiMatchOutcome;
+import ca.uhn.fhir.jpa.entity.EmpiLink;
+import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
+
+public class MatchedPersonCandidate {
+ private final ResourcePersistentId myCandidatePersonPid;
+ private final EmpiMatchOutcome myEmpiMatchOutcome;
+
+ public MatchedPersonCandidate(ResourcePersistentId theCandidate, EmpiMatchOutcome theEmpiMatchOutcome) {
+ myCandidatePersonPid = theCandidate;
+ myEmpiMatchOutcome = theEmpiMatchOutcome;
+ }
+
+ public MatchedPersonCandidate(ResourcePersistentId thePersonPid, EmpiLink theEmpiLink) {
+ myCandidatePersonPid = thePersonPid;
+ myEmpiMatchOutcome = new EmpiMatchOutcome(theEmpiLink.getVector(), theEmpiLink.getScore()).setMatchResultEnum(theEmpiLink.getMatchResult());
+ }
+
+ public ResourcePersistentId getCandidatePersonPid() {
+ return myCandidatePersonPid;
+ }
+
+ public EmpiMatchOutcome getMatchResult() {
+ return myEmpiMatchOutcome;
+ }
+
+ public boolean isMatch() {
+ return myEmpiMatchOutcome.isMatch();
+ }
+}
diff --git a/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/BaseEmpiR4Test.java b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/BaseEmpiR4Test.java
new file mode 100644
index 00000000000..b71dc7441b9
--- /dev/null
+++ b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/BaseEmpiR4Test.java
@@ -0,0 +1,432 @@
+package ca.uhn.fhir.jpa.empi;
+
+import ca.uhn.fhir.context.FhirContext;
+import ca.uhn.fhir.empi.api.EmpiConstants;
+import ca.uhn.fhir.empi.api.EmpiLinkSourceEnum;
+import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
+import ca.uhn.fhir.empi.api.IEmpiSettings;
+import ca.uhn.fhir.empi.model.EmpiTransactionContext;
+import ca.uhn.fhir.empi.rules.svc.EmpiResourceMatcherSvc;
+import ca.uhn.fhir.empi.util.EIDHelper;
+import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
+import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
+import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome;
+import ca.uhn.fhir.jpa.dao.data.IEmpiLinkDao;
+import ca.uhn.fhir.jpa.dao.index.IdHelperService;
+import ca.uhn.fhir.jpa.empi.config.EmpiConsumerConfig;
+import ca.uhn.fhir.jpa.empi.config.EmpiSearchParameterLoader;
+import ca.uhn.fhir.jpa.empi.config.EmpiSubmitterConfig;
+import ca.uhn.fhir.jpa.empi.config.TestEmpiConfigR4;
+import ca.uhn.fhir.jpa.empi.dao.EmpiLinkDaoSvc;
+import ca.uhn.fhir.jpa.empi.matcher.IsLinkedTo;
+import ca.uhn.fhir.jpa.empi.matcher.IsMatchedToAPerson;
+import ca.uhn.fhir.jpa.empi.matcher.IsPossibleDuplicateOf;
+import ca.uhn.fhir.jpa.empi.matcher.IsPossibleLinkedTo;
+import ca.uhn.fhir.jpa.empi.matcher.IsPossibleMatchWith;
+import ca.uhn.fhir.jpa.empi.matcher.IsSamePersonAs;
+import ca.uhn.fhir.jpa.empi.svc.EmpiMatchLinkSvc;
+import ca.uhn.fhir.jpa.entity.EmpiLink;
+import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
+import ca.uhn.fhir.jpa.searchparam.registry.SearchParamRegistryImpl;
+import ca.uhn.fhir.jpa.subscription.match.config.SubscriptionProcessorConfig;
+import ca.uhn.fhir.jpa.test.BaseJpaR4Test;
+import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
+import ca.uhn.fhir.rest.api.server.IBundleProvider;
+import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
+import ca.uhn.fhir.rest.param.TokenParam;
+import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
+import org.hamcrest.Matcher;
+import org.hl7.fhir.instance.model.api.IAnyResource;
+import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.hl7.fhir.r4.model.ContactPoint;
+import org.hl7.fhir.r4.model.DateType;
+import org.hl7.fhir.r4.model.Patient;
+import org.hl7.fhir.r4.model.Person;
+import org.hl7.fhir.r4.model.Practitioner;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+import javax.annotation.Nonnull;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Function;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.slf4j.LoggerFactory.getLogger;
+
+@ExtendWith(SpringExtension.class)
+@ContextConfiguration(classes = {EmpiSubmitterConfig.class, EmpiConsumerConfig.class, TestEmpiConfigR4.class, SubscriptionProcessorConfig.class})
+abstract public class BaseEmpiR4Test extends BaseJpaR4Test {
+ private static final Logger ourLog = getLogger(BaseEmpiR4Test.class);
+
+ public static final String NAME_GIVEN_JANE = "Jane";
+ public static final String NAME_GIVEN_PAUL = "Paul";
+ public static final String TEST_NAME_FAMILY = "Doe";
+ protected static final String TEST_ID_SYSTEM = "http://a.tv/";
+ protected static final String JANE_ID = "ID.JANE.123";
+ protected static final String PAUL_ID = "ID.PAUL.456";
+ private static final ContactPoint TEST_TELECOM = new ContactPoint()
+ .setSystem(ContactPoint.ContactPointSystem.PHONE)
+ .setValue("555-555-5555");
+ private static final String NAME_GIVEN_FRANK = "Frank";
+ protected static final String FRANK_ID = "ID.FRANK.789";
+
+ @Autowired
+ protected FhirContext myFhirContext;
+ @Autowired
+ protected IFhirResourceDao myPersonDao;
+ @Autowired
+ protected IFhirResourceDao myPatientDao;
+ @Autowired
+ protected IFhirResourceDao myPractitionerDao;
+ @Autowired
+ protected EmpiResourceMatcherSvc myEmpiResourceMatcherSvc;
+ @Autowired
+ protected IEmpiLinkDao myEmpiLinkDao;
+ @Autowired
+ protected EmpiLinkDaoSvc myEmpiLinkDaoSvc;
+ @Autowired
+ protected IdHelperService myIdHelperService;
+ @Autowired
+ protected IEmpiSettings myEmpiConfig;
+ @Autowired
+ protected EmpiMatchLinkSvc myEmpiMatchLinkSvc;
+ @Autowired
+ protected EIDHelper myEIDHelper;
+ @Autowired
+ EmpiSearchParameterLoader myEmpiSearchParameterLoader;
+ @Autowired
+ SearchParamRegistryImpl mySearchParamRegistry;
+ @Autowired
+ private IInterceptorBroadcaster myInterceptorBroadcaster;
+
+ protected ServletRequestDetails myRequestDetails;
+
+ @BeforeEach
+ public void beforeSetRequestDetails() {
+ myRequestDetails = new ServletRequestDetails(myInterceptorBroadcaster);
+ }
+
+ @Override
+ @AfterEach
+ public void after() throws IOException {
+ myEmpiLinkDao.deleteAll();
+ assertEquals(0, myEmpiLinkDao.count());
+ super.after();
+ }
+
+ protected void saveLink(EmpiLink theEmpiLink) {
+ myEmpiLinkDaoSvc.save(theEmpiLink);
+ }
+
+ @Nonnull
+ protected Person createUnmanagedPerson() {
+ return createPerson(new Person(), false);
+ }
+
+ @Nonnull
+ protected Person createPerson() {
+ return createPerson(new Person(), true);
+ }
+
+ @Nonnull
+ protected Patient createPatient() {
+ return createPatient(new Patient());
+ }
+
+ @Nonnull
+ protected Person createPerson(Person thePerson) {
+ return createPerson(thePerson, true);
+ }
+
+ @Nonnull
+ protected Person createPerson(Person thePerson, boolean theEmpiManaged) {
+ if (theEmpiManaged) {
+ thePerson.getMeta().addTag().setSystem(EmpiConstants.SYSTEM_EMPI_MANAGED).setCode(EmpiConstants.CODE_HAPI_EMPI_MANAGED);
+ thePerson.setActive(true);
+ }
+ DaoMethodOutcome outcome = myPersonDao.create(thePerson);
+ Person person = (Person) outcome.getResource();
+ person.setId(outcome.getId());
+ return person;
+ }
+
+ @Nonnull
+ protected Patient createPatient(Patient thePatient) {
+ //Note that since our empi-rules block on active=true, all patients must be active.
+ thePatient.setActive(true);
+ DaoMethodOutcome outcome = myPatientDao.create(thePatient);
+ Patient patient = (Patient) outcome.getResource();
+ patient.setId(outcome.getId());
+ return patient;
+ }
+
+ @Nonnull
+ protected Practitioner createPractitioner(Practitioner thePractitioner) {
+ //Note that since our empi-rules block on active=true, all patients must be active.
+ thePractitioner.setActive(true);
+ DaoMethodOutcome daoMethodOutcome = myPractitionerDao.create(thePractitioner);
+ thePractitioner.setId(daoMethodOutcome.getId());
+ return thePractitioner;
+ }
+
+ @Nonnull
+ protected Patient buildPatientWithNameAndId(String theGivenName, String theId) {
+ return buildPatientWithNameIdAndBirthday(theGivenName, theId, null);
+ }
+
+ @Nonnull
+ protected Practitioner buildPractitionerWithNameAndId(String theGivenName, String theId) {
+ return buildPractitionerWithNameIdAndBirthday(theGivenName, theId, null);
+ }
+
+ @Nonnull
+ protected Person buildPersonWithNameAndId(String theGivenName, String theId) {
+ return buildPersonWithNameIdAndBirthday(theGivenName, theId, null);
+ }
+
+
+ @Nonnull
+ protected Patient buildPatientWithNameIdAndBirthday(String theGivenName, String theId, Date theBirthday) {
+ Patient patient = new Patient();
+ patient.getNameFirstRep().addGiven(theGivenName);
+ patient.getNameFirstRep().setFamily(TEST_NAME_FAMILY);
+ patient.addIdentifier().setSystem(TEST_ID_SYSTEM).setValue(theId);
+ patient.setBirthDate(theBirthday);
+ patient.setTelecom(Collections.singletonList(TEST_TELECOM));
+ DateType dateType = new DateType(theBirthday);
+ dateType.setPrecision(TemporalPrecisionEnum.DAY);
+ patient.setBirthDateElement(dateType);
+ return patient;
+ }
+
+ @Nonnull
+ protected Practitioner buildPractitionerWithNameIdAndBirthday(String theGivenName, String theId, Date theBirthday) {
+ Practitioner practitioner = new Practitioner();
+ practitioner.addName().addGiven(theGivenName);
+ practitioner.addName().setFamily(TEST_NAME_FAMILY);
+ practitioner.addIdentifier().setSystem(TEST_ID_SYSTEM).setValue(theId);
+ practitioner.setBirthDate(theBirthday);
+ practitioner.setTelecom(Collections.singletonList(TEST_TELECOM));
+ DateType dateType = new DateType(theBirthday);
+ dateType.setPrecision(TemporalPrecisionEnum.DAY);
+ practitioner.setBirthDateElement(dateType);
+ return practitioner;
+ }
+
+ @Nonnull
+ protected Person buildPersonWithNameIdAndBirthday(String theGivenName, String theId, Date theBirthday) {
+ Person person = new Person();
+ person.addName().addGiven(theGivenName);
+ person.addName().setFamily(TEST_NAME_FAMILY);
+ person.addIdentifier().setSystem(TEST_ID_SYSTEM).setValue(theId);
+ person.setBirthDate(theBirthday);
+ DateType dateType = new DateType(theBirthday);
+ dateType.setPrecision(TemporalPrecisionEnum.DAY);
+ person.setBirthDateElement(dateType);
+ return person;
+ }
+
+ @Nonnull
+ protected Patient buildJanePatient() {
+ return buildPatientWithNameAndId(NAME_GIVEN_JANE, JANE_ID);
+ }
+
+ @Nonnull
+ protected Practitioner buildJanePractitioner() {
+ return buildPractitionerWithNameAndId(NAME_GIVEN_JANE, JANE_ID);
+ }
+
+ @Nonnull
+ protected Person buildJanePerson() {
+ return buildPersonWithNameAndId(NAME_GIVEN_JANE, JANE_ID);
+ }
+
+ @Nonnull
+ protected Patient buildPaulPatient() {
+ return buildPatientWithNameAndId(NAME_GIVEN_PAUL, PAUL_ID);
+ }
+
+ @Nonnull
+ protected Patient buildFrankPatient() {
+ return buildPatientWithNameAndId(NAME_GIVEN_FRANK, FRANK_ID);
+ }
+
+ @Nonnull
+ protected Patient buildJaneWithBirthday(Date theToday) {
+ return buildPatientWithNameIdAndBirthday(NAME_GIVEN_JANE, JANE_ID, theToday);
+ }
+
+ protected void assertLinkCount(long theExpectedCount) {
+ assertEquals(theExpectedCount, myEmpiLinkDao.count());
+ }
+
+ protected Person getPersonFromTarget(IAnyResource theBaseResource) {
+ Optional matchedLinkForTargetPid = myEmpiLinkDaoSvc.getMatchedLinkForTargetPid(myIdHelperService.getPidOrNull(theBaseResource));
+ if (matchedLinkForTargetPid.isPresent()) {
+ Long personPid = matchedLinkForTargetPid.get().getPersonPid();
+ return (Person) myPersonDao.readByPid(new ResourcePersistentId(personPid));
+ } else {
+ return null;
+ }
+ }
+
+ protected Person getPersonFromEmpiLink(EmpiLink theEmpiLink) {
+ return (Person) myPersonDao.readByPid(new ResourcePersistentId(theEmpiLink.getPersonPid()));
+ }
+
+ protected Patient addExternalEID(Patient thePatient, String theEID) {
+ thePatient.addIdentifier().setSystem(myEmpiConfig.getEmpiRules().getEnterpriseEIDSystem()).setValue(theEID);
+ return thePatient;
+ }
+
+ protected Person addExternalEID(Person thePerson, String theEID) {
+ thePerson.addIdentifier().setSystem(myEmpiConfig.getEmpiRules().getEnterpriseEIDSystem()).setValue(theEID);
+ return thePerson;
+ }
+
+ protected Patient clearExternalEIDs(Patient thePatient) {
+ thePatient.getIdentifier().removeIf(theIdentifier -> theIdentifier.getSystem().equalsIgnoreCase(myEmpiConfig.getEmpiRules().getEnterpriseEIDSystem()));
+ return thePatient;
+ }
+
+ protected Patient createPatientAndUpdateLinks(Patient thePatient) {
+ thePatient = createPatient(thePatient);
+ myEmpiMatchLinkSvc.updateEmpiLinksForEmpiTarget(thePatient, createContextForCreate());
+ return thePatient;
+ }
+
+ protected EmpiTransactionContext createContextForCreate() {
+ EmpiTransactionContext ctx = new EmpiTransactionContext();
+ ctx.setRestOperation(EmpiTransactionContext.OperationType.CREATE_RESOURCE);
+ ctx.setTransactionLogMessages(null);
+ return ctx;
+ }
+
+ protected EmpiTransactionContext createContextForUpdate() {
+ EmpiTransactionContext ctx = new EmpiTransactionContext();
+ ctx.setRestOperation(EmpiTransactionContext.OperationType.UPDATE_RESOURCE);
+ ctx.setTransactionLogMessages(null);
+ return ctx;
+ }
+
+ protected Patient updatePatientAndUpdateLinks(Patient thePatient) {
+ thePatient = (Patient) myPatientDao.update(thePatient).getResource();
+ myEmpiMatchLinkSvc.updateEmpiLinksForEmpiTarget(thePatient, createContextForUpdate());
+ return thePatient;
+ }
+
+ protected Practitioner createPractitionerAndUpdateLinks(Practitioner thePractitioner) {
+ thePractitioner.setActive(true);
+ DaoMethodOutcome daoMethodOutcome = myPractitionerDao.create(thePractitioner);
+ thePractitioner.setId(daoMethodOutcome.getId());
+ myEmpiMatchLinkSvc.updateEmpiLinksForEmpiTarget(thePractitioner, createContextForCreate());
+ return thePractitioner;
+ }
+
+ protected Matcher samePersonAs(IAnyResource... theBaseResource) {
+ return IsSamePersonAs.samePersonAs(myIdHelperService, myEmpiLinkDaoSvc, theBaseResource);
+ }
+
+ protected Matcher linkedTo(IAnyResource... theBaseResource) {
+ return IsLinkedTo.linkedTo(myIdHelperService, myEmpiLinkDaoSvc, theBaseResource);
+ }
+
+ protected Matcher possibleLinkedTo(IAnyResource... theBaseResource) {
+ return IsPossibleLinkedTo.possibleLinkedTo(myIdHelperService, myEmpiLinkDaoSvc, theBaseResource);
+ }
+
+ protected Matcher possibleMatchWith(IAnyResource... theBaseResource) {
+ return IsPossibleMatchWith.possibleMatchWith(myIdHelperService, myEmpiLinkDaoSvc, theBaseResource);
+ }
+
+ protected Matcher possibleDuplicateOf(IAnyResource... theBaseResource) {
+ return IsPossibleDuplicateOf.possibleDuplicateOf(myIdHelperService, myEmpiLinkDaoSvc, theBaseResource);
+ }
+
+ protected Matcher matchedToAPerson() {
+ return IsMatchedToAPerson.matchedToAPerson(myIdHelperService, myEmpiLinkDaoSvc);
+ }
+
+ protected Person getOnlyActivePerson() {
+ List resources = getAllActivePersons();
+ assertEquals(1, resources.size());
+ return (Person) resources.get(0);
+ }
+
+ @Nonnull
+ protected List getAllActivePersons() {
+ return getAllPersons(true);
+ }
+
+ @Nonnull
+ protected List getAllPersons() {
+ return getAllPersons(false);
+ }
+
+ @Nonnull
+ private List getAllPersons(boolean theOnlyActive) {
+ SearchParameterMap map = new SearchParameterMap();
+ map.setLoadSynchronous(true);
+ if (theOnlyActive) {
+ map.add("active", new TokenParam().setValue("true"));
+ }
+ IBundleProvider bundle = myPersonDao.search(map);
+ return bundle.getResources(0, 999);
+ }
+
+ @Nonnull
+ protected EmpiLink createResourcesAndBuildTestEmpiLink() {
+ Person person = createPerson();
+ Patient patient = createPatient();
+
+ EmpiLink empiLink = myEmpiLinkDaoSvc.newEmpiLink();
+ empiLink.setLinkSource(EmpiLinkSourceEnum.MANUAL);
+ empiLink.setMatchResult(EmpiMatchResultEnum.MATCH);
+ empiLink.setPersonPid(myIdHelperService.getPidOrNull(person));
+ empiLink.setTargetPid(myIdHelperService.getPidOrNull(patient));
+ return empiLink;
+ }
+
+ protected void loadEmpiSearchParameters() {
+ myEmpiSearchParameterLoader.daoUpdateEmpiSearchParameters();
+ mySearchParamRegistry.forceRefresh();
+ }
+
+ protected void logAllLinks() {
+ ourLog.info("Logging all EMPI Links:");
+ List links = myEmpiLinkDao.findAll();
+ for (EmpiLink link : links) {
+ ourLog.info(link.toString());
+ }
+ }
+
+ protected void assertLinksMatchResult(EmpiMatchResultEnum... theExpectedValues) {
+ assertFields(EmpiLink::getMatchResult, theExpectedValues);
+ }
+
+ protected void assertLinksNewPerson(Boolean... theExpectedValues) {
+ assertFields(EmpiLink::getNewPerson, theExpectedValues);
+ }
+
+ protected void assertLinksMatchedByEid(Boolean... theExpectedValues) {
+ assertFields(EmpiLink::getEidMatch, theExpectedValues);
+ }
+
+ private void assertFields(Function theAccessor, T... theExpectedValues) {
+ List links = myEmpiLinkDao.findAll();
+ assertEquals(theExpectedValues.length, links.size());
+ for (int i = 0; i < links.size(); ++i) {
+ assertEquals(theExpectedValues[i], theAccessor.apply(links.get(i)), "Value at index " + i + " was not equal");
+ }
+ }
+
+}
diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/config/BaseTestMdmConfig.java b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/config/BaseTestEmpiConfig.java
similarity index 54%
rename from hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/config/BaseTestMdmConfig.java
rename to hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/config/BaseTestEmpiConfig.java
index 5b02acb4570..2eeebefc5e9 100644
--- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/config/BaseTestMdmConfig.java
+++ b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/config/BaseTestEmpiConfig.java
@@ -1,9 +1,9 @@
-package ca.uhn.fhir.jpa.mdm.config;
+package ca.uhn.fhir.jpa.empi.config;
-import ca.uhn.fhir.mdm.api.IMdmSettings;
-import ca.uhn.fhir.mdm.rules.config.MdmRuleValidator;
-import ca.uhn.fhir.mdm.rules.config.MdmSettings;
-import ca.uhn.fhir.jpa.mdm.helper.MdmLinkHelper;
+import ca.uhn.fhir.empi.api.IEmpiSettings;
+import ca.uhn.fhir.empi.rules.config.EmpiRuleValidator;
+import ca.uhn.fhir.empi.rules.config.EmpiSettings;
+import ca.uhn.fhir.jpa.empi.helper.EmpiLinkHelper;
import com.google.common.base.Charsets;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Value;
@@ -15,20 +15,20 @@ import org.springframework.core.io.Resource;
import java.io.IOException;
@Configuration
-public abstract class BaseTestMdmConfig {
+public abstract class BaseTestEmpiConfig {
- @Value("${mdm.prevent_eid_updates:true}")
+ @Value("${empi.prevent_eid_updates:true}")
boolean myPreventEidUpdates;
- @Value("${mdm.prevent_multiple_eids:true}")
+ @Value("${empi.prevent_multiple_eids:true}")
boolean myPreventMultipleEids;
@Bean
- IMdmSettings mdmSettings(MdmRuleValidator theMdmRuleValidator) throws IOException {
+ IEmpiSettings empiSettings(EmpiRuleValidator theEmpiRuleValidator) throws IOException {
DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
- Resource resource = resourceLoader.getResource("mdm/mdm-rules.json");
+ Resource resource = resourceLoader.getResource("empi/empi-rules.json");
String json = IOUtils.toString(resource.getInputStream(), Charsets.UTF_8);
- return new MdmSettings(theMdmRuleValidator)
+ return new EmpiSettings(theEmpiRuleValidator)
.setEnabled(false)
.setScriptText(json)
.setPreventEidUpdates(myPreventEidUpdates)
@@ -36,7 +36,7 @@ public abstract class BaseTestMdmConfig {
}
@Bean
- MdmLinkHelper mdmLinkHelper() {
- return new MdmLinkHelper();
+ EmpiLinkHelper empiLinkHelper() {
+ return new EmpiLinkHelper();
}
}
diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/config/TestMdmConfigR4.java b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/config/TestEmpiConfigR4.java
similarity index 75%
rename from hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/config/TestMdmConfigR4.java
rename to hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/config/TestEmpiConfigR4.java
index 204db38ffdd..50ae0500ed6 100644
--- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/config/TestMdmConfigR4.java
+++ b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/config/TestEmpiConfigR4.java
@@ -1,9 +1,9 @@
-package ca.uhn.fhir.jpa.mdm.config;
+package ca.uhn.fhir.jpa.empi.config;
import ca.uhn.fhir.jpa.subscription.channel.config.SubscriptionChannelConfig;
import ca.uhn.fhir.jpa.subscription.submit.config.SubscriptionSubmitterConfig;
import org.springframework.context.annotation.Import;
@Import({SubscriptionSubmitterConfig.class, SubscriptionChannelConfig.class})
-public class TestMdmConfigR4 extends BaseTestMdmConfig {
+public class TestEmpiConfigR4 extends BaseTestEmpiConfig {
}
diff --git a/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/dao/EmpiLinkDaoSvcTest.java b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/dao/EmpiLinkDaoSvcTest.java
new file mode 100644
index 00000000000..a2733bc18a3
--- /dev/null
+++ b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/dao/EmpiLinkDaoSvcTest.java
@@ -0,0 +1,55 @@
+package ca.uhn.fhir.jpa.empi.dao;
+
+import ca.uhn.fhir.empi.api.EmpiLinkSourceEnum;
+import ca.uhn.fhir.empi.api.IEmpiSettings;
+import ca.uhn.fhir.empi.rules.json.EmpiRulesJson;
+import ca.uhn.fhir.jpa.empi.BaseEmpiR4Test;
+import ca.uhn.fhir.jpa.entity.EmpiLink;
+import ca.uhn.fhir.jpa.util.TestUtil;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class EmpiLinkDaoSvcTest extends BaseEmpiR4Test {
+ @Autowired
+ EmpiLinkDaoSvc myEmpiLinkDaoSvc;
+ @Autowired
+ IEmpiSettings myEmpiSettings;
+
+ @Test
+ public void testCreate() {
+ EmpiLink empiLink = createResourcesAndBuildTestEmpiLink();
+ assertThat(empiLink.getCreated(), is(nullValue()));
+ assertThat(empiLink.getUpdated(), is(nullValue()));
+ myEmpiLinkDaoSvc.save(empiLink);
+ assertThat(empiLink.getCreated(), is(notNullValue()));
+ assertThat(empiLink.getUpdated(), is(notNullValue()));
+ assertTrue(empiLink.getUpdated().getTime() - empiLink.getCreated().getTime() < 1000);
+ }
+
+ @Test
+ public void testUpdate() {
+ EmpiLink createdLink = myEmpiLinkDaoSvc.save(createResourcesAndBuildTestEmpiLink());
+ assertThat(createdLink.getLinkSource(), is(EmpiLinkSourceEnum.MANUAL));
+ TestUtil.sleepOneClick();
+ createdLink.setLinkSource(EmpiLinkSourceEnum.AUTO);
+ EmpiLink updatedLink = myEmpiLinkDaoSvc.save(createdLink);
+ assertNotEquals(updatedLink.getCreated(), updatedLink.getUpdated());
+ }
+
+ @Test
+ public void testNew() {
+ EmpiLink newLink = myEmpiLinkDaoSvc.newEmpiLink();
+ EmpiRulesJson rules = myEmpiSettings.getEmpiRules();
+ assertEquals("1", rules.getVersion());
+ assertEquals(rules.getVersion(), newLink.getVersion());
+ }
+
+}
diff --git a/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/entity/EmpiEnumTest.java b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/entity/EmpiEnumTest.java
new file mode 100644
index 00000000000..b7518ed65d8
--- /dev/null
+++ b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/entity/EmpiEnumTest.java
@@ -0,0 +1,20 @@
+package ca.uhn.fhir.jpa.empi.entity;
+
+import ca.uhn.fhir.empi.api.EmpiLinkSourceEnum;
+import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class EmpiEnumTest {
+ @Test
+ public void empiEnumOrdinals() {
+ // This test is here to enforce that new values in these enums are always added to the end
+
+ assertEquals(6, EmpiMatchResultEnum.values().length);
+ assertEquals(EmpiMatchResultEnum.REDIRECT, EmpiMatchResultEnum.values()[EmpiMatchResultEnum.values().length - 1]);
+
+ assertEquals(2, EmpiLinkSourceEnum.values().length);
+ assertEquals(EmpiLinkSourceEnum.MANUAL, EmpiLinkSourceEnum.values()[EmpiLinkSourceEnum.values().length - 1]);
+ }
+}
diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/helper/BaseMdmHelper.java b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/helper/BaseEmpiHelper.java
similarity index 73%
rename from hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/helper/BaseMdmHelper.java
rename to hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/helper/BaseEmpiHelper.java
index 1389c4c5ea1..fab4e3c4895 100644
--- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/helper/BaseMdmHelper.java
+++ b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/helper/BaseEmpiHelper.java
@@ -1,11 +1,11 @@
-package ca.uhn.fhir.jpa.mdm.helper;
+package ca.uhn.fhir.jpa.empi.helper;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.interceptor.api.IInterceptorService;
import ca.uhn.fhir.interceptor.api.Pointcut;
-import ca.uhn.fhir.jpa.mdm.broker.MdmQueueConsumerLoader;
-import ca.uhn.fhir.jpa.mdm.config.MdmSubscriptionLoader;
+import ca.uhn.fhir.jpa.empi.broker.EmpiQueueConsumerLoader;
+import ca.uhn.fhir.jpa.empi.config.EmpiSubscriptionLoader;
import ca.uhn.fhir.jpa.subscription.channel.impl.LinkedBlockingChannel;
import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionLoader;
import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionRegistry;
@@ -27,21 +27,21 @@ import static org.mockito.Mockito.when;
/**
* How to use this Rule:
*
- * This rule is to be used whenever you want to have the MdmInterceptor loaded, and be able
- * to execute creates/updates/deletes while being assured that all MDM work has been done before exiting.
+ * This rule is to be used whenever you want to have the EmpiInterceptor loaded, and be able
+ * to execute creates/updates/deletes while being assured that all EMPI work has been done before exiting.
* Provides two types of method:
*
- * 1. doUpdate/doCreate. These methods do not wait for Asynchronous MDM work to be done. Use these when you are expecting
+ * 1. doUpdate/doCreate. These methods do not wait for Asynchronous EMPI work to be done. Use these when you are expecting
* the calls to fail, as those hooks will never be called.
*
- * 2. createWithLatch/updateWithLatch. These methods will await the MDM hooks, which are only triggered post-MDM processing
- * You should use these when you are expecting successful processing of the resource, and need to wait for async MDM linking
+ * 2. createWithLatch/updateWithLatch. These methods will await the EMPI hooks, which are only triggered post-EMPI processing
+ * You should use these when you are expecting successful processing of the resource, and need to wait for async EMPI linking
* work to be done.
*
* Note: all create/update functions take an optional isExternalHttpRequest boolean, to make it appear as though the request's
* origin is an HTTP request.
*/
-public abstract class BaseMdmHelper implements BeforeEachCallback, AfterEachCallback {
+public abstract class BaseEmpiHelper implements BeforeEachCallback, AfterEachCallback {
@Mock
protected ServletRequestDetails myMockSrd;
@Mock
@@ -50,15 +50,15 @@ public abstract class BaseMdmHelper implements BeforeEachCallback, AfterEachCall
protected RestfulServer myMockRestfulServer;
@Mock
protected FhirContext myMockFhirContext;
- protected PointcutLatch myAfterMdmLatch = new PointcutLatch(Pointcut.MDM_AFTER_PERSISTED_RESOURCE_CHECKED);
+ protected PointcutLatch myAfterEmpiLatch = new PointcutLatch(Pointcut.EMPI_AFTER_PERSISTED_RESOURCE_CHECKED);
@Autowired
- MdmQueueConsumerLoader myMdmQueueConsumerLoader;
+ EmpiQueueConsumerLoader myEmpiQueueConsumerLoader;
@Autowired
SubscriptionRegistry mySubscriptionRegistry;
@Autowired
SubscriptionLoader mySubscriptionLoader;
@Autowired
- MdmSubscriptionLoader myMdmSubscriptionLoader;
+ EmpiSubscriptionLoader myEmpiSubscriptionLoader;
@Mock
private IInterceptorBroadcaster myMockInterceptorBroadcaster;
@Autowired
@@ -66,9 +66,9 @@ public abstract class BaseMdmHelper implements BeforeEachCallback, AfterEachCall
@Override
public void afterEach(ExtensionContext context) throws Exception {
- myInterceptorService.unregisterInterceptor(myAfterMdmLatch);
- myAfterMdmLatch.clear();
- waitUntilMdmQueueIsEmpty();
+ myInterceptorService.unregisterInterceptor(myAfterEmpiLatch);
+ myAfterEmpiLatch.clear();
+ waitUntilEmpiQueueIsEmpty();
}
@Override
@@ -83,11 +83,11 @@ public abstract class BaseMdmHelper implements BeforeEachCallback, AfterEachCall
when(myMockRestfulServer.getFhirContext()).thenReturn(myMockFhirContext);
//This sets up our basic interceptor, and also attached the latch so we can await the hook calls.
- myInterceptorService.registerAnonymousInterceptor(Pointcut.MDM_AFTER_PERSISTED_RESOURCE_CHECKED, myAfterMdmLatch);
+ myInterceptorService.registerAnonymousInterceptor(Pointcut.EMPI_AFTER_PERSISTED_RESOURCE_CHECKED, myAfterEmpiLatch);
// We need to call this because subscriptions will get deleted in @After cleanup
waitForActivatedSubscriptionCount(0);
- myMdmSubscriptionLoader.daoUpdateMdmSubscriptions();
+ myEmpiSubscriptionLoader.daoUpdateEmpiSubscriptions();
mySubscriptionLoader.syncSubscriptions();
waitForActivatedSubscriptionCount(2);
}
@@ -97,12 +97,12 @@ public abstract class BaseMdmHelper implements BeforeEachCallback, AfterEachCall
await("Active Subscription Count has reached " + theSize).until(() -> mySubscriptionRegistry.size() >= theSize);
}
- private void waitUntilMdmQueueIsEmpty() {
+ private void waitUntilEmpiQueueIsEmpty() {
await().until(() -> getExecutorQueueSize() == 0);
}
public int getExecutorQueueSize() {
- LinkedBlockingChannel channel = (LinkedBlockingChannel) myMdmQueueConsumerLoader.getMdmChannelForUnitTest();
+ LinkedBlockingChannel channel = (LinkedBlockingChannel) myEmpiQueueConsumerLoader.getEmpiChannelForUnitTest();
return channel.getQueueSizeForUnitTest();
}
diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/helper/MdmHelperConfig.java b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/helper/EmpiHelperConfig.java
similarity index 58%
rename from hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/helper/MdmHelperConfig.java
rename to hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/helper/EmpiHelperConfig.java
index 42feead36fc..8e9a91e1c56 100644
--- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/helper/MdmHelperConfig.java
+++ b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/helper/EmpiHelperConfig.java
@@ -1,8 +1,8 @@
-package ca.uhn.fhir.jpa.mdm.helper;
+package ca.uhn.fhir.jpa.empi.helper;
-import ca.uhn.fhir.mdm.api.IMdmSettings;
-import ca.uhn.fhir.mdm.rules.config.MdmRuleValidator;
-import ca.uhn.fhir.mdm.rules.config.MdmSettings;
+import ca.uhn.fhir.empi.api.IEmpiSettings;
+import ca.uhn.fhir.empi.rules.config.EmpiRuleValidator;
+import ca.uhn.fhir.empi.rules.config.EmpiSettings;
import com.google.common.base.Charsets;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Value;
@@ -13,27 +13,27 @@ import org.springframework.core.io.Resource;
import java.io.IOException;
-public class MdmHelperConfig {
+public class EmpiHelperConfig {
@Bean
- public MdmHelperR4 mdmHelperR4() {
- return new MdmHelperR4();
+ public EmpiHelperR4 empiHelperR4() {
+ return new EmpiHelperR4();
}
- @Value("${mdm.prevent_eid_updates:false}")
+ @Value("${empi.prevent_eid_updates:false}")
boolean myPreventEidUpdates;
- @Value("${mdm.prevent_multiple_eids:true}")
+ @Value("${empi.prevent_multiple_eids:true}")
boolean myPreventMultipleEids;
@Primary
@Bean
- IMdmSettings mdmSettings(MdmRuleValidator theMdmRuleValidator) throws IOException {
+ IEmpiSettings empiSettings(EmpiRuleValidator theEmpiRuleValidator) throws IOException {
DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
- Resource resource = resourceLoader.getResource("mdm/mdm-rules.json");
+ Resource resource = resourceLoader.getResource("empi/empi-rules.json");
String json = IOUtils.toString(resource.getInputStream(), Charsets.UTF_8);
// Set Enabled to true, and set strict mode.
- return new MdmSettings(theMdmRuleValidator)
+ return new EmpiSettings(theEmpiRuleValidator)
.setEnabled(true)
.setScriptText(json)
.setPreventEidUpdates(myPreventEidUpdates)
diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/helper/MdmHelperR4.java b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/helper/EmpiHelperR4.java
similarity index 87%
rename from hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/helper/MdmHelperR4.java
rename to hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/helper/EmpiHelperR4.java
index 5bc2e30c6e7..f1faca6fef8 100644
--- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/helper/MdmHelperR4.java
+++ b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/helper/EmpiHelperR4.java
@@ -1,4 +1,4 @@
-package ca.uhn.fhir.jpa.mdm.helper;
+package ca.uhn.fhir.jpa.empi.helper;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
@@ -9,7 +9,7 @@ import ca.uhn.fhir.rest.server.TransactionLogMessages;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.springframework.beans.factory.annotation.Autowired;
-public class MdmHelperR4 extends BaseMdmHelper {
+public class EmpiHelperR4 extends BaseEmpiHelper {
@Autowired
private FhirContext myFhirContext;
@Autowired
@@ -20,10 +20,10 @@ public class MdmHelperR4 extends BaseMdmHelper {
}
public OutcomeAndLogMessageWrapper createWithLatch(IBaseResource theBaseResource, boolean isExternalHttpRequest) throws InterruptedException {
- myAfterMdmLatch.setExpectedCount(1);
+ myAfterEmpiLatch.setExpectedCount(1);
DaoMethodOutcome daoMethodOutcome = doCreateResource(theBaseResource, isExternalHttpRequest);
- myAfterMdmLatch.awaitExpected();
- return new OutcomeAndLogMessageWrapper(daoMethodOutcome, myAfterMdmLatch.getLatchInvocationParameterOfType(TransactionLogMessages.class));
+ myAfterEmpiLatch.awaitExpected();
+ return new OutcomeAndLogMessageWrapper(daoMethodOutcome, myAfterEmpiLatch.getLatchInvocationParameterOfType(TransactionLogMessages.class));
}
public OutcomeAndLogMessageWrapper updateWithLatch(IBaseResource theIBaseResource) throws InterruptedException {
@@ -31,10 +31,10 @@ public class MdmHelperR4 extends BaseMdmHelper {
}
public OutcomeAndLogMessageWrapper updateWithLatch(IBaseResource theIBaseResource, boolean isExternalHttpRequest) throws InterruptedException {
- myAfterMdmLatch.setExpectedCount(1);
+ myAfterEmpiLatch.setExpectedCount(1);
DaoMethodOutcome daoMethodOutcome = doUpdateResource(theIBaseResource, isExternalHttpRequest);
- myAfterMdmLatch.awaitExpected();
- return new OutcomeAndLogMessageWrapper(daoMethodOutcome, myAfterMdmLatch.getLatchInvocationParameterOfType(TransactionLogMessages.class));
+ myAfterEmpiLatch.awaitExpected();
+ return new OutcomeAndLogMessageWrapper(daoMethodOutcome, myAfterEmpiLatch.getLatchInvocationParameterOfType(TransactionLogMessages.class));
}
public DaoMethodOutcome doCreateResource(IBaseResource theResource, boolean isExternalHttpRequest) {
@@ -53,7 +53,7 @@ public class MdmHelperR4 extends BaseMdmHelper {
/**
* OutcomeAndLogMessageWrapper is a simple wrapper class which is _excellent_. It allows us to skip the fact that java doesn't allow
* multiple returns, and wraps both the Method Outcome of the DAO, _and_ the TransactionLogMessages that were passed to the pointcut
- * by the MDM module.
+ * by the EMPI module.
*/
public class OutcomeAndLogMessageWrapper {
DaoMethodOutcome myDaoMethodOutcome;
diff --git a/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/helper/EmpiLinkHelper.java b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/helper/EmpiLinkHelper.java
new file mode 100644
index 00000000000..b4c0cfaed2b
--- /dev/null
+++ b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/helper/EmpiLinkHelper.java
@@ -0,0 +1,31 @@
+package ca.uhn.fhir.jpa.empi.helper;
+
+import ca.uhn.fhir.jpa.dao.data.IEmpiLinkDao;
+import ca.uhn.fhir.jpa.entity.EmpiLink;
+import ca.uhn.fhir.model.primitive.IdDt;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+@Service
+public class EmpiLinkHelper {
+ private static final Logger ourLog = LoggerFactory.getLogger(EmpiLinkHelper.class);
+
+ @Autowired
+ IEmpiLinkDao myEmpiLinkDao;
+
+ @Transactional
+ public void logEmpiLinks() {
+ List links = myEmpiLinkDao.findAll();
+ ourLog.info("All EMPI Links:");
+ for (EmpiLink link : links) {
+ IdDt personId = link.getPerson().getIdDt().toVersionless();
+ IdDt targetId = link.getTarget().getIdDt().toVersionless();
+ ourLog.info("{}: {}, {}, {}, {}", link.getId(), personId, targetId, link.getMatchResult(), link.getLinkSource());
+ }
+ }
+}
diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmExpungeTest.java b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/interceptor/EmpiExpungeTest.java
similarity index 59%
rename from hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmExpungeTest.java
rename to hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/interceptor/EmpiExpungeTest.java
index a0ca44cc446..415f80bfbe1 100644
--- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmExpungeTest.java
+++ b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/interceptor/EmpiExpungeTest.java
@@ -1,16 +1,18 @@
-package ca.uhn.fhir.jpa.mdm.interceptor;
+package ca.uhn.fhir.jpa.empi.interceptor;
+import ca.uhn.fhir.empi.api.EmpiLinkSourceEnum;
+import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
import ca.uhn.fhir.interceptor.api.IInterceptorService;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.api.model.ExpungeOptions;
-import ca.uhn.fhir.jpa.entity.MdmLink;
-import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
+import ca.uhn.fhir.jpa.dao.data.IEmpiLinkDao;
+import ca.uhn.fhir.jpa.empi.BaseEmpiR4Test;
+import ca.uhn.fhir.jpa.entity.EmpiLink;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
-import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
-import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import org.hl7.fhir.r4.model.Patient;
+import org.hl7.fhir.r4.model.Person;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -21,16 +23,18 @@ import static org.hamcrest.core.StringContains.containsString;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
-public class MdmExpungeTest extends BaseMdmR4Test {
+public class EmpiExpungeTest extends BaseEmpiR4Test {
@Autowired
IInterceptorService myInterceptorService;
@Autowired
- IMdmStorageInterceptor myMdmStorageInterceptor;
+ IEmpiStorageInterceptor myEmpiStorageInterceptor;
@Autowired
DaoConfig myDaoConfig;
+ @Autowired
+ IEmpiLinkDao myEmpiLinkDao;
private ResourceTable myTargetEntity;
- private ResourceTable mySourceEntity;
+ private ResourceTable myPersonEntity;
private IdDt myTargetId;
@BeforeEach
@@ -39,21 +43,21 @@ public class MdmExpungeTest extends BaseMdmR4Test {
myTargetEntity = (ResourceTable) myPatientDao.create(new Patient()).getEntity();
myTargetId = myTargetEntity.getIdDt().toVersionless();
- mySourceEntity = (ResourceTable) myPatientDao.create(new Patient()).getEntity();
+ myPersonEntity = (ResourceTable) myPersonDao.create(new Person()).getEntity();
- MdmLink mdmLink = myMdmLinkDaoSvc.newMdmLink();
- mdmLink.setLinkSource(MdmLinkSourceEnum.MANUAL);
- mdmLink.setMatchResult(MdmMatchResultEnum.MATCH);
- mdmLink.setGoldenResourcePid(mySourceEntity.getId());
- mdmLink.setSourcePid(myTargetEntity.getId());
- saveLink(mdmLink);
+ EmpiLink empiLink = myEmpiLinkDaoSvc.newEmpiLink();
+ empiLink.setLinkSource(EmpiLinkSourceEnum.MANUAL);
+ empiLink.setMatchResult(EmpiMatchResultEnum.MATCH);
+ empiLink.setPersonPid(myPersonEntity.getId());
+ empiLink.setTargetPid(myTargetEntity.getId());
+ saveLink(empiLink);
}
@Test
- public void testUninterceptedDeleteRemovesMdmReference() {
- assertEquals(1, myMdmLinkDao.count());
+ public void testUninterceptedDeleteRemovesEMPIReference() {
+ assertEquals(1, myEmpiLinkDao.count());
myPatientDao.delete(myTargetEntity.getIdDt());
- assertEquals(1, myMdmLinkDao.count());
+ assertEquals(1, myEmpiLinkDao.count());
ExpungeOptions expungeOptions = new ExpungeOptions();
expungeOptions.setExpungeDeletedResources(true);
try {
@@ -63,14 +67,14 @@ public class MdmExpungeTest extends BaseMdmR4Test {
assertThat(e.getMessage(), containsString("ViolationException"));
assertThat(e.getMessage(), containsString("FK_EMPI_LINK_TARGET"));
}
- myInterceptorService.registerInterceptor(myMdmStorageInterceptor);
+ myInterceptorService.registerInterceptor(myEmpiStorageInterceptor);
myPatientDao.expunge(myTargetId.toVersionless(), expungeOptions, null);
- assertEquals(0, myMdmLinkDao.count());
+ assertEquals(0, myEmpiLinkDao.count());
}
@AfterEach
public void afterUnregisterInterceptor() {
- myInterceptorService.unregisterInterceptor(myMdmStorageInterceptor);
+ myInterceptorService.unregisterInterceptor(myEmpiStorageInterceptor);
}
}
diff --git a/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/interceptor/EmpiStorageInterceptorIT.java b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/interceptor/EmpiStorageInterceptorIT.java
new file mode 100644
index 00000000000..e3575c743a7
--- /dev/null
+++ b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/interceptor/EmpiStorageInterceptorIT.java
@@ -0,0 +1,299 @@
+package ca.uhn.fhir.jpa.empi.interceptor;
+
+import ca.uhn.fhir.empi.model.CanonicalEID;
+import ca.uhn.fhir.empi.rules.config.EmpiSettings;
+import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome;
+import ca.uhn.fhir.jpa.dao.index.IdHelperService;
+import ca.uhn.fhir.jpa.empi.BaseEmpiR4Test;
+import ca.uhn.fhir.jpa.empi.helper.EmpiHelperConfig;
+import ca.uhn.fhir.jpa.empi.helper.EmpiHelperR4;
+import ca.uhn.fhir.jpa.entity.EmpiLink;
+import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
+import ca.uhn.fhir.rest.api.server.IBundleProvider;
+import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
+import ca.uhn.fhir.rest.server.TransactionLogMessages;
+import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException;
+import org.hl7.fhir.instance.model.api.IBaseResource;
+import org.hl7.fhir.instance.model.api.IIdType;
+import org.hl7.fhir.r4.model.Enumerations;
+import org.hl7.fhir.r4.model.Organization;
+import org.hl7.fhir.r4.model.Patient;
+import org.hl7.fhir.r4.model.Person;
+import org.hl7.fhir.r4.model.SearchParameter;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.ContextConfiguration;
+
+import java.util.Date;
+import java.util.List;
+
+import static ca.uhn.fhir.empi.api.EmpiConstants.CODE_HAPI_EMPI_MANAGED;
+import static ca.uhn.fhir.empi.api.EmpiConstants.SYSTEM_EMPI_MANAGED;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.fail;
+import static org.slf4j.LoggerFactory.getLogger;
+
+@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
+@ContextConfiguration(classes = {EmpiHelperConfig.class})
+public class EmpiStorageInterceptorIT extends BaseEmpiR4Test {
+
+ private static final Logger ourLog = getLogger(EmpiStorageInterceptorIT.class);
+
+ @RegisterExtension
+ @Autowired
+ public EmpiHelperR4 myEmpiHelper;
+ @Autowired
+ private IdHelperService myIdHelperService;
+
+ @BeforeEach
+ public void before() {
+ super.loadEmpiSearchParameters();
+ }
+
+ @Test
+ public void testCreatePractitioner() throws InterruptedException {
+ myEmpiHelper.createWithLatch(buildPractitionerWithNameAndId("somename", "some_id"));
+ assertLinkCount(1);
+ }
+
+ @Test
+ public void testCreatePerson() {
+ myPersonDao.create(new Person());
+ assertLinkCount(0);
+ }
+
+ @Test
+ public void testDeletePersonDeletesLinks() throws InterruptedException {
+ myEmpiHelper.createWithLatch(buildPaulPatient());
+ assertLinkCount(1);
+ Person person = getOnlyActivePerson();
+ myPersonDao.delete(person.getIdElement());
+ assertLinkCount(0);
+ }
+
+ @Test
+ public void testCreatePersonWithEmpiTagForbidden() throws InterruptedException {
+ //Creating a person with the EMPI-MANAGED tag should fail
+ Person person = new Person();
+ person.getMeta().addTag(SYSTEM_EMPI_MANAGED, CODE_HAPI_EMPI_MANAGED, "User is managed by EMPI");
+ try {
+ myEmpiHelper.doCreateResource(person, true);
+ fail();
+ } catch (ForbiddenOperationException e) {
+ assertEquals("Cannot create or modify Resources that are managed by EMPI.", e.getMessage());
+ }
+ }
+
+ @Test
+ public void testCreatingPersonWithInsufficentEMPIAttributesIsNotEMPIProcessed() throws InterruptedException {
+ myEmpiHelper.doCreateResource(new Patient(), true);
+ assertLinkCount(0);
+ }
+
+ @Test
+ public void testCreatingPatientWithOneOrMoreMatchingAttributesIsEMPIProcessed() throws InterruptedException {
+ myEmpiHelper.createWithLatch(buildPaulPatient());
+ assertLinkCount(1);
+ }
+
+ @Test
+ public void testCreateOrganizationWithEmpiTagForbidden() throws InterruptedException {
+ //Creating a organization with the EMPI-MANAGED tag should fail
+ Organization organization = new Organization();
+ organization.getMeta().addTag(SYSTEM_EMPI_MANAGED, CODE_HAPI_EMPI_MANAGED, "User is managed by EMPI");
+ try {
+ myEmpiHelper.doCreateResource(organization, true);
+ fail();
+ } catch (ForbiddenOperationException e) {
+ assertEquals("Cannot create or modify Resources that are managed by EMPI.", e.getMessage());
+ }
+ }
+
+ @Test
+ public void testUpdateOrganizationWithEmpiTagForbidden() throws InterruptedException {
+ //Creating a organization with the EMPI-MANAGED tag should fail
+ Organization organization = new Organization();
+ myEmpiHelper.doCreateResource(organization, true);
+ organization.getMeta().addTag(SYSTEM_EMPI_MANAGED, CODE_HAPI_EMPI_MANAGED, "User is managed by EMPI");
+ try {
+ myEmpiHelper.doUpdateResource(organization, true);
+ fail();
+ } catch (ForbiddenOperationException e) {
+ assertEquals("The HAPI-EMPI tag on a resource may not be changed once created.", e.getMessage());
+ }
+ }
+
+ @Test
+ public void testPersonRecordsManagedByEmpiAllShareSameTag() throws InterruptedException {
+ myEmpiHelper.createWithLatch(buildJanePatient());
+ myEmpiHelper.createWithLatch(buildPaulPatient());
+
+ IBundleProvider search = myPersonDao.search(new SearchParameterMap().setLoadSynchronous(true));
+ List resources = search.getResources(0, search.size());
+
+ for (IBaseResource person : resources) {
+ assertThat(person.getMeta().getTag(SYSTEM_EMPI_MANAGED, CODE_HAPI_EMPI_MANAGED), is(notNullValue()));
+ }
+ }
+
+ @Test
+ public void testNonEmpiManagedPersonCannotHaveEmpiManagedTagAddedToThem() {
+ //Person created manually.
+ Person person = new Person();
+ DaoMethodOutcome daoMethodOutcome = myEmpiHelper.doCreateResource(person, true);
+ assertNotNull(daoMethodOutcome.getId());
+
+ //Updating that person to set them as EMPI managed is not allowed.
+ person.getMeta().addTag(SYSTEM_EMPI_MANAGED, CODE_HAPI_EMPI_MANAGED, "User is managed by EMPI");
+ try {
+ myEmpiHelper.doUpdateResource(person, true);
+ fail();
+ } catch (ForbiddenOperationException e) {
+ assertEquals("The HAPI-EMPI tag on a resource may not be changed once created.", e.getMessage());
+ }
+ }
+
+ @Test
+ public void testEmpiManagedPersonCannotBeModifiedByPersonUpdateRequest() throws InterruptedException {
+ // When EMPI is enabled, only the EMPI system is allowed to modify Person links of Persons with the EMPI-MANAGED tag.
+ Patient patient = new Patient();
+ IIdType patientId = myEmpiHelper.createWithLatch(buildPaulPatient()).getDaoMethodOutcome().getId().toUnqualifiedVersionless();
+
+ patient.setId(patientId);
+
+ //Updating a Person who was created via EMPI should fail.
+ EmpiLink empiLink = myEmpiLinkDaoSvc.getMatchedLinkForTargetPid(myIdHelperService.getPidOrNull(patient)).get();
+ Long personPid = empiLink.getPersonPid();
+ Person empiPerson = (Person) myPersonDao.readByPid(new ResourcePersistentId(personPid));
+ empiPerson.setGender(Enumerations.AdministrativeGender.MALE);
+ try {
+ myEmpiHelper.doUpdateResource(empiPerson, true);
+ fail();
+ } catch (ForbiddenOperationException e) {
+ assertEquals("Cannot create or modify Resources that are managed by EMPI.", e.getMessage());
+ }
+ }
+
+ @Test
+ public void testEmpiPointcutReceivesTransactionLogMessages() throws InterruptedException {
+ EmpiHelperR4.OutcomeAndLogMessageWrapper wrapper = myEmpiHelper.createWithLatch(buildJanePatient());
+
+ TransactionLogMessages empiTransactionLogMessages = wrapper.getLogMessages();
+
+ //There is no TransactionGuid here as there is no TransactionLog in this context.
+ assertThat(empiTransactionLogMessages.getTransactionGuid(), is(nullValue()));
+
+ List messages = empiTransactionLogMessages.getValues();
+ assertThat(messages.isEmpty(), is(false));
+ }
+
+ @Test
+ public void testWhenASingularPatientUpdatesExternalEidThatPersonEidIsUpdated() throws InterruptedException {
+ Patient jane = addExternalEID(buildJanePatient(), "some_eid");
+ EmpiHelperR4.OutcomeAndLogMessageWrapper latch = myEmpiHelper.createWithLatch(jane);
+ jane.setId(latch.getDaoMethodOutcome().getId());
+ clearExternalEIDs(jane);
+ jane = addExternalEID(jane, "some_new_eid");
+
+ EmpiHelperR4.OutcomeAndLogMessageWrapper outcomeWrapper = myEmpiHelper.updateWithLatch(jane);
+ Person person = getPersonFromTarget(jane);
+ List externalEids = myEIDHelper.getExternalEid(person);
+ assertThat(externalEids, hasSize(1));
+ assertThat("some_new_eid", is(equalTo(externalEids.get(0).getValue())));
+ }
+
+ @Test
+ public void testWhenEidUpdatesAreDisabledForbidsUpdatesToEidsOnTargets() throws InterruptedException {
+ setPreventEidUpdates(true);
+ Patient jane = addExternalEID(buildJanePatient(), "some_eid");
+ EmpiHelperR4.OutcomeAndLogMessageWrapper latch = myEmpiHelper.createWithLatch(jane);
+ jane.setId(latch.getDaoMethodOutcome().getId());
+ clearExternalEIDs(jane);
+ jane = addExternalEID(jane, "some_new_eid");
+ try {
+ myEmpiHelper.doUpdateResource(jane, true);
+ fail();
+ } catch (ForbiddenOperationException e) {
+ assertThat(e.getMessage(), is(equalTo("While running with EID updates disabled, EIDs may not be updated on Patient/Practitioner resources")));
+ }
+ setPreventEidUpdates(false);
+ }
+
+ @Test
+ public void testWhenMultipleEidsAreDisabledThatTheInterceptorRejectsCreatesWithThem() {
+ setPreventMultipleEids(true);
+ Patient patient = buildJanePatient();
+ addExternalEID(patient, "123");
+ addExternalEID(patient, "456");
+ try {
+ myEmpiHelper.doCreateResource(patient, true);
+ fail();
+ } catch (ForbiddenOperationException e) {
+ assertThat(e.getMessage(), is(equalTo("While running with multiple EIDs disabled, Patient/Practitioner resources may have at most one EID.")));
+ }
+
+ setPreventMultipleEids(false);
+ }
+
+ @Test
+ public void testInterceptorHandlesNonEmpiResources() {
+ setPreventEidUpdates(true);
+
+ //Create some arbitrary resource.
+ SearchParameter fooSp = new SearchParameter();
+ fooSp.setCode("foo");
+ fooSp.addBase("Bundle");
+ fooSp.setType(Enumerations.SearchParamType.REFERENCE);
+ fooSp.setTitle("FOO SP");
+ fooSp.setExpression("Bundle.entry[0].resource.as(Composition).encounter");
+ fooSp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
+ fooSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE);
+
+ myEmpiHelper.doCreateResource(fooSp, true);
+ fooSp.setXpathUsage(SearchParameter.XPathUsageType.PHONETIC);
+ myEmpiHelper.doUpdateResource(fooSp, true);
+ }
+
+ @Test
+ public void testPatientsWithNoEIDCanBeUpdated() throws InterruptedException {
+ setPreventEidUpdates(true);
+ Patient p = buildPaulPatient();
+ EmpiHelperR4.OutcomeAndLogMessageWrapper wrapper = myEmpiHelper.createWithLatch(p);
+
+ p.setId(wrapper.getDaoMethodOutcome().getId());
+ p.setBirthDate(new Date());
+ myEmpiHelper.updateWithLatch(p);
+ setPreventEidUpdates(false);
+ }
+
+ @Test
+ public void testPatientsCanHaveEIDAddedInStrictMode() throws InterruptedException {
+ setPreventEidUpdates(true);
+ Patient p = buildPaulPatient();
+ EmpiHelperR4.OutcomeAndLogMessageWrapper messageWrapper = myEmpiHelper.createWithLatch(p);
+ p.setId(messageWrapper.getDaoMethodOutcome().getId());
+ addExternalEID(p, "external eid");
+ myEmpiHelper.updateWithLatch(p);
+ setPreventEidUpdates(false);
+ }
+
+ private void setPreventEidUpdates(boolean thePrevent) {
+ ((EmpiSettings) myEmpiConfig).setPreventEidUpdates(thePrevent);
+ }
+
+ private void setPreventMultipleEids(boolean thePrevent) {
+ ((EmpiSettings) myEmpiConfig).setPreventMultipleEids(thePrevent);
+ }
+
+}
diff --git a/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/matcher/BasePersonMatcher.java b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/matcher/BasePersonMatcher.java
new file mode 100644
index 00000000000..e6ee7f961c8
--- /dev/null
+++ b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/matcher/BasePersonMatcher.java
@@ -0,0 +1,79 @@
+package ca.uhn.fhir.jpa.empi.matcher;
+
+import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
+import ca.uhn.fhir.jpa.dao.index.IdHelperService;
+import ca.uhn.fhir.jpa.empi.dao.EmpiLinkDaoSvc;
+import ca.uhn.fhir.jpa.entity.EmpiLink;
+import org.hamcrest.TypeSafeMatcher;
+import org.hl7.fhir.instance.model.api.IAnyResource;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public abstract class BasePersonMatcher extends TypeSafeMatcher {
+ private static final Logger ourLog = LoggerFactory.getLogger(BasePersonMatcher.class);
+
+ protected IdHelperService myIdHelperService;
+ protected EmpiLinkDaoSvc myEmpiLinkDaoSvc;
+ protected Collection myBaseResources;
+
+ protected BasePersonMatcher(IdHelperService theIdHelperService, EmpiLinkDaoSvc theEmpiLinkDaoSvc, IAnyResource... theBaseResource) {
+ myIdHelperService = theIdHelperService;
+ myEmpiLinkDaoSvc = theEmpiLinkDaoSvc;
+ myBaseResources = Arrays.stream(theBaseResource).collect(Collectors.toList());
+ }
+
+ @Nullable
+ protected Long getMatchedPersonPidFromResource(IAnyResource theResource) {
+ Long retval;
+ if (isPatientOrPractitioner(theResource)) {
+ EmpiLink matchLink = getMatchedEmpiLink(theResource);
+ retval = matchLink == null ? null : matchLink.getPersonPid();
+ } else if (isPerson(theResource)) {
+ retval = myIdHelperService.getPidOrNull(theResource);
+ } else {
+ throw new IllegalArgumentException("Resources of type " + theResource.getIdElement().getResourceType() + " cannot be persons!");
+ }
+ return retval;
+ }
+
+ protected List getPossibleMatchedPersonPidsFromTarget(IAnyResource theBaseResource) {
+ return getEmpiLinksForTarget(theBaseResource, EmpiMatchResultEnum.POSSIBLE_MATCH).stream().map(EmpiLink::getPersonPid).collect(Collectors.toList());
+ }
+
+ protected boolean isPatientOrPractitioner(IAnyResource theResource) {
+ String resourceType = theResource.getIdElement().getResourceType();
+ return (resourceType.equalsIgnoreCase("Patient") || resourceType.equalsIgnoreCase("Practitioner"));
+ }
+
+ protected EmpiLink getMatchedEmpiLink(IAnyResource thePatientOrPractitionerResource) {
+ List empiLinks = getEmpiLinksForTarget(thePatientOrPractitionerResource, EmpiMatchResultEnum.MATCH);
+ if (empiLinks.size() == 0) {
+ return null;
+ } else if (empiLinks.size() == 1) {
+ return empiLinks.get(0);
+ } else {
+ throw new IllegalStateException("Its illegal to have more than 1 match for a given target! we found " + empiLinks.size() + " for resource with id: " + thePatientOrPractitionerResource.getIdElement().toUnqualifiedVersionless());
+ }
+ }
+
+ protected boolean isPerson(IAnyResource theIncomingResource) {
+ return (theIncomingResource.getIdElement().getResourceType().equalsIgnoreCase("Person"));
+ }
+
+ protected List getEmpiLinksForTarget(IAnyResource thePatientOrPractitionerResource, EmpiMatchResultEnum theMatchResult) {
+ Long pidOrNull = myIdHelperService.getPidOrNull(thePatientOrPractitionerResource);
+ List matchLinkForTarget = myEmpiLinkDaoSvc.getEmpiLinksByTargetPidAndMatchResult(pidOrNull, theMatchResult);
+ if (!matchLinkForTarget.isEmpty()) {
+ return matchLinkForTarget;
+ } else {
+ return new ArrayList<>();
+ }
+ }
+}
diff --git a/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/matcher/IsLinkedTo.java b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/matcher/IsLinkedTo.java
new file mode 100644
index 00000000000..4a18b40d2c1
--- /dev/null
+++ b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/matcher/IsLinkedTo.java
@@ -0,0 +1,48 @@
+package ca.uhn.fhir.jpa.empi.matcher;
+
+import ca.uhn.fhir.jpa.dao.index.IdHelperService;
+import ca.uhn.fhir.jpa.empi.dao.EmpiLinkDaoSvc;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hl7.fhir.instance.model.api.IAnyResource;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * A Matcher which allows us to check that a target patient/practitioner at a given link level.
+ * is linked to a set of patients/practitioners via a person.
+ *
+ */
+public class IsLinkedTo extends BasePersonMatcher {
+
+ private List baseResourcePersonPids;
+ private Long incomingResourcePersonPid;
+
+ protected IsLinkedTo(IdHelperService theIdHelperService, EmpiLinkDaoSvc theEmpiLinkDaoSvc, IAnyResource... theBaseResource) {
+ super(theIdHelperService, theEmpiLinkDaoSvc, theBaseResource);
+ }
+
+
+ @Override
+ protected boolean matchesSafely(IAnyResource theIncomingResource) {
+ incomingResourcePersonPid = getMatchedPersonPidFromResource(theIncomingResource);
+
+ //OK, lets grab all the person pids of the resources passed in via the constructor.
+ baseResourcePersonPids = myBaseResources.stream()
+ .map(this::getMatchedPersonPidFromResource)
+ .collect(Collectors.toList());
+
+ //The resources are linked if all person pids match the incoming person pid.
+ return baseResourcePersonPids.stream()
+ .allMatch(pid -> pid.equals(incomingResourcePersonPid));
+ }
+
+ @Override
+ public void describeTo(Description theDescription) {
+ }
+
+ public static Matcher linkedTo(IdHelperService theIdHelperService, EmpiLinkDaoSvc theEmpiLinkDaoSvc, IAnyResource... theBaseResource) {
+ return new IsLinkedTo(theIdHelperService, theEmpiLinkDaoSvc, theBaseResource);
+ }
+}
diff --git a/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/matcher/IsMatchedToAPerson.java b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/matcher/IsMatchedToAPerson.java
new file mode 100644
index 00000000000..4d4fcd9d1e5
--- /dev/null
+++ b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/matcher/IsMatchedToAPerson.java
@@ -0,0 +1,37 @@
+package ca.uhn.fhir.jpa.empi.matcher;
+
+import ca.uhn.fhir.jpa.dao.index.IdHelperService;
+import ca.uhn.fhir.jpa.empi.dao.EmpiLinkDaoSvc;
+import ca.uhn.fhir.jpa.entity.EmpiLink;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+import org.hl7.fhir.instance.model.api.IAnyResource;
+
+import java.util.Optional;
+
+public class IsMatchedToAPerson extends TypeSafeMatcher {
+
+ private final IdHelperService myIdHelperService;
+ private final EmpiLinkDaoSvc myEmpiLinkDaoSvc;
+
+ public IsMatchedToAPerson(IdHelperService theIdHelperService, EmpiLinkDaoSvc theEmpiLinkDaoSvc) {
+ myIdHelperService = theIdHelperService;
+ myEmpiLinkDaoSvc = theEmpiLinkDaoSvc;
+ }
+
+ @Override
+ protected boolean matchesSafely(IAnyResource theIncomingResource) {
+ Optional matchedLinkForTargetPid = myEmpiLinkDaoSvc.getMatchedLinkForTargetPid(myIdHelperService.getPidOrNull(theIncomingResource));
+ return matchedLinkForTargetPid.isPresent();
+ }
+
+ @Override
+ public void describeTo(Description theDescription) {
+ theDescription.appendText("patient/practitioner was not linked to a Person.");
+ }
+
+ public static Matcher matchedToAPerson(IdHelperService theIdHelperService, EmpiLinkDaoSvc theEmpiLinkDaoSvc) {
+ return new IsMatchedToAPerson(theIdHelperService, theEmpiLinkDaoSvc);
+ }
+}
diff --git a/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/matcher/IsPossibleDuplicateOf.java b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/matcher/IsPossibleDuplicateOf.java
new file mode 100644
index 00000000000..4e42a78b57b
--- /dev/null
+++ b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/matcher/IsPossibleDuplicateOf.java
@@ -0,0 +1,61 @@
+package ca.uhn.fhir.jpa.empi.matcher;
+
+import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
+import ca.uhn.fhir.jpa.dao.index.IdHelperService;
+import ca.uhn.fhir.jpa.empi.dao.EmpiLinkDaoSvc;
+import ca.uhn.fhir.jpa.entity.EmpiLink;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hl7.fhir.instance.model.api.IAnyResource;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+public class IsPossibleDuplicateOf extends BasePersonMatcher {
+ /**
+ * Matcher with tells us if there is an EmpiLink with between these two resources that are considered POSSIBLE DUPLICATE.
+ * For use only on persons.
+ */
+ private Long incomingPersonPid;
+
+ protected IsPossibleDuplicateOf(IdHelperService theIdHelperService, EmpiLinkDaoSvc theEmpiLinkDaoSvc, IAnyResource... theBaseResource) {
+ super(theIdHelperService, theEmpiLinkDaoSvc, theBaseResource);
+ }
+
+ @Override
+ protected boolean matchesSafely(IAnyResource theIncomingResource) {
+
+ incomingPersonPid = getMatchedPersonPidFromResource(theIncomingResource);
+
+ List personPidsToMatch = myBaseResources.stream()
+ .map(this::getMatchedPersonPidFromResource)
+ .collect(Collectors.toList());
+
+
+ //Returns true if there is a POSSIBLE_DUPLICATE between the incoming resource, and all of the resources passed in via the constructor.
+ return personPidsToMatch.stream()
+ .map(baseResourcePid -> {
+ Optional duplicateLink = myEmpiLinkDaoSvc.getEmpiLinksByPersonPidTargetPidAndMatchResult(baseResourcePid, incomingPersonPid, EmpiMatchResultEnum.POSSIBLE_DUPLICATE);
+ if (!duplicateLink.isPresent()) {
+ duplicateLink = myEmpiLinkDaoSvc.getEmpiLinksByPersonPidTargetPidAndMatchResult(incomingPersonPid, baseResourcePid, EmpiMatchResultEnum.POSSIBLE_DUPLICATE);
+ }
+ return duplicateLink;
+ }).allMatch(Optional::isPresent);
+ }
+
+ @Override
+ public void describeTo(Description theDescription) {
+ theDescription.appendText("Person was not duplicate of Person/" + incomingPersonPid);
+ }
+
+ @Override
+ protected void describeMismatchSafely(IAnyResource item, Description mismatchDescription) {
+ super.describeMismatchSafely(item, mismatchDescription);
+ mismatchDescription.appendText("No Empi Link With POSSIBLE_DUPLICATE was found");
+ }
+
+ public static Matcher possibleDuplicateOf(IdHelperService theIdHelperService, EmpiLinkDaoSvc theEmpiLinkDaoSvc, IAnyResource... theBaseResource) {
+ return new IsPossibleDuplicateOf(theIdHelperService, theEmpiLinkDaoSvc, theBaseResource);
+ }
+}
diff --git a/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/matcher/IsPossibleLinkedTo.java b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/matcher/IsPossibleLinkedTo.java
new file mode 100644
index 00000000000..9e435d7d8a8
--- /dev/null
+++ b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/matcher/IsPossibleLinkedTo.java
@@ -0,0 +1,47 @@
+package ca.uhn.fhir.jpa.empi.matcher;
+
+import ca.uhn.fhir.jpa.dao.index.IdHelperService;
+import ca.uhn.fhir.jpa.empi.dao.EmpiLinkDaoSvc;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hl7.fhir.instance.model.api.IAnyResource;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * A Matcher which allows us to check that a target patient/practitioner at a given link level.
+ * is linked to a set of patients/practitioners via a person.
+ *
+ */
+public class IsPossibleLinkedTo extends BasePersonMatcher {
+
+ private List baseResourcePersonPids;
+ private Long incomingResourcePersonPid;
+
+ protected IsPossibleLinkedTo(IdHelperService theIdHelperService, EmpiLinkDaoSvc theEmpiLinkDaoSvc, IAnyResource... theTargetResources) {
+ super(theIdHelperService, theEmpiLinkDaoSvc, theTargetResources);
+ }
+
+ @Override
+ protected boolean matchesSafely(IAnyResource thePersonResource) {
+ incomingResourcePersonPid = myIdHelperService.getPidOrNull(thePersonResource);;
+
+ //OK, lets grab all the person pids of the resources passed in via the constructor.
+ baseResourcePersonPids = myBaseResources.stream()
+ .flatMap(iBaseResource -> getPossibleMatchedPersonPidsFromTarget(iBaseResource).stream())
+ .collect(Collectors.toList());
+
+ //The resources are linked if all person pids match the incoming person pid.
+ return baseResourcePersonPids.stream()
+ .allMatch(pid -> pid.equals(incomingResourcePersonPid));
+ }
+
+ @Override
+ public void describeTo(Description theDescription) {
+ }
+
+ public static Matcher possibleLinkedTo(IdHelperService theIdHelperService, EmpiLinkDaoSvc theEmpiLinkDaoSvc, IAnyResource... theBaseResource) {
+ return new IsPossibleLinkedTo(theIdHelperService, theEmpiLinkDaoSvc, theBaseResource);
+ }
+}
diff --git a/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/matcher/IsPossibleMatchWith.java b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/matcher/IsPossibleMatchWith.java
new file mode 100644
index 00000000000..58796bd4239
--- /dev/null
+++ b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/matcher/IsPossibleMatchWith.java
@@ -0,0 +1,58 @@
+package ca.uhn.fhir.jpa.empi.matcher;
+
+import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
+import ca.uhn.fhir.jpa.dao.index.IdHelperService;
+import ca.uhn.fhir.jpa.empi.dao.EmpiLinkDaoSvc;
+import ca.uhn.fhir.jpa.entity.EmpiLink;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hl7.fhir.instance.model.api.IAnyResource;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * Matcher with tells us if there is an EmpiLink with between these two resources that are considered POSSIBLE_MATCH
+ */
+public class IsPossibleMatchWith extends BasePersonMatcher {
+
+ protected IsPossibleMatchWith(IdHelperService theIdHelperService, EmpiLinkDaoSvc theEmpiLinkDaoSvc, IAnyResource... theBaseResource) {
+ super(theIdHelperService, theEmpiLinkDaoSvc, theBaseResource);
+ }
+
+ @Override
+ protected boolean matchesSafely(IAnyResource theIncomingResource) {
+ List empiLinks = getEmpiLinksForTarget(theIncomingResource, EmpiMatchResultEnum.POSSIBLE_MATCH);
+
+ List personPidsToMatch = myBaseResources.stream()
+ .map(this::getMatchedPersonPidFromResource)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList());
+
+ if (personPidsToMatch.isEmpty()) {
+ personPidsToMatch = myBaseResources.stream()
+ .flatMap(iBaseResource -> getPossibleMatchedPersonPidsFromTarget(iBaseResource).stream())
+ .collect(Collectors.toList());
+ }
+
+ List empiLinkSourcePersonPids = empiLinks.stream().map(EmpiLink::getPersonPid).collect(Collectors.toList());
+
+ return empiLinkSourcePersonPids.containsAll(personPidsToMatch);
+ }
+
+ @Override
+ public void describeTo(Description theDescription) {
+ theDescription.appendText(" no link found with POSSIBLE_MATCH to the requested PIDS");
+ }
+
+ @Override
+ protected void describeMismatchSafely(IAnyResource item, Description mismatchDescription) {
+ super.describeMismatchSafely(item, mismatchDescription);
+ mismatchDescription.appendText("No Empi Link With POSSIBLE_MATCH was found");
+ }
+
+ public static Matcher possibleMatchWith(IdHelperService theIdHelperService, EmpiLinkDaoSvc theEmpiLinkDaoSvc, IAnyResource... theBaseResource) {
+ return new IsPossibleMatchWith(theIdHelperService, theEmpiLinkDaoSvc, theBaseResource);
+ }
+}
diff --git a/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/matcher/IsSamePersonAs.java b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/matcher/IsSamePersonAs.java
new file mode 100644
index 00000000000..96d4f8b3ccd
--- /dev/null
+++ b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/matcher/IsSamePersonAs.java
@@ -0,0 +1,46 @@
+package ca.uhn.fhir.jpa.empi.matcher;
+
+import ca.uhn.fhir.jpa.dao.index.IdHelperService;
+import ca.uhn.fhir.jpa.empi.dao.EmpiLinkDaoSvc;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hl7.fhir.instance.model.api.IAnyResource;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class IsSamePersonAs extends BasePersonMatcher {
+
+ private List personPidsToMatch;
+ private Long incomingPersonPid;
+
+ public IsSamePersonAs(IdHelperService theIdHelperService, EmpiLinkDaoSvc theEmpiLinkDaoSvc, IAnyResource... theBaseResource) {
+ super(theIdHelperService, theEmpiLinkDaoSvc, theBaseResource);
+ }
+
+ @Override
+ protected boolean matchesSafely(IAnyResource theIncomingResource) {
+ incomingPersonPid = getMatchedPersonPidFromResource(theIncomingResource);
+ personPidsToMatch = myBaseResources.stream().map(this::getMatchedPersonPidFromResource).collect(Collectors.toList());
+ boolean allToCheckAreSame = personPidsToMatch.stream().allMatch(pid -> pid.equals(personPidsToMatch.get(0)));
+ if (!allToCheckAreSame) {
+ throw new IllegalStateException("You wanted to do a person comparison, but the pool of persons you submitted for checking don't match! We won't even check the incoming person against them.");
+ }
+ return personPidsToMatch.contains(incomingPersonPid);
+ }
+
+ @Override
+ public void describeTo(Description theDescription) {
+ theDescription.appendText("patient/practitioner linked to Person/" + personPidsToMatch);
+ }
+
+ @Override
+ protected void describeMismatchSafely(IAnyResource item, Description mismatchDescription) {
+ super.describeMismatchSafely(item, mismatchDescription);
+ mismatchDescription.appendText(" was actually linked to Person/" + incomingPersonPid);
+ }
+
+ public static Matcher samePersonAs(IdHelperService theIdHelperService, EmpiLinkDaoSvc theEmpiLinkDaoSvc, IAnyResource... theBaseResource) {
+ return new IsSamePersonAs(theIdHelperService, theEmpiLinkDaoSvc, theBaseResource);
+ }
+}
diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/BaseLinkR4Test.java b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/provider/BaseLinkR4Test.java
similarity index 53%
rename from hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/BaseLinkR4Test.java
rename to hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/provider/BaseLinkR4Test.java
index d44a313340d..91461942939 100644
--- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/BaseLinkR4Test.java
+++ b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/provider/BaseLinkR4Test.java
@@ -1,11 +1,11 @@
-package ca.uhn.fhir.jpa.mdm.provider;
+package ca.uhn.fhir.jpa.empi.provider;
-import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
-import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
+import ca.uhn.fhir.empi.api.EmpiLinkSourceEnum;
+import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
-import ca.uhn.fhir.jpa.entity.MdmLink;
-import org.hl7.fhir.instance.model.api.IAnyResource;
+import ca.uhn.fhir.jpa.entity.EmpiLink;
import org.hl7.fhir.r4.model.Patient;
+import org.hl7.fhir.r4.model.Person;
import org.hl7.fhir.r4.model.StringType;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
@@ -18,20 +18,20 @@ import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
public abstract class BaseLinkR4Test extends BaseProviderR4Test {
- protected static final StringType NO_MATCH_RESULT = new StringType(MdmMatchResultEnum.NO_MATCH.name());
- protected static final StringType MATCH_RESULT = new StringType(MdmMatchResultEnum.MATCH.name());
- protected static final StringType POSSIBLE_MATCH_RESULT = new StringType(MdmMatchResultEnum.POSSIBLE_MATCH.name());
- protected static final StringType POSSIBLE_DUPLICATE_RESULT = new StringType(MdmMatchResultEnum.POSSIBLE_DUPLICATE.name());
+ protected static final StringType NO_MATCH_RESULT = new StringType(EmpiMatchResultEnum.NO_MATCH.name());
+ protected static final StringType MATCH_RESULT = new StringType(EmpiMatchResultEnum.MATCH.name());
+ protected static final StringType POSSIBLE_MATCH_RESULT = new StringType(EmpiMatchResultEnum.POSSIBLE_MATCH.name());
+ protected static final StringType POSSIBLE_DUPLICATE_RESULT = new StringType(EmpiMatchResultEnum.POSSIBLE_DUPLICATE.name());
@Autowired
DaoConfig myDaoConfig;
protected Patient myPatient;
- protected IAnyResource mySourcePatient;
- protected MdmLink myLink;
+ protected Person myPerson;
+ protected EmpiLink myLink;
protected StringType myPatientId;
- protected StringType mySourcePatientId;
- protected StringType myVersionlessGodlenResourceId;
+ protected StringType myPersonId;
+ protected StringType myVersionlessPersonId;
@Override
@BeforeEach
@@ -41,15 +41,15 @@ public abstract class BaseLinkR4Test extends BaseProviderR4Test {
myPatient = createPatientAndUpdateLinks(buildPaulPatient());
myPatientId = new StringType(myPatient.getIdElement().getValue());
- mySourcePatient = getGoldenResourceFromTargetResource(myPatient);
- mySourcePatientId = new StringType(mySourcePatient.getIdElement().getValue());
- myVersionlessGodlenResourceId = new StringType(mySourcePatient.getIdElement().toVersionless().getValue());
+ myPerson = getPersonFromTarget(myPatient);
+ myPersonId = new StringType(myPerson.getIdElement().getValue());
+ myVersionlessPersonId = new StringType(myPerson.getIdElement().toVersionless().getValue());
myLink = getOnlyPatientLink();
// Tests require our initial link to be a POSSIBLE_MATCH
- myLink.setMatchResult(MdmMatchResultEnum.POSSIBLE_MATCH);
+ myLink.setMatchResult(EmpiMatchResultEnum.POSSIBLE_MATCH);
saveLink(myLink);
- assertEquals(MdmLinkSourceEnum.AUTO, myLink.getLinkSource());
+ assertEquals(EmpiLinkSourceEnum.AUTO, myLink.getLinkSource());
myDaoConfig.setExpungeEnabled(true);
}
@@ -60,12 +60,12 @@ public abstract class BaseLinkR4Test extends BaseProviderR4Test {
}
@Nonnull
- protected MdmLink getOnlyPatientLink() {
- return myMdmLinkDaoSvc.findMdmLinkBySource(myPatient).get();
+ protected EmpiLink getOnlyPatientLink() {
+ return myEmpiLinkDaoSvc.findEmpiLinkByTarget(myPatient).get();
}
@Nonnull
- protected List getPatientLinks() {
- return myMdmLinkDaoSvc.findMdmLinksBySourceResource(myPatient);
+ protected List getPatientLinks() {
+ return myEmpiLinkDaoSvc.findEmpiLinksByTarget(myPatient);
}
}
diff --git a/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/provider/BaseProviderR4Test.java b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/provider/BaseProviderR4Test.java
new file mode 100644
index 00000000000..f610447a760
--- /dev/null
+++ b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/provider/BaseProviderR4Test.java
@@ -0,0 +1,52 @@
+package ca.uhn.fhir.jpa.empi.provider;
+
+import ca.uhn.fhir.empi.api.IEmpiControllerSvc;
+import ca.uhn.fhir.empi.api.IEmpiExpungeSvc;
+import ca.uhn.fhir.empi.api.IEmpiMatchFinderSvc;
+import ca.uhn.fhir.empi.api.IEmpiSubmitSvc;
+import ca.uhn.fhir.empi.provider.EmpiProviderR4;
+import ca.uhn.fhir.empi.rules.config.EmpiSettings;
+import ca.uhn.fhir.jpa.empi.BaseEmpiR4Test;
+import com.google.common.base.Charsets;
+import org.apache.commons.io.IOUtils;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.io.DefaultResourceLoader;
+import org.springframework.core.io.Resource;
+
+import java.io.IOException;
+
+public abstract class BaseProviderR4Test extends BaseEmpiR4Test {
+ EmpiProviderR4 myEmpiProviderR4;
+ @Autowired
+ private IEmpiMatchFinderSvc myEmpiMatchFinderSvc;
+ @Autowired
+ private IEmpiControllerSvc myEmpiControllerSvc;
+ @Autowired
+ private IEmpiExpungeSvc myEmpiResetSvc;
+ @Autowired
+ private IEmpiSubmitSvc myEmpiBatchSvc;
+ @Autowired
+ private EmpiSettings myEmpiSettings;
+
+ private String defaultScript;
+
+ protected void setEmpiRuleJson(String theString) throws IOException {
+ DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
+ Resource resource = resourceLoader.getResource(theString);
+ String json = IOUtils.toString(resource.getInputStream(), Charsets.UTF_8);
+ myEmpiSettings.setScriptText(json);
+ }
+
+ @BeforeEach
+ public void before() {
+ myEmpiProviderR4 = new EmpiProviderR4(myFhirContext, myEmpiControllerSvc, myEmpiMatchFinderSvc, myEmpiResetSvc, myEmpiBatchSvc);
+ defaultScript = myEmpiSettings.getScriptText();
+ }
+ @AfterEach
+ public void after() throws IOException {
+ super.after();
+ myEmpiSettings.setScriptText(defaultScript);
+ }
+}
diff --git a/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/provider/EmpiProviderBatchR4Test.java b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/provider/EmpiProviderBatchR4Test.java
new file mode 100644
index 00000000000..03dd8c52aad
--- /dev/null
+++ b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/provider/EmpiProviderBatchR4Test.java
@@ -0,0 +1,127 @@
+package ca.uhn.fhir.jpa.empi.provider;
+
+import ca.uhn.fhir.interceptor.api.IInterceptorService;
+import ca.uhn.fhir.interceptor.api.Pointcut;
+import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
+import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
+import ca.uhn.test.concurrency.PointcutLatch;
+import org.hl7.fhir.r4.model.IdType;
+import org.hl7.fhir.r4.model.Person;
+import org.hl7.fhir.r4.model.Practitioner;
+import org.hl7.fhir.r4.model.StringType;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.io.IOException;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.fail;
+
+public class EmpiProviderBatchR4Test extends BaseLinkR4Test {
+
+ protected Practitioner myPractitioner;
+ protected StringType myPractitionerId;
+ protected Person myPractitionerPerson;
+ protected StringType myPractitionerPersonId;
+
+ @Autowired
+ IInterceptorService myInterceptorService;
+ PointcutLatch afterEmpiLatch = new PointcutLatch(Pointcut.EMPI_AFTER_PERSISTED_RESOURCE_CHECKED);
+
+
+ @BeforeEach
+ public void before() {
+ super.before();
+ myPractitioner = createPractitionerAndUpdateLinks(buildPractitionerWithNameAndId("some_pract", "some_pract_id"));
+ myPractitionerId = new StringType(myPractitioner.getIdElement().getValue());
+ myPractitionerPerson = getPersonFromTarget(myPractitioner);
+ myPractitionerPersonId = new StringType(myPractitionerPerson.getIdElement().getValue());
+ myInterceptorService.registerAnonymousInterceptor(Pointcut.EMPI_AFTER_PERSISTED_RESOURCE_CHECKED, afterEmpiLatch);
+ }
+
+ @AfterEach
+ public void after() throws IOException {
+ myInterceptorService.unregisterInterceptor(afterEmpiLatch);
+ super.after();
+ }
+
+ @Test
+ public void testBatchRunOnAllPractitioners() throws InterruptedException {
+ StringType criteria = null;
+ myEmpiProviderR4.clearEmpiLinks(null, myRequestDetails);
+
+ afterEmpiLatch.runWithExpectedCount(1, () -> myEmpiProviderR4.empiBatchPractitionerType(criteria, null));
+ assertLinkCount(1);
+ }
+ @Test
+ public void testBatchRunOnSpecificPractitioner() throws InterruptedException {
+ myEmpiProviderR4.clearEmpiLinks(null, myRequestDetails);
+ afterEmpiLatch.runWithExpectedCount(1, () -> myEmpiProviderR4.empiBatchPractitionerInstance(myPractitioner.getIdElement(), null));
+ assertLinkCount(1);
+ }
+
+ @Test
+ public void testBatchRunOnNonExistentSpecificPractitioner() {
+ myEmpiProviderR4.clearEmpiLinks(null, myRequestDetails);
+ try {
+ myEmpiProviderR4.empiBatchPractitionerInstance(new IdType("Practitioner/999"), null);
+ fail();
+ } catch (ResourceNotFoundException e){}
+ }
+
+ @Test
+ public void testBatchRunOnAllPatients() throws InterruptedException {
+ assertLinkCount(2);
+ StringType criteria = null;
+ myEmpiProviderR4.clearEmpiLinks(null, myRequestDetails);
+ afterEmpiLatch.runWithExpectedCount(1, () -> myEmpiProviderR4.empiBatchPatientType(criteria, null));
+ assertLinkCount(1);
+ }
+
+ @Test
+ public void testBatchRunOnSpecificPatient() throws InterruptedException {
+ assertLinkCount(2);
+ myEmpiProviderR4.clearEmpiLinks(null, myRequestDetails);
+ afterEmpiLatch.runWithExpectedCount(1, () -> myEmpiProviderR4.empiBatchPatientInstance(myPatient.getIdElement(), null));
+ assertLinkCount(1);
+ }
+
+ @Test
+ public void testBatchRunOnNonExistentSpecificPatient() {
+ assertLinkCount(2);
+ myEmpiProviderR4.clearEmpiLinks(null, myRequestDetails);
+ try {
+ myEmpiProviderR4.empiBatchPatientInstance(new IdType("Patient/999"), null);
+ fail();
+ } catch (ResourceNotFoundException e){}
+ }
+
+ @Test
+ public void testBatchRunOnAllTypes() throws InterruptedException {
+ assertLinkCount(2);
+ StringType criteria = new StringType("");
+ myEmpiProviderR4.clearEmpiLinks(null, myRequestDetails);
+ afterEmpiLatch.runWithExpectedCount(2, () -> {
+ myEmpiProviderR4.empiBatchOnAllTargets(criteria, null);
+ });
+ assertLinkCount(2);
+ }
+
+ @Test
+ public void testBatchRunOnAllTypesWithInvalidCriteria() {
+ assertLinkCount(2);
+ StringType criteria = new StringType("death-date=2020-06-01");
+ myEmpiProviderR4.clearEmpiLinks(null, myRequestDetails);
+
+ try {
+ myEmpiProviderR4.empiBatchPractitionerType(criteria, null);
+ fail();
+ } catch(InvalidRequestException e) {
+ assertThat(e.getMessage(), is(equalTo("Failed to parse match URL[death-date=2020-06-01] - Resource type Practitioner does not have a parameter with name: death-date")));
+ }
+ }
+}
diff --git a/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/provider/EmpiProviderClearLinkR4Test.java b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/provider/EmpiProviderClearLinkR4Test.java
new file mode 100644
index 00000000000..c5bf15f4b17
--- /dev/null
+++ b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/provider/EmpiProviderClearLinkR4Test.java
@@ -0,0 +1,168 @@
+package ca.uhn.fhir.jpa.empi.provider;
+
+import ca.uhn.fhir.jpa.entity.EmpiLink;
+import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
+import ca.uhn.fhir.model.primitive.IdDt;
+import ca.uhn.fhir.rest.api.server.IBundleProvider;
+import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
+import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
+import org.hl7.fhir.r4.model.Parameters;
+import org.hl7.fhir.r4.model.Patient;
+import org.hl7.fhir.r4.model.Person;
+import org.hl7.fhir.r4.model.Practitioner;
+import org.hl7.fhir.r4.model.Reference;
+import org.hl7.fhir.r4.model.StringType;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import javax.annotation.Nonnull;
+import java.util.List;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+public class EmpiProviderClearLinkR4Test extends BaseLinkR4Test {
+ protected Practitioner myPractitioner;
+ protected StringType myPractitionerId;
+ protected Person myPractitionerPerson;
+ protected StringType myPractitionerPersonId;
+
+ @BeforeEach
+ public void before() {
+ super.before();
+ myPractitioner = createPractitionerAndUpdateLinks(new Practitioner());
+ myPractitionerId = new StringType(myPractitioner.getIdElement().getValue());
+ myPractitionerPerson = getPersonFromTarget(myPractitioner);
+ myPractitionerPersonId = new StringType(myPractitionerPerson.getIdElement().getValue());
+ }
+
+ @Test
+ public void testClearAllLinks() {
+ assertLinkCount(2);
+ myEmpiProviderR4.clearEmpiLinks(null, myRequestDetails);
+ assertNoLinksExist();
+ }
+
+ private void assertNoLinksExist() {
+ assertNoPatientLinksExist();
+ assertNoPractitionerLinksExist();
+ }
+
+ private void assertNoPatientLinksExist() {
+ assertThat(getPatientLinks(), hasSize(0));
+ }
+
+ private void assertNoPractitionerLinksExist() {
+ assertThat(getPractitionerLinks(), hasSize(0));
+ }
+
+ @Test
+ public void testClearPatientLinks() {
+ assertLinkCount(2);
+ Person read = myPersonDao.read(new IdDt(myPersonId.getValueAsString()).toVersionless());
+ assertThat(read, is(notNullValue()));
+ myEmpiProviderR4.clearEmpiLinks(new StringType("Patient"), myRequestDetails);
+ assertNoPatientLinksExist();
+ try {
+ myPersonDao.read(new IdDt(myPersonId.getValueAsString()).toVersionless());
+ fail();
+ } catch (ResourceNotFoundException e) {}
+
+ }
+ @Test
+ public void testPersonsWithMultipleHistoricalVersionsCanBeDeleted() {
+ createPatientAndUpdateLinks(buildJanePatient());
+ createPatientAndUpdateLinks(buildJanePatient());
+ createPatientAndUpdateLinks(buildJanePatient());
+ createPatientAndUpdateLinks(buildJanePatient());
+ Patient patientAndUpdateLinks = createPatientAndUpdateLinks(buildJanePatient());
+ Person person = getPersonFromTarget(patientAndUpdateLinks);
+ assertThat(person, is(notNullValue()));
+ myEmpiProviderR4.clearEmpiLinks(null, myRequestDetails);
+ assertNoPatientLinksExist();
+ person = getPersonFromTarget(patientAndUpdateLinks);
+ assertThat(person, is(nullValue()));
+ }
+
+ @Test
+ public void testPersonWithLinksToOtherPersonsCanBeDeleted() {
+ createPatientAndUpdateLinks(buildJanePatient());
+ Patient patientAndUpdateLinks1 = createPatientAndUpdateLinks(buildJanePatient());
+ Patient patientAndUpdateLinks = createPatientAndUpdateLinks(buildPaulPatient());
+
+ Person personFromTarget = getPersonFromTarget(patientAndUpdateLinks);
+ Person personFromTarget2 = getPersonFromTarget(patientAndUpdateLinks1);
+ linkPersons(personFromTarget, personFromTarget2);
+
+ //SUT
+ myEmpiProviderR4.clearEmpiLinks(null, myRequestDetails);
+
+ assertNoPatientLinksExist();
+ IBundleProvider search = myPersonDao.search(new SearchParameterMap().setLoadSynchronous(true));
+ assertThat(search.size(), is(equalTo(0)));
+ }
+
+ @Test
+ public void testPersonsWithCircularReferenceCanBeCleared() {
+ Patient patientAndUpdateLinks = createPatientAndUpdateLinks(buildPaulPatient());
+ Patient patientAndUpdateLinks1 = createPatientAndUpdateLinks(buildJanePatient());
+ Patient patientAndUpdateLinks2 = createPatientAndUpdateLinks(buildFrankPatient());
+
+ Person personFromTarget = getPersonFromTarget(patientAndUpdateLinks);
+ Person personFromTarget1 = getPersonFromTarget(patientAndUpdateLinks1);
+ Person personFromTarget2 = getPersonFromTarget(patientAndUpdateLinks2);
+
+ // A -> B -> C -> A linkages.
+ linkPersons(personFromTarget, personFromTarget1);
+ linkPersons(personFromTarget1, personFromTarget2);
+ linkPersons(personFromTarget2, personFromTarget);
+
+ //SUT
+ Parameters parameters = myEmpiProviderR4.clearEmpiLinks(null, myRequestDetails);
+ assertNoPatientLinksExist();
+ IBundleProvider search = myPersonDao.search(new SearchParameterMap().setLoadSynchronous(true));
+ assertThat(search.size(), is(equalTo(0)));
+
+ }
+
+ private void linkPersons(Person theSourcePerson, Person theTargetPerson) {
+ Person.PersonLinkComponent plc1 = new Person.PersonLinkComponent();
+ plc1.setAssurance(Person.IdentityAssuranceLevel.LEVEL2);
+ plc1.setTarget(new Reference(theTargetPerson.getIdElement().toUnqualifiedVersionless()));
+ theSourcePerson.getLink().add(plc1);
+ myPersonDao.update(theSourcePerson);
+ }
+
+ @Test
+ public void testClearPractitionerLinks() {
+ assertLinkCount(2);
+ Person read = myPersonDao.read(new IdDt(myPractitionerPersonId.getValueAsString()).toVersionless());
+ assertThat(read, is(notNullValue()));
+ myEmpiProviderR4.clearEmpiLinks(new StringType("Practitioner"), myRequestDetails);
+ assertNoPractitionerLinksExist();
+ try {
+ myPersonDao.read(new IdDt(myPractitionerPersonId.getValueAsString()).toVersionless());
+ fail();
+ } catch (ResourceNotFoundException e) {}
+ }
+
+ @Test
+ public void testClearInvalidTargetType() {
+ try {
+ myEmpiProviderR4.clearEmpiLinks(new StringType("Observation"), myRequestDetails);
+ fail();
+ } catch (InvalidRequestException e) {
+ assertThat(e.getMessage(), is(equalTo("$empi-clear does not support resource type: Observation")));
+ }
+ }
+
+ @Nonnull
+ protected List getPractitionerLinks() {
+ return myEmpiLinkDaoSvc.findEmpiLinksByTarget(myPractitioner);
+ }
+}
diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderMatchR4Test.java b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/provider/EmpiProviderMatchR4Test.java
similarity index 51%
rename from hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderMatchR4Test.java
rename to hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/provider/EmpiProviderMatchR4Test.java
index 9b25fb413a6..f7785341f1e 100644
--- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderMatchR4Test.java
+++ b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/provider/EmpiProviderMatchR4Test.java
@@ -1,12 +1,11 @@
-package ca.uhn.fhir.jpa.mdm.provider;
+package ca.uhn.fhir.jpa.empi.provider;
-import ca.uhn.fhir.mdm.api.MdmConstants;
+import ca.uhn.fhir.empi.api.EmpiConstants;
import com.google.common.collect.Ordering;
import org.hl7.fhir.r4.model.Bundle;
+import org.hl7.fhir.r4.model.Enumerations;
import org.hl7.fhir.r4.model.Extension;
-import org.hl7.fhir.r4.model.Medication;
import org.hl7.fhir.r4.model.Patient;
-import org.hl7.fhir.r4.model.StringType;
import org.hl7.fhir.r4.model.codesystems.MatchGrade;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -16,13 +15,11 @@ import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.stream.Collectors;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.*;
-public class MdmProviderMatchR4Test extends BaseProviderR4Test {
+public class EmpiProviderMatchR4Test extends BaseProviderR4Test {
- private static final Logger ourLog = LoggerFactory.getLogger(MdmProviderMatchR4Test.class);
+ private static final Logger ourLog = LoggerFactory.getLogger(EmpiProviderMatchR4Test.class);
public static final String NAME_GIVEN_JANET = NAME_GIVEN_JANE + "t";
@@ -30,6 +27,7 @@ public class MdmProviderMatchR4Test extends BaseProviderR4Test {
@BeforeEach
public void before() {
super.before();
+ super.loadEmpiSearchParameters();
}
@Test
@@ -39,7 +37,7 @@ public class MdmProviderMatchR4Test extends BaseProviderR4Test {
Patient createdJane = createPatient(jane);
Patient newJane = buildJanePatient();
- Bundle result = myMdmProviderR4.match(newJane);
+ Bundle result = myEmpiProviderR4.match(newJane);
assertEquals(1, result.getEntry().size());
Bundle.BundleEntryComponent entry0 = result.getEntry().get(0);
@@ -49,56 +47,7 @@ public class MdmProviderMatchR4Test extends BaseProviderR4Test {
assertEquals(Bundle.SearchEntryMode.MATCH, searchComponent.getMode());
assertEquals(2.0 / 3.0, searchComponent.getScore().doubleValue(), 0.01);
- Extension matchGradeExtension = searchComponent.getExtensionByUrl(MdmConstants.FIHR_STRUCTURE_DEF_MATCH_GRADE_URL_NAMESPACE);
- assertNotNull(matchGradeExtension);
- assertEquals(MatchGrade.CERTAIN.toCode(), matchGradeExtension.getValue().toString());
- }
-
- @Test
- public void testMedicationMatch() throws Exception {
- createDummyOrganization();
-
-
- Medication medication = buildMedication("Organization/mfr");
- Medication createdMedication = createMedication(medication);
- Medication newMedication = buildMedication("Organization/mfr");
-
- Bundle result = myMdmProviderR4.serverMatch(newMedication, new StringType("Medication"));
- assertEquals(1, result.getEntry().size());
-
- Bundle.BundleEntryComponent entry0 = result.getEntry().get(0);
- assertEquals(createdMedication.getId(), entry0.getResource().getId());
-
- Bundle.BundleEntrySearchComponent searchComponent = entry0.getSearch();
- assertEquals(Bundle.SearchEntryMode.MATCH, searchComponent.getMode());
-
- //Since there is only
- assertEquals(1.0 / 1.0, searchComponent.getScore().doubleValue(), 0.01);
- Extension matchGradeExtension = searchComponent.getExtensionByUrl(MdmConstants.FIHR_STRUCTURE_DEF_MATCH_GRADE_URL_NAMESPACE);
- assertNotNull(matchGradeExtension);
- assertEquals(MatchGrade.CERTAIN.toCode(), matchGradeExtension.getValue().toString());
-
- }
-
-
- @Test
- public void testServerLevelMatch() throws Exception {
- Patient jane = buildJanePatient();
- jane.setActive(true);
- Patient createdJane = createPatient(jane);
- Patient newJane = buildJanePatient();
-
- Bundle result = myMdmProviderR4.serverMatch(newJane, new StringType("Patient"));
- assertEquals(1, result.getEntry().size());
-
- Bundle.BundleEntryComponent entry0 = result.getEntry().get(0);
- assertEquals(createdJane.getId(), entry0.getResource().getId());
-
- Bundle.BundleEntrySearchComponent searchComponent = entry0.getSearch();
- assertEquals(Bundle.SearchEntryMode.MATCH, searchComponent.getMode());
-
- assertEquals(2.0 / 3.0, searchComponent.getScore().doubleValue(), 0.01);
- Extension matchGradeExtension = searchComponent.getExtensionByUrl(MdmConstants.FIHR_STRUCTURE_DEF_MATCH_GRADE_URL_NAMESPACE);
+ Extension matchGradeExtension = searchComponent.getExtensionByUrl(EmpiConstants.FIHR_STRUCTURE_DEF_MATCH_GRADE_URL_NAMESPACE);
assertNotNull(matchGradeExtension);
assertEquals(MatchGrade.CERTAIN.toCode(), matchGradeExtension.getValue().toString());
}
@@ -114,7 +63,7 @@ public class MdmProviderMatchR4Test extends BaseProviderR4Test {
Patient newJane = buildJanePatient();
- Bundle result = myMdmProviderR4.match(newJane);
+ Bundle result = myEmpiProviderR4.match(newJane);
assertEquals(2, result.getEntry().size());
Bundle.BundleEntryComponent entry0 = result.getEntry().get(0);
@@ -138,19 +87,19 @@ public class MdmProviderMatchR4Test extends BaseProviderR4Test {
Patient paul = buildPaulPatient();
paul.setActive(true);
- Bundle result = myMdmProviderR4.match(paul);
+ Bundle result = myEmpiProviderR4.match(paul);
assertEquals(0, result.getEntry().size());
}
@Test
public void testMatchWithEmptySearchParamCandidates() throws Exception {
- setMdmRuleJson("mdm/empty-candidate-search-params.json");
+ setEmpiRuleJson("empi/empty-candidate-search-params.json");
Patient jane = buildJanePatient();
jane.setActive(true);
Patient createdJane = createPatient(jane);
Patient newJane = buildJanePatient();
- Bundle result = myMdmProviderR4.match(newJane);
+ Bundle result = myEmpiProviderR4.match(newJane);
assertEquals(1, result.getEntry().size());
assertEquals(createdJane.getId(), result.getEntryFirstRep().getResource().getId());
}
diff --git a/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/provider/EmpiProviderMergePersonsR4Test.java b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/provider/EmpiProviderMergePersonsR4Test.java
new file mode 100644
index 00000000000..aaa7c06a1b6
--- /dev/null
+++ b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/provider/EmpiProviderMergePersonsR4Test.java
@@ -0,0 +1,137 @@
+package ca.uhn.fhir.jpa.empi.provider;
+
+import ca.uhn.fhir.empi.api.EmpiLinkSourceEnum;
+import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
+import ca.uhn.fhir.empi.util.AssuranceLevelUtil;
+import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
+import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
+import org.hl7.fhir.r4.model.Person;
+import org.hl7.fhir.r4.model.StringType;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.endsWith;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+
+public class EmpiProviderMergePersonsR4Test extends BaseProviderR4Test {
+
+ private Person myFromPerson;
+ private StringType myFromPersonId;
+ private Person myToPerson;
+ private StringType myToPersonId;
+
+ @Override
+ @BeforeEach
+ public void before() {
+ super.before();
+ super.loadEmpiSearchParameters();
+
+ myFromPerson = createPerson();
+ myFromPersonId = new StringType(myFromPerson.getIdElement().getValue());
+ myToPerson = createPerson();
+ myToPersonId = new StringType(myToPerson.getIdElement().getValue());
+ }
+
+ @Test
+ public void testMerge() {
+ Person mergedPerson = myEmpiProviderR4.mergePersons(myFromPersonId, myToPersonId, myRequestDetails);
+ assertEquals(myToPerson.getIdElement(), mergedPerson.getIdElement());
+ assertThat(mergedPerson, is(samePersonAs(myToPerson)));
+ assertEquals(2, getAllPersons().size());
+ assertEquals(1, getAllActivePersons().size());
+
+ Person fromPerson = myPersonDao.read(myFromPerson.getIdElement().toUnqualifiedVersionless());
+ assertThat(fromPerson.getActive(), is(false));
+ List links = fromPerson.getLink();
+ assertThat(links, hasSize(1));
+ assertThat(links.get(0).getTarget().getReference(), is (myToPerson.getIdElement().toUnqualifiedVersionless().getValue()));
+ assertThat(links.get(0).getAssurance(), is (AssuranceLevelUtil.getAssuranceLevel(EmpiMatchResultEnum.REDIRECT, EmpiLinkSourceEnum.MANUAL).toR4()));
+ }
+
+ @Test
+ public void testUnmanagedMerge() {
+ StringType fromPersonId = new StringType(createUnmanagedPerson().getIdElement().getValue());
+ StringType toPersonId = new StringType(createUnmanagedPerson().getIdElement().getValue());
+ try {
+ myEmpiProviderR4.mergePersons(fromPersonId, toPersonId, myRequestDetails);
+ fail();
+ } catch (InvalidRequestException e) {
+ assertEquals("Only EMPI managed resources can be merged. Empi managed resource have the HAPI-EMPI tag.", e.getMessage());
+ }
+ }
+
+ @Test
+ public void testMergePatients() {
+ try {
+ StringType patientId = new StringType(createPatient().getIdElement().getValue());
+ StringType otherPatientId = new StringType(createPatient().getIdElement().getValue());
+ myEmpiProviderR4.mergePersons(patientId, otherPatientId, myRequestDetails);
+ fail();
+ } catch (InvalidRequestException e) {
+ assertThat(e.getMessage(), endsWith("must have form Person/ where is the id of the person"));
+ }
+
+ }
+
+ @Test
+ public void testNullParams() {
+ try {
+ myEmpiProviderR4.mergePersons(null, null, myRequestDetails);
+ fail();
+ } catch (InvalidRequestException e) {
+ assertEquals("fromPersonId cannot be null", e.getMessage());
+ }
+ try {
+ myEmpiProviderR4.mergePersons(null, myToPersonId, myRequestDetails);
+ fail();
+ } catch (InvalidRequestException e) {
+ assertEquals("fromPersonId cannot be null", e.getMessage());
+ }
+ try {
+ myEmpiProviderR4.mergePersons(myFromPersonId, null, myRequestDetails);
+ fail();
+ } catch (InvalidRequestException e) {
+ assertEquals("toPersonId cannot be null", e.getMessage());
+ }
+ }
+
+ @Test
+ public void testBadParams() {
+ try {
+ myEmpiProviderR4.mergePersons(new StringType("Patient/1"), new StringType("Patient/2"), myRequestDetails);
+ fail();
+ } catch (InvalidRequestException e) {
+ assertThat(e.getMessage(), endsWith(" must have form Person/ where is the id of the person"));
+ }
+ try {
+ myEmpiProviderR4.mergePersons(myFromPersonId, new StringType("Patient/2"), myRequestDetails);
+ fail();
+ } catch (InvalidRequestException e) {
+ assertThat(e.getMessage(), endsWith(" must have form Person/ where is the id of the person"));
+ }
+ try {
+ myEmpiProviderR4.mergePersons(new StringType("Person/1"), new StringType("Person/1"), myRequestDetails);
+ fail();
+ } catch (InvalidRequestException e) {
+ assertEquals("fromPersonId must be different from toPersonId", e.getMessage());
+ }
+ try {
+ myEmpiProviderR4.mergePersons(new StringType("Person/abc"), myToPersonId, myRequestDetails);
+ fail();
+ } catch (ResourceNotFoundException e) {
+ assertEquals("Resource Person/abc is not known", e.getMessage());
+ }
+ try {
+ myEmpiProviderR4.mergePersons(myFromPersonId, new StringType("Person/abc"), myRequestDetails);
+ fail();
+ } catch (ResourceNotFoundException e) {
+ assertEquals("Resource Person/abc is not known", e.getMessage());
+ }
+ }
+}
diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderQueryLinkR4Test.java b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/provider/EmpiProviderQueryLinkR4Test.java
similarity index 54%
rename from hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderQueryLinkR4Test.java
rename to hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/provider/EmpiProviderQueryLinkR4Test.java
index db4a22829df..276119ba1ab 100644
--- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderQueryLinkR4Test.java
+++ b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/provider/EmpiProviderQueryLinkR4Test.java
@@ -1,16 +1,15 @@
-package ca.uhn.fhir.jpa.mdm.provider;
+package ca.uhn.fhir.jpa.empi.provider;
-import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
-import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
-import ca.uhn.fhir.jpa.entity.MdmLink;
+import ca.uhn.fhir.empi.api.EmpiLinkSourceEnum;
+import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
+import ca.uhn.fhir.jpa.entity.EmpiLink;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
-import org.hl7.fhir.instance.model.api.IAnyResource;
-import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.BooleanType;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.Parameters;
import org.hl7.fhir.r4.model.Patient;
+import org.hl7.fhir.r4.model.Person;
import org.hl7.fhir.r4.model.StringType;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -26,11 +25,11 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
-public class MdmProviderQueryLinkR4Test extends BaseLinkR4Test {
- private static final Logger ourLog = LoggerFactory.getLogger(MdmProviderQueryLinkR4Test.class);
+public class EmpiProviderQueryLinkR4Test extends BaseLinkR4Test {
+ private static final Logger ourLog = LoggerFactory.getLogger(EmpiProviderQueryLinkR4Test.class);
private StringType myLinkSource;
- private StringType myGoldenResource1Id;
- private StringType myGoldenResource2Id;
+ private StringType myPerson1Id;
+ private StringType myPerson2Id;
@Override
@BeforeEach
@@ -41,27 +40,27 @@ public class MdmProviderQueryLinkR4Test extends BaseLinkR4Test {
createPatientAndUpdateLinks(buildJanePatient());
// Add a possible duplicate
- myLinkSource = new StringType(MdmLinkSourceEnum.AUTO.name());
- Patient sourcePatient1 = createGoldenPatient();
- myGoldenResource1Id = new StringType(sourcePatient1.getIdElement().toVersionless().getValue());
- Long sourcePatient1Pid = myIdHelperService.getPidOrNull(sourcePatient1);
- Patient sourcePatient2 = createGoldenPatient();
- myGoldenResource2Id = new StringType(sourcePatient2.getIdElement().toVersionless().getValue());
- Long sourcePatient2Pid = myIdHelperService.getPidOrNull(sourcePatient2);
+ myLinkSource = new StringType(EmpiLinkSourceEnum.AUTO.name());
+ Person person1 = createPerson();
+ myPerson1Id = new StringType(person1.getIdElement().toVersionless().getValue());
+ Long person1Pid = myIdHelperService.getPidOrNull(person1);
+ Person person2 = createPerson();
+ myPerson2Id = new StringType(person2.getIdElement().toVersionless().getValue());
+ Long person2Pid = myIdHelperService.getPidOrNull(person2);
- MdmLink possibleDuplicateMdmLink = myMdmLinkDaoSvc.newMdmLink().setGoldenResourcePid(sourcePatient1Pid).setSourcePid(sourcePatient2Pid).setMatchResult(MdmMatchResultEnum.POSSIBLE_DUPLICATE).setLinkSource(MdmLinkSourceEnum.AUTO);
- saveLink(possibleDuplicateMdmLink);
+ EmpiLink possibleDuplicateEmpiLink = myEmpiLinkDaoSvc.newEmpiLink().setPersonPid(person1Pid).setTargetPid(person2Pid).setMatchResult(EmpiMatchResultEnum.POSSIBLE_DUPLICATE).setLinkSource(EmpiLinkSourceEnum.AUTO);
+ saveLink(possibleDuplicateEmpiLink);
}
@Test
public void testQueryLinkOneMatch() {
- Parameters result = myMdmProviderR4.queryLinks(mySourcePatientId, myPatientId, null, null, myRequestDetails);
+ Parameters result = myEmpiProviderR4.queryLinks(myPersonId, myPatientId, null, null, myRequestDetails);
ourLog.info(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(result));
List list = result.getParameter();
assertThat(list, hasSize(1));
List part = list.get(0).getPart();
- assertMdmLink(7, part, mySourcePatientId.getValue(), myPatientId.getValue(), MdmMatchResultEnum.POSSIBLE_MATCH, "false", "true", null);
+ assertEmpiLink(7, part, myPersonId.getValue(), myPatientId.getValue(), EmpiMatchResultEnum.POSSIBLE_MATCH, "false", "true", null);
}
@Test
@@ -69,41 +68,41 @@ public class MdmProviderQueryLinkR4Test extends BaseLinkR4Test {
// Add a third patient
Patient patient = createPatientAndUpdateLinks(buildJanePatient());
IdType patientId = patient.getIdElement().toVersionless();
- IAnyResource goldenResource = getGoldenResourceFromTargetResource(patient);
- IIdType goldenResourceId = goldenResource.getIdElement().toVersionless();
+ Person person = getPersonFromTarget(patient);
+ IdType personId = person.getIdElement().toVersionless();
- Parameters result = myMdmProviderR4.queryLinks(null, null, null, myLinkSource, myRequestDetails);
+ Parameters result = myEmpiProviderR4.queryLinks(null, null, null, myLinkSource, myRequestDetails);
ourLog.info(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(result));
List list = result.getParameter();
assertThat(list, hasSize(3));
List part = list.get(2).getPart();
- assertMdmLink(7, part, goldenResourceId.getValue(), patientId.getValue(), MdmMatchResultEnum.MATCH, "false", "false", "2");
+ assertEmpiLink(7, part, personId.getValue(), patientId.getValue(), EmpiMatchResultEnum.MATCH, "false", "false", "2");
}
@Test
public void testQueryPossibleDuplicates() {
- Parameters result = myMdmProviderR4.getDuplicateGoldenResources(myRequestDetails);
+ Parameters result = myEmpiProviderR4.getDuplicatePersons(myRequestDetails);
ourLog.info(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(result));
List list = result.getParameter();
assertThat(list, hasSize(1));
List part = list.get(0).getPart();
- assertMdmLink(2, part, myGoldenResource1Id.getValue(), myGoldenResource2Id.getValue(), MdmMatchResultEnum.POSSIBLE_DUPLICATE, "false", "false", null);
+ assertEmpiLink(2, part, myPerson1Id.getValue(), myPerson2Id.getValue(), EmpiMatchResultEnum.POSSIBLE_DUPLICATE, "false", "false", null);
}
@Test
public void testNotDuplicate() {
{
- Parameters result = myMdmProviderR4.getDuplicateGoldenResources(myRequestDetails);
+ Parameters result = myEmpiProviderR4.getDuplicatePersons(myRequestDetails);
List list = result.getParameter();
assertThat(list, hasSize(1));
}
{
- Parameters result = myMdmProviderR4.notDuplicate(myGoldenResource1Id, myGoldenResource2Id, myRequestDetails);
+ Parameters result = myEmpiProviderR4.notDuplicate(myPerson1Id, myPerson2Id, myRequestDetails);
ourLog.info(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(result));
assertEquals("success", result.getParameterFirstRep().getName());
assertTrue(((BooleanType) (result.getParameterFirstRep().getValue())).booleanValue());
}
- Parameters result = myMdmProviderR4.getDuplicateGoldenResources(myRequestDetails);
+ Parameters result = myEmpiProviderR4.getDuplicatePersons(myRequestDetails);
List list = result.getParameter();
assertThat(list, hasSize(0));
}
@@ -111,18 +110,18 @@ public class MdmProviderQueryLinkR4Test extends BaseLinkR4Test {
@Test
public void testNotDuplicateBadId() {
try {
- myMdmProviderR4.notDuplicate(myGoldenResource1Id, new StringType("Patient/notAnId123"), myRequestDetails);
+ myEmpiProviderR4.notDuplicate(myPerson1Id, new StringType("Person/notAnId123"), myRequestDetails);
fail();
} catch (ResourceNotFoundException e) {
- assertEquals("Resource Patient/notAnId123 is not known", e.getMessage());
+ assertEquals("Resource Person/notAnId123 is not known", e.getMessage());
}
}
- private void assertMdmLink(int theExpectedSize, List thePart, String theGoldenResourceId, String theTargetId, MdmMatchResultEnum theMatchResult, String theEidMatch, String theNewGoldenResource, String theScore) {
+ private void assertEmpiLink(int theExpectedSize, List thePart, String thePersonId, String theTargetId, EmpiMatchResultEnum theMatchResult, String theEidMatch, String theNewPerson, String theScore) {
assertThat(thePart, hasSize(theExpectedSize));
- assertThat(thePart.get(0).getName(), is("goldenResourceId"));
- assertThat(thePart.get(0).getValue().toString(), is(removeVersion(theGoldenResourceId)));
- assertThat(thePart.get(1).getName(), is("sourceResourceId"));
+ assertThat(thePart.get(0).getName(), is("personId"));
+ assertThat(thePart.get(0).getValue().toString(), is(removeVersion(thePersonId)));
+ assertThat(thePart.get(1).getName(), is("targetId"));
assertThat(thePart.get(1).getValue().toString(), is(removeVersion(theTargetId)));
if (theExpectedSize > 2) {
assertThat(thePart.get(2).getName(), is("matchResult"));
@@ -133,8 +132,8 @@ public class MdmProviderQueryLinkR4Test extends BaseLinkR4Test {
assertThat(thePart.get(4).getName(), is("eidMatch"));
assertThat(thePart.get(4).getValue().primitiveValue(), is(theEidMatch));
- assertThat(thePart.get(5).getName(), is("hadToCreateNewResource"));
- assertThat(thePart.get(5).getValue().primitiveValue(), is(theNewGoldenResource));
+ assertThat(thePart.get(5).getName(), is("newPerson"));
+ assertThat(thePart.get(5).getValue().primitiveValue(), is(theNewPerson));
assertThat(thePart.get(6).getName(), is("score"));
assertThat(thePart.get(6).getValue().primitiveValue(), is(theScore));
diff --git a/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/provider/EmpiProviderUpdateLinkR4Test.java b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/provider/EmpiProviderUpdateLinkR4Test.java
new file mode 100644
index 00000000000..01ee296f568
--- /dev/null
+++ b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/provider/EmpiProviderUpdateLinkR4Test.java
@@ -0,0 +1,142 @@
+package ca.uhn.fhir.jpa.empi.provider;
+
+import ca.uhn.fhir.empi.api.EmpiConstants;
+import ca.uhn.fhir.empi.api.EmpiLinkSourceEnum;
+import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
+import ca.uhn.fhir.jpa.entity.EmpiLink;
+import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
+import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
+import org.hl7.fhir.r4.model.Patient;
+import org.hl7.fhir.r4.model.Person;
+import org.hl7.fhir.r4.model.StringType;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.endsWith;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.matchesPattern;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+
+public class EmpiProviderUpdateLinkR4Test extends BaseLinkR4Test {
+ @Test
+ public void testUpdateLinkNoMatch() {
+ assertLinkCount(1);
+ myEmpiProviderR4.updateLink(myPersonId, myPatientId, NO_MATCH_RESULT, myRequestDetails);
+ assertLinkCount(2);
+
+ List links = getPatientLinks();
+ assertEquals(EmpiLinkSourceEnum.MANUAL, links.get(0).getLinkSource());
+ assertEquals(EmpiMatchResultEnum.NO_MATCH, links.get(0).getMatchResult());
+ assertEquals(EmpiLinkSourceEnum.AUTO, links.get(1).getLinkSource());
+ assertEquals(EmpiMatchResultEnum.MATCH, links.get(1).getMatchResult());
+ assertNotEquals(links.get(0).getPersonPid(), links.get(1).getPersonPid());
+ }
+
+ @Test
+ public void testUpdateLinkMatch() {
+ assertLinkCount(1);
+ myEmpiProviderR4.updateLink(myPersonId, myPatientId, MATCH_RESULT, myRequestDetails);
+ assertLinkCount(1);
+
+ List links = getPatientLinks();
+ assertEquals(EmpiLinkSourceEnum.MANUAL, links.get(0).getLinkSource());
+ assertEquals(EmpiMatchResultEnum.MATCH, links.get(0).getMatchResult());
+ }
+
+ @Test
+ public void testUpdateLinkTwiceFailsDueToWrongVersion() {
+ myEmpiProviderR4.updateLink(myPersonId, myPatientId, MATCH_RESULT, myRequestDetails);
+ try {
+ myEmpiProviderR4.updateLink(myPersonId, myPatientId, NO_MATCH_RESULT, myRequestDetails);
+ fail();
+ } catch (ResourceVersionConflictException e) {
+ assertThat(e.getMessage(), matchesPattern("Requested resource Person/\\d+/_history/1 is not the latest version. Latest version is Person/\\d+/_history/2"));
+ }
+ }
+
+ @Test
+ public void testUpdateLinkTwiceWorksWhenNoVersionProvided() {
+ myEmpiProviderR4.updateLink(myPersonId, myPatientId, MATCH_RESULT, myRequestDetails);
+ Person person = myEmpiProviderR4.updateLink(myVersionlessPersonId, myPatientId, NO_MATCH_RESULT, myRequestDetails);
+ assertThat(person.getLink(), hasSize(0));
+ }
+
+ @Test
+ public void testUnlinkLink() {
+ myEmpiProviderR4.updateLink(myPersonId, myPatientId, NO_MATCH_RESULT, myRequestDetails);
+ try {
+ myEmpiProviderR4.updateLink(myPersonId, myPatientId, MATCH_RESULT, myRequestDetails);
+ fail();
+ } catch (ResourceVersionConflictException e) {
+ assertThat(e.getMessage(), matchesPattern("Requested resource Person/\\d+/_history/1 is not the latest version. Latest version is Person/\\d+/_history/2"));
+ }
+ }
+
+ @Test
+ public void testUpdateIllegalResultPM() {
+ try {
+ myEmpiProviderR4.updateLink(myPersonId, myPatientId, POSSIBLE_MATCH_RESULT, myRequestDetails);
+ fail();
+ } catch (InvalidRequestException e) {
+ assertEquals("$empi-update-link illegal matchResult value 'POSSIBLE_MATCH'. Must be NO_MATCH or MATCH", e.getMessage());
+ }
+ }
+
+ @Test
+ public void testUpdateIllegalResultPD() {
+ try {
+ myEmpiProviderR4.updateLink(myPersonId, myPatientId, POSSIBLE_DUPLICATE_RESULT, myRequestDetails);
+ fail();
+ } catch (InvalidRequestException e) {
+ assertEquals("$empi-update-link illegal matchResult value 'POSSIBLE_DUPLICATE'. Must be NO_MATCH or MATCH", e.getMessage());
+ }
+ }
+
+ @Test
+ public void testUpdateIllegalFirstArg() {
+ try {
+ myEmpiProviderR4.updateLink(myPatientId, myPatientId, NO_MATCH_RESULT, myRequestDetails);
+ fail();
+ } catch (InvalidRequestException e) {
+ assertThat(e.getMessage(), endsWith(" must have form Person/ where is the id of the person"));
+ }
+ }
+
+ @Test
+ public void testUpdateIllegalSecondArg() {
+ try {
+ myEmpiProviderR4.updateLink(myPersonId, myPersonId, NO_MATCH_RESULT, myRequestDetails);
+ fail();
+ } catch (InvalidRequestException e) {
+ assertThat(e.getMessage(), endsWith("must have form Patient/ or Practitioner/ where is the id of the resource"));
+ }
+ }
+
+ @Test
+ public void testUpdateStrangePerson() {
+ Person person = createUnmanagedPerson();
+ try {
+ myEmpiProviderR4.updateLink(new StringType(person.getIdElement().getValue()), myPatientId, NO_MATCH_RESULT, myRequestDetails);
+ fail();
+ } catch (InvalidRequestException e) {
+ assertEquals("Only EMPI Managed Person resources may be updated via this operation. The Person resource provided is not tagged as managed by hapi-empi", e.getMessage());
+ }
+ }
+
+ @Test
+ public void testExcludedPerson() {
+ Patient patient = new Patient();
+ patient.getMeta().addTag().setSystem(EmpiConstants.SYSTEM_EMPI_MANAGED).setCode(EmpiConstants.CODE_NO_EMPI_MANAGED);
+ createPatient(patient);
+ try {
+ myEmpiProviderR4.updateLink(myPersonId, new StringType(patient.getIdElement().getValue()), NO_MATCH_RESULT, myRequestDetails);
+ fail();
+ } catch (InvalidRequestException e) {
+ assertEquals("The target is marked with the " + EmpiConstants.CODE_NO_EMPI_MANAGED + " tag which means it may not be EMPI linked.", e.getMessage());
+ }
+ }
+}
diff --git a/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/searchparam/SearchParameterTest.java b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/searchparam/SearchParameterTest.java
new file mode 100644
index 00000000000..5343272192a
--- /dev/null
+++ b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/searchparam/SearchParameterTest.java
@@ -0,0 +1,56 @@
+package ca.uhn.fhir.jpa.empi.searchparam;
+
+import ca.uhn.fhir.jpa.empi.BaseEmpiR4Test;
+import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
+import ca.uhn.fhir.rest.api.server.IBundleProvider;
+import ca.uhn.fhir.rest.param.TokenParam;
+import org.hl7.fhir.r4.model.Patient;
+import org.hl7.fhir.r4.model.Person;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class SearchParameterTest extends BaseEmpiR4Test {
+ private static final Logger ourLog = LoggerFactory.getLogger(SearchParameterTest.class);
+
+ @BeforeEach
+ public void before() {
+ super.loadEmpiSearchParameters();
+ }
+
+ @Test
+ public void testCanFindPossibleMatches() {
+ // Create a possible match
+ Patient patient = buildJanePatient();
+ patient.getNameFirstRep().setFamily("familyone");
+ patient = createPatientAndUpdateLinks(patient);
+
+ Patient patient2 = buildJanePatient();
+ patient2.getNameFirstRep().setFamily("pleasedonotmatchatall");
+ patient2 = createPatientAndUpdateLinks(patient2);
+
+ assertThat(patient2, is(possibleMatchWith(patient)));
+ // Now confirm we can find it using our custom search parameter
+
+ SearchParameterMap map = new SearchParameterMap();
+ map.setLoadSynchronous(true);
+ map.add("assurance", new TokenParam(Person.IdentityAssuranceLevel.LEVEL2.toCode()));
+ IBundleProvider result = myPersonDao.search(map);
+
+ assertEquals(1, result.size().intValue());
+ Person person = (Person) result.getResources(0, 1).get(0);
+ String encoded = myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(person);
+ ourLog.info("Search result: {}", encoded);
+ List links = person.getLink();
+ assertEquals(2, links.size());
+ assertEquals(Person.IdentityAssuranceLevel.LEVEL2, links.get(0).getAssurance());
+ assertEquals(Person.IdentityAssuranceLevel.LEVEL1, links.get(1).getAssurance());
+ }
+}
diff --git a/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/svc/EmpiBatchSvcImplTest.java b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/svc/EmpiBatchSvcImplTest.java
new file mode 100644
index 00000000000..3961ce70b01
--- /dev/null
+++ b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/svc/EmpiBatchSvcImplTest.java
@@ -0,0 +1,99 @@
+package ca.uhn.fhir.jpa.empi.svc;
+
+import ca.uhn.fhir.empi.api.IEmpiSubmitSvc;
+import ca.uhn.fhir.interceptor.api.IInterceptorService;
+import ca.uhn.fhir.interceptor.api.Pointcut;
+import ca.uhn.fhir.jpa.empi.BaseEmpiR4Test;
+import ca.uhn.test.concurrency.PointcutLatch;
+import org.apache.commons.lang3.time.DateUtils;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.io.IOException;
+import java.util.Date;
+
+class EmpiBatchSvcImplTest extends BaseEmpiR4Test {
+
+ @Autowired
+ IEmpiSubmitSvc myEmpiSubmitSvc;
+
+ @Autowired
+ IInterceptorService myInterceptorService;
+
+ PointcutLatch afterEmpiLatch = new PointcutLatch(Pointcut.EMPI_AFTER_PERSISTED_RESOURCE_CHECKED);
+
+ @BeforeEach
+ public void before() {
+ myInterceptorService.registerAnonymousInterceptor(Pointcut.EMPI_AFTER_PERSISTED_RESOURCE_CHECKED, afterEmpiLatch);
+ }
+ @AfterEach
+ public void after() throws IOException {
+ myInterceptorService.unregisterInterceptor(afterEmpiLatch);
+ afterEmpiLatch.clear();
+ super.after();
+ }
+
+ @Test
+ public void testEmpiBatchRunWorksOverMultipleTargetTypes() throws InterruptedException {
+
+ for (int i =0; i < 10; i++) {
+ createPatient(buildJanePatient());
+ }
+
+ for(int i = 0; i< 10; i++) {
+ createPractitioner(buildPractitionerWithNameAndId("test", "id"));
+ }
+
+ assertLinkCount(0);
+
+ //SUT
+ afterEmpiLatch.runWithExpectedCount(20, () -> myEmpiSubmitSvc.submitAllTargetTypesToEmpi(null));
+
+ assertLinkCount(20);
+ }
+
+ @Test
+ public void testEmpiBatchOnPatientType() throws Exception {
+
+ for (int i =0; i < 10; i++) {
+ createPatient(buildPatientWithNameAndId("test", "id"));
+ }
+
+ assertLinkCount(0);
+
+ //SUT
+ afterEmpiLatch.runWithExpectedCount(10, () -> myEmpiSubmitSvc.submitTargetTypeToEmpi("Patient", null));
+
+ assertLinkCount(10);
+ }
+
+ @Test
+ public void testEmpiBatchOnPractitionerType() throws Exception {
+
+ for (int i =0; i < 10; i++) {
+ createPractitioner(buildPractitionerWithNameAndId("test", "id"));
+ }
+
+ assertLinkCount(0);
+
+ //SUT
+ afterEmpiLatch.runWithExpectedCount(10, () -> myEmpiSubmitSvc.submitAllTargetTypesToEmpi(null));
+
+ assertLinkCount(10);
+ }
+
+ @Test
+ public void testEmpiOnTargetTypeWithCriteria() throws InterruptedException {
+ createPatient(buildPatientWithNameIdAndBirthday("gary", "gary_id", new Date()));
+ createPatient(buildPatientWithNameIdAndBirthday("john", "john_id", DateUtils.addDays(new Date(), -300)));
+
+ assertLinkCount(0);
+
+ //SUT
+ afterEmpiLatch.runWithExpectedCount(1, () -> myEmpiSubmitSvc.submitAllTargetTypesToEmpi("Patient?name=gary"));
+
+ assertLinkCount(1);
+ }
+}
diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmCandidateSearchCriteriaBuilderSvcTest.java b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/svc/EmpiCandidateSearchCriteriaBuilderSvcTest.java
similarity index 59%
rename from hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmCandidateSearchCriteriaBuilderSvcTest.java
rename to hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/svc/EmpiCandidateSearchCriteriaBuilderSvcTest.java
index db439e71cf9..457d4737d75 100644
--- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmCandidateSearchCriteriaBuilderSvcTest.java
+++ b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/svc/EmpiCandidateSearchCriteriaBuilderSvcTest.java
@@ -1,8 +1,8 @@
-package ca.uhn.fhir.jpa.mdm.svc;
+package ca.uhn.fhir.jpa.empi.svc;
-import ca.uhn.fhir.mdm.rules.json.MdmResourceSearchParamJson;
-import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
-import ca.uhn.fhir.jpa.mdm.svc.candidate.MdmCandidateSearchCriteriaBuilderSvc;
+import ca.uhn.fhir.empi.rules.json.EmpiResourceSearchParamJson;
+import ca.uhn.fhir.jpa.empi.BaseEmpiR4Test;
+import ca.uhn.fhir.jpa.empi.svc.candidate.EmpiCandidateSearchCriteriaBuilderSvc;
import org.hl7.fhir.r4.model.HumanName;
import org.hl7.fhir.r4.model.Patient;
import org.junit.jupiter.api.Test;
@@ -20,16 +20,16 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
-public class MdmCandidateSearchCriteriaBuilderSvcTest extends BaseMdmR4Test {
+public class EmpiCandidateSearchCriteriaBuilderSvcTest extends BaseEmpiR4Test {
@Autowired
- MdmCandidateSearchCriteriaBuilderSvc myMdmCandidateSearchCriteriaBuilderSvc;
+ EmpiCandidateSearchCriteriaBuilderSvc myEmpiCandidateSearchCriteriaBuilderSvc;
@Test
public void testEmptyCase() {
Patient patient = new Patient();
- MdmResourceSearchParamJson searchParamJson = new MdmResourceSearchParamJson();
+ EmpiResourceSearchParamJson searchParamJson = new EmpiResourceSearchParamJson();
searchParamJson.addSearchParam("family");
- Optional result = myMdmCandidateSearchCriteriaBuilderSvc.buildResourceQueryString("Patient", patient, Collections.emptyList(), searchParamJson);
+ Optional result = myEmpiCandidateSearchCriteriaBuilderSvc.buildResourceQueryString("Patient", patient, Collections.emptyList(), searchParamJson);
assertFalse(result.isPresent());
}
@@ -37,9 +37,9 @@ public class MdmCandidateSearchCriteriaBuilderSvcTest extends BaseMdmR4Test {
public void testSimpleCase() {
Patient patient = new Patient();
patient.addName().setFamily("Fernandez");
- MdmResourceSearchParamJson searchParamJson = new MdmResourceSearchParamJson();
+ EmpiResourceSearchParamJson searchParamJson = new EmpiResourceSearchParamJson();
searchParamJson.addSearchParam("family");
- Optional result = myMdmCandidateSearchCriteriaBuilderSvc.buildResourceQueryString("Patient", patient, Collections.emptyList(), searchParamJson);
+ Optional result = myEmpiCandidateSearchCriteriaBuilderSvc.buildResourceQueryString("Patient", patient, Collections.emptyList(), searchParamJson);
assertTrue(result.isPresent());
assertEquals("Patient?family=Fernandez", result.get());
}
@@ -51,10 +51,10 @@ public class MdmCandidateSearchCriteriaBuilderSvcTest extends BaseMdmR4Test {
humanName.addGiven("Jose");
humanName.addGiven("Martin");
humanName.setFamily("Fernandez");
- MdmResourceSearchParamJson searchParamJson = new MdmResourceSearchParamJson();
+ EmpiResourceSearchParamJson searchParamJson = new EmpiResourceSearchParamJson();
searchParamJson.addSearchParam("given");
searchParamJson.addSearchParam("family");
- Optional result = myMdmCandidateSearchCriteriaBuilderSvc.buildResourceQueryString("Patient", patient, Collections.emptyList(), searchParamJson);
+ Optional result = myEmpiCandidateSearchCriteriaBuilderSvc.buildResourceQueryString("Patient", patient, Collections.emptyList(), searchParamJson);
assertTrue(result.isPresent());
assertThat(result.get(), anyOf(equalTo("Patient?given=Jose,Martin&family=Fernandez"), equalTo("Patient?given=Martin,Jose&family=Fernandez")));
}
@@ -63,9 +63,9 @@ public class MdmCandidateSearchCriteriaBuilderSvcTest extends BaseMdmR4Test {
public void testIdentifier() {
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:oid:1.2.36.146.595.217.0.1").setValue("12345");
- MdmResourceSearchParamJson searchParamJson = new MdmResourceSearchParamJson();
+ EmpiResourceSearchParamJson searchParamJson = new EmpiResourceSearchParamJson();
searchParamJson.addSearchParam("identifier");
- Optional result = myMdmCandidateSearchCriteriaBuilderSvc.buildResourceQueryString("Patient", patient, Collections.emptyList(), searchParamJson);
+ Optional result = myEmpiCandidateSearchCriteriaBuilderSvc.buildResourceQueryString("Patient", patient, Collections.emptyList(), searchParamJson);
assertTrue(result.isPresent());
assertEquals(result.get(), "Patient?identifier=urn%3Aoid%3A1.2.36.146.595.217.0.1%7C12345");
}
@@ -74,9 +74,9 @@ public class MdmCandidateSearchCriteriaBuilderSvcTest extends BaseMdmR4Test {
public void testIdentifierSpaceIsEscaped() {
Patient patient = new Patient();
patient.addIdentifier().setSystem("urn:oid:1.2.36.146.595.217.0.1").setValue("abc def");
- MdmResourceSearchParamJson searchParamJson = new MdmResourceSearchParamJson();
+ EmpiResourceSearchParamJson searchParamJson = new EmpiResourceSearchParamJson();
searchParamJson.addSearchParam("identifier");
- Optional result = myMdmCandidateSearchCriteriaBuilderSvc.buildResourceQueryString("Patient", patient, Collections.emptyList(), searchParamJson);
+ Optional result = myEmpiCandidateSearchCriteriaBuilderSvc.buildResourceQueryString("Patient", patient, Collections.emptyList(), searchParamJson);
assertTrue(result.isPresent());
assertEquals("Patient?identifier=urn%3Aoid%3A1.2.36.146.595.217.0.1%7Cabc%20def", result.get());
}
@@ -84,7 +84,7 @@ public class MdmCandidateSearchCriteriaBuilderSvcTest extends BaseMdmR4Test {
@Test
public void testOmittingCandidateSearchParamsIsAllowed() {
Patient patient = new Patient();
- Optional result = myMdmCandidateSearchCriteriaBuilderSvc.buildResourceQueryString("Patient", patient, Collections.emptyList(), null);
+ Optional result = myEmpiCandidateSearchCriteriaBuilderSvc.buildResourceQueryString("Patient", patient, Collections.emptyList(), null);
assertThat(result.isPresent(), is(true));
assertThat(result.get(), is(equalTo("Patient?")));
}
@@ -93,7 +93,7 @@ public class MdmCandidateSearchCriteriaBuilderSvcTest extends BaseMdmR4Test {
public void testEmptyCandidateSearchParamsWorksInConjunctionWithFilterParams() {
Patient patient = new Patient();
List filterParams = Collections.singletonList("active=true");
- Optional result = myMdmCandidateSearchCriteriaBuilderSvc.buildResourceQueryString("Patient", patient, filterParams, null);
+ Optional result = myEmpiCandidateSearchCriteriaBuilderSvc.buildResourceQueryString("Patient", patient, filterParams, null);
assertThat(result.isPresent(), is(true));
assertThat(result.get(), is(equalTo("Patient?active=true")));
}
diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmCandidateSearchSvcTest.java b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/svc/EmpiCandidateSearchSvcTest.java
similarity index 75%
rename from hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmCandidateSearchSvcTest.java
rename to hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/svc/EmpiCandidateSearchSvcTest.java
index 55288727bb1..17c56fb94e7 100644
--- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmCandidateSearchSvcTest.java
+++ b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/svc/EmpiCandidateSearchSvcTest.java
@@ -1,7 +1,7 @@
-package ca.uhn.fhir.jpa.mdm.svc;
+package ca.uhn.fhir.jpa.empi.svc;
-import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
-import ca.uhn.fhir.jpa.mdm.svc.candidate.MdmCandidateSearchSvc;
+import ca.uhn.fhir.jpa.empi.BaseEmpiR4Test;
+import ca.uhn.fhir.jpa.empi.svc.candidate.EmpiCandidateSearchSvc;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.Practitioner;
@@ -17,10 +17,10 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
import static org.junit.jupiter.api.Assertions.assertEquals;
-public class MdmCandidateSearchSvcTest extends BaseMdmR4Test {
+public class EmpiCandidateSearchSvcTest extends BaseEmpiR4Test {
@Autowired
- MdmCandidateSearchSvc myMdmCandidateSearchSvc;
+ EmpiCandidateSearchSvc myEmpiCandidateSearchSvc;
@Test
public void testFindCandidates() {
@@ -29,7 +29,7 @@ public class MdmCandidateSearchSvcTest extends BaseMdmR4Test {
createPatient(jane);
Patient newJane = buildJanePatient();
- Collection result = myMdmCandidateSearchSvc.findCandidates("Patient", newJane);
+ Collection result = myEmpiCandidateSearchSvc.findCandidates("Patient", newJane);
assertEquals(1, result.size());
}
@@ -44,7 +44,7 @@ public class MdmCandidateSearchSvcTest extends BaseMdmR4Test {
Patient newJane = buildJaneWithBirthday(today);
- Collection result = myMdmCandidateSearchSvc.findCandidates("Patient", newJane);
+ Collection result = myEmpiCandidateSearchSvc.findCandidates("Patient", newJane);
assertEquals(1, result.size());
}
@@ -62,7 +62,7 @@ public class MdmCandidateSearchSvcTest extends BaseMdmR4Test {
incomingPatient.setActive(true);
incomingPatient.setGeneralPractitioner(Collections.singletonList(new Reference(practitionerAndUpdateLinks.getId())));
- Collection patient = myMdmCandidateSearchSvc.findCandidates("Patient", incomingPatient);
+ Collection patient = myEmpiCandidateSearchSvc.findCandidates("Patient", incomingPatient);
assertThat(patient, hasSize(1));
}
}
diff --git a/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/svc/EmpiLinkSvcTest.java b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/svc/EmpiLinkSvcTest.java
new file mode 100644
index 00000000000..98591556def
--- /dev/null
+++ b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/svc/EmpiLinkSvcTest.java
@@ -0,0 +1,167 @@
+package ca.uhn.fhir.jpa.empi.svc;
+
+import ca.uhn.fhir.empi.api.EmpiLinkSourceEnum;
+import ca.uhn.fhir.empi.api.EmpiMatchOutcome;
+import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
+import ca.uhn.fhir.empi.api.IEmpiLinkSvc;
+import ca.uhn.fhir.jpa.empi.BaseEmpiR4Test;
+import ca.uhn.fhir.jpa.entity.EmpiLink;
+import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
+import org.hl7.fhir.r4.model.IdType;
+import org.hl7.fhir.r4.model.Patient;
+import org.hl7.fhir.r4.model.Person;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.io.IOException;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+public class EmpiLinkSvcTest extends BaseEmpiR4Test {
+ private static final EmpiMatchOutcome POSSIBLE_MATCH = new EmpiMatchOutcome(null, null).setMatchResultEnum(EmpiMatchResultEnum.POSSIBLE_MATCH);
+ @Autowired
+ IEmpiLinkSvc myEmpiLinkSvc;
+
+ @Override
+ @AfterEach
+ public void after() throws IOException {
+ myExpungeEverythingService.expungeEverythingByType(EmpiLink.class);
+ super.after();
+ }
+
+ @Test
+ public void compareEmptyPatients() {
+ Patient patient = new Patient();
+ patient.setId("Patient/1");
+ EmpiMatchResultEnum result = myEmpiResourceMatcherSvc.getMatchResult(patient, patient).getMatchResultEnum();
+ assertEquals(EmpiMatchResultEnum.NO_MATCH, result);
+ }
+
+ @Test
+ public void testCreateRemoveLink() {
+ assertLinkCount(0);
+ Person person = createPerson();
+ IdType personId = person.getIdElement().toUnqualifiedVersionless();
+ assertEquals(0, person.getLink().size());
+ Patient patient = createPatient();
+
+ {
+ myEmpiLinkSvc.updateLink(person, patient, POSSIBLE_MATCH, EmpiLinkSourceEnum.AUTO, createContextForCreate());
+ assertLinkCount(1);
+ Person newPerson = myPersonDao.read(personId);
+ assertEquals(1, newPerson.getLink().size());
+ }
+
+ {
+ myEmpiLinkSvc.updateLink(person, patient, EmpiMatchOutcome.NO_MATCH, EmpiLinkSourceEnum.MANUAL, createContextForCreate());
+ assertLinkCount(1);
+ Person newPerson = myPersonDao.read(personId);
+ assertEquals(0, newPerson.getLink().size());
+ }
+ }
+
+
+ @Test
+ public void testPossibleDuplicate() {
+ assertLinkCount(0);
+ Person person = createPerson();
+ Person target = createPerson();
+
+ myEmpiLinkSvc.updateLink(person, target, EmpiMatchOutcome.POSSIBLE_DUPLICATE, EmpiLinkSourceEnum.AUTO, createContextForCreate());
+ assertLinkCount(1);
+ }
+
+ @Test
+ public void testNoMatchBlocksPossibleDuplicate() {
+ assertLinkCount(0);
+ Person person = createPerson();
+ Person target = createPerson();
+
+ Long personPid = myIdHelperService.getPidOrNull(person);
+ Long targetPid = myIdHelperService.getPidOrNull(target);
+ assertFalse(myEmpiLinkDaoSvc.getLinkByPersonPidAndTargetPid(personPid, targetPid).isPresent());
+ assertFalse(myEmpiLinkDaoSvc.getLinkByPersonPidAndTargetPid(targetPid, personPid).isPresent());
+
+ saveNoMatchLink(personPid, targetPid);
+
+ myEmpiLinkSvc.updateLink(person, target, EmpiMatchOutcome.POSSIBLE_DUPLICATE, EmpiLinkSourceEnum.AUTO, createContextForCreate());
+ assertFalse(myEmpiLinkDaoSvc.getEmpiLinksByPersonPidTargetPidAndMatchResult(personPid, targetPid, EmpiMatchResultEnum.POSSIBLE_DUPLICATE).isPresent());
+ assertLinkCount(1);
+ }
+
+ @Test
+ public void testNoMatchBlocksPossibleDuplicateReversed() {
+ assertLinkCount(0);
+ Person person = createPerson();
+ Person target = createPerson();
+
+ Long personPid = myIdHelperService.getPidOrNull(person);
+ Long targetPid = myIdHelperService.getPidOrNull(target);
+ assertFalse(myEmpiLinkDaoSvc.getLinkByPersonPidAndTargetPid(personPid, targetPid).isPresent());
+ assertFalse(myEmpiLinkDaoSvc.getLinkByPersonPidAndTargetPid(targetPid, personPid).isPresent());
+
+ saveNoMatchLink(targetPid, personPid);
+
+ myEmpiLinkSvc.updateLink(person, target, EmpiMatchOutcome.POSSIBLE_DUPLICATE, EmpiLinkSourceEnum.AUTO, createContextForCreate());
+ assertFalse(myEmpiLinkDaoSvc.getEmpiLinksByPersonPidTargetPidAndMatchResult(personPid, targetPid, EmpiMatchResultEnum.POSSIBLE_DUPLICATE).isPresent());
+ assertLinkCount(1);
+ }
+
+ private void saveNoMatchLink(Long thePersonPid, Long theTargetPid) {
+ EmpiLink noMatchLink = myEmpiLinkDaoSvc.newEmpiLink()
+ .setPersonPid(thePersonPid)
+ .setTargetPid(theTargetPid)
+ .setLinkSource(EmpiLinkSourceEnum.MANUAL)
+ .setMatchResult(EmpiMatchResultEnum.NO_MATCH);
+ saveLink(noMatchLink);
+ }
+
+ @Test
+ public void testManualEmpiLinksCannotBeModifiedBySystem() {
+ Person person = createPerson(buildJanePerson());
+ Patient patient = createPatient(buildJanePatient());
+
+ myEmpiLinkSvc.updateLink(person, patient, EmpiMatchOutcome.NO_MATCH, EmpiLinkSourceEnum.MANUAL, createContextForCreate());
+ try {
+ myEmpiLinkSvc.updateLink(person, patient, EmpiMatchOutcome.NEW_PERSON_MATCH, EmpiLinkSourceEnum.AUTO, null);
+ fail();
+ } catch (InternalErrorException e) {
+ assertThat(e.getMessage(), is(equalTo("EMPI system is not allowed to modify links on manually created links")));
+ }
+ }
+
+ @Test
+ public void testAutomaticallyAddedNO_MATCHEmpiLinksAreNotAllowed() {
+ Person person = createPerson(buildJanePerson());
+ Patient patient = createPatient(buildJanePatient());
+
+ // Test: it should be impossible to have a AUTO NO_MATCH record. The only NO_MATCH records in the system must be MANUAL.
+ try {
+ myEmpiLinkSvc.updateLink(person, patient, EmpiMatchOutcome.NO_MATCH, EmpiLinkSourceEnum.AUTO, null);
+ fail();
+ } catch (InternalErrorException e) {
+ assertThat(e.getMessage(), is(equalTo("EMPI system is not allowed to automatically NO_MATCH a resource")));
+ }
+ }
+
+ @Test
+ public void testSyncDoesNotSyncNoMatchLinks() {
+ Person person = createPerson(buildJanePerson());
+ Patient patient1 = createPatient(buildJanePatient());
+ Patient patient2 = createPatient(buildJanePatient());
+ assertEquals(0, myEmpiLinkDao.count());
+
+ myEmpiLinkDaoSvc.createOrUpdateLinkEntity(person, patient1, EmpiMatchOutcome.NEW_PERSON_MATCH, EmpiLinkSourceEnum.MANUAL, createContextForCreate());
+ myEmpiLinkDaoSvc.createOrUpdateLinkEntity(person, patient2, EmpiMatchOutcome.NO_MATCH, EmpiLinkSourceEnum.MANUAL, createContextForCreate());
+ myEmpiLinkSvc.syncEmpiLinksToPersonLinks(person, createContextForCreate());
+ assertTrue(person.hasLink());
+ assertEquals(patient1.getIdElement().toVersionless().getValue(), person.getLinkFirstRep().getTarget().getReference());
+ }
+}
diff --git a/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/svc/EmpiLinkUpdaterSvcImplTest.java b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/svc/EmpiLinkUpdaterSvcImplTest.java
new file mode 100644
index 00000000000..3e812f48de4
--- /dev/null
+++ b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/svc/EmpiLinkUpdaterSvcImplTest.java
@@ -0,0 +1,10 @@
+package ca.uhn.fhir.jpa.empi.svc;
+
+import ca.uhn.fhir.jpa.empi.provider.EmpiProviderUpdateLinkR4Test;
+
+/**
+ * Tests for this service are in the test for the provider that wraps this service:
+ * @see EmpiProviderUpdateLinkR4Test
+ */
+public class EmpiLinkUpdaterSvcImplTest {
+}
diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvcMultipleEidModeTest.java b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/svc/EmpiMatchLinkSvcMultipleEidModeTest.java
similarity index 55%
rename from hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvcMultipleEidModeTest.java
rename to hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/svc/EmpiMatchLinkSvcMultipleEidModeTest.java
index e08fb1a8453..be9a192688c 100644
--- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvcMultipleEidModeTest.java
+++ b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/svc/EmpiMatchLinkSvcMultipleEidModeTest.java
@@ -1,13 +1,14 @@
-package ca.uhn.fhir.jpa.mdm.svc;
+package ca.uhn.fhir.jpa.empi.svc;
-import ca.uhn.fhir.jpa.entity.MdmLink;
-import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
-import ca.uhn.fhir.mdm.api.MdmConstants;
-import ca.uhn.fhir.mdm.model.CanonicalEID;
-import ca.uhn.fhir.mdm.util.EIDHelper;
-import org.hl7.fhir.instance.model.api.IAnyResource;
+import ca.uhn.fhir.empi.api.EmpiConstants;
+import ca.uhn.fhir.empi.model.CanonicalEID;
+import ca.uhn.fhir.empi.util.EIDHelper;
+import ca.uhn.fhir.jpa.empi.BaseEmpiR4Test;
+import ca.uhn.fhir.jpa.entity.EmpiLink;
import org.hl7.fhir.r4.model.Identifier;
import org.hl7.fhir.r4.model.Patient;
+import org.hl7.fhir.r4.model.Person;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
@@ -17,9 +18,9 @@ import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
-import static ca.uhn.fhir.mdm.api.MdmMatchResultEnum.MATCH;
-import static ca.uhn.fhir.mdm.api.MdmMatchResultEnum.POSSIBLE_DUPLICATE;
-import static ca.uhn.fhir.mdm.api.MdmMatchResultEnum.POSSIBLE_MATCH;
+import static ca.uhn.fhir.empi.api.EmpiMatchResultEnum.MATCH;
+import static ca.uhn.fhir.empi.api.EmpiMatchResultEnum.POSSIBLE_DUPLICATE;
+import static ca.uhn.fhir.empi.api.EmpiMatchResultEnum.POSSIBLE_MATCH;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
@@ -29,24 +30,28 @@ import static org.hamcrest.Matchers.not;
import static org.slf4j.LoggerFactory.getLogger;
@TestPropertySource(properties = {
- "mdm.prevent_multiple_eids=false"
+ "empi.prevent_multiple_eids=false"
})
-public class MdmMatchLinkSvcMultipleEidModeTest extends BaseMdmR4Test {
- private static final Logger ourLog = getLogger(MdmMatchLinkSvcMultipleEidModeTest.class);
+public class EmpiMatchLinkSvcMultipleEidModeTest extends BaseEmpiR4Test {
+ private static final Logger ourLog = getLogger(EmpiMatchLinkSvcMultipleEidModeTest.class);
@Autowired
private EIDHelper myEidHelper;
+ @BeforeEach
+ public void before() {
+ super.loadEmpiSearchParameters();
+ }
+
@Test
- public void testIncomingPatientWithEIDThatMatchesGoldenResourceWithHapiEidAddsExternalEidsToGoldenResource() {
- // Existing GoldenResource with system-assigned EID found linked from matched Patient. incoming Patient has EID.
- // Replace GoldenResource system-assigned EID with Patient EID.
+ public void testIncomingPatientWithEIDThatMatchesPersonWithHapiEidAddsExternalEidsToPerson() {
+ // Existing Person with system-assigned EID found linked from matched Patient. incoming Patient has EID. Replace Person system-assigned EID with Patient EID.
Patient patient = createPatientAndUpdateLinks(buildJanePatient());
assertLinksMatchResult(MATCH);
- assertLinksCreatedNewResource(true);
+ assertLinksNewPerson(true);
assertLinksMatchedByEid(false);
- IAnyResource janeGoldenResource = getGoldenResourceFromTargetResource(patient);
- List hapiEid = myEidHelper.getHapiEid(janeGoldenResource);
+ Person janePerson = getPersonFromTarget(patient);
+ List hapiEid = myEidHelper.getHapiEid(janePerson);
String foundHapiEid = hapiEid.get(0).getValue();
Patient janePatient = buildJanePatient();
@@ -54,28 +59,28 @@ public class MdmMatchLinkSvcMultipleEidModeTest extends BaseMdmR4Test {
addExternalEID(janePatient, "67890");
createPatientAndUpdateLinks(janePatient);
assertLinksMatchResult(MATCH, MATCH);
- assertLinksCreatedNewResource(true, false);
+ assertLinksNewPerson(true, false);
assertLinksMatchedByEid(false, false);
- //We want to make sure the patients were linked to the same GoldenResource.
- assertThat(patient, is(sameGoldenResourceAs(janePatient)));
+ //We want to make sure the patients were linked to the same person.
+ assertThat(patient, is(samePersonAs(janePatient)));
- Patient sourcePatient = (Patient) getGoldenResourceFromTargetResource(patient);
+ Person person = getPersonFromTarget(patient);
- List identifier = sourcePatient.getIdentifier();
+ List identifier = person.getIdentifier();
//The collision should have kept the old identifier
Identifier firstIdentifier = identifier.get(0);
- assertThat(firstIdentifier.getSystem(), is(equalTo(MdmConstants.HAPI_ENTERPRISE_IDENTIFIER_SYSTEM)));
+ assertThat(firstIdentifier.getSystem(), is(equalTo(EmpiConstants.HAPI_ENTERPRISE_IDENTIFIER_SYSTEM)));
assertThat(firstIdentifier.getValue(), is(equalTo(foundHapiEid)));
//The collision should have added a new identifier with the external system.
Identifier secondIdentifier = identifier.get(1);
- assertThat(secondIdentifier.getSystem(), is(equalTo(myMdmSettings.getMdmRules().getEnterpriseEIDSystem())));
+ assertThat(secondIdentifier.getSystem(), is(equalTo(myEmpiConfig.getEmpiRules().getEnterpriseEIDSystem())));
assertThat(secondIdentifier.getValue(), is(equalTo("12345")));
Identifier thirdIdentifier = identifier.get(2);
- assertThat(thirdIdentifier.getSystem(), is(equalTo(myMdmSettings.getMdmRules().getEnterpriseEIDSystem())));
+ assertThat(thirdIdentifier.getSystem(), is(equalTo(myEmpiConfig.getEmpiRules().getEnterpriseEIDSystem())));
assertThat(thirdIdentifier.getValue(), is(equalTo("67890")));
}
@@ -90,7 +95,7 @@ public class MdmMatchLinkSvcMultipleEidModeTest extends BaseMdmR4Test {
addExternalEID(patient1, "id_4");
createPatientAndUpdateLinks(patient1);
assertLinksMatchResult(MATCH);
- assertLinksCreatedNewResource(true);
+ assertLinksNewPerson(true);
assertLinksMatchedByEid(false);
Patient patient2 = buildPaulPatient();
@@ -98,37 +103,38 @@ public class MdmMatchLinkSvcMultipleEidModeTest extends BaseMdmR4Test {
addExternalEID(patient2, "id_1");
patient2 = createPatientAndUpdateLinks(patient2);
assertLinksMatchResult(MATCH, MATCH);
- assertLinksCreatedNewResource(true, false);
+ assertLinksNewPerson(true, false);
assertLinksMatchedByEid(false, true);
- assertThat(patient1, is(sameGoldenResourceAs(patient2)));
+ assertThat(patient1, is(samePersonAs(patient2)));
clearExternalEIDs(patient2);
addExternalEID(patient2, "id_6");
- //At this point, there should be 5 EIDs on the GoldenResource
- Patient patientFromTarget = (Patient) getGoldenResourceFromTargetResource(patient2);
- assertThat(patientFromTarget.getIdentifier(), hasSize(5));
+ //At this point, there should be 5 EIDs on the person
+ Person personFromTarget = getPersonFromTarget(patient2);
+ assertThat(personFromTarget.getIdentifier(), hasSize(5));
updatePatientAndUpdateLinks(patient2);
assertLinksMatchResult(MATCH, MATCH);
- assertLinksCreatedNewResource(true, false);
+ assertLinksNewPerson(true, false);
assertLinksMatchedByEid(false, true);
- assertThat(patient1, is(sameGoldenResourceAs(patient2)));
+ assertThat(patient1, is(samePersonAs(patient2)));
- patientFromTarget = (Patient) getGoldenResourceFromTargetResource(patient2);
- assertThat(patientFromTarget.getIdentifier(), hasSize(6));
+ personFromTarget = getPersonFromTarget(patient2);
+ assertThat(personFromTarget.getIdentifier(), hasSize(6));
}
@Test
- public void testDuplicateGoldenResourceLinkIsCreatedWhenAnIncomingPatientArrivesWithEIDThatMatchesAnotherEIDPatient() {
+ public void testDuplicatePersonLinkIsCreatedWhenAnIncomingPatientArrivesWithEIDThatMatchesAnotherEIDPatient() {
+
Patient patient1 = buildJanePatient();
addExternalEID(patient1, "eid-1");
addExternalEID(patient1, "eid-11");
patient1 = createPatientAndUpdateLinks(patient1);
assertLinksMatchResult(MATCH);
- assertLinksCreatedNewResource(true);
+ assertLinksNewPerson(true);
assertLinksMatchedByEid(false);
Patient patient2 = buildJanePatient();
@@ -136,32 +142,32 @@ public class MdmMatchLinkSvcMultipleEidModeTest extends BaseMdmR4Test {
addExternalEID(patient2, "eid-22");
patient2 = createPatientAndUpdateLinks(patient2);
assertLinksMatchResult(MATCH, MATCH, POSSIBLE_DUPLICATE);
- assertLinksCreatedNewResource(true, true, false);
+ assertLinksNewPerson(true, true, false);
assertLinksMatchedByEid(false, false, true);
- List possibleDuplicates = myMdmLinkDaoSvc.getPossibleDuplicates();
+ List possibleDuplicates = myEmpiLinkDaoSvc.getPossibleDuplicates();
assertThat(possibleDuplicates, hasSize(1));
List duplicatePids = Stream.of(patient1, patient2)
- .map(this::getGoldenResourceFromTargetResource)
+ .map(this::getPersonFromTarget)
.map(myIdHelperService::getPidOrNull)
.collect(Collectors.toList());
- //The two GoldenResources related to the patients should both show up in the only existing POSSIBLE_DUPLICATE MdmLink.
- MdmLink mdmLink = possibleDuplicates.get(0);
- assertThat(mdmLink.getGoldenResourcePid(), is(in(duplicatePids)));
- assertThat(mdmLink.getSourcePid(), is(in(duplicatePids)));
+ //The two Persons related to the patients should both show up in the only existing POSSIBLE_DUPLICATE EmpiLink.
+ EmpiLink empiLink = possibleDuplicates.get(0);
+ assertThat(empiLink.getPersonPid(), is(in(duplicatePids)));
+ assertThat(empiLink.getTargetPid(), is(in(duplicatePids)));
}
@Test
// Test Case #5
- public void testWhenPatientEidUpdateWouldCauseALinkChangeThatDuplicateGoldenResourceIsCreatedInstead() {
+ public void testWhenPatientEidUpdateWouldCauseALinkChangeThatDuplicatePersonIsCreatedInstead() {
Patient patient1 = buildJanePatient();
addExternalEID(patient1, "eid-1");
addExternalEID(patient1, "eid-11");
patient1 = createPatientAndUpdateLinks(patient1);
assertLinksMatchResult(MATCH);
- assertLinksCreatedNewResource(true);
+ assertLinksNewPerson(true);
assertLinksMatchedByEid(false);
Patient patient2 = buildPaulPatient();
@@ -169,36 +175,36 @@ public class MdmMatchLinkSvcMultipleEidModeTest extends BaseMdmR4Test {
addExternalEID(patient2, "eid-22");
patient2 = createPatientAndUpdateLinks(patient2);
assertLinksMatchResult(MATCH, MATCH);
- assertLinksCreatedNewResource(true, true);
+ assertLinksNewPerson(true, true);
assertLinksMatchedByEid(false, false);
Patient patient3 = buildPaulPatient();
addExternalEID(patient3, "eid-22");
patient3 = createPatientAndUpdateLinks(patient3);
assertLinksMatchResult(MATCH, MATCH, MATCH);
- assertLinksCreatedNewResource(true, true, false);
+ assertLinksNewPerson(true, true, false);
assertLinksMatchedByEid(false, false, true);
- //Now, Patient 2 and 3 are linked, and the GoldenResource has 2 eids.
- assertThat(patient2, is(sameGoldenResourceAs(patient3)));
+ //Now, Patient 2 and 3 are linked, and the person has 2 eids.
+ assertThat(patient2, is(samePersonAs(patient3)));
//Now lets change one of the EIDs on the second patient to one that matches our original patient.
- //This should create a situation in which the incoming EIDs are matched to _two_ different GoldenResources. In this case, we want to
- //set them all to possible_match, and set the two GoldenResources as possible duplicates.
+ //This should create a situation in which the incoming EIDs are matched to _two_ different persons. In this case, we want to
+ //set them all to possible_match, and set the two persons as possible duplicates.
patient2.getIdentifier().clear();
addExternalEID(patient2, "eid-11");
addExternalEID(patient2, "eid-22");
patient2 = updatePatientAndUpdateLinks(patient2);
logAllLinks();
assertLinksMatchResult(MATCH, POSSIBLE_MATCH, MATCH, POSSIBLE_MATCH, POSSIBLE_DUPLICATE);
- assertLinksCreatedNewResource(true, true, false, false, false);
+ assertLinksNewPerson(true, true, false, false, false);
assertLinksMatchedByEid(false, true, true, true, true);
- assertThat(patient2, is(not(matchedToAGoldenResource())));
+ assertThat(patient2, is(not(matchedToAPerson())));
assertThat(patient2, is(possibleMatchWith(patient1)));
assertThat(patient2, is(possibleMatchWith(patient3)));
- List possibleDuplicates = myMdmLinkDaoSvc.getPossibleDuplicates();
+ List possibleDuplicates = myEmpiLinkDaoSvc.getPossibleDuplicates();
assertThat(possibleDuplicates, hasSize(1));
assertThat(patient3, is(possibleDuplicateOf(patient1)));
}
diff --git a/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/svc/EmpiMatchLinkSvcTest.java b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/svc/EmpiMatchLinkSvcTest.java
new file mode 100644
index 00000000000..948b5c86aee
--- /dev/null
+++ b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/svc/EmpiMatchLinkSvcTest.java
@@ -0,0 +1,588 @@
+package ca.uhn.fhir.jpa.empi.svc;
+
+import ca.uhn.fhir.empi.api.EmpiConstants;
+import ca.uhn.fhir.empi.api.EmpiLinkSourceEnum;
+import ca.uhn.fhir.empi.api.EmpiMatchOutcome;
+import ca.uhn.fhir.empi.api.IEmpiLinkSvc;
+import ca.uhn.fhir.empi.model.CanonicalEID;
+import ca.uhn.fhir.empi.util.EIDHelper;
+import ca.uhn.fhir.empi.util.PersonHelper;
+import ca.uhn.fhir.jpa.empi.BaseEmpiR4Test;
+import ca.uhn.fhir.jpa.entity.EmpiLink;
+import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
+import ca.uhn.fhir.rest.api.server.IBundleProvider;
+import org.hl7.fhir.instance.model.api.IAnyResource;
+import org.hl7.fhir.r4.model.Enumerations;
+import org.hl7.fhir.r4.model.HumanName;
+import org.hl7.fhir.r4.model.Identifier;
+import org.hl7.fhir.r4.model.Patient;
+import org.hl7.fhir.r4.model.Person;
+import org.hl7.fhir.r4.model.Practitioner;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static ca.uhn.fhir.empi.api.EmpiMatchResultEnum.MATCH;
+import static ca.uhn.fhir.empi.api.EmpiMatchResultEnum.NO_MATCH;
+import static ca.uhn.fhir.empi.api.EmpiMatchResultEnum.POSSIBLE_DUPLICATE;
+import static ca.uhn.fhir.empi.api.EmpiMatchResultEnum.POSSIBLE_MATCH;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.blankOrNullString;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.equalToIgnoringCase;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.in;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.slf4j.LoggerFactory.getLogger;
+
+public class EmpiMatchLinkSvcTest extends BaseEmpiR4Test {
+ private static final Logger ourLog = getLogger(EmpiMatchLinkSvcTest.class);
+ @Autowired
+ IEmpiLinkSvc myEmpiLinkSvc;
+ @Autowired
+ private EIDHelper myEidHelper;
+ @Autowired
+ private PersonHelper myPersonHelper;
+
+ @BeforeEach
+ public void before() {
+ super.loadEmpiSearchParameters();
+ }
+
+ @Test
+ public void testAddPatientLinksToNewPersonIfNoneFound() {
+ createPatientAndUpdateLinks(buildJanePatient());
+ assertLinkCount(1);
+ assertLinksMatchResult(MATCH);
+ assertLinksNewPerson(true);
+ assertLinksMatchedByEid(false);
+ }
+
+ @Test
+ public void testAddPatientLinksToNewPersonIfNoMatch() {
+ Patient patient1 = createPatientAndUpdateLinks(buildJanePatient());
+ Patient patient2 = createPatientAndUpdateLinks(buildPaulPatient());
+
+ assertLinkCount(2);
+ assertThat(patient1, is(not(samePersonAs(patient2))));
+ assertLinksMatchResult(MATCH, MATCH);
+ assertLinksNewPerson(true, true);
+ assertLinksMatchedByEid(false, false);
+ }
+
+ @Test
+ public void testAddPatientLinksToExistingPersonIfMatch() {
+ Patient patient1 = createPatientAndUpdateLinks(buildJanePatient());
+ assertLinkCount(1);
+
+ Patient patient2 = createPatientAndUpdateLinks(buildJanePatient());
+ assertLinkCount(2);
+
+ assertThat(patient1, is(samePersonAs(patient2)));
+ assertLinksMatchResult(MATCH, MATCH);
+ assertLinksNewPerson(true, false);
+ assertLinksMatchedByEid(false, false);
+ }
+
+ @Test
+ public void testWhenMatchOccursOnPersonThatHasBeenManuallyNOMATCHedThatItIsBlocked() {
+ Patient originalJane = createPatientAndUpdateLinks(buildJanePatient());
+ IBundleProvider search = myPersonDao.search(new SearchParameterMap());
+ IAnyResource janePerson = (IAnyResource) search.getResources(0, 1).get(0);
+
+ //Create a manual NO_MATCH between janePerson and unmatchedJane.
+ Patient unmatchedJane = createPatient(buildJanePatient());
+ myEmpiLinkSvc.updateLink(janePerson, unmatchedJane, EmpiMatchOutcome.NO_MATCH, EmpiLinkSourceEnum.MANUAL, createContextForCreate());
+
+ //rerun EMPI rules against unmatchedJane.
+ myEmpiMatchLinkSvc.updateEmpiLinksForEmpiTarget(unmatchedJane, createContextForCreate());
+
+ assertThat(unmatchedJane, is(not(samePersonAs(janePerson))));
+ assertThat(unmatchedJane, is(not(linkedTo(originalJane))));
+
+ assertLinksMatchResult(MATCH, NO_MATCH, MATCH);
+ assertLinksNewPerson(true, false, true);
+ assertLinksMatchedByEid(false, false, false);
+ }
+
+ @Test
+ public void testWhenPOSSIBLE_MATCHOccursOnPersonThatHasBeenManuallyNOMATCHedThatItIsBlocked() {
+ Patient originalJane = createPatientAndUpdateLinks(buildJanePatient());
+ IBundleProvider search = myPersonDao.search(new SearchParameterMap());
+ IAnyResource janePerson = (IAnyResource) search.getResources(0, 1).get(0);
+
+ Patient unmatchedPatient = createPatient(buildJanePatient());
+
+ //This simulates an admin specifically saying that unmatchedPatient does NOT match janePerson.
+ myEmpiLinkSvc.updateLink(janePerson, unmatchedPatient, EmpiMatchOutcome.NO_MATCH, EmpiLinkSourceEnum.MANUAL, createContextForCreate());
+ //TODO change this so that it will only partially match.
+
+ //Now normally, when we run update links, it should link to janePerson. However, this manual NO_MATCH link
+ //should cause a whole new Person to be created.
+ myEmpiMatchLinkSvc.updateEmpiLinksForEmpiTarget(unmatchedPatient, createContextForCreate());
+
+ assertThat(unmatchedPatient, is(not(samePersonAs(janePerson))));
+ assertThat(unmatchedPatient, is(not(linkedTo(originalJane))));
+
+ assertLinksMatchResult(MATCH, NO_MATCH, MATCH);
+ assertLinksNewPerson(true, false, true);
+ assertLinksMatchedByEid(false, false, false);
+ }
+
+ @Test
+ public void testWhenPatientIsCreatedWithEIDThatItPropagatesToNewPerson() {
+ String sampleEID = "sample-eid";
+ Patient janePatient = addExternalEID(buildJanePatient(), sampleEID);
+ janePatient = createPatientAndUpdateLinks(janePatient);
+
+ Optional empiLink = myEmpiLinkDaoSvc.getMatchedLinkForTargetPid(janePatient.getIdElement().getIdPartAsLong());
+ assertThat(empiLink.isPresent(), is(true));
+
+ Person person = getPersonFromEmpiLink(empiLink.get());
+ List externalEid = myEidHelper.getExternalEid(person);
+
+ assertThat(externalEid.get(0).getSystem(), is(equalTo(myEmpiConfig.getEmpiRules().getEnterpriseEIDSystem())));
+ assertThat(externalEid.get(0).getValue(), is(equalTo(sampleEID)));
+ }
+
+ @Test
+ public void testWhenPatientIsCreatedWithoutAnEIDThePersonGetsAutomaticallyAssignedOne() {
+ Patient patient = createPatientAndUpdateLinks(buildJanePatient());
+ EmpiLink empiLink = myEmpiLinkDaoSvc.getMatchedLinkForTargetPid(patient.getIdElement().getIdPartAsLong()).get();
+
+ Person person = getPersonFromEmpiLink(empiLink);
+ Identifier identifierFirstRep = person.getIdentifierFirstRep();
+ assertThat(identifierFirstRep.getSystem(), is(equalTo(EmpiConstants.HAPI_ENTERPRISE_IDENTIFIER_SYSTEM)));
+ assertThat(identifierFirstRep.getValue(), not(blankOrNullString()));
+ }
+
+ @Test
+ public void testPatientAttributesAreCopiedOverWhenPersonIsCreatedFromPatient() {
+ Patient patient = createPatientAndUpdateLinks(buildPatientWithNameIdAndBirthday("Gary", "GARY_ID", new Date()));
+
+ Optional empiLink = myEmpiLinkDaoSvc.getMatchedLinkForTargetPid(patient.getIdElement().getIdPartAsLong());
+ Person read = getPersonFromEmpiLink(empiLink.get());
+
+ assertThat(read.getNameFirstRep().getFamily(), is(equalTo(patient.getNameFirstRep().getFamily())));
+ assertThat(read.getNameFirstRep().getGivenAsSingleString(), is(equalTo(patient.getNameFirstRep().getGivenAsSingleString())));
+ assertThat(read.getBirthDateElement().toHumanDisplay(), is(equalTo(patient.getBirthDateElement().toHumanDisplay())));
+ assertThat(read.getTelecomFirstRep().getValue(), is(equalTo(patient.getTelecomFirstRep().getValue())));
+ assertThat(read.getPhoto().getData(), is(equalTo(patient.getPhotoFirstRep().getData())));
+ assertThat(read.getGender(), is(equalTo(patient.getGender())));
+ }
+
+ @Test
+ public void testPatientMatchingAnotherPatientLinksToSamePerson() {
+ Patient janePatient = createPatientAndUpdateLinks(buildJanePatient());
+ Patient sameJanePatient = createPatientAndUpdateLinks(buildJanePatient());
+ assertThat(janePatient, is(samePersonAs(sameJanePatient)));
+ }
+
+ @Test
+ public void testIncomingPatientWithEIDThatMatchesPersonWithHapiEidAddsExternalEidToPerson() {
+ // Existing Person with system-assigned EID found linked from matched Patient. incoming Patient has EID. Replace Person system-assigned EID with Patient EID.
+ Patient patient = createPatientAndUpdateLinks(buildJanePatient());
+
+ Person janePerson = getPersonFromTarget(patient);
+ List hapiEid = myEidHelper.getHapiEid(janePerson);
+ String foundHapiEid = hapiEid.get(0).getValue();
+
+ Patient janePatient = addExternalEID(buildJanePatient(), "12345");
+ createPatientAndUpdateLinks(janePatient);
+
+ //We want to make sure the patients were linked to the same person.
+ assertThat(patient, is(samePersonAs(janePatient)));
+
+ Person person = getPersonFromTarget(patient);
+
+ List identifier = person.getIdentifier();
+
+ //The collision should have kept the old identifier
+ Identifier firstIdentifier = identifier.get(0);
+ assertThat(firstIdentifier.getSystem(), is(equalTo(EmpiConstants.HAPI_ENTERPRISE_IDENTIFIER_SYSTEM)));
+ assertThat(firstIdentifier.getValue(), is(equalTo(foundHapiEid)));
+
+ //The collision should have added a new identifier with the external system.
+ Identifier secondIdentifier = identifier.get(1);
+ assertThat(secondIdentifier.getSystem(), is(equalTo(myEmpiConfig.getEmpiRules().getEnterpriseEIDSystem())));
+ assertThat(secondIdentifier.getValue(), is(equalTo("12345")));
+ }
+
+ @Test
+ public void testIncomingPatientWithEidMatchesAnotherPatientWithSameEIDAreLinked() {
+ Patient patient1 = addExternalEID(buildJanePatient(), "uniqueid");
+ createPatientAndUpdateLinks(patient1);
+
+ Patient patient2 = addExternalEID(buildPaulPatient(), "uniqueid");
+ createPatientAndUpdateLinks(patient2);
+
+ assertThat(patient1, is(samePersonAs(patient2)));
+ }
+
+ @Test
+ public void testHavingMultipleEIDsOnIncomingPatientMatchesCorrectly() {
+
+ Patient patient1 = buildJanePatient();
+ addExternalEID(patient1, "id_1");
+ addExternalEID(patient1, "id_2");
+ addExternalEID(patient1, "id_3");
+ addExternalEID(patient1, "id_4");
+ createPatientAndUpdateLinks(patient1);
+
+ Patient patient2 = buildPaulPatient();
+ addExternalEID(patient2, "id_5");
+ addExternalEID(patient2, "id_1");
+ createPatientAndUpdateLinks(patient2);
+
+ assertThat(patient1, is(samePersonAs(patient2)));
+ }
+
+ @Test
+ public void testDuplicatePersonLinkIsCreatedWhenAnIncomingPatientArrivesWithEIDThatMatchesAnotherEIDPatient() {
+
+ Patient patient1 = addExternalEID(buildJanePatient(), "eid-1");
+ patient1 = createPatientAndUpdateLinks(patient1);
+
+ Patient patient2 = addExternalEID(buildJanePatient(), "eid-2");
+ patient2 = createPatientAndUpdateLinks(patient2);
+
+ List possibleDuplicates = myEmpiLinkDaoSvc.getPossibleDuplicates();
+ assertThat(possibleDuplicates, hasSize(1));
+
+
+ List duplicatePids = Stream.of(patient1, patient2)
+ .map(this::getPersonFromTarget)
+ .map(myIdHelperService::getPidOrNull)
+ .collect(Collectors.toList());
+
+ //The two Persons related to the patients should both show up in the only existing POSSIBLE_DUPLICATE EmpiLink.
+ EmpiLink empiLink = possibleDuplicates.get(0);
+ assertThat(empiLink.getPersonPid(), is(in(duplicatePids)));
+ assertThat(empiLink.getTargetPid(), is(in(duplicatePids)));
+ }
+
+ @Test
+ public void testPatientWithNoEmpiTagIsNotMatched() {
+ // Patient with "no-empi" tag is not matched
+ Patient janePatient = buildJanePatient();
+ janePatient.getMeta().addTag(EmpiConstants.SYSTEM_EMPI_MANAGED, EmpiConstants.CODE_NO_EMPI_MANAGED, "Don't EMPI on me!");
+ createPatientAndUpdateLinks(janePatient);
+ assertLinkCount(0);
+ }
+
+ @Test
+ public void testPractitionersDoNotMatchToPatients() {
+ Patient janePatient = createPatientAndUpdateLinks(buildJanePatient());
+ Practitioner janePractitioner = createPractitionerAndUpdateLinks(buildJanePractitioner());
+
+ assertLinkCount(2);
+ assertThat(janePatient, is(not(samePersonAs(janePractitioner))));
+ }
+
+ @Test
+ public void testPractitionersThatMatchShouldLink() {
+ Practitioner janePractitioner = createPractitionerAndUpdateLinks(buildJanePractitioner());
+ Practitioner anotherJanePractitioner = createPractitionerAndUpdateLinks(buildJanePractitioner());
+
+ assertLinkCount(2);
+ assertThat(anotherJanePractitioner, is(samePersonAs(janePractitioner)));
+ }
+
+ @Test
+ public void testWhenThereAreNoMATCHOrPOSSIBLE_MATCHOutcomesThatANewPersonIsCreated() {
+ /**
+ * CASE 1: No MATCHED and no PROBABLE_MATCHED outcomes -> a new Person resource
+ * is created and linked to that Pat/Prac.
+ */
+ assertLinkCount(0);
+ Patient janePatient = createPatientAndUpdateLinks(buildJanePatient());
+ assertLinkCount(1);
+ assertThat(janePatient, is(matchedToAPerson()));
+ }
+
+ @Test
+ public void testWhenAllMATCHResultsAreToSamePersonThatTheyAreLinked() {
+ /**
+ * CASE 2: All of the MATCHED Pat/Prac resources are already linked to the same Person ->
+ * a new Link is created between the new Pat/Prac and that Person and is set to MATCHED.
+ */
+ Patient janePatient = createPatientAndUpdateLinks(buildJanePatient());
+ Patient janePatient2 = createPatientAndUpdateLinks(buildJanePatient());
+
+ assertLinkCount(2);
+ assertThat(janePatient, is(samePersonAs(janePatient2)));
+
+ Patient incomingJanePatient = createPatientAndUpdateLinks(buildJanePatient());
+ assertThat(incomingJanePatient, is(samePersonAs(janePatient, janePatient2)));
+ assertThat(incomingJanePatient, is(linkedTo(janePatient, janePatient2)));
+ }
+
+ @Test
+ public void testMATCHResultWithMultipleCandidatesCreatesPOSSIBLE_DUPLICATELinksAndNoPersonIsCreated() {
+ /**
+ * CASE 3: The MATCHED Pat/Prac resources link to more than one Person -> Mark all links as POSSIBLE_MATCH.
+ * All other Person resources are marked as POSSIBLE_DUPLICATE of this first Person.
+ */
+ Patient janePatient = createPatientAndUpdateLinks(buildJanePatient());
+
+ Patient janePatient2 = createPatient(buildJanePatient());
+
+ //In a normal situation, janePatient2 would just match to jane patient, but here we need to hack it so they are their
+ //own individual Persons for the purpose of this test.
+ IAnyResource person = myPersonHelper.createPersonFromEmpiTarget(janePatient2);
+ myEmpiLinkSvc.updateLink(person, janePatient2, EmpiMatchOutcome.NEW_PERSON_MATCH, EmpiLinkSourceEnum.AUTO, createContextForCreate());
+ assertThat(janePatient, is(not(samePersonAs(janePatient2))));
+
+ //In theory, this will match both Persons!
+ Patient incomingJanePatient = createPatientAndUpdateLinks(buildJanePatient());
+
+ //There should now be a single POSSIBLE_DUPLICATE link with
+ assertThat(janePatient, is(possibleDuplicateOf(janePatient2)));
+
+ //There should now be 2 POSSIBLE_MATCH links with this person.
+ assertThat(incomingJanePatient, is(possibleMatchWith(janePatient, janePatient2)));
+
+ //Ensure there is no successful MATCH links for incomingJanePatient
+ Optional matchedLinkForTargetPid = myEmpiLinkDaoSvc.getMatchedLinkForTargetPid(myIdHelperService.getPidOrNull(incomingJanePatient));
+ assertThat(matchedLinkForTargetPid.isPresent(), is(false));
+
+ logAllLinks();
+ assertLinksMatchResult(MATCH, MATCH, POSSIBLE_MATCH, POSSIBLE_MATCH, POSSIBLE_DUPLICATE);
+ assertLinksNewPerson(true, true, false, false, false);
+ assertLinksMatchedByEid(false, false, false, false, false);
+ }
+
+ @Test
+ public void testWhenAllMatchResultsArePOSSIBLE_MATCHThattheyAreLinkedAndNoPersonIsCreated() {
+ /**
+ * CASE 4: Only POSSIBLE_MATCH outcomes -> In this case, empi-link records are created with POSSIBLE_MATCH
+ * outcome and await manual assignment to either NO_MATCH or MATCHED. Person link is added.
+ */
+ Patient patient = buildJanePatient();
+ patient.getNameFirstRep().setFamily("familyone");
+ patient = createPatientAndUpdateLinks(patient);
+ assertThat(patient, is(samePersonAs(patient)));
+
+ Patient patient2 = buildJanePatient();
+ patient2.getNameFirstRep().setFamily("pleasedonotmatchatall");
+ patient2 = createPatientAndUpdateLinks(patient2);
+
+ assertThat(patient2, is(possibleMatchWith(patient)));
+
+ Patient patient3 = buildJanePatient();
+ patient3.getNameFirstRep().setFamily("pleasedonotmatchatall");
+ patient3 = createPatientAndUpdateLinks(patient3);
+
+ assertThat(patient3, is(possibleMatchWith(patient2)));
+ assertThat(patient3, is(possibleMatchWith(patient)));
+
+ IBundleProvider bundle = myPersonDao.search(new SearchParameterMap());
+ assertEquals(1, bundle.size());
+ Person person = (Person) bundle.getResources(0, 1).get(0);
+ assertEquals(Person.IdentityAssuranceLevel.LEVEL2, person.getLink().get(0).getAssurance());
+ assertEquals(Person.IdentityAssuranceLevel.LEVEL1, person.getLink().get(1).getAssurance());
+ assertEquals(Person.IdentityAssuranceLevel.LEVEL1, person.getLink().get(2).getAssurance());
+
+ assertLinksMatchResult(MATCH, POSSIBLE_MATCH, POSSIBLE_MATCH);
+ assertLinksNewPerson(true, false, false);
+ assertLinksMatchedByEid(false, false, false);
+ }
+
+ @Test
+ public void testWhenAnIncomingResourceHasMatchesAndPossibleMatchesThatItLinksToMatch() {
+ Patient patient = buildJanePatient();
+ patient.getNameFirstRep().setFamily("familyone");
+ patient = createPatientAndUpdateLinks(patient);
+ assertThat(patient, is(samePersonAs(patient)));
+
+ Patient patient2 = buildJanePatient();
+ patient2.getNameFirstRep().setFamily("pleasedonotmatchatall");
+ patient2 = createPatientAndUpdateLinks(patient2);
+
+ Patient patient3 = buildJanePatient();
+ patient3.getNameFirstRep().setFamily("familyone");
+ patient3 = createPatientAndUpdateLinks(patient3);
+
+ assertThat(patient2, is(not(samePersonAs(patient))));
+ assertThat(patient2, is(possibleMatchWith(patient)));
+ assertThat(patient3, is(samePersonAs(patient)));
+ }
+
+ @Test
+ public void testAutoMatchesGenerateAssuranceLevel3() {
+ Patient patient = createPatientAndUpdateLinks(buildJanePatient());
+ Person janePerson = getPersonFromTarget(patient);
+ Person.PersonLinkComponent linkFirstRep = janePerson.getLinkFirstRep();
+
+ assertThat(linkFirstRep.getTarget().getReference(), is(equalTo(patient.getIdElement().toVersionless().toString())));
+ assertThat(linkFirstRep.getAssurance(), is(equalTo(Person.IdentityAssuranceLevel.LEVEL2)));
+ }
+
+ @Test
+ public void testManualMatchesGenerateAssuranceLevel4() {
+ Patient patient = createPatientAndUpdateLinks(buildJanePatient());
+ Person janePerson = getPersonFromTarget(patient);
+ myEmpiLinkSvc.updateLink(janePerson, patient, EmpiMatchOutcome.NEW_PERSON_MATCH, EmpiLinkSourceEnum.MANUAL, createContextForCreate());
+
+ janePerson = getPersonFromTarget(patient);
+ Person.PersonLinkComponent linkFirstRep = janePerson.getLinkFirstRep();
+
+ assertThat(linkFirstRep.getTarget().getReference(), is(equalTo(patient.getIdElement().toVersionless().toString())));
+ assertThat(linkFirstRep.getAssurance(), is(equalTo(Person.IdentityAssuranceLevel.LEVEL3)));
+ }
+
+ //Case #1
+ @Test
+ public void testPatientUpdateOverwritesPersonDataOnChanges() {
+ Patient janePatient = createPatientAndUpdateLinks(buildJanePatient());
+ Person janePerson = getPersonFromTarget(janePatient);
+
+ //Change Jane's name to paul.
+ Patient patient1 = buildPaulPatient();
+ patient1.setId(janePatient.getId());
+ Patient janePaulPatient = updatePatientAndUpdateLinks(patient1);
+
+ assertThat(janePerson, is(samePersonAs(janePaulPatient)));
+
+ //Ensure the related person was updated with new info.
+ Person personFromTarget = getPersonFromTarget(janePaulPatient);
+ HumanName nameFirstRep = personFromTarget.getNameFirstRep();
+ assertThat(nameFirstRep.getGivenAsSingleString(), is(equalToIgnoringCase("paul")));
+ }
+
+ @Test
+ public void testPatientCreateDoesNotOverwritePersonAttributesThatAreInvolvedInLinking() {
+ Patient paul = buildPaulPatient();
+ paul.setGender(Enumerations.AdministrativeGender.MALE);
+ paul = createPatientAndUpdateLinks(paul);
+
+ Person personFromTarget = getPersonFromTarget(paul);
+ assertThat(personFromTarget.getGender(), is(equalTo(Enumerations.AdministrativeGender.MALE)));
+
+ Patient paul2 = buildPaulPatient();
+ paul2.setGender(Enumerations.AdministrativeGender.FEMALE);
+ paul2 = createPatientAndUpdateLinks(paul2);
+
+ assertThat(paul2, is(samePersonAs(paul)));
+
+ //Newly matched patients aren't allowed to overwrite Person Attributes unless they are empty, so gender should still be set to male.
+ Person paul2Person = getPersonFromTarget(paul2);
+ assertThat(paul2Person.getGender(), is(equalTo(Enumerations.AdministrativeGender.MALE)));
+ }
+
+ @Test
+ //Test Case #1
+ public void testPatientUpdatesOverwritePersonData() {
+ Patient paul = buildPaulPatient();
+ String incorrectBirthdate = "1980-06-27";
+ paul.getBirthDateElement().setValueAsString(incorrectBirthdate);
+ paul = createPatientAndUpdateLinks(paul);
+
+ Person personFromTarget = getPersonFromTarget(paul);
+ assertThat(personFromTarget.getBirthDateElement().getValueAsString(), is(incorrectBirthdate));
+
+ String correctBirthdate = "1990-06-28";
+ paul.getBirthDateElement().setValueAsString(correctBirthdate);
+
+ paul = updatePatientAndUpdateLinks(paul);
+
+ personFromTarget = getPersonFromTarget(paul);
+ assertThat(personFromTarget.getBirthDateElement().getValueAsString(), is(equalTo(correctBirthdate)));
+ assertLinkCount(1);
+ }
+
+ @Test
+ // Test Case #3
+ public void testUpdatedEidThatWouldRelinkAlsoCausesPossibleDuplicate() {
+ String EID_1 = "123";
+ String EID_2 = "456";
+
+ Patient paul = createPatientAndUpdateLinks(addExternalEID(buildPaulPatient(), EID_1));
+ Person originalPaulPerson = getPersonFromTarget(paul);
+
+ Patient jane = createPatientAndUpdateLinks(addExternalEID(buildJanePatient(), EID_2));
+ Person originalJanePerson = getPersonFromTarget(jane);
+
+ clearExternalEIDs(paul);
+ addExternalEID(paul, EID_2);
+ updatePatientAndUpdateLinks(paul);
+
+ assertThat(originalJanePerson, is(possibleDuplicateOf(originalPaulPerson)));
+ assertThat(jane, is(samePersonAs(paul)));
+ }
+
+ @Test
+ //Test Case #2
+ public void testSinglyLinkedPersonThatGetsAnUpdatedEidSimplyUpdatesEID() {
+ String EID_1 = "123";
+ String EID_2 = "456";
+
+ Patient paul = createPatientAndUpdateLinks(addExternalEID(buildPaulPatient(), EID_1));
+ Person originalPaulPerson = getPersonFromTarget(paul);
+ String oldEid = myEidHelper.getExternalEid(originalPaulPerson).get(0).getValue();
+ assertThat(oldEid, is(equalTo(EID_1)));
+
+ clearExternalEIDs(paul);
+ addExternalEID(paul, EID_2);
+
+ paul = updatePatientAndUpdateLinks(paul);
+
+ assertNoDuplicates();
+
+ Person newlyFoundPaulPerson = getPersonFromTarget(paul);
+ assertThat(originalPaulPerson, is(samePersonAs(newlyFoundPaulPerson)));
+ String newEid = myEidHelper.getExternalEid(newlyFoundPaulPerson).get(0).getValue();
+ assertThat(newEid, is(equalTo(EID_2)));
+ }
+
+ private void assertNoDuplicates() {
+ List possibleDuplicates = myEmpiLinkDaoSvc.getPossibleDuplicates();
+ assertThat(possibleDuplicates, hasSize(0));
+ }
+
+ @Test
+ //Test Case #3
+ public void testWhenAnEidChangeWouldCauseARelinkingThatAPossibleDuplicateIsCreated() {
+ Patient patient1 = buildJanePatient();
+ addExternalEID(patient1, "eid-1");
+ patient1 = createPatientAndUpdateLinks(patient1);
+
+ Patient patient2 = buildPaulPatient();
+ addExternalEID(patient2, "eid-2");
+ patient2 = createPatientAndUpdateLinks(patient2);
+
+ Patient patient3 = buildPaulPatient();
+ addExternalEID(patient3, "eid-2");
+ patient3 = createPatientAndUpdateLinks(patient3);
+
+ //Now, Patient 2 and 3 are linked, and the person has 2 eids.
+ assertThat(patient2, is(samePersonAs(patient3)));
+ assertNoDuplicates();
+ // Person A -> {P1}
+ // Person B -> {P2, P3}
+
+ patient2.getIdentifier().clear();
+ addExternalEID(patient2, "eid-1");
+ patient2 = updatePatientAndUpdateLinks(patient2);
+
+ // Person A -> {P1, P2}
+ // Person B -> {P3}
+ // Possible duplicates A<->B
+
+ assertThat(patient2, is(samePersonAs(patient1)));
+
+ List possibleDuplicates = myEmpiLinkDaoSvc.getPossibleDuplicates();
+ assertThat(possibleDuplicates, hasSize(1));
+ assertThat(patient3, is(possibleDuplicateOf(patient1)));
+
+ }
+}
diff --git a/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/svc/EmpiPersonMergerSvcTest.java b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/svc/EmpiPersonMergerSvcTest.java
new file mode 100644
index 00000000000..c7cd05b2c9f
--- /dev/null
+++ b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/svc/EmpiPersonMergerSvcTest.java
@@ -0,0 +1,421 @@
+package ca.uhn.fhir.jpa.empi.svc;
+
+import ca.uhn.fhir.empi.api.EmpiLinkSourceEnum;
+import ca.uhn.fhir.empi.api.EmpiMatchOutcome;
+import ca.uhn.fhir.empi.api.EmpiMatchResultEnum;
+import ca.uhn.fhir.empi.api.IEmpiPersonMergerSvc;
+import ca.uhn.fhir.empi.model.EmpiTransactionContext;
+import ca.uhn.fhir.interceptor.api.IInterceptorService;
+import ca.uhn.fhir.jpa.empi.BaseEmpiR4Test;
+import ca.uhn.fhir.jpa.empi.helper.EmpiLinkHelper;
+import ca.uhn.fhir.jpa.empi.interceptor.IEmpiStorageInterceptor;
+import ca.uhn.fhir.jpa.entity.EmpiLink;
+import ca.uhn.fhir.rest.server.TransactionLogMessages;
+import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
+import org.hl7.fhir.r4.model.Address;
+import org.hl7.fhir.r4.model.DateType;
+import org.hl7.fhir.r4.model.Enumerations;
+import org.hl7.fhir.r4.model.HumanName;
+import org.hl7.fhir.r4.model.IdType;
+import org.hl7.fhir.r4.model.Patient;
+import org.hl7.fhir.r4.model.Person;
+import org.hl7.fhir.r4.model.Reference;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Example;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.hasSize;
+import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+
+public class EmpiPersonMergerSvcTest extends BaseEmpiR4Test {
+ public static final String GIVEN_NAME = "Jenn";
+ public static final String FAMILY_NAME = "Chan";
+ public static final String POSTAL_CODE = "M6G 1B4";
+ private static final String BAD_GIVEN_NAME = "Bob";
+ private static final EmpiMatchOutcome POSSIBLE_MATCH = new EmpiMatchOutcome(null, null).setMatchResultEnum(EmpiMatchResultEnum.POSSIBLE_MATCH);
+
+ @Autowired
+ IEmpiPersonMergerSvc myEmpiPersonMergerSvc;
+ @Autowired
+ EmpiLinkHelper myEmpiLinkHelper;
+ @Autowired
+ IEmpiStorageInterceptor myEmpiStorageInterceptor;
+ @Autowired
+ IInterceptorService myInterceptorService;
+
+ private Person myFromPerson;
+ private Person myToPerson;
+ private Long myFromPersonPid;
+ private Long myToPersonPid;
+ private Patient myTargetPatient1;
+ private Patient myTargetPatient2;
+ private Patient myTargetPatient3;
+
+ @BeforeEach
+ public void before() {
+ super.loadEmpiSearchParameters();
+
+ myFromPerson = createPerson();
+ IdType fromPersonId = myFromPerson.getIdElement().toUnqualifiedVersionless();
+ myFromPersonPid = myIdHelperService.getPidOrThrowException(fromPersonId);
+ myToPerson = createPerson();
+ IdType toPersonId = myToPerson.getIdElement().toUnqualifiedVersionless();
+ myToPersonPid = myIdHelperService.getPidOrThrowException(toPersonId);
+
+ myTargetPatient1 = createPatient();
+
+ myTargetPatient2 = createPatient();
+
+ myTargetPatient3 = createPatient();
+
+ // Register the empi storage interceptor after the creates so the delete hook is fired when we merge
+ myInterceptorService.registerInterceptor(myEmpiStorageInterceptor);
+ }
+
+ @Override
+ @AfterEach
+ public void after() throws IOException {
+ myInterceptorService.unregisterInterceptor(myEmpiStorageInterceptor);
+ super.after();
+ }
+
+ @Test
+ public void emptyMerge() {
+ assertEquals(2, getAllPersons().size());
+ assertEquals(2, getAllActivePersons().size());
+
+ Person mergedPerson = mergePersons();
+ assertEquals(myToPerson.getIdElement(), mergedPerson.getIdElement());
+ assertThat(mergedPerson, is(samePersonAs(mergedPerson)));
+ assertEquals(2, getAllPersons().size());
+ assertEquals(1, getAllActivePersons().size());
+ }
+
+ private Person mergePersons() {
+ assertEquals(0, redirectLinkCount());
+ Person retval = (Person) myEmpiPersonMergerSvc.mergePersons(myFromPerson, myToPerson, createEmpiContext());
+ assertEquals(1, redirectLinkCount());
+ return retval;
+ }
+
+ private int redirectLinkCount() {
+ EmpiLink empiLink = new EmpiLink().setMatchResult(EmpiMatchResultEnum.REDIRECT);
+ Example example = Example.of(empiLink);
+ return myEmpiLinkDao.findAll(example).size();
+ }
+
+ private EmpiTransactionContext createEmpiContext() {
+ return new EmpiTransactionContext(TransactionLogMessages.createFromTransactionGuid(UUID.randomUUID().toString()), EmpiTransactionContext.OperationType.MERGE_PERSONS);
+ }
+
+ @Test
+ public void mergeRemovesPossibleDuplicatesLink() {
+ EmpiLink empiLink = myEmpiLinkDaoSvc.newEmpiLink().setPersonPid(myToPersonPid).setTargetPid(myFromPersonPid).setMatchResult(EmpiMatchResultEnum.POSSIBLE_DUPLICATE).setLinkSource(EmpiLinkSourceEnum.AUTO);
+ saveLink(empiLink);
+
+ {
+ List foundLinks = myEmpiLinkDao.findAll();
+ assertEquals(1, foundLinks.size());
+ assertEquals(EmpiMatchResultEnum.POSSIBLE_DUPLICATE, foundLinks.get(0).getMatchResult());
+ }
+
+ mergePersons();
+
+ {
+ List