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.provider; 021 022import ca.uhn.fhir.context.FhirContext; 023import ca.uhn.fhir.i18n.Msg; 024import ca.uhn.fhir.interceptor.api.HookParams; 025import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster; 026import ca.uhn.fhir.interceptor.api.Pointcut; 027import ca.uhn.fhir.mdm.api.IMdmControllerSvc; 028import ca.uhn.fhir.mdm.api.IMdmSettings; 029import ca.uhn.fhir.mdm.api.IMdmSubmitSvc; 030import ca.uhn.fhir.mdm.api.MdmConstants; 031import ca.uhn.fhir.mdm.api.paging.MdmPageRequest; 032import ca.uhn.fhir.mdm.api.params.MdmQuerySearchParameters; 033import ca.uhn.fhir.mdm.model.MdmCreateOrUpdateParams; 034import ca.uhn.fhir.mdm.model.MdmMergeGoldenResourcesParams; 035import ca.uhn.fhir.mdm.model.MdmTransactionContext; 036import ca.uhn.fhir.mdm.model.MdmUnduplicateGoldenResourceParams; 037import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkJson; 038import ca.uhn.fhir.mdm.model.mdmevents.MdmSubmitEvent; 039import ca.uhn.fhir.model.api.annotation.Description; 040import ca.uhn.fhir.rest.annotation.IdParam; 041import ca.uhn.fhir.rest.annotation.Operation; 042import ca.uhn.fhir.rest.annotation.OperationParam; 043import ca.uhn.fhir.rest.api.Constants; 044import ca.uhn.fhir.rest.api.server.RequestDetails; 045import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; 046import ca.uhn.fhir.rest.server.provider.ProviderConstants; 047import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; 048import ca.uhn.fhir.util.ParametersUtil; 049import jakarta.annotation.Nonnull; 050import org.apache.commons.lang3.StringUtils; 051import org.hl7.fhir.instance.model.api.IAnyResource; 052import org.hl7.fhir.instance.model.api.IBaseBundle; 053import org.hl7.fhir.instance.model.api.IBaseParameters; 054import org.hl7.fhir.instance.model.api.IBaseResource; 055import org.hl7.fhir.instance.model.api.IIdType; 056import org.hl7.fhir.instance.model.api.IPrimitiveType; 057import org.slf4j.Logger; 058import org.springframework.data.domain.Page; 059 060import java.math.BigDecimal; 061import java.util.ArrayList; 062import java.util.Collections; 063import java.util.List; 064import java.util.stream.Collectors; 065 066import static ca.uhn.fhir.rest.api.Constants.PARAM_OFFSET; 067import static org.apache.commons.lang3.ObjectUtils.isNotEmpty; 068import static org.apache.commons.lang3.StringUtils.isNotBlank; 069import static org.slf4j.LoggerFactory.getLogger; 070 071public class MdmProviderDstu3Plus extends BaseMdmProvider { 072 private static final Logger ourLog = getLogger(MdmProviderDstu3Plus.class); 073 074 private static final String PATIENT_RESOURCE = "Patient"; 075 private static final String PRACTITIONER_RESOURCE = "Practitioner"; 076 077 private final IMdmControllerSvc myMdmControllerSvc; 078 private final IMdmSubmitSvc myMdmSubmitSvc; 079 private final IMdmSettings myMdmSettings; 080 private final MdmControllerHelper myMdmControllerHelper; 081 082 private final IInterceptorBroadcaster myInterceptorBroadcaster; 083 084 public static final int DEFAULT_PAGE_SIZE = 20; 085 public static final int MAX_PAGE_SIZE = 100; 086 087 /** 088 * Constructor 089 * <p> 090 * Note that this is not a spring bean. Any necessary injections should 091 * happen in the constructor 092 */ 093 public MdmProviderDstu3Plus( 094 FhirContext theFhirContext, 095 IMdmControllerSvc theMdmControllerSvc, 096 MdmControllerHelper theMdmHelper, 097 IMdmSubmitSvc theMdmSubmitSvc, 098 IInterceptorBroadcaster theIInterceptorBroadcaster, 099 IMdmSettings theIMdmSettings) { 100 super(theFhirContext); 101 myMdmControllerSvc = theMdmControllerSvc; 102 myMdmControllerHelper = theMdmHelper; 103 myMdmSubmitSvc = theMdmSubmitSvc; 104 myInterceptorBroadcaster = theIInterceptorBroadcaster; 105 myMdmSettings = theIMdmSettings; 106 } 107 108 /** 109 * Searches for matches for the provided patient resource 110 * @param thePatient - the patient resource 111 * @param theRequestDetails - the request details 112 * @return - any matches to the provided patient resource 113 */ 114 @Operation(name = ProviderConstants.EMPI_MATCH, typeName = "Patient") 115 public IBaseBundle match( 116 @OperationParam(name = ProviderConstants.MDM_MATCH_RESOURCE, min = 1, max = 1, typeName = "Patient") 117 IAnyResource thePatient, 118 RequestDetails theRequestDetails) { 119 if (thePatient == null) { 120 throw new InvalidRequestException(Msg.code(1498) + "resource may not be null"); 121 } 122 return myMdmControllerHelper.getMatchesAndPossibleMatchesForResource(thePatient, "Patient", theRequestDetails); 123 } 124 125 /** 126 * Searches for matches for hte provided resource. 127 * 128 * @param theResource - the resource to match on 129 * @param theResourceType - the resource type 130 * @param theRequestDetails - the request details 131 * @return - any matches to the provided resource 132 */ 133 @Operation(name = ProviderConstants.MDM_MATCH) 134 public IBaseBundle serverMatch( 135 @OperationParam(name = ProviderConstants.MDM_MATCH_RESOURCE, min = 1, max = 1) IAnyResource theResource, 136 @OperationParam(name = ProviderConstants.MDM_RESOURCE_TYPE, min = 1, max = 1, typeName = "string") 137 IPrimitiveType<String> theResourceType, 138 RequestDetails theRequestDetails) { 139 if (theResource == null) { 140 throw new InvalidRequestException(Msg.code(1499) + "resource may not be null"); 141 } 142 return myMdmControllerHelper.getMatchesAndPossibleMatchesForResource( 143 theResource, theResourceType.getValueAsString(), theRequestDetails); 144 } 145 146 @Operation(name = ProviderConstants.MDM_MERGE_GOLDEN_RESOURCES) 147 public IBaseResource mergeGoldenResources( 148 @OperationParam( 149 name = ProviderConstants.MDM_MERGE_GR_FROM_GOLDEN_RESOURCE_ID, 150 min = 1, 151 max = 1, 152 typeName = "string") 153 IPrimitiveType<String> theFromGoldenResourceId, 154 @OperationParam( 155 name = ProviderConstants.MDM_MERGE_GR_TO_GOLDEN_RESOURCE_ID, 156 min = 1, 157 max = 1, 158 typeName = "string") 159 IPrimitiveType<String> theToGoldenResourceId, 160 @OperationParam(name = ProviderConstants.MDM_MERGE_RESOURCE, max = 1) IAnyResource theMergedResource, 161 RequestDetails theRequestDetails) { 162 validateMergeParameters(theFromGoldenResourceId, theToGoldenResourceId); 163 164 MdmTransactionContext.OperationType operationType = (theMergedResource == null) 165 ? MdmTransactionContext.OperationType.MERGE_GOLDEN_RESOURCES 166 : MdmTransactionContext.OperationType.MANUAL_MERGE_GOLDEN_RESOURCES; 167 MdmTransactionContext txContext = createMdmContext( 168 theRequestDetails, 169 operationType, 170 getResourceType(ProviderConstants.MDM_MERGE_GR_FROM_GOLDEN_RESOURCE_ID, theFromGoldenResourceId)); 171 172 MdmMergeGoldenResourcesParams params = new MdmMergeGoldenResourcesParams(); 173 params.setFromGoldenResourceId(theFromGoldenResourceId.getValueAsString()); 174 params.setToGoldenResourceId(theToGoldenResourceId.getValueAsString()); 175 params.setManuallyMergedResource(theMergedResource); 176 params.setMdmTransactionContext(txContext); 177 params.setRequestDetails(theRequestDetails); 178 179 return myMdmControllerSvc.mergeGoldenResources(params); 180 } 181 182 @Operation(name = ProviderConstants.MDM_UPDATE_LINK) 183 public IBaseResource updateLink( 184 @OperationParam(name = ProviderConstants.MDM_UPDATE_LINK_GOLDEN_RESOURCE_ID, min = 1, max = 1) 185 IPrimitiveType<String> theGoldenResourceId, 186 @OperationParam(name = ProviderConstants.MDM_UPDATE_LINK_RESOURCE_ID, min = 1, max = 1) 187 IPrimitiveType<String> theResourceId, 188 @OperationParam(name = ProviderConstants.MDM_UPDATE_LINK_MATCH_RESULT, min = 1, max = 1) 189 IPrimitiveType<String> theMatchResult, 190 ServletRequestDetails theRequestDetails) { 191 validateUpdateLinkParameters(theGoldenResourceId, theResourceId, theMatchResult); 192 193 MdmCreateOrUpdateParams updateLinkParams = new MdmCreateOrUpdateParams(); 194 updateLinkParams.setGoldenResourceId(theGoldenResourceId.getValueAsString()); 195 updateLinkParams.setResourceId(theResourceId.getValue()); 196 updateLinkParams.setMatchResult(MdmControllerUtil.extractMatchResultOrNull(theMatchResult.getValue())); 197 updateLinkParams.setMdmContext(createMdmContext( 198 theRequestDetails, 199 MdmTransactionContext.OperationType.UPDATE_LINK, 200 getResourceType(ProviderConstants.MDM_UPDATE_LINK_GOLDEN_RESOURCE_ID, theGoldenResourceId))); 201 updateLinkParams.setRequestDetails(theRequestDetails); 202 203 return myMdmControllerSvc.updateLink(updateLinkParams); 204 } 205 206 @Operation(name = ProviderConstants.MDM_CREATE_LINK) 207 public IBaseResource createLink( 208 @OperationParam(name = ProviderConstants.MDM_CREATE_LINK_GOLDEN_RESOURCE_ID, min = 1, max = 1) 209 IPrimitiveType<String> theGoldenResourceId, 210 @OperationParam(name = ProviderConstants.MDM_CREATE_LINK_RESOURCE_ID, min = 1, max = 1) 211 IPrimitiveType<String> theResourceId, 212 @OperationParam(name = ProviderConstants.MDM_CREATE_LINK_MATCH_RESULT, min = 0, max = 1) 213 IPrimitiveType<String> theMatchResult, 214 ServletRequestDetails theRequestDetails) { 215 validateCreateLinkParameters(theGoldenResourceId, theResourceId, theMatchResult); 216 217 MdmCreateOrUpdateParams params = new MdmCreateOrUpdateParams(); 218 params.setGoldenResourceId(theGoldenResourceId.getValueAsString()); 219 params.setResourceId(theResourceId.getValue()); 220 params.setMatchResult(MdmControllerUtil.extractMatchResultOrNull(extractStringOrNull(theMatchResult))); 221 params.setRequestDetails(theRequestDetails); 222 params.setMdmContext(createMdmContext( 223 theRequestDetails, 224 MdmTransactionContext.OperationType.CREATE_LINK, 225 getResourceType(ProviderConstants.MDM_CREATE_LINK_GOLDEN_RESOURCE_ID, theGoldenResourceId))); 226 227 return myMdmControllerSvc.createLink(params); 228 } 229 230 @Operation( 231 name = ProviderConstants.OPERATION_MDM_CLEAR, 232 returnParameters = { 233 @OperationParam(name = ProviderConstants.OPERATION_BATCH_RESPONSE_JOB_ID, typeName = "decimal") 234 }) 235 public IBaseParameters clearMdmLinks( 236 @OperationParam( 237 name = ProviderConstants.OPERATION_MDM_CLEAR_RESOURCE_NAME, 238 min = 0, 239 max = OperationParam.MAX_UNLIMITED, 240 typeName = "string") 241 List<IPrimitiveType<String>> theResourceNames, 242 @OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_SIZE, typeName = "decimal", min = 0, max = 1) 243 IPrimitiveType<BigDecimal> theBatchSize, 244 ServletRequestDetails theRequestDetails) { 245 246 List<String> resourceNames = new ArrayList<>(); 247 248 if (isNotEmpty(theResourceNames)) { 249 resourceNames.addAll( 250 theResourceNames.stream().map(IPrimitiveType::getValue).collect(Collectors.toList())); 251 validateResourceNames(resourceNames); 252 } else { 253 resourceNames.addAll(myMdmSettings.getMdmRules().getMdmTypes()); 254 } 255 256 return myMdmControllerSvc.submitMdmClearJob(resourceNames, theBatchSize, theRequestDetails); 257 } 258 259 private void validateResourceNames(List<String> theResourceNames) { 260 for (String resourceName : theResourceNames) { 261 if (!myMdmSettings.isSupportedMdmType(resourceName)) { 262 throw new InvalidRequestException(Msg.code(1500) + ProviderConstants.OPERATION_MDM_CLEAR 263 + " does not support resource type: " + resourceName); 264 } 265 } 266 } 267 268 // Is a set of the OR sufficient ot the contenxt she's investigating? 269 @Operation(name = ProviderConstants.MDM_QUERY_LINKS, idempotent = true) 270 public IBaseParameters queryLinks( 271 @OperationParam( 272 name = ProviderConstants.MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID, 273 min = 0, 274 max = 1, 275 typeName = "string") 276 IPrimitiveType<String> theGoldenResourceId, 277 @OperationParam(name = ProviderConstants.MDM_QUERY_LINKS_RESOURCE_ID, min = 0, max = 1, typeName = "string") 278 IPrimitiveType<String> theResourceId, 279 @OperationParam( 280 name = ProviderConstants.MDM_QUERY_LINKS_MATCH_RESULT, 281 min = 0, 282 max = 1, 283 typeName = "string") 284 IPrimitiveType<String> theMatchResult, 285 @OperationParam(name = ProviderConstants.MDM_QUERY_LINKS_LINK_SOURCE, min = 0, max = 1, typeName = "string") 286 IPrimitiveType<String> theLinkSource, 287 @Description( 288 value = 289 "Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.") 290 @OperationParam(name = PARAM_OFFSET, min = 0, max = 1, typeName = "integer") 291 IPrimitiveType<Integer> theOffset, 292 @Description( 293 value = 294 "Results from this method are returned across multiple pages. This parameter controls the size of those pages.") 295 @OperationParam(name = Constants.PARAM_COUNT, min = 0, max = 1, typeName = "integer") 296 IPrimitiveType<Integer> theCount, 297 @OperationParam(name = Constants.PARAM_SORT, min = 0, max = 1, typeName = "string") 298 IPrimitiveType<String> theSort, 299 ServletRequestDetails theRequestDetails, 300 @OperationParam(name = ProviderConstants.MDM_RESOURCE_TYPE, min = 0, max = 1, typeName = "string") 301 IPrimitiveType<String> theResourceType) { 302 MdmPageRequest mdmPageRequest = new MdmPageRequest(theOffset, theCount, DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE); 303 MdmTransactionContext mdmContext = createMdmContext( 304 theRequestDetails, 305 MdmTransactionContext.OperationType.QUERY_LINKS, 306 getResourceType( 307 ProviderConstants.MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID, theGoldenResourceId, theResourceType)); 308 MdmQuerySearchParameters mdmQuerySearchParameters = new MdmQuerySearchParameters(mdmPageRequest) 309 .setGoldenResourceId(extractStringOrNull(theGoldenResourceId)) 310 .setSourceId(extractStringOrNull(theResourceId)) 311 .setLinkSource(extractStringOrNull(theLinkSource)) 312 .setMatchResult(extractStringOrNull(theMatchResult)) 313 .setResourceType(extractStringOrNull(theResourceType)) 314 .setSort(extractStringOrNull(theSort)); 315 316 Page<MdmLinkJson> mdmLinkJson = 317 myMdmControllerSvc.queryLinks(mdmQuerySearchParameters, mdmContext, theRequestDetails); 318 return parametersFromMdmLinks(mdmLinkJson, true, theRequestDetails, mdmPageRequest); 319 } 320 321 @Operation(name = ProviderConstants.MDM_DUPLICATE_GOLDEN_RESOURCES, idempotent = true) 322 public IBaseParameters getDuplicateGoldenResources( 323 @Description( 324 formalDefinition = 325 "Results from this method are returned across multiple pages. This parameter controls the offset when fetching a page.") 326 @OperationParam(name = PARAM_OFFSET, min = 0, max = 1, typeName = "integer") 327 IPrimitiveType<Integer> theOffset, 328 @Description( 329 formalDefinition = 330 "Results from this method are returned across multiple pages. This parameter controls the size of those pages.") 331 @OperationParam(name = Constants.PARAM_COUNT, min = 0, max = 1, typeName = "integer") 332 IPrimitiveType<Integer> theCount, 333 ServletRequestDetails theRequestDetails, 334 @Description(formalDefinition = "This parameter controls the returned resource type.") 335 @OperationParam(name = ProviderConstants.MDM_RESOURCE_TYPE, min = 0, max = 1, typeName = "string") 336 IPrimitiveType<String> theResourceType) { 337 338 MdmPageRequest mdmPageRequest = new MdmPageRequest(theOffset, theCount, DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE); 339 340 Page<MdmLinkJson> possibleDuplicates = myMdmControllerSvc.getDuplicateGoldenResources( 341 createMdmContext( 342 theRequestDetails, MdmTransactionContext.OperationType.DUPLICATE_GOLDEN_RESOURCES, null), 343 mdmPageRequest, 344 theRequestDetails, 345 extractStringOrNull(theResourceType)); 346 347 return parametersFromMdmLinks(possibleDuplicates, false, theRequestDetails, mdmPageRequest); 348 } 349 350 @Operation(name = ProviderConstants.MDM_NOT_DUPLICATE) 351 public IBaseParameters notDuplicate( 352 @OperationParam( 353 name = ProviderConstants.MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID, 354 min = 1, 355 max = 1, 356 typeName = "string") 357 IPrimitiveType<String> theGoldenResourceId, 358 @OperationParam(name = ProviderConstants.MDM_QUERY_LINKS_RESOURCE_ID, min = 1, max = 1, typeName = "string") 359 IPrimitiveType<String> theResourceId, 360 ServletRequestDetails theRequestDetails) { 361 validateNotDuplicateParameters(theGoldenResourceId, theResourceId); 362 363 MdmUnduplicateGoldenResourceParams params = new MdmUnduplicateGoldenResourceParams(); 364 params.setRequestDetails(theRequestDetails); 365 params.setGoldenResourceId(theGoldenResourceId.getValueAsString()); 366 params.setTargetGoldenResourceId(theResourceId.getValueAsString()); 367 params.setMdmContext(createMdmContext( 368 theRequestDetails, 369 MdmTransactionContext.OperationType.NOT_DUPLICATE, 370 getResourceType(ProviderConstants.MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID, theGoldenResourceId))); 371 372 myMdmControllerSvc.unduplicateGoldenResource(params); 373 374 IBaseParameters retval = ParametersUtil.newInstance(myFhirContext); 375 ParametersUtil.addParameterToParametersBoolean(myFhirContext, retval, "success", true); 376 return retval; 377 } 378 379 @Operation( 380 name = ProviderConstants.OPERATION_MDM_SUBMIT, 381 idempotent = false, 382 returnParameters = { 383 @OperationParam(name = ProviderConstants.OPERATION_MDM_SUBMIT_OUT_PARAM_SUBMITTED, typeName = "integer") 384 }) 385 public IBaseParameters mdmBatchOnAllSourceResources( 386 @OperationParam(name = ProviderConstants.MDM_BATCH_RUN_RESOURCE_TYPE, min = 0, max = 1, typeName = "string") 387 IPrimitiveType<String> theResourceType, 388 @OperationParam(name = ProviderConstants.MDM_BATCH_RUN_CRITERIA, min = 0, max = 1, typeName = "string") 389 IPrimitiveType<String> theCriteria, 390 @OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_SIZE, typeName = "decimal", min = 0, max = 1) 391 IPrimitiveType<BigDecimal> theBatchSize, 392 ServletRequestDetails theRequestDetails) { 393 String criteria = convertStringTypeToString(theCriteria); 394 String resourceType = convertStringTypeToString(theResourceType); 395 long submittedCount; 396 IBaseParameters retval; 397 if (theRequestDetails.isPreferRespondAsync()) { 398 List<String> urls = buildUrlsForJob(criteria, resourceType); 399 retval = myMdmControllerSvc.submitMdmSubmitJob(urls, theBatchSize, theRequestDetails); 400 } else { 401 submittedCount = synchronousMdmSubmit(resourceType, null, criteria, theRequestDetails); 402 retval = buildMdmOutParametersWithCount(submittedCount); 403 } 404 405 return retval; 406 } 407 408 @Nonnull 409 private List<String> buildUrlsForJob(String criteria, String resourceType) { 410 List<String> urls = new ArrayList<>(); 411 if (isNotBlank(resourceType)) { 412 String theUrl = resourceType + "?" + criteria; 413 urls.add(theUrl); 414 } else { 415 myMdmSettings.getMdmRules().getMdmTypes().stream() 416 .map(type -> type + "?" + criteria) 417 .forEach(urls::add); 418 } 419 return urls; 420 } 421 422 private String convertStringTypeToString(IPrimitiveType<String> theCriteria) { 423 return theCriteria == null ? "" : theCriteria.getValueAsString(); 424 } 425 426 @Operation( 427 name = ProviderConstants.OPERATION_MDM_SUBMIT, 428 idempotent = false, 429 typeName = "Patient", 430 returnParameters = { 431 @OperationParam(name = ProviderConstants.OPERATION_BATCH_RESPONSE_JOB_ID, typeName = "integer") 432 }) 433 public IBaseParameters mdmBatchPatientInstance(@IdParam IIdType theIdParam, RequestDetails theRequest) { 434 435 long submittedCount = synchronousMdmSubmit(null, theIdParam, null, theRequest); 436 return buildMdmOutParametersWithCount(submittedCount); 437 } 438 439 @Operation( 440 name = ProviderConstants.OPERATION_MDM_SUBMIT, 441 idempotent = false, 442 typeName = "Patient", 443 returnParameters = { 444 @OperationParam(name = ProviderConstants.OPERATION_BATCH_RESPONSE_JOB_ID, typeName = "integer") 445 }) 446 public IBaseParameters mdmBatchPatientType( 447 @OperationParam(name = ProviderConstants.MDM_BATCH_RUN_CRITERIA, typeName = "string") 448 IPrimitiveType<String> theCriteria, 449 @OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_SIZE, typeName = "decimal", min = 0, max = 1) 450 IPrimitiveType<BigDecimal> theBatchSize, 451 ServletRequestDetails theRequest) { 452 if (theRequest.isPreferRespondAsync()) { 453 String theUrl = PATIENT_RESOURCE + "?"; 454 return myMdmControllerSvc.submitMdmSubmitJob(Collections.singletonList(theUrl), theBatchSize, theRequest); 455 } else { 456 String criteria = convertStringTypeToString(theCriteria); 457 long submittedCount = synchronousMdmSubmit(PATIENT_RESOURCE, null, criteria, theRequest); 458 return buildMdmOutParametersWithCount(submittedCount); 459 } 460 } 461 462 @Operation( 463 name = ProviderConstants.OPERATION_MDM_SUBMIT, 464 idempotent = false, 465 typeName = "Practitioner", 466 returnParameters = { 467 @OperationParam(name = ProviderConstants.OPERATION_BATCH_RESPONSE_JOB_ID, typeName = "integer") 468 }) 469 public IBaseParameters mdmBatchPractitionerInstance(@IdParam IIdType theIdParam, RequestDetails theRequest) { 470 long submittedCount = synchronousMdmSubmit(null, theIdParam, null, theRequest); 471 return buildMdmOutParametersWithCount(submittedCount); 472 } 473 474 @Operation( 475 name = ProviderConstants.OPERATION_MDM_SUBMIT, 476 idempotent = false, 477 typeName = "Practitioner", 478 returnParameters = { 479 @OperationParam(name = ProviderConstants.OPERATION_BATCH_RESPONSE_JOB_ID, typeName = "integer") 480 }) 481 public IBaseParameters mdmBatchPractitionerType( 482 @OperationParam(name = ProviderConstants.MDM_BATCH_RUN_CRITERIA, typeName = "string") 483 IPrimitiveType<String> theCriteria, 484 @OperationParam(name = ProviderConstants.OPERATION_MDM_BATCH_SIZE, typeName = "decimal", min = 0, max = 1) 485 IPrimitiveType<BigDecimal> theBatchSize, 486 ServletRequestDetails theRequest) { 487 if (theRequest.isPreferRespondAsync()) { 488 String theUrl = PRACTITIONER_RESOURCE + "?"; 489 return myMdmControllerSvc.submitMdmSubmitJob(Collections.singletonList(theUrl), theBatchSize, theRequest); 490 } else { 491 String criteria = convertStringTypeToString(theCriteria); 492 long submittedCount = synchronousMdmSubmit(PRACTITIONER_RESOURCE, null, criteria, theRequest); 493 return buildMdmOutParametersWithCount(submittedCount); 494 } 495 } 496 497 /** 498 * Helper function to build the out-parameters for all batch MDM operations. 499 */ 500 public IBaseParameters buildMdmOutParametersWithCount(long theCount) { 501 IBaseParameters retval = ParametersUtil.newInstance(myFhirContext); 502 ParametersUtil.addParameterToParametersLong( 503 myFhirContext, retval, ProviderConstants.OPERATION_MDM_SUBMIT_OUT_PARAM_SUBMITTED, theCount); 504 return retval; 505 } 506 507 private String getResourceType(String theParamName, IPrimitiveType<String> theResourceId) { 508 if (theResourceId != null) { 509 return getResourceType(theParamName, theResourceId.getValueAsString()); 510 } else { 511 return MdmConstants.UNKNOWN_MDM_TYPES; 512 } 513 } 514 515 private String getResourceType( 516 String theParamName, IPrimitiveType<String> theResourceId, IPrimitiveType theResourceType) { 517 return theResourceType != null 518 ? theResourceType.getValueAsString() 519 : getResourceType(theParamName, theResourceId); 520 } 521 522 private String getResourceType(String theParamName, String theResourceId) { 523 if (StringUtils.isEmpty(theResourceId)) { 524 return MdmConstants.UNKNOWN_MDM_TYPES; 525 } 526 IIdType idType = MdmControllerUtil.getGoldenIdDtOrThrowException(theParamName, theResourceId); 527 return idType.getResourceType(); 528 } 529 530 private long synchronousMdmSubmit( 531 String theResourceType, IIdType theIdType, String theCriteria, RequestDetails theRequestDetails) { 532 long submittedCount = -1; 533 List<String> urls = new ArrayList<>(); 534 if (theIdType != null) { 535 submittedCount = mdmSubmitWithId(theIdType, theRequestDetails, urls); 536 } else if (isNotBlank(theResourceType)) { 537 submittedCount = submitResourceTypeWithCriteria(theResourceType, theCriteria, theRequestDetails, urls); 538 } else { 539 submittedCount = submitAll(theCriteria, theRequestDetails, urls); 540 } 541 542 if (myInterceptorBroadcaster.hasHooks(Pointcut.MDM_SUBMIT)) { 543 // MDM_SUBMIT synchronous submit 544 MdmSubmitEvent submitEvent = new MdmSubmitEvent(); 545 submitEvent.setBatchJob(false); 546 submitEvent.setUrls(urls); 547 548 HookParams hookParams = new HookParams(); 549 hookParams.add(RequestDetails.class, theRequestDetails); 550 hookParams.add(MdmSubmitEvent.class, submitEvent); 551 myInterceptorBroadcaster.callHooks(Pointcut.MDM_SUBMIT, hookParams); 552 } 553 554 return submittedCount; 555 } 556 557 private long mdmSubmitWithId(IIdType theIdType, RequestDetails theRequestDetails, List<String> theUrls) { 558 theUrls.add(theIdType.getValue()); 559 return myMdmSubmitSvc.submitSourceResourceToMdm(theIdType, theRequestDetails); 560 } 561 562 private long submitResourceTypeWithCriteria( 563 String theResourceType, String theCriteria, RequestDetails theRequestDetails, List<String> theUrls) { 564 String url = theResourceType; 565 if (isNotEmpty(theCriteria)) { 566 url += "?" + theCriteria; 567 } 568 theUrls.add(url); 569 570 return myMdmSubmitSvc.submitSourceResourceTypeToMdm(theResourceType, theCriteria, theRequestDetails); 571 } 572 573 private long submitAll(String theCriteria, RequestDetails theRequestDetails, List<String> theUrls) { 574 // submitAllSourceTypes only runs through 575 // valid MDM source types 576 List<String> resourceTypes = myMdmSettings.getMdmRules().getMdmTypes(); 577 for (String resourceType : resourceTypes) { 578 String url = resourceType + (isNotEmpty(theCriteria) ? "?" + theCriteria : ""); 579 theUrls.add(url); 580 } 581 return myMdmSubmitSvc.submitAllSourceTypesToMdm(theCriteria, theRequestDetails); 582 } 583}