001package ca.uhn.fhir.util;
002
003/*
004 * #%L
005 * HAPI FHIR - Core Library
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.context.*;
024import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
025import org.hl7.fhir.instance.model.api.IBase;
026import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
027import org.hl7.fhir.instance.model.api.IBaseResource;
028import org.hl7.fhir.instance.model.api.IPrimitiveType;
029
030import java.util.List;
031
032import static org.apache.commons.lang3.StringUtils.isNotBlank;
033
034/**
035 * Utilities for dealing with OperationOutcome resources across various model versions
036 */
037public class OperationOutcomeUtil {
038
039        /**
040         * Add an issue to an OperationOutcome
041         *  @param theCtx              The fhir context
042         * @param theOperationOutcome The OO resource to add to
043         * @param theSeverity         The severity (fatal | error | warning | information)
044         * @param theDetails          The details string
045         * @param theCode
046         * @return Returns the newly added issue
047         */
048        public static IBase addIssue(FhirContext theCtx, IBaseOperationOutcome theOperationOutcome, String theSeverity, String theDetails, String theLocation, String theCode) {
049                IBase issue = createIssue(theCtx, theOperationOutcome);
050                populateDetails(theCtx, issue, theSeverity, theDetails, theLocation, theCode);
051                return issue;
052        }
053
054        private static IBase createIssue(FhirContext theCtx, IBaseResource theOutcome) {
055                RuntimeResourceDefinition ooDef = theCtx.getResourceDefinition(theOutcome);
056                BaseRuntimeChildDefinition issueChild = ooDef.getChildByName("issue");
057                BaseRuntimeElementCompositeDefinition<?> issueElement = (BaseRuntimeElementCompositeDefinition<?>) issueChild.getChildByName("issue");
058
059                IBase issue = issueElement.newInstance();
060                issueChild.getMutator().addValue(theOutcome, issue);
061                return issue;
062        }
063
064        public static String getFirstIssueDetails(FhirContext theCtx, IBaseOperationOutcome theOutcome) {
065                return getFirstIssueStringPart(theCtx, theOutcome, "diagnostics");
066        }
067
068        public static String getFirstIssueLocation(FhirContext theCtx, IBaseOperationOutcome theOutcome) {
069                return getFirstIssueStringPart(theCtx, theOutcome, "location");
070        }
071
072        private static String getFirstIssueStringPart(FhirContext theCtx, IBaseOperationOutcome theOutcome, String name) {
073                if (theOutcome == null) {
074                        return null;
075                }
076
077                RuntimeResourceDefinition ooDef = theCtx.getResourceDefinition(theOutcome);
078                BaseRuntimeChildDefinition issueChild = ooDef.getChildByName("issue");
079
080                List<IBase> issues = issueChild.getAccessor().getValues(theOutcome);
081                if (issues.isEmpty()) {
082                        return null;
083                }
084
085                IBase issue = issues.get(0);
086                BaseRuntimeElementCompositeDefinition<?> issueElement = (BaseRuntimeElementCompositeDefinition<?>) theCtx.getElementDefinition(issue.getClass());
087                BaseRuntimeChildDefinition detailsChild = issueElement.getChildByName(name);
088
089                List<IBase> details = detailsChild.getAccessor().getValues(issue);
090                if (details.isEmpty()) {
091                        return null;
092                }
093                return ((IPrimitiveType<?>) details.get(0)).getValueAsString();
094        }
095
096        /**
097         * Returns true if the given OperationOutcome has 1 or more Operation.issue repetitions
098         */
099        public static boolean hasIssues(FhirContext theCtx, IBaseOperationOutcome theOutcome) {
100                if (theOutcome == null) {
101                        return false;
102                }
103                return getIssueCount(theCtx, theOutcome) > 0;
104        }
105
106        public static int getIssueCount(FhirContext theCtx, IBaseOperationOutcome theOutcome) {
107                RuntimeResourceDefinition ooDef = theCtx.getResourceDefinition(theOutcome);
108                BaseRuntimeChildDefinition issueChild = ooDef.getChildByName("issue");
109                return issueChild.getAccessor().getValues(theOutcome).size();
110        }
111
112        public static IBaseOperationOutcome newInstance(FhirContext theCtx) {
113                RuntimeResourceDefinition ooDef = theCtx.getResourceDefinition("OperationOutcome");
114                try {
115                        return (IBaseOperationOutcome) ooDef.getImplementingClass().newInstance();
116                } catch (InstantiationException e) {
117                        throw new InternalErrorException("Unable to instantiate OperationOutcome", e);
118                } catch (IllegalAccessException e) {
119                        throw new InternalErrorException("Unable to instantiate OperationOutcome", e);
120                }
121        }
122
123        private static void populateDetails(FhirContext theCtx, IBase theIssue, String theSeverity, String theDetails, String theLocation, String theCode) {
124                BaseRuntimeElementCompositeDefinition<?> issueElement = (BaseRuntimeElementCompositeDefinition<?>) theCtx.getElementDefinition(theIssue.getClass());
125                BaseRuntimeChildDefinition detailsChild;
126                detailsChild = issueElement.getChildByName("diagnostics");
127
128                BaseRuntimeChildDefinition codeChild = issueElement.getChildByName("code");
129                IPrimitiveType<?> codeElem = (IPrimitiveType<?>) codeChild.getChildByName("code").newInstance(codeChild.getInstanceConstructorArguments());
130                codeElem.setValueAsString(theCode);
131                codeChild.getMutator().addValue(theIssue, codeElem);
132
133                BaseRuntimeElementDefinition<?> stringDef = detailsChild.getChildByName(detailsChild.getElementName());
134                BaseRuntimeChildDefinition severityChild = issueElement.getChildByName("severity");
135
136                IPrimitiveType<?> severityElem = (IPrimitiveType<?>) severityChild.getChildByName("severity").newInstance(severityChild.getInstanceConstructorArguments());
137                severityElem.setValueAsString(theSeverity);
138                severityChild.getMutator().addValue(theIssue, severityElem);
139
140                IPrimitiveType<?> string = (IPrimitiveType<?>) stringDef.newInstance();
141                string.setValueAsString(theDetails);
142                detailsChild.getMutator().setValue(theIssue, string);
143
144                addLocationToIssue(theCtx, theIssue, theLocation);
145        }
146
147        public static void addLocationToIssue(FhirContext theContext, IBase theIssue, String theLocation) {
148                if (isNotBlank(theLocation)) {
149                        BaseRuntimeElementCompositeDefinition<?> issueElement = (BaseRuntimeElementCompositeDefinition<?>) theContext.getElementDefinition(theIssue.getClass());
150                        BaseRuntimeChildDefinition locationChild = issueElement.getChildByName("location");
151                        IPrimitiveType<?> locationElem = (IPrimitiveType<?>) locationChild.getChildByName("location").newInstance(locationChild.getInstanceConstructorArguments());
152                        locationElem.setValueAsString(theLocation);
153                        locationChild.getMutator().addValue(theIssue, locationElem);
154                }
155        }
156}