From 759f06ce48aabbf59bd55e63ef0f49084bdbebb6 Mon Sep 17 00:00:00 2001 From: Nick Date: Thu, 14 Jan 2021 14:24:59 -0500 Subject: [PATCH] Added optional manual override to link update --- .../docs/server_jpa_mdm/mdm_operations.md | 11 +++- .../jpa/mdm/svc/MdmControllerSvcImpl.java | 4 +- .../jpa/mdm/svc/MdmLinkUpdaterSvcImpl.java | 7 ++- .../provider/MdmProviderUpdateLinkR4Test.java | 62 +++++++++++++------ .../uhn/fhir/mdm/api/IMdmControllerSvc.java | 2 +- .../uhn/fhir/mdm/api/IMdmLinkUpdaterSvc.java | 2 +- .../mdm/provider/MdmProviderDstu3Plus.java | 5 +- .../server/provider/ProviderConstants.java | 1 + 8 files changed, 66 insertions(+), 28 deletions(-) diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa_mdm/mdm_operations.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa_mdm/mdm_operations.md index 426dfcbaf09..32819da2999 100644 --- a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa_mdm/mdm_operations.md +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa_mdm/mdm_operations.md @@ -240,7 +240,7 @@ Use the `$mdm-update-link` operation to change the `matchResult` update of an md String 1..1 - The id of the source resource. + The id of the target resource. @@ -251,6 +251,15 @@ Use the `$mdm-update-link` operation to change the `matchResult` update of an md Must be either MATCH or NO_MATCH. + + resource + Resource + 0.1 + + Optional manually merged Golden Resource. All values except for the metadata, PID and identifiers will be copied from this resource, if it is present. If no value is specified, all fields from the resource pointed to by "resourceId" will be copied instead. + . + + diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmControllerSvcImpl.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmControllerSvcImpl.java index 824e6ffd237..209a986fa10 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmControllerSvcImpl.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmControllerSvcImpl.java @@ -81,14 +81,14 @@ public class MdmControllerSvcImpl implements IMdmControllerSvc { } @Override - public IAnyResource updateLink(String theGoldenResourceId, String theSourceResourceId, String theMatchResult, MdmTransactionContext theMdmTransactionContext) { + public IAnyResource updateLink(String theGoldenResourceId, String theSourceResourceId, IAnyResource theManuallyMergedGoldenResource, 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); - return myIMdmLinkUpdaterSvc.updateLink(goldenResource, source, matchResult, theMdmTransactionContext); + return myIMdmLinkUpdaterSvc.updateLink(goldenResource, source, theManuallyMergedGoldenResource, matchResult, theMdmTransactionContext); } @Override diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkUpdaterSvcImpl.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkUpdaterSvcImpl.java index 2dc47bfe805..068bc6c67fb 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkUpdaterSvcImpl.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmLinkUpdaterSvcImpl.java @@ -69,7 +69,8 @@ public class MdmLinkUpdaterSvcImpl implements IMdmLinkUpdaterSvc { @Transactional @Override - public IAnyResource updateLink(IAnyResource theGoldenResource, IAnyResource theSourceResource, MdmMatchResultEnum theMatchResult, MdmTransactionContext theMdmContext) { + public IAnyResource updateLink(IAnyResource theGoldenResource, IAnyResource theSourceResource, IAnyResource theManuallyMergedGoldenResource, + MdmMatchResultEnum theMatchResult, MdmTransactionContext theMdmContext) { String sourceType = myFhirContext.getResourceType(theSourceResource); validateUpdateLinkRequest(theGoldenResource, theSourceResource, theMatchResult, sourceType); @@ -93,8 +94,8 @@ public class MdmLinkUpdaterSvcImpl implements IMdmLinkUpdaterSvc { mdmLink.setLinkSource(MdmLinkSourceEnum.MANUAL); myMdmLinkDaoSvc.save(mdmLink); - // TODO NG MDM Since it's a manual link update, we need to allow the caller to optionally override this behavior with what they thing should go into the golden resource - myMdmSurvivorshipService.applySurvivorshipRulesToGoldenResource(theSourceResource, theGoldenResource, theMdmContext); + IAnyResource resource = (theManuallyMergedGoldenResource == null) ? theSourceResource : theManuallyMergedGoldenResource; + myMdmSurvivorshipService.applySurvivorshipRulesToGoldenResource(resource, theGoldenResource, theMdmContext); myMdmResourceDaoSvc.upsertGoldenResource(theGoldenResource, theMdmContext.getResourceType()); if (theMatchResult == MdmMatchResultEnum.NO_MATCH) { diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderUpdateLinkR4Test.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderUpdateLinkR4Test.java index 4e17421872d..5948a1f10b5 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderUpdateLinkR4Test.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/provider/MdmProviderUpdateLinkR4Test.java @@ -1,10 +1,10 @@ package ca.uhn.fhir.jpa.mdm.provider; +import ca.uhn.fhir.jpa.entity.MdmLink; import ca.uhn.fhir.mdm.api.MdmConstants; import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; import ca.uhn.fhir.mdm.util.MessageHelper; -import ca.uhn.fhir.jpa.entity.MdmLink; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException; import org.hl7.fhir.r4.model.Patient; @@ -12,11 +12,17 @@ import org.hl7.fhir.r4.model.StringType; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import java.util.Date; import java.util.List; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.*; -import static org.junit.jupiter.api.Assertions.*; +import static org.hamcrest.Matchers.endsWith; +import static org.hamcrest.Matchers.matchesPattern; +import static org.hamcrest.Matchers.startsWith; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; public class MdmProviderUpdateLinkR4Test extends BaseLinkR4Test { @@ -26,7 +32,7 @@ public class MdmProviderUpdateLinkR4Test extends BaseLinkR4Test { @Test public void testUpdateLinkNoMatch() { assertLinkCount(1); - myMdmProvider.updateLink(mySourcePatientId, myPatientId, NO_MATCH_RESULT, myRequestDetails); + myMdmProvider.updateLink(mySourcePatientId, myPatientId, NO_MATCH_RESULT, null, myRequestDetails); assertLinkCount(2); List links = getPatientLinks(); @@ -40,7 +46,7 @@ public class MdmProviderUpdateLinkR4Test extends BaseLinkR4Test { @Test public void testUpdateLinkMatch() { assertLinkCount(1); - myMdmProvider.updateLink(mySourcePatientId, myPatientId, MATCH_RESULT, myRequestDetails); + myMdmProvider.updateLink(mySourcePatientId, myPatientId, MATCH_RESULT, null, myRequestDetails); assertLinkCount(1); List links = getPatientLinks(); @@ -48,14 +54,34 @@ public class MdmProviderUpdateLinkR4Test extends BaseLinkR4Test { assertEquals(MdmMatchResultEnum.MATCH, links.get(0).getMatchResult()); } + @Test + public void testUpdateLinkWithOverride() { + assertLinkCount(1); + Patient patient = new Patient(); + patient.addName().addGiven("Given"); + patient.addName().setFamily("Family"); + patient.setBirthDate(new Date()); + + Patient updatedPerson = (Patient) myMdmProvider + .updateLink(mySourcePatientId, myPatientId, MATCH_RESULT, patient, myRequestDetails); + assertLinkCount(1); + + List links = getPatientLinks(); + assertEquals(MdmLinkSourceEnum.MANUAL, links.get(0).getLinkSource()); + assertEquals(MdmMatchResultEnum.MATCH, links.get(0).getMatchResult()); + + assertEquals(patient.getNameFirstRep().getNameAsSingleString(), updatedPerson.getNameFirstRep().getNameAsSingleString()); + assertEquals(patient.getBirthDate(), updatedPerson.getBirthDate()); + } + @Test public void testUpdateLinkTwiceFailsDueToWrongVersion() { - myMdmProvider.updateLink(mySourcePatientId, myPatientId, MATCH_RESULT, myRequestDetails); + myMdmProvider.updateLink(mySourcePatientId, myPatientId, MATCH_RESULT, null, myRequestDetails); materiallyChangeGoldenPatient(); try { - myMdmProvider.updateLink(mySourcePatientId, myPatientId, NO_MATCH_RESULT, myRequestDetails); + myMdmProvider.updateLink(mySourcePatientId, myPatientId, NO_MATCH_RESULT, null, myRequestDetails); fail(); } catch (ResourceVersionConflictException e) { assertThat(e.getMessage(), matchesPattern("Requested resource Patient/\\d+/_history/1 is not the latest version. Latest version is Patient/\\d+/_history/2")); @@ -70,19 +96,19 @@ public class MdmProviderUpdateLinkR4Test extends BaseLinkR4Test { @Test public void testUpdateLinkTwiceDoesNotThrowValidationErrorWhenNoVersionIsProvided() { - myMdmProvider.updateLink(mySourcePatientId, myPatientId, MATCH_RESULT, myRequestDetails); - Patient patient = (Patient) myMdmProvider.updateLink(myVersionlessGodlenResourceId, myPatientId, NO_MATCH_RESULT, myRequestDetails); + myMdmProvider.updateLink(mySourcePatientId, myPatientId, MATCH_RESULT, null, myRequestDetails); + Patient patient = (Patient) myMdmProvider.updateLink(myVersionlessGodlenResourceId, myPatientId, NO_MATCH_RESULT, null, myRequestDetails); assertNotNull(patient); // if this wasn't allowed - a validation exception would be thrown } @Test public void testUnlinkLink() { - myMdmProvider.updateLink(mySourcePatientId, myPatientId, NO_MATCH_RESULT, myRequestDetails); + myMdmProvider.updateLink(mySourcePatientId, myPatientId, NO_MATCH_RESULT, null, myRequestDetails); materiallyChangeGoldenPatient(); try { - myMdmProvider.updateLink(mySourcePatientId, myPatientId, MATCH_RESULT, myRequestDetails); + myMdmProvider.updateLink(mySourcePatientId, myPatientId, MATCH_RESULT, null, myRequestDetails); fail(); } catch (ResourceVersionConflictException e) { assertThat(e.getMessage(), matchesPattern("Requested resource Patient/\\d+/_history/1 is not the latest version. Latest version is Patient/\\d+/_history/2")); @@ -92,7 +118,7 @@ public class MdmProviderUpdateLinkR4Test extends BaseLinkR4Test { @Test public void testUpdateIllegalResultForPossibleMatch() { try { - myMdmProvider.updateLink(mySourcePatientId, myPatientId, POSSIBLE_MATCH_RESULT, myRequestDetails); + myMdmProvider.updateLink(mySourcePatientId, myPatientId, POSSIBLE_MATCH_RESULT, null, myRequestDetails); fail(); } catch (InvalidRequestException e) { assertEquals("$mdm-update-link illegal matchResult value 'POSSIBLE_MATCH'. Must be NO_MATCH or MATCH", e.getMessage()); @@ -102,7 +128,7 @@ public class MdmProviderUpdateLinkR4Test extends BaseLinkR4Test { @Test public void testUpdateIllegalResultPD() { try { - myMdmProvider.updateLink(mySourcePatientId, myPatientId, POSSIBLE_DUPLICATE_RESULT, myRequestDetails); + myMdmProvider.updateLink(mySourcePatientId, myPatientId, POSSIBLE_DUPLICATE_RESULT, null, myRequestDetails); fail(); } catch (InvalidRequestException e) { assertEquals("$mdm-update-link illegal matchResult value 'POSSIBLE_DUPLICATE'. Must be NO_MATCH or MATCH", e.getMessage()); @@ -112,7 +138,7 @@ public class MdmProviderUpdateLinkR4Test extends BaseLinkR4Test { @Test public void testUpdateIllegalSecondArg() { try { - myMdmProvider.updateLink(myPatientId, new StringType(""), NO_MATCH_RESULT, myRequestDetails); + myMdmProvider.updateLink(myPatientId, new StringType(""), NO_MATCH_RESULT, null, myRequestDetails); fail(); } catch (InvalidRequestException e) { assertThat(e.getMessage(), endsWith(" must have form / where is the id of the resource and is the type of the resource")); @@ -122,7 +148,7 @@ public class MdmProviderUpdateLinkR4Test extends BaseLinkR4Test { @Test public void testUpdateIllegalFirstArg() { try { - myMdmProvider.updateLink(new StringType(""), myPatientId, NO_MATCH_RESULT, myRequestDetails); + myMdmProvider.updateLink(new StringType(""), myPatientId, NO_MATCH_RESULT, null, myRequestDetails); fail(); } catch (InvalidRequestException e) { assertThat(e.getMessage(), endsWith(" must have form / where is the id of the resource")); @@ -132,7 +158,7 @@ public class MdmProviderUpdateLinkR4Test extends BaseLinkR4Test { @Test public void testAttemptingToModifyANonExistentLinkFails() { try { - myMdmProvider.updateLink(mySourcePatientId, mySourcePatientId, NO_MATCH_RESULT, myRequestDetails); + myMdmProvider.updateLink(mySourcePatientId, mySourcePatientId, NO_MATCH_RESULT, null, myRequestDetails); fail(); } catch (InvalidRequestException e) { assertThat(e.getMessage(), startsWith("No link")); @@ -143,7 +169,7 @@ public class MdmProviderUpdateLinkR4Test extends BaseLinkR4Test { public void testUpdateStrangePatient() { Patient patient = createPatient(); try { - myMdmProvider.updateLink(new StringType(patient.getIdElement().getValue()), myPatientId, NO_MATCH_RESULT, myRequestDetails); + myMdmProvider.updateLink(new StringType(patient.getIdElement().getValue()), myPatientId, NO_MATCH_RESULT, null, myRequestDetails); fail(); } catch (InvalidRequestException e) { String expectedMessage = myMessageHelper.getMessageForUnmanagedResource(); @@ -157,7 +183,7 @@ public class MdmProviderUpdateLinkR4Test extends BaseLinkR4Test { patient.getMeta().addTag().setSystem(MdmConstants.SYSTEM_MDM_MANAGED).setCode(MdmConstants.CODE_NO_MDM_MANAGED); createPatient(patient); try { - myMdmProvider.updateLink(mySourcePatientId, new StringType(patient.getIdElement().getValue()), NO_MATCH_RESULT, myRequestDetails); + myMdmProvider.updateLink(mySourcePatientId, new StringType(patient.getIdElement().getValue()), NO_MATCH_RESULT, null, myRequestDetails); fail(); } catch (InvalidRequestException e) { assertEquals(myMessageHelper.getMessageForUnsupportedSourceResource(), e.getMessage()); diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmControllerSvc.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmControllerSvc.java index fb0e9ebe981..0d2d4cb5762 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmControllerSvc.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmControllerSvc.java @@ -36,5 +36,5 @@ public interface IMdmControllerSvc { IAnyResource mergeGoldenResources(String theFromGoldenResourceId, String theToGoldenResourceId, IAnyResource theManuallyMergedGoldenResource, MdmTransactionContext theMdmTransactionContext); - IAnyResource updateLink(String theGoldenResourceId, String theSourceResourceId, String theMatchResult, MdmTransactionContext theMdmTransactionContext); + IAnyResource updateLink(String theGoldenResourceId, String theSourceResourceId, IAnyResource theManuallyMergedGoldenResource, String theMatchResult, MdmTransactionContext theMdmTransactionContext); } diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmLinkUpdaterSvc.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmLinkUpdaterSvc.java index 0c5597e9b73..27d1583d28b 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmLinkUpdaterSvc.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/api/IMdmLinkUpdaterSvc.java @@ -24,6 +24,6 @@ import ca.uhn.fhir.mdm.model.MdmTransactionContext; import org.hl7.fhir.instance.model.api.IAnyResource; public interface IMdmLinkUpdaterSvc { - IAnyResource updateLink(IAnyResource theGoldenResource, IAnyResource theSourceResource, MdmMatchResultEnum theMatchResult, MdmTransactionContext theMdmContext); + IAnyResource updateLink(IAnyResource theGoldenResource, IAnyResource theSourceResource, IAnyResource theManuallyMergedGoldenResource, MdmMatchResultEnum theMatchResult, MdmTransactionContext theMdmContext); void notDuplicateGoldenResource(IAnyResource theGoldenResource, IAnyResource theTargetGoldenResource, MdmTransactionContext theMdmContext); } diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/MdmProviderDstu3Plus.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/MdmProviderDstu3Plus.java index c811b9e70e1..c7901c12fab 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/MdmProviderDstu3Plus.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/provider/MdmProviderDstu3Plus.java @@ -159,10 +159,11 @@ public class MdmProviderDstu3Plus extends BaseMdmProvider { public IBaseResource updateLink(@OperationParam(name = ProviderConstants.MDM_UPDATE_LINK_GOLDEN_RESOURCE_ID, min = 1, max = 1) IPrimitiveType theGoldenResourceId, @OperationParam(name = ProviderConstants.MDM_UPDATE_LINK_RESOURCE_ID, min = 1, max = 1) IPrimitiveType theResourceId, @OperationParam(name = ProviderConstants.MDM_UPDATE_LINK_MATCH_RESULT, min = 1, max = 1) IPrimitiveType theMatchResult, + @OperationParam(name = ProviderConstants.MDM_UPDATE_LINK_RESOURCE, max = 1) IAnyResource theManuallyMergedResource, ServletRequestDetails theRequestDetails) { validateUpdateLinkParameters(theGoldenResourceId, theResourceId, theMatchResult); - return myMdmControllerSvc.updateLink(theGoldenResourceId.getValueAsString(), theResourceId.getValue(), theMatchResult.getValue(), - createMdmContext(theRequestDetails, MdmTransactionContext.OperationType.UPDATE_LINK, + return myMdmControllerSvc.updateLink(theGoldenResourceId.getValueAsString(), theResourceId.getValue(), theManuallyMergedResource, + theMatchResult.getValue(), createMdmContext(theRequestDetails, MdmTransactionContext.OperationType.UPDATE_LINK, getResourceType(ProviderConstants.MDM_UPDATE_LINK_GOLDEN_RESOURCE_ID, theGoldenResourceId)) ); } diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ProviderConstants.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ProviderConstants.java index a6258b46f95..0d98d9bd1f8 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ProviderConstants.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/provider/ProviderConstants.java @@ -75,6 +75,7 @@ public class ProviderConstants { public static final String MDM_UPDATE_LINK = "$mdm-update-link"; public static final String MDM_UPDATE_LINK_GOLDEN_RESOURCE_ID = "goldenResourceId"; public static final String MDM_UPDATE_LINK_RESOURCE_ID = "resourceId"; + public static final String MDM_UPDATE_LINK_RESOURCE = "resource"; public static final String MDM_UPDATE_LINK_MATCH_RESULT = "matchResult"; public static final String MDM_QUERY_LINKS = "$mdm-query-links";