adding pointcuts for mdm operations (#5116)

* adding pointcuts for mdm operations

* adding changelog

* cleanup

* cleaning up

* formatting

* review fixes

* formatting

* more cleanup

* cleanup

* moving changelog

* fixing a bug

* cleanup

* formatting

* version bump

* fixing broken merge

* fixing the version

---------

Co-authored-by: leif stawnyczy <leifstawnyczy@leifs-MacBook-Pro.local>
This commit is contained in:
TipzCM 2023-08-02 16:38:33 -04:00 committed by GitHub
parent fd5c2c2862
commit a48c602f71
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
122 changed files with 2059 additions and 342 deletions

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -2250,7 +2250,7 @@ public enum Pointcut implements IPointcut {
* <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.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>
* <li>ca.uhn.fhir.mdm.model.mdmevents.MdmLinkEvent - Contains information about the change event, including target and golden resource IDs and the operation type.</li>
* </ul>
* </p>
* <p>
@ -2261,7 +2261,192 @@ public enum Pointcut implements IPointcut {
void.class,
"ca.uhn.fhir.rest.server.messaging.ResourceOperationMessage",
"ca.uhn.fhir.rest.server.TransactionLogMessages",
"ca.uhn.fhir.mdm.api.MdmLinkEvent"),
"ca.uhn.fhir.mdm.model.mdmevents.MdmLinkEvent"),
/**
* <b>MDM Create Link</b>
* This hook is invoked after an MDM link is created,
* and changes have been persisted to the database.
* <p>
* Hook may accept the following parameters:
* </p>
* <ul>
* <li>
* ca.uhn.fhir.rest.api.server.RequestDetails - An object containing details about the request that is about to be processed, including details such as the
* resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
* pulled out of the servlet request.
* </li>
* <li>
* ca.uhn.fhir.mdm.api.MdmLinkChangeEvent - Contains information about the link event, including target and golden resource IDs and the operation type.
* </li>
* </ul>
* <p>
* Hooks should return <code>void</code>.
* </p>
*/
MDM_POST_CREATE_LINK(
void.class, "ca.uhn.fhir.rest.api.server.RequestDetails", "ca.uhn.fhir.mdm.model.mdmevents.MdmLinkEvent"),
/**
* <b>MDM Update Link</b>
* This hook is invoked after an MDM link is updated,
* and changes have been persisted to the database.
* <p>
* Hook may accept the following parameters:
* </p>
* <ul>
* <li>
* ca.uhn.fhir.rest.api.server.RequestDetails - An object containing details about the request that is about to be processed, including details such as the
* resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
* pulled out of the servlet request.
* </li>
* <li>
* ca.uhn.fhir.mdm.api.MdmLinkChangeEvent - Contains information about the link event, including target and golden resource IDs and the operation type.
* </li>
* </ul>
* <p>
* Hooks should return <code>void</code>.
* </p>
*/
MDM_POST_UPDATE_LINK(
void.class, "ca.uhn.fhir.rest.api.server.RequestDetails", "ca.uhn.fhir.mdm.model.mdmevents.MdmLinkEvent"),
/**
* <b>MDM Merge Golden Resources</b>
* This hook is invoked after 2 golden resources have been
* merged together and results persisted.
* <p>
* Hook may accept the following parameters:
* </p>
* <ul>
* <li>
* ca.uhn.fhir.rest.api.server.RequestDetails - An object containing details about the request that is about to be processed, including details such as the
* resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
* pulled out of the servlet request.
* </li>
* <li>
* ca.uhn.fhir.mdm.model.mdmevents.MdmMergeEvent - Contains information about the from and to resources.
* </li>
* </ul>
* <p>
* Hooks should return <code>void</code>.
* </p>
*/
MDM_POST_MERGE_GOLDEN_RESOURCES(
void.class, "ca.uhn.fhir.rest.api.server.RequestDetails", "ca.uhn.fhir.mdm.model.mdmevents.MdmMergeEvent"),
/**
* <b>MDM Link History Hook:</b>
* This hook is invoked after link histories are queried,
* but before the results are returned to the caller.
* <p>
* Hook may accept the following parameters:
* </p>
* <ul>
* <li>
* ca.uhn.fhir.rest.api.server.RequestDetails - An object containing details about the request that is about to be processed.
* </li>
* <li>
* ca.uhn.fhir.mdm.model.mdmevents.MdmHistoryEvent - An MDM History Event containing
* information about the requested golden resource ids and/or source ids input, and
* the returned link histories.
* </li>
* </ul>
*/
MDM_POST_LINK_HISTORY(
void.class,
"ca.uhn.fhir.rest.api.server.RequestDetails",
"ca.uhn.fhir.mdm.model.mdmevents.MdmHistoryEvent"),
/**
* <b>MDM Not Duplicate/Unduplicate Hook:</b>
* This hook is invoked after 2 golden resources with an existing link
* of "POSSIBLE_DUPLICATE" get unlinked/unduplicated.
* <p>
* This hook accepts the following parameters:
* </p>
* <ul>
* <li>
* ca.uhn.fhir.rest.api.server.RequestDetails - An object containing details about the request that is about to be processed.
* </li>
* <li>
* ca.uhn.fhir.mdm.model.mdmevents.MdmLinkEvent - the resulting final link
* between the 2 golden resources; now a NO_MATCH link.
* </li>
* </ul>
*/
MDM_POST_NOT_DUPLICATE(
void.class, "ca.uhn.fhir.rest.api.server.RequestDetails", "ca.uhn.fhir.mdm.model.mdmevents.MdmLinkEvent"),
/**
* <b>MDM Clear Hook:</b>
* This hook is invoked when an mdm clear operation is requested.
* <p>
* This hook accepts the following parameters:
* </p>
* <ul>
* <li>
* ca.uhn.fhir.rest.api.server.RequestDetails - An object containing details about the request that is about to be processed.
* </li>
* <li>
* ca.uhn.fhir.mdm.model.mdmevents.MdmClearEvent - the event containing information on the clear command,
* including the type filter (if any) and the batch size (if any).
* </li>
* </ul>
*/
MDM_CLEAR(
void.class, "ca.uhn.fhir.rest.api.server.RequestDetails", "ca.uhn.fhir.mdm.model.mdmevents.MdmClearEvent"),
/**
* <b>MDM Submit Hook:</b>
* This hook is invoked whenever when mdm submit operation is requested.
* MDM submits can be invoked in multiple ways.
* Some of which accept asynchronous calling, and some of which do not.
* <p>
* If the MDM Submit operation is asynchronous
* (typically because the Prefer: respond-async header has been provided)
* this hook will be invoked after the job is submitted, but before it has
* necessarily been executed.
* </p>
* <p>
* If the MDM Submit operation is synchronous,
* this hook will be invoked immediately after the submit operation
* has been executed, but before the call is returned to the caller.
* </p>
* <ul>
* <li>
* On Patient Type. Can be synchronous or asynchronous.
* </li>
* <li>
* On Practitioner Type. Can be synchronous or asynchronous.
* </li>
* <li>
* On specific patient instances. Is always synchronous.
* </li>
* <li>
* On specific practitioner instances. Is always synchronous.
* </li>
* <li>
* On the server (ie, not on any resource) with or without a resource filter.
* Can be synchronous or asynchronous.
* </li>
* </ul>
* <p>
* In all cases, this hook will take the following parameters:
* </p>
* <ul>
* <li>
* ca.uhn.fhir.rest.api.server.RequestDetails - An object containing details about the request that is about to be processed.
* </li>
* <li>
* ca.uhn.fhir.mdm.model.mdmevents.MdmSubmitEvent - An event with the Mdm Submit information
* (urls specifying paths that will be searched for MDM submit, as well as
* if this was an asynchronous request or not).
* </li>
* </ul>
*/
MDM_SUBMIT(
void.class, "ca.uhn.fhir.rest.api.server.RequestDetails", "ca.uhn.fhir.mdm.model.mdmevents.MdmSubmitEvent"),
/**
* <b>JPA Hook:</b>

View File

@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-bom</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<packaging>pom</packaging>
<name>HAPI FHIR BOM</name>
@ -12,7 +12,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-cli</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -0,0 +1,8 @@
---
type: add
issue: 5090
jira: SMILE-5987
title: "Adding pointcuts for the following MDM Operations:
MDM_CREATE_LINK, MDM_UPDATE_LINK, MDM_MERGE_GOLDEN_RESOURCES,
MDM_LINK_HISTORY, MDM_NOT_DUPLICATE, MDM_CLEAR, MDM_SUBMIT.
"

View File

@ -11,7 +11,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -3,7 +3,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -3,7 +3,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -31,9 +31,9 @@ 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.MdmLinkEvent;
import ca.uhn.fhir.mdm.log.Logs;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkEvent;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.server.TransactionLogMessages;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;

View File

@ -20,6 +20,9 @@
package ca.uhn.fhir.jpa.mdm.svc;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.api.svc.IIdHelperService;
import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc;
@ -30,11 +33,15 @@ import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchOutcome;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.mdm.log.Logs;
import ca.uhn.fhir.mdm.model.MdmMergeGoldenResourcesParams;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.mdm.model.mdmevents.MdmEventResource;
import ca.uhn.fhir.mdm.model.mdmevents.MdmMergeEvent;
import ca.uhn.fhir.mdm.util.GoldenResourceHelper;
import ca.uhn.fhir.mdm.util.MdmPartitionHelper;
import ca.uhn.fhir.mdm.util.MdmResourceUtil;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import org.hl7.fhir.instance.model.api.IAnyResource;
@ -71,64 +78,90 @@ public class GoldenResourceMergerSvcImpl implements IGoldenResourceMergerSvc {
@Autowired
MdmPartitionHelper myMdmPartitionHelper;
@Autowired
IInterceptorBroadcaster myInterceptorBroadcaster;
@Override
@Transactional
public IAnyResource mergeGoldenResources(
IAnyResource theFromGoldenResource,
IAnyResource theMergedResource,
IAnyResource theToGoldenResource,
MdmTransactionContext theMdmTransactionContext) {
String resourceType = theMdmTransactionContext.getResourceType();
public IAnyResource mergeGoldenResources(MdmMergeGoldenResourcesParams theParams) {
MdmTransactionContext mdmTransactionContext = theParams.getMdmTransactionContext();
IAnyResource mergedResource = theParams.getManuallyMergedResource();
IAnyResource fromGoldenResource = theParams.getFromGoldenResource();
IAnyResource toGoldenResource = theParams.getToGoldenResource();
if (theMergedResource != null) {
if (myGoldenResourceHelper.hasIdentifier(theMergedResource)) {
String resourceType = mdmTransactionContext.getResourceType();
if (mergedResource != null) {
if (myGoldenResourceHelper.hasIdentifier(mergedResource)) {
throw new IllegalArgumentException(
Msg.code(751) + "Manually merged resource can not contain identifiers");
}
myGoldenResourceHelper.mergeIndentifierFields(
theFromGoldenResource, theMergedResource, theMdmTransactionContext);
myGoldenResourceHelper.mergeIndentifierFields(
theToGoldenResource, theMergedResource, theMdmTransactionContext);
myGoldenResourceHelper.mergeIndentifierFields(fromGoldenResource, mergedResource, mdmTransactionContext);
myGoldenResourceHelper.mergeIndentifierFields(toGoldenResource, mergedResource, mdmTransactionContext);
theMergedResource.setId(theToGoldenResource.getId());
theToGoldenResource = (IAnyResource) myMdmResourceDaoSvc
.upsertGoldenResource(theMergedResource, resourceType)
mergedResource.setId(toGoldenResource.getId());
toGoldenResource = (IAnyResource) myMdmResourceDaoSvc
.upsertGoldenResource(mergedResource, resourceType)
.getResource();
} else {
myGoldenResourceHelper.mergeIndentifierFields(
theFromGoldenResource, theToGoldenResource, theMdmTransactionContext);
myGoldenResourceHelper.mergeNonIdentiferFields(
theFromGoldenResource, theToGoldenResource, theMdmTransactionContext);
myGoldenResourceHelper.mergeIndentifierFields(fromGoldenResource, toGoldenResource, mdmTransactionContext);
myGoldenResourceHelper.mergeNonIdentiferFields(fromGoldenResource, toGoldenResource, mdmTransactionContext);
// Save changes to the golden resource
myMdmResourceDaoSvc.upsertGoldenResource(theToGoldenResource, resourceType);
myMdmResourceDaoSvc.upsertGoldenResource(toGoldenResource, resourceType);
}
myMdmPartitionHelper.validateMdmResourcesPartitionMatches(theFromGoldenResource, theToGoldenResource);
myMdmPartitionHelper.validateMdmResourcesPartitionMatches(fromGoldenResource, toGoldenResource);
// Merge the links from the FROM to the TO resource. Clean up dangling links.
mergeGoldenResourceLinks(
theFromGoldenResource,
theToGoldenResource,
theFromGoldenResource.getIdElement(),
theMdmTransactionContext);
fromGoldenResource, toGoldenResource, fromGoldenResource.getIdElement(), mdmTransactionContext);
// Create the new REDIRECT link
addMergeLink(theToGoldenResource, theFromGoldenResource, resourceType, theMdmTransactionContext);
addMergeLink(toGoldenResource, fromGoldenResource, resourceType, mdmTransactionContext);
// Strip the golden resource tag from the now-deprecated resource.
myMdmResourceDaoSvc.removeGoldenResourceTag(theFromGoldenResource, resourceType);
myMdmResourceDaoSvc.removeGoldenResourceTag(fromGoldenResource, resourceType);
// Add the REDIRECT tag to that same deprecated resource.
MdmResourceUtil.setGoldenResourceRedirected(theFromGoldenResource);
MdmResourceUtil.setGoldenResourceRedirected(fromGoldenResource);
// Save the deprecated resource.
myMdmResourceDaoSvc.upsertGoldenResource(theFromGoldenResource, resourceType);
myMdmResourceDaoSvc.upsertGoldenResource(fromGoldenResource, resourceType);
log(
theMdmTransactionContext,
"Merged " + theFromGoldenResource.getIdElement().toVersionless() + " into "
+ theToGoldenResource.getIdElement().toVersionless());
return theToGoldenResource;
mdmTransactionContext,
"Merged " + fromGoldenResource.getIdElement().toVersionless() + " into "
+ toGoldenResource.getIdElement().toVersionless());
// invoke hooks
invokeMdmMergeGoldenResourcesHook(theParams, fromGoldenResource, toGoldenResource);
return toGoldenResource;
}
private void invokeMdmMergeGoldenResourcesHook(
MdmMergeGoldenResourcesParams theParams, IAnyResource fromGoldenResource, IAnyResource toGoldenResource) {
if (myInterceptorBroadcaster.hasHooks(Pointcut.MDM_POST_MERGE_GOLDEN_RESOURCES)) {
// pointcut for MDM_POST_MERGE_GOLDEN_RESOURCES
MdmMergeEvent event = new MdmMergeEvent();
MdmEventResource from = new MdmEventResource();
from.setId(
fromGoldenResource.getIdElement().toUnqualifiedVersionless().getValue());
from.setResourceType(fromGoldenResource.fhirType());
from.setIsGoldenResource(true);
event.setFromResource(from);
MdmEventResource to = new MdmEventResource();
to.setId(toGoldenResource.getIdElement().toUnqualifiedVersionless().getValue());
to.setResourceType(toGoldenResource.fhirType());
to.setIsGoldenResource(true);
event.setToResource(to);
HookParams params = new HookParams();
params.add(MdmMergeEvent.class, event);
params.add(RequestDetails.class, theParams.getRequestDetails());
myInterceptorBroadcaster.callHooks(Pointcut.MDM_POST_MERGE_GOLDEN_RESOURCES, params);
}
}
/**

View File

@ -20,9 +20,9 @@
package ca.uhn.fhir.jpa.mdm.svc;
import ca.uhn.fhir.mdm.api.IMdmLink;
import ca.uhn.fhir.mdm.api.MdmLinkJson;
import ca.uhn.fhir.mdm.api.MdmLinkWithRevision;
import ca.uhn.fhir.mdm.api.MdmLinkWithRevisionJson;
import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkJson;
import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkWithRevisionJson;
/**
* Contract for decoupling API dependency from the base / JPA modules.

View File

@ -22,6 +22,9 @@ package ca.uhn.fhir.jpa.mdm.svc;
import ca.uhn.fhir.batch2.api.IJobCoordinator;
import ca.uhn.fhir.batch2.model.JobInstanceStartRequest;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.interceptor.model.ReadPartitionIdRequestDetails;
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.batch.models.Batch2JobStartResponse;
@ -32,8 +35,6 @@ import ca.uhn.fhir.mdm.api.IMdmLinkCreateSvc;
import ca.uhn.fhir.mdm.api.IMdmLinkQuerySvc;
import ca.uhn.fhir.mdm.api.IMdmLinkUpdaterSvc;
import ca.uhn.fhir.mdm.api.MdmHistorySearchParameters;
import ca.uhn.fhir.mdm.api.MdmLinkJson;
import ca.uhn.fhir.mdm.api.MdmLinkWithRevisionJson;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.mdm.api.MdmQuerySearchParameters;
import ca.uhn.fhir.mdm.api.paging.MdmPageRequest;
@ -41,9 +42,15 @@ import ca.uhn.fhir.mdm.batch2.clear.MdmClearAppCtx;
import ca.uhn.fhir.mdm.batch2.clear.MdmClearJobParameters;
import ca.uhn.fhir.mdm.batch2.submit.MdmSubmitAppCtx;
import ca.uhn.fhir.mdm.batch2.submit.MdmSubmitJobParameters;
import ca.uhn.fhir.mdm.model.MdmCreateOrUpdateParams;
import ca.uhn.fhir.mdm.model.MdmMergeGoldenResourcesParams;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.mdm.model.MdmUnduplicateGoldenResourceParams;
import ca.uhn.fhir.mdm.model.mdmevents.MdmClearEvent;
import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkJson;
import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkWithRevisionJson;
import ca.uhn.fhir.mdm.model.mdmevents.MdmSubmitEvent;
import ca.uhn.fhir.mdm.provider.MdmControllerHelper;
import ca.uhn.fhir.mdm.provider.MdmControllerUtil;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.provider.ProviderConstants;
@ -93,24 +100,43 @@ public class MdmControllerSvcImpl implements IMdmControllerSvc {
@Autowired
IJobCoordinator myJobCoordinator;
@Autowired
IInterceptorBroadcaster myInterceptorBroadcaster;
public MdmControllerSvcImpl() {}
@Override
public IAnyResource mergeGoldenResources(
String theFromGoldenResourceId,
String theToGoldenResourceId,
IAnyResource theManuallyMergedGoldenResource,
MdmTransactionContext theMdmTransactionContext) {
IAnyResource fromGoldenResource = myMdmControllerHelper.getLatestGoldenResourceFromIdOrThrowException(
ProviderConstants.MDM_MERGE_GR_FROM_GOLDEN_RESOURCE_ID, theFromGoldenResourceId);
IAnyResource toGoldenResource = myMdmControllerHelper.getLatestGoldenResourceFromIdOrThrowException(
ProviderConstants.MDM_MERGE_GR_TO_GOLDEN_RESOURCE_ID, theToGoldenResourceId);
public IAnyResource mergeGoldenResources(MdmMergeGoldenResourcesParams theParams) {
if (theParams.getFromGoldenResource() == null) {
theParams.setFromGoldenResource(myMdmControllerHelper.getLatestGoldenResourceFromIdOrThrowException(
ProviderConstants.MDM_MERGE_GR_FROM_GOLDEN_RESOURCE_ID, theParams.getFromGoldenResourceId()));
}
IAnyResource fromGoldenResource = theParams.getFromGoldenResource();
;
if (theParams.getToGoldenResource() == null) {
theParams.setToGoldenResource(myMdmControllerHelper.getLatestGoldenResourceFromIdOrThrowException(
ProviderConstants.MDM_MERGE_GR_TO_GOLDEN_RESOURCE_ID, theParams.getToGoldenResourceId()));
}
IAnyResource toGoldenResource = theParams.getToGoldenResource();
myMdmControllerHelper.validateMergeResources(fromGoldenResource, toGoldenResource);
myMdmControllerHelper.validateSameVersion(fromGoldenResource, theFromGoldenResourceId);
myMdmControllerHelper.validateSameVersion(toGoldenResource, theToGoldenResourceId);
myMdmControllerHelper.validateSameVersion(fromGoldenResource, theParams.getFromGoldenResourceId());
myMdmControllerHelper.validateSameVersion(toGoldenResource, theParams.getToGoldenResourceId());
return myGoldenResourceMergerSvc.mergeGoldenResources(
fromGoldenResource, theManuallyMergedGoldenResource, toGoldenResource, theMdmTransactionContext);
return myGoldenResourceMergerSvc.mergeGoldenResources(theParams);
}
@Override
public IAnyResource updateLink(
String theGoldenResourceId,
String theSourceResourceId,
String theMatchResult,
MdmTransactionContext theMdmTransactionContext) {
MdmCreateOrUpdateParams params = new MdmCreateOrUpdateParams();
params.setResourceId(theSourceResourceId);
params.setGoldenResourceId(theGoldenResourceId);
params.setMdmContext(theMdmTransactionContext);
params.setMatchResult(MdmMatchResultEnum.valueOf(theMatchResult));
return updateLink(params);
}
@Override
@ -228,21 +254,25 @@ public class MdmControllerSvcImpl implements IMdmControllerSvc {
return resultPage;
}
@Override
public IAnyResource updateLink(
String theGoldenResourceId,
String theSourceResourceId,
String theMatchResult,
MdmTransactionContext theMdmTransactionContext) {
MdmMatchResultEnum matchResult = MdmControllerUtil.extractMatchResultOrNull(theMatchResult);
IAnyResource goldenResource = myMdmControllerHelper.getLatestGoldenResourceFromIdOrThrowException(
ProviderConstants.MDM_UPDATE_LINK_GOLDEN_RESOURCE_ID, theGoldenResourceId);
IAnyResource source = myMdmControllerHelper.getLatestSourceFromIdOrThrowException(
ProviderConstants.MDM_UPDATE_LINK_RESOURCE_ID, theSourceResourceId);
myMdmControllerHelper.validateSameVersion(goldenResource, theGoldenResourceId);
myMdmControllerHelper.validateSameVersion(source, theSourceResourceId);
private void convertAndValidateParameters(MdmCreateOrUpdateParams theParams) {
if (theParams.getGoldenResource() == null) {
IAnyResource goldenResource = myMdmControllerHelper.getLatestGoldenResourceFromIdOrThrowException(
ProviderConstants.MDM_UPDATE_LINK_GOLDEN_RESOURCE_ID, theParams.getGoldenResourceId());
theParams.setGoldenResource(goldenResource);
}
if (theParams.getSourceResource() == null) {
IAnyResource source = myMdmControllerHelper.getLatestSourceFromIdOrThrowException(
ProviderConstants.MDM_UPDATE_LINK_RESOURCE_ID, theParams.getResourceId());
theParams.setSourceResource(source);
}
myMdmControllerHelper.validateSameVersion(theParams.getGoldenResource(), theParams.getGoldenResourceId());
myMdmControllerHelper.validateSameVersion(theParams.getSourceResource(), theParams.getResourceId());
}
return myIMdmLinkUpdaterSvc.updateLink(goldenResource, source, matchResult, theMdmTransactionContext);
@Override
public IAnyResource updateLink(MdmCreateOrUpdateParams theParams) {
convertAndValidateParameters(theParams);
return myIMdmLinkUpdaterSvc.updateLink(theParams);
}
@Override
@ -251,15 +281,21 @@ public class MdmControllerSvcImpl implements IMdmControllerSvc {
String theSourceResourceId,
@Nullable String theMatchResult,
MdmTransactionContext theMdmTransactionContext) {
MdmMatchResultEnum matchResult = MdmControllerUtil.extractMatchResultOrNull(theMatchResult);
IAnyResource goldenResource = myMdmControllerHelper.getLatestGoldenResourceFromIdOrThrowException(
ProviderConstants.MDM_CREATE_LINK_GOLDEN_RESOURCE_ID, theGoldenResourceId);
IAnyResource source = myMdmControllerHelper.getLatestSourceFromIdOrThrowException(
ProviderConstants.MDM_CREATE_LINK_RESOURCE_ID, theSourceResourceId);
myMdmControllerHelper.validateSameVersion(goldenResource, theGoldenResourceId);
myMdmControllerHelper.validateSameVersion(source, theSourceResourceId);
MdmCreateOrUpdateParams params = new MdmCreateOrUpdateParams();
params.setGoldenResourceId(theGoldenResourceId);
params.setResourceId(theSourceResourceId);
params.setMdmContext(theMdmTransactionContext);
if (theMatchResult != null) {
params.setMatchResult(MdmMatchResultEnum.valueOf(theMatchResult));
}
return createLink(params);
}
return myIMdmLinkCreateSvc.createLink(goldenResource, source, matchResult, theMdmTransactionContext);
@Override
public IAnyResource createLink(MdmCreateOrUpdateParams theParams) {
convertAndValidateParameters(theParams);
return myIMdmLinkCreateSvc.createLink(theParams);
}
@Override
@ -269,9 +305,10 @@ public class MdmControllerSvcImpl implements IMdmControllerSvc {
ServletRequestDetails theRequestDetails) {
MdmClearJobParameters params = new MdmClearJobParameters();
params.setResourceNames(theResourceNames);
if (theBatchSize != null
boolean hasBatchSize = theBatchSize != null
&& theBatchSize.getValue() != null
&& theBatchSize.getValue().longValue() > 0) {
&& theBatchSize.getValue().longValue() > 0;
if (hasBatchSize) {
params.setBatchSize(theBatchSize.getValue().intValue());
}
@ -287,6 +324,20 @@ public class MdmControllerSvcImpl implements IMdmControllerSvc {
Batch2JobStartResponse response = myJobCoordinator.startInstance(theRequestDetails, request);
String id = response.getInstanceId();
if (myInterceptorBroadcaster.hasHooks(Pointcut.MDM_CLEAR)) {
// MDM_CLEAR hook:
MdmClearEvent event = new MdmClearEvent();
event.setResourceTypes(theResourceNames);
if (hasBatchSize) {
event.setBatchSize(theBatchSize.getValue().longValue());
}
HookParams hookParams = new HookParams();
hookParams.add(RequestDetails.class, theRequestDetails);
hookParams.add(MdmClearEvent.class, event);
myInterceptorBroadcaster.callHooks(Pointcut.MDM_CLEAR, hookParams);
}
IBaseParameters retVal = ParametersUtil.newInstance(myFhirContext);
ParametersUtil.addParameterToParametersString(
myFhirContext, retVal, ProviderConstants.OPERATION_BATCH_RESPONSE_JOB_ID, id);
@ -297,10 +348,10 @@ public class MdmControllerSvcImpl implements IMdmControllerSvc {
public IBaseParameters submitMdmSubmitJob(
List<String> theUrls, IPrimitiveType<BigDecimal> theBatchSize, ServletRequestDetails theRequestDetails) {
MdmSubmitJobParameters params = new MdmSubmitJobParameters();
if (theBatchSize != null
boolean hasBatchSize = theBatchSize != null
&& theBatchSize.getValue() != null
&& theBatchSize.getValue().longValue() > 0) {
&& theBatchSize.getValue().longValue() > 0;
if (hasBatchSize) {
params.setBatchSize(theBatchSize.getValue().intValue());
}
params.setRequestPartitionId(RequestPartitionId.allPartitions());
@ -317,6 +368,22 @@ public class MdmControllerSvcImpl implements IMdmControllerSvc {
IBaseParameters retVal = ParametersUtil.newInstance(myFhirContext);
ParametersUtil.addParameterToParametersString(
myFhirContext, retVal, ProviderConstants.OPERATION_BATCH_RESPONSE_JOB_ID, id);
if (myInterceptorBroadcaster.hasHooks(Pointcut.MDM_SUBMIT)) {
// MDM_SUBMIT batch submit job
MdmSubmitEvent event = new MdmSubmitEvent();
event.setBatchJob(true);
event.setUrls(theUrls);
if (hasBatchSize) {
event.setBatchSize(theBatchSize.getValue().longValue());
}
HookParams hookParams = new HookParams();
hookParams.add(RequestDetails.class, theRequestDetails);
hookParams.add(MdmSubmitEvent.class, event);
myInterceptorBroadcaster.callHooks(Pointcut.MDM_SUBMIT, hookParams);
}
return retVal;
}
@ -325,12 +392,43 @@ public class MdmControllerSvcImpl implements IMdmControllerSvc {
String theGoldenResourceId,
String theTargetGoldenResourceId,
MdmTransactionContext theMdmTransactionContext) {
IAnyResource goldenResource = myMdmControllerHelper.getLatestGoldenResourceFromIdOrThrowException(
ProviderConstants.MDM_UPDATE_LINK_GOLDEN_RESOURCE_ID, theGoldenResourceId);
IAnyResource target = myMdmControllerHelper.getLatestGoldenResourceFromIdOrThrowException(
ProviderConstants.MDM_UPDATE_LINK_RESOURCE_ID, theTargetGoldenResourceId);
MdmUnduplicateGoldenResourceParams params = new MdmUnduplicateGoldenResourceParams();
params.setTargetGoldenResourceId(theTargetGoldenResourceId);
params.setGoldenResourceId(theGoldenResourceId);
params.setMdmContext(theMdmTransactionContext);
myIMdmLinkUpdaterSvc.notDuplicateGoldenResource(goldenResource, target, theMdmTransactionContext);
unduplicateGoldenResource(params);
}
@Override
public void unduplicateGoldenResource(MdmUnduplicateGoldenResourceParams theParams) {
if (theParams.getGoldenResource() == null) {
IAnyResource goldenResource = myMdmControllerHelper.getLatestGoldenResourceFromIdOrThrowException(
ProviderConstants.MDM_UPDATE_LINK_GOLDEN_RESOURCE_ID, theParams.getGoldenResourceId());
theParams.setGoldenResource(goldenResource);
}
if (theParams.getTargetGoldenResource() == null) {
IAnyResource target = myMdmControllerHelper.getLatestGoldenResourceFromIdOrThrowException(
ProviderConstants.MDM_UPDATE_LINK_RESOURCE_ID, theParams.getTargetGoldenResourceId());
theParams.setTargetGoldenResource(target);
}
myIMdmLinkUpdaterSvc.unduplicateGoldenResource(theParams);
}
@Override
public IAnyResource mergeGoldenResources(
String theFromGoldenResourceId,
String theToGoldenResourceId,
IAnyResource theManuallyMergedGoldenResource,
MdmTransactionContext theMdmTransactionContext) {
MdmMergeGoldenResourcesParams params = new MdmMergeGoldenResourcesParams();
params.setToGoldenResourceId(theToGoldenResourceId);
params.setFromGoldenResourceId(theFromGoldenResourceId);
params.setToGoldenResourceId(theToGoldenResourceId);
params.setManuallyMergedResource(theManuallyMergedGoldenResource);
params.setMdmTransactionContext(theMdmTransactionContext);
return mergeGoldenResources(params);
}
private void validateMdmQueryPermissions(

View File

@ -21,6 +21,9 @@ package ca.uhn.fhir.jpa.mdm.svc;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.api.svc.IIdHelperService;
import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc;
@ -31,11 +34,13 @@ import ca.uhn.fhir.mdm.api.IMdmSettings;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.mdm.log.Logs;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.mdm.model.MdmCreateOrUpdateParams;
import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkEvent;
import ca.uhn.fhir.mdm.util.MdmPartitionHelper;
import ca.uhn.fhir.mdm.util.MdmResourceUtil;
import ca.uhn.fhir.mdm.util.MessageHelper;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import org.hl7.fhir.instance.model.api.IAnyResource;
@ -53,9 +58,11 @@ public class MdmLinkCreateSvcImpl implements IMdmLinkCreateSvc {
@Autowired
FhirContext myFhirContext;
@SuppressWarnings("rawtypes")
@Autowired
IIdHelperService myIdHelperService;
@SuppressWarnings("rawtypes")
@Autowired
MdmLinkDaoSvc myMdmLinkDaoSvc;
@ -68,49 +75,56 @@ public class MdmLinkCreateSvcImpl implements IMdmLinkCreateSvc {
@Autowired
MdmPartitionHelper myMdmPartitionHelper;
@Autowired
private IMdmModelConverterSvc myModelConverter;
@Autowired
private IInterceptorBroadcaster myInterceptorBroadcaster;
@SuppressWarnings({"unchecked", "rawtypes"})
@Transactional
@Override
public IAnyResource createLink(
IAnyResource theGoldenResource,
IAnyResource theSourceResource,
MdmMatchResultEnum theMatchResult,
MdmTransactionContext theMdmContext) {
String sourceType = myFhirContext.getResourceType(theSourceResource);
public IAnyResource createLink(MdmCreateOrUpdateParams theParams) {
IAnyResource sourceResource = theParams.getSourceResource();
IAnyResource goldenResource = theParams.getGoldenResource();
MdmMatchResultEnum matchResult = theParams.getMatchResult();
validateCreateLinkRequest(theGoldenResource, theSourceResource, sourceType);
String sourceType = myFhirContext.getResourceType(sourceResource);
IResourcePersistentId goldenResourceId = myIdHelperService.getPidOrThrowException(theGoldenResource);
IResourcePersistentId targetId = myIdHelperService.getPidOrThrowException(theSourceResource);
validateCreateLinkRequest(goldenResource, sourceResource, sourceType);
IResourcePersistentId goldenResourceId = myIdHelperService.getPidOrThrowException(goldenResource);
IResourcePersistentId targetId = myIdHelperService.getPidOrThrowException(sourceResource);
// check if the golden resource and the source resource are in the same partition, throw error if not
myMdmPartitionHelper.validateMdmResourcesPartitionMatches(theGoldenResource, theSourceResource);
myMdmPartitionHelper.validateMdmResourcesPartitionMatches(goldenResource, sourceResource);
Optional<? extends IMdmLink> optionalMdmLink =
myMdmLinkDaoSvc.getLinkByGoldenResourcePidAndSourceResourcePid(goldenResourceId, targetId);
if (optionalMdmLink.isPresent()) {
throw new InvalidRequestException(
Msg.code(753) + myMessageHelper.getMessageForPresentLink(theGoldenResource, theSourceResource));
Msg.code(753) + myMessageHelper.getMessageForPresentLink(goldenResource, sourceResource));
}
List<? extends IMdmLink> mdmLinks =
myMdmLinkDaoSvc.getMdmLinksBySourcePidAndMatchResult(targetId, MdmMatchResultEnum.MATCH);
if (mdmLinks.size() > 0 && theMatchResult == MdmMatchResultEnum.MATCH) {
if (mdmLinks.size() > 0 && matchResult == MdmMatchResultEnum.MATCH) {
throw new InvalidRequestException(
Msg.code(754) + myMessageHelper.getMessageForMultipleGoldenRecords(theSourceResource));
Msg.code(754) + myMessageHelper.getMessageForMultipleGoldenRecords(sourceResource));
}
IMdmLink mdmLink = myMdmLinkDaoSvc.getOrCreateMdmLinkByGoldenResourceAndSourceResource(
theGoldenResource, theSourceResource);
IMdmLink mdmLink =
myMdmLinkDaoSvc.getOrCreateMdmLinkByGoldenResourceAndSourceResource(goldenResource, sourceResource);
mdmLink.setLinkSource(MdmLinkSourceEnum.MANUAL);
mdmLink.setMdmSourceType(sourceType);
if (theMatchResult == null) {
if (matchResult == null) {
mdmLink.setMatchResult(MdmMatchResultEnum.MATCH);
} else {
mdmLink.setMatchResult(theMatchResult);
mdmLink.setMatchResult(matchResult);
}
// Add partition for the mdm link if it doesn't exist
RequestPartitionId goldenResourcePartitionId =
(RequestPartitionId) theGoldenResource.getUserData(Constants.RESOURCE_PARTITION_ID);
(RequestPartitionId) goldenResource.getUserData(Constants.RESOURCE_PARTITION_ID);
if (goldenResourcePartitionId != null
&& goldenResourcePartitionId.hasPartitionIds()
&& goldenResourcePartitionId.getFirstPartitionIdOrNull() != null
@ -119,11 +133,20 @@ public class MdmLinkCreateSvcImpl implements IMdmLinkCreateSvc {
goldenResourcePartitionId.getFirstPartitionIdOrNull(),
goldenResourcePartitionId.getPartitionDate()));
}
ourLog.info("Manually creating a " + theGoldenResource.getIdElement().toVersionless() + " to "
+ theSourceResource.getIdElement().toVersionless() + " mdm link.");
ourLog.info("Manually creating a " + goldenResource.getIdElement().toVersionless() + " to "
+ sourceResource.getIdElement().toVersionless() + " mdm link.");
myMdmLinkDaoSvc.save(mdmLink);
return theGoldenResource;
if (myInterceptorBroadcaster.hasHooks(Pointcut.MDM_POST_CREATE_LINK)) {
// pointcut for MDM_POST_CREATE_LINK
MdmLinkEvent event = new MdmLinkEvent();
event.addMdmLink(myModelConverter.toJson(mdmLink));
HookParams hookParams = new HookParams();
hookParams.add(RequestDetails.class, theParams.getRequestDetails()).add(MdmLinkEvent.class, event);
myInterceptorBroadcaster.callHooks(Pointcut.MDM_POST_CREATE_LINK, hookParams);
}
return goldenResource;
}
private void validateCreateLinkRequest(

View File

@ -23,14 +23,14 @@ import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc;
import ca.uhn.fhir.mdm.api.IMdmLink;
import ca.uhn.fhir.mdm.api.IMdmLinkQuerySvc;
import ca.uhn.fhir.mdm.api.MdmHistorySearchParameters;
import ca.uhn.fhir.mdm.api.MdmLinkJson;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmLinkWithRevision;
import ca.uhn.fhir.mdm.api.MdmLinkWithRevisionJson;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.mdm.api.MdmQuerySearchParameters;
import ca.uhn.fhir.mdm.api.paging.MdmPageRequest;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkJson;
import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkWithRevisionJson;
import org.hl7.fhir.instance.model.api.IIdType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

@ -21,6 +21,9 @@ package ca.uhn.fhir.jpa.mdm.svc;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.api.svc.IIdHelperService;
import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc;
@ -32,11 +35,15 @@ import ca.uhn.fhir.mdm.api.IMdmSurvivorshipService;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.mdm.log.Logs;
import ca.uhn.fhir.mdm.model.MdmCreateOrUpdateParams;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.mdm.model.MdmUnduplicateGoldenResourceParams;
import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkEvent;
import ca.uhn.fhir.mdm.util.MdmPartitionHelper;
import ca.uhn.fhir.mdm.util.MdmResourceUtil;
import ca.uhn.fhir.mdm.util.MessageHelper;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.provider.ProviderConstants;
@ -56,9 +63,11 @@ public class MdmLinkUpdaterSvcImpl implements IMdmLinkUpdaterSvc {
@Autowired
FhirContext myFhirContext;
@SuppressWarnings("rawtypes")
@Autowired
IIdHelperService myIdHelperService;
@SuppressWarnings("rawtypes")
@Autowired
MdmLinkDaoSvc myMdmLinkDaoSvc;
@ -80,52 +89,60 @@ public class MdmLinkUpdaterSvcImpl implements IMdmLinkUpdaterSvc {
@Autowired
MdmPartitionHelper myMdmPartitionHelper;
@Autowired
IInterceptorBroadcaster myInterceptorBroadcaster;
@Autowired
private IMdmModelConverterSvc myModelConverter;
@SuppressWarnings({"rawtypes", "unchecked"})
@Transactional
@Override
public IAnyResource updateLink(
IAnyResource theGoldenResource,
IAnyResource theSourceResource,
MdmMatchResultEnum theMatchResult,
MdmTransactionContext theMdmContext) {
String sourceType = myFhirContext.getResourceType(theSourceResource);
public IAnyResource updateLink(MdmCreateOrUpdateParams theParams) {
IAnyResource sourceResource = theParams.getSourceResource();
IAnyResource goldenResource = theParams.getGoldenResource();
MdmTransactionContext mdmContext = theParams.getMdmContext();
MdmMatchResultEnum matchResult = theParams.getMatchResult();
validateUpdateLinkRequest(theGoldenResource, theSourceResource, theMatchResult, sourceType);
String sourceType = myFhirContext.getResourceType(sourceResource);
IResourcePersistentId goldenResourceId = myIdHelperService.getPidOrThrowException(theGoldenResource);
IResourcePersistentId sourceResourceId = myIdHelperService.getPidOrThrowException(theSourceResource);
validateUpdateLinkRequest(goldenResource, sourceResource, matchResult, sourceType);
IResourcePersistentId goldenResourceId = myIdHelperService.getPidOrThrowException(goldenResource);
IResourcePersistentId sourceResourceId = myIdHelperService.getPidOrThrowException(sourceResource);
// check if the golden resource and the source resource are in the same partition if cross partition mdm is not
// allowed, throw error if not
myMdmPartitionHelper.validateMdmResourcesPartitionMatches(theGoldenResource, theSourceResource);
myMdmPartitionHelper.validateMdmResourcesPartitionMatches(goldenResource, sourceResource);
Optional<? extends IMdmLink> optionalMdmLink =
myMdmLinkDaoSvc.getLinkByGoldenResourcePidAndSourceResourcePid(goldenResourceId, sourceResourceId);
if (optionalMdmLink.isEmpty()) {
throw new InvalidRequestException(
Msg.code(738) + myMessageHelper.getMessageForNoLink(theGoldenResource, theSourceResource));
Msg.code(738) + myMessageHelper.getMessageForNoLink(goldenResource, sourceResource));
}
IMdmLink mdmLink = optionalMdmLink.get();
validateNoMatchPresentWhenAcceptingPossibleMatch(theSourceResource, goldenResourceId, theMatchResult);
validateNoMatchPresentWhenAcceptingPossibleMatch(sourceResource, goldenResourceId, matchResult);
if (mdmLink.getMatchResult() == theMatchResult) {
ourLog.warn("MDM Link for " + theGoldenResource.getIdElement().toVersionless() + ", "
+ theSourceResource.getIdElement().toVersionless() + " already has value " + theMatchResult
if (mdmLink.getMatchResult() == matchResult) {
ourLog.warn("MDM Link for " + goldenResource.getIdElement().toVersionless() + ", "
+ sourceResource.getIdElement().toVersionless() + " already has value " + matchResult
+ ". Nothing to do.");
return theGoldenResource;
return goldenResource;
}
ourLog.info("Manually updating MDM Link for "
+ theGoldenResource.getIdElement().toVersionless() + ", "
+ theSourceResource.getIdElement().toVersionless() + " from " + mdmLink.getMatchResult() + " to "
+ theMatchResult + ".");
mdmLink.setMatchResult(theMatchResult);
+ goldenResource.getIdElement().toVersionless() + ", "
+ sourceResource.getIdElement().toVersionless() + " from " + mdmLink.getMatchResult() + " to "
+ matchResult + ".");
mdmLink.setMatchResult(matchResult);
mdmLink.setLinkSource(MdmLinkSourceEnum.MANUAL);
// Add partition for the mdm link if it doesn't exist
RequestPartitionId goldenResourcePartitionId =
(RequestPartitionId) theGoldenResource.getUserData(Constants.RESOURCE_PARTITION_ID);
(RequestPartitionId) goldenResource.getUserData(Constants.RESOURCE_PARTITION_ID);
if (goldenResourcePartitionId != null
&& goldenResourcePartitionId.hasPartitionIds()
&& goldenResourcePartitionId.getFirstPartitionIdOrNull() != null
@ -136,29 +153,39 @@ public class MdmLinkUpdaterSvcImpl implements IMdmLinkUpdaterSvc {
}
myMdmLinkDaoSvc.save(mdmLink);
if (theMatchResult == MdmMatchResultEnum.MATCH) {
if (matchResult == MdmMatchResultEnum.MATCH) {
// only apply survivorship rules in case of a match
myMdmSurvivorshipService.applySurvivorshipRulesToGoldenResource(
theSourceResource, theGoldenResource, theMdmContext);
myMdmSurvivorshipService.applySurvivorshipRulesToGoldenResource(sourceResource, goldenResource, mdmContext);
}
myMdmResourceDaoSvc.upsertGoldenResource(theGoldenResource, theMdmContext.getResourceType());
if (theMatchResult == MdmMatchResultEnum.NO_MATCH) {
myMdmResourceDaoSvc.upsertGoldenResource(goldenResource, mdmContext.getResourceType());
if (matchResult == MdmMatchResultEnum.NO_MATCH) {
// We need to return no match for when a Golden Resource has already been found elsewhere
if (myMdmLinkDaoSvc
.getMdmLinksBySourcePidAndMatchResult(sourceResourceId, MdmMatchResultEnum.MATCH)
.isEmpty()) {
// Need to find a new Golden Resource to link this target to
myMdmMatchLinkSvc.updateMdmLinksForMdmSource(theSourceResource, theMdmContext);
myMdmMatchLinkSvc.updateMdmLinksForMdmSource(sourceResource, mdmContext);
}
}
return theGoldenResource;
if (myInterceptorBroadcaster.hasHooks(Pointcut.MDM_POST_UPDATE_LINK)) {
// pointcut for MDM_POST_UPDATE_LINK
MdmLinkEvent event = new MdmLinkEvent();
event.addMdmLink(myModelConverter.toJson(mdmLink));
HookParams hookParams = new HookParams();
hookParams.add(RequestDetails.class, theParams.getRequestDetails()).add(MdmLinkEvent.class, event);
myInterceptorBroadcaster.callHooks(Pointcut.MDM_POST_UPDATE_LINK, hookParams);
}
return goldenResource;
}
/**
* When updating POSSIBLE_MATCH link to a MATCH we need to validate that a MATCH to a different golden resource
* doesn't exist, because a resource mustn't be a MATCH to more than one golden resource
*/
@SuppressWarnings({"rawtypes", "unchecked"})
private void validateNoMatchPresentWhenAcceptingPossibleMatch(
IAnyResource theSourceResource,
IResourcePersistentId theGoldenResourceId,
@ -221,31 +248,45 @@ public class MdmLinkUpdaterSvcImpl implements IMdmLinkUpdaterSvc {
@Transactional
@Override
public void notDuplicateGoldenResource(
IAnyResource theGoldenResource, IAnyResource theTargetGoldenResource, MdmTransactionContext theMdmContext) {
validateNotDuplicateGoldenResourceRequest(theGoldenResource, theTargetGoldenResource);
@SuppressWarnings({"unchecked", "rawtypes"})
public void unduplicateGoldenResource(MdmUnduplicateGoldenResourceParams theParams) {
IAnyResource goldenResource = theParams.getGoldenResource();
IAnyResource targetGoldenResource = theParams.getTargetGoldenResource();
IResourcePersistentId goldenResourceId = myIdHelperService.getPidOrThrowException(theGoldenResource);
IResourcePersistentId targetId = myIdHelperService.getPidOrThrowException(theTargetGoldenResource);
validateNotDuplicateGoldenResourceRequest(goldenResource, targetGoldenResource);
IResourcePersistentId goldenResourceId = myIdHelperService.getPidOrThrowException(goldenResource);
IResourcePersistentId targetId = myIdHelperService.getPidOrThrowException(targetGoldenResource);
Optional<? extends IMdmLink> oMdmLink =
myMdmLinkDaoSvc.getLinkByGoldenResourcePidAndSourceResourcePid(goldenResourceId, targetId);
if (oMdmLink.isEmpty()) {
throw new InvalidRequestException(Msg.code(745) + "No link exists between "
+ theGoldenResource.getIdElement().toVersionless() + " and "
+ theTargetGoldenResource.getIdElement().toVersionless());
+ goldenResource.getIdElement().toVersionless() + " and "
+ targetGoldenResource.getIdElement().toVersionless());
}
IMdmLink mdmLink = oMdmLink.get();
if (!mdmLink.isPossibleDuplicate()) {
throw new InvalidRequestException(
Msg.code(746) + theGoldenResource.getIdElement().toVersionless() + " and "
+ theTargetGoldenResource.getIdElement().toVersionless()
Msg.code(746) + goldenResource.getIdElement().toVersionless() + " and "
+ targetGoldenResource.getIdElement().toVersionless()
+ " are not linked as POSSIBLE_DUPLICATE.");
}
mdmLink.setMatchResult(MdmMatchResultEnum.NO_MATCH);
mdmLink.setLinkSource(MdmLinkSourceEnum.MANUAL);
myMdmLinkDaoSvc.save(mdmLink);
IMdmLink retval = myMdmLinkDaoSvc.save(mdmLink);
if (myInterceptorBroadcaster.hasHooks(Pointcut.MDM_POST_NOT_DUPLICATE)) {
// MDM_POST_NOT_DUPLICATE hook
MdmLinkEvent event = new MdmLinkEvent();
event.addMdmLink(myModelConverter.toJson(retval));
HookParams params = new HookParams();
params.add(RequestDetails.class, theParams.getRequestDetails());
params.add(MdmLinkEvent.class, event);
myInterceptorBroadcaster.callHooks(Pointcut.MDM_POST_NOT_DUPLICATE, params);
}
}
/**

View File

@ -21,9 +21,9 @@ package ca.uhn.fhir.jpa.mdm.svc;
import ca.uhn.fhir.jpa.api.svc.IIdHelperService;
import ca.uhn.fhir.mdm.api.IMdmLink;
import ca.uhn.fhir.mdm.api.MdmLinkJson;
import ca.uhn.fhir.mdm.api.MdmLinkWithRevision;
import ca.uhn.fhir.mdm.api.MdmLinkWithRevisionJson;
import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkJson;
import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkWithRevisionJson;
import org.springframework.beans.factory.annotation.Autowired;
public class MdmModelConverterSvcImpl implements IMdmModelConverterSvc {

View File

@ -133,7 +133,7 @@ abstract public class BaseMdmR4Test extends BaseJpaR4Test {
@Autowired
SearchParamRegistryImpl mySearchParamRegistry;
@Autowired
private IInterceptorBroadcaster myInterceptorBroadcaster;
protected IInterceptorBroadcaster myInterceptorBroadcaster;
@Autowired
private DaoRegistry myDaoRegistry;
@Autowired

View File

@ -5,7 +5,7 @@ import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome;
import ca.uhn.fhir.mdm.api.MdmLinkEvent;
import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkEvent;
import ca.uhn.fhir.rest.server.TransactionLogMessages;
import ca.uhn.fhir.rest.server.messaging.ResourceOperationMessage;
import ca.uhn.test.concurrency.PointcutLatch;

View File

@ -4,8 +4,8 @@ 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.mdm.api.MdmLinkEvent;
import ca.uhn.fhir.mdm.api.MdmLinkJson;
import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkEvent;
import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkJson;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.server.messaging.ResourceOperationMessage;

View File

@ -29,7 +29,7 @@ import java.util.List;
public abstract class BaseProviderR4Test extends BaseMdmR4Test {
protected MdmProviderDstu3Plus myMdmProvider;
@Autowired
private IMdmControllerSvc myMdmControllerSvc;
protected IMdmControllerSvc myMdmControllerSvc;
@Autowired
private IMdmSubmitSvc myMdmSubmitSvc;
@Autowired
@ -56,7 +56,12 @@ public abstract class BaseProviderR4Test extends BaseMdmR4Test {
@BeforeEach
public void before() throws Exception {
myMdmProvider = new MdmProviderDstu3Plus(myFhirContext, myMdmControllerSvc, myMdmHelper, myMdmSubmitSvc, myMdmSettings);
myMdmProvider = new MdmProviderDstu3Plus(myFhirContext,
myMdmControllerSvc,
myMdmHelper,
myMdmSubmitSvc,
myInterceptorBroadcaster,
myMdmSettings);
defaultScript = myMdmSettings.getScriptText();
}

View File

@ -0,0 +1,602 @@
package ca.uhn.fhir.jpa.mdm.provider;
import ca.uhn.fhir.batch2.api.IJobCoordinator;
import ca.uhn.fhir.batch2.model.JobInstanceStartRequest;
import ca.uhn.fhir.interceptor.api.Hook;
import ca.uhn.fhir.interceptor.api.IInterceptorService;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.batch.models.Batch2JobStartResponse;
import ca.uhn.fhir.jpa.entity.MdmLink;
import ca.uhn.fhir.jpa.mdm.helper.MdmLinkHelper;
import ca.uhn.fhir.jpa.mdm.helper.testmodels.MDMState;
import ca.uhn.fhir.jpa.model.dao.JpaPid;
import ca.uhn.fhir.mdm.api.IMdmSubmitSvc;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.mdm.model.mdmevents.MdmClearEvent;
import ca.uhn.fhir.mdm.model.mdmevents.MdmHistoryEvent;
import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkEvent;
import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkJson;
import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkWithRevisionJson;
import ca.uhn.fhir.mdm.model.mdmevents.MdmMergeEvent;
import ca.uhn.fhir.mdm.model.mdmevents.MdmSubmitEvent;
import ca.uhn.fhir.mdm.provider.MdmLinkHistoryProviderDstu3Plus;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.test.concurrency.PointcutLatch;
import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.Practitioner;
import org.hl7.fhir.r4.model.StringType;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.SpyBean;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
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.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
public class MdmOperationPointcutsIT extends BaseProviderR4Test {
/**
* mdm link history can submit by
* sourceids, goldenids, or both.
* We will use this enum in the MDM_LINK_HISTORY pointcut
* test.
*/
private enum LinkHistoryParameters {
SOURCE_IDS,
GOLDEN_IDS,
BOTH
}
/**
* There are multiple way to do an $mdm-submit batch job.
* This enum tracks the various ways.
* All of them should hit our interceptor.
*/
private enum MdmSubmitEndpoint {
PATIENT_INSTANCE(false,false),
PATIENT_TYPE(true, true, false),
PRACTITIONER_INSTANCE(false, false),
PRACTITIONER_TYPE(true, true, false),
RANDOM_MDM_RESOURCE(true, false),
ALL_RESOURCES(true, false);
private final boolean[] myAsyncOptions;
private final boolean myTakesCriteria;
MdmSubmitEndpoint(boolean theHasCriteria, boolean... theOptions) {
myTakesCriteria = theHasCriteria;
myAsyncOptions = theOptions;
}
public boolean[] getAsyncOptions() {
return myAsyncOptions;
}
public boolean canTakeCriteria() {
return myTakesCriteria;
}
}
private static final Logger ourLog = LoggerFactory.getLogger(MdmOperationPointcutsIT.class);
@Autowired
private MdmLinkHelper myMdmLinkHelper;
@Autowired
private IInterceptorService myInterceptorService;
@SpyBean
private IJobCoordinator myJobCoordinator;
@SpyBean
private IMdmSubmitSvc myMdmSubmitSvc;
private MdmLinkHistoryProviderDstu3Plus myLinkHistoryProvider;
private final List<Object> myInterceptors = new ArrayList<>();
@BeforeEach
public void before() throws Exception {
super.before();
myLinkHistoryProvider = new MdmLinkHistoryProviderDstu3Plus(
myFhirContext,
myMdmControllerSvc,
myInterceptorBroadcaster
);
}
@AfterEach
public void after() throws IOException {
super.after();
myInterceptorService.unregisterInterceptors(myInterceptors);
myInterceptors.clear();
}
@Nested
class MdmProviderDstu3PlusTest {
@Test
public void mergeGoldenResources_withInterceptor_firesHook() {
// setup
AtomicBoolean called = new AtomicBoolean(false);
String inputState = """
GP1, AUTO, POSSIBLE_DUPLICATE, GP2
""";
MDMState<Patient, JpaPid> state = new MDMState<>();
state.setInputState(inputState);
// we won't use for validation, just setup
myMdmLinkHelper.setup(state);
Patient gp1 = state.getParameter("GP1");
Patient gp2 = state.getParameter("GP2");
Object intereptor = new Object() {
@Hook(Pointcut.MDM_POST_MERGE_GOLDEN_RESOURCES)
void onUpdate(RequestDetails theDetails, MdmMergeEvent theEvent) {
called.getAndSet(true);
assertEquals("Patient/" + gp1.getIdPart(), theEvent.getFromResource().getId());
assertEquals("Patient/" + gp2.getIdPart(), theEvent.getToResource().getId());
assertTrue(theEvent.getFromResource().isGoldenResource() && theEvent.getToResource().isGoldenResource());
}
};
myInterceptors.add(intereptor);
myInterceptorService.registerInterceptor(intereptor);
// test
myMdmProvider.mergeGoldenResources(
new StringType(gp1.getId()), // from
new StringType(gp2.getId()), // to
null, // merged resource
new SystemRequestDetails() // request details
);
// verify
assertTrue(called.get());
}
@Test
public void mdmUpdate_withInterceptor_firesHook() {
// setup
Patient p1 = createPatient();
Patient gp1 = createGoldenPatient();
MdmLink link = (MdmLink) myMdmLinkDaoSvc.newMdmLink();
link.setLinkSource(MdmLinkSourceEnum.AUTO);
link.setMatchResult(MdmMatchResultEnum.POSSIBLE_MATCH);
link.setCreated(new Date());
link.setGoldenResourcePersistenceId(runInTransaction(() -> myIdHelperService.getPidOrNull(RequestPartitionId.allPartitions(), gp1)));
link.setSourcePersistenceId(runInTransaction(() -> myIdHelperService.getPidOrNull(RequestPartitionId.allPartitions(), p1)));
myMdmLinkDaoSvc.save(link);
MdmMatchResultEnum toSave = MdmMatchResultEnum.MATCH;
AtomicBoolean called = new AtomicBoolean(false);
Object intereptor = new Object() {
@Hook(Pointcut.MDM_POST_UPDATE_LINK)
void onUpdate(RequestDetails theDetails, MdmLinkEvent theEvent) {
called.getAndSet(true);
assertEquals(1, theEvent.getMdmLinks().size());
MdmLinkJson link = theEvent.getMdmLinks().get(0);
assertEquals(toSave, link.getMatchResult());
assertEquals("Patient/" + p1.getIdPart(), link.getSourceId());
assertEquals("Patient/" + gp1.getIdPart(), link.getGoldenResourceId());
}
};
myInterceptors.add(intereptor);
myInterceptorService.registerInterceptor(intereptor);
// test
myMdmProvider.updateLink(
new StringType(gp1.getId()), // golden resource id
new StringType(p1.getId()), // resource id
new StringType(toSave.name()), // link type
new ServletRequestDetails() // request details
);
// verify
assertTrue(called.get());
}
@Test
public void createLink_withInterceptor_firesHook() {
// setup
AtomicBoolean called = new AtomicBoolean(false);
Patient patient = createPatient();
Patient golden = createGoldenPatient();
MdmMatchResultEnum match = MdmMatchResultEnum.MATCH;
Object intereptor = new Object() {
@Hook(Pointcut.MDM_POST_CREATE_LINK)
void onCreate(RequestDetails theDetails, MdmLinkEvent theEvent) {
called.getAndSet(true);
assertEquals(1, theEvent.getMdmLinks().size());
MdmLinkJson link = theEvent.getMdmLinks().get(0);
assertEquals(match, link.getMatchResult());
assertEquals("Patient/" + patient.getIdPart(), link.getSourceId());
assertEquals("Patient/" + golden.getIdPart(), link.getGoldenResourceId());
}
};
myInterceptors.add(intereptor);
myInterceptorService.registerInterceptor(intereptor);
// test
myMdmProvider.createLink(
new StringType(golden.getId()),
new StringType(patient.getId()),
new StringType(match.name()),
new ServletRequestDetails()
);
// validation
assertTrue(called.get());
}
@Test
public void notDuplicate_withInterceptor_firesHook() {
// setup
AtomicBoolean called = new AtomicBoolean();
String initialState = """
GP1, AUTO, POSSIBLE_DUPLICATE, GP2
""";
MDMState<Patient, JpaPid> state = new MDMState<>();
state.setInputState(initialState);
// we won't use for validation, just setup
myMdmLinkHelper.setup(state);
Patient gp1 = state.getParameter("GP1");
Patient gp2 = state.getParameter("GP2");
// interceptor
Object interceptor = new Object() {
@Hook(Pointcut.MDM_POST_NOT_DUPLICATE)
void call(RequestDetails theRequestDetails, MdmLinkEvent theEvent) {
called.getAndSet(true);
assertEquals(1, theEvent.getMdmLinks().size());
MdmLinkJson link = theEvent.getMdmLinks().get(0);
assertEquals("Patient/" + gp2.getIdPart(), link.getSourceId());
assertEquals("Patient/" + gp1.getIdPart(), link.getGoldenResourceId());
assertEquals(MdmMatchResultEnum.NO_MATCH, link.getMatchResult());
}
};
myInterceptors.add(interceptor);
myInterceptorRegistry.registerInterceptor(interceptor);
// test
myMdmProvider.notDuplicate(
new StringType(gp1.getId()),
new StringType(gp2.getId()),
new ServletRequestDetails()
);
// verify
assertTrue(called.get());
}
@ParameterizedTest
@ValueSource(strings = {
"Patient,Practitioner,Medication",
"Patient",
""
})
public void clearMdmLinks_withHook_firesInterceptor(String theResourceTypes) {
// setup
AtomicBoolean called = new AtomicBoolean();
Batch2JobStartResponse response = new Batch2JobStartResponse();
response.setInstanceId("test");
List<IPrimitiveType<String>> resourceTypes = new ArrayList<>();
if (isNotBlank(theResourceTypes)) {
String[] rts = theResourceTypes.split(",");
for (String rt : rts) {
resourceTypes.add(new StringType(rt));
}
}
// when
// we don't care to actually submit the job, so we'll mock it here
doReturn(response)
.when(myJobCoordinator).startInstance(any(RequestDetails.class), any(JobInstanceStartRequest.class));
// interceptor
Object interceptor = new Object() {
@Hook(Pointcut.MDM_CLEAR)
void call(RequestDetails theRequestDetails, MdmClearEvent theEvent) {
called.set(true);
assertNotNull(theEvent.getResourceTypes());
if (isNotBlank(theResourceTypes)) {
assertEquals(resourceTypes.size(), theEvent.getResourceTypes().size());
for (IPrimitiveType<String> resourceName : resourceTypes) {
assertTrue(theEvent.getResourceTypes()
.contains(resourceName.getValueAsString()));
}
} else {
// null or empty resource types means all
// mdm resource types
myMdmSettings.getMdmRules()
.getMdmTypes().forEach(rtype -> {
assertTrue(theEvent.getResourceTypes().contains(rtype));
});
}
}
};
myInterceptors.add(interceptor);
myInterceptorRegistry.registerInterceptor(interceptor);
// test
myMdmProvider.clearMdmLinks(
resourceTypes, // resource type filter
null, // batchsize
new ServletRequestDetails()
);
// verify
assertTrue(called.get());
}
@ParameterizedTest
@EnumSource(MdmSubmitEndpoint.class)
public void mdmSubmit_interceptor_differentPaths(MdmSubmitEndpoint theMdmSubmitEndpoint) throws InterruptedException {
// setup
AtomicBoolean called = new AtomicBoolean();
Batch2JobStartResponse res = new Batch2JobStartResponse();
res.setInstanceId("test");
List<String> urls = new ArrayList<>();
boolean[] asyncValue = new boolean[1];
PointcutLatch latch = new PointcutLatch(theMdmSubmitEndpoint.name());
// when
// we don't actually want to start batch jobs, so we'll mock it
doReturn(res)
.when(myJobCoordinator).startInstance(any(RequestDetails.class), any(JobInstanceStartRequest.class));
doReturn(1L)
.when(myMdmSubmitSvc).submitSourceResourceTypeToMdm(anyString(), any(), any(RequestDetails.class));
// use identifier because it's on almost every resource type
StringType[] criteria = theMdmSubmitEndpoint.canTakeCriteria() ?
new StringType[] { new StringType("identifier=true"), null }
: new StringType[] { null };
ServletRequestDetails request = new ServletRequestDetails();
// register an interceptor
Object interceptor = new Object() {
@Hook(Pointcut.MDM_SUBMIT)
void call(RequestDetails theRequestDetails, MdmSubmitEvent theEvent) {
called.set(true);
assertEquals(asyncValue[0], theEvent.isBatchJob());
String urlStr = String.join(", ", urls);
assertEquals(urls.size(), theEvent.getUrls().size(),
urlStr + " <-> " + String.join(", ", theEvent.getUrls()));
for (String url : urls) {
assertTrue(theEvent.getUrls().contains(url), "[" + urlStr + "] does not contain " + url + ".");
}
latch.call(1);
}
};
myInterceptors.add(interceptor);
myInterceptorRegistry.registerInterceptor(interceptor);
for (StringType criterion : criteria) {
for (boolean respondAsync : theMdmSubmitEndpoint.getAsyncOptions()) {
ourLog.info("\nRunning test for {}; async: {}", theMdmSubmitEndpoint.name(), respondAsync);
// reset
asyncValue[0] = respondAsync;
called.set(false);
urls.clear();
ServletRequestDetails req = spy(request);
doReturn(respondAsync).when(req).isPreferRespondAsync();
// test
latch.setExpectedCount(1);
switch (theMdmSubmitEndpoint) {
case PATIENT_INSTANCE:
// patient must exist to do the mdm submit
Patient p = new Patient();
p.setActive(true);
p.addName()
.setFamily("Simpson")
.addGiven("Homer");
long patientId = myPatientDao.create(p)
.getId().getIdPartAsLong();
IdType patientIdType = new IdType("Patient/" + patientId);
urls.add(patientIdType.getValue());
myMdmProvider.mdmBatchPatientInstance(
patientIdType,
req
);
break;
case PATIENT_TYPE:
if (respondAsync) {
urls.add("Patient?");
} else {
urls.add(createUrl("Patient", criterion));
}
myMdmProvider.mdmBatchPatientType(
criterion, // criteria
null, // batch size
req // request
);
break;
case PRACTITIONER_INSTANCE:
// practitioner must exist to do mdm submit
Practitioner practitioner = new Practitioner();
practitioner.setActive(true);
practitioner.addName()
.setFamily("Hibbert")
.addGiven("Julius");
long practitionerId = myPractitionerDao.create(practitioner)
.getId().getIdPartAsLong();
IdType practitionerIdType = new IdType("Practitioner/" + practitionerId);
urls.add(practitionerIdType.getValue());
myMdmProvider.mdmBatchPractitionerInstance(
practitionerIdType,
req
);
break;
case PRACTITIONER_TYPE:
if (respondAsync) {
urls.add("Practitioner?");
} else {
urls.add(createUrl("Practitioner", criterion));
}
myMdmProvider.mdmBatchPractitionerType(
criterion, // criteria
null, // batchsize
req // request
);
break;
case RANDOM_MDM_RESOURCE:
// these tests use the mdm rules in:
// resources/mdm/mdm-rules.json
// Medication is one of the allowable mdm types
String resourceType = "Medication";
urls.add(createUrl(resourceType, criterion));
myMdmProvider.mdmBatchOnAllSourceResources(
new StringType(resourceType),
criterion,
null,
req
);
break;
case ALL_RESOURCES:
myMdmSettings.getMdmRules()
.getMdmTypes().forEach(rtype -> {
urls.add(createUrl(rtype, criterion));
});
myMdmProvider.mdmBatchOnAllSourceResources(
null, // resource type (null is all)
criterion, // criteria
null, // batchsize
req
);
break;
}
// verify
latch.awaitExpected();
assertTrue(called.get());
}
}
}
}
private String createUrl(String theResourceType, StringType theCriteria) {
String url = theResourceType;
if (theCriteria != null) {
url += "?" + theCriteria.getValueAsString();
}
return url;
}
@Nested
class MdmLinkHistoryProviderDstu3PlusTest {
@ParameterizedTest
@EnumSource(LinkHistoryParameters.class)
public void historyLinks_withPointcut_firesHook(LinkHistoryParameters theParametersToSend) {
// setup
AtomicBoolean called = new AtomicBoolean();
List<IPrimitiveType<String>> sourceIds = new ArrayList<>();
List<IPrimitiveType<String>> goldenResourceIds = new ArrayList<>();
Patient p1 = createPatient();
sourceIds.add(new StringType("Patient/" + p1.getIdPart()));
Patient gp1 = createGoldenPatient();
goldenResourceIds.add(new StringType("Patient/" + gp1.getIdPart()));
MdmLink link = (MdmLink) myMdmLinkDaoSvc.newMdmLink();
link.setLinkSource(MdmLinkSourceEnum.AUTO);
link.setMatchResult(MdmMatchResultEnum.POSSIBLE_MATCH);
link.setCreated(new Date());
link.setGoldenResourcePersistenceId(runInTransaction(() -> myIdHelperService.getPidOrNull(RequestPartitionId.allPartitions(), gp1)));
link.setSourcePersistenceId(runInTransaction(() -> myIdHelperService.getPidOrNull(RequestPartitionId.allPartitions(), p1)));
myMdmLinkDaoSvc.save(link);
// save a change
link.setMatchResult(MdmMatchResultEnum.MATCH);
myMdmLinkDaoSvc.save(link);
// interceptor
Object interceptor = new Object() {
@Hook(Pointcut.MDM_POST_LINK_HISTORY)
void onHistory(RequestDetails theRequestDetails, MdmHistoryEvent theEvent) {
called.getAndSet(true);
List<MdmLinkWithRevisionJson> history = theEvent.getMdmLinkRevisions();
List<String> gids = theEvent.getGoldenResourceIds();
List<String> sids = theEvent.getSourceIds();
if (theParametersToSend == LinkHistoryParameters.SOURCE_IDS) {
assertTrue(gids.isEmpty());
} else if (theParametersToSend == LinkHistoryParameters.GOLDEN_IDS) {
assertTrue(sids.isEmpty());
} else {
assertFalse(sids.isEmpty() && gids.isEmpty());
}
assertFalse(history.isEmpty());
assertEquals(2, history.size());
}
};
myInterceptors.add(interceptor);
myInterceptorRegistry.registerInterceptor(interceptor);
// test
List<IPrimitiveType<String>> sourceIdsToSend = theParametersToSend != LinkHistoryParameters.GOLDEN_IDS ? sourceIds : new ArrayList<>();
List<IPrimitiveType<String>> goldenIdsToSend = theParametersToSend != LinkHistoryParameters.SOURCE_IDS ? goldenResourceIds : new ArrayList<>();
IBaseParameters retval = myLinkHistoryProvider.historyLinks(
goldenIdsToSend,
sourceIdsToSend,
new ServletRequestDetails()
);
// verify
assertTrue(called.get());
assertFalse(retval.isEmpty());
}
}
}

View File

@ -9,7 +9,7 @@ import ca.uhn.fhir.jpa.mdm.provider.BaseLinkR4Test;
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc;
import ca.uhn.fhir.jpa.test.Batch2JobHelper;
import ca.uhn.fhir.mdm.api.IMdmControllerSvc;
import ca.uhn.fhir.mdm.api.MdmLinkJson;
import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkJson;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.mdm.api.MdmQuerySearchParameters;

View File

@ -13,7 +13,9 @@ import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchOutcome;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.mdm.interceptor.IMdmStorageInterceptor;
import ca.uhn.fhir.mdm.model.MdmMergeGoldenResourcesParams;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.rest.server.TransactionLogMessages;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import org.hl7.fhir.instance.model.api.IBaseResource;
@ -107,11 +109,15 @@ public class MdmGoldenResourceMergerSvcTest extends BaseMdmR4Test {
assertEquals(0, redirectLinkCount());
Patient from = theFlipToAndFromGoldenResources ? myToGoldenPatient : myFromGoldenPatient;
Patient to = theFlipToAndFromGoldenResources ? myFromGoldenPatient : myToGoldenPatient;
MdmMergeGoldenResourcesParams params = new MdmMergeGoldenResourcesParams();
params.setFromGoldenResource(from);
params.setToGoldenResource(to);
params.setMdmTransactionContext(createMdmContext());
params.setRequestDetails(new SystemRequestDetails());
Patient retval = (Patient) myGoldenResourceMergerSvc.mergeGoldenResources(
from,
null,
to,
createMdmContext()
params
);
assertEquals(1, redirectLinkCount());
return retval;
@ -214,8 +220,15 @@ public class MdmGoldenResourceMergerSvcTest extends BaseMdmR4Test {
MdmTransactionContext ctx = createMdmContext();
ctx.setRestOperation(MdmTransactionContext.OperationType.MANUAL_MERGE_GOLDEN_RESOURCES);
MdmMergeGoldenResourcesParams params = new MdmMergeGoldenResourcesParams();
params.setFromGoldenResource(myFromGoldenPatient);
params.setManuallyMergedResource(manuallyMergedPatient);
params.setToGoldenResource(myToGoldenPatient);
params.setMdmTransactionContext(ctx);
params.setRequestDetails(new SystemRequestDetails());
Patient mergedSourcePatient = (Patient) myGoldenResourceMergerSvc
.mergeGoldenResources(myFromGoldenPatient, manuallyMergedPatient, myToGoldenPatient, ctx);
.mergeGoldenResources(params);
HumanName returnedName = mergedSourcePatient.getNameFirstRep();
assertEquals("TestGiven TestFamily", returnedName.getNameAsSingleString());
@ -243,11 +256,13 @@ public class MdmGoldenResourceMergerSvcTest extends BaseMdmR4Test {
}
private Patient mergeGoldenResources(Patient theFrom, Patient theTo) {
MdmMergeGoldenResourcesParams params = new MdmMergeGoldenResourcesParams();
params.setFromGoldenResource(theFrom);
params.setToGoldenResource(theTo);
params.setMdmTransactionContext(createMdmContext());
params.setRequestDetails(new SystemRequestDetails());
Patient retval = (Patient) myGoldenResourceMergerSvc.mergeGoldenResources(
theFrom,
null,
theTo,
createMdmContext()
params
);
assertEquals(1, redirectLinkCount());
return retval;

View File

@ -1,18 +1,21 @@
package ca.uhn.fhir.jpa.mdm.svc;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.jpa.api.svc.IIdHelperService;
import ca.uhn.fhir.jpa.entity.MdmLink;
import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc;
import ca.uhn.fhir.mdm.util.MdmPartitionHelper;
import ca.uhn.fhir.jpa.model.dao.JpaPid;
import ca.uhn.fhir.mdm.api.IMdmLink;
import ca.uhn.fhir.mdm.api.IMdmSettings;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.mdm.model.MdmCreateOrUpdateParams;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.mdm.util.MdmPartitionHelper;
import ca.uhn.fhir.mdm.util.MdmResourceUtil;
import ca.uhn.fhir.mdm.util.MessageHelper;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.rest.api.server.storage.BaseResourcePersistentId;
import org.hl7.fhir.r4.model.Patient;
import org.junit.jupiter.api.BeforeEach;
@ -36,8 +39,10 @@ class MdmLinkCreateSvcImplTest {
@SuppressWarnings("unused")
@Spy
FhirContext myFhirContext = FhirContext.forR4();
@SuppressWarnings("rawtypes")
@Mock
IIdHelperService myIdHelperService;
@SuppressWarnings("rawtypes")
@Mock
MdmLinkDaoSvc myMdmLinkDaoSvc;
@Mock
@ -47,9 +52,16 @@ class MdmLinkCreateSvcImplTest {
@Mock
MdmPartitionHelper myMdmPartitionHelper;
@Mock
IInterceptorBroadcaster myInterceptorBroadcaster;
@Mock
IMdmModelConverterSvc myIMdmModelConverterSvc;
@InjectMocks
MdmLinkCreateSvcImpl myMdmLinkCreateSvc = new MdmLinkCreateSvcImpl();
@SuppressWarnings({"unchecked", "rawtypes"})
@Test
public void testCreateLink() {
ArgumentCaptor<IMdmLink> mdmLinkCaptor = ArgumentCaptor.forClass(IMdmLink.class);
@ -63,7 +75,13 @@ class MdmLinkCreateSvcImplTest {
Patient sourcePatient = new Patient();
MdmTransactionContext ctx = new MdmTransactionContext();
myMdmLinkCreateSvc.createLink(goldenPatient, sourcePatient, MdmMatchResultEnum.MATCH, ctx);
MdmCreateOrUpdateParams params = new MdmCreateOrUpdateParams();
params.setGoldenResource(goldenPatient);
params.setSourceResource(sourcePatient);
params.setMatchResult(MdmMatchResultEnum.MATCH);
params.setMdmContext(ctx);
params.setRequestDetails(new SystemRequestDetails());
myMdmLinkCreateSvc.createLink(params);
IMdmLink mdmLink = mdmLinkCaptor.getValue();
@ -72,6 +90,7 @@ class MdmLinkCreateSvcImplTest {
}
@SuppressWarnings({"unchecked"})
@BeforeEach
public void setup() {
JpaPid goldenId = JpaPid.fromId(1L);

View File

@ -6,8 +6,8 @@ import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
import ca.uhn.fhir.mdm.api.IMdmLinkQuerySvc;
import ca.uhn.fhir.mdm.api.MdmHistorySearchParameters;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmLinkWithRevisionJson;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkWithRevisionJson;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import org.hl7.fhir.r4.model.Patient;
import org.junit.jupiter.api.Test;

View File

@ -3,14 +3,13 @@ package ca.uhn.fhir.jpa.mdm.svc;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome;
import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
import ca.uhn.fhir.jpa.mdm.helper.MdmLinkHelper;
import ca.uhn.fhir.mdm.api.IMdmLink;
import ca.uhn.fhir.mdm.api.IMdmLinkUpdaterSvc;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchOutcome;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.mdm.model.MdmCreateOrUpdateParams;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.mdm.rules.json.MdmRulesJson;
import ca.uhn.fhir.mdm.util.MessageHelper;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
@ -18,10 +17,8 @@ import org.hl7.fhir.r4.model.Patient;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ResourceUtils;
import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
@ -63,12 +60,21 @@ class MdmLinkUpdaterSvcImplIT extends BaseMdmR4Test {
Patient patientC = createPatientFromJsonInputFileWithPossibleMatches( List.of(goldenA, goldenB) );
MdmTransactionContext mdmTransactionContext = getPatientUpdateLinkContext();
// update POSSIBLE_MATCH Patient C -> GR A to MATCH (should work OK)
myMdmLinkUpdaterSvc.updateLink(goldenA, patientC, MdmMatchResultEnum.MATCH, mdmTransactionContext);
MdmCreateOrUpdateParams params = new MdmCreateOrUpdateParams();
params.setMdmContext(mdmTransactionContext);
params.setRequestDetails(new SystemRequestDetails());
params.setGoldenResource(goldenA);
params.setSourceResource(patientC);
params.setMatchResult(MdmMatchResultEnum.MATCH);
// update POSSIBLE_MATCH Patient C -> GR A to MATCH (should work OK)
myMdmLinkUpdaterSvc.updateLink(params);
params.setGoldenResource(goldenB);
// update POSSIBLE_MATCH Patient C -> GR B to MATCH (should throw exception)
InvalidRequestException thrown = assertThrows(InvalidRequestException.class,
() -> myMdmLinkUpdaterSvc.updateLink(goldenB, patientC, MdmMatchResultEnum.MATCH, mdmTransactionContext));
() -> myMdmLinkUpdaterSvc.updateLink(params));
String expectedExceptionMessage = Msg.code(2218) + myMessageHelper.getMessageForAlreadyAcceptedLink(goldenA, patientC);
assertEquals(expectedExceptionMessage, thrown.getMessage());
@ -88,11 +94,21 @@ class MdmLinkUpdaterSvcImplIT extends BaseMdmR4Test {
Patient patientC = createPatientFromJsonInputFileWithPossibleMatches( List.of(goldenA, goldenB) );
MdmTransactionContext mdmTransactionContext = getPatientUpdateLinkContext();
MdmCreateOrUpdateParams params = new MdmCreateOrUpdateParams();
params.setGoldenResource(goldenA);
params.setSourceResource(patientC);
params.setMdmContext(mdmTransactionContext);
params.setMatchResult(MdmMatchResultEnum.MATCH);
params.setRequestDetails(new SystemRequestDetails());
// update POSSIBLE_MATCH Patient C -> GR A to MATCH (should work OK)
myMdmLinkUpdaterSvc.updateLink(goldenA, patientC, MdmMatchResultEnum.MATCH, mdmTransactionContext);
myMdmLinkUpdaterSvc.updateLink(params);
params.setMatchResult(MdmMatchResultEnum.NO_MATCH);
params.setGoldenResource(goldenB);
// update POSSIBLE_MATCH Patient C -> GR B to NO_MATCH (should work OK)
myMdmLinkUpdaterSvc.updateLink(goldenB, patientC, MdmMatchResultEnum.NO_MATCH, mdmTransactionContext);
myMdmLinkUpdaterSvc.updateLink(params);
}
private Patient createPatientFromJsonInputFileWithPossibleMatches(List<Patient> theGoldens) throws Exception {

View File

@ -5,7 +5,9 @@ import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
import ca.uhn.fhir.mdm.api.IMdmLinkUpdaterSvc;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchOutcome;
import ca.uhn.fhir.mdm.model.MdmCreateOrUpdateParams;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import org.hl7.fhir.r4.model.Patient;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
@ -41,7 +43,13 @@ class MdmLinkUpdaterSvcImplTest extends BaseMdmR4Test {
MdmTransactionContext mdmCtx = buildUpdateLinkMdmTransactionContext();
myMdmLinkUpdaterSvc.updateLink(originalJaneGolden, jane, NO_MATCH, mdmCtx);
MdmCreateOrUpdateParams params = new MdmCreateOrUpdateParams();
params.setMdmContext(mdmCtx);
params.setGoldenResource(originalJaneGolden);
params.setSourceResource(jane);
params.setMatchResult(NO_MATCH);
params.setRequestDetails(new SystemRequestDetails());
myMdmLinkUpdaterSvc.updateLink(params);
Patient newJaneGolden = getGoldenResourceFromTargetResource(jane);
assertNotEquals(newJaneGolden.getId(), originalJaneGolden.getId());
@ -83,7 +91,13 @@ class MdmLinkUpdaterSvcImplTest extends BaseMdmR4Test {
myMdmSettings.getMdmRules().setVersion("2");
myMdmLinkUpdaterSvc.updateLink(goldenPatient, patient1, MATCH, mdmCtx);
MdmCreateOrUpdateParams params = new MdmCreateOrUpdateParams();
params.setRequestDetails(new SystemRequestDetails());
params.setGoldenResource(goldenPatient);
params.setSourceResource(patient1);
params.setMatchResult(MATCH);
params.setMdmContext(mdmCtx);
myMdmLinkUpdaterSvc.updateLink(params);
final List<MdmLink> targets = myMdmLinkDaoSvc.findMdmLinksByGoldenResource(goldenPatient);
assertFalse(targets.isEmpty());

View File

@ -20,11 +20,13 @@ import ca.uhn.fhir.mdm.blocklist.json.BlockListJson;
import ca.uhn.fhir.mdm.blocklist.json.BlockListRuleJson;
import ca.uhn.fhir.mdm.blocklist.svc.IBlockListRuleProvider;
import ca.uhn.fhir.mdm.model.CanonicalEID;
import ca.uhn.fhir.mdm.model.MdmCreateOrUpdateParams;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.mdm.util.EIDHelper;
import ca.uhn.fhir.mdm.util.GoldenResourceHelper;
import ca.uhn.fhir.mdm.util.MdmResourceUtil;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId;
import ca.uhn.fhir.rest.param.TokenParam;
import org.hl7.fhir.instance.model.api.IAnyResource;
@ -661,7 +663,13 @@ public class MdmMatchLinkSvcTest {
Patient originalJaneGolden = getGoldenResourceFromTargetResource(jane);
MdmTransactionContext mdmCtx = buildUpdateLinkMdmTransactionContext();
myMdmLinkUpdaterSvc.updateLink(originalPaulGolden, paul, NO_MATCH, mdmCtx);
MdmCreateOrUpdateParams params = new MdmCreateOrUpdateParams();
params.setGoldenResource(originalPaulGolden);
params.setSourceResource(paul);
params.setMatchResult(NO_MATCH);
params.setRequestDetails(new SystemRequestDetails());
params.setMdmContext(mdmCtx);
myMdmLinkUpdaterSvc.updateLink(params);
clearExternalEIDs(paul);
addExternalEID(paul, EID_2);

View File

@ -4,8 +4,8 @@ import ca.uhn.fhir.jpa.entity.MdmLink;
import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
import ca.uhn.fhir.jpa.model.entity.EnversRevision;
import ca.uhn.fhir.mdm.api.IMdmLink;
import ca.uhn.fhir.mdm.api.MdmLinkJson;
import ca.uhn.fhir.mdm.api.MdmLinkWithRevisionJson;
import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkJson;
import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkWithRevisionJson;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmLinkWithRevision;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -7,7 +7,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -7,7 +7,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -19,7 +19,7 @@
*/
package ca.uhn.fhir.mdm.api;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.mdm.model.MdmMergeGoldenResourcesParams;
import org.hl7.fhir.instance.model.api.IAnyResource;
public interface IGoldenResourceMergerSvc {
@ -32,9 +32,5 @@ public interface IGoldenResourceMergerSvc {
* @param theToGoldenResource the golden resource we are merging to
* @return updated theToGoldenResource with the merged fields and links.
*/
IAnyResource mergeGoldenResources(
IAnyResource theFromGoldenResource,
IAnyResource theManuallyMergedResource,
IAnyResource theToGoldenResource,
MdmTransactionContext theMdmTransactionContext);
IAnyResource mergeGoldenResources(MdmMergeGoldenResourcesParams theParams);
}

View File

@ -20,7 +20,12 @@
package ca.uhn.fhir.mdm.api;
import ca.uhn.fhir.mdm.api.paging.MdmPageRequest;
import ca.uhn.fhir.mdm.model.MdmCreateOrUpdateParams;
import ca.uhn.fhir.mdm.model.MdmMergeGoldenResourcesParams;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.mdm.model.MdmUnduplicateGoldenResourceParams;
import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkJson;
import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkWithRevisionJson;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import org.hl7.fhir.instance.model.api.IAnyResource;
@ -82,29 +87,62 @@ public interface IMdmControllerSvc {
RequestDetails theRequestDetails,
String theRequestResourceType);
@Deprecated(forRemoval = true, since = "6.8.0")
void notDuplicateGoldenResource(
String theGoldenResourceId,
String theTargetGoldenResourceId,
MdmTransactionContext theMdmTransactionContext);
default void unduplicateGoldenResource(MdmUnduplicateGoldenResourceParams theParams) {
notDuplicateGoldenResource(
theParams.getGoldenResourceId(), theParams.getTargetGoldenResourceId(), theParams.getMdmContext());
}
@Deprecated(forRemoval = true, since = "6.8.0")
IAnyResource mergeGoldenResources(
String theFromGoldenResourceId,
String theToGoldenResourceId,
IAnyResource theManuallyMergedGoldenResource,
MdmTransactionContext theMdmTransactionContext);
default IAnyResource mergeGoldenResources(MdmMergeGoldenResourcesParams theParams) {
return mergeGoldenResources(
theParams.getFromGoldenResourceId(),
theParams.getToGoldenResourceId(),
theParams.getManuallyMergedResource(),
theParams.getMdmTransactionContext());
}
@Deprecated(forRemoval = true, since = "6.8.0")
IAnyResource updateLink(
String theGoldenResourceId,
String theSourceResourceId,
String theMatchResult,
MdmTransactionContext theMdmTransactionContext);
default IAnyResource updateLink(MdmCreateOrUpdateParams theParams) {
String matchResult = theParams.getMatchResult() == null
? null
: theParams.getMatchResult().name();
return updateLink(
theParams.getGoldenResourceId(), theParams.getResourceId(), matchResult, theParams.getMdmContext());
}
@Deprecated(forRemoval = true, since = "6.8.0")
IAnyResource createLink(
String theGoldenResourceId,
String theSourceResourceId,
@Nullable String theMatchResult,
MdmTransactionContext theMdmTransactionContext);
default IAnyResource createLink(MdmCreateOrUpdateParams theParams) {
String matchResult = theParams.getMatchResult() == null
? null
: theParams.getMatchResult().name();
return createLink(
theParams.getGoldenResourceId(), theParams.getResourceId(), matchResult, theParams.getMdmContext());
}
IBaseParameters submitMdmClearJob(
List<String> theResourceNames,
IPrimitiveType<BigDecimal> theBatchSize,

View File

@ -19,13 +19,9 @@
*/
package ca.uhn.fhir.mdm.api;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.mdm.model.MdmCreateOrUpdateParams;
import org.hl7.fhir.instance.model.api.IAnyResource;
public interface IMdmLinkCreateSvc {
IAnyResource createLink(
IAnyResource theGoldenResource,
IAnyResource theSourceResource,
MdmMatchResultEnum theMatchResult,
MdmTransactionContext theMdmContext);
IAnyResource createLink(MdmCreateOrUpdateParams theParams);
}

View File

@ -21,6 +21,8 @@ package ca.uhn.fhir.mdm.api;
import ca.uhn.fhir.mdm.api.paging.MdmPageRequest;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkJson;
import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkWithRevisionJson;
import org.hl7.fhir.instance.model.api.IIdType;
import org.springframework.data.domain.Page;

View File

@ -19,16 +19,12 @@
*/
package ca.uhn.fhir.mdm.api;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.mdm.model.MdmCreateOrUpdateParams;
import ca.uhn.fhir.mdm.model.MdmUnduplicateGoldenResourceParams;
import org.hl7.fhir.instance.model.api.IAnyResource;
public interface IMdmLinkUpdaterSvc {
IAnyResource updateLink(
IAnyResource theGoldenResource,
IAnyResource theSourceResource,
MdmMatchResultEnum theMatchResult,
MdmTransactionContext theMdmContext);
IAnyResource updateLink(MdmCreateOrUpdateParams theParams);
void notDuplicateGoldenResource(
IAnyResource theGoldenResource, IAnyResource theTargetGoldenResource, MdmTransactionContext theMdmContext);
void unduplicateGoldenResource(MdmUnduplicateGoldenResourceParams theParams);
}

View File

@ -55,6 +55,7 @@ public interface IMdmSubmitSvc {
* @param theCriteria The FHIR search critieria for filtering the resources to be submitted for MDM processing.
* @return the number of resources submitted for MDM processing.
*/
@Deprecated(forRemoval = true, since = "6.8.0")
long submitPractitionerTypeToMdm(String theCriteria, RequestDetails theRequestDetails);
/**
@ -63,6 +64,7 @@ public interface IMdmSubmitSvc {
* @param theCriteria The FHIR search critieria for filtering the resources to be submitted for MDM processing.
* @return the number of resources submitted for MDM processing.
*/
@Deprecated(forRemoval = true, since = "6.8.0")
long submitPatientTypeToMdm(String theCriteria, RequestDetails theRequestDetails);
/**

View File

@ -19,7 +19,7 @@
*/
package ca.uhn.fhir.mdm.api.paging;
import ca.uhn.fhir.mdm.api.MdmLinkJson;
import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkJson;
import ca.uhn.fhir.rest.server.RestfulServerUtils;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import org.springframework.data.domain.Page;

View File

@ -0,0 +1,96 @@
package ca.uhn.fhir.mdm.model;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.hl7.fhir.instance.model.api.IAnyResource;
public class MdmCreateOrUpdateParams {
/**
* The golden resource id
*/
private String myGoldenResourceId;
/**
* The golden resource to update for the link.
*/
private IAnyResource myGoldenResource;
/**
* The source id.
*/
private String myResourceId;
/**
* The source resource linked to the golden resource
*/
private IAnyResource mySourceResource;
/**
* Match result to update the link to.
*/
private MdmMatchResultEnum myMatchResult;
/**
* The context.
*/
private MdmTransactionContext myMdmContext;
/**
* The request details.
*/
private RequestDetails myRequestDetails;
public String getGoldenResourceId() {
return myGoldenResourceId;
}
public void setGoldenResourceId(String theGoldenResourceId) {
myGoldenResourceId = theGoldenResourceId;
}
public String getResourceId() {
return myResourceId;
}
public void setResourceId(String theResourceId) {
myResourceId = theResourceId;
}
public IAnyResource getGoldenResource() {
return myGoldenResource;
}
public void setGoldenResource(IAnyResource theGoldenResource) {
myGoldenResource = theGoldenResource;
}
public IAnyResource getSourceResource() {
return mySourceResource;
}
public void setSourceResource(IAnyResource theSourceResource) {
mySourceResource = theSourceResource;
}
public MdmMatchResultEnum getMatchResult() {
return myMatchResult;
}
public void setMatchResult(MdmMatchResultEnum theMatchResult) {
myMatchResult = theMatchResult;
}
public MdmTransactionContext getMdmContext() {
return myMdmContext;
}
public void setMdmContext(MdmTransactionContext theMdmContext) {
myMdmContext = theMdmContext;
}
public RequestDetails getRequestDetails() {
return myRequestDetails;
}
public void setRequestDetails(RequestDetails theRequestDetails) {
myRequestDetails = theRequestDetails;
}
}

View File

@ -0,0 +1,74 @@
package ca.uhn.fhir.mdm.model;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.hl7.fhir.instance.model.api.IAnyResource;
public class MdmMergeGoldenResourcesParams {
private String myFromGoldenResourceId;
private IAnyResource myFromGoldenResource;
private String myToGoldenResourceId;
private IAnyResource myManuallyMergedResource;
private IAnyResource myToGoldenResource;
private MdmTransactionContext myMdmTransactionContext;
private RequestDetails myRequestDetails;
public String getFromGoldenResourceId() {
return myFromGoldenResourceId;
}
public void setFromGoldenResourceId(String theTheFromGoldenResourceId) {
myFromGoldenResourceId = theTheFromGoldenResourceId;
}
public String getToGoldenResourceId() {
return myToGoldenResourceId;
}
public void setToGoldenResourceId(String theTheToGoldenResourceId) {
myToGoldenResourceId = theTheToGoldenResourceId;
}
public IAnyResource getFromGoldenResource() {
return myFromGoldenResource;
}
public void setFromGoldenResource(IAnyResource theFromGoldenResource) {
myFromGoldenResource = theFromGoldenResource;
}
public IAnyResource getManuallyMergedResource() {
return myManuallyMergedResource;
}
public void setManuallyMergedResource(IAnyResource theManuallyMergedResource) {
myManuallyMergedResource = theManuallyMergedResource;
}
public IAnyResource getToGoldenResource() {
return myToGoldenResource;
}
public void setToGoldenResource(IAnyResource theToGoldenResource) {
myToGoldenResource = theToGoldenResource;
}
public MdmTransactionContext getMdmTransactionContext() {
return myMdmTransactionContext;
}
public void setMdmTransactionContext(MdmTransactionContext theMdmTransactionContext) {
myMdmTransactionContext = theMdmTransactionContext;
}
public RequestDetails getRequestDetails() {
return myRequestDetails;
}
public void setRequestDetails(RequestDetails theRequestDetails) {
myRequestDetails = theRequestDetails;
}
}

View File

@ -0,0 +1,67 @@
package ca.uhn.fhir.mdm.model;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.hl7.fhir.instance.model.api.IAnyResource;
public class MdmUnduplicateGoldenResourceParams {
private String myGoldenResourceId;
private IAnyResource myGoldenResource;
private String myTargetGoldenResourceId;
private IAnyResource myTargetGoldenResource;
private MdmTransactionContext myMdmContext;
private RequestDetails myRequestDetails;
public IAnyResource getGoldenResource() {
return myGoldenResource;
}
public void setGoldenResource(IAnyResource theGoldenResource) {
myGoldenResource = theGoldenResource;
}
public IAnyResource getTargetGoldenResource() {
return myTargetGoldenResource;
}
public void setTargetGoldenResource(IAnyResource theTargetGoldenResource) {
myTargetGoldenResource = theTargetGoldenResource;
}
public MdmTransactionContext getMdmContext() {
return myMdmContext;
}
public void setMdmContext(MdmTransactionContext theMdmContext) {
myMdmContext = theMdmContext;
}
public RequestDetails getRequestDetails() {
return myRequestDetails;
}
public void setRequestDetails(RequestDetails theRequestDetails) {
myRequestDetails = theRequestDetails;
}
public String getGoldenResourceId() {
return myGoldenResourceId;
}
public void setGoldenResourceId(String theGoldenResourceId) {
myGoldenResourceId = theGoldenResourceId;
}
public String getTargetGoldenResourceId() {
return myTargetGoldenResourceId;
}
public void setTargetGoldenResourceId(String theTargetGoldenResourceId) {
myTargetGoldenResourceId = theTargetGoldenResourceId;
}
}

View File

@ -0,0 +1,40 @@
package ca.uhn.fhir.mdm.model.mdmevents;
import ca.uhn.fhir.model.api.IModelJson;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.ArrayList;
import java.util.List;
public class MdmClearEvent implements IModelJson {
@JsonProperty("resourceTypes")
private List<String> myResourceTypes;
/**
* True if this submit was done asynchronously
* (ie, was submitted as a batch job).
* False if submitted directly to mdm.
*/
@JsonProperty("batchSize")
private Long myBatchSize;
public Long getBatchSize() {
return myBatchSize;
}
public void setBatchSize(Long theBatchSize) {
myBatchSize = theBatchSize;
}
public List<String> getResourceTypes() {
if (myResourceTypes == null) {
myResourceTypes = new ArrayList<>();
}
return myResourceTypes;
}
public void setResourceTypes(List<String> theResourceTypes) {
myResourceTypes = theResourceTypes;
}
}

View File

@ -0,0 +1,48 @@
package ca.uhn.fhir.mdm.model.mdmevents;
import ca.uhn.fhir.model.api.IModelJson;
import com.fasterxml.jackson.annotation.JsonProperty;
public class MdmEventResource implements IModelJson {
/**
* The id of the resource that's part of this event.
*/
@JsonProperty("id")
private String myId;
/**
* The resource type.
*/
@JsonProperty("resourceType")
private String myResourceType;
/**
* True if this is a golden resource; false otherwise.
*/
@JsonProperty("isGoldenResource")
private boolean myIsGoldenResource;
public String getId() {
return myId;
}
public void setId(String theId) {
myId = theId;
}
public String getResourceType() {
return myResourceType;
}
public void setResourceType(String theResourceType) {
myResourceType = theResourceType;
}
public boolean isGoldenResource() {
return myIsGoldenResource;
}
public void setIsGoldenResource(boolean theGoldenResource) {
myIsGoldenResource = theGoldenResource;
}
}

View File

@ -0,0 +1,63 @@
package ca.uhn.fhir.mdm.model.mdmevents;
import ca.uhn.fhir.model.api.IModelJson;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.ArrayList;
import java.util.List;
public class MdmHistoryEvent implements IModelJson {
/**
* List of golden resource ids queried.
* Can be empty.
*/
@JsonProperty("goldenResourceIds")
private List<String> myGoldenResourceIds;
/**
* List of source ids queried.
* Can be empty.
*/
@JsonProperty("sourceIds")
private List<String> mySourceIds;
/**
* The associated link revisions returned from the search.
*/
@JsonProperty("mdmLinkRevisions")
private List<MdmLinkWithRevisionJson> myMdmLinkRevisions;
public List<String> getGoldenResourceIds() {
if (myGoldenResourceIds == null) {
myGoldenResourceIds = new ArrayList<>();
}
return myGoldenResourceIds;
}
public void setGoldenResourceIds(List<String> theGoldenResourceIds) {
myGoldenResourceIds = theGoldenResourceIds;
}
public List<String> getSourceIds() {
if (mySourceIds == null) {
mySourceIds = new ArrayList<>();
}
return mySourceIds;
}
public void setSourceIds(List<String> theSourceIds) {
mySourceIds = theSourceIds;
}
public List<MdmLinkWithRevisionJson> getMdmLinkRevisions() {
if (myMdmLinkRevisions == null) {
myMdmLinkRevisions = new ArrayList<>();
}
return myMdmLinkRevisions;
}
public void setMdmLinkRevisions(List<MdmLinkWithRevisionJson> theMdmLinkRevisions) {
myMdmLinkRevisions = theMdmLinkRevisions;
}
}

View File

@ -17,7 +17,7 @@
* limitations under the License.
* #L%
*/
package ca.uhn.fhir.mdm.api;
package ca.uhn.fhir.mdm.model.mdmevents;
import ca.uhn.fhir.model.api.IModelJson;

View File

@ -17,8 +17,10 @@
* limitations under the License.
* #L%
*/
package ca.uhn.fhir.mdm.api;
package ca.uhn.fhir.mdm.model.mdmevents;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.model.api.IModelJson;
import com.fasterxml.jackson.annotation.JsonProperty;

View File

@ -0,0 +1,26 @@
package ca.uhn.fhir.mdm.model.mdmevents;
import ca.uhn.fhir.model.api.IModelJson;
public class MdmMergeEvent implements IModelJson {
private MdmEventResource myFromResource;
private MdmEventResource myToResource;
public MdmEventResource getFromResource() {
return myFromResource;
}
public void setFromResource(MdmEventResource theFromResource) {
myFromResource = theFromResource;
}
public MdmEventResource getToResource() {
return myToResource;
}
public void setToResource(MdmEventResource theToResource) {
myToResource = theToResource;
}
}

View File

@ -0,0 +1,62 @@
package ca.uhn.fhir.mdm.model.mdmevents;
import ca.uhn.fhir.model.api.IModelJson;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.ArrayList;
import java.util.List;
public class MdmSubmitEvent implements IModelJson {
/**
* Batch size; only applicable if this is a batch job
*/
@JsonProperty("batchSize")
private Long myBatchSize;
/**
* The search/resource urls used in this job
*/
@JsonProperty("urls")
private List<String> myUrls;
/**
* True if this submit was done asynchronously
* (ie, was submitted as a batch job).
* False if submitted directly to mdm.
*/
@JsonProperty("batch_job")
private boolean myIsBatchJob;
public Long getBatchSize() {
return myBatchSize;
}
public void setBatchSize(Long theBatchSize) {
myBatchSize = theBatchSize;
}
public List<String> getUrls() {
if (myUrls == null) {
myUrls = new ArrayList<>();
}
return myUrls;
}
public void setUrls(List<String> theUrls) {
myUrls = theUrls;
}
public MdmSubmitEvent addUrl(String theUrl) {
getUrls().add(theUrl);
return this;
}
public boolean isBatchJob() {
return myIsBatchJob;
}
public void setBatchJob(boolean theBatchJob) {
myIsBatchJob = theBatchJob;
}
}

View File

@ -21,13 +21,13 @@ package ca.uhn.fhir.mdm.provider;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.mdm.api.MdmLinkJson;
import ca.uhn.fhir.mdm.api.MdmLinkWithRevisionJson;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.mdm.api.paging.MdmPageLinkBuilder;
import ca.uhn.fhir.mdm.api.paging.MdmPageLinkTuple;
import ca.uhn.fhir.mdm.api.paging.MdmPageRequest;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkJson;
import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkWithRevisionJson;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.TransactionLogMessages;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;

View File

@ -21,11 +21,16 @@ package ca.uhn.fhir.mdm.provider;
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.mdm.api.IMdmControllerSvc;
import ca.uhn.fhir.mdm.api.MdmHistorySearchParameters;
import ca.uhn.fhir.mdm.api.MdmLinkWithRevisionJson;
import ca.uhn.fhir.mdm.model.mdmevents.MdmHistoryEvent;
import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkWithRevisionJson;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.provider.ProviderConstants;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.util.ParametersUtil;
@ -34,7 +39,9 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.slf4j.Logger;
import java.util.List;
import java.util.stream.Collectors;
import static org.apache.commons.collections4.CollectionUtils.isNotEmpty;
import static org.slf4j.LoggerFactory.getLogger;
public class MdmLinkHistoryProviderDstu3Plus extends BaseMdmProvider {
@ -42,9 +49,15 @@ public class MdmLinkHistoryProviderDstu3Plus extends BaseMdmProvider {
private final IMdmControllerSvc myMdmControllerSvc;
public MdmLinkHistoryProviderDstu3Plus(FhirContext theFhirContext, IMdmControllerSvc theMdmControllerSvc) {
private final IInterceptorBroadcaster myInterceptorBroadcaster;
public MdmLinkHistoryProviderDstu3Plus(
FhirContext theFhirContext,
IMdmControllerSvc theMdmControllerSvc,
IInterceptorBroadcaster theIInterceptorBroadcaster) {
super(theFhirContext);
myMdmControllerSvc = theMdmControllerSvc;
myInterceptorBroadcaster = theIInterceptorBroadcaster;
}
@Operation(name = ProviderConstants.MDM_LINK_HISTORY, idempotent = true)
@ -79,6 +92,27 @@ public class MdmLinkHistoryProviderDstu3Plus extends BaseMdmProvider {
parametersFromMdmLinkRevisions(retVal, mdmLinkRevisionsFromSvc);
if (myInterceptorBroadcaster.hasHooks(Pointcut.MDM_POST_LINK_HISTORY)) {
// MDM_POST_LINK_HISTORY hook
MdmHistoryEvent historyEvent = new MdmHistoryEvent();
historyEvent.setMdmLinkRevisions(mdmLinkRevisionsFromSvc);
if (isNotEmpty(theResourceIds)) {
historyEvent.setSourceIds(theResourceIds.stream()
.map(IPrimitiveType::getValueAsString)
.collect(Collectors.toList()));
}
if (isNotEmpty(theMdmGoldenResourceIds)) {
historyEvent.setGoldenResourceIds(theMdmGoldenResourceIds.stream()
.map(IPrimitiveType::getValueAsString)
.collect(Collectors.toList()));
}
HookParams params = new HookParams();
params.add(RequestDetails.class, theRequestDetails);
params.add(MdmHistoryEvent.class, historyEvent);
myInterceptorBroadcaster.callHooks(Pointcut.MDM_POST_LINK_HISTORY, params);
}
return retVal;
}
}

View File

@ -21,14 +21,21 @@ package ca.uhn.fhir.mdm.provider;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.interceptor.api.HookParams;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.mdm.api.IMdmControllerSvc;
import ca.uhn.fhir.mdm.api.IMdmSettings;
import ca.uhn.fhir.mdm.api.IMdmSubmitSvc;
import ca.uhn.fhir.mdm.api.MdmConstants;
import ca.uhn.fhir.mdm.api.MdmLinkJson;
import ca.uhn.fhir.mdm.api.MdmQuerySearchParameters;
import ca.uhn.fhir.mdm.api.paging.MdmPageRequest;
import ca.uhn.fhir.mdm.model.MdmCreateOrUpdateParams;
import ca.uhn.fhir.mdm.model.MdmMergeGoldenResourcesParams;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.mdm.model.MdmUnduplicateGoldenResourceParams;
import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkJson;
import ca.uhn.fhir.mdm.model.mdmevents.MdmSubmitEvent;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Operation;
@ -57,16 +64,23 @@ import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import static ca.uhn.fhir.rest.api.Constants.PARAM_OFFSET;
import static org.apache.commons.lang3.ObjectUtils.isNotEmpty;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.slf4j.LoggerFactory.getLogger;
public class MdmProviderDstu3Plus extends BaseMdmProvider {
private static final Logger ourLog = getLogger(MdmProviderDstu3Plus.class);
private static final String PATIENT_RESOURCE = "Patient";
private static final String PRACTITIONER_RESOURCE = "Practitioner";
private final IMdmControllerSvc myMdmControllerSvc;
private final IMdmSubmitSvc myMdmSubmitSvc;
private final IMdmSettings myMdmSettings;
private final MdmControllerHelper myMdmControllerHelper;
private final IInterceptorBroadcaster myInterceptorBroadcaster;
public static final int DEFAULT_PAGE_SIZE = 20;
public static final int MAX_PAGE_SIZE = 100;
@ -81,14 +95,22 @@ public class MdmProviderDstu3Plus extends BaseMdmProvider {
IMdmControllerSvc theMdmControllerSvc,
MdmControllerHelper theMdmHelper,
IMdmSubmitSvc theMdmSubmitSvc,
IInterceptorBroadcaster theIInterceptorBroadcaster,
IMdmSettings theIMdmSettings) {
super(theFhirContext);
myMdmControllerSvc = theMdmControllerSvc;
myMdmControllerHelper = theMdmHelper;
myMdmSubmitSvc = theMdmSubmitSvc;
myInterceptorBroadcaster = theIInterceptorBroadcaster;
myMdmSettings = theIMdmSettings;
}
/**
* Searches for matches for the provided patient resource
* @param thePatient - the patient resource
* @param theRequestDetails - the request details
* @return - any matches to the provided patient resource
*/
@Operation(name = ProviderConstants.EMPI_MATCH, typeName = "Patient")
public IBaseBundle match(
@OperationParam(name = ProviderConstants.MDM_MATCH_RESOURCE, min = 1, max = 1, typeName = "Patient")
@ -100,6 +122,14 @@ public class MdmProviderDstu3Plus extends BaseMdmProvider {
return myMdmControllerHelper.getMatchesAndPossibleMatchesForResource(thePatient, "Patient", theRequestDetails);
}
/**
* Searches for matches for hte provided resource.
*
* @param theResource - the resource to match on
* @param theResourceType - the resource type
* @param theRequestDetails - the request details
* @return - any matches to the provided resource
*/
@Operation(name = ProviderConstants.MDM_MATCH)
public IBaseBundle serverMatch(
@OperationParam(name = ProviderConstants.MDM_MATCH_RESOURCE, min = 1, max = 1) IAnyResource theResource,
@ -138,11 +168,15 @@ public class MdmProviderDstu3Plus extends BaseMdmProvider {
theRequestDetails,
operationType,
getResourceType(ProviderConstants.MDM_MERGE_GR_FROM_GOLDEN_RESOURCE_ID, theFromGoldenResourceId));
return myMdmControllerSvc.mergeGoldenResources(
theFromGoldenResourceId.getValueAsString(),
theToGoldenResourceId.getValueAsString(),
theMergedResource,
txContext);
MdmMergeGoldenResourcesParams params = new MdmMergeGoldenResourcesParams();
params.setFromGoldenResourceId(theFromGoldenResourceId.getValueAsString());
params.setToGoldenResourceId(theToGoldenResourceId.getValueAsString());
params.setManuallyMergedResource(theMergedResource);
params.setMdmTransactionContext(txContext);
params.setRequestDetails(theRequestDetails);
return myMdmControllerSvc.mergeGoldenResources(params);
}
@Operation(name = ProviderConstants.MDM_UPDATE_LINK)
@ -155,14 +189,18 @@ public class MdmProviderDstu3Plus extends BaseMdmProvider {
IPrimitiveType<String> theMatchResult,
ServletRequestDetails theRequestDetails) {
validateUpdateLinkParameters(theGoldenResourceId, theResourceId, theMatchResult);
return myMdmControllerSvc.updateLink(
theGoldenResourceId.getValueAsString(),
theResourceId.getValue(),
theMatchResult.getValue(),
createMdmContext(
theRequestDetails,
MdmTransactionContext.OperationType.UPDATE_LINK,
getResourceType(ProviderConstants.MDM_UPDATE_LINK_GOLDEN_RESOURCE_ID, theGoldenResourceId)));
MdmCreateOrUpdateParams updateLinkParams = new MdmCreateOrUpdateParams();
updateLinkParams.setGoldenResourceId(theGoldenResourceId.getValueAsString());
updateLinkParams.setResourceId(theResourceId.getValue());
updateLinkParams.setMatchResult(MdmControllerUtil.extractMatchResultOrNull(theMatchResult.getValue()));
updateLinkParams.setMdmContext(createMdmContext(
theRequestDetails,
MdmTransactionContext.OperationType.UPDATE_LINK,
getResourceType(ProviderConstants.MDM_UPDATE_LINK_GOLDEN_RESOURCE_ID, theGoldenResourceId)));
updateLinkParams.setRequestDetails(theRequestDetails);
return myMdmControllerSvc.updateLink(updateLinkParams);
}
@Operation(name = ProviderConstants.MDM_CREATE_LINK)
@ -175,14 +213,18 @@ public class MdmProviderDstu3Plus extends BaseMdmProvider {
IPrimitiveType<String> theMatchResult,
ServletRequestDetails theRequestDetails) {
validateCreateLinkParameters(theGoldenResourceId, theResourceId, theMatchResult);
return myMdmControllerSvc.createLink(
theGoldenResourceId.getValueAsString(),
theResourceId.getValue(),
extractStringOrNull(theMatchResult),
createMdmContext(
theRequestDetails,
MdmTransactionContext.OperationType.CREATE_LINK,
getResourceType(ProviderConstants.MDM_CREATE_LINK_GOLDEN_RESOURCE_ID, theGoldenResourceId)));
MdmCreateOrUpdateParams params = new MdmCreateOrUpdateParams();
params.setGoldenResourceId(theGoldenResourceId.getValueAsString());
params.setResourceId(theResourceId.getValue());
params.setMatchResult(MdmControllerUtil.extractMatchResultOrNull(extractStringOrNull(theMatchResult)));
params.setRequestDetails(theRequestDetails);
params.setMdmContext(createMdmContext(
theRequestDetails,
MdmTransactionContext.OperationType.CREATE_LINK,
getResourceType(ProviderConstants.MDM_CREATE_LINK_GOLDEN_RESOURCE_ID, theGoldenResourceId)));
return myMdmControllerSvc.createLink(params);
}
@Operation(
@ -203,7 +245,7 @@ public class MdmProviderDstu3Plus extends BaseMdmProvider {
List<String> resourceNames = new ArrayList<>();
if (theResourceNames != null) {
if (isNotEmpty(theResourceNames)) {
resourceNames.addAll(
theResourceNames.stream().map(IPrimitiveType::getValue).collect(Collectors.toList()));
validateResourceNames(resourceNames);
@ -316,15 +358,18 @@ public class MdmProviderDstu3Plus extends BaseMdmProvider {
@OperationParam(name = ProviderConstants.MDM_QUERY_LINKS_RESOURCE_ID, min = 1, max = 1, typeName = "string")
IPrimitiveType<String> theResourceId,
ServletRequestDetails theRequestDetails) {
validateNotDuplicateParameters(theGoldenResourceId, theResourceId);
myMdmControllerSvc.notDuplicateGoldenResource(
theGoldenResourceId.getValue(),
theResourceId.getValue(),
createMdmContext(
theRequestDetails,
MdmTransactionContext.OperationType.NOT_DUPLICATE,
getResourceType(ProviderConstants.MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID, theGoldenResourceId)));
MdmUnduplicateGoldenResourceParams params = new MdmUnduplicateGoldenResourceParams();
params.setRequestDetails(theRequestDetails);
params.setGoldenResourceId(theGoldenResourceId.getValueAsString());
params.setTargetGoldenResourceId(theResourceId.getValueAsString());
params.setMdmContext(createMdmContext(
theRequestDetails,
MdmTransactionContext.OperationType.NOT_DUPLICATE,
getResourceType(ProviderConstants.MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID, theGoldenResourceId)));
myMdmControllerSvc.unduplicateGoldenResource(params);
IBaseParameters retval = ParametersUtil.newInstance(myFhirContext);
ParametersUtil.addParameterToParametersBoolean(myFhirContext, retval, "success", true);
@ -348,24 +393,22 @@ public class MdmProviderDstu3Plus extends BaseMdmProvider {
String criteria = convertStringTypeToString(theCriteria);
String resourceType = convertStringTypeToString(theResourceType);
long submittedCount;
IBaseParameters retval;
if (theRequestDetails.isPreferRespondAsync()) {
List<String> urls = buildUrlsForJob(criteria, resourceType);
return myMdmControllerSvc.submitMdmSubmitJob(urls, theBatchSize, theRequestDetails);
retval = myMdmControllerSvc.submitMdmSubmitJob(urls, theBatchSize, theRequestDetails);
} else {
if (StringUtils.isNotBlank(resourceType)) {
submittedCount =
myMdmSubmitSvc.submitSourceResourceTypeToMdm(resourceType, criteria, theRequestDetails);
} else {
submittedCount = myMdmSubmitSvc.submitAllSourceTypesToMdm(criteria, theRequestDetails);
}
return buildMdmOutParametersWithCount(submittedCount);
submittedCount = synchronousMdmSubmit(resourceType, null, criteria, theRequestDetails);
retval = buildMdmOutParametersWithCount(submittedCount);
}
return retval;
}
@Nonnull
private List<String> buildUrlsForJob(String criteria, String resourceType) {
List<String> urls = new ArrayList<>();
if (StringUtils.isNotBlank(resourceType)) {
if (isNotBlank(resourceType)) {
String theUrl = resourceType + "?" + criteria;
urls.add(theUrl);
} else {
@ -388,7 +431,8 @@ public class MdmProviderDstu3Plus extends BaseMdmProvider {
@OperationParam(name = ProviderConstants.OPERATION_BATCH_RESPONSE_JOB_ID, typeName = "integer")
})
public IBaseParameters mdmBatchPatientInstance(@IdParam IIdType theIdParam, RequestDetails theRequest) {
long submittedCount = myMdmSubmitSvc.submitSourceResourceToMdm(theIdParam, theRequest);
long submittedCount = synchronousMdmSubmit(null, theIdParam, null, theRequest);
return buildMdmOutParametersWithCount(submittedCount);
}
@ -406,11 +450,11 @@ public class MdmProviderDstu3Plus extends BaseMdmProvider {
IPrimitiveType<BigDecimal> theBatchSize,
ServletRequestDetails theRequest) {
if (theRequest.isPreferRespondAsync()) {
String theUrl = "Patient?";
String theUrl = PATIENT_RESOURCE + "?";
return myMdmControllerSvc.submitMdmSubmitJob(Collections.singletonList(theUrl), theBatchSize, theRequest);
} else {
String criteria = convertStringTypeToString(theCriteria);
long submittedCount = myMdmSubmitSvc.submitPatientTypeToMdm(criteria, theRequest);
long submittedCount = synchronousMdmSubmit(PATIENT_RESOURCE, null, criteria, theRequest);
return buildMdmOutParametersWithCount(submittedCount);
}
}
@ -423,7 +467,7 @@ public class MdmProviderDstu3Plus extends BaseMdmProvider {
@OperationParam(name = ProviderConstants.OPERATION_BATCH_RESPONSE_JOB_ID, typeName = "integer")
})
public IBaseParameters mdmBatchPractitionerInstance(@IdParam IIdType theIdParam, RequestDetails theRequest) {
long submittedCount = myMdmSubmitSvc.submitSourceResourceToMdm(theIdParam, theRequest);
long submittedCount = synchronousMdmSubmit(null, theIdParam, null, theRequest);
return buildMdmOutParametersWithCount(submittedCount);
}
@ -441,11 +485,11 @@ public class MdmProviderDstu3Plus extends BaseMdmProvider {
IPrimitiveType<BigDecimal> theBatchSize,
ServletRequestDetails theRequest) {
if (theRequest.isPreferRespondAsync()) {
String theUrl = "Practitioner?";
String theUrl = PRACTITIONER_RESOURCE + "?";
return myMdmControllerSvc.submitMdmSubmitJob(Collections.singletonList(theUrl), theBatchSize, theRequest);
} else {
String criteria = convertStringTypeToString(theCriteria);
long submittedCount = myMdmSubmitSvc.submitPractitionerTypeToMdm(criteria, theRequest);
long submittedCount = synchronousMdmSubmit(PRACTITIONER_RESOURCE, null, criteria, theRequest);
return buildMdmOutParametersWithCount(submittedCount);
}
}
@ -482,4 +526,58 @@ public class MdmProviderDstu3Plus extends BaseMdmProvider {
IIdType idType = MdmControllerUtil.getGoldenIdDtOrThrowException(theParamName, theResourceId);
return idType.getResourceType();
}
private long synchronousMdmSubmit(
String theResourceType, IIdType theIdType, String theCriteria, RequestDetails theRequestDetails) {
long submittedCount = -1;
List<String> urls = new ArrayList<>();
if (theIdType != null) {
submittedCount = mdmSubmitWithId(theIdType, theRequestDetails, urls);
} else if (isNotBlank(theResourceType)) {
submittedCount = submitResourceTypeWithCriteria(theResourceType, theCriteria, theRequestDetails, urls);
} else {
submittedCount = submitAll(theCriteria, theRequestDetails, urls);
}
if (myInterceptorBroadcaster.hasHooks(Pointcut.MDM_SUBMIT)) {
// MDM_SUBMIT synchronous submit
MdmSubmitEvent submitEvent = new MdmSubmitEvent();
submitEvent.setBatchJob(false);
submitEvent.setUrls(urls);
HookParams hookParams = new HookParams();
hookParams.add(RequestDetails.class, theRequestDetails);
hookParams.add(MdmSubmitEvent.class, submitEvent);
myInterceptorBroadcaster.callHooks(Pointcut.MDM_SUBMIT, hookParams);
}
return submittedCount;
}
private long mdmSubmitWithId(IIdType theIdType, RequestDetails theRequestDetails, List<String> theUrls) {
theUrls.add(theIdType.getValue());
return myMdmSubmitSvc.submitSourceResourceToMdm(theIdType, theRequestDetails);
}
private long submitResourceTypeWithCriteria(
String theResourceType, String theCriteria, RequestDetails theRequestDetails, List<String> theUrls) {
String url = theResourceType;
if (isNotEmpty(theCriteria)) {
url += "?" + theCriteria;
}
theUrls.add(url);
return myMdmSubmitSvc.submitSourceResourceTypeToMdm(theResourceType, theCriteria, theRequestDetails);
}
private long submitAll(String theCriteria, RequestDetails theRequestDetails, List<String> theUrls) {
// submitAllSourceTypes only runs through
// valid MDM source types
List<String> resourceTypes = myMdmSettings.getMdmRules().getMdmTypes();
for (String resourceType : resourceTypes) {
String url = resourceType + (isNotEmpty(theCriteria) ? "?" + theCriteria : "");
theUrls.add(url);
}
return myMdmSubmitSvc.submitAllSourceTypesToMdm(theCriteria, theRequestDetails);
}
}

View File

@ -22,6 +22,7 @@ package ca.uhn.fhir.mdm.provider;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
import ca.uhn.fhir.mdm.api.IMdmControllerSvc;
import ca.uhn.fhir.mdm.api.IMdmSettings;
@ -55,19 +56,28 @@ public class MdmProviderLoader {
@Autowired
private JpaStorageSettings myStorageSettings;
private MdmProviderDstu3Plus myMdmProvider;
@Autowired
private IInterceptorBroadcaster myInterceptorBroadcaster;
private BaseMdmProvider myMdmProvider;
private MdmLinkHistoryProviderDstu3Plus myMdmHistoryProvider;
public void loadProvider() {
switch (myFhirContext.getVersion().getVersion()) {
case DSTU3:
case R4:
myMdmProvider = new MdmProviderDstu3Plus(
myFhirContext, myMdmControllerSvc, myMdmControllerHelper, myMdmSubmitSvc, myMdmSettings);
myResourceProviderFactory.addSupplier(() -> myMdmProvider);
myResourceProviderFactory.addSupplier(() -> new MdmProviderDstu3Plus(
myFhirContext,
myMdmControllerSvc,
myMdmControllerHelper,
myMdmSubmitSvc,
myInterceptorBroadcaster,
myMdmSettings));
if (myStorageSettings.isNonResourceDbHistoryEnabled()) {
myMdmHistoryProvider = new MdmLinkHistoryProviderDstu3Plus(myFhirContext, myMdmControllerSvc);
myResourceProviderFactory.addSupplier(() -> myMdmHistoryProvider);
myResourceProviderFactory.addSupplier(() -> {
return new MdmLinkHistoryProviderDstu3Plus(
myFhirContext, myMdmControllerSvc, myInterceptorBroadcaster);
});
}
break;
default:

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -7,7 +7,7 @@
<parent>
<artifactId>hapi-fhir-serviceloaders</artifactId>
<groupId>ca.uhn.hapi.fhir</groupId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -7,7 +7,7 @@
<parent>
<artifactId>hapi-fhir-serviceloaders</artifactId>
<groupId>ca.uhn.hapi.fhir</groupId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
@ -21,7 +21,7 @@
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-caching-api</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
</dependency>
<dependency>

View File

@ -7,7 +7,7 @@
<parent>
<artifactId>hapi-fhir-serviceloaders</artifactId>
<groupId>ca.uhn.hapi.fhir</groupId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -7,7 +7,7 @@
<parent>
<artifactId>hapi-fhir</artifactId>
<groupId>ca.uhn.hapi.fhir</groupId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>hapi-deployable-pom</artifactId>
<groupId>ca.uhn.hapi.fhir</groupId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
</parent>
<artifactId>hapi-fhir-spring-boot-sample-client-apache</artifactId>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -7,7 +7,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -7,7 +7,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -7,7 +7,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>6.9.1-SNAPSHOT</version>
<version>6.9.2-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

Some files were not shown because too many files have changed in this diff Show More