From 8b69d161bd7ede67a879b08246e44e654838ae2d Mon Sep 17 00:00:00 2001 From: Tadgh Date: Tue, 30 Mar 2021 17:12:55 -0400 Subject: [PATCH] Update docs --- .../uhn/fhir/rest/param/ReferenceParam.java | 2 + .../fhir/docs/server_jpa_mdm/mdm_expansion.md | 28 ++ .../ca/uhn/fhir/jpa/config/BaseConfig.java | 7 +- ...ava => MdmSearchExpandingInterceptor.java} | 23 +- .../jpa/mdm/config/MdmConsumerConfig.java | 6 +- .../MdmSubmitterInterceptorLoader.java | 4 +- .../MdmSearchExpandingInterceptorIT.java | 338 ++---------------- 7 files changed, 92 insertions(+), 316 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa_mdm/mdm_expansion.md rename hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/{MdmSearchExpandingInterceptorInterceptor.java => MdmSearchExpandingInterceptor.java} (73%) diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ReferenceParam.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ReferenceParam.java index 583edaa6731..32cdaf427d0 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ReferenceParam.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/ReferenceParam.java @@ -127,7 +127,9 @@ public class ReferenceParam extends BaseParam /*implements IQueryParameterType*/ myMdmExpand = true; theQualifier = ""; //TODO GGG i probably have to deal with chaining here? like refusing the mdm qualifier if i can detect its chained? + //TODO GGG just throw an error if they try to chain } + String q = theQualifier; if (isNotBlank(q)) { if (q.startsWith(":")) { diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa_mdm/mdm_expansion.md b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa_mdm/mdm_expansion.md new file mode 100644 index 00000000000..c4766cc71d1 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa_mdm/mdm_expansion.md @@ -0,0 +1,28 @@ +# MDM Expansion + +Once you have MDM enabled, and you have many linked resources, it can be useful to search across all resourcess. Let's say you have the following MDM links in your database: +``` +Patient/1 --> Patient/3 +Patient/2 --> Patient/3 +``` +This indicates that both Patient/1 and Patient/2 are MDM-mathed to the same golden resource (Patient/3). +What if you want to get all observations from Patient/1, but also include any observations from all of their linked resources. You could do this by first querying the [$mdm-query-links]() endpoint, and then making a subsequent call like the following +```http request +GET http://example.com:8000/Observation?subject=Patient/1,Patient/2,Patient/3 +``` + +But HAPI-FHIR allows a shorthand for this, by means of a Search Parameter qualifier, as follows: +```http request +GET http://example.com:8000/Observation?subject:mdm=Patient/1 +``` + +This `:mdm` parameter qualifier instructs an interceptor in HAPI fhir to expand the set of resources included in the search by their MDM-matched resources. The two above HTTP requests will return the same result. + +## Enabling MDM Expansion + +On top of needing to instantiate an MDM module, you must enable in the [DaoConfig](/hapi-fhir/apidocs/hapi-fhir-jpaserver-api/ca/uhn/fhir/jpa/api/config/DaoConfig.html) bean, using the [Allow MDM Expansion](/hapi-fhir/apidocs/hapi-fhir-jpaserver-api/ca/uhn/fhir/jpa/api/config/DaoConfig.html#setAllowMdmExpansion(boolean)) property. + +
+It is important to note that enabling this functionality can lead to incorrect data being returned by a request, if your MDM links are incorrect. +
+ diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseConfig.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseConfig.java index 4d40bf04fde..3432af617ce 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseConfig.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseConfig.java @@ -61,7 +61,7 @@ import ca.uhn.fhir.jpa.entity.Search; import ca.uhn.fhir.jpa.graphql.JpaStorageServices; import ca.uhn.fhir.jpa.interceptor.CascadingDeleteInterceptor; import ca.uhn.fhir.jpa.interceptor.JpaConsentContextServices; -import ca.uhn.fhir.jpa.interceptor.MdmSearchExpandingInterceptorInterceptor; +import ca.uhn.fhir.jpa.interceptor.MdmSearchExpandingInterceptor; import ca.uhn.fhir.jpa.interceptor.OverridePathBasedReferentialIntegrityForDeletesInterceptor; import ca.uhn.fhir.rest.server.interceptor.ResponseTerminologyTranslationInterceptor; import ca.uhn.fhir.jpa.interceptor.validation.RepositoryValidatingRuleBuilder; @@ -476,8 +476,9 @@ public abstract class BaseConfig { } @Bean - public MdmSearchExpandingInterceptorInterceptor mdmSearchExpandingInterceptorInterceptor() { - return new MdmSearchExpandingInterceptorInterceptor(); + @Lazy + public MdmSearchExpandingInterceptor mdmSearchExpandingInterceptorInterceptor() { + return new MdmSearchExpandingInterceptor(); } @Bean diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/MdmSearchExpandingInterceptorInterceptor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/MdmSearchExpandingInterceptor.java similarity index 73% rename from hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/MdmSearchExpandingInterceptorInterceptor.java rename to hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/MdmSearchExpandingInterceptor.java index d7fabc86763..63f3d3574bc 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/MdmSearchExpandingInterceptorInterceptor.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/interceptor/MdmSearchExpandingInterceptor.java @@ -23,6 +23,7 @@ package ca.uhn.fhir.jpa.interceptor; import ca.uhn.fhir.interceptor.api.Hook; import ca.uhn.fhir.interceptor.api.Interceptor; import ca.uhn.fhir.interceptor.api.Pointcut; +import ca.uhn.fhir.jpa.api.config.DaoConfig; import ca.uhn.fhir.jpa.dao.mdm.MdmLinkExpandSvc; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.mdm.log.Logs; @@ -30,6 +31,7 @@ import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.param.ReferenceParam; +import joptsimple.internal.Strings; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; @@ -44,17 +46,22 @@ import static org.slf4j.LoggerFactory.getLogger; * by the HAPI FHIR Server with a static hard-coded resource. */ @Interceptor -public class MdmSearchExpandingInterceptorInterceptor { +public class MdmSearchExpandingInterceptor { private static final Logger ourLog = Logs.getMdmTroubleshootingLog(); @Autowired private MdmLinkExpandSvc myMdmLinkExpandSvc; + @Autowired + private DaoConfig myDaoConfig; + @Hook(Pointcut.STORAGE_PRESEARCH_REGISTERED) public void hook(SearchParameterMap theSearchParameterMap) { - for (List> andList : theSearchParameterMap.values()) { - for (List orList : andList) { - expandAnyReferenceParameters(orList); + if (myDaoConfig.isAllowMdmExpansion()) { + for (List> andList : theSearchParameterMap.values()) { + for (List orList : andList) { + expandAnyReferenceParameters(orList); + } } } } @@ -69,10 +76,12 @@ public class MdmSearchExpandingInterceptorInterceptor { if (iQueryParameterType instanceof ReferenceParam) { ReferenceParam refParam = (ReferenceParam) iQueryParameterType; if (refParam.isMdmExpand()) { - Set strings = myMdmLinkExpandSvc.expandMdmBySourceResourceId(new IdDt(refParam.getValue())); - if (!strings.isEmpty()) { + ourLog.debug("Found a reference parameter to expand: {}", refParam.toString()); + Set expandedResourceIds = myMdmLinkExpandSvc.expandMdmBySourceResourceId(new IdDt(refParam.getValue())); + if (!expandedResourceIds.isEmpty()) { + ourLog.debug("Parameter has been expanded to: {}", String.join(", ", expandedResourceIds)); toRemove.add(refParam); - strings.stream().map(resourceId -> new ReferenceParam(refParam.getResourceType() + "/" + resourceId)).forEach(toAdd::add); + expandedResourceIds.stream().map(resourceId -> new ReferenceParam(refParam.getResourceType() + "/" + resourceId)).forEach(toAdd::add); } } } diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmConsumerConfig.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmConsumerConfig.java index 3ebb0dc6f4c..03d4aacd491 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmConsumerConfig.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmConsumerConfig.java @@ -21,7 +21,7 @@ package ca.uhn.fhir.jpa.mdm.config; */ import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.jpa.interceptor.MdmSearchExpandingInterceptorInterceptor; +import ca.uhn.fhir.jpa.interceptor.MdmSearchExpandingInterceptor; import ca.uhn.fhir.jpa.mdm.svc.MdmSurvivorshipSvcImpl; import ca.uhn.fhir.mdm.api.IMdmControllerSvc; import ca.uhn.fhir.mdm.api.IMdmExpungeSvc; @@ -81,8 +81,8 @@ public class MdmConsumerConfig { } @Bean - MdmSearchExpandingInterceptorInterceptor myMdmSearchExpandingInterceptorInterceptor() { - return new MdmSearchExpandingInterceptorInterceptor(); + MdmSearchExpandingInterceptor myMdmSearchExpandingInterceptorInterceptor() { + return new MdmSearchExpandingInterceptor(); } @Bean diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmSubmitterInterceptorLoader.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmSubmitterInterceptorLoader.java index 5d9360bb62d..912d39331ea 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmSubmitterInterceptorLoader.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmSubmitterInterceptorLoader.java @@ -20,7 +20,7 @@ package ca.uhn.fhir.jpa.mdm.interceptor; * #L% */ -import ca.uhn.fhir.jpa.interceptor.MdmSearchExpandingInterceptorInterceptor; +import ca.uhn.fhir.jpa.interceptor.MdmSearchExpandingInterceptor; import ca.uhn.fhir.mdm.api.IMdmSettings; import ca.uhn.fhir.mdm.log.Logs; import ca.uhn.fhir.interceptor.api.IInterceptorService; @@ -42,7 +42,7 @@ public class MdmSubmitterInterceptorLoader { @Autowired private IMdmStorageInterceptor myIMdmStorageInterceptor; @Autowired - private MdmSearchExpandingInterceptorInterceptor myMdmSearchExpandingInterceptorInterceptor; + private MdmSearchExpandingInterceptor myMdmSearchExpandingInterceptorInterceptor; @Autowired private IInterceptorService myInterceptorService; @Autowired diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmSearchExpandingInterceptorIT.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmSearchExpandingInterceptorIT.java index 69a87e09de4..f1f5eeb2216 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmSearchExpandingInterceptorIT.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmSearchExpandingInterceptorIT.java @@ -1,31 +1,17 @@ package ca.uhn.fhir.jpa.mdm.interceptor; +import ca.uhn.fhir.jpa.api.config.DaoConfig; import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome; -import ca.uhn.fhir.jpa.dao.index.IdHelperService; -import ca.uhn.fhir.jpa.entity.MdmLink; import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test; import ca.uhn.fhir.jpa.mdm.helper.MdmHelperConfig; import ca.uhn.fhir.jpa.mdm.helper.MdmHelperR4; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; -import ca.uhn.fhir.mdm.model.CanonicalEID; -import ca.uhn.fhir.mdm.rules.config.MdmSettings; -import ca.uhn.fhir.model.dstu2.resource.Observation; import ca.uhn.fhir.rest.api.server.IBundleProvider; -import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId; import ca.uhn.fhir.rest.param.ReferenceOrListParam; import ca.uhn.fhir.rest.param.ReferenceParam; -import ca.uhn.fhir.rest.param.StringParam; -import ca.uhn.fhir.rest.param.TokenParam; -import ca.uhn.fhir.rest.server.TransactionLogMessages; -import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException; -import org.hl7.fhir.instance.model.api.IAnyResource; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.hl7.fhir.instance.model.api.IIdType; -import org.hl7.fhir.r4.model.Enumerations; -import org.hl7.fhir.r4.model.Medication; -import org.hl7.fhir.r4.model.Organization; -import org.hl7.fhir.r4.model.Patient; -import org.hl7.fhir.r4.model.SearchParameter; +import org.hl7.fhir.r4.model.CodeableConcept; +import org.hl7.fhir.r4.model.Observation; +import org.hl7.fhir.r4.model.Reference; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.slf4j.Logger; @@ -33,24 +19,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.ContextConfiguration; -import java.util.Date; -import java.util.List; - -import static ca.uhn.fhir.mdm.api.MdmConstants.CODE_GOLDEN_RECORD; -import static ca.uhn.fhir.mdm.api.MdmConstants.CODE_GOLDEN_RECORD_REDIRECTED; -import static ca.uhn.fhir.mdm.api.MdmConstants.CODE_HAPI_MDM_MANAGED; -import static ca.uhn.fhir.mdm.api.MdmConstants.SYSTEM_GOLDEN_RECORD_STATUS; -import static ca.uhn.fhir.mdm.api.MdmConstants.SYSTEM_MDM_MANAGED; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.notNullValue; -import static org.hamcrest.CoreMatchers.startsWith; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.hasSize; -import static org.hamcrest.Matchers.nullValue; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.fail; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; import static org.slf4j.LoggerFactory.getLogger; @DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) @@ -63,286 +34,51 @@ public class MdmSearchExpandingInterceptorIT extends BaseMdmR4Test { @Autowired public MdmHelperR4 myMdmHelper; @Autowired - private IdHelperService myIdHelperService; + private DaoConfig myDaoConfig; @Test - public void testCreatePractitioner() throws InterruptedException { + public void testReferenceExpansionWorks() throws InterruptedException { + myDaoConfig.setAllowMdmExpansion(false); MdmHelperR4.OutcomeAndLogMessageWrapper withLatch = myMdmHelper.createWithLatch(addExternalEID(buildJanePatient(), "123")); - IIdType id = withLatch.getDaoMethodOutcome().getId(); - myMdmHelper.createWithLatch(addExternalEID(buildJanePatient(), "123")); - myMdmHelper.createWithLatch(addExternalEID(buildJanePatient(), "123")); - myMdmHelper.createWithLatch(addExternalEID(buildJanePatient(), "123")); + MdmHelperR4.OutcomeAndLogMessageWrapper withLatch1 = myMdmHelper.createWithLatch(addExternalEID(buildJanePatient(), "123")); + MdmHelperR4.OutcomeAndLogMessageWrapper withLatch2 = myMdmHelper.createWithLatch(addExternalEID(buildJanePatient(), "123")); + MdmHelperR4.OutcomeAndLogMessageWrapper withLatch3 = myMdmHelper.createWithLatch(addExternalEID(buildJanePatient(), "123")); + assertLinkCount(4); + String id = withLatch.getDaoMethodOutcome().getId().getIdPart(); + String id1 = withLatch1.getDaoMethodOutcome().getId().getIdPart(); + String id2 = withLatch2.getDaoMethodOutcome().getId().getIdPart(); + String id3 = withLatch3.getDaoMethodOutcome().getId().getIdPart(); + //Create an Observation for each Patient + createObservationWithSubject(id); + createObservationWithSubject(id1); + createObservationWithSubject(id2); + createObservationWithSubject(id3); SearchParameterMap searchParameterMap = new SearchParameterMap(); searchParameterMap.setLoadSynchronous(true); ReferenceOrListParam referenceOrListParam = new ReferenceOrListParam(); - referenceOrListParam.addOr(new ReferenceParam(id.toVersionless()).setMdmExpand(true)); - referenceOrListParam.addOr(new ReferenceParam(id.toVersionless())); - referenceOrListParam.addOr(new ReferenceParam(id.toVersionless())); + referenceOrListParam.addOr(new ReferenceParam("Patient/" + id).setMdmExpand(true)); searchParameterMap.add(Observation.SP_SUBJECT, referenceOrListParam); - searchParameterMap.add(Observation.SP_CATEGORY, new TokenParam("test-1", "test-2")); - searchParameterMap.add(Observation.SP_ENCOUNTER, new ReferenceParam("Encounter/abc")); + //With MDM Expansion disabled, this should return 1 result. + IBundleProvider search = myObservationDao.search(searchParameterMap); + assertThat(search.size(), is(equalTo(1))); - myObservationDao.search(searchParameterMap); - + //Once MDM Expansion is allowed, this should now return 4 resourecs. + myDaoConfig.setAllowMdmExpansion(true); + search = myObservationDao.search(searchParameterMap); + assertThat(search.size(), is(equalTo(4))); } - @Test - public void testSearchExpandingInterceptorWorks() { - SearchParameterMap subject = new SearchParameterMap("subject", new ReferenceParam("Patient/123").setMdmExpand(true)).setLoadSynchronous(true); - myObservationDao.search(subject); + private Observation createObservationWithSubject(String thePatientId) { + Observation observation = new Observation(); + observation.setSubject(new Reference("Patient/" + thePatientId)); + observation.setCode(new CodeableConcept().setText("Made for Patient/" + thePatientId)); + DaoMethodOutcome daoMethodOutcome = myObservationDao.create(observation); + return (Observation) daoMethodOutcome.getResource(); + } - - @Test - public void testDeleteGoldenResourceDeletesLinks() throws InterruptedException { - myMdmHelper.createWithLatch(buildPaulPatient()); - assertLinkCount(1); - Patient sourcePatient = getOnlyGoldenPatient(); - myPatientDao.delete(sourcePatient.getIdElement()); - assertLinkCount(0); - } - - @Test - public void testCreatePatientWithMdmTagForbidden() throws InterruptedException { - //Creating a golden resource with the MDM-MANAGED tag should fail - Patient patient = new Patient(); - patient.getMeta().addTag(SYSTEM_MDM_MANAGED, CODE_HAPI_MDM_MANAGED, "User is managed by MDM"); - try { - myMdmHelper.doCreateResource(patient, true); - fail(); - } catch (ForbiddenOperationException e) { - assertThat(e.getMessage(), startsWith("Cannot create or modify Resources that are managed by MDM.")); - } - } - - @Test - public void testCreatePatientWithGoldenRecordTagForbidden() throws InterruptedException { - Patient patient = new Patient(); - patient.getMeta().addTag(SYSTEM_GOLDEN_RECORD_STATUS, CODE_GOLDEN_RECORD, "Golden Record"); - try { - myMdmHelper.doCreateResource(patient, true); - fail(); - } catch (ForbiddenOperationException e) { - assertThat(e.getMessage(), startsWith("Cannot create or modify Resources that are managed by MDM.")); - } - } - - @Test - public void testCreateMedicationWithGoldenRecordRedirectTagForbidden() throws InterruptedException { - Medication medication = new Medication(); - medication.getMeta().addTag(SYSTEM_GOLDEN_RECORD_STATUS, CODE_GOLDEN_RECORD_REDIRECTED, "Golden Record"); - try { - myMdmHelper.doCreateResource(medication, true); - fail(); - } catch (ForbiddenOperationException e) { - assertThat(e.getMessage(), startsWith("Cannot create or modify Resources that are managed by MDM.")); - } - } - - @Test - public void testCreatingGoldenResourceWithInsufficentMDMAttributesIsNotMDMProcessed() throws InterruptedException { - myMdmHelper.doCreateResource(new Patient(), true); - assertLinkCount(0); - } - - @Test - public void testCreatingPatientWithOneOrMoreMatchingAttributesIsMDMProcessed() throws InterruptedException { - myMdmHelper.createWithLatch(buildPaulPatient()); - assertLinkCount(1); - } - - @Test - public void testCreateOrganizationWithMdmTagForbidden() throws InterruptedException { - //Creating a organization with the MDM-MANAGED tag should fail - Organization organization = new Organization(); - organization.getMeta().addTag(SYSTEM_MDM_MANAGED, CODE_HAPI_MDM_MANAGED, "User is managed by MDM"); - try { - myMdmHelper.doCreateResource(organization, true); - fail(); - } catch (ForbiddenOperationException e) { - assertThat(e.getMessage(), startsWith("Cannot create or modify Resources that are managed by MDM.")); - } - } - - @Test - public void testUpdateOrganizationWithMdmTagForbidden() throws InterruptedException { - //Creating a organization with the MDM-MANAGED tag should fail - Organization organization = new Organization(); - myMdmHelper.doCreateResource(organization, true); - organization.getMeta().addTag(SYSTEM_MDM_MANAGED, CODE_HAPI_MDM_MANAGED, "User is managed by MDM"); - try { - myMdmHelper.doUpdateResource(organization, true); - fail(); - } catch (ForbiddenOperationException e) { - assertEquals("The HAPI-MDM tag on a resource may not be changed once created.", e.getMessage()); - } - } - - @Test - public void testGoldenResourceRecordsManagedByMdmAllShareSameTag() throws InterruptedException { - myMdmHelper.createWithLatch(buildJanePatient()); - myMdmHelper.createWithLatch(buildPaulPatient()); - - //TODO GGG MDM: this test is out of date, since we now are using golden record Patients - IBundleProvider search = myPatientDao.search(buildGoldenResourceSearchParameterMap()); - List resources = search.getResources(0, search.size()); - - for (IBaseResource r : resources) { - assertThat(r.getMeta().getTag(SYSTEM_MDM_MANAGED, CODE_HAPI_MDM_MANAGED), is(notNullValue())); - } - } - - @Test - public void testNonMdmManagedGoldenResourceCannotHaveMdmManagedTagAddedToThem() { - // GoldenResource created manually. - Patient patient = new Patient(); - DaoMethodOutcome daoMethodOutcome = myMdmHelper.doCreateResource(patient, true); - assertNotNull(daoMethodOutcome.getId()); - - //Updating that patient to set them as MDM managed is not allowed. - patient.getMeta().addTag(SYSTEM_MDM_MANAGED, CODE_HAPI_MDM_MANAGED, "User is managed by MDM"); - try { - myMdmHelper.doUpdateResource(patient, true); - fail(); - } catch (ForbiddenOperationException e) { - assertEquals("The HAPI-MDM tag on a resource may not be changed once created.", e.getMessage()); - } - } - - @Test - public void testMdmManagedGoldenResourceCannotBeModifiedByGoldenResourceUpdateRequest() throws InterruptedException { - // When MDM is enabled, only the MDM system is allowed to modify GoldenResource links of GoldenResources with the MDM-MANAGED tag. - Patient patient = new Patient(); - IIdType patientId = myMdmHelper.createWithLatch(buildPaulPatient()).getDaoMethodOutcome().getId().toUnqualifiedVersionless(); - - patient.setId(patientId); - - // Updating a Golden Resource Patient who was created via MDM should fail. - MdmLink mdmLink = myMdmLinkDaoSvc.getMatchedLinkForSourcePid(myIdHelperService.getPidOrNull(patient)).get(); - Long sourcePatientPid = mdmLink.getGoldenResourcePid(); - Patient goldenResourcePatient = (Patient) myPatientDao.readByPid(new ResourcePersistentId(sourcePatientPid)); - goldenResourcePatient.setGender(Enumerations.AdministrativeGender.MALE); - try { - myMdmHelper.doUpdateResource(goldenResourcePatient, true); - fail(); - } catch (ForbiddenOperationException e) { - assertThat(e.getMessage(), startsWith("Cannot create or modify Resources that are managed by MDM.")); - } - } - - @Test - public void testMdmPointcutReceivesTransactionLogMessages() throws InterruptedException { - MdmHelperR4.OutcomeAndLogMessageWrapper wrapper = myMdmHelper.createWithLatch(buildJanePatient()); - - TransactionLogMessages mdmTransactionLogMessages = wrapper.getLogMessages(); - - //There is no TransactionGuid here as there is no TransactionLog in this context. - assertThat(mdmTransactionLogMessages.getTransactionGuid(), is(nullValue())); - - List messages = mdmTransactionLogMessages.getValues(); - assertThat(messages.isEmpty(), is(false)); - } - - @Test - public void testWhenASingularPatientUpdatesExternalEidThatGoldenResourceEidIsUpdated() throws InterruptedException { - Patient jane = addExternalEID(buildJanePatient(), "some_eid"); - MdmHelperR4.OutcomeAndLogMessageWrapper latch = myMdmHelper.createWithLatch(jane); - jane.setId(latch.getDaoMethodOutcome().getId()); - clearExternalEIDs(jane); - jane = addExternalEID(jane, "some_new_eid"); - - MdmHelperR4.OutcomeAndLogMessageWrapper outcomeWrapper = myMdmHelper.updateWithLatch(jane); - IAnyResource patient = getGoldenResourceFromTargetResource(jane); - List externalEids = myEIDHelper.getExternalEid(patient); - assertThat(externalEids, hasSize(1)); - assertThat("some_new_eid", is(equalTo(externalEids.get(0).getValue()))); - } - - @Test - public void testWhenEidUpdatesAreDisabledForbidsUpdatesToEidsOnTargets() throws InterruptedException { - setPreventEidUpdates(true); - Patient jane = addExternalEID(buildJanePatient(), "some_eid"); - MdmHelperR4.OutcomeAndLogMessageWrapper latch = myMdmHelper.createWithLatch(jane); - jane.setId(latch.getDaoMethodOutcome().getId()); - clearExternalEIDs(jane); - jane = addExternalEID(jane, "some_new_eid"); - try { - myMdmHelper.doUpdateResource(jane, true); - fail(); - } catch (ForbiddenOperationException e) { - assertThat(e.getMessage(), is(equalTo("While running with EID updates disabled, EIDs may not be updated on source resources"))); - } - setPreventEidUpdates(false); - } - - @Test - public void testWhenMultipleEidsAreDisabledThatTheInterceptorRejectsCreatesWithThem() { - setPreventMultipleEids(true); - Patient patient = buildJanePatient(); - addExternalEID(patient, "123"); - addExternalEID(patient, "456"); - try { - myMdmHelper.doCreateResource(patient, true); - fail(); - } catch (ForbiddenOperationException e) { - assertThat(e.getMessage(), is(equalTo("While running with multiple EIDs disabled, source resources may have at most one EID."))); - } - - setPreventMultipleEids(false); - } - - @Test - public void testInterceptorHandlesNonMdmResources() { - setPreventEidUpdates(true); - - //Create some arbitrary resource. - SearchParameter fooSp = new SearchParameter(); - fooSp.setCode("foo"); - fooSp.addBase("Bundle"); - fooSp.setType(Enumerations.SearchParamType.REFERENCE); - fooSp.setTitle("FOO SP"); - fooSp.setExpression("Bundle.entry[0].resource.as(Composition).encounter"); - fooSp.setXpathUsage(SearchParameter.XPathUsageType.NORMAL); - fooSp.setStatus(Enumerations.PublicationStatus.ACTIVE); - - myMdmHelper.doCreateResource(fooSp, true); - fooSp.setXpathUsage(SearchParameter.XPathUsageType.PHONETIC); - myMdmHelper.doUpdateResource(fooSp, true); - } - - @Test - public void testPatientsWithNoEIDCanBeUpdated() throws InterruptedException { - setPreventEidUpdates(true); - Patient p = buildPaulPatient(); - MdmHelperR4.OutcomeAndLogMessageWrapper wrapper = myMdmHelper.createWithLatch(p); - - p.setId(wrapper.getDaoMethodOutcome().getId()); - p.setBirthDate(new Date()); - myMdmHelper.updateWithLatch(p); - setPreventEidUpdates(false); - } - - @Test - public void testPatientsCanHaveEIDAddedInStrictMode() throws InterruptedException { - setPreventEidUpdates(true); - Patient p = buildPaulPatient(); - MdmHelperR4.OutcomeAndLogMessageWrapper messageWrapper = myMdmHelper.createWithLatch(p); - p.setId(messageWrapper.getDaoMethodOutcome().getId()); - addExternalEID(p, "external eid"); - myMdmHelper.updateWithLatch(p); - setPreventEidUpdates(false); - } - - private void setPreventEidUpdates(boolean thePrevent) { - ((MdmSettings) myMdmSettings).setPreventEidUpdates(thePrevent); - } - - private void setPreventMultipleEids(boolean thePrevent) { - ((MdmSettings) myMdmSettings).setPreventMultipleEids(thePrevent); - } - }