From 558f419955e4cc0322669113a830d7692fe4a9c1 Mon Sep 17 00:00:00 2001 From: Ken Stevens Date: Fri, 5 Jun 2020 12:21:09 -0400 Subject: [PATCH] Empi 54 not duplicate (#1900) add operation to unduplicate persons --- .../docs/server_jpa_empi/empi_operations.md | 142 +++++++++++++----- .../java/ca/uhn/fhir/jpa/entity/EmpiLink.java | 5 +- .../fhir/jpa/empi/svc/EmpiLinkSvcImpl.java | 23 ++- .../jpa/empi/svc/EmpiLinkUpdaterSvcImpl.java | 52 ++++++- .../provider/EmpiProviderQueryLinkR4Test.java | 49 +++++- .../fhir/jpa/empi/svc/EmpiLinkSvcTest.java | 58 +++++++ .../fhir/empi/api/IEmpiLinkUpdaterSvc.java | 3 + .../fhir/empi/provider/BaseEmpiProvider.java | 7 +- .../fhir/empi/provider/EmpiProviderDstu3.java | 15 ++ .../fhir/empi/provider/EmpiProviderR4.java | 14 ++ .../server/provider/ProviderConstants.java | 1 + 11 files changed, 317 insertions(+), 52 deletions(-) diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa_empi/empi_operations.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa_empi/empi_operations.md index d894e0d5043..fcbb2868100 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa_empi/empi_operations.md +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa_empi/empi_operations.md @@ -2,6 +2,8 @@ Several operations exist that can be used to manage EMPI links. These operations are supplied by a [plain provider](/docs/server_plain/resource_providers.html#plain-providers) called [EmpiProvider](/hapi-fhir/apidocs/hapi-fhir-server-empi/ca/uhn/fhir/empi/provider/EmpiProviderR4.html). +In cases where the operation changes data, if a resource id parameter contains a version (e.g. `Person/123/_history/1`), then the operation will fail with a 409 CONFLICT if that is not the latest version of that resource. This could be used to prevent update conflicts in an environment where multiple users are working on the same set of empi links. + ## Query links Ue the `$empi-query-links` operation to view empi links. The results returned are based on the parameters provided. All parameters are optional. This operation takes the following parameters: @@ -74,27 +76,25 @@ The following request body could be used to find all POSSIBLE_MATCH links in the This operation returns a `Parameters` resource that looks like the following: ```json - - - - - - - - - - - - - - - - - - - - - +{ + "resourceType": "Parameters", + "parameter": [ { + "name": "link", + "part": [ { + "name": "personId", + "valueString": "Person/123" + }, { + "name": "targetId", + "valueString": "Patient/456" + }, { + "name": "matchResult", + "valueString": "POSSIBLE_MATCH" + }, { + "name": "linkSource", + "valueString": "AUTO" + } ] + } ] +} ``` ## Querying links via the Person resource @@ -155,19 +155,93 @@ This operation returns `Parameters` similar to `$empi-query-links`: ```json - - - - - - - - - - - - - +{ + "resourceType": "Parameters", + "parameter": [ { + "name": "link", + "part": [ { + "name": "personId", + "valueString": "Person/123" + }, { + "name": "targetId", + "valueString": "Person/456" + }, { + "name": "matchResult", + "valueString": "POSSIBLE_DUPLICATE" + }, { + "name": "linkSource", + "valueString": "AUTO" + } ] + } ] +} +``` + +## Unduplicate Persons + +Use the `$empi-not-duplicate` operation to mark duplicate persons as not duplicates. This operation takes the following parameters: + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeCardinalityDescription
personIdString1..1 + The id of the Person resource. +
targetIdString1..1 + The id of the Person that personId has a possible duplicate link to. +
+ +### Example + +Use an HTTP POST to the following URL to invoke this operation: + +```url +http://example.com/$empi-not-duplicate +``` + +The following request body could be used: + +```json +{ + "resourceType": "Parameters", + "parameter": [ { + "name": "personId", + "valueString": "Person/123" + }, { + "name": "targetId", + "valueString": "Person/456" + } ] +} +``` + +When the operation is successful, it returns the following `Parameters`: + +```json +{ + "resourceType": "Parameters", + "parameter": [ { + "name": "success", + "valueBoolean": true + } ] +} ``` ## Update Link diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/EmpiLink.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/EmpiLink.java index 41df01a2b6f..a194026e18a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/EmpiLink.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/EmpiLink.java @@ -25,7 +25,6 @@ import ca.uhn.fhir.empi.api.EmpiMatchResultEnum; import ca.uhn.fhir.jpa.model.entity.ResourceTable; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; -import org.hibernate.annotations.OptimisticLock; import javax.persistence.Column; import javax.persistence.Entity; @@ -157,6 +156,10 @@ public class EmpiLink { return myMatchResult == EmpiMatchResultEnum.POSSIBLE_MATCH; } + public boolean isPossibleDuplicate() { + return myMatchResult == EmpiMatchResultEnum.POSSIBLE_DUPLICATE; + } + public EmpiLinkSourceEnum getLinkSource() { return myLinkSource; } diff --git a/hapi-fhir-jpaserver-empi/src/main/java/ca/uhn/fhir/jpa/empi/svc/EmpiLinkSvcImpl.java b/hapi-fhir-jpaserver-empi/src/main/java/ca/uhn/fhir/jpa/empi/svc/EmpiLinkSvcImpl.java index c69e644b78f..641207873c1 100644 --- a/hapi-fhir-jpaserver-empi/src/main/java/ca/uhn/fhir/jpa/empi/svc/EmpiLinkSvcImpl.java +++ b/hapi-fhir-jpaserver-empi/src/main/java/ca/uhn/fhir/jpa/empi/svc/EmpiLinkSvcImpl.java @@ -63,13 +63,19 @@ public class EmpiLinkSvcImpl implements IEmpiLinkSvc { @Override @Transactional - public void updateLink(IAnyResource thePerson, IAnyResource theResource, EmpiMatchResultEnum theMatchResult, EmpiLinkSourceEnum theLinkSource, EmpiTransactionContext theEmpiTransactionContext) { - IIdType resourceId = theResource.getIdElement().toUnqualifiedVersionless(); + public void updateLink(IAnyResource thePerson, IAnyResource theTarget, EmpiMatchResultEnum theMatchResult, EmpiLinkSourceEnum theLinkSource, EmpiTransactionContext theEmpiTransactionContext) { + IIdType resourceId = theTarget.getIdElement().toUnqualifiedVersionless(); - validateRequestIsLegal(thePerson, theResource, theMatchResult, theLinkSource); + if (theMatchResult == EmpiMatchResultEnum.POSSIBLE_DUPLICATE && personsLinkedAsNoMatch(thePerson, theTarget)) { + log(theEmpiTransactionContext, thePerson.getIdElement().toUnqualifiedVersionless() + + " is linked as NO_MATCH with " + + theTarget.getIdElement().toUnqualifiedVersionless() + + " not linking as POSSIBLE_DUPLICATE."); + return; + } + validateRequestIsLegal(thePerson, theTarget, theMatchResult, theLinkSource); switch (theMatchResult) { case MATCH: - //deleteCurrentMatch(theResource); myPersonHelper.addOrUpdateLink(thePerson, resourceId, AssuranceLevelUtil.getAssuranceLevel(theMatchResult, theLinkSource), theEmpiTransactionContext); myEmpiResourceDaoSvc.updatePerson(thePerson); break; @@ -83,8 +89,15 @@ public class EmpiLinkSvcImpl implements IEmpiLinkSvc { break; } myEmpiResourceDaoSvc.updatePerson(thePerson); - createOrUpdateLinkEntity(thePerson, theResource, theMatchResult, theLinkSource, theEmpiTransactionContext); + createOrUpdateLinkEntity(thePerson, theTarget, theMatchResult, theLinkSource, theEmpiTransactionContext); + } + private boolean personsLinkedAsNoMatch(IAnyResource thePerson, IAnyResource theTarget) { + Long personId = myIdHelperService.getPidOrThrowException(thePerson); + Long targetId = myIdHelperService.getPidOrThrowException(theTarget); + // TODO perf collapse into one query + return myEmpiLinkDaoSvc.getEmpiLinksByPersonPidTargetPidAndMatchResult(personId, targetId, EmpiMatchResultEnum.NO_MATCH).isPresent() || + myEmpiLinkDaoSvc.getEmpiLinksByPersonPidTargetPidAndMatchResult(targetId, personId, EmpiMatchResultEnum.NO_MATCH).isPresent(); } @Override diff --git a/hapi-fhir-jpaserver-empi/src/main/java/ca/uhn/fhir/jpa/empi/svc/EmpiLinkUpdaterSvcImpl.java b/hapi-fhir-jpaserver-empi/src/main/java/ca/uhn/fhir/jpa/empi/svc/EmpiLinkUpdaterSvcImpl.java index 459425c712e..9093ab62f64 100644 --- a/hapi-fhir-jpaserver-empi/src/main/java/ca/uhn/fhir/jpa/empi/svc/EmpiLinkUpdaterSvcImpl.java +++ b/hapi-fhir-jpaserver-empi/src/main/java/ca/uhn/fhir/jpa/empi/svc/EmpiLinkUpdaterSvcImpl.java @@ -33,7 +33,11 @@ import ca.uhn.fhir.jpa.dao.EmpiLinkDaoSvc; import ca.uhn.fhir.jpa.dao.index.IdHelperService; import ca.uhn.fhir.jpa.entity.EmpiLink; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.rest.server.provider.ProviderConstants; +import ca.uhn.fhir.util.ParametersUtil; import org.hl7.fhir.instance.model.api.IAnyResource; +import org.hl7.fhir.instance.model.api.IBaseParameters; +import org.hl7.fhir.r4.model.Parameters; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; @@ -92,15 +96,15 @@ public class EmpiLinkUpdaterSvcImpl implements IEmpiLinkUpdaterSvc { private void validateUpdateLinkRequest(IAnyResource thePerson, IAnyResource theTarget, EmpiMatchResultEnum theMatchResult, String theTargetType) { String personType = myFhirContext.getResourceType(thePerson); if (theMatchResult != EmpiMatchResultEnum.NO_MATCH && - theMatchResult != EmpiMatchResultEnum.MATCH) { + theMatchResult != EmpiMatchResultEnum.MATCH) { throw new InvalidRequestException("Match Result may only be set to " + EmpiMatchResultEnum.NO_MATCH + " or " + EmpiMatchResultEnum.MATCH); } if (!"Person".equals(personType)) { - throw new InvalidRequestException("First argument to updateLink must be a Person. Was " + personType); + throw new InvalidRequestException("First argument to " + ProviderConstants.EMPI_UPDATE_LINK + " must be a Person. Was " + personType); } if (!EmpiUtil.supportedTargetType(theTargetType)) { - throw new InvalidRequestException("Second argument to updateLink must be a Patient or Practitioner. Was " + theTargetType); + throw new InvalidRequestException("Second argument to " + ProviderConstants.EMPI_UPDATE_LINK + " must be a Patient or Practitioner. Was " + theTargetType); } if (!EmpiUtil.isEmpiManaged(thePerson)) { @@ -111,4 +115,46 @@ public class EmpiLinkUpdaterSvcImpl implements IEmpiLinkUpdaterSvc { throw new InvalidRequestException("The target is marked with the " + EmpiConstants.CODE_NO_EMPI_MANAGED + " tag which means it may not be EMPI linked."); } } + + @Transactional + @Override + public IBaseParameters notDuplicatePerson(IAnyResource thePerson, IAnyResource theTarget, EmpiTransactionContext theEmpiContext) { + validateNotDuplicatePersonRequest(thePerson, theTarget); + + Long personId = myIdHelperService.getPidOrThrowException(thePerson); + Long targetId = myIdHelperService.getPidOrThrowException(theTarget); + + Optional oEmpiLink = myEmpiLinkDaoSvc.getLinkByPersonPidAndTargetPid(personId, targetId); + if (!oEmpiLink.isPresent()) { + throw new InvalidRequestException("No link exists between " + thePerson.getIdElement().toVersionless() + " and " + theTarget.getIdElement().toVersionless()); + } + + EmpiLink empiLink = oEmpiLink.get(); + if (!empiLink.isPossibleDuplicate()) { + throw new InvalidRequestException(thePerson.getIdElement().toVersionless() + " and " + theTarget.getIdElement().toVersionless() + " are not linked as POSSIBLE_DUPLICATE."); + } + empiLink.setMatchResult(EmpiMatchResultEnum.NO_MATCH); + empiLink.setLinkSource(EmpiLinkSourceEnum.MANUAL); + myEmpiLinkDaoSvc.save(empiLink); + + Parameters retval = (Parameters) ParametersUtil.newInstance(myFhirContext); + retval.addParameter("success", true); + return retval; + } + + private void validateNotDuplicatePersonRequest(IAnyResource thePerson, IAnyResource theTarget) { + String personType = myFhirContext.getResourceType(thePerson); + String targetType = myFhirContext.getResourceType(theTarget); + if (!"Person".equals(personType)) { + throw new InvalidRequestException("First argument to " + ProviderConstants.EMPI_UPDATE_LINK + " must be a Person. Was " + personType); + } + if (!"Person".equals(targetType)) { + throw new InvalidRequestException("Second argument to " + ProviderConstants.EMPI_UPDATE_LINK + " must be a Person . Was " + targetType); + } + + if (!EmpiUtil.isEmpiManaged(thePerson) || !EmpiUtil.isEmpiManaged(theTarget)) { + throw new InvalidRequestException("Only EMPI Managed Person resources may be updated via this operation. The Person resource provided is not tagged as managed by hapi-empi"); + } + + } } diff --git a/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/provider/EmpiProviderQueryLinkR4Test.java b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/provider/EmpiProviderQueryLinkR4Test.java index 3cf4823491e..a9b9356c23b 100644 --- a/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/provider/EmpiProviderQueryLinkR4Test.java +++ b/hapi-fhir-jpaserver-empi/src/test/java/ca/uhn/fhir/jpa/empi/provider/EmpiProviderQueryLinkR4Test.java @@ -4,6 +4,8 @@ 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.r4.model.BooleanType; import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Parameters; import org.hl7.fhir.r4.model.Patient; @@ -19,12 +21,15 @@ import java.util.List; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; public class EmpiProviderQueryLinkR4Test extends BaseLinkR4Test { -private static final Logger ourLog = LoggerFactory.getLogger(EmpiProviderQueryLinkR4Test.class); + private static final Logger ourLog = LoggerFactory.getLogger(EmpiProviderQueryLinkR4Test.class); private StringType myLinkSource; - private IdType myPerson1Id; - private IdType myPerson2Id; + private StringType myPerson1Id; + private StringType myPerson2Id; @Before public void before() { @@ -36,20 +41,20 @@ private static final Logger ourLog = LoggerFactory.getLogger(EmpiProviderQueryLi // Add a possible duplicate myLinkSource = new StringType(EmpiLinkSourceEnum.AUTO.name()); Person person1 = createPerson(); - myPerson1Id = person1.getIdElement().toVersionless(); + myPerson1Id = new StringType(person1.getIdElement().toVersionless().getValue()); Long person1Pid = myIdHelperService.getPidOrNull(person1); Person person2 = createPerson(); - myPerson2Id = person2.getIdElement().toVersionless(); + myPerson2Id = new StringType(person2.getIdElement().toVersionless().getValue()); Long person2Pid = myIdHelperService.getPidOrNull(person2); - EmpiLink empiLink = new EmpiLink().setPersonPid(person1Pid).setTargetPid(person2Pid).setMatchResult(EmpiMatchResultEnum.POSSIBLE_DUPLICATE).setLinkSource(EmpiLinkSourceEnum.AUTO); - myEmpiLinkDaoSvc.save(empiLink); + EmpiLink possibleDuplicateEmpiLink = new EmpiLink().setPersonPid(person1Pid).setTargetPid(person2Pid).setMatchResult(EmpiMatchResultEnum.POSSIBLE_DUPLICATE).setLinkSource(EmpiLinkSourceEnum.AUTO); + myEmpiLinkDaoSvc.save(possibleDuplicateEmpiLink); } @Test public void testQueryLinkOneMatch() { Parameters result = myEmpiProviderR4.queryLinks(myPersonId, myPatientId, null, null, myRequestDetails); - ourLog.info(myFhirContext.newXmlParser().setPrettyPrint(true).encodeResourceToString(result)); + ourLog.info(myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(result)); List list = result.getParameter(); assertThat(list, hasSize(1)); List part = list.get(0).getPart(); @@ -82,6 +87,34 @@ private static final Logger ourLog = LoggerFactory.getLogger(EmpiProviderQueryLi assertEmpiLink(2, part, myPerson1Id.getValue(), myPerson2Id.getValue(), EmpiMatchResultEnum.POSSIBLE_DUPLICATE); } + @Test + public void testNotDuplicate() { + { + Parameters result = myEmpiProviderR4.getDuplicatePersons(myRequestDetails); + List list = result.getParameter(); + assertThat(list, hasSize(1)); + } + { + 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 = myEmpiProviderR4.getDuplicatePersons(myRequestDetails); + List list = result.getParameter(); + assertThat(list, hasSize(0)); + } + + @Test + public void testNotDuplicateBadId() { + try { + myEmpiProviderR4.notDuplicate(myPerson1Id, new StringType("Person/notAnId123"), myRequestDetails); + fail(); + } catch (ResourceNotFoundException e) { + assertEquals("Resource Person/notAnId123 is not known", e.getMessage()); + } + } + private void assertEmpiLink(int theExpectedSize, List thePart, String thePersonId, String theTargetId, EmpiMatchResultEnum theMatchResult) { assertThat(thePart, hasSize(theExpectedSize)); assertThat(thePart.get(0).getName(), is("personId")); 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 index d7025aabccf..1080b554e39 100644 --- 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 @@ -16,6 +16,7 @@ import org.springframework.beans.factory.annotation.Autowired; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -30,6 +31,7 @@ public class EmpiLinkSvcTest extends BaseEmpiR4Test { myExpungeEverythingService.expungeEverythingByType(EmpiLink.class); super.after(); } + @Test public void compareEmptyPatients() { Patient patient = new Patient(); @@ -61,6 +63,62 @@ public class EmpiLinkSvcTest extends BaseEmpiR4Test { } } + + @Test + public void testPossibleDuplicate() { + assertLinkCount(0); + Person person = createPerson(); + Person target = createPerson(); + + myEmpiLinkSvc.updateLink(person, target, EmpiMatchResultEnum.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, EmpiMatchResultEnum.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, EmpiMatchResultEnum.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 = new EmpiLink() + .setPersonPid(thePersonPid) + .setTargetPid(theTargetPid) + .setLinkSource(EmpiLinkSourceEnum.MANUAL) + .setMatchResult(EmpiMatchResultEnum.NO_MATCH); + myEmpiLinkDaoSvc.save(noMatchLink); + } + @Test public void testManualEmpiLinksCannotBeModifiedBySystem() { Person person = createPerson(buildJanePerson()); diff --git a/hapi-fhir-server-empi/src/main/java/ca/uhn/fhir/empi/api/IEmpiLinkUpdaterSvc.java b/hapi-fhir-server-empi/src/main/java/ca/uhn/fhir/empi/api/IEmpiLinkUpdaterSvc.java index 956e6ff0c06..ce740df93c8 100644 --- a/hapi-fhir-server-empi/src/main/java/ca/uhn/fhir/empi/api/IEmpiLinkUpdaterSvc.java +++ b/hapi-fhir-server-empi/src/main/java/ca/uhn/fhir/empi/api/IEmpiLinkUpdaterSvc.java @@ -22,7 +22,10 @@ package ca.uhn.fhir.empi.api; import ca.uhn.fhir.empi.model.EmpiTransactionContext; import org.hl7.fhir.instance.model.api.IAnyResource; +import org.hl7.fhir.instance.model.api.IBaseParameters; public interface IEmpiLinkUpdaterSvc { IAnyResource updateLink(IAnyResource thePerson, IAnyResource theTarget, EmpiMatchResultEnum theMatchResult, EmpiTransactionContext theEmpiContext); + + IBaseParameters notDuplicatePerson(IAnyResource thePerson, IAnyResource theTarget, EmpiTransactionContext theEmpiContext); } diff --git a/hapi-fhir-server-empi/src/main/java/ca/uhn/fhir/empi/provider/BaseEmpiProvider.java b/hapi-fhir-server-empi/src/main/java/ca/uhn/fhir/empi/provider/BaseEmpiProvider.java index aabff47a39f..b526b766c99 100644 --- a/hapi-fhir-server-empi/src/main/java/ca/uhn/fhir/empi/provider/BaseEmpiProvider.java +++ b/hapi-fhir-server-empi/src/main/java/ca/uhn/fhir/empi/provider/BaseEmpiProvider.java @@ -40,7 +40,7 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType; public abstract class BaseEmpiProvider { - private final FhirContext myFhirContext; + protected final FhirContext myFhirContext; private final IResourceLoader myResourceLoader; public BaseEmpiProvider(FhirContext theFhirContext, IResourceLoader theResourceLoader) { @@ -125,6 +125,11 @@ public abstract class BaseEmpiProvider { } } + protected void validateNotDuplicateParameters(IPrimitiveType thePersonId, IPrimitiveType theTargetId) { + validateNotNull(ProviderConstants.EMPI_UPDATE_LINK_PERSON_ID, thePersonId); + validateNotNull(ProviderConstants.EMPI_UPDATE_LINK_TARGET_ID, theTargetId); + } + protected EmpiTransactionContext createEmpiContext(RequestDetails theRequestDetails) { TransactionLogMessages transactionLogMessages = TransactionLogMessages.createFromTransactionGuid(theRequestDetails.getTransactionGuid()); return new EmpiTransactionContext(transactionLogMessages, EmpiTransactionContext.OperationType.MERGE_PERSONS); diff --git a/hapi-fhir-server-empi/src/main/java/ca/uhn/fhir/empi/provider/EmpiProviderDstu3.java b/hapi-fhir-server-empi/src/main/java/ca/uhn/fhir/empi/provider/EmpiProviderDstu3.java index a82135ed253..5a4a02ce9bd 100644 --- a/hapi-fhir-server-empi/src/main/java/ca/uhn/fhir/empi/provider/EmpiProviderDstu3.java +++ b/hapi-fhir-server-empi/src/main/java/ca/uhn/fhir/empi/provider/EmpiProviderDstu3.java @@ -134,4 +134,19 @@ public class EmpiProviderDstu3 extends BaseEmpiProvider { public Parameters getDuplicatePersons(ServletRequestDetails theRequestDetails) { return (Parameters) myEmpiLinkQuerySvc.getPossibleDuplicates(createEmpiContext(theRequestDetails)); } + + @Operation(name = ProviderConstants.EMPI_NOT_DUPLICATE) + // TODO KHS can this return void? + public Parameters notDuplicate(@OperationParam(name=ProviderConstants.EMPI_QUERY_LINKS_PERSON_ID, min = 1, max = 1) StringType thePersonId, + @OperationParam(name=ProviderConstants.EMPI_QUERY_LINKS_TARGET_ID, min = 1, max = 1) StringType theTargetId, + ServletRequestDetails theRequestDetails) { + + validateNotDuplicateParameters(thePersonId, theTargetId); + IAnyResource person = getLatestPersonFromIdOrThrowException(ProviderConstants.EMPI_UPDATE_LINK_PERSON_ID, thePersonId.getValue()); + IAnyResource target = getLatestPersonFromIdOrThrowException(ProviderConstants.EMPI_UPDATE_LINK_TARGET_ID, theTargetId.getValue()); + validateSameVersion(person, thePersonId); + validateSameVersion(target, theTargetId); + + return (Parameters) myEmpiLinkUpdaterSvc.notDuplicatePerson(person, target, createEmpiContext(theRequestDetails)); + } } diff --git a/hapi-fhir-server-empi/src/main/java/ca/uhn/fhir/empi/provider/EmpiProviderR4.java b/hapi-fhir-server-empi/src/main/java/ca/uhn/fhir/empi/provider/EmpiProviderR4.java index 5e958151303..ca887f32f37 100644 --- a/hapi-fhir-server-empi/src/main/java/ca/uhn/fhir/empi/provider/EmpiProviderR4.java +++ b/hapi-fhir-server-empi/src/main/java/ca/uhn/fhir/empi/provider/EmpiProviderR4.java @@ -135,4 +135,18 @@ public class EmpiProviderR4 extends BaseEmpiProvider { public Parameters getDuplicatePersons(ServletRequestDetails theRequestDetails) { return (Parameters) myEmpiLinkQuerySvc.getPossibleDuplicates(createEmpiContext(theRequestDetails)); } + + @Operation(name = ProviderConstants.EMPI_NOT_DUPLICATE) + public Parameters notDuplicate(@OperationParam(name=ProviderConstants.EMPI_QUERY_LINKS_PERSON_ID, min = 1, max = 1) StringType thePersonId, + @OperationParam(name=ProviderConstants.EMPI_QUERY_LINKS_TARGET_ID, min = 1, max = 1) StringType theTargetId, + ServletRequestDetails theRequestDetails) { + + validateNotDuplicateParameters(thePersonId, theTargetId); + IAnyResource person = getLatestPersonFromIdOrThrowException(ProviderConstants.EMPI_UPDATE_LINK_PERSON_ID, thePersonId.getValue()); + IAnyResource target = getLatestPersonFromIdOrThrowException(ProviderConstants.EMPI_UPDATE_LINK_TARGET_ID, theTargetId.getValue()); + validateSameVersion(person, thePersonId); + validateSameVersion(target, theTargetId); + + return (Parameters) myEmpiLinkUpdaterSvc.notDuplicatePerson(person, target, createEmpiContext(theRequestDetails)); + } } diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ProviderConstants.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ProviderConstants.java index c2d273b4ca8..2e58d1bc04c 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ProviderConstants.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ProviderConstants.java @@ -80,4 +80,5 @@ public class ProviderConstants { public static final String EMPI_QUERY_LINKS_LINK_SOURCE = "linkSource"; public static final String EMPI_DUPLICATE_PERSONS = "$empi-duplicate-persons"; + public static final String EMPI_NOT_DUPLICATE = "$empi-not-duplicate"; }