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