Addressed more code review comments

This commit is contained in:
Nick 2020-12-08 15:32:01 -05:00
parent efb429b319
commit 4c7b597983
21 changed files with 174 additions and 347 deletions

View File

@ -25,7 +25,7 @@ import ca.uhn.fhir.mdm.api.MdmConstants;
import ca.uhn.fhir.mdm.api.IMdmSettings;
import ca.uhn.fhir.mdm.model.CanonicalEID;
import ca.uhn.fhir.mdm.util.EIDHelper;
import ca.uhn.fhir.mdm.util.MdmUtil;
import ca.uhn.fhir.mdm.util.MdmResourceUtil;
import ca.uhn.fhir.mdm.util.GoldenResourceHelper;
import ca.uhn.fhir.interceptor.api.Hook;
import ca.uhn.fhir.interceptor.api.Pointcut;
@ -84,7 +84,7 @@ public class MdmStorageInterceptor implements IMdmStorageInterceptor {
forbidIfHasMultipleEids(theUpdatedResource);
}
if (myGoldenResourceHelper.isDeactivated(theUpdatedResource)) {
if (MdmResourceUtil.isGoldenRecordRedirected(theUpdatedResource)) {
ourLog.debug("Deleting MDM links to deactivated Golden resource {}", theUpdatedResource.getIdElement().toUnqualifiedVersionless());
int deleted = myMdmLinkDeleteSvc.deleteNonRedirectWithAnyReferenceTo(theUpdatedResource);
if (deleted > 0) {
@ -131,7 +131,7 @@ public class MdmStorageInterceptor implements IMdmStorageInterceptor {
* Will throw a forbidden error if a request attempts to add/remove the MDM tag on a Resource.
*/
private void forbidModifyingMdmTag(IBaseResource theNewResource, IBaseResource theOldResource) {
if (MdmUtil.isMdmManaged(theNewResource) != MdmUtil.isMdmManaged(theOldResource)) {
if (MdmResourceUtil.isMdmManaged(theNewResource) != MdmResourceUtil.isMdmManaged(theOldResource)) {
throwBlockMdmManagedTagChange();
}
}
@ -153,10 +153,10 @@ public class MdmStorageInterceptor implements IMdmStorageInterceptor {
}
private void forbidIfMdmManagedTagIsPresent(IBaseResource theResource) {
if (MdmUtil.isMdmManaged(theResource)) {
if (MdmResourceUtil.isMdmManaged(theResource)) {
throwModificationBlockedByMdm();
}
if (MdmUtil.hasGoldenRecordSystemTag(theResource)) {
if (MdmResourceUtil.hasGoldenRecordSystemTag(theResource)) {
throwModificationBlockedByMdm();
}
}

View File

@ -30,6 +30,7 @@ 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.rest.server.exceptions.InvalidRequestException;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.slf4j.Logger;
@ -77,7 +78,7 @@ public class GoldenResourceMergerSvcImpl implements IGoldenResourceMergerSvc {
myMdmResourceDaoSvc.removeGoldenResourceTag(theFromGoldenResource, resourceType);
//Add the REDIRECT tag to that same deprecated resource.
myGoldenResourceHelper.deactivateResource(theFromGoldenResource);
MdmResourceUtil.setGoldenResourceRedirected(theFromGoldenResource);
//Save the deprecated resource.
myMdmResourceDaoSvc.upsertGoldenResource(theFromGoldenResource, resourceType);

View File

@ -28,7 +28,7 @@ import ca.uhn.fhir.mdm.api.IMdmLinkUpdaterSvc;
import ca.uhn.fhir.mdm.api.IMdmSettings;
import ca.uhn.fhir.mdm.log.Logs;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.mdm.util.MdmUtil;
import ca.uhn.fhir.mdm.util.MdmResourceUtil;
import ca.uhn.fhir.mdm.util.MessageHelper;
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc;
@ -117,11 +117,11 @@ public class MdmLinkUpdaterSvcImpl implements IMdmLinkUpdaterSvc {
throw new InvalidRequestException(myMessageHelper.getMessageForArgumentTypeMismatchInUpdate(goldenRecordType, theSourceType));
}
if (!MdmUtil.isMdmManaged(theGoldenRecord)) {
if (!MdmResourceUtil.isMdmManaged(theGoldenRecord)) {
throw new InvalidRequestException(myMessageHelper.getMessageForUnmanagedResource());
}
if (!MdmUtil.isMdmAllowed(theSourceResource)) {
if (!MdmResourceUtil.isMdmAllowed(theSourceResource)) {
throw new InvalidRequestException(myMessageHelper.getMessageForUnsupportedSourceResource());
}
}
@ -158,7 +158,7 @@ public class MdmLinkUpdaterSvcImpl implements IMdmLinkUpdaterSvc {
throw new InvalidRequestException("First argument to " + ProviderConstants.MDM_UPDATE_LINK + " must be the same resource type as the second argument. Was " + goldenResourceType + "/" + targetType);
}
if (!MdmUtil.isMdmManaged(theGoldenResource) || !MdmUtil.isMdmManaged(theTarget)) {
if (!MdmResourceUtil.isMdmManaged(theGoldenResource) || !MdmResourceUtil.isMdmManaged(theTarget)) {
throw new InvalidRequestException("Only MDM Managed Golden Resources may be updated via this operation. The resource provided is not tagged as managed by HAPI-MDM");
}
}

View File

@ -25,7 +25,7 @@ import ca.uhn.fhir.mdm.api.MdmMatchOutcome;
import ca.uhn.fhir.mdm.api.IMdmLinkSvc;
import ca.uhn.fhir.mdm.log.Logs;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.mdm.util.MdmUtil;
import ca.uhn.fhir.mdm.util.MdmResourceUtil;
import ca.uhn.fhir.mdm.util.GoldenResourceHelper;
import ca.uhn.fhir.jpa.mdm.svc.candidate.CandidateList;
import ca.uhn.fhir.jpa.mdm.svc.candidate.MdmGoldenResourceFindingSvc;
@ -67,7 +67,7 @@ public class MdmMatchLinkSvc {
* @return an {@link TransactionLogMessages} which contains all informational messages related to MDM processing of this resource.
*/
public MdmTransactionContext updateMdmLinksForMdmSource(IAnyResource theResource, MdmTransactionContext theMdmTransactionContext) {
if (MdmUtil.isMdmAllowed(theResource)) {
if (MdmResourceUtil.isMdmAllowed(theResource)) {
return doMdmUpdate(theResource, theMdmTransactionContext);
} else {
return null;

View File

@ -24,7 +24,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.mdm.api.IMdmSettings;
import ca.uhn.fhir.mdm.log.Logs;
import ca.uhn.fhir.mdm.rules.json.MdmResourceSearchParamJson;
import ca.uhn.fhir.mdm.util.MdmUtil;
import ca.uhn.fhir.mdm.util.MdmResourceUtil;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
@ -56,7 +56,7 @@ public class MdmResourceFilteringSvc {
* @return whether or not MDM processing should proceed
*/
public boolean shouldBeProcessed(IAnyResource theResource) {
if (MdmUtil.isMdmManaged(theResource)) {
if (MdmResourceUtil.isMdmManaged(theResource)) {
ourLog.debug("MDM Message handler is dropping [{}] as it is MDM-managed.", theResource);
return false;
}

View File

@ -30,7 +30,7 @@ import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
import ca.uhn.fhir.mdm.rules.svc.MdmResourceMatcherSvc;
import ca.uhn.fhir.mdm.util.EIDHelper;
import ca.uhn.fhir.mdm.util.MdmUtil;
import ca.uhn.fhir.mdm.util.MdmResourceUtil;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
@ -160,11 +160,11 @@ abstract public class BaseMdmR4Test extends BaseJpaR4Test {
@Nonnull
protected Patient createPatient(Patient thePatient, boolean theMdmManaged, boolean isRedirect) {
if (theMdmManaged) {
MdmUtil.setMdmManaged(thePatient);
MdmResourceUtil.setMdmManaged(thePatient);
if (isRedirect) {
MdmUtil.setGoldenResourceRedirected(thePatient);
MdmResourceUtil.setGoldenResourceRedirected(thePatient);
} else {
MdmUtil.setGoldenResource(thePatient);
MdmResourceUtil.setGoldenResource(thePatient);
}
}

View File

@ -1,7 +1,7 @@
package ca.uhn.fhir.jpa.mdm.matcher;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.mdm.util.MdmUtil;
import ca.uhn.fhir.mdm.util.MdmResourceUtil;
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc;
import ca.uhn.fhir.jpa.entity.MdmLink;
@ -36,7 +36,7 @@ public abstract class BaseGoldenResourceMatcher extends TypeSafeMatcher<IAnyReso
protected Long getMatchedResourcePidFromResource(IAnyResource theResource) {
Long retval;
boolean isGoldenRecord = MdmUtil.isMdmManaged(theResource);
boolean isGoldenRecord = MdmResourceUtil.isMdmManaged(theResource);
if (isGoldenRecord) {
return myIdHelperService.getPidOrNull(theResource);
}

View File

@ -3,7 +3,7 @@ package ca.uhn.fhir.jpa.mdm.provider;
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.MdmUtil;
import ca.uhn.fhir.mdm.util.MdmResourceUtil;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import org.hl7.fhir.r4.model.Patient;
@ -43,7 +43,7 @@ public class MdmProviderMergeGoldenResourcesR4Test extends BaseProviderR4Test {
Patient mergedSourcePatient = (Patient) myMdmProviderR4.mergeGoldenResources(myFromGoldenPatientId,
myToGoldenPatientId, myRequestDetails);
assertTrue(MdmUtil.isGoldenRecord(myFromGoldenPatient));
assertTrue(MdmResourceUtil.isGoldenRecord(myFromGoldenPatient));
assertEquals(myToGoldenPatient.getIdElement(), mergedSourcePatient.getIdElement());
assertThat(mergedSourcePatient, is(sameGoldenResourceAs(myToGoldenPatient)));
assertEquals(1, getAllRedirectedGoldenPatients().size());
@ -51,7 +51,7 @@ public class MdmProviderMergeGoldenResourcesR4Test extends BaseProviderR4Test {
Patient fromSourcePatient = myPatientDao.read(myFromGoldenPatient.getIdElement().toUnqualifiedVersionless());
assertThat(fromSourcePatient.getActive(), is(false));
assertTrue(MdmUtil.isGoldenRecordRedirected(fromSourcePatient));
assertTrue(MdmResourceUtil.isGoldenRecordRedirected(fromSourcePatient));
//TODO GGG eventually this will need to check a redirect... this is a hack which doesnt work
// Optional<Identifier> redirect = fromSourcePatient.getIdentifier().stream().filter(theIdentifier -> theIdentifier.getSystem().equals("REDIRECT")).findFirst();

View File

@ -10,7 +10,7 @@ import ca.uhn.fhir.mdm.api.MdmMatchOutcome;
import ca.uhn.fhir.mdm.model.CanonicalEID;
import ca.uhn.fhir.mdm.util.EIDHelper;
import ca.uhn.fhir.mdm.util.GoldenResourceHelper;
import ca.uhn.fhir.mdm.util.MdmUtil;
import ca.uhn.fhir.mdm.util.MdmResourceUtil;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.TokenParam;
import org.hl7.fhir.instance.model.api.IAnyResource;
@ -446,7 +446,7 @@ public class MdmMatchLinkSvcTest extends BaseMdmR4Test {
Patient janeGoldenResourcePatient = myGoldenResourceHelper.createGoldenResourceFromMdmSourceResource(janePatient);
// golden record now contains HAPI-generated EID and HAPI tag
assertTrue(MdmUtil.isMdmManaged(janeGoldenResourcePatient));
assertTrue(MdmResourceUtil.isMdmManaged(janeGoldenResourcePatient));
assertFalse(myEidHelper.getHapiEid(janeGoldenResourcePatient).isEmpty());
// original checks - verifies that EIDs are assigned

View File

@ -1,7 +1,7 @@
package ca.uhn.fhir.jpa.mdm.svc;
import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
import ca.uhn.fhir.mdm.util.MdmUtil;
import ca.uhn.fhir.mdm.util.MdmResourceUtil;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.r4.model.Patient;
import org.junit.jupiter.api.Test;
@ -26,7 +26,7 @@ public class MdmResourceDaoSvcTest extends BaseMdmR4Test {
Patient badSourcePatient = addExternalEID(createRedirectedGoldenPatient(new Patient()), TEST_EID);
MdmUtil.setGoldenResourceRedirected(badSourcePatient);
MdmResourceUtil.setGoldenResourceRedirected(badSourcePatient);
myPatientDao.update(badSourcePatient);
Optional<IAnyResource> foundGoldenResource = myResourceDaoSvc.searchGoldenResourceByEID(TEST_EID, "Patient");

View File

@ -1,44 +0,0 @@
package ca.uhn.fhir.mdm.model;
/*-
* #%L
* HAPI FHIR - Master Data Management
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import org.hl7.fhir.r4.model.Person;
public enum CanonicalIdentityAssuranceLevel {
LEVEL1("level1"),
LEVEL2("level2"),
LEVEL3("level3"),
LEVEL4("level4");
private String myCanonicalLevel;
private CanonicalIdentityAssuranceLevel(String theCanonicalLevel) {
myCanonicalLevel = theCanonicalLevel;
}
public Person.IdentityAssuranceLevel toR4() {
return Person.IdentityAssuranceLevel.fromCode(myCanonicalLevel);
}
public org.hl7.fhir.dstu3.model.Person.IdentityAssuranceLevel toDstu3() {
return org.hl7.fhir.dstu3.model.Person.IdentityAssuranceLevel.fromCode(myCanonicalLevel);
}
}

View File

@ -22,7 +22,7 @@ package ca.uhn.fhir.mdm.provider;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.mdm.api.IMdmSettings;
import ca.uhn.fhir.mdm.util.MdmUtil;
import ca.uhn.fhir.mdm.util.MdmResourceUtil;
import ca.uhn.fhir.mdm.util.MessageHelper;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
@ -94,7 +94,7 @@ public class MdmControllerHelper {
);
}
if (!MdmUtil.isMdmManaged(theResource)) {
if (!MdmResourceUtil.isMdmManaged(theResource)) {
throw new InvalidRequestException(myMessageHelper.getMessageForUnmanagedResource());
}
}

View File

@ -1,72 +0,0 @@
package ca.uhn.fhir.mdm.util;
/*-
* #%L
* HAPI FHIR - Master Data Management
* %%
* Copyright (C) 2014 - 2020 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.mdm.model.CanonicalIdentityAssuranceLevel;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
/**
* Helper class to determine assurance level based on Link Source and Match Result.
* This is strictly for use in populating Golden Resource links.
*/
public final class AssuranceLevelUtil {
private AssuranceLevelUtil() {
}
public static CanonicalIdentityAssuranceLevel getAssuranceLevel(MdmMatchResultEnum theMatchResult, MdmLinkSourceEnum theSource) {
switch (theSource) {
case MANUAL:
return getAssuranceFromManualResult(theMatchResult);
case AUTO:
return getAssuranceFromAutoResult(theMatchResult);
}
throw new InvalidRequestException("Couldn't figure out an assurance level for result: " + theMatchResult + " and source " + theSource);
}
private static CanonicalIdentityAssuranceLevel getAssuranceFromAutoResult(MdmMatchResultEnum theMatchResult) {
switch (theMatchResult) {
case MATCH:
return CanonicalIdentityAssuranceLevel.LEVEL2;
case POSSIBLE_MATCH:
return CanonicalIdentityAssuranceLevel.LEVEL1;
case POSSIBLE_DUPLICATE:
case NO_MATCH:
default:
throw new InvalidRequestException("An AUTO MDM Link may not have a match result of " + theMatchResult);
}
}
private static CanonicalIdentityAssuranceLevel getAssuranceFromManualResult(MdmMatchResultEnum theMatchResult) {
switch (theMatchResult) {
case MATCH:
case REDIRECT:
return CanonicalIdentityAssuranceLevel.LEVEL3;
case NO_MATCH:
case POSSIBLE_DUPLICATE:
case POSSIBLE_MATCH:
default:
throw new InvalidRequestException("A MANUAL MDM Link may not have a match result of " + theMatchResult);
}
}
}

View File

@ -35,7 +35,6 @@ import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r4.model.BooleanType;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@ -54,7 +53,7 @@ public class GoldenResourceHelper {
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
private static final String FIELD_NAME_IDENTIFIER = "identifier";
static final String FIELD_NAME_IDENTIFIER = "identifier";
@Autowired
private IMdmSettings myMdmSettings;
@ -90,8 +89,8 @@ public class GoldenResourceHelper {
addHapiEidIfNoExternalEidIsPresent(newGoldenResource, goldenResourceIdentifier, theIncomingResource);
MdmUtil.setMdmManaged(newGoldenResource);
MdmUtil.setGoldenResource(newGoldenResource);
MdmResourceUtil.setMdmManaged(newGoldenResource);
MdmResourceUtil.setGoldenResource(newGoldenResource);
return (T) newGoldenResource;
}
@ -109,69 +108,10 @@ public class GoldenResourceHelper {
}
CanonicalEID hapiEid = myEIDHelper.createHapiEid();
theGoldenResourceIdentifier.getMutator().addValue(theNewGoldenResource, toId(hapiEid));
theGoldenResourceIdentifier.getMutator().addValue(theNewGoldenResource, IdentifierUtil.toId(myFhirContext, hapiEid));
// set identifier on the source resource
cloneEidIntoResource(theSourceResource, hapiEid);
}
private void cloneEidIntoResource(IBaseResource theResourceToCloneInto, CanonicalEID theEid) {
// get a ref to the actual ID Field
RuntimeResourceDefinition resourceDefinition = myFhirContext.getResourceDefinition(theResourceToCloneInto);
// hapi has 2 metamodels: for children and types
BaseRuntimeChildDefinition resourceIdentifier = resourceDefinition.getChildByName(FIELD_NAME_IDENTIFIER);
cloneEidIntoResource(resourceIdentifier, toId(theEid), theResourceToCloneInto);
}
/**
* Given an Child Definition of `identifier`, a R4/DSTU3 EID Identifier, and a new resource, clone the EID into that resources' identifier list.
*/
private void cloneEidIntoResource(BaseRuntimeChildDefinition theIdentifierDefinition, IBase theEid, IBase theResourceToCloneEidInto) {
// FHIR choice types - fields within fhir where we have a choice of ids
BaseRuntimeElementCompositeDefinition<?> childIdentifier = (BaseRuntimeElementCompositeDefinition<?>) theIdentifierDefinition.getChildByName(FIELD_NAME_IDENTIFIER);
IBase resourceNewIdentifier = childIdentifier.newInstance();
FhirTerser terser = myFhirContext.newTerser();
terser.cloneInto(theEid, resourceNewIdentifier, true);
theIdentifierDefinition.getMutator().addValue(theResourceToCloneEidInto, resourceNewIdentifier);
}
/**
* Clones specified composite field (collection). Composite field values must confirm to the collections
* contract.
*
* @param theFrom Resource to clone the specified filed from
* @param theTo Resource to clone the specified filed to
* @param field Field name to be copied
*/
private void cloneCompositeField(IBaseResource theFrom, IBaseResource theTo, String field) {
FhirTerser terser = myFhirContext.newTerser();
RuntimeResourceDefinition definition = myFhirContext.getResourceDefinition(theFrom);
BaseRuntimeChildDefinition childDefinition = definition.getChildByName(field);
IFhirPath fhirPath = myFhirContext.newFhirPath();
List<IBase> theFromFieldValues = childDefinition.getAccessor().getValues(theFrom);
List<IBase> theToFieldValues = childDefinition.getAccessor().getValues(theTo);
for (IBase theFromFieldValue : theFromFieldValues) {
if (contains(theFromFieldValue, theToFieldValues)) {
continue;
}
BaseRuntimeElementCompositeDefinition<?> compositeDefinition = (BaseRuntimeElementCompositeDefinition<?>) childDefinition.getChildByName(field);
IBase newFieldValue = compositeDefinition.newInstance();
terser.cloneInto(theFromFieldValue, newFieldValue, true);
theToFieldValues.add(newFieldValue);
}
}
private boolean contains(IBase theItem, List<IBase> theItems) {
PrimitiveTypeComparingPredicate predicate = new PrimitiveTypeComparingPredicate();
return theItems.stream().filter(i -> {
return predicate.test(i, theItem);
}).findFirst().isPresent();
TerserUtil.cloneEidIntoResource(myFhirContext, theSourceResource, hapiEid);
}
private void cloneAllExternalEidsIntoNewGoldenResource(BaseRuntimeChildDefinition theGoldenResourceIdentifier,
@ -186,7 +126,7 @@ public class GoldenResourceHelper {
String mdmSystem = myMdmSettings.getMdmRules().getEnterpriseEIDSystem();
String baseSystem = system.get().getValueAsString();
if (Objects.equals(baseSystem, mdmSystem)) {
cloneEidIntoResource(theGoldenResourceIdentifier, base, theNewGoldenResource);
TerserUtil.cloneEidIntoResource(myFhirContext, theGoldenResourceIdentifier, base, theNewGoldenResource);
ourLog.debug("System {} differs from system in the MDM rules {}", baseSystem, mdmSystem);
}
} else {
@ -292,45 +232,14 @@ public class GoldenResourceHelper {
if (goldenResourceExternalEids.contains(incomingExternalEid)) {
continue;
} else {
cloneEidIntoResource(theGoldenResource, incomingExternalEid);
TerserUtil.cloneEidIntoResource(myFhirContext, theGoldenResource, incomingExternalEid);
}
}
}
private <T extends IBase> T toId(CanonicalEID eid) {
switch (myFhirContext.getVersion().getVersion()) {
case R4:
return (T) eid.toR4();
case DSTU3:
return (T) eid.toDSTU3();
}
throw new IllegalStateException("Unsupported FHIR version " + myFhirContext.getVersion().getVersion());
}
private <T extends IBase> T toBooleanType(boolean theFlag) {
switch (myFhirContext.getVersion().getVersion()) {
case R4:
return (T) new BooleanType(theFlag);
case DSTU3:
return (T) new org.hl7.fhir.dstu3.model.BooleanType(theFlag);
}
throw new IllegalStateException("Unsupported FHIR version " + myFhirContext.getVersion().getVersion());
}
private <T extends IBase> boolean fromBooleanType(T theFlag) {
switch (myFhirContext.getVersion().getVersion()) {
case R4:
return ((BooleanType) theFlag).booleanValue();
case DSTU3:
return ((org.hl7.fhir.dstu3.model.BooleanType) theFlag).booleanValue();
}
throw new IllegalStateException("Unsupported FHIR version " + myFhirContext.getVersion().getVersion());
}
public void mergeFields(IBaseResource theFromGoldenResource, IBaseResource theToGoldenResource) {
// TODO NG - Revisit when merge rules are defined
cloneCompositeField(theFromGoldenResource, theToGoldenResource, FIELD_NAME_IDENTIFIER);
TerserUtil.cloneCompositeField(myFhirContext, theFromGoldenResource, theToGoldenResource, FIELD_NAME_IDENTIFIER);
// switch (myFhirContext.getVersion().getVersion()) {
// case R4:
@ -366,12 +275,4 @@ public class GoldenResourceHelper {
updateGoldenResourceExternalEidFromSourceResource(theGoldenResource, theSourceResource, theMdmTransactionContext);
}
}
public void deactivateResource(IAnyResource theResource) {
MdmUtil.setGoldenResourceRedirected(theResource);
}
public boolean isDeactivated(IBaseResource theGoldenResource) {
return MdmUtil.isGoldenRecordRedirected(theGoldenResource);
}
}

View File

@ -20,10 +20,15 @@ package ca.uhn.fhir.mdm.util;
* #L%
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.mdm.model.CanonicalEID;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import org.hl7.fhir.instance.model.api.IBase;
public class IdentifierUtil {
public final class IdentifierUtil {
private IdentifierUtil() {
}
public static CanonicalIdentifier identifierDtFromIdentifier(IBase theIdentifier) {
CanonicalIdentifier retval = new CanonicalIdentifier();
@ -43,4 +48,22 @@ public class IdentifierUtil {
}
return retval;
}
/**
* Retrieves appropriate FHIR Identifier model instance based on the context version
*
* @param theFhirContext FHIR context to use for determining the identifier version
* @param eid EID to get equivalent FHIR Identifier from
* @param <T> Generic Identifier base interface
* @return Returns appropriate R4 or DSTU3 Identifier instance
*/
public static <T extends IBase> T toId(FhirContext theFhirContext, CanonicalEID eid) {
switch (theFhirContext.getVersion().getVersion()) {
case R4:
return (T) eid.toR4();
case DSTU3:
return (T) eid.toDSTU3();
}
throw new IllegalStateException("Unsupported FHIR version " + theFhirContext.getVersion().getVersion());
}
}

View File

@ -27,9 +27,9 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
import javax.annotation.Nonnull;
import java.util.Optional;
public final class MdmUtil {
public final class MdmResourceUtil {
private MdmUtil() {
private MdmResourceUtil() {
}
/**

View File

@ -29,7 +29,11 @@ import org.hl7.fhir.r4.model.PrimitiveType;
import java.util.List;
import java.util.stream.Collectors;
public class NameUtil {
public final class NameUtil {
private NameUtil() {
}
public static List<String> extractGivenNames(FhirContext theFhirContext, IBase theBase) {
switch(theFhirContext.getVersion().getVersion()) {
case R4:

View File

@ -25,7 +25,7 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType;
import java.lang.reflect.Field;
import java.util.function.BiPredicate;
public class PrimitiveTypeComparingPredicate implements BiPredicate {
public class PrimitiveTypeEqualsPredicate implements BiPredicate {
@Override
public boolean test(Object theBase1, Object theBase2) {

View File

@ -0,0 +1,86 @@
package ca.uhn.fhir.mdm.util;
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.mdm.model.CanonicalEID;
import ca.uhn.fhir.util.FhirTerser;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseResource;
import java.util.List;
import static ca.uhn.fhir.mdm.util.GoldenResourceHelper.FIELD_NAME_IDENTIFIER;
final class TerserUtil {
private TerserUtil() {
}
/**
* Clones the specified canonical EID into the identifier field on the resource
*
* @param theFhirContext Context to pull resource definitions from
* @param theResourceToCloneInto Resource to set the EID on
* @param theEid EID to be set
*/
public static void cloneEidIntoResource(FhirContext theFhirContext, IBaseResource theResourceToCloneInto, CanonicalEID theEid) {
// get a ref to the actual ID Field
RuntimeResourceDefinition resourceDefinition = theFhirContext.getResourceDefinition(theResourceToCloneInto);
// hapi has 2 metamodels: for children and types
BaseRuntimeChildDefinition resourceIdentifier = resourceDefinition.getChildByName(FIELD_NAME_IDENTIFIER);
cloneEidIntoResource(theFhirContext, resourceIdentifier, IdentifierUtil.toId(theFhirContext, theEid), theResourceToCloneInto);
}
/**
* Given an Child Definition of `identifier`, a R4/DSTU3 EID Identifier, and a new resource, clone the EID into that resources' identifier list.
*/
public static void cloneEidIntoResource(FhirContext theFhirContext, BaseRuntimeChildDefinition theIdentifierDefinition, IBase theEid, IBase theResourceToCloneEidInto) {
// FHIR choice types - fields within fhir where we have a choice of ids
BaseRuntimeElementCompositeDefinition<?> childIdentifier = (BaseRuntimeElementCompositeDefinition<?>) theIdentifierDefinition.getChildByName(FIELD_NAME_IDENTIFIER);
IBase resourceNewIdentifier = childIdentifier.newInstance();
FhirTerser terser = theFhirContext.newTerser();
terser.cloneInto(theEid, resourceNewIdentifier, true);
theIdentifierDefinition.getMutator().addValue(theResourceToCloneEidInto, resourceNewIdentifier);
}
/**
* Clones specified composite field (collection). Composite field values must confirm to the collections
* contract.
*
* @param theFrom Resource to clone the specified filed from
* @param theTo Resource to clone the specified filed to
* @param field Field name to be copied
*/
public static void cloneCompositeField(FhirContext theFhirContext, IBaseResource theFrom, IBaseResource theTo, String field) {
FhirTerser terser = theFhirContext.newTerser();
RuntimeResourceDefinition definition = theFhirContext.getResourceDefinition(theFrom);
BaseRuntimeChildDefinition childDefinition = definition.getChildByName(field);
List<IBase> theFromFieldValues = childDefinition.getAccessor().getValues(theFrom);
List<IBase> theToFieldValues = childDefinition.getAccessor().getValues(theTo);
for (IBase theFromFieldValue : theFromFieldValues) {
if (contains(theFromFieldValue, theToFieldValues)) {
continue;
}
BaseRuntimeElementCompositeDefinition<?> compositeDefinition = (BaseRuntimeElementCompositeDefinition<?>) childDefinition.getChildByName(field);
IBase newFieldValue = compositeDefinition.newInstance();
terser.cloneInto(theFromFieldValue, newFieldValue, true);
theToFieldValues.add(newFieldValue);
}
}
private static boolean contains(IBase theItem, List<IBase> theItems) {
PrimitiveTypeEqualsPredicate predicate = new PrimitiveTypeEqualsPredicate();
return theItems.stream().anyMatch(i -> {
return predicate.test(i, theItem);
});
}
}

View File

@ -1,65 +0,0 @@
package ca.uhn.fhir.mdm.util;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import org.junit.jupiter.api.Test;
import static ca.uhn.fhir.mdm.api.MdmLinkSourceEnum.AUTO;
import static ca.uhn.fhir.mdm.api.MdmLinkSourceEnum.MANUAL;
import static ca.uhn.fhir.mdm.api.MdmMatchResultEnum.MATCH;
import static ca.uhn.fhir.mdm.api.MdmMatchResultEnum.NO_MATCH;
import static ca.uhn.fhir.mdm.api.MdmMatchResultEnum.POSSIBLE_DUPLICATE;
import static ca.uhn.fhir.mdm.api.MdmMatchResultEnum.POSSIBLE_MATCH;
import static ca.uhn.fhir.mdm.model.CanonicalIdentityAssuranceLevel.LEVEL1;
import static ca.uhn.fhir.mdm.model.CanonicalIdentityAssuranceLevel.LEVEL2;
import static ca.uhn.fhir.mdm.model.CanonicalIdentityAssuranceLevel.LEVEL3;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
public class AssuranceLevelUtilTest {
@Test
public void testValidPersonLinkLevels() {
assertThat(AssuranceLevelUtil.getAssuranceLevel(POSSIBLE_MATCH, AUTO), is(equalTo(LEVEL1)));
assertThat(AssuranceLevelUtil.getAssuranceLevel(MATCH, AUTO), is(equalTo(LEVEL2)));
assertThat(AssuranceLevelUtil.getAssuranceLevel(MATCH, MANUAL), is(equalTo(LEVEL3)));
}
@Test
public void testInvalidPersonLinkLevels() {
try {
AssuranceLevelUtil.getAssuranceLevel(NO_MATCH, AUTO);
fail();
} catch (InvalidRequestException e) {
assertEquals("An AUTO MDM Link may not have a match result of NO_MATCH", e.getMessage());
}
try {
AssuranceLevelUtil.getAssuranceLevel(POSSIBLE_DUPLICATE, AUTO);
fail();
} catch (InvalidRequestException e) {
assertEquals("An AUTO MDM Link may not have a match result of POSSIBLE_DUPLICATE", e.getMessage());
}
try {
AssuranceLevelUtil.getAssuranceLevel(NO_MATCH, MANUAL);
fail();
} catch (InvalidRequestException e) {
assertEquals("A MANUAL MDM Link may not have a match result of NO_MATCH", e.getMessage());
}
try {
AssuranceLevelUtil.getAssuranceLevel(POSSIBLE_MATCH, MANUAL);
fail();
} catch (InvalidRequestException e) {
assertEquals("A MANUAL MDM Link may not have a match result of POSSIBLE_MATCH", e.getMessage());
}
try {
AssuranceLevelUtil.getAssuranceLevel(POSSIBLE_DUPLICATE, MANUAL);
fail();
} catch (InvalidRequestException e) {
assertEquals("A MANUAL MDM Link may not have a match result of POSSIBLE_DUPLICATE", e.getMessage());
}
}
}

View File

@ -7,8 +7,8 @@ import org.hl7.fhir.r4.model.Address;
import org.hl7.fhir.r4.model.BooleanType;
import org.hl7.fhir.r4.model.DateType;
import org.hl7.fhir.r4.model.Enumerations;
import org.hl7.fhir.r4.model.Identifier;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.Person;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -18,7 +18,7 @@ import java.util.Collections;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
class PrimitiveTypeComparingPredicateTest {
class PrimitiveTypeEqualsPredicateTest {
private static FhirContext myFhirContext;
@ -32,7 +32,7 @@ class PrimitiveTypeComparingPredicateTest {
private IBase myNegativeTest;
private PrimitiveTypeComparingPredicate cut = new PrimitiveTypeComparingPredicate();
private PrimitiveTypeEqualsPredicate cut = new PrimitiveTypeEqualsPredicate();
@BeforeAll
public static void initContext() {
@ -47,10 +47,10 @@ class PrimitiveTypeComparingPredicateTest {
myPositiveTest2 = newPatient();
myPositiveTest3 = newPatient();
Patient patient = newPatient();
patient.setActive(false);
patient.setMultipleBirth(new BooleanType(false));
myNegativeTest = patient;
Patient inactivePatientForNegativeTest = newPatient();
inactivePatientForNegativeTest.setActive(false);
inactivePatientForNegativeTest.setMultipleBirth(new BooleanType(false));
myNegativeTest = inactivePatientForNegativeTest;
}
private Patient newPatient() {
@ -76,29 +76,22 @@ class PrimitiveTypeComparingPredicateTest {
@Test
public void testNegativeMatchOnDifferentTypes() {
Patient patient = newPatient();
Identifier identifier = patient.addIdentifier();
identifier.setValue("TEST_VALUE");
assertFalse(cut.test(myNegativeTest, identifier));
Person person = new Person();
person.addName().addGiven("John");
assertFalse(cut.test(myNegativeTest, person));
}
@Test
public void testSymmetry() {
public void testNulls() {
assertTrue(cut.test(null, null));
assertFalse(cut.test(myPositiveTest1, null));
assertFalse(cut.test(null, myPositiveTest1));
}
@Test
public void testPositiveMatchOnTheSameType() {
assertTrue(cut.test(myPositiveTest1, myPositiveTest2));
assertTrue(cut.test(myPositiveTest2, myPositiveTest1));
}
@Test
public void testReflexivity() {
assertTrue(cut.test(myPositiveTest1, myPositiveTest1));
}
@Test
public void testTransitivity() {
assertTrue(cut.test(myPositiveTest1, myPositiveTest2));
assertTrue(cut.test(myPositiveTest2, myPositiveTest3));
assertTrue(cut.test(myPositiveTest1, myPositiveTest3));
}
}