001package ca.uhn.fhir.jpa.mdm.svc; 002 003/*- 004 * #%L 005 * HAPI FHIR JPA Server - Master Data Management 006 * %% 007 * Copyright (C) 2014 - 2022 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.context.FhirContext; 024import ca.uhn.fhir.i18n.Msg; 025import ca.uhn.fhir.interceptor.model.RequestPartitionId; 026import ca.uhn.fhir.jpa.api.svc.IIdHelperService; 027import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc; 028import ca.uhn.fhir.jpa.mdm.util.MdmPartitionHelper; 029import ca.uhn.fhir.jpa.model.entity.PartitionablePartitionId; 030import ca.uhn.fhir.mdm.api.IMdmLink; 031import ca.uhn.fhir.mdm.api.IMdmLinkCreateSvc; 032import ca.uhn.fhir.mdm.api.IMdmSettings; 033import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; 034import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; 035import ca.uhn.fhir.mdm.log.Logs; 036import ca.uhn.fhir.mdm.model.MdmTransactionContext; 037import ca.uhn.fhir.mdm.util.MdmResourceUtil; 038import ca.uhn.fhir.mdm.util.MessageHelper; 039import ca.uhn.fhir.rest.api.Constants; 040import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId; 041import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; 042import org.hl7.fhir.instance.model.api.IAnyResource; 043import org.slf4j.Logger; 044import org.springframework.beans.factory.annotation.Autowired; 045import org.springframework.transaction.annotation.Transactional; 046 047import java.util.List; 048import java.util.Objects; 049import java.util.Optional; 050 051public class MdmLinkCreateSvcImpl implements IMdmLinkCreateSvc { 052 private static final Logger ourLog = Logs.getMdmTroubleshootingLog(); 053 054 @Autowired 055 FhirContext myFhirContext; 056 @Autowired 057 IIdHelperService myIdHelperService; 058 @Autowired 059 MdmLinkDaoSvc myMdmLinkDaoSvc; 060 @Autowired 061 IMdmSettings myMdmSettings; 062 @Autowired 063 MessageHelper myMessageHelper; 064 @Autowired 065 MdmPartitionHelper myMdmPartitionHelper; 066 067 @Transactional 068 @Override 069 public IAnyResource createLink(IAnyResource theGoldenResource, IAnyResource theSourceResource, MdmMatchResultEnum theMatchResult, MdmTransactionContext theMdmContext) { 070 String sourceType = myFhirContext.getResourceType(theSourceResource); 071 072 validateCreateLinkRequest(theGoldenResource, theSourceResource, sourceType); 073 074 ResourcePersistentId goldenResourceId = myIdHelperService.getPidOrThrowException(theGoldenResource); 075 ResourcePersistentId targetId = myIdHelperService.getPidOrThrowException(theSourceResource); 076 077 // check if the golden resource and the source resource are in the same partition, throw error if not 078 myMdmPartitionHelper.validateResourcesInSamePartition(theGoldenResource, theSourceResource); 079 080 Optional<? extends IMdmLink> optionalMdmLink = myMdmLinkDaoSvc.getLinkByGoldenResourcePidAndSourceResourcePid(goldenResourceId, targetId); 081 if (optionalMdmLink.isPresent()) { 082 throw new InvalidRequestException(Msg.code(753) + myMessageHelper.getMessageForPresentLink(theGoldenResource, theSourceResource)); 083 } 084 085 List<? extends IMdmLink> mdmLinks = myMdmLinkDaoSvc.getMdmLinksBySourcePidAndMatchResult(targetId, MdmMatchResultEnum.MATCH); 086 if (mdmLinks.size() > 0 && theMatchResult == MdmMatchResultEnum.MATCH) { 087 throw new InvalidRequestException(Msg.code(754) + myMessageHelper.getMessageForMultipleGoldenRecords(theSourceResource)); 088 } 089 090 IMdmLink mdmLink = myMdmLinkDaoSvc.getOrCreateMdmLinkByGoldenResourceAndSourceResource(theGoldenResource, theSourceResource); 091 mdmLink.setLinkSource(MdmLinkSourceEnum.MANUAL); 092 mdmLink.setMdmSourceType(sourceType); 093 if (theMatchResult == null) { 094 mdmLink.setMatchResult(MdmMatchResultEnum.MATCH); 095 } else { 096 mdmLink.setMatchResult(theMatchResult); 097 } 098 // Add partition for the mdm link if it doesn't exist 099 RequestPartitionId goldenResourcePartitionId = (RequestPartitionId) theGoldenResource.getUserData(Constants.RESOURCE_PARTITION_ID); 100 if (goldenResourcePartitionId != null && goldenResourcePartitionId.hasPartitionIds() && goldenResourcePartitionId.getFirstPartitionIdOrNull() != null && 101 (mdmLink.getPartitionId() == null || mdmLink.getPartitionId().getPartitionId() == null)) { 102 mdmLink.setPartitionId(new PartitionablePartitionId(goldenResourcePartitionId.getFirstPartitionIdOrNull(), goldenResourcePartitionId.getPartitionDate())); 103 } 104 ourLog.info("Manually creating a " + theGoldenResource.getIdElement().toVersionless() + " to " + theSourceResource.getIdElement().toVersionless() + " mdm link."); 105 myMdmLinkDaoSvc.save(mdmLink); 106 107 return theGoldenResource; 108 } 109 110 private void validateCreateLinkRequest(IAnyResource theGoldenRecord, IAnyResource theSourceResource, String theSourceType) { 111 String goldenRecordType = myFhirContext.getResourceType(theGoldenRecord); 112 113 if (!myMdmSettings.isSupportedMdmType(goldenRecordType)) { 114 throw new InvalidRequestException(Msg.code(755) + myMessageHelper.getMessageForUnsupportedFirstArgumentTypeInUpdate(goldenRecordType)); 115 } 116 117 if (!myMdmSettings.isSupportedMdmType(theSourceType)) { 118 throw new InvalidRequestException(Msg.code(756) + myMessageHelper.getMessageForUnsupportedSecondArgumentTypeInUpdate(theSourceType)); 119 } 120 121 if (!Objects.equals(goldenRecordType, theSourceType)) { 122 throw new InvalidRequestException(Msg.code(757) + myMessageHelper.getMessageForArgumentTypeMismatchInUpdate(goldenRecordType, theSourceType)); 123 } 124 125 if (!MdmResourceUtil.isMdmManaged(theGoldenRecord)) { 126 throw new InvalidRequestException(Msg.code(758) + myMessageHelper.getMessageForUnmanagedResource()); 127 } 128 129 if (!MdmResourceUtil.isMdmAllowed(theSourceResource)) { 130 throw new InvalidRequestException(Msg.code(759) + myMessageHelper.getMessageForUnsupportedSourceResource()); 131 } 132 } 133}