Skip to content

Commit

Permalink
MDM virtualization
Browse files Browse the repository at this point in the history
  • Loading branch information
jamesagnew committed Nov 13, 2024
1 parent 7c98bfd commit 0e1be11
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -69,10 +89,10 @@ public class MdmReadVirtualizationInterceptor<P extends IResourcePersistentId<?>
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) {
Expand All @@ -86,26 +106,12 @@ public void preShowResources(RequestDetails theRequestDetails, IPreResourceShowD
ListMultimap<String, Integer> candidateResourceIds = extractRemapCandidateResources(theDetails);
ListMultimap<String, ResourceReferenceInfo> 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<IIdType> allIds = new HashSet<>();
candidateResourceIds.keySet().forEach(t -> allIds.add(newIdType(t)));
candidateReferences.keySet().forEach(t -> allIds.add(newIdType(t)));
List<P> sourcePids =
myIdHelperService.getPidsOrThrowException(RequestPartitionId.allPartitions(), List.copyOf(allIds));
Collection<MdmPidTuple<P>> tuples = myMdmLinkDao.resolveGoldenResources(sourcePids);

// Resolve the link PIDs into FHIR IDs
Set<P> allPersistentIds = new HashSet<>();
tuples.forEach(t -> allPersistentIds.add(t.getGoldenPid()));
tuples.forEach(t -> allPersistentIds.add(t.getSourcePid()));
PersistentIdToForcedIdMap<P> persistentIdToFhirIdMap =
myIdHelperService.translatePidsToForcedIds(allPersistentIds);
CandidateMdmLinkedResources<P> candidates =
findCandidateMdmLinkedResources(candidateResourceIds, candidateReferences);

// Loop through each link and figure out whether we need to remap anything
for (MdmPidTuple<P> tuple : tuples) {
Optional<String> sourceIdOpt = persistentIdToFhirIdMap.get(tuple.getSourcePid());
for (MdmPidTuple<P> tuple : candidates.getTuples()) {
Optional<String> sourceIdOpt = candidates.getFhirIdForPersistentId(tuple.getSourcePid());
if (sourceIdOpt.isPresent()) {
String sourceId = sourceIdOpt.get();

Expand All @@ -114,7 +120,7 @@ public void preShowResources(RequestDetails theRequestDetails, IPreResourceShowD
if (!referencesToRemap.isEmpty()) {
P associatedGoldenResourcePid = tuple.getGoldenPid();
Optional<String> associatedGoldenResourceId =
persistentIdToFhirIdMap.get(associatedGoldenResourcePid);
candidates.getFhirIdForPersistentId(associatedGoldenResourcePid);
if (associatedGoldenResourceId.isPresent()) {
for (ResourceReferenceInfo referenceInfoToRemap : referencesToRemap) {
IBaseReference referenceToRemap = referenceInfoToRemap.getResourceReference();
Expand All @@ -124,7 +130,7 @@ public void preShowResources(RequestDetails theRequestDetails, IPreResourceShowD
}

// Filter out source resources
Optional<String> targetIdOpt = persistentIdToFhirIdMap.get(tuple.getGoldenPid());
Optional<String> targetIdOpt = candidates.getFhirIdForPersistentId(tuple.getGoldenPid());
if (targetIdOpt.isPresent()) {
Integer filteredIndex = null;
for (int sourceIdResourceIndex : candidateResourceIds.get(sourceId)) {
Expand Down Expand Up @@ -153,6 +159,31 @@ public void preShowResources(RequestDetails theRequestDetails, IPreResourceShowD
}
}

@Nonnull
private CandidateMdmLinkedResources<P> findCandidateMdmLinkedResources(
ListMultimap<String, Integer> candidateResourceIds,
ListMultimap<String, ResourceReferenceInfo> 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<IIdType> allIds = new HashSet<>();
candidateResourceIds.keySet().forEach(t -> allIds.add(newIdType(t)));
candidateReferences.keySet().forEach(t -> allIds.add(newIdType(t)));
List<P> sourcePids =
myIdHelperService.getPidsOrThrowException(RequestPartitionId.allPartitions(), List.copyOf(allIds));
Collection<MdmPidTuple<P>> tuples = myMdmLinkDao.resolveGoldenResources(sourcePids);

// Resolve the link PIDs into FHIR IDs
Set<P> allPersistentIds = new HashSet<>();
tuples.forEach(t -> allPersistentIds.add(t.getGoldenPid()));
tuples.forEach(t -> allPersistentIds.add(t.getSourcePid()));
PersistentIdToForcedIdMap<P> 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}
Expand Down Expand Up @@ -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<P extends IResourcePersistentId<?>> {
private final Collection<MdmPidTuple<P>> myTuples;
private final PersistentIdToForcedIdMap<P> myPersistentIdToFhirIdMap;

public CandidateMdmLinkedResources(
Collection<MdmPidTuple<P>> thePidTuples, PersistentIdToForcedIdMap<P> thePersistentIdToForcedIdMap) {
this.myTuples = thePidTuples;
this.myPersistentIdToFhirIdMap = thePersistentIdToForcedIdMap;
}

public Collection<MdmPidTuple<P>> getTuples() {
return myTuples;
}

public Optional<String> getFhirIdForPersistentId(P theSourcePid) {
return myPersistentIdToFhirIdMap.get(theSourcePid);
}
}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand Down

0 comments on commit 0e1be11

Please sign in to comment.