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}