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 {
*
* - ca.uhn.fhir.rest.server.messaging.ResourceOperationMessage - This parameter should not be modified as processing is complete when this hook is invoked.
* - ca.uhn.fhir.rest.server.TransactionLogMessages - This parameter is for informational messages provided by the MDM module during MDM processing.
+ * - ca.uhn.fhir.mdm.api.MdmLinkChangeEvent - Contains information about the change event, including target and golden resource IDs and the operation type.
*
*
*
* 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;
+ }
}