
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.context.FhirContext; 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.api.svc.IIdHelperService; 027import ca.uhn.fhir.jpa.api.svc.ResolveIdentityMode; 028import ca.uhn.fhir.jpa.model.dao.JpaPid; 029import ca.uhn.fhir.rest.api.server.SystemRequestDetails; 030import ca.uhn.fhir.util.FhirTerser; 031import org.hl7.fhir.instance.model.api.IBaseReference; 032import org.hl7.fhir.instance.model.api.IBaseResource; 033import org.hl7.fhir.instance.model.api.IIdType; 034 035import java.util.HashSet; 036import java.util.List; 037import java.util.Set; 038import java.util.stream.Collectors; 039 040/** 041 * Implementation of {@link IBulkExportMdmResourceExpander} that handles bulk export resource expansion 042 * when MDM mode is Match-Only and Eid Systems defined in mdm rules. 043 * 044 * <p>This expander is used during bulk export operations to expand Group resources by resolving 045 * MDM matching resources for the members in the group. Resources are 046 * matched based on just eids rather than the full MDM golden resource relationships.</p> 047 */ 048public class BulkExportMdmEidMatchOnlyResourceExpander implements IBulkExportMdmResourceExpander { 049 050 private final DaoRegistry myDaoRegistry; 051 private final MdmEidMatchOnlyExpandSvc myMdmEidMatchOnlyLinkExpandSvc; 052 private final FhirContext myFhirContext; 053 private final IIdHelperService<JpaPid> myIdHelperService; 054 055 /** 056 * Constructor 057 */ 058 public BulkExportMdmEidMatchOnlyResourceExpander( 059 DaoRegistry theDaoRegistry, 060 MdmEidMatchOnlyExpandSvc theMdmEidMatchOnlyLinkExpandSvc, 061 FhirContext theFhirContext, 062 IIdHelperService<JpaPid> theIdHelperService) { 063 myDaoRegistry = theDaoRegistry; 064 myMdmEidMatchOnlyLinkExpandSvc = theMdmEidMatchOnlyLinkExpandSvc; 065 myFhirContext = theFhirContext; 066 myIdHelperService = theIdHelperService; 067 } 068 069 /** 070 * Expands a Group resource and returns the Group members' resource persistent ids. 071 * The returned ids consists of group members + all MDM matched resources based on EID only. 072 * 073 * <p>This method:</p> 074 * <ol> 075 * <li>Reads the specified Group resource</li> 076 * <li>Extracts all member entity references from the Group</li> 077 * <li>For each member, uses EID matching to find all resources that have the same EID as the member, using eid system specified in mdm rules</li> 078 * <li>Converts the expanded resource IDs to persistent IDs (PIDs)</li> 079 * </ol> 080 * 081 * @param groupResourceId The ID of the Group resource to expand 082 * @param requestPartitionId The request partition ID 083 * @return A set of {@link JpaPid} objects representing all expanded resources 084 */ 085 @Override 086 public Set<JpaPid> expandGroup(String groupResourceId, RequestPartitionId requestPartitionId) { 087 // Read the Group resource 088 SystemRequestDetails srd = SystemRequestDetails.forRequestPartitionId(requestPartitionId); 089 IIdType groupId = myFhirContext.getVersion().newIdType(groupResourceId); 090 IFhirResourceDao<?> groupDao = myDaoRegistry.getResourceDao("Group"); 091 IBaseResource groupResource = groupDao.read(groupId, srd); 092 093 Set<String> allResourceIds = new HashSet<>(); 094 FhirTerser terser = myFhirContext.newTerser(); 095 // Extract all member.entity references from the Group resource 096 List<IBaseReference> memberEntities = 097 terser.getValues(groupResource, "Group.member.entity", IBaseReference.class); 098 // mdm expand each member based on eid 099 for (IBaseReference entityRef : memberEntities) { 100 if (!entityRef.getReferenceElement().isEmpty()) { 101 IIdType memberId = entityRef.getReferenceElement(); 102 Set<String> expanded = 103 myMdmEidMatchOnlyLinkExpandSvc.expandMdmBySourceResourceId(requestPartitionId, memberId); 104 allResourceIds.addAll(expanded); 105 } 106 } 107 // Convert all resourceIds to IIdType and resolve in batch 108 List<IIdType> idTypes = allResourceIds.stream() 109 .map(id -> myFhirContext.getVersion().newIdType(id)) 110 .collect(Collectors.toList()); 111 List<JpaPid> pidList = myIdHelperService.resolveResourcePids( 112 requestPartitionId, 113 idTypes, 114 ResolveIdentityMode.excludeDeleted().cacheOk()); 115 return new HashSet<>(pidList); 116 } 117 118 @Override 119 public void annotateResource(IBaseResource resource) { 120 // This function is normally used to add golden resource id to the exported resources, 121 // but in the Eid-based match only mode, there isn't any golden resource, so nothing to do here 122 } 123}