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.model.primitive.IdDt; 030import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; 031import jakarta.annotation.Nonnull; 032import org.hl7.fhir.instance.model.api.IBaseResource; 033import org.hl7.fhir.instance.model.api.IIdType; 034import org.slf4j.Logger; 035import org.springframework.beans.factory.annotation.Autowired; 036import org.springframework.stereotype.Service; 037import org.springframework.transaction.annotation.Transactional; 038 039import java.util.Collection; 040import java.util.Collections; 041import java.util.List; 042import java.util.Set; 043import java.util.stream.Collectors; 044 045@Service 046@Transactional 047public class MdmLinkExpandSvc implements IMdmLinkExpandSvc { 048 private static final Logger ourLog = Logs.getMdmTroubleshootingLog(); 049 050 @Autowired 051 IMdmLinkDao myMdmLinkDao; 052 053 @Autowired 054 IIdHelperService myIdHelperService; 055 056 public MdmLinkExpandSvc() {} 057 058 /** 059 * Given a source resource, perform MDM expansion and return all the resource IDs of all resources that are 060 * MDM-Matched to this resource. 061 * 062 * @param theResource The resource to MDM-Expand 063 * @return A set of strings representing the FHIR IDs of the expanded resources. 064 */ 065 @Override 066 public Set<String> expandMdmBySourceResource(RequestPartitionId theRequestPartitionId, IBaseResource theResource) { 067 ourLog.debug("About to MDM-expand source resource {}", theResource); 068 return expandMdmBySourceResourceId(theRequestPartitionId, theResource.getIdElement()); 069 } 070 071 /** 072 * Given a resource ID of a source resource, perform MDM expansion and return all the resource IDs of all resources that are 073 * MDM-Matched to this resource. 074 * 075 * @param theRequestPartitionId The partition ID associated with the request. 076 * @param theId The Resource ID of the resource to MDM-Expand 077 * @return A set of strings representing the FHIR ids of the expanded resources. 078 */ 079 @Override 080 public Set<String> expandMdmBySourceResourceId(RequestPartitionId theRequestPartitionId, IIdType theId) { 081 ourLog.debug("About to expand source resource with resource id {}", theId); 082 return expandMdmBySourceResourcePid( 083 theRequestPartitionId, 084 myIdHelperService.getPidOrThrowException(RequestPartitionId.allPartitions(), theId)); 085 } 086 087 /** 088 * 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 089 * MDM-Matched to this resource. 090 * 091 * @param theRequestPartitionId The partition ID associated with the request. 092 * @param theSourceResourcePid The PID of the resource to MDM-Expand 093 * @return A set of strings representing the FHIR ids of the expanded resources. 094 */ 095 @Override 096 public Set<String> expandMdmBySourceResourcePid( 097 RequestPartitionId theRequestPartitionId, IResourcePersistentId<?> theSourceResourcePid) { 098 ourLog.debug("About to expand source resource with PID {}", theSourceResourcePid); 099 final List<MdmPidTuple<?>> goldenPidSourcePidTuples = 100 myMdmLinkDao.expandPidsBySourcePidAndMatchResult(theSourceResourcePid, MdmMatchResultEnum.MATCH); 101 102 return flattenPidTuplesToSet(theRequestPartitionId, theSourceResourcePid, goldenPidSourcePidTuples); 103 } 104 105 /** 106 * Given a PID of a golden resource, perform MDM expansion and return all the resource IDs of all resources that are 107 * MDM-Matched to this golden resource. 108 * 109 * @param theRequestPartitionId Partition information from the request 110 * @param theGoldenResourcePid The PID of the golden resource to MDM-Expand. 111 * @return A set of strings representing the FHIR ids of the expanded resources. 112 */ 113 @Override 114 public Set<String> expandMdmByGoldenResourceId( 115 RequestPartitionId theRequestPartitionId, IResourcePersistentId<?> theGoldenResourcePid) { 116 ourLog.debug("About to expand golden resource with PID {}", theGoldenResourcePid); 117 final List<MdmPidTuple<?>> goldenPidSourcePidTuples = myMdmLinkDao.expandPidsByGoldenResourcePidAndMatchResult( 118 theGoldenResourcePid, MdmMatchResultEnum.MATCH); 119 return flattenPidTuplesToSet(theRequestPartitionId, theGoldenResourcePid, goldenPidSourcePidTuples); 120 } 121 122 /** 123 * Given a resource ID of a golden resource, perform MDM expansion and return all the resource IDs of all resources that are 124 * MDM-Matched to this golden resource. 125 * 126 * @param theRequestPartitionId Partition information from the request 127 * @param theGoldenResourcePid The resource ID of the golden resource to MDM-Expand. 128 * @return A set of strings representing the FHIR ids of the expanded resources. 129 */ 130 @Override 131 public Set<String> expandMdmByGoldenResourcePid( 132 RequestPartitionId theRequestPartitionId, IResourcePersistentId<?> theGoldenResourcePid) { 133 ourLog.debug("About to expand golden resource with PID {}", theGoldenResourcePid); 134 final List<MdmPidTuple<?>> goldenPidSourcePidTuples = myMdmLinkDao.expandPidsByGoldenResourcePidAndMatchResult( 135 theGoldenResourcePid, MdmMatchResultEnum.MATCH); 136 return flattenPidTuplesToSet(theRequestPartitionId, theGoldenResourcePid, goldenPidSourcePidTuples); 137 } 138 139 @Override 140 public Set<String> expandMdmByGoldenResourceId(RequestPartitionId theRequestPartitionId, IdDt theId) { 141 ourLog.debug("About to expand golden resource with golden resource id {}", theId); 142 IResourcePersistentId<?> pidOrThrowException = 143 myIdHelperService.getPidOrThrowException(RequestPartitionId.allPartitions(), theId); 144 return expandMdmByGoldenResourcePid(theRequestPartitionId, pidOrThrowException); 145 } 146 147 @Nonnull 148 public Set<String> flattenPidTuplesToSet( 149 RequestPartitionId theRequestPartitionId, 150 IResourcePersistentId<?> theInitialPid, 151 List<MdmPidTuple<?>> theGoldenPidSourcePidTuples) { 152 final Set<IResourcePersistentId> flattenedPids = theGoldenPidSourcePidTuples.stream() 153 .map(tuple -> flattenTuple(theRequestPartitionId, tuple)) 154 .flatMap(Collection::stream) 155 .collect(Collectors.toUnmodifiableSet()); 156 final Set<String> resourceIds = myIdHelperService.translatePidsToFhirResourceIds(flattenedPids); 157 ourLog.debug("Pid {} has been expanded to [{}]", theInitialPid, String.join(",", resourceIds)); 158 return resourceIds; 159 } 160 161 @Nonnull 162 static Set<IResourcePersistentId> flattenTuple(RequestPartitionId theRequestPartitionId, MdmPidTuple<?> theTuple) { 163 if (theRequestPartitionId.isPartitionCovered(theTuple.getGoldenPartitionId())) { 164 if (theRequestPartitionId.isPartitionCovered(theTuple.getSourcePartitionId())) { 165 return Set.of(theTuple.getSourcePid(), theTuple.getGoldenPid()); 166 } 167 return Set.of(theTuple.getGoldenPid()); 168 } 169 170 if (theRequestPartitionId.isPartitionCovered(theTuple.getSourcePartitionId())) { 171 return Set.of(theTuple.getSourcePid()); 172 } 173 174 return Collections.emptySet(); 175 } 176}