2849 Added new parameter to MDM processing

This commit is contained in:
Nick Goupinets 2021-07-29 15:52:09 -04:00
parent f957661dda
commit a2aa2ca1c3
8 changed files with 119 additions and 7 deletions

View File

@ -1986,13 +1986,17 @@ public enum Pointcut implements IPointcut {
* <ul> * <ul>
* <li>ca.uhn.fhir.rest.server.messaging.ResourceOperationMessage - This parameter should not be modified as processing is complete when this hook is invoked.</li> * <li>ca.uhn.fhir.rest.server.messaging.ResourceOperationMessage - This parameter should not be modified as processing is complete when this hook is invoked.</li>
* <li>ca.uhn.fhir.rest.server.TransactionLogMessages - This parameter is for informational messages provided by the MDM module during MDM processing.</li> * <li>ca.uhn.fhir.rest.server.TransactionLogMessages - This parameter is for informational messages provided by the MDM module during MDM processing.</li>
* <li>ca.uhn.fhir.mdm.api.MdmLinkChangeEvent - Contains information about the change event, including target and golden resource IDs and the operation type.</li>
* </ul> * </ul>
* </p> * </p>
* <p> * <p>
* Hooks should return <code>void</code>. * Hooks should return <code>void</code>.
* </p> * </p>
*/ */
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"),
/** /**
* <b>Performance Tracing Hook:</b> * <b>Performance Tracing Hook:</b>

View File

@ -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.ResourceModifiedJsonMessage;
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage; import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage;
import ca.uhn.fhir.mdm.api.IMdmSettings; 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.log.Logs;
import ca.uhn.fhir.mdm.model.MdmTransactionContext; import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.rest.server.TransactionLogMessages; import ca.uhn.fhir.rest.server.TransactionLogMessages;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.messaging.ResourceOperationMessage; import ca.uhn.fhir.rest.server.messaging.ResourceOperationMessage;
import org.hl7.fhir.instance.model.api.IAnyResource; 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.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message; import org.springframework.messaging.Message;
@ -89,11 +92,11 @@ public class MdmMessageHandler implements MessageHandler {
try { try {
switch (theMsg.getOperationType()) { switch (theMsg.getOperationType()) {
case CREATE: case CREATE:
handleCreatePatientOrPractitioner(theMsg, mdmContext); handleCreateResource(theMsg, mdmContext);
break; break;
case UPDATE: case UPDATE:
case MANUALLY_TRIGGERED: case MANUALLY_TRIGGERED:
handleUpdatePatientOrPractitioner(theMsg, mdmContext); handleUpdateResource(theMsg, mdmContext);
break; break;
case DELETE: case DELETE:
default: default:
@ -105,12 +108,15 @@ public class MdmMessageHandler implements MessageHandler {
} finally { } finally {
// Interceptor call: MDM_AFTER_PERSISTED_RESOURCE_CHECKED // 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()); outgoingMsg.setTransactionId(theMsg.getTransactionId());
HookParams params = new HookParams() HookParams params = new HookParams()
.add(ResourceOperationMessage.class, outgoingMsg) .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); 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); myMdmMatchLinkSvc.updateMdmLinksForMdmSource(getResourceFromPayload(theMsg), theMdmTransactionContext);
} }
@ -150,7 +156,7 @@ public class MdmMessageHandler implements MessageHandler {
return (IAnyResource) theMsg.getNewPayload(myFhirContext); return (IAnyResource) theMsg.getNewPayload(myFhirContext);
} }
private void handleUpdatePatientOrPractitioner(ResourceModifiedMessage theMsg, MdmTransactionContext theMdmTransactionContext) { private void handleUpdateResource(ResourceModifiedMessage theMsg, MdmTransactionContext theMdmTransactionContext) {
myMdmMatchLinkSvc.updateMdmLinksForMdmSource(getResourceFromPayload(theMsg), theMdmTransactionContext); myMdmMatchLinkSvc.updateMdmLinksForMdmSource(getResourceFromPayload(theMsg), theMdmTransactionContext);
} }

View File

@ -85,6 +85,8 @@ public class MdmEidUpdateService {
myMdmSurvivorshipService.applySurvivorshipRulesToGoldenResource(theTargetResource, updateContext.getMatchedGoldenResource(), theMdmTransactionContext); myMdmSurvivorshipService.applySurvivorshipRulesToGoldenResource(theTargetResource, updateContext.getMatchedGoldenResource(), theMdmTransactionContext);
myMdmResourceDaoSvc.upsertGoldenResource(updateContext.getMatchedGoldenResource(), theMdmTransactionContext.getResourceType()); myMdmResourceDaoSvc.upsertGoldenResource(updateContext.getMatchedGoldenResource(), theMdmTransactionContext.getResourceType());
} }
theMdmTransactionContext.getMdmLinkChangeEvent().setGoldenResourceId(updateContext.getExistingGoldenResource());
} }
private void handleNoEidsInCommon(IAnyResource theResource, MatchedGoldenResourceCandidate theMatchedGoldenResourceCandidate, MdmTransactionContext theMdmTransactionContext, MdmUpdateContext theUpdateContext) { private void handleNoEidsInCommon(IAnyResource theResource, MatchedGoldenResourceCandidate theMatchedGoldenResourceCandidate, MdmTransactionContext theMdmTransactionContext, MdmUpdateContext theUpdateContext) {

View File

@ -112,11 +112,14 @@ public class MdmMatchLinkSvc {
//Set all GoldenResources as POSSIBLE_DUPLICATE of the last GoldenResource. //Set all GoldenResources as POSSIBLE_DUPLICATE of the last GoldenResource.
IAnyResource firstGoldenResource = goldenResources.get(0); IAnyResource firstGoldenResource = goldenResources.get(0);
theMdmTransactionContext.getMdmLinkChangeEvent().setGoldenResourceId(firstGoldenResource);
goldenResources.subList(1, goldenResources.size()) goldenResources.subList(1, goldenResources.size())
.forEach(possibleDuplicateGoldenResource -> { .forEach(possibleDuplicateGoldenResource -> {
MdmMatchOutcome outcome = MdmMatchOutcome.POSSIBLE_DUPLICATE; MdmMatchOutcome outcome = MdmMatchOutcome.POSSIBLE_DUPLICATE;
outcome.setEidMatch(theCandidateList.isEidMatch()); outcome.setEidMatch(theCandidateList.isEidMatch());
myMdmLinkSvc.updateLink(firstGoldenResource, possibleDuplicateGoldenResource, outcome, MdmLinkSourceEnum.AUTO, theMdmTransactionContext); 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 // 2. Create source resource for the MDM source
// 3. UPDATE MDM LINK TABLE // 3. UPDATE MDM LINK TABLE
myMdmLinkSvc.updateLink(newGoldenResource, theResource, MdmMatchOutcome.NEW_GOLDEN_RESOURCE_MATCH, MdmLinkSourceEnum.AUTO, theMdmTransactionContext); 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) { private void handleMdmCreate(IAnyResource theTargetResource, MatchedGoldenResourceCandidate theGoldenResourceCandidate, MdmTransactionContext theMdmTransactionContext) {

View File

@ -106,5 +106,8 @@ public abstract class BaseMdmHelper implements BeforeEachCallback, AfterEachCall
return channel.getQueueSizeForUnitTest(); return channel.getQueueSizeForUnitTest();
} }
public PointcutLatch getAfterMdmLatch() {
return myAfterMdmLatch;
}
} }

View File

@ -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.MdmHelperConfig;
import ca.uhn.fhir.jpa.mdm.helper.MdmHelperR4; import ca.uhn.fhir.jpa.mdm.helper.MdmHelperR4;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; 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.model.CanonicalEID;
import ca.uhn.fhir.mdm.rules.config.MdmSettings; 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.IBundleProvider;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId; import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.rest.param.ReferenceParam; import ca.uhn.fhir.rest.param.ReferenceParam;
import ca.uhn.fhir.rest.server.TransactionLogMessages; import ca.uhn.fhir.rest.server.TransactionLogMessages;
import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException; 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.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; 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.Medication;
import org.hl7.fhir.r4.model.Organization; import org.hl7.fhir.r4.model.Organization;
import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.Practitioner;
import org.hl7.fhir.r4.model.SearchParameter; import org.hl7.fhir.r4.model.SearchParameter;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.api.extension.RegisterExtension;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired; 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.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextConfiguration;
import java.util.Date; import java.util.Date;
import java.util.List; 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;
import static ca.uhn.fhir.mdm.api.MdmConstants.CODE_GOLDEN_RECORD_REDIRECTED; import static ca.uhn.fhir.mdm.api.MdmConstants.CODE_GOLDEN_RECORD_REDIRECTED;
@ -67,6 +75,24 @@ public class MdmStorageInterceptorIT extends BaseMdmR4Test {
assertLinkCount(1); 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 @Test
public void testSearchExpandingInterceptorWorks() { public void testSearchExpandingInterceptorWorks() {
SearchParameterMap subject = new SearchParameterMap("subject", new ReferenceParam("Patient/123").setMdmExpand(true)).setLoadSynchronous(true); SearchParameterMap subject = new SearchParameterMap("subject", new ReferenceParam("Patient/123").setMdmExpand(true)).setLoadSynchronous(true);

View File

@ -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<String> 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<String> getDuplicateGoldenResourceIds() {
return myDuplicateGoldenResourceIds;
}
public void setDuplicateGoldenResourceIds(Set<String> theDuplicateGoldenResourceIds) {
myDuplicateGoldenResourceIds = theDuplicateGoldenResourceIds;
}
public MdmLinkChangeEvent addDuplicateGoldenResourceId(IBaseResource theDuplicateGoldenResourceId) {
String id = getIdAsString(theDuplicateGoldenResourceId);
if (id != null) {
getDuplicateGoldenResourceIds().add(id);
}
return this;
}
}

View File

@ -20,6 +20,7 @@ package ca.uhn.fhir.mdm.model;
* #L% * #L%
*/ */
import ca.uhn.fhir.mdm.api.MdmLinkChangeEvent;
import ca.uhn.fhir.rest.server.TransactionLogMessages; import ca.uhn.fhir.rest.server.TransactionLogMessages;
public class MdmTransactionContext { public class MdmTransactionContext {
@ -45,6 +46,8 @@ public class MdmTransactionContext {
private String myResourceType; private String myResourceType;
private MdmLinkChangeEvent myMdmLinkChangeEvent = new MdmLinkChangeEvent();
public TransactionLogMessages getTransactionLogMessages() { public TransactionLogMessages getTransactionLogMessages() {
return myTransactionLogMessages; return myTransactionLogMessages;
} }
@ -92,4 +95,12 @@ public class MdmTransactionContext {
public void setResourceType(String myResourceType) { public void setResourceType(String myResourceType) {
this.myResourceType = myResourceType; this.myResourceType = myResourceType;
} }
public MdmLinkChangeEvent getMdmLinkChangeEvent() {
return myMdmLinkChangeEvent;
}
public void setMdmLinkChangeEvent(MdmLinkChangeEvent theMdmLinkChangeEvent) {
myMdmLinkChangeEvent = theMdmLinkChangeEvent;
}
} }