001/* 002 * #%L 003 * HAPI FHIR JPA Server 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.provider; 021 022import ca.uhn.fhir.context.support.TranslateConceptResults; 023import ca.uhn.fhir.i18n.Msg; 024import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoConceptMap; 025import ca.uhn.fhir.jpa.api.model.TranslationRequest; 026import ca.uhn.fhir.jpa.model.util.JpaConstants; 027import ca.uhn.fhir.jpa.term.TermConceptMappingSvcImpl; 028import ca.uhn.fhir.rest.annotation.IdParam; 029import ca.uhn.fhir.rest.annotation.Operation; 030import ca.uhn.fhir.rest.annotation.OperationParam; 031import ca.uhn.fhir.rest.api.server.RequestDetails; 032import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; 033import ca.uhn.hapi.converters.canonical.VersionCanonicalizer; 034import org.hl7.fhir.instance.model.api.IBaseCoding; 035import org.hl7.fhir.instance.model.api.IBaseDatatype; 036import org.hl7.fhir.instance.model.api.IBaseParameters; 037import org.hl7.fhir.instance.model.api.IBaseResource; 038import org.hl7.fhir.instance.model.api.IIdType; 039import org.hl7.fhir.instance.model.api.IPrimitiveType; 040import org.hl7.fhir.r4.model.CodeableConcept; 041import org.hl7.fhir.r4.model.Coding; 042import org.hl7.fhir.r4.model.ConceptMap; 043import org.hl7.fhir.r4.model.Parameters; 044import org.springframework.beans.factory.annotation.Autowired; 045 046import javax.servlet.http.HttpServletRequest; 047 048import static ca.uhn.fhir.util.DatatypeUtil.toBooleanValue; 049import static ca.uhn.fhir.util.DatatypeUtil.toStringValue; 050import static org.apache.commons.lang3.StringUtils.isNotBlank; 051 052public abstract class BaseJpaResourceProviderConceptMap<T extends IBaseResource> extends BaseJpaResourceProvider<T> { 053 054 @Autowired 055 private VersionCanonicalizer myVersionCanonicalizer; 056 057 @Operation( 058 name = JpaConstants.OPERATION_TRANSLATE, 059 idempotent = true, 060 returnParameters = { 061 @OperationParam(name = "result", typeName = "boolean", min = 1, max = 1), 062 @OperationParam(name = "message", typeName = "string", min = 0, max = 1), 063 }) 064 public IBaseParameters translate( 065 HttpServletRequest theServletRequest, 066 @IdParam(optional = true) IIdType theId, 067 @OperationParam(name = "url", min = 0, max = 1, typeName = "uri") IPrimitiveType<String> theUrl, 068 @OperationParam(name = "conceptMapVersion", min = 0, max = 1, typeName = "string") 069 IPrimitiveType<String> theConceptMapVersion, 070 @OperationParam(name = "code", min = 0, max = 1, typeName = "code") IPrimitiveType<String> theSourceCode, 071 @OperationParam(name = "system", min = 0, max = 1, typeName = "uri") 072 IPrimitiveType<String> theSourceCodeSystem, 073 @OperationParam(name = "version", min = 0, max = 1, typeName = "string") 074 IPrimitiveType<String> theSourceCodeSystemVersion, 075 @OperationParam(name = "source", min = 0, max = 1, typeName = "uri") 076 IPrimitiveType<String> theSourceValueSet, 077 @OperationParam(name = "coding", min = 0, max = 1, typeName = "Coding") IBaseCoding theSourceCoding, 078 @OperationParam(name = "codeableConcept", min = 0, max = 1, typeName = "CodeableConcept") 079 IBaseDatatype theSourceCodeableConcept, 080 @OperationParam(name = "target", min = 0, max = 1, typeName = "uri") 081 IPrimitiveType<String> theTargetValueSet, 082 @OperationParam(name = "targetsystem", min = 0, max = 1, typeName = "uri") 083 IPrimitiveType<String> theTargetCodeSystem, 084 @OperationParam(name = "reverse", min = 0, max = 1, typeName = "boolean") 085 IPrimitiveType<Boolean> theReverse, 086 RequestDetails theRequestDetails) { 087 Coding sourceCoding = myVersionCanonicalizer.codingToCanonical(theSourceCoding); 088 CodeableConcept sourceCodeableConcept = 089 myVersionCanonicalizer.codeableConceptToCanonical(theSourceCodeableConcept); 090 091 boolean haveSourceCode = theSourceCode != null && isNotBlank(theSourceCode.getValue()); 092 boolean haveSourceCodeSystem = theSourceCodeSystem != null && theSourceCodeSystem.hasValue(); 093 boolean haveSourceCodeSystemVersion = 094 theSourceCodeSystemVersion != null && theSourceCodeSystemVersion.hasValue(); 095 boolean haveSourceCoding = sourceCoding != null && sourceCoding.hasCode(); 096 boolean haveSourceCodeableConcept = sourceCodeableConcept != null 097 && sourceCodeableConcept.hasCoding() 098 && sourceCodeableConcept.getCodingFirstRep().hasCode(); 099 boolean haveReverse = theReverse != null; 100 boolean haveId = theId != null && theId.hasIdPart(); 101 102 // <editor-fold desc="Filters"> 103 if ((!haveSourceCode && !haveSourceCoding && !haveSourceCodeableConcept) 104 || moreThanOneTrue(haveSourceCode, haveSourceCoding, haveSourceCodeableConcept)) { 105 throw new InvalidRequestException( 106 Msg.code(1154) 107 + "One (and only one) of the in parameters (code, coding, codeableConcept) must be provided, to identify the code that is to be translated."); 108 } 109 110 TranslationRequest translationRequest = new TranslationRequest(); 111 translationRequest.setUrl(toStringValue(theUrl)); 112 translationRequest.setConceptMapVersion(toStringValue(theConceptMapVersion)); 113 114 if (haveSourceCode) { 115 translationRequest.getCodeableConcept().addCoding().setCode(toStringValue(theSourceCode)); 116 117 if (haveSourceCodeSystem) { 118 translationRequest 119 .getCodeableConcept() 120 .getCodingFirstRep() 121 .setSystem(toStringValue(theSourceCodeSystem)); 122 } 123 124 if (haveSourceCodeSystemVersion) { 125 translationRequest 126 .getCodeableConcept() 127 .getCodingFirstRep() 128 .setVersion(toStringValue(theSourceCodeSystemVersion)); 129 } 130 } else if (haveSourceCoding) { 131 translationRequest.getCodeableConcept().addCoding(sourceCoding); 132 } else { 133 translationRequest.setCodeableConcept(sourceCodeableConcept); 134 } 135 136 translationRequest.setSource(toStringValue(theSourceValueSet)); 137 translationRequest.setTarget(toStringValue(theTargetValueSet)); 138 translationRequest.setTargetSystem(toStringValue(theTargetCodeSystem)); 139 140 if (haveReverse) { 141 translationRequest.setReverse(toBooleanValue(theReverse)); 142 } 143 144 if (haveId) { 145 translationRequest.setResourceId(theId); 146 } 147 148 startRequest(theServletRequest); 149 try { 150 IFhirResourceDaoConceptMap<ConceptMap> dao = (IFhirResourceDaoConceptMap<ConceptMap>) getDao(); 151 TranslateConceptResults result = dao.translate(translationRequest, theRequestDetails); 152 Parameters parameters = TermConceptMappingSvcImpl.toParameters(result); 153 return myVersionCanonicalizer.parametersFromCanonical(parameters); 154 } finally { 155 endRequest(theServletRequest); 156 } 157 } 158 159 private static boolean moreThanOneTrue(boolean... theBooleans) { 160 boolean haveOne = false; 161 for (boolean next : theBooleans) { 162 if (next) { 163 if (haveOne) { 164 return true; 165 } else { 166 haveOne = true; 167 } 168 } 169 } 170 return false; 171 } 172}