001/*- 002 * #%L 003 * HAPI FHIR - Master Data Management 004 * %% 005 * Copyright (C) 2014 - 2024 Smile CDR, Inc. 006 * %% 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 * #L% 019 */ 020package ca.uhn.fhir.mdm.svc; 021 022import ca.uhn.fhir.interceptor.model.RequestPartitionId; 023import ca.uhn.fhir.jpa.api.svc.IIdHelperService; 024import ca.uhn.fhir.mdm.api.IMdmLinkExpandSvc; 025import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; 026import ca.uhn.fhir.mdm.dao.IMdmLinkDao; 027import ca.uhn.fhir.mdm.log.Logs; 028import ca.uhn.fhir.mdm.model.MdmPidTuple; 029import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; 030import jakarta.annotation.Nonnull; 031import org.hl7.fhir.instance.model.api.IBaseResource; 032import org.hl7.fhir.instance.model.api.IIdType; 033import org.slf4j.Logger; 034import org.springframework.beans.factory.annotation.Autowired; 035import org.springframework.stereotype.Service; 036import org.springframework.transaction.annotation.Transactional; 037 038import java.util.Collection; 039import java.util.Collections; 040import java.util.List; 041import java.util.Set; 042import java.util.stream.Collectors; 043 044@Service 045@Transactional 046public class MdmLinkExpandSvc implements IMdmLinkExpandSvc { 047 private static final Logger ourLog = Logs.getMdmTroubleshootingLog(); 048 049 @Autowired 050 IMdmLinkDao myMdmLinkDao; 051 052 @Autowired 053 IIdHelperService myIdHelperService; 054 055 public MdmLinkExpandSvc() {} 056 057 /** 058 * Given a source resource, perform MDM expansion and return all the resource IDs of all resources that are 059 * MDM-Matched to this resource. 060 * 061 * @param theResource The resource to MDM-Expand 062 * @return A set of strings representing the FHIR IDs of the expanded resources. 063 */ 064 @Override 065 public Set<String> expandMdmBySourceResource(RequestPartitionId theRequestPartitionId, IBaseResource theResource) { 066 ourLog.debug("About to MDM-expand source resource {}", theResource); 067 return expandMdmBySourceResourceId(theRequestPartitionId, theResource.getIdElement()); 068 } 069 070 /** 071 * Given a resource ID of a source resource, perform MDM expansion and return all the resource IDs of all resources that are 072 * MDM-Matched to this resource. 073 * 074 * @param theRequestPartitionId The partition ID associated with the request. 075 * @param theId The Resource ID of the resource to MDM-Expand 076 * @return A set of strings representing the FHIR ids of the expanded resources. 077 */ 078 @Override 079 public Set<String> expandMdmBySourceResourceId(RequestPartitionId theRequestPartitionId, IIdType theId) { 080 ourLog.debug("About to expand source resource with resource id {}", theId); 081 return expandMdmBySourceResourcePid( 082 theRequestPartitionId, 083 myIdHelperService.getPidOrThrowException(RequestPartitionId.allPartitions(), theId)); 084 } 085 086 /** 087 * Given a partition ID and a PID of a source resource, perform MDM expansion and return all the resource IDs of all resources that are 088 * MDM-Matched to this resource. 089 * 090 * @param theRequestPartitionId The partition ID associated with the request. 091 * @param theSourceResourcePid The PID of the resource to MDM-Expand 092 * @return A set of strings representing the FHIR ids of the expanded resources. 093 */ 094 @Override 095 public Set<String> expandMdmBySourceResourcePid( 096 RequestPartitionId theRequestPartitionId, IResourcePersistentId<?> theSourceResourcePid) { 097 ourLog.debug("About to expand source resource with PID {}", theSourceResourcePid); 098 final List<MdmPidTuple<?>> goldenPidSourcePidTuples = 099 myMdmLinkDao.expandPidsBySourcePidAndMatchResult(theSourceResourcePid, MdmMatchResultEnum.MATCH); 100 101 return flattenPidTuplesToSet(theRequestPartitionId, theSourceResourcePid, goldenPidSourcePidTuples); 102 } 103 104 /** 105 * Given a PID of a golden resource, perform MDM expansion and return all the resource IDs of all resources that are 106 * MDM-Matched to this golden resource. 107 * 108 * @param theRequestPartitionId Partition information from the request 109 * @param theGoldenResourcePid The PID of the golden resource to MDM-Expand. 110 * @return A set of strings representing the FHIR ids of the expanded resources. 111 */ 112 @Override 113 public Set<String> expandMdmByGoldenResourceId( 114 RequestPartitionId theRequestPartitionId, IResourcePersistentId<?> theGoldenResourcePid) { 115 ourLog.debug("About to expand golden resource with PID {}", theGoldenResourcePid); 116 final List<MdmPidTuple<?>> goldenPidSourcePidTuples = myMdmLinkDao.expandPidsByGoldenResourcePidAndMatchResult( 117 theGoldenResourcePid, MdmMatchResultEnum.MATCH); 118 return flattenPidTuplesToSet(theRequestPartitionId, theGoldenResourcePid, goldenPidSourcePidTuples); 119 } 120 121 /** 122 * Given a resource ID of a golden resource, perform MDM expansion and return all the resource IDs of all resources that are 123 * MDM-Matched to this golden resource. 124 * 125 * @param theRequestPartitionId Partition information from the request 126 * @param theGoldenResourcePid The resource ID of the golden resource to MDM-Expand. 127 * @return A set of strings representing the FHIR ids of the expanded resources. 128 */ 129 @Override 130 public Set<String> expandMdmByGoldenResourcePid( 131 RequestPartitionId theRequestPartitionId, IResourcePersistentId<?> theGoldenResourcePid) { 132 ourLog.debug("About to expand golden resource with PID {}", theGoldenResourcePid); 133 final List<MdmPidTuple<?>> goldenPidSourcePidTuples = myMdmLinkDao.expandPidsByGoldenResourcePidAndMatchResult( 134 theGoldenResourcePid, MdmMatchResultEnum.MATCH); 135 return flattenPidTuplesToSet(theRequestPartitionId, theGoldenResourcePid, goldenPidSourcePidTuples); 136 } 137 138 @Override 139 public Set<String> expandMdmByGoldenResourceId(RequestPartitionId theRequestPartitionId, IIdType theId) { 140 ourLog.debug("About to expand golden resource with golden resource id {}", theId); 141 IResourcePersistentId<?> pidOrThrowException = 142 myIdHelperService.getPidOrThrowException(RequestPartitionId.allPartitions(), theId); 143 return expandMdmByGoldenResourcePid(theRequestPartitionId, pidOrThrowException); 144 } 145 146 @Nonnull 147 public Set<String> flattenPidTuplesToSet( 148 RequestPartitionId theRequestPartitionId, 149 IResourcePersistentId<?> theInitialPid, 150 List<MdmPidTuple<?>> theGoldenPidSourcePidTuples) { 151 final Set<IResourcePersistentId> flattenedPids = theGoldenPidSourcePidTuples.stream() 152 .map(tuple -> flattenTuple(theRequestPartitionId, tuple)) 153 .flatMap(Collection::stream) 154 .collect(Collectors.toUnmodifiableSet()); 155 final Set<String> resourceIds = myIdHelperService.translatePidsToFhirResourceIds(flattenedPids); 156 ourLog.debug("Pid {} has been expanded to [{}]", theInitialPid, String.join(",", resourceIds)); 157 return resourceIds; 158 } 159 160 @Nonnull 161 static Set<IResourcePersistentId> flattenTuple(RequestPartitionId theRequestPartitionId, MdmPidTuple<?> theTuple) { 162 if (theRequestPartitionId.isPartitionCovered(theTuple.getGoldenPartitionId())) { 163 if (theRequestPartitionId.isPartitionCovered(theTuple.getSourcePartitionId())) { 164 return Set.of(theTuple.getSourcePid(), theTuple.getGoldenPid()); 165 } 166 return Set.of(theTuple.getGoldenPid()); 167 } 168 169 if (theRequestPartitionId.isPartitionCovered(theTuple.getSourcePartitionId())) { 170 return Set.of(theTuple.getSourcePid()); 171 } 172 173 return Collections.emptySet(); 174 } 175}