
001package ca.uhn.fhir.mdm.svc; 002 003/*- 004 * #%L 005 * HAPI FHIR - Master Data Management 006 * %% 007 * Copyright (C) 2014 - 2023 Smile CDR, Inc. 008 * %% 009 * Licensed under the Apache License, Version 2.0 (the "License"); 010 * you may not use this file except in compliance with the License. 011 * You may obtain a copy of the License at 012 * 013 * http://www.apache.org/licenses/LICENSE-2.0 014 * 015 * Unless required by applicable law or agreed to in writing, software 016 * distributed under the License is distributed on an "AS IS" BASIS, 017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 018 * See the License for the specific language governing permissions and 019 * limitations under the License. 020 * #L% 021 */ 022 023import ca.uhn.fhir.i18n.Msg; 024import ca.uhn.fhir.interceptor.model.RequestPartitionId; 025import ca.uhn.fhir.jpa.api.dao.DaoRegistry; 026import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; 027import ca.uhn.fhir.jpa.dao.IResultIterator; 028import ca.uhn.fhir.jpa.dao.ISearchBuilder; 029import ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails; 030import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc; 031import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; 032import ca.uhn.fhir.mdm.api.IMdmChannelSubmitterSvc; 033import ca.uhn.fhir.mdm.api.IMdmSettings; 034import ca.uhn.fhir.mdm.api.IMdmSubmitSvc; 035import ca.uhn.fhir.mdm.log.Logs; 036import ca.uhn.fhir.rest.api.server.RequestDetails; 037import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; 038import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; 039import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; 040import ca.uhn.fhir.rest.server.provider.ProviderConstants; 041import org.hl7.fhir.instance.model.api.IBaseResource; 042import org.hl7.fhir.instance.model.api.IIdType; 043import org.slf4j.Logger; 044import org.springframework.beans.factory.annotation.Autowired; 045import org.springframework.transaction.annotation.Transactional; 046 047import javax.annotation.Nonnull; 048import javax.annotation.Nullable; 049import java.io.IOException; 050import java.util.ArrayList; 051import java.util.Collection; 052import java.util.Collections; 053import java.util.List; 054import java.util.UUID; 055 056public class MdmSubmitSvcImpl implements IMdmSubmitSvc { 057 058 private static final Logger ourLog = Logs.getMdmTroubleshootingLog(); 059 060 @Autowired 061 private DaoRegistry myDaoRegistry; 062 063 @Autowired 064 private MdmSearchParamSvc myMdmSearchParamSvc; 065 066 @Autowired 067 private IMdmChannelSubmitterSvc myMdmChannelSubmitterSvc; 068 069 @Autowired 070 private IMdmSettings myMdmSettings; 071 072 @Autowired 073 private IRequestPartitionHelperSvc myRequestPartitionHelperSvc; 074 075 public static final int DEFAULT_BUFFER_SIZE = 100; 076 077 private int myBufferSize = DEFAULT_BUFFER_SIZE; 078 079 public MdmSubmitSvcImpl() { 080 } 081 082 @Override 083 @Transactional 084 public long submitAllSourceTypesToMdm(@Nullable String theCriteria, @Nonnull RequestDetails theRequestDetails) { 085 long submittedCount = myMdmSettings.getMdmRules().getMdmTypes().stream() 086 .mapToLong(type -> submitSourceResourceTypeToMdm(type, theCriteria, theRequestDetails)) 087 .sum(); 088 089 return submittedCount; 090 } 091 092 @Override 093 @Transactional 094 public long submitSourceResourceTypeToMdm(String theSourceResourceType, @Nullable String theCriteria, @Nonnull RequestDetails theRequestDetails) { 095 if (theCriteria == null) { 096 ourLog.info("Submitting all resources of type {} to MDM", theSourceResourceType); 097 } else { 098 ourLog.info("Submitting resources of type {} with criteria {} to MDM", theSourceResourceType, theCriteria); 099 } 100 101 validateSourceType(theSourceResourceType); 102 SearchParameterMap spMap = myMdmSearchParamSvc.getSearchParameterMapFromCriteria(theSourceResourceType, theCriteria); 103 spMap.setLoadSynchronous(true); 104 spMap.setCount(myBufferSize); 105 ISearchBuilder searchBuilder = myMdmSearchParamSvc.generateSearchBuilderForType(theSourceResourceType); 106 107 RequestPartitionId requestPartitionId = myRequestPartitionHelperSvc.determineReadPartitionForRequestForSearchType(theRequestDetails, theSourceResourceType, spMap, null); 108 return submitAllMatchingResourcesToMdmChannel(spMap, searchBuilder, requestPartitionId); 109 } 110 111 private long submitAllMatchingResourcesToMdmChannel(SearchParameterMap theSpMap, ISearchBuilder theSearchBuilder, RequestPartitionId theRequestPartitionId) { 112 SearchRuntimeDetails searchRuntimeDetails = new SearchRuntimeDetails(null, UUID.randomUUID().toString()); 113 long total = 0; 114 try (IResultIterator query = theSearchBuilder.createQuery(theSpMap, searchRuntimeDetails, null, theRequestPartitionId)) { 115 Collection<IResourcePersistentId> pidBatch; 116 do { 117 pidBatch = query.getNextResultBatch(myBufferSize); 118 total += loadPidsAndSubmitToMdmChannel(theSearchBuilder, pidBatch); 119 } while (query.hasNext()); 120 } catch (IOException theE) { 121 throw new InternalErrorException(Msg.code(749) + "Failure while attempting to query resources for " + ProviderConstants.OPERATION_MDM_SUBMIT, theE); 122 } 123 ourLog.info("MDM Submit complete. Submitted a total of {} resources.", total); 124 return total; 125 } 126 127 /** 128 * Given a collection of IResourcePersistentId objects, and a search builder, load the IBaseResources and submit them to 129 * the MDM channel for processing. 130 * 131 * @param theSearchBuilder the related DAO search builder. 132 * @param thePidsToSubmit The collection of PIDs whos resources you want to submit for MDM processing. 133 * 134 * @return The total count of submitted resources. 135 */ 136 private long loadPidsAndSubmitToMdmChannel(ISearchBuilder theSearchBuilder, Collection<IResourcePersistentId> thePidsToSubmit) { 137 List<IBaseResource> resourcesToSubmit = new ArrayList<>(); 138 theSearchBuilder.loadResourcesByPid(thePidsToSubmit, Collections.emptyList(), resourcesToSubmit, false, null); 139 ourLog.info("Submitting {} resources to MDM", resourcesToSubmit.size()); 140 resourcesToSubmit 141 .forEach(resource -> myMdmChannelSubmitterSvc.submitResourceToMdmChannel(resource)); 142 return resourcesToSubmit.size(); 143 } 144 145 @Override 146 @Transactional 147 public long submitPractitionerTypeToMdm(@Nullable String theCriteria, @Nonnull RequestDetails theRequestDetails) { 148 return submitSourceResourceTypeToMdm("Practitioner", theCriteria, theRequestDetails); 149 } 150 151 @Override 152 @Transactional 153 public long submitPatientTypeToMdm(@Nullable String theCriteria, @Nonnull RequestDetails theRequestDetails) { 154 return submitSourceResourceTypeToMdm("Patient", theCriteria, theRequestDetails); 155 } 156 157 @Override 158 @Transactional 159 public long submitSourceResourceToMdm(IIdType theId, RequestDetails theRequestDetails) { 160 validateSourceType(theId.getResourceType()); 161 IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(theId.getResourceType()); 162 IBaseResource read = resourceDao.read(theId, theRequestDetails); 163 myMdmChannelSubmitterSvc.submitResourceToMdmChannel(read); 164 return 1; 165 } 166 167 @Override 168 public void setMdmSettings(IMdmSettings theMdmSettings) { 169 myMdmSettings = theMdmSettings; 170 } 171 172 private void validateSourceType(String theResourceType) { 173 if(!myMdmSettings.getMdmRules().getMdmTypes().contains(theResourceType)) { 174 throw new InvalidRequestException(Msg.code(750) + ProviderConstants.OPERATION_MDM_SUBMIT + " does not support resource type: " + theResourceType); 175 } 176 } 177 178 @Override 179 public void setBufferSize(int myBufferSize) { 180 this.myBufferSize = myBufferSize; 181 } 182}