Survivorship 5

This commit is contained in:
Nick 2021-01-08 19:25:25 -05:00
parent 30ba5822bd
commit 9338a4e73e
13 changed files with 150 additions and 33 deletions

View File

@ -436,7 +436,14 @@ public class IdHelperService {
@Nonnull
public Long getPidOrThrowException(IAnyResource theResource) {
return (Long) theResource.getUserData(RESOURCE_PID);
Long retVal = (Long) theResource.getUserData(RESOURCE_PID);
// FIXME NG Is this the intent?
if (retVal == null) {
throw new IllegalStateException(
String.format("Unable to find %s in the user data for %s with ID %s", RESOURCE_PID, theResource, theResource.getId())
);
}
return retVal;
}
public IIdType resourceIdFromPidOrThrowException(Long thePid) {

View File

@ -83,7 +83,8 @@ public class GoldenResourceMergerSvcImpl implements IGoldenResourceMergerSvc {
//Save the deprecated resource.
myMdmResourceDaoSvc.upsertGoldenResource(theFromGoldenResource, resourceType);
log(theMdmTransactionContext, "Merged " + theFromGoldenResource.getIdElement().toVersionless() + " into " + theToGoldenResource.getIdElement().toVersionless());
log(theMdmTransactionContext, "Merged " + theFromGoldenResource.getIdElement().toVersionless()
+ " into " + theToGoldenResource.getIdElement().toVersionless());
return theToGoldenResource;
}

View File

@ -54,8 +54,16 @@ public class MdmControllerSvcImpl implements IMdmControllerSvc {
IMdmLinkUpdaterSvc myIMdmLinkUpdaterSvc;
@Override
public IAnyResource mergeGoldenResources(String theFromGoldenResourceId, String theToGoldenResourceId, MdmTransactionContext theMdmTransactionContext) {
IAnyResource fromGoldenResource = myMdmControllerHelper.getLatestGoldenResourceFromIdOrThrowException(ProviderConstants.MDM_MERGE_GR_FROM_GOLDEN_RESOURCE_ID, theFromGoldenResourceId);
public IAnyResource mergeGoldenResources(String theFromGoldenResourceId, String theToGoldenResourceId, IAnyResource theFromGoldenResource, MdmTransactionContext theMdmTransactionContext) {
boolean isOverwritingGoldenResource = (theFromGoldenResource != null);
IAnyResource fromGoldenResource;
if (theFromGoldenResource == null) {
fromGoldenResource = myMdmControllerHelper.getLatestGoldenResourceFromIdOrThrowException(ProviderConstants.MDM_MERGE_GR_FROM_GOLDEN_RESOURCE_ID, theFromGoldenResourceId);
} else {
fromGoldenResource = theFromGoldenResource;
}
IAnyResource toGoldenResource = myMdmControllerHelper.getLatestGoldenResourceFromIdOrThrowException(ProviderConstants.MDM_MERGE_GR_TO_GOLDEN_RESOURCE_ID, theToGoldenResourceId);
myMdmControllerHelper.validateMergeResources(fromGoldenResource, toGoldenResource);
myMdmControllerHelper.validateSameVersion(fromGoldenResource, theFromGoldenResourceId);

View File

@ -64,11 +64,17 @@ public class MdmSurvivorshipSvcImpl implements IMdmSurvivorshipService {
*/
@Override
public <T extends IBase> void applySurvivorshipRulesToGoldenResource(T theTargetResource, T theGoldenResource, MdmTransactionContext theMdmTransactionContext) {
// TerserUtil.mergeFields(myFhirContext, (IBaseResource) theTargetResource, (IBaseResource) theGoldenResource, TerserUtil.DEFAULT_EXCLUDED_FIELDS);
if (MdmTransactionContext.OperationType.MERGE_GOLDEN_RESOURCES == theMdmTransactionContext.getRestOperation()) {
TerserUtil.mergeFieldsExceptIdAndMeta(myFhirContext, (IBaseResource) theTargetResource, (IBaseResource) theGoldenResource);
} else {
switch (theMdmTransactionContext.getRestOperation()) {
case MERGE_GOLDEN_RESOURCES:
if (theMdmTransactionContext.isForceResourceUpdate()) {
TerserUtil.overwriteFields(myFhirContext, (IBaseResource) theTargetResource, (IBaseResource) theGoldenResource, TerserUtil.EXCLUDE_IDS_AND_META);
break;
}
TerserUtil.mergeFields(myFhirContext, (IBaseResource) theTargetResource, (IBaseResource) theGoldenResource, TerserUtil.EXCLUDE_IDS_AND_META);
break;
default:
TerserUtil.overwriteFields(myFhirContext, (IBaseResource) theTargetResource, (IBaseResource) theGoldenResource, TerserUtil.EXCLUDE_IDS_AND_META);
break;
}
}
}

View File

@ -139,7 +139,9 @@ abstract public class BaseMdmR4Test extends BaseJpaR4Test {
@Nonnull
protected Patient createGoldenPatient() {
return createPatient(new Patient(), true, false);
Patient patient = new Patient();
patient.setActive(true);
return createPatient(patient, true, false);
}
@Nonnull

View File

@ -35,12 +35,10 @@ public class MdmProviderBatchR4Test extends BaseLinkR4Test {
protected IAnyResource myGoldenMedication;
protected StringType myGoldenMedicationId;
@Autowired
IInterceptorService myInterceptorService;
PointcutLatch afterMdmLatch = new PointcutLatch(Pointcut.MDM_AFTER_PERSISTED_RESOURCE_CHECKED);
@BeforeEach
public void before() {
super.before();
@ -58,7 +56,6 @@ public class MdmProviderBatchR4Test extends BaseLinkR4Test {
myGoldenMedication = getGoldenResourceFromTargetResource(myMedication);
myGoldenMedicationId = new StringType(myGoldenMedication.getIdElement().getValue());
myInterceptorService.registerAnonymousInterceptor(Pointcut.MDM_AFTER_PERSISTED_RESOURCE_CHECKED, afterMdmLatch);
}

View File

@ -38,19 +38,46 @@ public class MdmProviderMergeGoldenResourcesR4Test extends BaseProviderR4Test {
myToGoldenPatientId = new StringType(myToGoldenPatient.getIdElement().getValue());
}
@Test
public void testMergeWithOverride() {
myFromGoldenPatient.getName().clear();
myFromGoldenPatient.addName().setFamily("Family").addGiven("Given");
myFromGoldenPatient.addCommunication();
myFromGoldenPatient.addExtension();
Patient mergedSourcePatient = (Patient) myMdmProvider.mergeGoldenResources(myFromGoldenPatientId,
myToGoldenPatientId, myFromGoldenPatient, myRequestDetails);
assertEquals(myFromGoldenPatient.getName().size(), mergedSourcePatient.getName().size());
assertEquals(myFromGoldenPatient.getName().get(0).getNameAsSingleString(), mergedSourcePatient.getName().get(0).getNameAsSingleString());
assertEquals(myFromGoldenPatient.getCommunication().size(), mergedSourcePatient.getCommunication().size());
assertEquals(myFromGoldenPatient.getExtension().size(), mergedSourcePatient.getExtension().size());
Patient fromSourcePatient = myPatientDao.read(myFromGoldenPatient.getIdElement().toUnqualifiedVersionless());
assertEquals(fromSourcePatient.getName().size(), mergedSourcePatient.getName().size());
assertEquals(fromSourcePatient.getName().get(0).getNameAsSingleString(), mergedSourcePatient.getName().get(0).getNameAsSingleString());
}
@Test
public void testMerge() {
Patient mergedSourcePatient = (Patient) myMdmProvider.mergeGoldenResources(myFromGoldenPatientId,
myToGoldenPatientId, myRequestDetails);
myToGoldenPatientId, null, myRequestDetails);
// we do not check setActive anymore - as not all types support that
assertTrue(MdmResourceUtil.isGoldenRecord(mergedSourcePatient));
assertTrue(!MdmResourceUtil.isGoldenRecordRedirected(mergedSourcePatient));
assertTrue(MdmResourceUtil.isGoldenRecord(myFromGoldenPatient));
assertEquals(myToGoldenPatient.getIdElement(), mergedSourcePatient.getIdElement());
assertThat(mergedSourcePatient, is(sameGoldenResourceAs(myToGoldenPatient)));
assertEquals(1, getAllRedirectedGoldenPatients().size());
assertEquals(1, getAllGoldenPatients().size());
Patient fromSourcePatient = myPatientDao.read(myFromGoldenPatient.getIdElement().toUnqualifiedVersionless());
assertThat(fromSourcePatient.getActive(), is(false));
assertTrue(!MdmResourceUtil.isGoldenRecord(fromSourcePatient));
assertTrue(MdmResourceUtil.isGoldenRecordRedirected(fromSourcePatient));
//TODO GGG eventually this will need to check a redirect... this is a hack which doesnt work
@ -67,12 +94,39 @@ public class MdmProviderMergeGoldenResourcesR4Test extends BaseProviderR4Test {
assertEquals(link.getLinkSource(), MdmLinkSourceEnum.MANUAL);
}
@Test
public void testMergeWithManualOverride() {
Patient mergedSourcePatient = (Patient) myMdmProvider.mergeGoldenResources(myFromGoldenPatientId,
myToGoldenPatientId, myFromGoldenPatient, myRequestDetails);
assertTrue(!MdmResourceUtil.isGoldenRecord(myFromGoldenPatient));
assertTrue(MdmResourceUtil.isGoldenRecordRedirected(myFromGoldenPatient));
assertEquals(myToGoldenPatient.getIdElement(), mergedSourcePatient.getIdElement());
assertThat(mergedSourcePatient, is(sameGoldenResourceAs(myToGoldenPatient)));
assertEquals(1, getAllRedirectedGoldenPatients().size());
assertEquals(1, getAllGoldenPatients().size());
Patient fromSourcePatient = myPatientDao.read(myFromGoldenPatient.getIdElement().toUnqualifiedVersionless());
assertTrue(!MdmResourceUtil.isGoldenRecord(fromSourcePatient));
assertTrue(MdmResourceUtil.isGoldenRecordRedirected(fromSourcePatient));
List<MdmLink> links = myMdmLinkDaoSvc.findMdmLinksBySourceResource(myFromGoldenPatient);
assertThat(links, hasSize(1));
MdmLink link = links.get(0);
assertEquals(link.getSourcePid(), myFromGoldenPatient.getIdElement().toUnqualifiedVersionless().getIdPartAsLong());
assertEquals(link.getGoldenResourcePid(), myToGoldenPatient.getIdElement().toUnqualifiedVersionless().getIdPartAsLong());
assertEquals(link.getMatchResult(), MdmMatchResultEnum.REDIRECT);
assertEquals(link.getLinkSource(), MdmLinkSourceEnum.MANUAL);
}
@Test
public void testUnmanagedMerge() {
StringType fromGoldenResourceId = new StringType(createPatient().getIdElement().getValue());
StringType toGoldenResourceId = new StringType(createPatient().getIdElement().getValue());
try {
myMdmProvider.mergeGoldenResources(fromGoldenResourceId, toGoldenResourceId, myRequestDetails);
myMdmProvider.mergeGoldenResources(fromGoldenResourceId, toGoldenResourceId, null, myRequestDetails);
fail();
} catch (InvalidRequestException e) {
assertEquals("Only MDM managed resources can be merged. MDM managed resources must have the HAPI-MDM tag.", e.getMessage());
@ -82,19 +136,19 @@ public class MdmProviderMergeGoldenResourcesR4Test extends BaseProviderR4Test {
@Test
public void testNullParams() {
try {
myMdmProvider.mergeGoldenResources(null, null, myRequestDetails);
myMdmProvider.mergeGoldenResources(null, null, null, myRequestDetails);
fail();
} catch (InvalidRequestException e) {
assertEquals("fromGoldenResourceId cannot be null", e.getMessage());
}
try {
myMdmProvider.mergeGoldenResources(null, myToGoldenPatientId, myRequestDetails);
myMdmProvider.mergeGoldenResources(null, myToGoldenPatientId, null, myRequestDetails);
fail();
} catch (InvalidRequestException e) {
assertEquals("fromGoldenResourceId cannot be null", e.getMessage());
}
try {
myMdmProvider.mergeGoldenResources(myFromGoldenPatientId, null, myRequestDetails);
myMdmProvider.mergeGoldenResources(myFromGoldenPatientId, null, null, myRequestDetails);
fail();
} catch (InvalidRequestException e) {
assertEquals("toGoldenResourceId cannot be null", e.getMessage());
@ -104,28 +158,49 @@ public class MdmProviderMergeGoldenResourcesR4Test extends BaseProviderR4Test {
@Test
public void testBadParams() {
try {
myMdmProvider.mergeGoldenResources(new StringType("Patient/1"), new StringType("Patient/1"), myRequestDetails);
myMdmProvider.mergeGoldenResources(new StringType("Patient/1"), new StringType("Patient/1"), null, myRequestDetails);
fail();
} catch (InvalidRequestException e) {
assertEquals("fromGoldenResourceId must be different from toGoldenResourceId", e.getMessage());
}
try {
myMdmProvider.mergeGoldenResources(new StringType("Patient/abc"), myToGoldenPatientId, myRequestDetails);
myMdmProvider.mergeGoldenResources(myFromGoldenPatientId, myToGoldenPatientId, new Patient(), myRequestDetails);
fail();
} catch (InvalidRequestException e) {
assertEquals("resource.id cannot be null", e.getMessage());
}
try {
myMdmProvider.mergeGoldenResources(myFromGoldenPatientId, myToGoldenPatientId, myToGoldenPatient, myRequestDetails);
fail();
} catch (InvalidRequestException e) {
assertEquals("resource must be different from the one with toGoldenResourceId", e.getMessage());
}
try {
myMdmProvider.mergeGoldenResources(new StringType("Patient/abc"), myToGoldenPatientId, null, myRequestDetails);
fail();
} catch (ResourceNotFoundException e) {
assertEquals("Resource Patient/abc is not known", e.getMessage());
}
try {
myMdmProvider.mergeGoldenResources(new StringType("Organization/abc"), myToGoldenPatientId, myRequestDetails);
myMdmProvider.mergeGoldenResources(new StringType("Patient/abc"), myToGoldenPatientId, null, myRequestDetails);
fail();
} catch (ResourceNotFoundException e) {
assertEquals("Resource Patient/abc is not known", e.getMessage());
}
try {
myMdmProvider.mergeGoldenResources(new StringType("Organization/abc"), myToGoldenPatientId, null, myRequestDetails);
fail();
} catch (ResourceNotFoundException e) {
assertEquals("Resource Organization/abc is not known", e.getMessage());
}
try {
myMdmProvider.mergeGoldenResources(myFromGoldenPatientId, new StringType("Patient/abc"), myRequestDetails);
myMdmProvider.mergeGoldenResources(myFromGoldenPatientId, new StringType("Patient/abc"), null, myRequestDetails);
fail();
} catch (ResourceNotFoundException e) {
assertEquals("Resource Patient/abc is not known", e.getMessage());

View File

@ -34,7 +34,7 @@ public interface IMdmControllerSvc {
void notDuplicateGoldenResource(String theGoldenResourceId, String theTargetGoldenResourceId, MdmTransactionContext theMdmTransactionContext);
IAnyResource mergeGoldenResources(String theFromGoldenResourceId, String theToGoldenResourceId, MdmTransactionContext theMdmTransactionContext);
IAnyResource mergeGoldenResources(String theFromGoldenResourceId, String theToGoldenResourceId, IAnyResource theFromGoldenResource, MdmTransactionContext theMdmTransactionContext);
IAnyResource updateLink(String theGoldenResourceId, String theSourceResourceId, String theMatchResult, MdmTransactionContext theMdmTransactionContext);
}

View File

@ -44,6 +44,8 @@ public class MdmTransactionContext {
private String myResourceType;
private boolean myForceResourceUpdate;
public TransactionLogMessages getTransactionLogMessages() {
return myTransactionLogMessages;
}
@ -88,4 +90,11 @@ public class MdmTransactionContext {
this.myResourceType = myResourceType;
}
public boolean isForceResourceUpdate() {
return myForceResourceUpdate;
}
public void setForceResourceUpdate(boolean theForceResourceUpdate) {
myForceResourceUpdate = theForceResourceUpdate;
}
}

View File

@ -29,6 +29,7 @@ import ca.uhn.fhir.rest.server.TransactionLogMessages;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.provider.ProviderConstants;
import ca.uhn.fhir.util.ParametersUtil;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
@ -50,6 +51,16 @@ public abstract class BaseMdmProvider {
throw new InvalidRequestException("fromGoldenResourceId must be different from toGoldenResourceId");
}
}
protected void validateOptionalMergeResource(IAnyResource theFromGoldenResource, IPrimitiveType<String> theToGoldenResourceId) {
if (theFromGoldenResource == null) {
return;
}
validateNotNull(ProviderConstants.MDM_MERGE_RESOURCE_ID, theFromGoldenResource.getIdElement());
if (theFromGoldenResource.getIdElement().getValue().equals(theToGoldenResourceId.getValue())) {
throw new InvalidRequestException("resource must be different from the one with toGoldenResourceId");
}
validateMergeParameters(theFromGoldenResource.getIdElement(), theToGoldenResourceId);
}
private void validateNotNull(String theName, IPrimitiveType<String> theString) {
if (theString == null || theString.getValue() == null) {

View File

@ -144,14 +144,15 @@ public class MdmProviderDstu3Plus extends BaseMdmProvider {
@Operation(name = ProviderConstants.MDM_MERGE_GOLDEN_RESOURCES)
public IBaseResource mergeGoldenResources(@OperationParam(name = ProviderConstants.MDM_MERGE_GR_FROM_GOLDEN_RESOURCE_ID, min = 1, max = 1, typeName = "string") IPrimitiveType<String> theFromGoldenResourceId,
@OperationParam(name = ProviderConstants.MDM_MERGE_GR_TO_GOLDEN_RESOURCE_ID, min = 1, max = 1, typeName = "string") IPrimitiveType<String> theToGoldenResourceId,
@OperationParam()
@OperationParam(name = ProviderConstants.MDM_MERGE_RESOURCE, max = 1) IAnyResource theMergedResource,
RequestDetails theRequestDetails) {
validateMergeParameters(theFromGoldenResourceId, theToGoldenResourceId);
validateOptionalMergeResource(theMergedResource, theToGoldenResourceId);
return myMdmControllerSvc.mergeGoldenResources(theFromGoldenResourceId.getValueAsString(), theToGoldenResourceId.getValueAsString(),
createMdmContext(theRequestDetails, MdmTransactionContext.OperationType.MERGE_GOLDEN_RESOURCES,
getResourceType(ProviderConstants.MDM_MERGE_GR_FROM_GOLDEN_RESOURCE_ID, theFromGoldenResourceId))
);
MdmTransactionContext txContext = createMdmContext(theRequestDetails, MdmTransactionContext.OperationType.MERGE_GOLDEN_RESOURCES,
getResourceType(ProviderConstants.MDM_MERGE_GR_FROM_GOLDEN_RESOURCE_ID, theFromGoldenResourceId));
txContext.setForceResourceUpdate(theMergedResource != null);
return myMdmControllerSvc.mergeGoldenResources(theFromGoldenResourceId.getValueAsString(), theToGoldenResourceId.getValueAsString(), theMergedResource, txContext);
}
@Operation(name = ProviderConstants.MDM_UPDATE_LINK)

View File

@ -42,7 +42,7 @@ import static ca.uhn.fhir.mdm.util.GoldenResourceHelper.FIELD_NAME_IDENTIFIER;
public final class TerserUtil {
public static final Collection<String> IDS_AND_META_EXCLUDES =
Collections.unmodifiableSet(Stream.of("id", "meta", "identifier").collect(Collectors.toSet()));
Collections.unmodifiableSet(Stream.of("id", "identifier", "meta", "active").collect(Collectors.toSet()));
public static final Predicate<String> EXCLUDE_IDS_AND_META = new Predicate<String>() {
@Override

View File

@ -62,15 +62,15 @@ public class ProviderConstants {
* EMPI Operations
*/
public static final String EMPI_MATCH = "$match";
//TODO GGG MDM: implement a server-level MDM match to complement the FHIR-spec $match for /Patient
public static final String MDM_MATCH = "$mdm-match";
public static final String MDM_MATCH_RESOURCE = "resource";
public static final String MDM_RESOURCE_TYPE = "resourceType";
//TODO GGG MDM: rename all these vars
public static final String MDM_MERGE_GOLDEN_RESOURCES = "$mdm-merge-golden-resources";
public static final String MDM_MERGE_GR_FROM_GOLDEN_RESOURCE_ID = "fromGoldenResourceId";
public static final String MDM_MERGE_GR_TO_GOLDEN_RESOURCE_ID = "toGoldenResourceId";
public static final String MDM_MERGE_RESOURCE = "resource";
public static final String MDM_MERGE_RESOURCE_ID = "resource.id";
public static final String MDM_UPDATE_LINK = "$mdm-update-link";
public static final String MDM_UPDATE_LINK_GOLDEN_RESOURCE_ID = "goldenResourceId";