001package ca.uhn.fhir.jpa.provider;
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.ValueSetExpansionOptions;
027import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem;
028import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoValueSet;
029import ca.uhn.fhir.jpa.model.util.JpaConstants;
030import ca.uhn.fhir.model.dstu2.composite.CodeableConceptDt;
031import ca.uhn.fhir.model.dstu2.composite.CodingDt;
032import ca.uhn.fhir.model.dstu2.resource.Parameters;
033import ca.uhn.fhir.model.dstu2.resource.ValueSet;
034import ca.uhn.fhir.model.primitive.BooleanDt;
035import ca.uhn.fhir.model.primitive.CodeDt;
036import ca.uhn.fhir.model.primitive.IdDt;
037import ca.uhn.fhir.model.primitive.StringDt;
038import ca.uhn.fhir.model.primitive.UriDt;
039import ca.uhn.fhir.rest.annotation.IdParam;
040import ca.uhn.fhir.rest.annotation.Operation;
041import ca.uhn.fhir.rest.annotation.OperationParam;
042import ca.uhn.fhir.rest.api.server.RequestDetails;
043import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
044import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
045import ca.uhn.fhir.util.ParametersUtil;
046import org.hl7.fhir.instance.model.api.IBaseParameters;
047
048import javax.servlet.http.HttpServletRequest;
049
050import static org.apache.commons.lang3.StringUtils.isNotBlank;
051
052public class BaseJpaResourceProviderValueSetDstu2 extends JpaResourceProviderDstu2<ValueSet> {
053
054
055        @Operation(name = JpaConstants.OPERATION_EXPAND, idempotent = true)
056        public ValueSet expand(
057                HttpServletRequest theServletRequest,
058                @IdParam(optional = true) IdDt theId,
059                @OperationParam(name = "valueSet", min = 0, max = 1) ValueSet theValueSet,
060                @OperationParam(name = "identifier", min = 0, max = 1) UriDt theIdentifier,
061                @OperationParam(name = "filter", min = 0, max = 1) StringDt theFilter,
062                RequestDetails theRequestDetails) {
063
064                boolean haveId = theId != null && theId.hasIdPart();
065                boolean haveIdentifier = theIdentifier != null && isNotBlank(theIdentifier.getValue());
066                boolean haveValueSet = theValueSet != null && theValueSet.isEmpty() == false;
067
068                if (!haveId && !haveIdentifier && !haveValueSet) {
069                        throw new InvalidRequestException(Msg.code(1130) + "$expand operation at the type level (no ID specified) requires an identifier or a valueSet as a part of the request");
070                }
071
072                if (moreThanOneTrue(haveId, haveIdentifier, haveValueSet)) {
073                        throw new InvalidRequestException(Msg.code(1131) + "$expand must EITHER be invoked at the type level, or have an identifier specified, or have a ValueSet specified. Can not combine these options.");
074                }
075
076                startRequest(theServletRequest);
077                try {
078                        IFhirResourceDaoValueSet<ValueSet, CodingDt, CodeableConceptDt> dao = (IFhirResourceDaoValueSet<ValueSet, CodingDt, CodeableConceptDt>) getDao();
079                        if (haveId) {
080                                return dao.expand(theId, toFilterString(theFilter), theRequestDetails);
081                        } else if (haveIdentifier) {
082                                return dao.expandByIdentifier(theIdentifier.getValue(), toFilterString(theFilter));
083                        } else {
084                                return dao.expand(theValueSet, toFilterString(theFilter));
085                        }
086
087                } finally {
088                        endRequest(theServletRequest);
089                }
090        }
091
092        private ValueSetExpansionOptions toFilterString(StringDt theFilter) {
093                if (theFilter != null) {
094                        return ValueSetExpansionOptions.forOffsetAndCount(0, 1000).setFilter(theFilter.getValue());
095                }
096                return null;
097        }
098
099        @Operation(name = JpaConstants.OPERATION_LOOKUP, idempotent = true, returnParameters = {
100                @OperationParam(name = "name", type = StringDt.class, min = 1),
101                @OperationParam(name = "version", type = StringDt.class, min = 0),
102                @OperationParam(name = "display", type = StringDt.class, min = 1),
103                @OperationParam(name = "abstract", type = BooleanDt.class, min = 1),
104        })
105        public Parameters lookup(
106                HttpServletRequest theServletRequest,
107                @OperationParam(name = "code", min = 0, max = 1) CodeDt theCode,
108                @OperationParam(name = "system", min = 0, max = 1) UriDt theSystem,
109                @OperationParam(name = "coding", min = 0, max = 1) CodingDt theCoding,
110                RequestDetails theRequestDetails
111        ) {
112
113                startRequest(theServletRequest);
114                try {
115                        IFhirResourceDaoCodeSystem<ValueSet, CodingDt, CodeableConceptDt> dao = (IFhirResourceDaoCodeSystem<ValueSet, CodingDt, CodeableConceptDt>) getDao();
116                        IValidationSupport.LookupCodeResult result = dao.lookupCode(theCode, theSystem, theCoding, null, theRequestDetails);
117                        if (result.isFound() == false) {
118                                throw new ResourceNotFoundException(Msg.code(1132) + "Unable to find code[" + result.getSearchedForCode() + "] in system[" + result.getSearchedForSystem() + "]");
119                        }
120                        Parameters retVal = new Parameters();
121                        retVal.addParameter().setName("name").setValue(new StringDt(result.getCodeSystemDisplayName()));
122                        if (isNotBlank(result.getCodeSystemVersion())) {
123                                retVal.addParameter().setName("version").setValue(new StringDt(result.getCodeSystemVersion()));
124                        }
125                        retVal.addParameter().setName("display").setValue(new StringDt(result.getCodeDisplay()));
126                        retVal.addParameter().setName("abstract").setValue(new BooleanDt(result.isCodeIsAbstract()));
127                        return retVal;
128                } finally {
129                        endRequest(theServletRequest);
130                }
131        }
132
133        @Operation(name = JpaConstants.OPERATION_VALIDATE_CODE, idempotent = true, returnParameters = {
134                @OperationParam(name = "result", type = BooleanDt.class, min = 1),
135                @OperationParam(name = "message", type = StringDt.class),
136                @OperationParam(name = "display", type = StringDt.class)
137        })
138        public Parameters validateCode(
139                HttpServletRequest theServletRequest,
140                @IdParam(optional = true) IdDt theId,
141                @OperationParam(name = "identifier", min = 0, max = 1) UriDt theValueSetIdentifier,
142                @OperationParam(name = "code", min = 0, max = 1) CodeDt theCode,
143                @OperationParam(name = "system", min = 0, max = 1) UriDt theSystem,
144                @OperationParam(name = "display", min = 0, max = 1) StringDt theDisplay,
145                @OperationParam(name = "coding", min = 0, max = 1) CodingDt theCoding,
146                @OperationParam(name = "codeableConcept", min = 0, max = 1) CodeableConceptDt theCodeableConcept,
147                RequestDetails theRequestDetails
148        ) {
149
150                startRequest(theServletRequest);
151                try {
152                        IFhirResourceDaoValueSet<ValueSet, CodingDt, CodeableConceptDt> dao = (IFhirResourceDaoValueSet<ValueSet, CodingDt, CodeableConceptDt>) getDao();
153                        IValidationSupport.CodeValidationResult result = dao.validateCode(theValueSetIdentifier, theId, theCode, theSystem, theDisplay, theCoding, theCodeableConcept, theRequestDetails);
154                        return (Parameters) toValidateCodeResult(getContext(), result);
155                } finally {
156                        endRequest(theServletRequest);
157                }
158        }
159
160        public static IBaseParameters toValidateCodeResult(FhirContext theContext, IValidationSupport.CodeValidationResult theResult) {
161                IBaseParameters retVal = ParametersUtil.newInstance(theContext);
162
163                ParametersUtil.addParameterToParametersBoolean(theContext, retVal, "result", theResult.isOk());
164                if (isNotBlank(theResult.getMessage())) {
165                        ParametersUtil.addParameterToParametersString(theContext, retVal, "message", theResult.getMessage());
166                }
167                if (isNotBlank(theResult.getDisplay())) {
168                        ParametersUtil.addParameterToParametersString(theContext, retVal, "display", theResult.getDisplay());
169                }
170
171                return retVal;
172        }
173
174        private static boolean moreThanOneTrue(boolean... theBooleans) {
175                boolean haveOne = false;
176                for (boolean next : theBooleans) {
177                        if (next) {
178                                if (haveOne) {
179                                        return true;
180                                } else {
181                                        haveOne = true;
182                                }
183                        }
184                }
185                return false;
186        }
187
188
189}