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}