Added optional manual override to link update

This commit is contained in:
Nick 2021-01-14 14:24:59 -05:00
parent 8a5651093b
commit 759f06ce48
8 changed files with 66 additions and 28 deletions

View File

@ -240,7 +240,7 @@ Use the `$mdm-update-link` operation to change the `matchResult` update of an md
<td>String</td>
<td>1..1</td>
<td>
The id of the source resource.
The id of the target resource.
</td>
</tr>
<tr>
@ -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.
</td>
</tr>
<tr>
<td>resource</td>
<td>Resource</td>
<td>0.1</td>
<td>
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.
.
</td>
</tr>
</tbody>
</table>

View File

@ -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

View File

@ -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) {

View File

@ -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<MdmLink> 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<MdmLink> 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<MdmLink> 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 <resourceType>/<id> where <id> is the id of the resource and <resourceType> 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 <resourceType>/<id> where <id> 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());

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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<String> theGoldenResourceId,
@OperationParam(name = ProviderConstants.MDM_UPDATE_LINK_RESOURCE_ID, min = 1, max = 1) IPrimitiveType<String> theResourceId,
@OperationParam(name = ProviderConstants.MDM_UPDATE_LINK_MATCH_RESULT, min = 1, max = 1) IPrimitiveType<String> 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))
);
}

View File

@ -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";