001/*-
002 * #%L
003 * HAPI FHIR - Master Data Management
004 * %%
005 * Copyright (C) 2014 - 2025 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.i18n.Msg;
023import ca.uhn.fhir.interceptor.model.RequestPartitionId;
024import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
025import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
026import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
027import ca.uhn.fhir.mdm.api.IMdmLinkExpandSvc;
028import ca.uhn.fhir.mdm.model.CanonicalEID;
029import ca.uhn.fhir.mdm.util.EIDHelper;
030import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
031import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId;
032import ca.uhn.fhir.rest.param.TokenOrListParam;
033import ca.uhn.fhir.rest.param.TokenParam;
034import org.hl7.fhir.instance.model.api.IBaseResource;
035import org.hl7.fhir.instance.model.api.IIdType;
036
037import java.util.*;
038
039/**
040 * MDM link expansion service that is used when MDM mode is Match-Only and eid systems are defined in Mdm rules.
041 * Expands resources by finding other resources with the same eids.
042 */
043public class MdmEidMatchOnlyExpandSvc implements IMdmLinkExpandSvc {
044
045        private final DaoRegistry myDaoRegistry;
046
047        private EIDHelper myEidHelper;
048
049        public MdmEidMatchOnlyExpandSvc(DaoRegistry theDaoRegistry) {
050                myDaoRegistry = theDaoRegistry;
051        }
052
053        public void setMyEidHelper(EIDHelper theEidHelper) {
054                myEidHelper = theEidHelper;
055        }
056
057        /**
058         * MDM expands the resource with the given id by finding all resources with the same eids.
059         * @param theRequestPartitionId the partition to search in
060         * @param theId the resource ID to expand
061         * @return set of resource IDs with matching EIDs
062         */
063        @Override
064        public Set<String> expandMdmBySourceResourceId(RequestPartitionId theRequestPartitionId, IIdType theId) {
065                Set<String> result = new HashSet<>();
066                // 1. Resolve resource
067                String resourceType = theId.getResourceType();
068                SystemRequestDetails srd = SystemRequestDetails.forRequestPartitionId(theRequestPartitionId);
069                IFhirResourceDao<IBaseResource> resourceDao = myDaoRegistry.getResourceDao(resourceType);
070                IBaseResource resource = resourceDao.read(theId, srd);
071                // 2. Extract EIDs from resource using EIDHelper
072                List<CanonicalEID> eids = myEidHelper.getExternalEid(resource);
073                if (!eids.isEmpty()) {
074                        // 3. Search for resources of same type with the same eid
075                        var map = new SearchParameterMap();
076                        final TokenOrListParam tokenOrListParam = new TokenOrListParam();
077                        eids.forEach(eid -> tokenOrListParam.addOr(new TokenParam(eid.getSystem(), eid.getValue())));
078                        map.add("identifier", tokenOrListParam);
079                        List<IIdType> ids = resourceDao.searchForResourceIds(map, srd);
080                        for (IIdType id : ids) {
081                                result.add(id.toUnqualifiedVersionless().getValue());
082                        }
083                }
084                return result;
085        }
086
087        @Override
088        public Set<String> expandMdmBySourceResource(RequestPartitionId theRequestPartitionId, IBaseResource theResource) {
089                return expandMdmBySourceResourceId(theRequestPartitionId, theResource.getIdElement());
090        }
091
092        @Override
093        public Set<String> expandMdmBySourceResourcePid(
094                        RequestPartitionId theRequestPartitionId, IResourcePersistentId<?> theSourceResourcePid) {
095                throw new UnsupportedOperationException(
096                                Msg.code(2809) + "This operation is not implemented when using MDM in MATCH_ONLY mode.");
097        }
098
099        @Override
100        public Set<String> expandMdmByGoldenResourceId(
101                        RequestPartitionId theRequestPartitionId, IResourcePersistentId<?> theGoldenResourcePid) {
102                // This operation is not applicable when using MDM in MATCH_ONLY mode,
103                // return an emtpy set to rather than an exception to not affect existing code
104                return Collections.emptySet();
105        }
106
107        @Override
108        public Set<String> expandMdmByGoldenResourcePid(
109                        RequestPartitionId theRequestPartitionId, IResourcePersistentId<?> theGoldenResourcePid) {
110                // This operation is not applicable when using MDM in MATCH_ONLY mode,
111                // return an emtpy set to rather than an exception to not affect existing code
112                return Collections.emptySet();
113        }
114
115        @Override
116        public Set<String> expandMdmByGoldenResourceId(RequestPartitionId theRequestPartitionId, IIdType theId) {
117                // This operation is not applicable when using MDM in MATCH_ONLY mode,
118                // return an emtpy set to rather than an exception to not affect existing code
119                return Collections.emptySet();
120        }
121}