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