Pre-review commit
This commit is contained in:
parent
759f06ce48
commit
6cc40b134e
|
@ -251,15 +251,6 @@ 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>
|
||||
|
||||
|
|
|
@ -20,17 +20,18 @@ package ca.uhn.fhir.jpa.mdm.svc;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
|
||||
import ca.uhn.fhir.jpa.entity.MdmLink;
|
||||
import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc;
|
||||
import ca.uhn.fhir.mdm.api.IGoldenResourceMergerSvc;
|
||||
import ca.uhn.fhir.mdm.api.IMdmLinkSvc;
|
||||
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
|
||||
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
|
||||
import ca.uhn.fhir.mdm.api.IMdmLinkSvc;
|
||||
import ca.uhn.fhir.mdm.api.IGoldenResourceMergerSvc;
|
||||
import ca.uhn.fhir.mdm.log.Logs;
|
||||
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
|
||||
import ca.uhn.fhir.mdm.util.GoldenResourceHelper;
|
||||
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
|
||||
import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc;
|
||||
import ca.uhn.fhir.jpa.entity.MdmLink;
|
||||
import ca.uhn.fhir.mdm.util.MdmResourceUtil;
|
||||
import ca.uhn.fhir.mdm.util.TerserUtil;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -65,16 +66,24 @@ public class GoldenResourceMergerSvcImpl implements IGoldenResourceMergerSvc {
|
|||
Long toGoldenResourcePid = myIdHelperService.getPidOrThrowException(theToGoldenResource);
|
||||
String resourceType = theMdmTransactionContext.getResourceType();
|
||||
|
||||
// Merge attributes, to be determined when survivorship is solved.
|
||||
myGoldenResourceHelper.mergeIndentifierFields(theFromGoldenResource, theToGoldenResource, theMdmTransactionContext);
|
||||
if (theMergedResource != null ) {
|
||||
if (myGoldenResourceHelper.hasIdentifier(theMergedResource)) {
|
||||
throw new IllegalArgumentException("Manually merged resource can not contain identifiers");
|
||||
}
|
||||
myGoldenResourceHelper.mergeIndentifierFields(theFromGoldenResource, theMergedResource, theMdmTransactionContext);
|
||||
myGoldenResourceHelper.mergeIndentifierFields(theToGoldenResource, theMergedResource, theMdmTransactionContext);
|
||||
|
||||
IAnyResource mergeSource = ( theMergedResource == null ) ? theFromGoldenResource : theMergedResource;
|
||||
myGoldenResourceHelper.mergeNonIdentiferFields(mergeSource, theToGoldenResource, theMdmTransactionContext);
|
||||
theMergedResource.setId(theToGoldenResource.getId());
|
||||
theToGoldenResource = (IAnyResource) myMdmResourceDaoSvc.upsertGoldenResource(theMergedResource, resourceType).getResource();
|
||||
} else {
|
||||
myGoldenResourceHelper.mergeIndentifierFields(theFromGoldenResource, theToGoldenResource, theMdmTransactionContext);
|
||||
myGoldenResourceHelper.mergeNonIdentiferFields(theFromGoldenResource, theToGoldenResource, theMdmTransactionContext);
|
||||
//Save changes to the golden resource
|
||||
myMdmResourceDaoSvc.upsertGoldenResource(theToGoldenResource, resourceType);
|
||||
}
|
||||
|
||||
//Merge the links from the FROM to the TO resource. Clean up dangling links.
|
||||
mergeGoldenResourceLinks(theFromGoldenResource, theToGoldenResource, toGoldenResourcePid, theMdmTransactionContext);
|
||||
//Save changes to the golden resource
|
||||
myMdmResourceDaoSvc.upsertGoldenResource(theToGoldenResource, resourceType);
|
||||
|
||||
//Create the new REDIRECT link
|
||||
addMergeLink(toGoldenResourcePid, fromGoldenResourcePid, resourceType);
|
||||
|
|
|
@ -94,8 +94,11 @@ public class MdmLinkUpdaterSvcImpl implements IMdmLinkUpdaterSvc {
|
|||
mdmLink.setLinkSource(MdmLinkSourceEnum.MANUAL);
|
||||
myMdmLinkDaoSvc.save(mdmLink);
|
||||
|
||||
IAnyResource resource = (theManuallyMergedGoldenResource == null) ? theSourceResource : theManuallyMergedGoldenResource;
|
||||
myMdmSurvivorshipService.applySurvivorshipRulesToGoldenResource(resource, theGoldenResource, theMdmContext);
|
||||
// IAnyResource resource = (theManuallyMergedGoldenResource == null) ? theSourceResource : theManuallyMergedGoldenResource;
|
||||
if (theMatchResult == MdmMatchResultEnum.MATCH) {
|
||||
// only apply survivorship rules in case of a match
|
||||
myMdmSurvivorshipService.applySurvivorshipRulesToGoldenResource(theSourceResource, theGoldenResource, theMdmContext);
|
||||
}
|
||||
|
||||
myMdmResourceDaoSvc.upsertGoldenResource(theGoldenResource, theMdmContext.getResourceType());
|
||||
if (theMatchResult == MdmMatchResultEnum.NO_MATCH) {
|
||||
|
|
|
@ -34,6 +34,8 @@ public class MdmSurvivorshipSvcImpl implements IMdmSurvivorshipService {
|
|||
private FhirContext myFhirContext;
|
||||
|
||||
/**
|
||||
* TODO NG Update docs to tell taht we also do that in non GR merge - like linking
|
||||
*
|
||||
* Merges two golden resources by overwriting all field values on theGoldenResource param for all REST operation methods
|
||||
* except MERGE_GOLDEN_RESOURCES. In case of MERGE_GOLDEN_RESOURCES, it will attempt to copy field values from
|
||||
* theTargetResource that do not exist in theGoldenResource. PID, indentifiers and meta values are not affected by
|
||||
|
|
|
@ -4,6 +4,7 @@ import ca.uhn.fhir.jpa.entity.MdmLink;
|
|||
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
|
||||
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
|
||||
import ca.uhn.fhir.mdm.util.MdmResourceUtil;
|
||||
import ca.uhn.fhir.mdm.util.TerserUtil;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
|
@ -95,12 +96,11 @@ public class MdmProviderMergeGoldenResourcesR4Test extends BaseProviderR4Test {
|
|||
|
||||
@Test
|
||||
public void testMergeWithManualOverride() {
|
||||
Patient mergedSourcePatient = (Patient) myMdmProvider.mergeGoldenResources(myFromGoldenPatientId,
|
||||
myToGoldenPatientId, myFromGoldenPatient, myRequestDetails);
|
||||
Patient patient = TerserUtil.clone(myFhirContext, myFromGoldenPatient);
|
||||
patient.setIdElement(null);
|
||||
|
||||
myFromGoldenPatient = (Patient) myPatientDao.read(myFromGoldenPatient.getIdElement().toUnqualifiedVersionless());
|
||||
assertTrue(!MdmResourceUtil.isGoldenRecord(myFromGoldenPatient));
|
||||
assertTrue(MdmResourceUtil.isGoldenRecordRedirected(myFromGoldenPatient));
|
||||
Patient mergedSourcePatient = (Patient) myMdmProvider.mergeGoldenResources(myFromGoldenPatientId,
|
||||
myToGoldenPatientId, patient, myRequestDetails);
|
||||
|
||||
assertEquals(myToGoldenPatient.getIdElement(), mergedSourcePatient.getIdElement());
|
||||
assertThat(mergedSourcePatient, is(sameGoldenResourceAs(myToGoldenPatient)));
|
||||
|
|
|
@ -12,7 +12,6 @@ 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;
|
||||
|
@ -32,7 +31,7 @@ public class MdmProviderUpdateLinkR4Test extends BaseLinkR4Test {
|
|||
@Test
|
||||
public void testUpdateLinkNoMatch() {
|
||||
assertLinkCount(1);
|
||||
myMdmProvider.updateLink(mySourcePatientId, myPatientId, NO_MATCH_RESULT, null, myRequestDetails);
|
||||
myMdmProvider.updateLink(mySourcePatientId, myPatientId, NO_MATCH_RESULT, myRequestDetails);
|
||||
assertLinkCount(2);
|
||||
|
||||
List<MdmLink> links = getPatientLinks();
|
||||
|
@ -46,7 +45,7 @@ public class MdmProviderUpdateLinkR4Test extends BaseLinkR4Test {
|
|||
@Test
|
||||
public void testUpdateLinkMatch() {
|
||||
assertLinkCount(1);
|
||||
myMdmProvider.updateLink(mySourcePatientId, myPatientId, MATCH_RESULT, null, myRequestDetails);
|
||||
myMdmProvider.updateLink(mySourcePatientId, myPatientId, MATCH_RESULT, myRequestDetails);
|
||||
assertLinkCount(1);
|
||||
|
||||
List<MdmLink> links = getPatientLinks();
|
||||
|
@ -54,34 +53,14 @@ 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, null, myRequestDetails);
|
||||
myMdmProvider.updateLink(mySourcePatientId, myPatientId, MATCH_RESULT, myRequestDetails);
|
||||
|
||||
materiallyChangeGoldenPatient();
|
||||
|
||||
try {
|
||||
myMdmProvider.updateLink(mySourcePatientId, myPatientId, NO_MATCH_RESULT, null, myRequestDetails);
|
||||
myMdmProvider.updateLink(mySourcePatientId, myPatientId, NO_MATCH_RESULT, 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"));
|
||||
|
@ -96,19 +75,19 @@ public class MdmProviderUpdateLinkR4Test extends BaseLinkR4Test {
|
|||
|
||||
@Test
|
||||
public void testUpdateLinkTwiceDoesNotThrowValidationErrorWhenNoVersionIsProvided() {
|
||||
myMdmProvider.updateLink(mySourcePatientId, myPatientId, MATCH_RESULT, null, myRequestDetails);
|
||||
Patient patient = (Patient) myMdmProvider.updateLink(myVersionlessGodlenResourceId, myPatientId, NO_MATCH_RESULT, null, myRequestDetails);
|
||||
myMdmProvider.updateLink(mySourcePatientId, myPatientId, MATCH_RESULT, myRequestDetails);
|
||||
Patient patient = (Patient) myMdmProvider.updateLink(myVersionlessGodlenResourceId, myPatientId, NO_MATCH_RESULT, 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, null, myRequestDetails);
|
||||
myMdmProvider.updateLink(mySourcePatientId, myPatientId, NO_MATCH_RESULT, myRequestDetails);
|
||||
|
||||
materiallyChangeGoldenPatient();
|
||||
|
||||
try {
|
||||
myMdmProvider.updateLink(mySourcePatientId, myPatientId, MATCH_RESULT, null, myRequestDetails);
|
||||
myMdmProvider.updateLink(mySourcePatientId, myPatientId, MATCH_RESULT, 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"));
|
||||
|
@ -118,7 +97,7 @@ public class MdmProviderUpdateLinkR4Test extends BaseLinkR4Test {
|
|||
@Test
|
||||
public void testUpdateIllegalResultForPossibleMatch() {
|
||||
try {
|
||||
myMdmProvider.updateLink(mySourcePatientId, myPatientId, POSSIBLE_MATCH_RESULT, null, myRequestDetails);
|
||||
myMdmProvider.updateLink(mySourcePatientId, myPatientId, POSSIBLE_MATCH_RESULT, myRequestDetails);
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("$mdm-update-link illegal matchResult value 'POSSIBLE_MATCH'. Must be NO_MATCH or MATCH", e.getMessage());
|
||||
|
@ -128,7 +107,7 @@ public class MdmProviderUpdateLinkR4Test extends BaseLinkR4Test {
|
|||
@Test
|
||||
public void testUpdateIllegalResultPD() {
|
||||
try {
|
||||
myMdmProvider.updateLink(mySourcePatientId, myPatientId, POSSIBLE_DUPLICATE_RESULT, null, myRequestDetails);
|
||||
myMdmProvider.updateLink(mySourcePatientId, myPatientId, POSSIBLE_DUPLICATE_RESULT, myRequestDetails);
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("$mdm-update-link illegal matchResult value 'POSSIBLE_DUPLICATE'. Must be NO_MATCH or MATCH", e.getMessage());
|
||||
|
@ -138,7 +117,7 @@ public class MdmProviderUpdateLinkR4Test extends BaseLinkR4Test {
|
|||
@Test
|
||||
public void testUpdateIllegalSecondArg() {
|
||||
try {
|
||||
myMdmProvider.updateLink(myPatientId, new StringType(""), NO_MATCH_RESULT, null, myRequestDetails);
|
||||
myMdmProvider.updateLink(myPatientId, new StringType(""), NO_MATCH_RESULT, 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"));
|
||||
|
@ -148,7 +127,7 @@ public class MdmProviderUpdateLinkR4Test extends BaseLinkR4Test {
|
|||
@Test
|
||||
public void testUpdateIllegalFirstArg() {
|
||||
try {
|
||||
myMdmProvider.updateLink(new StringType(""), myPatientId, NO_MATCH_RESULT, null, myRequestDetails);
|
||||
myMdmProvider.updateLink(new StringType(""), myPatientId, NO_MATCH_RESULT, myRequestDetails);
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertThat(e.getMessage(), endsWith(" must have form <resourceType>/<id> where <id> is the id of the resource"));
|
||||
|
@ -158,7 +137,7 @@ public class MdmProviderUpdateLinkR4Test extends BaseLinkR4Test {
|
|||
@Test
|
||||
public void testAttemptingToModifyANonExistentLinkFails() {
|
||||
try {
|
||||
myMdmProvider.updateLink(mySourcePatientId, mySourcePatientId, NO_MATCH_RESULT, null, myRequestDetails);
|
||||
myMdmProvider.updateLink(mySourcePatientId, mySourcePatientId, NO_MATCH_RESULT, myRequestDetails);
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertThat(e.getMessage(), startsWith("No link"));
|
||||
|
@ -169,7 +148,7 @@ public class MdmProviderUpdateLinkR4Test extends BaseLinkR4Test {
|
|||
public void testUpdateStrangePatient() {
|
||||
Patient patient = createPatient();
|
||||
try {
|
||||
myMdmProvider.updateLink(new StringType(patient.getIdElement().getValue()), myPatientId, NO_MATCH_RESULT, null, myRequestDetails);
|
||||
myMdmProvider.updateLink(new StringType(patient.getIdElement().getValue()), myPatientId, NO_MATCH_RESULT, myRequestDetails);
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
String expectedMessage = myMessageHelper.getMessageForUnmanagedResource();
|
||||
|
@ -183,7 +162,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, null, myRequestDetails);
|
||||
myMdmProvider.updateLink(mySourcePatientId, new StringType(patient.getIdElement().getValue()), NO_MATCH_RESULT, myRequestDetails);
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals(myMessageHelper.getMessageForUnsupportedSourceResource(), e.getMessage());
|
||||
|
|
|
@ -159,10 +159,9 @@ 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(), theManuallyMergedResource,
|
||||
return myMdmControllerSvc.updateLink(theGoldenResourceId.getValueAsString(), theResourceId.getValue(), null,
|
||||
theMatchResult.getValue(), createMdmContext(theRequestDetails, MdmTransactionContext.OperationType.UPDATE_LINK,
|
||||
getResourceType(ProviderConstants.MDM_UPDATE_LINK_GOLDEN_RESOURCE_ID, theGoldenResourceId))
|
||||
);
|
||||
|
|
|
@ -241,6 +241,10 @@ public class GoldenResourceHelper {
|
|||
}
|
||||
}
|
||||
|
||||
public boolean hasIdentifier(IBaseResource theResource) {
|
||||
return TerserUtil.hasValues(myFhirContext, theResource, FIELD_NAME_IDENTIFIER);
|
||||
}
|
||||
|
||||
public void mergeIndentifierFields(IBaseResource theFromGoldenResource, IBaseResource theToGoldenResource, MdmTransactionContext theMdmTransactionContext) {
|
||||
TerserUtil.cloneCompositeField(myFhirContext, theFromGoldenResource, theToGoldenResource, FIELD_NAME_IDENTIFIER);
|
||||
}
|
||||
|
|
|
@ -89,6 +89,23 @@ public final class TerserUtil {
|
|||
theIdentifierDefinition.getMutator().addValue(theResourceToCloneEidInto, resourceNewIdentifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the specified fields has any values
|
||||
*
|
||||
* @param theFhirContext Context holding resource definition
|
||||
* @param theResource Resource to check if the specified field is set
|
||||
* @param theFieldName name of the field to check
|
||||
* @return Returns true if field exists and has any values set, and false otherwise
|
||||
*/
|
||||
public static boolean hasValues(FhirContext theFhirContext, IBaseResource theResource, String theFieldName) {
|
||||
RuntimeResourceDefinition resourceDefinition = theFhirContext.getResourceDefinition(theResource);
|
||||
BaseRuntimeChildDefinition resourceIdentifier = resourceDefinition.getChildByName(theFieldName);
|
||||
if (resourceIdentifier == null) {
|
||||
return false;
|
||||
}
|
||||
return !(resourceIdentifier.getAccessor().getValues(theResource).isEmpty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones specified composite field (collection). Composite field values must confirm to the collections
|
||||
* contract.
|
||||
|
@ -237,4 +254,13 @@ public final class TerserUtil {
|
|||
}
|
||||
}
|
||||
|
||||
public static <T extends IBaseResource> T clone(FhirContext theFhirContext, T theInstance) {
|
||||
RuntimeResourceDefinition definition = theFhirContext.getResourceDefinition(theInstance.getClass());
|
||||
T retVal = (T) definition.newInstance();
|
||||
|
||||
FhirTerser terser = theFhirContext.newTerser();
|
||||
terser.cloneInto(theInstance, retVal, true);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue