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