
001package ca.uhn.fhir.jpa.dao.dstu3; 002 003/* 004 * #%L 005 * HAPI FHIR JPA Server 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.i18n.Msg; 024import ca.uhn.fhir.context.FhirContext; 025import ca.uhn.fhir.context.support.IValidationSupport; 026import ca.uhn.fhir.context.support.IValidationSupport.CodeValidationResult; 027import ca.uhn.fhir.context.support.ValidationSupportContext; 028import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem; 029import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; 030import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource; 031import ca.uhn.fhir.jpa.model.entity.ResourceTable; 032import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; 033import ca.uhn.fhir.jpa.term.api.ITermCodeSystemStorageSvc; 034import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc; 035import ca.uhn.fhir.jpa.util.LogicUtil; 036import ca.uhn.fhir.rest.api.server.RequestDetails; 037import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId; 038import ca.uhn.fhir.rest.api.server.storage.TransactionDetails; 039import ca.uhn.fhir.rest.param.TokenParam; 040import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; 041import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_30_40; 042import org.hl7.fhir.convertors.factory.VersionConvertorFactory_30_40; 043import org.hl7.fhir.dstu3.model.CodeSystem; 044import org.hl7.fhir.dstu3.model.CodeableConcept; 045import org.hl7.fhir.dstu3.model.Coding; 046import org.hl7.fhir.instance.model.api.IBaseResource; 047import org.hl7.fhir.instance.model.api.IIdType; 048import org.hl7.fhir.instance.model.api.IPrimitiveType; 049import org.springframework.beans.factory.annotation.Autowired; 050 051import javax.annotation.Nonnull; 052import javax.transaction.Transactional; 053import java.util.ArrayList; 054import java.util.Date; 055import java.util.List; 056import java.util.Set; 057 058import static org.apache.commons.lang3.StringUtils.isNotBlank; 059 060@Transactional 061public class FhirResourceDaoCodeSystemDstu3 extends BaseHapiFhirResourceDao<CodeSystem> implements IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept> { 062 063 private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoCodeSystemDstu3.class); 064 @Autowired 065 protected ITermCodeSystemStorageSvc myTerminologyCodeSystemStorageSvc; 066 @Autowired 067 protected ITermDeferredStorageSvc myTermDeferredStorageSvc; 068 @Autowired 069 private IValidationSupport myValidationSupport; 070 @Autowired 071 private FhirContext myFhirContext; 072 073 public FhirResourceDaoCodeSystemDstu3() { 074 super(); 075 } 076 077 @Override 078 public List<IIdType> findCodeSystemIdsContainingSystemAndCode(String theCode, String theSystem, RequestDetails theRequest) { 079 List<ResourcePersistentId> ids = searchForIds(new SearchParameterMap(CodeSystem.SP_CODE, new TokenParam(theSystem, theCode)), theRequest); 080 List<IIdType> valueSetIds = new ArrayList<>(); 081 for (ResourcePersistentId next : ids) { 082 IIdType id = myIdHelperService.translatePidIdToForcedId(myFhirContext, "CodeSystem", next); 083 valueSetIds.add(id); 084 } 085 return valueSetIds; 086 } 087 088 @Nonnull 089 @Override 090 public IValidationSupport.LookupCodeResult lookupCode(IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, Coding theCoding, RequestDetails theRequestDetails) { 091 return lookupCode(theCode, theSystem, theCoding, null, theRequestDetails); 092 } 093 094 @Nonnull 095 @Override 096 public IValidationSupport.LookupCodeResult lookupCode(IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, Coding theCoding, IPrimitiveType<String> theDisplayLanguage, RequestDetails theRequestDetails) { 097 boolean haveCoding = theCoding != null && isNotBlank(theCoding.getSystem()) && isNotBlank(theCoding.getCode()); 098 boolean haveCode = theCode != null && theCode.isEmpty() == false; 099 boolean haveSystem = theSystem != null && theSystem.isEmpty() == false; 100 boolean haveDisplayLanguage = theDisplayLanguage != null && theDisplayLanguage.isEmpty() == false; 101 102 if (!haveCoding && !(haveSystem && haveCode)) { 103 throw new InvalidRequestException(Msg.code(1075) + "No code, coding, or codeableConcept provided to validate"); 104 } 105 if (!LogicUtil.multiXor(haveCoding, (haveSystem && haveCode)) || (haveSystem != haveCode)) { 106 throw new InvalidRequestException(Msg.code(1076) + "$lookup can only validate (system AND code) OR (coding.system AND coding.code)"); 107 } 108 109 String code; 110 String system; 111 if (haveCoding) { 112 code = theCoding.getCode(); 113 if (theCoding.hasVersion()) { 114 system = theCoding.getSystem() + "|" + theCoding.getVersion(); 115 } else { 116 system = theCoding.getSystem(); 117 } 118 } else { 119 code = theCode.getValue(); 120 system = theSystem.getValue(); 121 } 122 123 String displayLanguage = null; 124 if (haveDisplayLanguage) { 125 displayLanguage = theDisplayLanguage.getValue(); 126 } 127 128 ourLog.debug("Looking up {} / {}", system, code); 129 130 if (myValidationSupport.isCodeSystemSupported(new ValidationSupportContext(myValidationSupport), system)) { 131 ourLog.debug("Code system {} is supported", system); 132 IValidationSupport.LookupCodeResult result = myValidationSupport.lookupCode(new ValidationSupportContext(myValidationSupport), system, code, displayLanguage); 133 if (result != null) { 134 return result; 135 } 136 } 137 138 // We didn't find it.. 139 return IValidationSupport.LookupCodeResult.notFound(system, code); 140 } 141 142 @Override 143 public SubsumesResult subsumes(IPrimitiveType<String> theCodeA, IPrimitiveType<String> theCodeB, IPrimitiveType<String> theSystem, Coding theCodingA, Coding theCodingB, RequestDetails theRequestDetails) { 144 return myTerminologySvc.subsumes(theCodeA, theCodeB, theSystem, theCodingA, theCodingB); 145 } 146 147 @Override 148 protected void preDelete(CodeSystem theResourceToDelete, ResourceTable theEntityToDelete) { 149 super.preDelete(theResourceToDelete, theEntityToDelete); 150 151 myTermDeferredStorageSvc.deleteCodeSystemForResource(theEntityToDelete); 152 153 } 154 155 @Override 156 public ResourceTable updateEntity(RequestDetails theRequest, IBaseResource theResource, IBasePersistedResource theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing, 157 boolean theUpdateVersion, TransactionDetails theTransactionDetails, boolean theForceUpdate, boolean theCreateNewHistoryEntry) { 158 ResourceTable retVal = super.updateEntity(theRequest, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theTransactionDetails, theForceUpdate, theCreateNewHistoryEntry); 159 if (!retVal.isUnchangedInCurrentOperation()) { 160 161 CodeSystem csDstu3 = (CodeSystem) theResource; 162 163 org.hl7.fhir.r4.model.CodeSystem cs = (org.hl7.fhir.r4.model.CodeSystem) VersionConvertorFactory_30_40.convertResource(csDstu3, new BaseAdvisor_30_40(false)); 164 addPidToResource(theEntity, cs); 165 166 myTerminologyCodeSystemStorageSvc.storeNewCodeSystemVersionIfNeeded(cs, (ResourceTable) theEntity); 167 168 } 169 170 return retVal; 171 } 172 173 @Override 174 public CodeValidationResult validateCode(IIdType theCodeSystemId, IPrimitiveType<String> theCodeSystemUrl, IPrimitiveType<String> theVersion, IPrimitiveType<String> theCode, 175 IPrimitiveType<String> theDisplay, Coding theCoding, CodeableConcept theCodeableConcept, RequestDetails theRequestDetails) { 176 throw new UnsupportedOperationException(Msg.code(1077)); 177 } 178 179}