diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmReadVirtualizationInterceptorTest.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmReadVirtualizationInterceptorTest.java index 46162b8afad1..e2bc5a10c85c 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmReadVirtualizationInterceptorTest.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/interceptor/MdmReadVirtualizationInterceptorTest.java @@ -20,7 +20,6 @@ import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; @@ -31,13 +30,10 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.slf4j.LoggerFactory.getLogger; @ContextConfiguration(classes = {MdmHelperConfig.class}) public class MdmReadVirtualizationInterceptorTest extends BaseMdmR4Test { - private static final Logger ourLog = getLogger(MdmReadVirtualizationInterceptorTest.class); - @RegisterExtension @Autowired public MdmHelperR4 myMdmHelper; diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/interceptor/MdmReadVirtualizationInterceptor.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/interceptor/MdmReadVirtualizationInterceptor.java index 4399173154f6..8571d416f477 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/interceptor/MdmReadVirtualizationInterceptor.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/interceptor/MdmReadVirtualizationInterceptor.java @@ -1,3 +1,22 @@ +/*- + * #%L + * HAPI FHIR - Master Data Management + * %% + * Copyright (C) 2014 - 2024 Smile CDR, Inc. + * %% + * 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% + */ package ca.uhn.fhir.mdm.interceptor; import ca.uhn.fhir.context.FhirContext; @@ -8,10 +27,10 @@ import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.api.model.PersistentIdToForcedIdMap; import ca.uhn.fhir.jpa.api.svc.IIdHelperService; +import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.mdm.dao.IMdmLinkDao; import ca.uhn.fhir.mdm.model.MdmPidTuple; -import ca.uhn.fhir.mdm.rules.config.MdmSettings; import ca.uhn.fhir.mdm.svc.MdmSearchExpansionSvc; import ca.uhn.fhir.rest.api.server.IPreResourceShowDetails; import ca.uhn.fhir.rest.api.server.RequestDetails; @@ -20,6 +39,7 @@ import ca.uhn.fhir.util.ResourceReferenceInfo; import com.google.common.collect.ListMultimap; import com.google.common.collect.MultimapBuilder; +import jakarta.annotation.Nonnull; import org.hl7.fhir.instance.model.api.IBaseReference; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; @@ -69,10 +89,10 @@ public class MdmReadVirtualizationInterceptor

private DaoRegistry myDaoRegistry; @Autowired - private MdmSettings myMdmSettings; + private MdmSearchExpansionSvc myMdmSearchExpansionSvc; @Autowired - private MdmSearchExpansionSvc myMdmSearchExpansionSvc; + private HapiTransactionService myTxService; @Hook(Pointcut.STORAGE_PRESEARCH_REGISTERED) public void hook(RequestDetails theRequestDetails, SearchParameterMap theSearchParameterMap) { @@ -86,26 +106,12 @@ public void preShowResources(RequestDetails theRequestDetails, IPreResourceShowD ListMultimap candidateResourceIds = extractRemapCandidateResources(theDetails); ListMultimap candidateReferences = extractRemapCandidateReferences(theDetails); - // Resolve all the resource IDs we've seen that could be MDM candidates, - // and look for MDM links that have these IDs as either the source or the - // golden resource side of the link - Set allIds = new HashSet<>(); - candidateResourceIds.keySet().forEach(t -> allIds.add(newIdType(t))); - candidateReferences.keySet().forEach(t -> allIds.add(newIdType(t))); - List

sourcePids = - myIdHelperService.getPidsOrThrowException(RequestPartitionId.allPartitions(), List.copyOf(allIds)); - Collection> tuples = myMdmLinkDao.resolveGoldenResources(sourcePids); - - // Resolve the link PIDs into FHIR IDs - Set

allPersistentIds = new HashSet<>(); - tuples.forEach(t -> allPersistentIds.add(t.getGoldenPid())); - tuples.forEach(t -> allPersistentIds.add(t.getSourcePid())); - PersistentIdToForcedIdMap

persistentIdToFhirIdMap = - myIdHelperService.translatePidsToForcedIds(allPersistentIds); + CandidateMdmLinkedResources

candidates = + findCandidateMdmLinkedResources(candidateResourceIds, candidateReferences); // Loop through each link and figure out whether we need to remap anything - for (MdmPidTuple

tuple : tuples) { - Optional sourceIdOpt = persistentIdToFhirIdMap.get(tuple.getSourcePid()); + for (MdmPidTuple

tuple : candidates.getTuples()) { + Optional sourceIdOpt = candidates.getFhirIdForPersistentId(tuple.getSourcePid()); if (sourceIdOpt.isPresent()) { String sourceId = sourceIdOpt.get(); @@ -114,7 +120,7 @@ public void preShowResources(RequestDetails theRequestDetails, IPreResourceShowD if (!referencesToRemap.isEmpty()) { P associatedGoldenResourcePid = tuple.getGoldenPid(); Optional associatedGoldenResourceId = - persistentIdToFhirIdMap.get(associatedGoldenResourcePid); + candidates.getFhirIdForPersistentId(associatedGoldenResourcePid); if (associatedGoldenResourceId.isPresent()) { for (ResourceReferenceInfo referenceInfoToRemap : referencesToRemap) { IBaseReference referenceToRemap = referenceInfoToRemap.getResourceReference(); @@ -124,7 +130,7 @@ public void preShowResources(RequestDetails theRequestDetails, IPreResourceShowD } // Filter out source resources - Optional targetIdOpt = persistentIdToFhirIdMap.get(tuple.getGoldenPid()); + Optional targetIdOpt = candidates.getFhirIdForPersistentId(tuple.getGoldenPid()); if (targetIdOpt.isPresent()) { Integer filteredIndex = null; for (int sourceIdResourceIndex : candidateResourceIds.get(sourceId)) { @@ -153,6 +159,31 @@ public void preShowResources(RequestDetails theRequestDetails, IPreResourceShowD } } + @Nonnull + private CandidateMdmLinkedResources

findCandidateMdmLinkedResources( + ListMultimap candidateResourceIds, + ListMultimap candidateReferences) { + return myTxService.withSystemRequest().read(() -> { + // Resolve all the resource IDs we've seen that could be MDM candidates, + // and look for MDM links that have these IDs as either the source or the + // golden resource side of the link + Set allIds = new HashSet<>(); + candidateResourceIds.keySet().forEach(t -> allIds.add(newIdType(t))); + candidateReferences.keySet().forEach(t -> allIds.add(newIdType(t))); + List

sourcePids = + myIdHelperService.getPidsOrThrowException(RequestPartitionId.allPartitions(), List.copyOf(allIds)); + Collection> tuples = myMdmLinkDao.resolveGoldenResources(sourcePids); + + // Resolve the link PIDs into FHIR IDs + Set

allPersistentIds = new HashSet<>(); + tuples.forEach(t -> allPersistentIds.add(t.getGoldenPid())); + tuples.forEach(t -> allPersistentIds.add(t.getSourcePid())); + PersistentIdToForcedIdMap

persistentIdToFhirIdMap = + myIdHelperService.translatePidsToForcedIds(allPersistentIds); + return new CandidateMdmLinkedResources<>(tuples, persistentIdToFhirIdMap); + }); + } + /** * @return Returns a map where the keys are a typed ID (Patient/ABC) and the values are the index of * that resource within the {@link IPreResourceShowDetails} @@ -211,6 +242,25 @@ private IIdType newIdType(String targetId) { * Is the given resource a candidate for virtualization? */ private boolean isRemapCandidate(String theResourceType) { - return myMdmSettings.isSupportedMdmType(theResourceType); + return "Patient".equals(theResourceType); + } + + private static class CandidateMdmLinkedResources

> { + private final Collection> myTuples; + private final PersistentIdToForcedIdMap

myPersistentIdToFhirIdMap; + + public CandidateMdmLinkedResources( + Collection> thePidTuples, PersistentIdToForcedIdMap

thePersistentIdToForcedIdMap) { + this.myTuples = thePidTuples; + this.myPersistentIdToFhirIdMap = thePersistentIdToForcedIdMap; + } + + public Collection> getTuples() { + return myTuples; + } + + public Optional getFhirIdForPersistentId(P theSourcePid) { + return myPersistentIdToFhirIdMap.get(theSourcePid); + } } } diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/svc/MdmSearchExpansionSvc.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/svc/MdmSearchExpansionSvc.java index dbd0ac7f8206..804ed5310b6a 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/svc/MdmSearchExpansionSvc.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/svc/MdmSearchExpansionSvc.java @@ -1,3 +1,22 @@ +/*- + * #%L + * HAPI FHIR - Master Data Management + * %% + * Copyright (C) 2014 - 2024 Smile CDR, Inc. + * %% + * 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% + */ package ca.uhn.fhir.mdm.svc; import ca.uhn.fhir.interceptor.model.RequestPartitionId;