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}