From a2aa2ca1c37ec9f171285c0ef8d3502448c8b37e Mon Sep 17 00:00:00 2001 From: Nick Goupinets Date: Thu, 29 Jul 2021 15:52:09 -0400 Subject: [PATCH 01/13] 2849 Added new parameter to MDM processing --- .../ca/uhn/fhir/interceptor/api/Pointcut.java | 6 +- .../jpa/mdm/broker/MdmMessageHandler.java | 18 ++++-- .../fhir/jpa/mdm/svc/MdmEidUpdateService.java | 2 + .../uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java | 5 ++ .../fhir/jpa/mdm/helper/BaseMdmHelper.java | 3 + .../interceptor/MdmStorageInterceptorIT.java | 26 +++++++++ .../uhn/fhir/mdm/api/MdmLinkChangeEvent.java | 55 +++++++++++++++++++ .../fhir/mdm/model/MdmTransactionContext.java | 11 ++++ 8 files changed, 119 insertions(+), 7 deletions(-) create mode 100644 hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkChangeEvent.java diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java index db6dff8b7a1..d8f0f33ae9f 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java @@ -1986,13 +1986,17 @@ public enum Pointcut implements IPointcut { * *

*

* Hooks should return void. *

*/ - MDM_AFTER_PERSISTED_RESOURCE_CHECKED(void.class, "ca.uhn.fhir.rest.server.messaging.ResourceOperationMessage", "ca.uhn.fhir.rest.server.TransactionLogMessages"), + MDM_AFTER_PERSISTED_RESOURCE_CHECKED(void.class, + "ca.uhn.fhir.rest.server.messaging.ResourceOperationMessage", + "ca.uhn.fhir.rest.server.TransactionLogMessages", + "ca.uhn.fhir.mdm.api.MdmLinkChangeEvent"), /** * Performance Tracing Hook: diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java index 3a84eebb083..b80d2caafc7 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java @@ -30,12 +30,15 @@ import ca.uhn.fhir.jpa.mdm.svc.candidate.TooManyCandidatesException; import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedJsonMessage; import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage; import ca.uhn.fhir.mdm.api.IMdmSettings; +import ca.uhn.fhir.mdm.api.MdmLinkChangeEvent; import ca.uhn.fhir.mdm.log.Logs; import ca.uhn.fhir.mdm.model.MdmTransactionContext; import ca.uhn.fhir.rest.server.TransactionLogMessages; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.messaging.ResourceOperationMessage; import org.hl7.fhir.instance.model.api.IAnyResource; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.Message; @@ -89,11 +92,11 @@ public class MdmMessageHandler implements MessageHandler { try { switch (theMsg.getOperationType()) { case CREATE: - handleCreatePatientOrPractitioner(theMsg, mdmContext); + handleCreateResource(theMsg, mdmContext); break; case UPDATE: case MANUALLY_TRIGGERED: - handleUpdatePatientOrPractitioner(theMsg, mdmContext); + handleUpdateResource(theMsg, mdmContext); break; case DELETE: default: @@ -105,12 +108,15 @@ public class MdmMessageHandler implements MessageHandler { } finally { // Interceptor call: MDM_AFTER_PERSISTED_RESOURCE_CHECKED - ResourceOperationMessage outgoingMsg = new ResourceOperationMessage(myFhirContext, theMsg.getPayload(myFhirContext), theMsg.getOperationType()); + IBaseResource targetResource = theMsg.getPayload(myFhirContext); + ResourceOperationMessage outgoingMsg = new ResourceOperationMessage(myFhirContext, targetResource, theMsg.getOperationType()); outgoingMsg.setTransactionId(theMsg.getTransactionId()); HookParams params = new HookParams() .add(ResourceOperationMessage.class, outgoingMsg) - .add(TransactionLogMessages.class, mdmContext.getTransactionLogMessages()); + .add(TransactionLogMessages.class, mdmContext.getTransactionLogMessages()) + .add(MdmLinkChangeEvent.class, mdmContext.getMdmLinkChangeEvent()); + myInterceptorBroadcaster.callHooks(Pointcut.MDM_AFTER_PERSISTED_RESOURCE_CHECKED, params); } } @@ -142,7 +148,7 @@ public class MdmMessageHandler implements MessageHandler { } } - private void handleCreatePatientOrPractitioner(ResourceModifiedMessage theMsg, MdmTransactionContext theMdmTransactionContext) { + private void handleCreateResource(ResourceModifiedMessage theMsg, MdmTransactionContext theMdmTransactionContext) { myMdmMatchLinkSvc.updateMdmLinksForMdmSource(getResourceFromPayload(theMsg), theMdmTransactionContext); } @@ -150,7 +156,7 @@ public class MdmMessageHandler implements MessageHandler { return (IAnyResource) theMsg.getNewPayload(myFhirContext); } - private void handleUpdatePatientOrPractitioner(ResourceModifiedMessage theMsg, MdmTransactionContext theMdmTransactionContext) { + private void handleUpdateResource(ResourceModifiedMessage theMsg, MdmTransactionContext theMdmTransactionContext) { myMdmMatchLinkSvc.updateMdmLinksForMdmSource(getResourceFromPayload(theMsg), theMdmTransactionContext); } diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmEidUpdateService.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmEidUpdateService.java index 94db6849073..d23c27db9f1 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmEidUpdateService.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmEidUpdateService.java @@ -85,6 +85,8 @@ public class MdmEidUpdateService { myMdmSurvivorshipService.applySurvivorshipRulesToGoldenResource(theTargetResource, updateContext.getMatchedGoldenResource(), theMdmTransactionContext); myMdmResourceDaoSvc.upsertGoldenResource(updateContext.getMatchedGoldenResource(), theMdmTransactionContext.getResourceType()); } + + theMdmTransactionContext.getMdmLinkChangeEvent().setGoldenResourceId(updateContext.getExistingGoldenResource()); } private void handleNoEidsInCommon(IAnyResource theResource, MatchedGoldenResourceCandidate theMatchedGoldenResourceCandidate, MdmTransactionContext theMdmTransactionContext, MdmUpdateContext theUpdateContext) { diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java index fdd04914646..90020ffc259 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java @@ -112,11 +112,14 @@ public class MdmMatchLinkSvc { //Set all GoldenResources as POSSIBLE_DUPLICATE of the last GoldenResource. IAnyResource firstGoldenResource = goldenResources.get(0); + theMdmTransactionContext.getMdmLinkChangeEvent().setGoldenResourceId(firstGoldenResource); + goldenResources.subList(1, goldenResources.size()) .forEach(possibleDuplicateGoldenResource -> { MdmMatchOutcome outcome = MdmMatchOutcome.POSSIBLE_DUPLICATE; outcome.setEidMatch(theCandidateList.isEidMatch()); myMdmLinkSvc.updateLink(firstGoldenResource, possibleDuplicateGoldenResource, outcome, MdmLinkSourceEnum.AUTO, theMdmTransactionContext); + theMdmTransactionContext.getMdmLinkChangeEvent().addDuplicateGoldenResourceId(possibleDuplicateGoldenResource); }); } } @@ -129,6 +132,8 @@ public class MdmMatchLinkSvc { // 2. Create source resource for the MDM source // 3. UPDATE MDM LINK TABLE myMdmLinkSvc.updateLink(newGoldenResource, theResource, MdmMatchOutcome.NEW_GOLDEN_RESOURCE_MATCH, MdmLinkSourceEnum.AUTO, theMdmTransactionContext); + + theMdmTransactionContext.getMdmLinkChangeEvent().setGoldenResourceId(newGoldenResource); } private void handleMdmCreate(IAnyResource theTargetResource, MatchedGoldenResourceCandidate theGoldenResourceCandidate, MdmTransactionContext theMdmTransactionContext) { diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/helper/BaseMdmHelper.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/helper/BaseMdmHelper.java index 1389c4c5ea1..601b3ce2920 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/helper/BaseMdmHelper.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/helper/BaseMdmHelper.java @@ -106,5 +106,8 @@ public abstract class BaseMdmHelper implements BeforeEachCallback, AfterEachCall return channel.getQueueSizeForUnitTest(); } + public PointcutLatch getAfterMdmLatch() { + return myAfterMdmLatch; + } } diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java index c722b23dab2..7d24395852d 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java @@ -7,13 +7,16 @@ import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test; import ca.uhn.fhir.jpa.mdm.helper.MdmHelperConfig; import ca.uhn.fhir.jpa.mdm.helper.MdmHelperR4; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; +import ca.uhn.fhir.mdm.api.MdmLinkChangeEvent; import ca.uhn.fhir.mdm.model.CanonicalEID; import ca.uhn.fhir.mdm.rules.config.MdmSettings; +import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId; import ca.uhn.fhir.rest.param.ReferenceParam; import ca.uhn.fhir.rest.server.TransactionLogMessages; import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException; +import ca.uhn.fhir.rest.server.messaging.ResourceOperationMessage; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; @@ -21,16 +24,21 @@ import org.hl7.fhir.r4.model.Enumerations; import org.hl7.fhir.r4.model.Medication; import org.hl7.fhir.r4.model.Organization; import org.hl7.fhir.r4.model.Patient; +import org.hl7.fhir.r4.model.Practitioner; import org.hl7.fhir.r4.model.SearchParameter; 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.data.domain.Example; +import org.springframework.data.domain.ExampleMatcher; +import org.springframework.data.domain.Pageable; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import java.util.Date; import java.util.List; +import java.util.Optional; import static ca.uhn.fhir.mdm.api.MdmConstants.CODE_GOLDEN_RECORD; import static ca.uhn.fhir.mdm.api.MdmConstants.CODE_GOLDEN_RECORD_REDIRECTED; @@ -67,6 +75,24 @@ public class MdmStorageInterceptorIT extends BaseMdmR4Test { assertLinkCount(1); } + @Test + public void testCreateLinkChangeEvent() throws InterruptedException { + Practitioner pr = buildPractitionerWithNameAndId("Young", "AC-DC"); + myMdmHelper.createWithLatch(pr); + + ResourceOperationMessage resourceOperationMessage = myMdmHelper.getAfterMdmLatch().getLatchInvocationParameterOfType(ResourceOperationMessage.class); + assertNotNull(resourceOperationMessage); + assertEquals(pr.getId(), resourceOperationMessage.getId()); + + MdmLink example = new MdmLink(); + example.setSourcePid(pr.getIdElement().getIdPartAsLong()); + MdmLink link = myMdmLinkDao.findAll(Example.of(example)).get(0); + + MdmLinkChangeEvent linkChangeEvent = myMdmHelper.getAfterMdmLatch().getLatchInvocationParameterOfType(MdmLinkChangeEvent.class); + assertNotNull(linkChangeEvent); + assertEquals(link.getGoldenResourcePid(), new IdDt(linkChangeEvent.getGoldenResourceId()).getIdPartAsLong()); + } + @Test public void testSearchExpandingInterceptorWorks() { SearchParameterMap subject = new SearchParameterMap("subject", new ReferenceParam("Patient/123").setMdmExpand(true)).setLoadSynchronous(true); diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkChangeEvent.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkChangeEvent.java new file mode 100644 index 00000000000..374d955aba8 --- /dev/null +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkChangeEvent.java @@ -0,0 +1,55 @@ +package ca.uhn.fhir.mdm.api; + +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class MdmLinkChangeEvent { + + private String myGoldenResourceId; + private Set myDuplicateGoldenResourceIds = new HashSet<>(); + + public String getGoldenResourceId() { + return myGoldenResourceId; + } + + public void setGoldenResourceId(IBaseResource theGoldenResourceId) { + setGoldenResourceId(getIdAsString(theGoldenResourceId)); + } + + public void setGoldenResourceId(String theGoldenResourceId) { + myGoldenResourceId = theGoldenResourceId; + } + + private String getIdAsString(IBaseResource theResource) { + if (theResource == null) { + return null; + } + IIdType idElement = theResource.getIdElement(); + if (idElement == null) { + return null; + } + return idElement.getValueAsString(); + } + + public Set getDuplicateGoldenResourceIds() { + return myDuplicateGoldenResourceIds; + } + + public void setDuplicateGoldenResourceIds(Set theDuplicateGoldenResourceIds) { + myDuplicateGoldenResourceIds = theDuplicateGoldenResourceIds; + } + + public MdmLinkChangeEvent addDuplicateGoldenResourceId(IBaseResource theDuplicateGoldenResourceId) { + String id = getIdAsString(theDuplicateGoldenResourceId); + if (id != null) { + getDuplicateGoldenResourceIds().add(id); + } + return this; + } + +} diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmTransactionContext.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmTransactionContext.java index a2fb07fe200..db699fd602b 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmTransactionContext.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmTransactionContext.java @@ -20,6 +20,7 @@ package ca.uhn.fhir.mdm.model; * #L% */ +import ca.uhn.fhir.mdm.api.MdmLinkChangeEvent; import ca.uhn.fhir.rest.server.TransactionLogMessages; public class MdmTransactionContext { @@ -45,6 +46,8 @@ public class MdmTransactionContext { private String myResourceType; + private MdmLinkChangeEvent myMdmLinkChangeEvent = new MdmLinkChangeEvent(); + public TransactionLogMessages getTransactionLogMessages() { return myTransactionLogMessages; } @@ -92,4 +95,12 @@ public class MdmTransactionContext { public void setResourceType(String myResourceType) { this.myResourceType = myResourceType; } + + public MdmLinkChangeEvent getMdmLinkChangeEvent() { + return myMdmLinkChangeEvent; + } + + public void setMdmLinkChangeEvent(MdmLinkChangeEvent theMdmLinkChangeEvent) { + myMdmLinkChangeEvent = theMdmLinkChangeEvent; + } } From 0d34fe61c81da55d5d641a496cef957de1c5fd4b Mon Sep 17 00:00:00 2001 From: Nick Goupinets Date: Fri, 30 Jul 2021 11:17:56 -0400 Subject: [PATCH 02/13] Adding tests --- .../jpa/mdm/broker/MdmMessageHandler.java | 1 + .../interceptor/MdmStorageInterceptorIT.java | 36 +++++++++++++++++++ .../uhn/fhir/mdm/api/MdmLinkChangeEvent.java | 16 +++++++-- 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java index b80d2caafc7..967349c0823 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java @@ -111,6 +111,7 @@ public class MdmMessageHandler implements MessageHandler { IBaseResource targetResource = theMsg.getPayload(myFhirContext); ResourceOperationMessage outgoingMsg = new ResourceOperationMessage(myFhirContext, targetResource, theMsg.getOperationType()); outgoingMsg.setTransactionId(theMsg.getTransactionId()); + mdmContext.getMdmLinkChangeEvent().setTargetResourceId(targetResource); HookParams params = new HookParams() .add(ResourceOperationMessage.class, outgoingMsg) diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java index 7d24395852d..919911a3e25 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java @@ -91,6 +91,42 @@ public class MdmStorageInterceptorIT extends BaseMdmR4Test { MdmLinkChangeEvent linkChangeEvent = myMdmHelper.getAfterMdmLatch().getLatchInvocationParameterOfType(MdmLinkChangeEvent.class); assertNotNull(linkChangeEvent); assertEquals(link.getGoldenResourcePid(), new IdDt(linkChangeEvent.getGoldenResourceId()).getIdPartAsLong()); + assertEquals(link.getSourcePid(), new IdDt(linkChangeEvent.getTargetResourceId()).getIdPartAsLong()); + } + + @Test + public void testUpdateLinkChangeEvent() throws InterruptedException { + Patient patient1 = addExternalEID(buildJanePatient(), "eid-1"); + patient1 = createPatientAndUpdateLinks(patient1); + + Patient patient2 = addExternalEID(buildJanePatient(), "eid-2"); + patient2 = createPatientAndUpdateLinks(patient2); + + MdmLinkChangeEvent linkChangeEvent = myMdmHelper.getAfterMdmLatch().getLatchInvocationParameterOfType(MdmLinkChangeEvent.class); + assertNotNull(linkChangeEvent); +// assertEquals(link.getGoldenResourcePid(), new IdDt(linkChangeEvent.getGoldenResourceId()).getIdPartAsLong()); +// assertEquals(link.getSourcePid(), new IdDt(linkChangeEvent.getTargetResourceId()).getIdPartAsLong()); + } + + @Test + public void testDuplicateLinkChangeEvent() throws InterruptedException { + fail(); + + Practitioner pr = buildPractitionerWithNameAndId("Young", "AC-DC"); + myMdmHelper.createWithLatch(pr); + + ResourceOperationMessage resourceOperationMessage = myMdmHelper.getAfterMdmLatch().getLatchInvocationParameterOfType(ResourceOperationMessage.class); + assertNotNull(resourceOperationMessage); + assertEquals(pr.getId(), resourceOperationMessage.getId()); + + MdmLink example = new MdmLink(); + example.setSourcePid(pr.getIdElement().getIdPartAsLong()); + MdmLink link = myMdmLinkDao.findAll(Example.of(example)).get(0); + + MdmLinkChangeEvent linkChangeEvent = myMdmHelper.getAfterMdmLatch().getLatchInvocationParameterOfType(MdmLinkChangeEvent.class); + assertNotNull(linkChangeEvent); + assertEquals(link.getGoldenResourcePid(), new IdDt(linkChangeEvent.getGoldenResourceId()).getIdPartAsLong()); + assertEquals(link.getSourcePid(), new IdDt(linkChangeEvent.getTargetResourceId()).getIdPartAsLong()); } @Test diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkChangeEvent.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkChangeEvent.java index 374d955aba8..c0593b2929d 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkChangeEvent.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkChangeEvent.java @@ -1,15 +1,15 @@ package ca.uhn.fhir.mdm.api; +import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; -import java.util.ArrayList; import java.util.HashSet; -import java.util.List; import java.util.Set; public class MdmLinkChangeEvent { + private String myTargetResourceId; private String myGoldenResourceId; private Set myDuplicateGoldenResourceIds = new HashSet<>(); @@ -36,6 +36,18 @@ public class MdmLinkChangeEvent { return idElement.getValueAsString(); } + public String getTargetResourceId() { + return myTargetResourceId; + } + + public void setTargetResourceId(IBaseResource theTargetResource) { + setTargetResourceId(getIdAsString(theTargetResource)); + } + + public void setTargetResourceId(String theTargetResourceId) { + myTargetResourceId = theTargetResourceId; + } + public Set getDuplicateGoldenResourceIds() { return myDuplicateGoldenResourceIds; } From 891a6304d0e021021c540282e8ebf8b7adf6c0f4 Mon Sep 17 00:00:00 2001 From: Nick Goupinets Date: Wed, 4 Aug 2021 11:06:14 -0400 Subject: [PATCH 03/13] WIP --- .../dao/r4/FhirResourceDaoCodeSystemR4.java | 2 +- .../jpa/mdm/broker/MdmMessageHandler.java | 1 - .../uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java | 6 +++ .../interceptor/MdmStorageInterceptorIT.java | 54 ++++++++----------- .../uhn/fhir/mdm/api/MdmLinkChangeEvent.java | 36 ++++++++++++- .../fhir/mdm/model/MdmTransactionContext.java | 1 + 6 files changed, 65 insertions(+), 35 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCodeSystemR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCodeSystemR4.java index 2ff1ee51c70..335cef64375 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCodeSystemR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCodeSystemR4.java @@ -139,7 +139,7 @@ public class FhirResourceDaoCodeSystemR4 extends BaseHapiFhirResourceDao myDuplicateGoldenResourceIds = new HashSet<>(); public String getGoldenResourceId() { @@ -64,4 +88,12 @@ public class MdmLinkChangeEvent { return this; } + @Override + public String toString() { + return "MdmLinkChangeEvent{" + + "myTargetResourceId='" + myTargetResourceId + '\'' + + ", myGoldenResourceId='" + myGoldenResourceId + '\'' + + ", myDuplicateGoldenResourceIds=" + myDuplicateGoldenResourceIds + + '}'; + } } diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmTransactionContext.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmTransactionContext.java index db699fd602b..ce35ea3f150 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmTransactionContext.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmTransactionContext.java @@ -103,4 +103,5 @@ public class MdmTransactionContext { public void setMdmLinkChangeEvent(MdmLinkChangeEvent theMdmLinkChangeEvent) { myMdmLinkChangeEvent = theMdmLinkChangeEvent; } + } From 4be3334f6ff29f13629d65e67957475547de0c46 Mon Sep 17 00:00:00 2001 From: Nick Goupinets Date: Wed, 4 Aug 2021 16:33:59 -0400 Subject: [PATCH 04/13] WIP after persistence checked --- .../dao/r4/FhirResourceDaoCodeSystemR4.java | 2 +- .../jpa/mdm/broker/MdmMessageHandler.java | 30 ++++++++++++++++--- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCodeSystemR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCodeSystemR4.java index 6a01ab94c70..5f2aa32a2ec 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCodeSystemR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirResourceDaoCodeSystemR4.java @@ -138,7 +138,7 @@ public class FhirResourceDaoCodeSystemR4 extends BaseHapiFhirResourceDao mdmLinkBySource = myMdmLinkDaoSvc.findMdmLinkBySource(targetResource); + if (!mdmLinkBySource.isPresent()) { + ourLog.warn("Unable to find link by source for {}", targetResource.getIdElement()); + } + + mdmLinkBySource.ifPresent(link -> { + linkChangeEvent.setMdmMatchResult(link.getMatchResult()); + linkChangeEvent.setMdmLinkSource(link.getLinkSource()); + linkChangeEvent.setEidMatch(link.isEidMatchPresent()); + linkChangeEvent.setNewGoldenResource(link.getHadToCreateNewGoldenResource()); + linkChangeEvent.setScore(link.getScore()); + linkChangeEvent.setRuleCount(link.getRuleCount()); + }); + HookParams params = new HookParams() .add(ResourceOperationMessage.class, outgoingMsg) .add(TransactionLogMessages.class, mdmContext.getTransactionLogMessages()) From 8670f107b5118c274b00c92327478f26f55bc9ad Mon Sep 17 00:00:00 2001 From: Nick Goupinets Date: Tue, 17 Aug 2021 13:17:44 -0400 Subject: [PATCH 05/13] Updated link expansion --- .../ca/uhn/fhir/jpa/dao/data/IMdmLinkDao.java | 9 +++ .../fhir/jpa/dao/mdm/MdmLinkExpandSvc.java | 22 +++++- .../fhir/jpa/mdm/dao/MdmLinkDaoSvcTest.java | 9 +++ .../uhn/fhir/mdm/api/MdmLinkChangeEvent.java | 72 ++++++++++++++++++- 4 files changed, 106 insertions(+), 6 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkDao.java index 16834facbe2..be0a6289a4e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkDao.java @@ -67,4 +67,13 @@ public interface IMdmLinkDao extends JpaRepository { "AND ml.myMatchResult=:matchResult") List expandPidsBySourcePidAndMatchResult(@Param("sourcePid") Long theSourcePid, @Param("matchResult") MdmMatchResultEnum theMdmMatchResultEnum); + @Query("SELECT DISTINCT ml.myGoldenResourcePid as goldenPid, ml.mySourcePid as sourcePid " + + "FROM MdmLink ml " + + "INNER JOIN MdmLink ml2 " + + "ON ml.myGoldenResourcePid = ml2.myGoldenResourcePid " + + "WHERE (ml2.mySourcePid = :sourceOrGoldenPid OR ml2.myGoldenResourcePid = :sourceOrGoldenPid) " + + "AND ml2.myMatchResult=:matchResult " + + "AND ml.myMatchResult=:matchResult") + List expandPidsBySourceOrGoldenResourcePidAndMatchResult(@Param("sourceOrGoldenPid") Long theSourceOrGoldenPid, @Param("matchResult") MdmMatchResultEnum theMdmMatchResultEnum); + } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmLinkExpandSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmLinkExpandSvc.java index 66af7f5a706..b03e22d04b8 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmLinkExpandSvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmLinkExpandSvc.java @@ -72,6 +72,19 @@ public class MdmLinkExpandSvc { return expandMdmBySourceResourcePid(pidOrThrowException); } + /** + * Given a resource ID of a source resource or golden resource, perform MDM expansion and return all the resource + * IDs of all resources that are MDM-Matched to this resource. + * + * @param theId The Resource ID of the resource to MDM-Expand + * @return A set of strings representing the FHIR ids of the expanded resources. + */ + public Set expandMdmBySourceOrGoldenResourceId(IIdType theId) { + ourLog.debug("About to expand source resource with resource id {}", theId); + Long pidOrThrowException = myIdHelperService.getPidOrThrowException(theId); + return flatten(myMdmLinkDao.expandPidsBySourceOrGoldenResourcePidAndMatchResult(pidOrThrowException, MdmMatchResultEnum.MATCH)); + } + /** * Given a PID of a source resource, perform MDM expansion and return all the resource IDs of all resources that are * MDM-Matched to this resource. @@ -81,14 +94,17 @@ public class MdmLinkExpandSvc { */ public Set expandMdmBySourceResourcePid(Long theSourceResourcePid) { ourLog.debug("About to expand source resource with PID {}", theSourceResourcePid); - List goldenPidSourcePidTuples = myMdmLinkDao.expandPidsBySourcePidAndMatchResult(theSourceResourcePid, MdmMatchResultEnum.MATCH); + return flatten(myMdmLinkDao.expandPidsBySourcePidAndMatchResult(theSourceResourcePid, MdmMatchResultEnum.MATCH)); + } + + protected Set flatten(List thePidTuples) { Set flattenedPids = new HashSet<>(); - goldenPidSourcePidTuples.forEach(tuple -> { + thePidTuples.forEach(tuple -> { flattenedPids.add(tuple.getSourcePid()); flattenedPids.add(tuple.getGoldenPid()); }); Set resourceIds = myIdHelperService.translatePidsToFhirResourceIds(flattenedPids); - ourLog.debug("Pid {} has been expanded to [{}]", theSourceResourcePid, String.join(",", resourceIds)); + ourLog.debug("Expanded pids are [{}]", String.join(",", resourceIds)); return resourceIds; } diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/dao/MdmLinkDaoSvcTest.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/dao/MdmLinkDaoSvcTest.java index ae034af6fa7..b5deb25f0c6 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/dao/MdmLinkDaoSvcTest.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/dao/MdmLinkDaoSvcTest.java @@ -83,6 +83,15 @@ public class MdmLinkDaoSvcTest extends BaseMdmR4Test { assertThat(lists, hasSize(10)); + lists.stream() + .forEach(tuple -> { + assertThat(tuple.getGoldenPid(), is(equalTo(golden.getIdElement().getIdPartAsLong()))); + assertThat(tuple.getSourcePid(), is(in(expectedExpandedPids))); + }); + + lists = myMdmLinkDao.expandPidsBySourceOrGoldenResourcePidAndMatchResult(mdmLinks.get(0).getGoldenResourcePid(), MdmMatchResultEnum.MATCH); + assertThat(lists, hasSize(10)); + lists.stream() .forEach(tuple -> { assertThat(tuple.getGoldenPid(), is(equalTo(golden.getIdElement().getIdPartAsLong()))); diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkChangeEvent.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkChangeEvent.java index e2d6b199812..8e306c16e6e 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkChangeEvent.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkChangeEvent.java @@ -30,9 +30,21 @@ import java.util.Set; public class MdmLinkChangeEvent implements IModelJson { - @JsonProperty(value = "targetResourceId") + @JsonProperty(value = "matchResult") + private MdmMatchResultEnum myMdmMatchResult; + @JsonProperty(value = "linkSource") + private MdmLinkSourceEnum myMdmLinkSource; + @JsonProperty(value = "eidMatch") + private Boolean myEidMatch; + @JsonProperty(value = "newGoldenResource") + private Boolean myNewGoldenResource; + @JsonProperty(value = "score") + private Double myScore; + @JsonProperty(value = "ruleCount") + private Long myRuleCount; + @JsonProperty(value = "targetResourceId", required = true) private String myTargetResourceId; - @JsonProperty(value = "goldenResourceId") + @JsonProperty(value = "goldenResourceId", required = true) private String myGoldenResourceId; @JsonProperty(value = "duplicateResourceIds") private Set myDuplicateGoldenResourceIds = new HashSet<>(); @@ -88,10 +100,64 @@ public class MdmLinkChangeEvent implements IModelJson { return this; } + public MdmMatchResultEnum getMdmMatchResult() { + return myMdmMatchResult; + } + + public void setMdmMatchResult(MdmMatchResultEnum theMdmMatchResult) { + myMdmMatchResult = theMdmMatchResult; + } + + public MdmLinkSourceEnum getMdmLinkSource() { + return myMdmLinkSource; + } + + public void setMdmLinkSource(MdmLinkSourceEnum theMdmLinkSource) { + myMdmLinkSource = theMdmLinkSource; + } + + public Boolean getEidMatch() { + return myEidMatch; + } + + public void setEidMatch(Boolean theEidMatch) { + myEidMatch = theEidMatch; + } + + public Boolean getNewGoldenResource() { + return myNewGoldenResource; + } + + public void setNewGoldenResource(Boolean theNewGoldenResource) { + myNewGoldenResource = theNewGoldenResource; + } + + public Double getScore() { + return myScore; + } + + public void setScore(Double theScore) { + myScore = theScore; + } + + public Long getRuleCount() { + return myRuleCount; + } + + public void setRuleCount(Long theRuleCount) { + myRuleCount = theRuleCount; + } + @Override public String toString() { return "MdmLinkChangeEvent{" + - "myTargetResourceId='" + myTargetResourceId + '\'' + + "myMdmMatchResult=" + myMdmMatchResult + + ", myMdmLinkSource=" + myMdmLinkSource + + ", myEidMatch=" + myEidMatch + + ", myNewGoldenResource=" + myNewGoldenResource + + ", myScore=" + myScore + + ", myRuleCount=" + myRuleCount + + ", myTargetResourceId='" + myTargetResourceId + '\'' + ", myGoldenResourceId='" + myGoldenResourceId + '\'' + ", myDuplicateGoldenResourceIds=" + myDuplicateGoldenResourceIds + '}'; From 1e41621eca369ceb8e3a116db4002d1b334cc33f Mon Sep 17 00:00:00 2001 From: Nick Goupinets Date: Thu, 19 Aug 2021 16:43:51 -0400 Subject: [PATCH 06/13] Rolled back changes --- .../ca/uhn/fhir/jpa/dao/data/IMdmLinkDao.java | 9 -------- .../fhir/jpa/dao/mdm/MdmLinkExpandSvc.java | 22 +++---------------- .../fhir/jpa/mdm/dao/MdmLinkDaoSvcTest.java | 9 -------- 3 files changed, 3 insertions(+), 37 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkDao.java index be0a6289a4e..16834facbe2 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IMdmLinkDao.java @@ -67,13 +67,4 @@ public interface IMdmLinkDao extends JpaRepository { "AND ml.myMatchResult=:matchResult") List expandPidsBySourcePidAndMatchResult(@Param("sourcePid") Long theSourcePid, @Param("matchResult") MdmMatchResultEnum theMdmMatchResultEnum); - @Query("SELECT DISTINCT ml.myGoldenResourcePid as goldenPid, ml.mySourcePid as sourcePid " + - "FROM MdmLink ml " + - "INNER JOIN MdmLink ml2 " + - "ON ml.myGoldenResourcePid = ml2.myGoldenResourcePid " + - "WHERE (ml2.mySourcePid = :sourceOrGoldenPid OR ml2.myGoldenResourcePid = :sourceOrGoldenPid) " + - "AND ml2.myMatchResult=:matchResult " + - "AND ml.myMatchResult=:matchResult") - List expandPidsBySourceOrGoldenResourcePidAndMatchResult(@Param("sourceOrGoldenPid") Long theSourceOrGoldenPid, @Param("matchResult") MdmMatchResultEnum theMdmMatchResultEnum); - } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmLinkExpandSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmLinkExpandSvc.java index b03e22d04b8..66af7f5a706 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmLinkExpandSvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/mdm/MdmLinkExpandSvc.java @@ -72,19 +72,6 @@ public class MdmLinkExpandSvc { return expandMdmBySourceResourcePid(pidOrThrowException); } - /** - * Given a resource ID of a source resource or golden resource, perform MDM expansion and return all the resource - * IDs of all resources that are MDM-Matched to this resource. - * - * @param theId The Resource ID of the resource to MDM-Expand - * @return A set of strings representing the FHIR ids of the expanded resources. - */ - public Set expandMdmBySourceOrGoldenResourceId(IIdType theId) { - ourLog.debug("About to expand source resource with resource id {}", theId); - Long pidOrThrowException = myIdHelperService.getPidOrThrowException(theId); - return flatten(myMdmLinkDao.expandPidsBySourceOrGoldenResourcePidAndMatchResult(pidOrThrowException, MdmMatchResultEnum.MATCH)); - } - /** * Given a PID of a source resource, perform MDM expansion and return all the resource IDs of all resources that are * MDM-Matched to this resource. @@ -94,17 +81,14 @@ public class MdmLinkExpandSvc { */ public Set expandMdmBySourceResourcePid(Long theSourceResourcePid) { ourLog.debug("About to expand source resource with PID {}", theSourceResourcePid); - return flatten(myMdmLinkDao.expandPidsBySourcePidAndMatchResult(theSourceResourcePid, MdmMatchResultEnum.MATCH)); - } - - protected Set flatten(List thePidTuples) { + List goldenPidSourcePidTuples = myMdmLinkDao.expandPidsBySourcePidAndMatchResult(theSourceResourcePid, MdmMatchResultEnum.MATCH); Set flattenedPids = new HashSet<>(); - thePidTuples.forEach(tuple -> { + goldenPidSourcePidTuples.forEach(tuple -> { flattenedPids.add(tuple.getSourcePid()); flattenedPids.add(tuple.getGoldenPid()); }); Set resourceIds = myIdHelperService.translatePidsToFhirResourceIds(flattenedPids); - ourLog.debug("Expanded pids are [{}]", String.join(",", resourceIds)); + ourLog.debug("Pid {} has been expanded to [{}]", theSourceResourcePid, String.join(",", resourceIds)); return resourceIds; } diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/dao/MdmLinkDaoSvcTest.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/dao/MdmLinkDaoSvcTest.java index b5deb25f0c6..ae034af6fa7 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/dao/MdmLinkDaoSvcTest.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/dao/MdmLinkDaoSvcTest.java @@ -83,15 +83,6 @@ public class MdmLinkDaoSvcTest extends BaseMdmR4Test { assertThat(lists, hasSize(10)); - lists.stream() - .forEach(tuple -> { - assertThat(tuple.getGoldenPid(), is(equalTo(golden.getIdElement().getIdPartAsLong()))); - assertThat(tuple.getSourcePid(), is(in(expectedExpandedPids))); - }); - - lists = myMdmLinkDao.expandPidsBySourceOrGoldenResourcePidAndMatchResult(mdmLinks.get(0).getGoldenResourcePid(), MdmMatchResultEnum.MATCH); - assertThat(lists, hasSize(10)); - lists.stream() .forEach(tuple -> { assertThat(tuple.getGoldenPid(), is(equalTo(golden.getIdElement().getIdPartAsLong()))); From 6ea58f74d1e3317683242a8fd0f94ac8bb2b881d Mon Sep 17 00:00:00 2001 From: Nick Goupinets Date: Mon, 23 Aug 2021 10:55:09 -0400 Subject: [PATCH 07/13] Code review --- .../jpa/mdm/broker/MdmMessageHandler.java | 22 +++----- .../jpa/mdm/config/MdmConsumerConfig.java | 11 +++- .../jpa/mdm/svc/IMdmModelConverterSvc.java | 39 ++++++++++++++ ...cImpl.java => MdmLinkQuerySvcImplSvc.java} | 32 ++++-------- .../jpa/mdm/svc/MdmModelConverterSvcImpl.java | 52 +++++++++++++++++++ .../interceptor/MdmStorageInterceptorIT.java | 7 +-- ...LinkChangeEvent.java => MdmLinkEvent.java} | 21 +++++++- .../java/ca/uhn/fhir/mdm/api/MdmLinkJson.java | 19 ++++++- .../fhir/mdm/model/MdmTransactionContext.java | 12 ++--- 9 files changed, 160 insertions(+), 55 deletions(-) create mode 100644 hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/IMdmModelConverterSvc.java rename hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/{MdmLinkQuerySvcImpl.java => MdmLinkQuerySvcImplSvc.java} (73%) create mode 100644 hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmModelConverterSvcImpl.java rename hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/{MdmLinkChangeEvent.java => MdmLinkEvent.java} (86%) diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java index 5f103311abb..701f00b034b 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java @@ -26,15 +26,14 @@ import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.jpa.entity.MdmLink; import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc; -import ca.uhn.fhir.jpa.mdm.svc.MdmLinkSvcImpl; +import ca.uhn.fhir.jpa.mdm.svc.IMdmModelConverterSvc; import ca.uhn.fhir.jpa.mdm.svc.MdmMatchLinkSvc; import ca.uhn.fhir.jpa.mdm.svc.MdmResourceFilteringSvc; import ca.uhn.fhir.jpa.mdm.svc.candidate.TooManyCandidatesException; import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedJsonMessage; import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage; -import ca.uhn.fhir.mdm.api.IMdmLinkSvc; import ca.uhn.fhir.mdm.api.IMdmSettings; -import ca.uhn.fhir.mdm.api.MdmLinkChangeEvent; +import ca.uhn.fhir.mdm.api.MdmLinkEvent; import ca.uhn.fhir.mdm.log.Logs; import ca.uhn.fhir.mdm.model.MdmTransactionContext; import ca.uhn.fhir.rest.server.TransactionLogMessages; @@ -42,7 +41,6 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.messaging.ResourceOperationMessage; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBaseResource; -import org.hl7.fhir.instance.model.api.IIdType; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.Message; @@ -69,6 +67,8 @@ public class MdmMessageHandler implements MessageHandler { private MdmResourceFilteringSvc myMdmResourceFilteringSvc; @Autowired private IMdmSettings myMdmSettings; + @Autowired + private IMdmModelConverterSvc myModelConverter; @Override public void handleMessage(Message theMessage) throws MessagingException { @@ -119,25 +119,17 @@ public class MdmMessageHandler implements MessageHandler { ResourceOperationMessage outgoingMsg = new ResourceOperationMessage(myFhirContext, targetResource, theMsg.getOperationType()); outgoingMsg.setTransactionId(theMsg.getTransactionId()); - MdmLinkChangeEvent linkChangeEvent = mdmContext.getMdmLinkChangeEvent(); + MdmLinkEvent linkChangeEvent = mdmContext.getMdmLinkChangeEvent(); Optional mdmLinkBySource = myMdmLinkDaoSvc.findMdmLinkBySource(targetResource); if (!mdmLinkBySource.isPresent()) { ourLog.warn("Unable to find link by source for {}", targetResource.getIdElement()); } - mdmLinkBySource.ifPresent(link -> { - linkChangeEvent.setMdmMatchResult(link.getMatchResult()); - linkChangeEvent.setMdmLinkSource(link.getLinkSource()); - linkChangeEvent.setEidMatch(link.isEidMatchPresent()); - linkChangeEvent.setNewGoldenResource(link.getHadToCreateNewGoldenResource()); - linkChangeEvent.setScore(link.getScore()); - linkChangeEvent.setRuleCount(link.getRuleCount()); - }); - + mdmLinkBySource.ifPresent(link -> linkChangeEvent.setFromLink(myModelConverter.toJson(link))); HookParams params = new HookParams() .add(ResourceOperationMessage.class, outgoingMsg) .add(TransactionLogMessages.class, mdmContext.getTransactionLogMessages()) - .add(MdmLinkChangeEvent.class, mdmContext.getMdmLinkChangeEvent()); + .add(MdmLinkEvent.class, mdmContext.getMdmLinkChangeEvent()); myInterceptorBroadcaster.callHooks(Pointcut.MDM_AFTER_PERSISTED_RESOURCE_CHECKED, params); } diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmConsumerConfig.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmConsumerConfig.java index 51981cdecaa..56f81f8b87a 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmConsumerConfig.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmConsumerConfig.java @@ -31,15 +31,17 @@ import ca.uhn.fhir.jpa.mdm.dao.MdmLinkFactory; import ca.uhn.fhir.jpa.mdm.interceptor.IMdmStorageInterceptor; import ca.uhn.fhir.jpa.mdm.interceptor.MdmStorageInterceptor; import ca.uhn.fhir.jpa.mdm.svc.GoldenResourceMergerSvcImpl; +import ca.uhn.fhir.jpa.mdm.svc.IMdmModelConverterSvc; import ca.uhn.fhir.jpa.mdm.svc.MdmClearSvcImpl; import ca.uhn.fhir.jpa.mdm.svc.MdmControllerSvcImpl; import ca.uhn.fhir.jpa.mdm.svc.MdmEidUpdateService; import ca.uhn.fhir.jpa.mdm.svc.MdmGoldenResourceDeletingSvc; -import ca.uhn.fhir.jpa.mdm.svc.MdmLinkQuerySvcImpl; +import ca.uhn.fhir.jpa.mdm.svc.MdmLinkQuerySvcImplSvc; import ca.uhn.fhir.jpa.mdm.svc.MdmLinkSvcImpl; import ca.uhn.fhir.jpa.mdm.svc.MdmLinkUpdaterSvcImpl; import ca.uhn.fhir.jpa.mdm.svc.MdmMatchFinderSvcImpl; import ca.uhn.fhir.jpa.mdm.svc.MdmMatchLinkSvc; +import ca.uhn.fhir.jpa.mdm.svc.MdmModelConverterSvcImpl; import ca.uhn.fhir.jpa.mdm.svc.MdmResourceDaoSvc; import ca.uhn.fhir.jpa.mdm.svc.MdmResourceFilteringSvc; import ca.uhn.fhir.jpa.mdm.svc.MdmSearchParamSvc; @@ -179,7 +181,12 @@ public class MdmConsumerConfig { @Bean IMdmLinkQuerySvc mdmLinkQuerySvc() { - return new MdmLinkQuerySvcImpl(); + return new MdmLinkQuerySvcImplSvc(); + } + + @Bean + IMdmModelConverterSvc mdmModelConverterSvc() { + return new MdmModelConverterSvcImpl(); } @Bean diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/IMdmModelConverterSvc.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/IMdmModelConverterSvc.java new file mode 100644 index 00000000000..67c534a9489 --- /dev/null +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/IMdmModelConverterSvc.java @@ -0,0 +1,39 @@ +package ca.uhn.fhir.jpa.mdm.svc; + +/*- + * #%L + * HAPI FHIR JPA Server - Master Data Management + * %% + * Copyright (C) 2014 - 2021 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import ca.uhn.fhir.jpa.entity.MdmLink; +import ca.uhn.fhir.mdm.api.MdmLinkJson; + +/** + * Contract for decoupling API dependency from the base / JPA modules. + */ +public interface IMdmModelConverterSvc { + + /** + * Creates JSON representation of the provided MDM link + * + * @param theLink Link to convert + * @return Returns the converted link + */ + public MdmLinkJson toJson(MdmLink theLink); + +} diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkQuerySvcImpl.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkQuerySvcImplSvc.java similarity index 73% rename from hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkQuerySvcImpl.java rename to hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkQuerySvcImplSvc.java index ea00e81ec4d..996dbe8e5cc 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkQuerySvcImpl.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkQuerySvcImplSvc.java @@ -36,20 +36,24 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Example; import org.springframework.data.domain.Page; -public class MdmLinkQuerySvcImpl implements IMdmLinkQuerySvc { +public class MdmLinkQuerySvcImplSvc implements IMdmLinkQuerySvc { - private static final Logger ourLog = LoggerFactory.getLogger(MdmLinkQuerySvcImpl.class); + private static final Logger ourLog = LoggerFactory.getLogger(MdmLinkQuerySvcImplSvc.class); + + @Autowired + MdmLinkDaoSvc myMdmLinkDaoSvc; @Autowired IdHelperService myIdHelperService; + @Autowired - MdmLinkDaoSvc myMdmLinkDaoSvc; + IMdmModelConverterSvc myMdmModelConverterSvc; @Override public Page queryLinks(IIdType theGoldenResourceId, IIdType theSourceResourceId, MdmMatchResultEnum theMatchResult, MdmLinkSourceEnum theLinkSource, MdmTransactionContext theMdmContext, MdmPageRequest thePageRequest) { Example exampleLink = exampleLinkFromParameters(theGoldenResourceId, theSourceResourceId, theMatchResult, theLinkSource); Page mdmLinkByExample = myMdmLinkDaoSvc.findMdmLinkByExample(exampleLink, thePageRequest); - Page map = mdmLinkByExample.map(this::toJson); + Page map = mdmLinkByExample.map(myMdmModelConverterSvc::toJson); return map; } @@ -57,28 +61,10 @@ public class MdmLinkQuerySvcImpl implements IMdmLinkQuerySvc { public Page getDuplicateGoldenResources(MdmTransactionContext theMdmContext, MdmPageRequest thePageRequest) { Example exampleLink = exampleLinkFromParameters(null, null, MdmMatchResultEnum.POSSIBLE_DUPLICATE, null); Page mdmLinkPage = myMdmLinkDaoSvc.findMdmLinkByExample(exampleLink, thePageRequest); - Page map = mdmLinkPage.map(this::toJson); + Page map = mdmLinkPage.map(myMdmModelConverterSvc::toJson); return map; } - private MdmLinkJson toJson(MdmLink theLink) { - MdmLinkJson retval = new MdmLinkJson(); - String sourceId = myIdHelperService.resourceIdFromPidOrThrowException(theLink.getSourcePid()).toVersionless().getValue(); - retval.setSourceId(sourceId); - String goldenResourceId = myIdHelperService.resourceIdFromPidOrThrowException(theLink.getGoldenResourcePid()).toVersionless().getValue(); - retval.setGoldenResourceId(goldenResourceId); - retval.setCreated(theLink.getCreated()); - retval.setEidMatch(theLink.getEidMatch()); - retval.setLinkSource(theLink.getLinkSource()); - retval.setMatchResult(theLink.getMatchResult()); - retval.setLinkCreatedNewResource(theLink.getHadToCreateNewGoldenResource()); - retval.setScore(theLink.getScore()); - retval.setUpdated(theLink.getUpdated()); - retval.setVector(theLink.getVector()); - retval.setVersion(theLink.getVersion()); - return retval; - } - private Example exampleLinkFromParameters(IIdType theGoldenResourceId, IIdType theSourceId, MdmMatchResultEnum theMatchResult, MdmLinkSourceEnum theLinkSource) { MdmLink mdmLink = myMdmLinkDaoSvc.newMdmLink(); if (theGoldenResourceId != null) { diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmModelConverterSvcImpl.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmModelConverterSvcImpl.java new file mode 100644 index 00000000000..195ac388e68 --- /dev/null +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmModelConverterSvcImpl.java @@ -0,0 +1,52 @@ +package ca.uhn.fhir.jpa.mdm.svc; + +/*- + * #%L + * HAPI FHIR JPA Server - Master Data Management + * %% + * Copyright (C) 2014 - 2021 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import ca.uhn.fhir.jpa.dao.index.IdHelperService; +import ca.uhn.fhir.jpa.entity.MdmLink; +import ca.uhn.fhir.mdm.api.MdmLinkJson; +import org.springframework.beans.factory.annotation.Autowired; + +public class MdmModelConverterSvcImpl implements IMdmModelConverterSvc { + + @Autowired + IdHelperService myIdHelperService; + + public MdmLinkJson toJson(MdmLink theLink) { + MdmLinkJson retval = new MdmLinkJson(); + String sourceId = myIdHelperService.resourceIdFromPidOrThrowException(theLink.getSourcePid()).toVersionless().getValue(); + retval.setSourceId(sourceId); + String goldenResourceId = myIdHelperService.resourceIdFromPidOrThrowException(theLink.getGoldenResourcePid()).toVersionless().getValue(); + retval.setGoldenResourceId(goldenResourceId); + retval.setCreated(theLink.getCreated()); + retval.setEidMatch(theLink.getEidMatch()); + retval.setLinkSource(theLink.getLinkSource()); + retval.setMatchResult(theLink.getMatchResult()); + retval.setLinkCreatedNewResource(theLink.getHadToCreateNewGoldenResource()); + retval.setScore(theLink.getScore()); + retval.setUpdated(theLink.getUpdated()); + retval.setVector(theLink.getVector()); + retval.setVersion(theLink.getVersion()); + retval.setRuleCount(theLink.getRuleCount()); + return retval; + } + +} diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java index 4d729730f7c..cd1518cbc59 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java @@ -7,7 +7,7 @@ import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test; import ca.uhn.fhir.jpa.mdm.helper.MdmHelperConfig; import ca.uhn.fhir.jpa.mdm.helper.MdmHelperR4; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; -import ca.uhn.fhir.mdm.api.MdmLinkChangeEvent; +import ca.uhn.fhir.mdm.api.MdmLinkEvent; import ca.uhn.fhir.mdm.model.CanonicalEID; import ca.uhn.fhir.mdm.model.MdmTransactionContext; import ca.uhn.fhir.mdm.rules.config.MdmSettings; @@ -32,14 +32,11 @@ import org.junit.jupiter.api.extension.RegisterExtension; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Example; -import org.springframework.data.domain.ExampleMatcher; -import org.springframework.data.domain.Pageable; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import java.util.Date; import java.util.List; -import java.util.Optional; import static ca.uhn.fhir.mdm.api.MdmConstants.CODE_GOLDEN_RECORD; import static ca.uhn.fhir.mdm.api.MdmConstants.CODE_GOLDEN_RECORD_REDIRECTED; @@ -89,7 +86,7 @@ public class MdmStorageInterceptorIT extends BaseMdmR4Test { MdmLink link = getLinkByTargetId(pr); - MdmLinkChangeEvent linkChangeEvent = myMdmHelper.getAfterMdmLatch().getLatchInvocationParameterOfType(MdmLinkChangeEvent.class); + MdmLinkEvent linkChangeEvent = myMdmHelper.getAfterMdmLatch().getLatchInvocationParameterOfType(MdmLinkEvent.class); assertNotNull(linkChangeEvent); assertEquals(link.getGoldenResourcePid(), new IdDt(linkChangeEvent.getGoldenResourceId()).getIdPartAsLong()); assertEquals(link.getSourcePid(), new IdDt(linkChangeEvent.getTargetResourceId()).getIdPartAsLong()); diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkChangeEvent.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkEvent.java similarity index 86% rename from hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkChangeEvent.java rename to hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkEvent.java index 8e306c16e6e..7eda6f328d8 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkChangeEvent.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkEvent.java @@ -28,7 +28,7 @@ import org.hl7.fhir.instance.model.api.IIdType; import java.util.HashSet; import java.util.Set; -public class MdmLinkChangeEvent implements IModelJson { +public class MdmLinkEvent implements IModelJson { @JsonProperty(value = "matchResult") private MdmMatchResultEnum myMdmMatchResult; @@ -92,7 +92,7 @@ public class MdmLinkChangeEvent implements IModelJson { myDuplicateGoldenResourceIds = theDuplicateGoldenResourceIds; } - public MdmLinkChangeEvent addDuplicateGoldenResourceId(IBaseResource theDuplicateGoldenResourceId) { + public MdmLinkEvent addDuplicateGoldenResourceId(IBaseResource theDuplicateGoldenResourceId) { String id = getIdAsString(theDuplicateGoldenResourceId); if (id != null) { getDuplicateGoldenResourceIds().add(id); @@ -121,6 +121,10 @@ public class MdmLinkChangeEvent implements IModelJson { } public void setEidMatch(Boolean theEidMatch) { + if (theEidMatch == null) { + myEidMatch = Boolean.FALSE; + return; + } myEidMatch = theEidMatch; } @@ -129,6 +133,10 @@ public class MdmLinkChangeEvent implements IModelJson { } public void setNewGoldenResource(Boolean theNewGoldenResource) { + if (theNewGoldenResource == null) { + myNewGoldenResource = Boolean.FALSE; + return; + } myNewGoldenResource = theNewGoldenResource; } @@ -148,6 +156,15 @@ public class MdmLinkChangeEvent implements IModelJson { myRuleCount = theRuleCount; } + public void setFromLink(MdmLinkJson theMdmLinkJson) { + setMdmMatchResult(theMdmLinkJson.getMatchResult()); + setMdmLinkSource(theMdmLinkJson.getLinkSource()); + setEidMatch(theMdmLinkJson.getEidMatch()); + setNewGoldenResource(theMdmLinkJson.getLinkCreatedNewResource()); + setScore(theMdmLinkJson.getScore()); + setRuleCount(theMdmLinkJson.getRuleCount()); + } + @Override public String toString() { return "MdmLinkChangeEvent{" + diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkJson.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkJson.java index 744aa384823..424298418b4 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkJson.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkJson.java @@ -48,11 +48,15 @@ public class MdmLinkJson implements IModelJson { @JsonProperty("version") private String myVersion; - /** This link was created as a result of an eid match **/ + /** + * This link was created as a result of an eid match + **/ @JsonProperty("eidMatch") private Boolean myEidMatch; - /** This link created a new golden resource **/ + /** + * This link created a new golden resource + **/ @JsonProperty("linkCreatedNewGoldenResource") private Boolean myLinkCreatedNewResource; @@ -62,6 +66,9 @@ public class MdmLinkJson implements IModelJson { @JsonProperty("score") private Double myScore; + @JsonProperty("ruleCount") + private Long myRuleCount; + public String getGoldenResourceId() { return myGoldenResourceId; } @@ -160,4 +167,12 @@ public class MdmLinkJson implements IModelJson { myScore = theScore; return this; } + + public Long getRuleCount() { + return myRuleCount; + } + + public void setRuleCount(Long theRuleCount) { + myRuleCount = theRuleCount; + } } diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmTransactionContext.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmTransactionContext.java index ce35ea3f150..9853918a9d7 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmTransactionContext.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmTransactionContext.java @@ -20,7 +20,7 @@ package ca.uhn.fhir.mdm.model; * #L% */ -import ca.uhn.fhir.mdm.api.MdmLinkChangeEvent; +import ca.uhn.fhir.mdm.api.MdmLinkEvent; import ca.uhn.fhir.rest.server.TransactionLogMessages; public class MdmTransactionContext { @@ -46,7 +46,7 @@ public class MdmTransactionContext { private String myResourceType; - private MdmLinkChangeEvent myMdmLinkChangeEvent = new MdmLinkChangeEvent(); + private MdmLinkEvent myMdmLinkEvent = new MdmLinkEvent(); public TransactionLogMessages getTransactionLogMessages() { return myTransactionLogMessages; @@ -96,12 +96,12 @@ public class MdmTransactionContext { this.myResourceType = myResourceType; } - public MdmLinkChangeEvent getMdmLinkChangeEvent() { - return myMdmLinkChangeEvent; + public MdmLinkEvent getMdmLinkChangeEvent() { + return myMdmLinkEvent; } - public void setMdmLinkChangeEvent(MdmLinkChangeEvent theMdmLinkChangeEvent) { - myMdmLinkChangeEvent = theMdmLinkChangeEvent; + public void setMdmLinkChangeEvent(MdmLinkEvent theMdmLinkEvent) { + myMdmLinkEvent = theMdmLinkEvent; } } From 3a5e391c77690c0cc57975cd6dd9cf478fe174a0 Mon Sep 17 00:00:00 2001 From: Nick Goupinets Date: Mon, 23 Aug 2021 11:00:04 -0400 Subject: [PATCH 08/13] Fixed pointcut def --- .../src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java index d8f0f33ae9f..30f81b90889 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java @@ -1996,7 +1996,7 @@ public enum Pointcut implements IPointcut { MDM_AFTER_PERSISTED_RESOURCE_CHECKED(void.class, "ca.uhn.fhir.rest.server.messaging.ResourceOperationMessage", "ca.uhn.fhir.rest.server.TransactionLogMessages", - "ca.uhn.fhir.mdm.api.MdmLinkChangeEvent"), + "ca.uhn.fhir.mdm.api.MdmLinkEvent"), /** * Performance Tracing Hook: From 91a961f9fd6ec0b4cfc61746891a1b086cb30799 Mon Sep 17 00:00:00 2001 From: Nick Goupinets Date: Fri, 27 Aug 2021 10:48:19 -0400 Subject: [PATCH 09/13] Added changelog --- .../hapi/fhir/changelog/5_6_0/2850-updated-mdm-pointcut.yaml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/2850-updated-mdm-pointcut.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/2850-updated-mdm-pointcut.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/2850-updated-mdm-pointcut.yaml new file mode 100644 index 00000000000..712e336d130 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/2850-updated-mdm-pointcut.yaml @@ -0,0 +1,4 @@ +--- +type: add +issue: 2850 +title: "Updated handling of MDM_AFTER_PERSISTED_RESOURCE_CHECKED pointcut to include additional MDM related info." From 4e012236c21e3614bc2615ffed7bfe67c9c5ea01 Mon Sep 17 00:00:00 2001 From: Nick Goupinets Date: Fri, 27 Aug 2021 16:31:23 -0400 Subject: [PATCH 10/13] Updated comments and added misc refactorings --- .../fhir/jpa/mdm/broker/MdmMessageHandler.java | 4 ++-- .../fhir/jpa/mdm/svc/MdmEidUpdateService.java | 2 +- .../uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java | 12 ++++++------ .../candidate/FindCandidateByExampleSvc.java | 10 +++++----- .../ca/uhn/fhir/jpa/mdm/BaseMdmR4Test.java | 4 ++-- .../interceptor/MdmStorageInterceptorIT.java | 18 ++++++++++++------ .../fhir/mdm/model/MdmTransactionContext.java | 2 +- 7 files changed, 29 insertions(+), 23 deletions(-) diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java index 701f00b034b..6589d6b41e9 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java @@ -119,7 +119,7 @@ public class MdmMessageHandler implements MessageHandler { ResourceOperationMessage outgoingMsg = new ResourceOperationMessage(myFhirContext, targetResource, theMsg.getOperationType()); outgoingMsg.setTransactionId(theMsg.getTransactionId()); - MdmLinkEvent linkChangeEvent = mdmContext.getMdmLinkChangeEvent(); + MdmLinkEvent linkChangeEvent = mdmContext.getMdmLinkEvent(); Optional mdmLinkBySource = myMdmLinkDaoSvc.findMdmLinkBySource(targetResource); if (!mdmLinkBySource.isPresent()) { ourLog.warn("Unable to find link by source for {}", targetResource.getIdElement()); @@ -129,7 +129,7 @@ public class MdmMessageHandler implements MessageHandler { HookParams params = new HookParams() .add(ResourceOperationMessage.class, outgoingMsg) .add(TransactionLogMessages.class, mdmContext.getTransactionLogMessages()) - .add(MdmLinkEvent.class, mdmContext.getMdmLinkChangeEvent()); + .add(MdmLinkEvent.class, mdmContext.getMdmLinkEvent()); myInterceptorBroadcaster.callHooks(Pointcut.MDM_AFTER_PERSISTED_RESOURCE_CHECKED, params); } diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmEidUpdateService.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmEidUpdateService.java index d23c27db9f1..2e035615200 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmEidUpdateService.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmEidUpdateService.java @@ -86,7 +86,7 @@ public class MdmEidUpdateService { myMdmResourceDaoSvc.upsertGoldenResource(updateContext.getMatchedGoldenResource(), theMdmTransactionContext.getResourceType()); } - theMdmTransactionContext.getMdmLinkChangeEvent().setGoldenResourceId(updateContext.getExistingGoldenResource()); + theMdmTransactionContext.getMdmLinkEvent().setGoldenResourceId(updateContext.getExistingGoldenResource()); } private void handleNoEidsInCommon(IAnyResource theResource, MatchedGoldenResourceCandidate theMatchedGoldenResourceCandidate, MdmTransactionContext theMdmTransactionContext, MdmUpdateContext theUpdateContext) { diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java index e0f6b932557..ce285b58bac 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java @@ -78,7 +78,7 @@ public class MdmMatchLinkSvc { private MdmTransactionContext doMdmUpdate(IAnyResource theResource, MdmTransactionContext theMdmTransactionContext) { CandidateList candidateList = myMdmGoldenResourceFindingSvc.findGoldenResourceCandidates(theResource); - theMdmTransactionContext.getMdmLinkChangeEvent().setTargetResourceId(theResource); + theMdmTransactionContext.getMdmLinkEvent().setTargetResourceId(theResource); if (candidateList.isEmpty()) { handleMdmWithNoCandidates(theResource, theMdmTransactionContext); @@ -114,14 +114,14 @@ public class MdmMatchLinkSvc { //Set all GoldenResources as POSSIBLE_DUPLICATE of the last GoldenResource. IAnyResource firstGoldenResource = goldenResources.get(0); - theMdmTransactionContext.getMdmLinkChangeEvent().setGoldenResourceId(firstGoldenResource); + theMdmTransactionContext.getMdmLinkEvent().setGoldenResourceId(firstGoldenResource); goldenResources.subList(1, goldenResources.size()) .forEach(possibleDuplicateGoldenResource -> { MdmMatchOutcome outcome = MdmMatchOutcome.POSSIBLE_DUPLICATE; outcome.setEidMatch(theCandidateList.isEidMatch()); myMdmLinkSvc.updateLink(firstGoldenResource, possibleDuplicateGoldenResource, outcome, MdmLinkSourceEnum.AUTO, theMdmTransactionContext); - theMdmTransactionContext.getMdmLinkChangeEvent().addDuplicateGoldenResourceId(possibleDuplicateGoldenResource); + theMdmTransactionContext.getMdmLinkEvent().addDuplicateGoldenResourceId(possibleDuplicateGoldenResource); }); } } @@ -135,7 +135,7 @@ public class MdmMatchLinkSvc { // 3. UPDATE MDM LINK TABLE myMdmLinkSvc.updateLink(newGoldenResource, theResource, MdmMatchOutcome.NEW_GOLDEN_RESOURCE_MATCH, MdmLinkSourceEnum.AUTO, theMdmTransactionContext); - theMdmTransactionContext.getMdmLinkChangeEvent().setGoldenResourceId(newGoldenResource); + theMdmTransactionContext.getMdmLinkEvent().setGoldenResourceId(newGoldenResource); } private void handleMdmCreate(IAnyResource theTargetResource, MatchedGoldenResourceCandidate theGoldenResourceCandidate, MdmTransactionContext theMdmTransactionContext) { @@ -145,7 +145,7 @@ public class MdmMatchLinkSvc { if (myGoldenResourceHelper.isPotentialDuplicate(goldenResource, theTargetResource)) { log(theMdmTransactionContext, "Duplicate detected based on the fact that both resources have different external EIDs."); IAnyResource newGoldenResource = myGoldenResourceHelper.createGoldenResourceFromMdmSourceResource(theTargetResource, theMdmTransactionContext); - theMdmTransactionContext.getMdmLinkChangeEvent().setGoldenResourceId(newGoldenResource); + theMdmTransactionContext.getMdmLinkEvent().setGoldenResourceId(newGoldenResource); myMdmLinkSvc.updateLink(newGoldenResource, theTargetResource, MdmMatchOutcome.NEW_GOLDEN_RESOURCE_MATCH, MdmLinkSourceEnum.AUTO, theMdmTransactionContext); myMdmLinkSvc.updateLink(newGoldenResource, goldenResource, MdmMatchOutcome.POSSIBLE_DUPLICATE, MdmLinkSourceEnum.AUTO, theMdmTransactionContext); @@ -155,7 +155,7 @@ public class MdmMatchLinkSvc { myEidUpdateService.applySurvivorshipRulesAndSaveGoldenResource(theTargetResource, goldenResource, theMdmTransactionContext); } - theMdmTransactionContext.getMdmLinkChangeEvent().setGoldenResourceId(goldenResource); + theMdmTransactionContext.getMdmLinkEvent().setGoldenResourceId(goldenResource); myMdmLinkSvc.updateLink(goldenResource, theTargetResource, theGoldenResourceCandidate.getMatchResult(), MdmLinkSourceEnum.AUTO, theMdmTransactionContext); } } diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/FindCandidateByExampleSvc.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/FindCandidateByExampleSvc.java index 43606a95818..5533c82667b 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/FindCandidateByExampleSvc.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/candidate/FindCandidateByExampleSvc.java @@ -54,9 +54,9 @@ public class FindCandidateByExampleSvc extends BaseCandidateFinder { private IMdmMatchFinderSvc myMdmMatchFinderSvc; /** - * Attempt to find matching Golden Resources by resolving them from similar Matching target resources, where target resource - * can be either Patient or Practitioner. Runs MDM logic over the existing target resources, then finds their - * entries in the MdmLink table, and returns all the matches found therein. + * Attempt to find matching Golden Resources by resolving them from similar Matching target resources. Runs MDM logic + * over the existing target resources, then finds their entries in the MdmLink table, and returns all the matches + * found therein. * * @param theTarget the {@link IBaseResource} which we want to find candidate Golden Resources for. * @return an Optional list of {@link MatchedGoldenResourceCandidate} indicating matches. @@ -69,8 +69,8 @@ public class FindCandidateByExampleSvc extends BaseCandidateFinder { List matchedCandidates = myMdmMatchFinderSvc.getMatchedTargets(myFhirContext.getResourceType(theTarget), theTarget); - //Convert all possible match targets to their equivalent Golden Resources by looking up in the MdmLink table, - //while ensuring that the matches aren't in our NO_MATCH list. + // Convert all possible match targets to their equivalent Golden Resources by looking up in the MdmLink table, + // while ensuring that the matches aren't in our NO_MATCH list. // The data flow is as follows -> // MatchedTargetCandidate -> Golden Resource -> MdmLink -> MatchedGoldenResourceCandidate matchedCandidates = matchedCandidates.stream().filter(mc -> mc.isMatch() || mc.isPossibleMatch()).collect(Collectors.toList()); diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/BaseMdmR4Test.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/BaseMdmR4Test.java index 96115a95bf2..60936356c54 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/BaseMdmR4Test.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/BaseMdmR4Test.java @@ -326,14 +326,14 @@ abstract public class BaseMdmR4Test extends BaseJpaR4Test { assertEquals(theExpectedCount, myMdmLinkDao.count()); } - protected IAnyResource getGoldenResourceFromTargetResource(IAnyResource theBaseResource) { + protected T getGoldenResourceFromTargetResource(T theBaseResource) { String resourceType = theBaseResource.getIdElement().getResourceType(); IFhirResourceDao relevantDao = myDaoRegistry.getResourceDao(resourceType); Optional matchedLinkForTargetPid = myMdmLinkDaoSvc.getMatchedLinkForSourcePid(myIdHelperService.getPidOrNull(theBaseResource)); if (matchedLinkForTargetPid.isPresent()) { Long goldenResourcePid = matchedLinkForTargetPid.get().getGoldenResourcePid(); - return (IAnyResource) relevantDao.readByPid(new ResourcePersistentId(goldenResourcePid)); + return (T) relevantDao.readByPid(new ResourcePersistentId(goldenResourcePid)); } else { return null; } diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java index cd1518cbc59..330e1380bef 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java @@ -6,8 +6,12 @@ import ca.uhn.fhir.jpa.entity.MdmLink; import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test; import ca.uhn.fhir.jpa.mdm.helper.MdmHelperConfig; import ca.uhn.fhir.jpa.mdm.helper.MdmHelperR4; +import ca.uhn.fhir.jpa.mdm.svc.MdmLinkSvcImpl; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; +import ca.uhn.fhir.mdm.api.IMdmLinkSvc; import ca.uhn.fhir.mdm.api.MdmLinkEvent; +import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; +import ca.uhn.fhir.mdm.api.MdmMatchOutcome; import ca.uhn.fhir.mdm.model.CanonicalEID; import ca.uhn.fhir.mdm.model.MdmTransactionContext; import ca.uhn.fhir.mdm.rules.config.MdmSettings; @@ -68,6 +72,8 @@ public class MdmStorageInterceptorIT extends BaseMdmR4Test { public MdmHelperR4 myMdmHelper; @Autowired private IdHelperService myIdHelperService; + @Autowired + private IMdmLinkSvc myMdmLinkSvc; @Test public void testCreatePractitioner() throws InterruptedException { @@ -105,17 +111,17 @@ public class MdmStorageInterceptorIT extends BaseMdmR4Test { MdmTransactionContext ctx = createContextForCreate("Patient"); myMdmMatchLinkSvc.updateMdmLinksForMdmSource(patient1, ctx); - ourLog.info(ctx.getMdmLinkChangeEvent().toString()); - assertEquals(patient1.getIdElement().getValue(), ctx.getMdmLinkChangeEvent().getTargetResourceId()); - assertEquals(getLinkByTargetId(patient1).getGoldenResourcePid(), new IdDt(ctx.getMdmLinkChangeEvent().getGoldenResourceId()).getIdPartAsLong()); + ourLog.info(ctx.getMdmLinkEvent().toString()); + assertEquals(patient1.getIdElement().getValue(), ctx.getMdmLinkEvent().getTargetResourceId()); + assertEquals(getLinkByTargetId(patient1).getGoldenResourcePid(), new IdDt(ctx.getMdmLinkEvent().getGoldenResourceId()).getIdPartAsLong()); Patient patient2 = addExternalEID(buildJanePatient(), "eid-2"); myMdmHelper.createWithLatch(patient2); ctx = createContextForCreate("Patient"); myMdmMatchLinkSvc.updateMdmLinksForMdmSource(patient2, ctx); - ourLog.info(ctx.getMdmLinkChangeEvent().toString()); - assertEquals(patient2.getIdElement().getValue(), ctx.getMdmLinkChangeEvent().getTargetResourceId()); - assertEquals(getLinkByTargetId(patient2).getGoldenResourcePid(), new IdDt(ctx.getMdmLinkChangeEvent().getGoldenResourceId()).getIdPartAsLong()); + ourLog.info(ctx.getMdmLinkEvent().toString()); + assertEquals(patient2.getIdElement().getValue(), ctx.getMdmLinkEvent().getTargetResourceId()); + assertEquals(getLinkByTargetId(patient2).getGoldenResourcePid(), new IdDt(ctx.getMdmLinkEvent().getGoldenResourceId()).getIdPartAsLong()); } @Test diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmTransactionContext.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmTransactionContext.java index 9853918a9d7..4f13e7ac706 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmTransactionContext.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmTransactionContext.java @@ -96,7 +96,7 @@ public class MdmTransactionContext { this.myResourceType = myResourceType; } - public MdmLinkEvent getMdmLinkChangeEvent() { + public MdmLinkEvent getMdmLinkEvent() { return myMdmLinkEvent; } From 2a60e91713ae20cd983365cdd8bdb51e3f097ecf Mon Sep 17 00:00:00 2001 From: Nick Goupinets Date: Thu, 2 Sep 2021 17:41:57 -0400 Subject: [PATCH 11/13] WIP Dup Test --- .../uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java | 1 + .../fhir/jpa/mdm/interceptor/MdmEventIT.java | 190 ++++++++++++++++++ .../interceptor/MdmStorageInterceptorIT.java | 32 +-- 3 files changed, 193 insertions(+), 30 deletions(-) create mode 100644 hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmEventIT.java diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java index ce285b58bac..2c2eba5342e 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java @@ -101,6 +101,7 @@ public class MdmMatchLinkSvc { handleMdmWithSingleCandidate(theResource, firstMatch, theMdmTransactionContext); } else { log(theMdmTransactionContext, "MDM received multiple match candidates, that were linked to different Golden Resources. Setting POSSIBLE_DUPLICATES and POSSIBLE_MATCHES."); + //Set them all as POSSIBLE_MATCH List goldenResources = new ArrayList<>(); for (MatchedGoldenResourceCandidate matchedGoldenResourceCandidate : theCandidateList.getCandidates()) { diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmEventIT.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmEventIT.java new file mode 100644 index 00000000000..1bfdd0c1cb2 --- /dev/null +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmEventIT.java @@ -0,0 +1,190 @@ +package ca.uhn.fhir.jpa.mdm.interceptor; + +import ca.uhn.fhir.jpa.dao.index.IdHelperService; +import ca.uhn.fhir.jpa.entity.MdmLink; +import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test; +import ca.uhn.fhir.jpa.mdm.helper.MdmHelperConfig; +import ca.uhn.fhir.jpa.mdm.helper.MdmHelperR4; +import ca.uhn.fhir.jpa.search.HapiLuceneAnalysisConfigurer; +import ca.uhn.fhir.mdm.api.MdmLinkEvent; +import ca.uhn.fhir.mdm.model.MdmTransactionContext; +import ca.uhn.fhir.model.primitive.IdDt; +import ca.uhn.fhir.rest.server.messaging.ResourceOperationMessage; +import org.hibernate.dialect.H2Dialect; +import org.hibernate.search.backend.lucene.cfg.LuceneBackendSettings; +import org.hibernate.search.backend.lucene.cfg.LuceneIndexSettings; +import org.hibernate.search.engine.cfg.BackendSettings; +import org.hibernate.search.mapper.orm.cfg.HibernateOrmMapperSettings; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r4.model.Patient; +import org.hl7.fhir.r4.model.Practitioner; +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.context.annotation.Bean; +import org.springframework.context.annotation.Primary; +import org.springframework.data.domain.Example; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.TestPropertySource; + +import java.util.Properties; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.slf4j.LoggerFactory.getLogger; + +@TestPropertySource(properties = { + "mdm.prevent_multiple_eids=false" +}) +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) +@ContextConfiguration(classes = {MdmHelperConfig.class}) +public class MdmEventIT extends BaseMdmR4Test { + + private static final Logger ourLog = getLogger(MdmEventIT.class); + + @RegisterExtension + @Autowired + public MdmHelperR4 myMdmHelper; + @Autowired + private IdHelperService myIdHelperService; + + @Bean + @Primary + public Properties jpaProperties() { + Properties extraProperties = new Properties(); + extraProperties.put("hibernate.format_sql", "true"); + extraProperties.put("hibernate.show_sql", "true"); + extraProperties.put("hibernate.hbm2ddl.auto", "update"); + extraProperties.put("hibernate.dialect", H2Dialect.class.getName()); + + extraProperties.put(BackendSettings.backendKey(BackendSettings.TYPE), "lucene"); + extraProperties.put(BackendSettings.backendKey(LuceneBackendSettings.ANALYSIS_CONFIGURER), HapiLuceneAnalysisConfigurer.class.getName()); + extraProperties.put(BackendSettings.backendKey(LuceneIndexSettings.DIRECTORY_TYPE), "local-heap"); + extraProperties.put(BackendSettings.backendKey(LuceneBackendSettings.LUCENE_VERSION), "LUCENE_CURRENT"); + extraProperties.put(HibernateOrmMapperSettings.ENABLED, "true"); + + return extraProperties; + } + + + @Test + public void testDuplicateLinkChangeEvent() throws InterruptedException { + Patient patient1 = buildJanePatient(); + addExternalEID(patient1, "eid-1"); + addExternalEID(patient1, "eid-11"); + patient1 = createPatientAndUpdateLinks(patient1); + + Patient patient2 = buildPaulPatient(); + addExternalEID(patient2, "eid-2"); + addExternalEID(patient2, "eid-22"); + patient2 = createPatientAndUpdateLinks(patient2); + + Patient patient3 = buildPaulPatient(); + addExternalEID(patient3, "eid-22"); + patient3 = createPatientAndUpdateLinks(patient3); + + patient2.getIdentifier().clear(); + addExternalEID(patient2, "eid-11"); + addExternalEID(patient2, "eid-22"); + + patient2 = (Patient) myPatientDao.update(patient2).getResource(); + MdmTransactionContext ctx = myMdmMatchLinkSvc.updateMdmLinksForMdmSource(patient2, createContextForUpdate(patient2.getIdElement().getResourceType())); + + MdmLinkEvent mdmLinkEvent = ctx.getMdmLinkEvent(); + assertFalse(mdmLinkEvent.getDuplicateGoldenResourceIds().isEmpty()); + } + + // @Test + public void testCreateLinkChangeEvent() throws InterruptedException { + Practitioner pr = buildPractitionerWithNameAndId("Young", "AC-DC"); + myMdmHelper.createWithLatch(pr); + + ResourceOperationMessage resourceOperationMessage = myMdmHelper.getAfterMdmLatch().getLatchInvocationParameterOfType(ResourceOperationMessage.class); + assertNotNull(resourceOperationMessage); + assertEquals(pr.getId(), resourceOperationMessage.getId()); + + MdmLink link = getLinkByTargetId(pr); + + MdmLinkEvent linkChangeEvent = myMdmHelper.getAfterMdmLatch().getLatchInvocationParameterOfType(MdmLinkEvent.class); + assertNotNull(linkChangeEvent); + assertEquals(link.getGoldenResourcePid(), new IdDt(linkChangeEvent.getGoldenResourceId()).getIdPartAsLong()); + assertEquals(link.getSourcePid(), new IdDt(linkChangeEvent.getTargetResourceId()).getIdPartAsLong()); + } + + /* + @Test + public void testDuplicateLinkChangeEvent() throws InterruptedException { + logAllLinks(); + for (IBaseResource r : myPatientDao.search(new SearchParameterMap()).getAllResources()) { + ourLog.info("Found {}", r); + } + + Patient myPatient = createPatientAndUpdateLinks(buildPaulPatient()); + StringType myPatientId = new StringType(myPatient.getIdElement().getValue()); + + Patient mySourcePatient = getGoldenResourceFromTargetResource(myPatient); + StringType mySourcePatientId = new StringType(mySourcePatient.getIdElement().getValue()); + StringType myVersionlessGodlenResourceId = new StringType(mySourcePatient.getIdElement().toVersionless().getValue()); + + MdmLink myLink = myMdmLinkDaoSvc.findMdmLinkBySource(myPatient).get(); + // Tests require our initial link to be a POSSIBLE_MATCH + myLink.setMatchResult(MdmMatchResultEnum.POSSIBLE_MATCH); + saveLink(myLink); + assertEquals(MdmLinkSourceEnum.AUTO, myLink.getLinkSource()); +// myDaoConfig.setExpungeEnabled(true); + + // Add a second patient + createPatientAndUpdateLinks(buildJanePatient()); + + // Add a possible duplicate + StringType myLinkSource = new StringType(MdmLinkSourceEnum.AUTO.name()); + Patient sourcePatient1 = createGoldenPatient(); + StringType myGoldenResource1Id = new StringType(sourcePatient1.getIdElement().toVersionless().getValue()); + Long sourcePatient1Pid = myIdHelperService.getPidOrNull(sourcePatient1); + Patient sourcePatient2 = createGoldenPatient(); + StringType myGoldenResource2Id = new StringType(sourcePatient2.getIdElement().toVersionless().getValue()); + Long sourcePatient2Pid = myIdHelperService.getPidOrNull(sourcePatient2); + + + MdmLink possibleDuplicateMdmLink = myMdmLinkDaoSvc.newMdmLink().setGoldenResourcePid(sourcePatient1Pid).setSourcePid(sourcePatient2Pid).setMatchResult(MdmMatchResultEnum.POSSIBLE_DUPLICATE).setLinkSource(MdmLinkSourceEnum.AUTO); + saveLink(possibleDuplicateMdmLink); + + logAllLinks(); + + ResourceOperationMessage resourceOperationMessage = myMdmHelper.getAfterMdmLatch().getLatchInvocationParameterOfType(ResourceOperationMessage.class); + assertNotNull(resourceOperationMessage); + assertEquals(sourcePatient2.getId(), resourceOperationMessage.getId()); + } + */ + + private MdmLink getLinkByTargetId(IBaseResource theResource) { + MdmLink example = new MdmLink(); + example.setSourcePid(theResource.getIdElement().getIdPartAsLong()); + return myMdmLinkDao.findAll(Example.of(example)).get(0); + } + + @Test + public void testUpdateLinkChangeEvent() throws InterruptedException { + Patient patient1 = addExternalEID(buildJanePatient(), "eid-1"); + myMdmHelper.createWithLatch(patient1); + + MdmTransactionContext ctx = createContextForCreate("Patient"); + myMdmMatchLinkSvc.updateMdmLinksForMdmSource(patient1, ctx); + ourLog.info(ctx.getMdmLinkEvent().toString()); + assertEquals(patient1.getIdElement().getValue(), ctx.getMdmLinkEvent().getTargetResourceId()); + assertEquals(getLinkByTargetId(patient1).getGoldenResourcePid(), new IdDt(ctx.getMdmLinkEvent().getGoldenResourceId()).getIdPartAsLong()); + + Patient patient2 = addExternalEID(buildJanePatient(), "eid-2"); + myMdmHelper.createWithLatch(patient2); + ctx = createContextForCreate("Patient"); + myMdmMatchLinkSvc.updateMdmLinksForMdmSource(patient2, ctx); + ourLog.info(ctx.getMdmLinkEvent().toString()); + assertEquals(patient2.getIdElement().getValue(), ctx.getMdmLinkEvent().getTargetResourceId()); + assertEquals(getLinkByTargetId(patient2).getGoldenResourcePid(), new IdDt(ctx.getMdmLinkEvent().getGoldenResourceId()).getIdPartAsLong()); + } + + +} diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java index 330e1380bef..30281f7fd90 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java @@ -6,12 +6,7 @@ import ca.uhn.fhir.jpa.entity.MdmLink; import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test; import ca.uhn.fhir.jpa.mdm.helper.MdmHelperConfig; import ca.uhn.fhir.jpa.mdm.helper.MdmHelperR4; -import ca.uhn.fhir.jpa.mdm.svc.MdmLinkSvcImpl; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; -import ca.uhn.fhir.mdm.api.IMdmLinkSvc; -import ca.uhn.fhir.mdm.api.MdmLinkEvent; -import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; -import ca.uhn.fhir.mdm.api.MdmMatchOutcome; import ca.uhn.fhir.mdm.model.CanonicalEID; import ca.uhn.fhir.mdm.model.MdmTransactionContext; import ca.uhn.fhir.mdm.rules.config.MdmSettings; @@ -21,7 +16,6 @@ import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId; import ca.uhn.fhir.rest.param.ReferenceParam; import ca.uhn.fhir.rest.server.TransactionLogMessages; import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException; -import ca.uhn.fhir.rest.server.messaging.ResourceOperationMessage; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; @@ -29,7 +23,6 @@ import org.hl7.fhir.r4.model.Enumerations; import org.hl7.fhir.r4.model.Medication; import org.hl7.fhir.r4.model.Organization; import org.hl7.fhir.r4.model.Patient; -import org.hl7.fhir.r4.model.Practitioner; import org.hl7.fhir.r4.model.SearchParameter; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -55,9 +48,7 @@ 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.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.slf4j.LoggerFactory.getLogger; @@ -72,8 +63,6 @@ public class MdmStorageInterceptorIT extends BaseMdmR4Test { public MdmHelperR4 myMdmHelper; @Autowired private IdHelperService myIdHelperService; - @Autowired - private IMdmLinkSvc myMdmLinkSvc; @Test public void testCreatePractitioner() throws InterruptedException { @@ -81,23 +70,6 @@ public class MdmStorageInterceptorIT extends BaseMdmR4Test { assertLinkCount(1); } - @Test - public void testCreateLinkChangeEvent() throws InterruptedException { - Practitioner pr = buildPractitionerWithNameAndId("Young", "AC-DC"); - myMdmHelper.createWithLatch(pr); - - ResourceOperationMessage resourceOperationMessage = myMdmHelper.getAfterMdmLatch().getLatchInvocationParameterOfType(ResourceOperationMessage.class); - assertNotNull(resourceOperationMessage); - assertEquals(pr.getId(), resourceOperationMessage.getId()); - - MdmLink link = getLinkByTargetId(pr); - - MdmLinkEvent linkChangeEvent = myMdmHelper.getAfterMdmLatch().getLatchInvocationParameterOfType(MdmLinkEvent.class); - assertNotNull(linkChangeEvent); - assertEquals(link.getGoldenResourcePid(), new IdDt(linkChangeEvent.getGoldenResourceId()).getIdPartAsLong()); - assertEquals(link.getSourcePid(), new IdDt(linkChangeEvent.getTargetResourceId()).getIdPartAsLong()); - } - private MdmLink getLinkByTargetId(IBaseResource theResource) { MdmLink example = new MdmLink(); example.setSourcePid(theResource.getIdElement().getIdPartAsLong()); @@ -113,7 +85,7 @@ public class MdmStorageInterceptorIT extends BaseMdmR4Test { myMdmMatchLinkSvc.updateMdmLinksForMdmSource(patient1, ctx); ourLog.info(ctx.getMdmLinkEvent().toString()); assertEquals(patient1.getIdElement().getValue(), ctx.getMdmLinkEvent().getTargetResourceId()); - assertEquals(getLinkByTargetId(patient1).getGoldenResourcePid(), new IdDt(ctx.getMdmLinkEvent().getGoldenResourceId()).getIdPartAsLong()); + assertEquals(getLinkByTargetId(patient1).getGoldenResourcePid(), new IdDt(ctx.getMdmLinkEvent().getGoldenResourceId()).getIdPartAsLong()); Patient patient2 = addExternalEID(buildJanePatient(), "eid-2"); myMdmHelper.createWithLatch(patient2); @@ -121,7 +93,7 @@ public class MdmStorageInterceptorIT extends BaseMdmR4Test { myMdmMatchLinkSvc.updateMdmLinksForMdmSource(patient2, ctx); ourLog.info(ctx.getMdmLinkEvent().toString()); assertEquals(patient2.getIdElement().getValue(), ctx.getMdmLinkEvent().getTargetResourceId()); - assertEquals(getLinkByTargetId(patient2).getGoldenResourcePid(), new IdDt(ctx.getMdmLinkEvent().getGoldenResourceId()).getIdPartAsLong()); + assertEquals(getLinkByTargetId(patient2).getGoldenResourcePid(), new IdDt(ctx.getMdmLinkEvent().getGoldenResourceId()).getIdPartAsLong()); } @Test From c63a5038419e7ce6e549c28711b0f1e4b36ebcbd Mon Sep 17 00:00:00 2001 From: Nick Goupinets Date: Fri, 3 Sep 2021 16:29:26 -0400 Subject: [PATCH 12/13] Updated link processing for MDM events --- .../java/ca/uhn/fhir/jpa/entity/MdmLink.java | 3 +- .../jpa/mdm/broker/MdmMessageHandler.java | 14 +- .../fhir/jpa/mdm/svc/MdmEidUpdateService.java | 2 - .../uhn/fhir/jpa/mdm/svc/MdmLinkSvcImpl.java | 10 +- .../uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java | 8 - .../jpa/mdm/svc/MdmModelConverterSvcImpl.java | 28 ++-- .../fhir/jpa/mdm/interceptor/MdmEventIT.java | 115 ++++---------- .../interceptor/MdmStorageInterceptorIT.java | 20 --- .../java/ca/uhn/fhir/mdm/api/IMdmLink.java | 24 +++ .../ca/uhn/fhir/mdm/api/MdmLinkEvent.java | 148 ++---------------- .../fhir/mdm/model/MdmTransactionContext.java | 18 ++- 11 files changed, 106 insertions(+), 284 deletions(-) create mode 100644 hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmLink.java diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/MdmLink.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/MdmLink.java index cd8cbff422d..4892ed8c1f9 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/MdmLink.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/MdmLink.java @@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.entity; * #L% */ +import ca.uhn.fhir.mdm.api.IMdmLink; import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; import ca.uhn.fhir.jpa.model.entity.ResourceTable; @@ -47,7 +48,7 @@ import java.util.Date; @Table(name = "MPI_LINK", uniqueConstraints = { @UniqueConstraint(name = "IDX_EMPI_PERSON_TGT", columnNames = {"PERSON_PID", "TARGET_PID"}), }) -public class MdmLink { +public class MdmLink implements IMdmLink { public static final int VERSION_LENGTH = 16; private static final int MATCH_RESULT_LENGTH = 16; private static final int LINK_SOURCE_LENGTH = 16; diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java index 6589d6b41e9..7695be8f448 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/broker/MdmMessageHandler.java @@ -119,17 +119,17 @@ public class MdmMessageHandler implements MessageHandler { ResourceOperationMessage outgoingMsg = new ResourceOperationMessage(myFhirContext, targetResource, theMsg.getOperationType()); outgoingMsg.setTransactionId(theMsg.getTransactionId()); - MdmLinkEvent linkChangeEvent = mdmContext.getMdmLinkEvent(); - Optional mdmLinkBySource = myMdmLinkDaoSvc.findMdmLinkBySource(targetResource); - if (!mdmLinkBySource.isPresent()) { - ourLog.warn("Unable to find link by source for {}", targetResource.getIdElement()); - } + MdmLinkEvent linkChangeEvent = new MdmLinkEvent(); + mdmContext.getMdmLinks() + .stream() + .forEach(l -> { + linkChangeEvent.addMdmLink(myModelConverter.toJson((MdmLink) l)); + }); - mdmLinkBySource.ifPresent(link -> linkChangeEvent.setFromLink(myModelConverter.toJson(link))); HookParams params = new HookParams() .add(ResourceOperationMessage.class, outgoingMsg) .add(TransactionLogMessages.class, mdmContext.getTransactionLogMessages()) - .add(MdmLinkEvent.class, mdmContext.getMdmLinkEvent()); + .add(MdmLinkEvent.class, linkChangeEvent); myInterceptorBroadcaster.callHooks(Pointcut.MDM_AFTER_PERSISTED_RESOURCE_CHECKED, params); } diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmEidUpdateService.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmEidUpdateService.java index 2e035615200..94db6849073 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmEidUpdateService.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmEidUpdateService.java @@ -85,8 +85,6 @@ public class MdmEidUpdateService { myMdmSurvivorshipService.applySurvivorshipRulesToGoldenResource(theTargetResource, updateContext.getMatchedGoldenResource(), theMdmTransactionContext); myMdmResourceDaoSvc.upsertGoldenResource(updateContext.getMatchedGoldenResource(), theMdmTransactionContext.getResourceType()); } - - theMdmTransactionContext.getMdmLinkEvent().setGoldenResourceId(updateContext.getExistingGoldenResource()); } private void handleNoEidsInCommon(IAnyResource theResource, MatchedGoldenResourceCandidate theMatchedGoldenResourceCandidate, MdmTransactionContext theMdmTransactionContext, MdmUpdateContext theUpdateContext) { diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkSvcImpl.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkSvcImpl.java index 3d744f43d11..7e4b95dad0c 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkSvcImpl.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkSvcImpl.java @@ -53,6 +53,8 @@ public class MdmLinkSvcImpl implements IMdmLinkSvc { private MdmLinkDaoSvc myMdmLinkDaoSvc; @Autowired private IdHelperService myIdHelperService; + @Autowired + private IMdmModelConverterSvc myMdmModelConverterSvc; @Override @Transactional @@ -69,7 +71,8 @@ public class MdmLinkSvcImpl implements IMdmLinkSvc { validateRequestIsLegal(theGoldenResource, theSourceResource, matchResultEnum, theLinkSource); myMdmResourceDaoSvc.upsertGoldenResource(theGoldenResource, theMdmTransactionContext.getResourceType()); - createOrUpdateLinkEntity(theGoldenResource, theSourceResource, theMatchOutcome, theLinkSource, theMdmTransactionContext); + MdmLink link = createOrUpdateLinkEntity(theGoldenResource, theSourceResource, theMatchOutcome, theLinkSource, theMdmTransactionContext); + theMdmTransactionContext.addMdmLink(link); } private boolean goldenResourceLinkedAsNoMatch(IAnyResource theGoldenResource, IAnyResource theSourceResource) { @@ -87,6 +90,7 @@ public class MdmLinkSvcImpl implements IMdmLinkSvc { MdmLink mdmLink = optionalMdmLink.get(); log(theMdmTransactionContext, "Deleting MdmLink [" + theGoldenResource.getIdElement().toVersionless() + " -> " + theSourceResource.getIdElement().toVersionless() + "] with result: " + mdmLink.getMatchResult()); myMdmLinkDaoSvc.deleteLink(mdmLink); + theMdmTransactionContext.addMdmLink(mdmLink); } } @@ -129,8 +133,8 @@ public class MdmLinkSvcImpl implements IMdmLinkSvc { } } - private void createOrUpdateLinkEntity(IBaseResource theGoldenResource, IBaseResource theSourceResource, MdmMatchOutcome theMatchOutcome, MdmLinkSourceEnum theLinkSource, MdmTransactionContext theMdmTransactionContext) { - myMdmLinkDaoSvc.createOrUpdateLinkEntity(theGoldenResource, theSourceResource, theMatchOutcome, theLinkSource, theMdmTransactionContext); + private MdmLink createOrUpdateLinkEntity(IBaseResource theGoldenResource, IBaseResource theSourceResource, MdmMatchOutcome theMatchOutcome, MdmLinkSourceEnum theLinkSource, MdmTransactionContext theMdmTransactionContext) { + return myMdmLinkDaoSvc.createOrUpdateLinkEntity(theGoldenResource, theSourceResource, theMatchOutcome, theLinkSource, theMdmTransactionContext); } private void log(MdmTransactionContext theMdmTransactionContext, String theMessage) { diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java index 2c2eba5342e..8eda5742dd2 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmMatchLinkSvc.java @@ -78,8 +78,6 @@ public class MdmMatchLinkSvc { private MdmTransactionContext doMdmUpdate(IAnyResource theResource, MdmTransactionContext theMdmTransactionContext) { CandidateList candidateList = myMdmGoldenResourceFindingSvc.findGoldenResourceCandidates(theResource); - theMdmTransactionContext.getMdmLinkEvent().setTargetResourceId(theResource); - if (candidateList.isEmpty()) { handleMdmWithNoCandidates(theResource, theMdmTransactionContext); } else if (candidateList.exactlyOneMatch()) { @@ -115,14 +113,12 @@ public class MdmMatchLinkSvc { //Set all GoldenResources as POSSIBLE_DUPLICATE of the last GoldenResource. IAnyResource firstGoldenResource = goldenResources.get(0); - theMdmTransactionContext.getMdmLinkEvent().setGoldenResourceId(firstGoldenResource); goldenResources.subList(1, goldenResources.size()) .forEach(possibleDuplicateGoldenResource -> { MdmMatchOutcome outcome = MdmMatchOutcome.POSSIBLE_DUPLICATE; outcome.setEidMatch(theCandidateList.isEidMatch()); myMdmLinkSvc.updateLink(firstGoldenResource, possibleDuplicateGoldenResource, outcome, MdmLinkSourceEnum.AUTO, theMdmTransactionContext); - theMdmTransactionContext.getMdmLinkEvent().addDuplicateGoldenResourceId(possibleDuplicateGoldenResource); }); } } @@ -135,8 +131,6 @@ public class MdmMatchLinkSvc { // 2. Create source resource for the MDM source // 3. UPDATE MDM LINK TABLE myMdmLinkSvc.updateLink(newGoldenResource, theResource, MdmMatchOutcome.NEW_GOLDEN_RESOURCE_MATCH, MdmLinkSourceEnum.AUTO, theMdmTransactionContext); - - theMdmTransactionContext.getMdmLinkEvent().setGoldenResourceId(newGoldenResource); } private void handleMdmCreate(IAnyResource theTargetResource, MatchedGoldenResourceCandidate theGoldenResourceCandidate, MdmTransactionContext theMdmTransactionContext) { @@ -146,7 +140,6 @@ public class MdmMatchLinkSvc { if (myGoldenResourceHelper.isPotentialDuplicate(goldenResource, theTargetResource)) { log(theMdmTransactionContext, "Duplicate detected based on the fact that both resources have different external EIDs."); IAnyResource newGoldenResource = myGoldenResourceHelper.createGoldenResourceFromMdmSourceResource(theTargetResource, theMdmTransactionContext); - theMdmTransactionContext.getMdmLinkEvent().setGoldenResourceId(newGoldenResource); myMdmLinkSvc.updateLink(newGoldenResource, theTargetResource, MdmMatchOutcome.NEW_GOLDEN_RESOURCE_MATCH, MdmLinkSourceEnum.AUTO, theMdmTransactionContext); myMdmLinkSvc.updateLink(newGoldenResource, goldenResource, MdmMatchOutcome.POSSIBLE_DUPLICATE, MdmLinkSourceEnum.AUTO, theMdmTransactionContext); @@ -156,7 +149,6 @@ public class MdmMatchLinkSvc { myEidUpdateService.applySurvivorshipRulesAndSaveGoldenResource(theTargetResource, goldenResource, theMdmTransactionContext); } - theMdmTransactionContext.getMdmLinkEvent().setGoldenResourceId(goldenResource); myMdmLinkSvc.updateLink(goldenResource, theTargetResource, theGoldenResourceCandidate.getMatchResult(), MdmLinkSourceEnum.AUTO, theMdmTransactionContext); } } diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmModelConverterSvcImpl.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmModelConverterSvcImpl.java index 195ac388e68..89a8ae5c34e 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmModelConverterSvcImpl.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmModelConverterSvcImpl.java @@ -31,22 +31,22 @@ public class MdmModelConverterSvcImpl implements IMdmModelConverterSvc { IdHelperService myIdHelperService; public MdmLinkJson toJson(MdmLink theLink) { - MdmLinkJson retval = new MdmLinkJson(); + MdmLinkJson retVal = new MdmLinkJson(); String sourceId = myIdHelperService.resourceIdFromPidOrThrowException(theLink.getSourcePid()).toVersionless().getValue(); - retval.setSourceId(sourceId); + retVal.setSourceId(sourceId); String goldenResourceId = myIdHelperService.resourceIdFromPidOrThrowException(theLink.getGoldenResourcePid()).toVersionless().getValue(); - retval.setGoldenResourceId(goldenResourceId); - retval.setCreated(theLink.getCreated()); - retval.setEidMatch(theLink.getEidMatch()); - retval.setLinkSource(theLink.getLinkSource()); - retval.setMatchResult(theLink.getMatchResult()); - retval.setLinkCreatedNewResource(theLink.getHadToCreateNewGoldenResource()); - retval.setScore(theLink.getScore()); - retval.setUpdated(theLink.getUpdated()); - retval.setVector(theLink.getVector()); - retval.setVersion(theLink.getVersion()); - retval.setRuleCount(theLink.getRuleCount()); - return retval; + retVal.setGoldenResourceId(goldenResourceId); + retVal.setCreated(theLink.getCreated()); + retVal.setEidMatch(theLink.getEidMatch()); + retVal.setLinkSource(theLink.getLinkSource()); + retVal.setMatchResult(theLink.getMatchResult()); + retVal.setLinkCreatedNewResource(theLink.getHadToCreateNewGoldenResource()); + retVal.setScore(theLink.getScore()); + retVal.setUpdated(theLink.getUpdated()); + retVal.setVector(theLink.getVector()); + retVal.setVersion(theLink.getVersion()); + retVal.setRuleCount(theLink.getRuleCount()); + return retVal; } } diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmEventIT.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmEventIT.java index 1bfdd0c1cb2..39ec89b4c7d 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmEventIT.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmEventIT.java @@ -6,7 +6,10 @@ import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test; import ca.uhn.fhir.jpa.mdm.helper.MdmHelperConfig; import ca.uhn.fhir.jpa.mdm.helper.MdmHelperR4; import ca.uhn.fhir.jpa.search.HapiLuceneAnalysisConfigurer; +import ca.uhn.fhir.mdm.api.IMdmLink; import ca.uhn.fhir.mdm.api.MdmLinkEvent; +import ca.uhn.fhir.mdm.api.MdmLinkJson; +import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; import ca.uhn.fhir.mdm.model.MdmTransactionContext; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.server.messaging.ResourceOperationMessage; @@ -29,6 +32,7 @@ import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.TestPropertySource; +import java.util.List; import java.util.Properties; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -51,53 +55,38 @@ public class MdmEventIT extends BaseMdmR4Test { @Autowired private IdHelperService myIdHelperService; - @Bean - @Primary - public Properties jpaProperties() { - Properties extraProperties = new Properties(); - extraProperties.put("hibernate.format_sql", "true"); - extraProperties.put("hibernate.show_sql", "true"); - extraProperties.put("hibernate.hbm2ddl.auto", "update"); - extraProperties.put("hibernate.dialect", H2Dialect.class.getName()); - - extraProperties.put(BackendSettings.backendKey(BackendSettings.TYPE), "lucene"); - extraProperties.put(BackendSettings.backendKey(LuceneBackendSettings.ANALYSIS_CONFIGURER), HapiLuceneAnalysisConfigurer.class.getName()); - extraProperties.put(BackendSettings.backendKey(LuceneIndexSettings.DIRECTORY_TYPE), "local-heap"); - extraProperties.put(BackendSettings.backendKey(LuceneBackendSettings.LUCENE_VERSION), "LUCENE_CURRENT"); - extraProperties.put(HibernateOrmMapperSettings.ENABLED, "true"); - - return extraProperties; - } - - @Test public void testDuplicateLinkChangeEvent() throws InterruptedException { Patient patient1 = buildJanePatient(); addExternalEID(patient1, "eid-1"); addExternalEID(patient1, "eid-11"); - patient1 = createPatientAndUpdateLinks(patient1); + myMdmHelper.createWithLatch(patient1); Patient patient2 = buildPaulPatient(); addExternalEID(patient2, "eid-2"); addExternalEID(patient2, "eid-22"); - patient2 = createPatientAndUpdateLinks(patient2); + myMdmHelper.createWithLatch(patient2); Patient patient3 = buildPaulPatient(); addExternalEID(patient3, "eid-22"); - patient3 = createPatientAndUpdateLinks(patient3); + myMdmHelper.createWithLatch(patient3); patient2.getIdentifier().clear(); addExternalEID(patient2, "eid-11"); addExternalEID(patient2, "eid-22"); - patient2 = (Patient) myPatientDao.update(patient2).getResource(); - MdmTransactionContext ctx = myMdmMatchLinkSvc.updateMdmLinksForMdmSource(patient2, createContextForUpdate(patient2.getIdElement().getResourceType())); + myMdmHelper.updateWithLatch(patient2); - MdmLinkEvent mdmLinkEvent = ctx.getMdmLinkEvent(); - assertFalse(mdmLinkEvent.getDuplicateGoldenResourceIds().isEmpty()); + MdmLinkEvent linkChangeEvent = myMdmHelper.getAfterMdmLatch().getLatchInvocationParameterOfType(MdmLinkEvent.class); + assertNotNull(linkChangeEvent); + + // MdmTransactionContext ctx = myMdmMatchLinkSvc.updateMdmLinksForMdmSource(patient2, createContextForUpdate(patient2.getIdElement().getResourceType())); + + List mdmLinkEvent = linkChangeEvent.getMdmLinks(); + assertEquals(3, mdmLinkEvent.size()); } - // @Test + @Test public void testCreateLinkChangeEvent() throws InterruptedException { Practitioner pr = buildPractitionerWithNameAndId("Young", "AC-DC"); myMdmHelper.createWithLatch(pr); @@ -110,56 +99,13 @@ public class MdmEventIT extends BaseMdmR4Test { MdmLinkEvent linkChangeEvent = myMdmHelper.getAfterMdmLatch().getLatchInvocationParameterOfType(MdmLinkEvent.class); assertNotNull(linkChangeEvent); - assertEquals(link.getGoldenResourcePid(), new IdDt(linkChangeEvent.getGoldenResourceId()).getIdPartAsLong()); - assertEquals(link.getSourcePid(), new IdDt(linkChangeEvent.getTargetResourceId()).getIdPartAsLong()); + + assertEquals(1, linkChangeEvent.getMdmLinks().size()); + MdmLinkJson l = linkChangeEvent.getMdmLinks().get(0); + assertEquals(link.getGoldenResourcePid(), new IdDt(l.getGoldenResourceId()).getIdPartAsLong()); + assertEquals(link.getSourcePid(), new IdDt(l.getSourceId()).getIdPartAsLong()); } - /* - @Test - public void testDuplicateLinkChangeEvent() throws InterruptedException { - logAllLinks(); - for (IBaseResource r : myPatientDao.search(new SearchParameterMap()).getAllResources()) { - ourLog.info("Found {}", r); - } - - Patient myPatient = createPatientAndUpdateLinks(buildPaulPatient()); - StringType myPatientId = new StringType(myPatient.getIdElement().getValue()); - - Patient mySourcePatient = getGoldenResourceFromTargetResource(myPatient); - StringType mySourcePatientId = new StringType(mySourcePatient.getIdElement().getValue()); - StringType myVersionlessGodlenResourceId = new StringType(mySourcePatient.getIdElement().toVersionless().getValue()); - - MdmLink myLink = myMdmLinkDaoSvc.findMdmLinkBySource(myPatient).get(); - // Tests require our initial link to be a POSSIBLE_MATCH - myLink.setMatchResult(MdmMatchResultEnum.POSSIBLE_MATCH); - saveLink(myLink); - assertEquals(MdmLinkSourceEnum.AUTO, myLink.getLinkSource()); -// myDaoConfig.setExpungeEnabled(true); - - // Add a second patient - createPatientAndUpdateLinks(buildJanePatient()); - - // Add a possible duplicate - StringType myLinkSource = new StringType(MdmLinkSourceEnum.AUTO.name()); - Patient sourcePatient1 = createGoldenPatient(); - StringType myGoldenResource1Id = new StringType(sourcePatient1.getIdElement().toVersionless().getValue()); - Long sourcePatient1Pid = myIdHelperService.getPidOrNull(sourcePatient1); - Patient sourcePatient2 = createGoldenPatient(); - StringType myGoldenResource2Id = new StringType(sourcePatient2.getIdElement().toVersionless().getValue()); - Long sourcePatient2Pid = myIdHelperService.getPidOrNull(sourcePatient2); - - - MdmLink possibleDuplicateMdmLink = myMdmLinkDaoSvc.newMdmLink().setGoldenResourcePid(sourcePatient1Pid).setSourcePid(sourcePatient2Pid).setMatchResult(MdmMatchResultEnum.POSSIBLE_DUPLICATE).setLinkSource(MdmLinkSourceEnum.AUTO); - saveLink(possibleDuplicateMdmLink); - - logAllLinks(); - - ResourceOperationMessage resourceOperationMessage = myMdmHelper.getAfterMdmLatch().getLatchInvocationParameterOfType(ResourceOperationMessage.class); - assertNotNull(resourceOperationMessage); - assertEquals(sourcePatient2.getId(), resourceOperationMessage.getId()); - } - */ - private MdmLink getLinkByTargetId(IBaseResource theResource) { MdmLink example = new MdmLink(); example.setSourcePid(theResource.getIdElement().getIdPartAsLong()); @@ -171,19 +117,14 @@ public class MdmEventIT extends BaseMdmR4Test { Patient patient1 = addExternalEID(buildJanePatient(), "eid-1"); myMdmHelper.createWithLatch(patient1); - MdmTransactionContext ctx = createContextForCreate("Patient"); - myMdmMatchLinkSvc.updateMdmLinksForMdmSource(patient1, ctx); - ourLog.info(ctx.getMdmLinkEvent().toString()); - assertEquals(patient1.getIdElement().getValue(), ctx.getMdmLinkEvent().getTargetResourceId()); - assertEquals(getLinkByTargetId(patient1).getGoldenResourcePid(), new IdDt(ctx.getMdmLinkEvent().getGoldenResourceId()).getIdPartAsLong()); + MdmLinkEvent linkChangeEvent = myMdmHelper.getAfterMdmLatch().getLatchInvocationParameterOfType(MdmLinkEvent.class); + assertNotNull(linkChangeEvent); + assertEquals(1, linkChangeEvent.getMdmLinks().size()); - Patient patient2 = addExternalEID(buildJanePatient(), "eid-2"); - myMdmHelper.createWithLatch(patient2); - ctx = createContextForCreate("Patient"); - myMdmMatchLinkSvc.updateMdmLinksForMdmSource(patient2, ctx); - ourLog.info(ctx.getMdmLinkEvent().toString()); - assertEquals(patient2.getIdElement().getValue(), ctx.getMdmLinkEvent().getTargetResourceId()); - assertEquals(getLinkByTargetId(patient2).getGoldenResourcePid(), new IdDt(ctx.getMdmLinkEvent().getGoldenResourceId()).getIdPartAsLong()); + MdmLinkJson link = linkChangeEvent.getMdmLinks().get(0); + assertEquals(patient1.getResourceType() + "/" + patient1.getIdElement().getIdPart(), link.getSourceId()); + assertEquals(getLinkByTargetId(patient1).getGoldenResourcePid(), new IdDt(link.getGoldenResourceId()).getIdPartAsLong()); + assertEquals(MdmMatchResultEnum.MATCH, link.getMatchResult()); } diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java index 30281f7fd90..4509b9052da 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmStorageInterceptorIT.java @@ -76,26 +76,6 @@ public class MdmStorageInterceptorIT extends BaseMdmR4Test { return myMdmLinkDao.findAll(Example.of(example)).get(0); } - @Test - public void testUpdateLinkChangeEvent() throws InterruptedException { - Patient patient1 = addExternalEID(buildJanePatient(), "eid-1"); - myMdmHelper.createWithLatch(patient1); - - MdmTransactionContext ctx = createContextForCreate("Patient"); - myMdmMatchLinkSvc.updateMdmLinksForMdmSource(patient1, ctx); - ourLog.info(ctx.getMdmLinkEvent().toString()); - assertEquals(patient1.getIdElement().getValue(), ctx.getMdmLinkEvent().getTargetResourceId()); - assertEquals(getLinkByTargetId(patient1).getGoldenResourcePid(), new IdDt(ctx.getMdmLinkEvent().getGoldenResourceId()).getIdPartAsLong()); - - Patient patient2 = addExternalEID(buildJanePatient(), "eid-2"); - myMdmHelper.createWithLatch(patient2); - ctx = createContextForCreate("Patient"); - myMdmMatchLinkSvc.updateMdmLinksForMdmSource(patient2, ctx); - ourLog.info(ctx.getMdmLinkEvent().toString()); - assertEquals(patient2.getIdElement().getValue(), ctx.getMdmLinkEvent().getTargetResourceId()); - assertEquals(getLinkByTargetId(patient2).getGoldenResourcePid(), new IdDt(ctx.getMdmLinkEvent().getGoldenResourceId()).getIdPartAsLong()); - } - @Test public void testSearchExpandingInterceptorWorks() { SearchParameterMap subject = new SearchParameterMap("subject", new ReferenceParam("Patient/123").setMdmExpand(true)).setLoadSynchronous(true); diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmLink.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmLink.java new file mode 100644 index 00000000000..bf87929d9bd --- /dev/null +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmLink.java @@ -0,0 +1,24 @@ +package ca.uhn.fhir.mdm.api; + +/*- + * #%L + * HAPI FHIR - Master Data Management + * %% + * Copyright (C) 2014 - 2021 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +public interface IMdmLink { +} diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkEvent.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkEvent.java index 7eda6f328d8..c65ee1c9e2d 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkEvent.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkEvent.java @@ -25,158 +25,32 @@ import com.fasterxml.jackson.annotation.JsonProperty; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; +import java.util.ArrayList; import java.util.HashSet; +import java.util.List; import java.util.Set; public class MdmLinkEvent implements IModelJson { - @JsonProperty(value = "matchResult") - private MdmMatchResultEnum myMdmMatchResult; - @JsonProperty(value = "linkSource") - private MdmLinkSourceEnum myMdmLinkSource; - @JsonProperty(value = "eidMatch") - private Boolean myEidMatch; - @JsonProperty(value = "newGoldenResource") - private Boolean myNewGoldenResource; - @JsonProperty(value = "score") - private Double myScore; - @JsonProperty(value = "ruleCount") - private Long myRuleCount; - @JsonProperty(value = "targetResourceId", required = true) - private String myTargetResourceId; - @JsonProperty(value = "goldenResourceId", required = true) - private String myGoldenResourceId; - @JsonProperty(value = "duplicateResourceIds") - private Set myDuplicateGoldenResourceIds = new HashSet<>(); + private List myMdmLinks = new ArrayList<>(); - public String getGoldenResourceId() { - return myGoldenResourceId; + public List getMdmLinks() { + return myMdmLinks; } - public void setGoldenResourceId(IBaseResource theGoldenResourceId) { - setGoldenResourceId(getIdAsString(theGoldenResourceId)); + public void setMdmLinks(List theMdmLinks) { + myMdmLinks = theMdmLinks; } - public void setGoldenResourceId(String theGoldenResourceId) { - myGoldenResourceId = theGoldenResourceId; - } - - private String getIdAsString(IBaseResource theResource) { - if (theResource == null) { - return null; - } - IIdType idElement = theResource.getIdElement(); - if (idElement == null) { - return null; - } - return idElement.getValueAsString(); - } - - public String getTargetResourceId() { - return myTargetResourceId; - } - - public void setTargetResourceId(IBaseResource theTargetResource) { - setTargetResourceId(getIdAsString(theTargetResource)); - } - - public void setTargetResourceId(String theTargetResourceId) { - myTargetResourceId = theTargetResourceId; - } - - public Set getDuplicateGoldenResourceIds() { - return myDuplicateGoldenResourceIds; - } - - public void setDuplicateGoldenResourceIds(Set theDuplicateGoldenResourceIds) { - myDuplicateGoldenResourceIds = theDuplicateGoldenResourceIds; - } - - public MdmLinkEvent addDuplicateGoldenResourceId(IBaseResource theDuplicateGoldenResourceId) { - String id = getIdAsString(theDuplicateGoldenResourceId); - if (id != null) { - getDuplicateGoldenResourceIds().add(id); - } + public MdmLinkEvent addMdmLink(MdmLinkJson theMdmLink) { + getMdmLinks().add(theMdmLink); return this; } - public MdmMatchResultEnum getMdmMatchResult() { - return myMdmMatchResult; - } - - public void setMdmMatchResult(MdmMatchResultEnum theMdmMatchResult) { - myMdmMatchResult = theMdmMatchResult; - } - - public MdmLinkSourceEnum getMdmLinkSource() { - return myMdmLinkSource; - } - - public void setMdmLinkSource(MdmLinkSourceEnum theMdmLinkSource) { - myMdmLinkSource = theMdmLinkSource; - } - - public Boolean getEidMatch() { - return myEidMatch; - } - - public void setEidMatch(Boolean theEidMatch) { - if (theEidMatch == null) { - myEidMatch = Boolean.FALSE; - return; - } - myEidMatch = theEidMatch; - } - - public Boolean getNewGoldenResource() { - return myNewGoldenResource; - } - - public void setNewGoldenResource(Boolean theNewGoldenResource) { - if (theNewGoldenResource == null) { - myNewGoldenResource = Boolean.FALSE; - return; - } - myNewGoldenResource = theNewGoldenResource; - } - - public Double getScore() { - return myScore; - } - - public void setScore(Double theScore) { - myScore = theScore; - } - - public Long getRuleCount() { - return myRuleCount; - } - - public void setRuleCount(Long theRuleCount) { - myRuleCount = theRuleCount; - } - - public void setFromLink(MdmLinkJson theMdmLinkJson) { - setMdmMatchResult(theMdmLinkJson.getMatchResult()); - setMdmLinkSource(theMdmLinkJson.getLinkSource()); - setEidMatch(theMdmLinkJson.getEidMatch()); - setNewGoldenResource(theMdmLinkJson.getLinkCreatedNewResource()); - setScore(theMdmLinkJson.getScore()); - setRuleCount(theMdmLinkJson.getRuleCount()); - } - @Override public String toString() { - return "MdmLinkChangeEvent{" + - "myMdmMatchResult=" + myMdmMatchResult + - ", myMdmLinkSource=" + myMdmLinkSource + - ", myEidMatch=" + myEidMatch + - ", myNewGoldenResource=" + myNewGoldenResource + - ", myScore=" + myScore + - ", myRuleCount=" + myRuleCount + - ", myTargetResourceId='" + myTargetResourceId + '\'' + - ", myGoldenResourceId='" + myGoldenResourceId + '\'' + - ", myDuplicateGoldenResourceIds=" + myDuplicateGoldenResourceIds + + return "MdmLinkEvent{" + + "myMdmLinks=" + myMdmLinks + '}'; } } diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmTransactionContext.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmTransactionContext.java index 4f13e7ac706..9047462d50f 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmTransactionContext.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/MdmTransactionContext.java @@ -20,9 +20,13 @@ package ca.uhn.fhir.mdm.model; * #L% */ +import ca.uhn.fhir.mdm.api.IMdmLink; import ca.uhn.fhir.mdm.api.MdmLinkEvent; import ca.uhn.fhir.rest.server.TransactionLogMessages; +import java.util.ArrayList; +import java.util.List; + public class MdmTransactionContext { public enum OperationType { @@ -46,7 +50,7 @@ public class MdmTransactionContext { private String myResourceType; - private MdmLinkEvent myMdmLinkEvent = new MdmLinkEvent(); + private List myMdmLinkEvents = new ArrayList<>(); public TransactionLogMessages getTransactionLogMessages() { return myTransactionLogMessages; @@ -96,12 +100,16 @@ public class MdmTransactionContext { this.myResourceType = myResourceType; } - public MdmLinkEvent getMdmLinkEvent() { - return myMdmLinkEvent; + public List getMdmLinks() { + return myMdmLinkEvents; } - public void setMdmLinkChangeEvent(MdmLinkEvent theMdmLinkEvent) { - myMdmLinkEvent = theMdmLinkEvent; + public MdmTransactionContext addMdmLink(IMdmLink theMdmLinkEvent) { + getMdmLinks().add(theMdmLinkEvent); + return this; } + public void setMdmLinks(List theMdmLinkEvents) { + myMdmLinkEvents = theMdmLinkEvents; + } } From 010628fa86094000f3bda8b166d212bbd29ef31b Mon Sep 17 00:00:00 2001 From: Nick Goupinets Date: Tue, 7 Sep 2021 11:24:37 -0400 Subject: [PATCH 13/13] Added asserts --- .../fhir/jpa/mdm/interceptor/MdmEventIT.java | 17 ++++++++++++++--- .../java/ca/uhn/fhir/mdm/api/MdmLinkJson.java | 18 ++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmEventIT.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmEventIT.java index 39ec89b4c7d..cafbdebbff2 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmEventIT.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmEventIT.java @@ -80,7 +80,19 @@ public class MdmEventIT extends BaseMdmR4Test { MdmLinkEvent linkChangeEvent = myMdmHelper.getAfterMdmLatch().getLatchInvocationParameterOfType(MdmLinkEvent.class); assertNotNull(linkChangeEvent); - // MdmTransactionContext ctx = myMdmMatchLinkSvc.updateMdmLinksForMdmSource(patient2, createContextForUpdate(patient2.getIdElement().getResourceType())); + ourLog.info("Got event: {}", linkChangeEvent); + + long expectTwoPossibleMatchesForPatientTwo = linkChangeEvent.getMdmLinks() + .stream() + .filter(l -> l.getSourceId().equals(patient2.getIdElement().toVersionless().getValueAsString()) && l.getMatchResult() == MdmMatchResultEnum.POSSIBLE_MATCH) + .count(); + assertEquals(2, expectTwoPossibleMatchesForPatientTwo); + + long expectOnePossibleDuplicate = linkChangeEvent.getMdmLinks() + .stream() + .filter(l -> l.getMatchResult() == MdmMatchResultEnum.POSSIBLE_DUPLICATE) + .count(); + assertEquals(1, expectOnePossibleDuplicate); List mdmLinkEvent = linkChangeEvent.getMdmLinks(); assertEquals(3, mdmLinkEvent.size()); @@ -122,10 +134,9 @@ public class MdmEventIT extends BaseMdmR4Test { assertEquals(1, linkChangeEvent.getMdmLinks().size()); MdmLinkJson link = linkChangeEvent.getMdmLinks().get(0); - assertEquals(patient1.getResourceType() + "/" + patient1.getIdElement().getIdPart(), link.getSourceId()); + assertEquals(patient1.getIdElement().toVersionless().getValueAsString(), link.getSourceId()); assertEquals(getLinkByTargetId(patient1).getGoldenResourcePid(), new IdDt(link.getGoldenResourceId()).getIdPartAsLong()); assertEquals(MdmMatchResultEnum.MATCH, link.getMatchResult()); } - } diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkJson.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkJson.java index 424298418b4..d21f8deada6 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkJson.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/MdmLinkJson.java @@ -175,4 +175,22 @@ public class MdmLinkJson implements IModelJson { public void setRuleCount(Long theRuleCount) { myRuleCount = theRuleCount; } + + @Override + public String toString() { + return "MdmLinkJson{" + + "myGoldenResourceId='" + myGoldenResourceId + '\'' + + ", mySourceId='" + mySourceId + '\'' + + ", myMatchResult=" + myMatchResult + + ", myLinkSource=" + myLinkSource + + ", myCreated=" + myCreated + + ", myUpdated=" + myUpdated + + ", myVersion='" + myVersion + '\'' + + ", myEidMatch=" + myEidMatch + + ", myLinkCreatedNewResource=" + myLinkCreatedNewResource + + ", myVector=" + myVector + + ", myScore=" + myScore + + ", myRuleCount=" + myRuleCount + + '}'; + } }