From a2aa2ca1c37ec9f171285c0ef8d3502448c8b37e Mon Sep 17 00:00:00 2001 From: Nick Goupinets Date: Thu, 29 Jul 2021 15:52:09 -0400 Subject: [PATCH] 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; + } }