001package ca.uhn.fhir.util; 002 003/* 004 * #%L 005 * HAPI FHIR - Core Library 006 * %% 007 * Copyright (C) 2014 - 2023 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.BaseRuntimeChildDefinition; 024import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition; 025import ca.uhn.fhir.context.BaseRuntimeElementDefinition; 026import ca.uhn.fhir.context.FhirContext; 027import ca.uhn.fhir.context.RuntimeResourceDefinition; 028import ca.uhn.fhir.i18n.Msg; 029import ca.uhn.fhir.rest.api.Constants; 030import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; 031import org.hl7.fhir.instance.model.api.IBase; 032import org.hl7.fhir.instance.model.api.IBaseCoding; 033import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; 034import org.hl7.fhir.instance.model.api.IBaseResource; 035import org.hl7.fhir.instance.model.api.ICompositeType; 036import org.hl7.fhir.instance.model.api.IPrimitiveType; 037 038import javax.annotation.Nullable; 039import java.util.List; 040 041import static org.apache.commons.lang3.StringUtils.isNotBlank; 042 043/** 044 * Utilities for dealing with OperationOutcome resources across various model versions 045 */ 046public class OperationOutcomeUtil { 047 048 /** 049 * Add an issue to an OperationOutcome 050 * @param theCtx The fhir context 051 * @param theOperationOutcome The OO resource to add to 052 * @param theSeverity The severity (fatal | error | warning | information) 053 * @param theDetails The details string 054 * @param theCode 055 * @return Returns the newly added issue 056 */ 057 public static IBase addIssue(FhirContext theCtx, IBaseOperationOutcome theOperationOutcome, String theSeverity, String theDetails, String theLocation, String theCode) { 058 return addIssue(theCtx, theOperationOutcome, theSeverity, theDetails, theLocation, theCode, null, null, null); 059 } 060 061 public static IBase addIssue(FhirContext theCtx, IBaseOperationOutcome theOperationOutcome, String theSeverity, String theDetails, String theLocation, String theCode, @Nullable String theDetailSystem, @Nullable String theDetailCode, @Nullable String theDetailDescription) { 062 IBase issue = createIssue(theCtx, theOperationOutcome); 063 populateDetails(theCtx, issue, theSeverity, theDetails, theLocation, theCode, theDetailSystem, theDetailCode, theDetailDescription); 064 return issue; 065 } 066 067 private static IBase createIssue(FhirContext theCtx, IBaseResource theOutcome) { 068 RuntimeResourceDefinition ooDef = theCtx.getResourceDefinition(theOutcome); 069 BaseRuntimeChildDefinition issueChild = ooDef.getChildByName("issue"); 070 BaseRuntimeElementCompositeDefinition<?> issueElement = (BaseRuntimeElementCompositeDefinition<?>) issueChild.getChildByName("issue"); 071 072 IBase issue = issueElement.newInstance(); 073 issueChild.getMutator().addValue(theOutcome, issue); 074 return issue; 075 } 076 077 public static String getFirstIssueDetails(FhirContext theCtx, IBaseOperationOutcome theOutcome) { 078 return getFirstIssueStringPart(theCtx, theOutcome, "diagnostics"); 079 } 080 081 public static String getFirstIssueLocation(FhirContext theCtx, IBaseOperationOutcome theOutcome) { 082 return getFirstIssueStringPart(theCtx, theOutcome, "location"); 083 } 084 085 private static String getFirstIssueStringPart(FhirContext theCtx, IBaseOperationOutcome theOutcome, String name) { 086 if (theOutcome == null) { 087 return null; 088 } 089 090 RuntimeResourceDefinition ooDef = theCtx.getResourceDefinition(theOutcome); 091 BaseRuntimeChildDefinition issueChild = ooDef.getChildByName("issue"); 092 093 List<IBase> issues = issueChild.getAccessor().getValues(theOutcome); 094 if (issues.isEmpty()) { 095 return null; 096 } 097 098 IBase issue = issues.get(0); 099 BaseRuntimeElementCompositeDefinition<?> issueElement = (BaseRuntimeElementCompositeDefinition<?>) theCtx.getElementDefinition(issue.getClass()); 100 BaseRuntimeChildDefinition detailsChild = issueElement.getChildByName(name); 101 102 List<IBase> details = detailsChild.getAccessor().getValues(issue); 103 if (details.isEmpty()) { 104 return null; 105 } 106 return ((IPrimitiveType<?>) details.get(0)).getValueAsString(); 107 } 108 109 /** 110 * Returns true if the given OperationOutcome has 1 or more Operation.issue repetitions 111 */ 112 public static boolean hasIssues(FhirContext theCtx, IBaseOperationOutcome theOutcome) { 113 if (theOutcome == null) { 114 return false; 115 } 116 return getIssueCount(theCtx, theOutcome) > 0; 117 } 118 119 public static int getIssueCount(FhirContext theCtx, IBaseOperationOutcome theOutcome) { 120 RuntimeResourceDefinition ooDef = theCtx.getResourceDefinition(theOutcome); 121 BaseRuntimeChildDefinition issueChild = ooDef.getChildByName("issue"); 122 return issueChild.getAccessor().getValues(theOutcome).size(); 123 } 124 125 public static boolean hasIssuesOfSeverity(FhirContext theCtx, IBaseOperationOutcome theOutcome, String theSeverity) { 126 RuntimeResourceDefinition ooDef = theCtx.getResourceDefinition(theOutcome); 127 BaseRuntimeChildDefinition issueChild = ooDef.getChildByName("issue"); 128 List<IBase> issues = issueChild.getAccessor().getValues(theOutcome); 129 130 if (issues.isEmpty()) { 131 return false; // if there are no issues at all, there are no issues of the required severity 132 } 133 134 IBase firstIssue = issues.get(0); 135 BaseRuntimeElementCompositeDefinition<?> issueElement = (BaseRuntimeElementCompositeDefinition<?>) theCtx.getElementDefinition(firstIssue.getClass()); 136 BaseRuntimeChildDefinition severityChild = issueElement.getChildByName("severity"); 137 138 return issues.stream() 139 .flatMap(t -> severityChild.getAccessor().getValues(t).stream()) 140 .map(t -> (IPrimitiveType<?>) t) 141 .map(IPrimitiveType::getValueAsString) 142 .anyMatch(theSeverity::equals); 143 } 144 145 public static IBaseOperationOutcome newInstance(FhirContext theCtx) { 146 RuntimeResourceDefinition ooDef = theCtx.getResourceDefinition("OperationOutcome"); 147 try { 148 return (IBaseOperationOutcome) ooDef.getImplementingClass().newInstance(); 149 } catch (InstantiationException e) { 150 throw new InternalErrorException(Msg.code(1803) + "Unable to instantiate OperationOutcome", e); 151 } catch (IllegalAccessException e) { 152 throw new InternalErrorException(Msg.code(1804) + "Unable to instantiate OperationOutcome", e); 153 } 154 } 155 156 private static void populateDetails(FhirContext theCtx, IBase theIssue, String theSeverity, String theDetails, String theLocation, String theCode, String theDetailSystem, String theDetailCode, String theDetailDescription) { 157 BaseRuntimeElementCompositeDefinition<?> issueElement = (BaseRuntimeElementCompositeDefinition<?>) theCtx.getElementDefinition(theIssue.getClass()); 158 BaseRuntimeChildDefinition diagnosticsChild; 159 diagnosticsChild = issueElement.getChildByName("diagnostics"); 160 161 BaseRuntimeChildDefinition codeChild = issueElement.getChildByName("code"); 162 IPrimitiveType<?> codeElem = (IPrimitiveType<?>) codeChild.getChildByName("code").newInstance(codeChild.getInstanceConstructorArguments()); 163 codeElem.setValueAsString(theCode); 164 codeChild.getMutator().addValue(theIssue, codeElem); 165 166 BaseRuntimeElementDefinition<?> stringDef = diagnosticsChild.getChildByName(diagnosticsChild.getElementName()); 167 BaseRuntimeChildDefinition severityChild = issueElement.getChildByName("severity"); 168 169 IPrimitiveType<?> severityElem = (IPrimitiveType<?>) severityChild.getChildByName("severity").newInstance(severityChild.getInstanceConstructorArguments()); 170 severityElem.setValueAsString(theSeverity); 171 severityChild.getMutator().addValue(theIssue, severityElem); 172 173 IPrimitiveType<?> string = (IPrimitiveType<?>) stringDef.newInstance(); 174 string.setValueAsString(theDetails); 175 diagnosticsChild.getMutator().setValue(theIssue, string); 176 177 addLocationToIssue(theCtx, theIssue, theLocation); 178 179 if (isNotBlank(theDetailSystem)) { 180 BaseRuntimeChildDefinition detailsChild = issueElement.getChildByName("details"); 181 if (detailsChild != null) { 182 BaseRuntimeElementDefinition<?> codeableConceptDef = theCtx.getElementDefinition("CodeableConcept"); 183 IBase codeableConcept = codeableConceptDef.newInstance(); 184 185 BaseRuntimeElementDefinition<?> codingDef = theCtx.getElementDefinition("Coding"); 186 IBaseCoding coding = (IBaseCoding) codingDef.newInstance(); 187 coding.setSystem(theDetailSystem); 188 coding.setCode(theDetailCode); 189 coding.setDisplay(theDetailDescription); 190 191 codeableConceptDef.getChildByName("coding").getMutator().addValue(codeableConcept, coding); 192 193 detailsChild.getMutator().addValue(theIssue, codeableConcept); 194 } 195 } 196 } 197 198 public static void addLocationToIssue(FhirContext theContext, IBase theIssue, String theLocation) { 199 if (isNotBlank(theLocation)) { 200 BaseRuntimeElementCompositeDefinition<?> issueElement = (BaseRuntimeElementCompositeDefinition<?>) theContext.getElementDefinition(theIssue.getClass()); 201 BaseRuntimeChildDefinition locationChild = issueElement.getChildByName("location"); 202 IPrimitiveType<?> locationElem = (IPrimitiveType<?>) locationChild.getChildByName("location").newInstance(locationChild.getInstanceConstructorArguments()); 203 locationElem.setValueAsString(theLocation); 204 locationChild.getMutator().addValue(theIssue, locationElem); 205 } 206 } 207 208 public static IBase addIssueWithMessageId(FhirContext myCtx, IBaseOperationOutcome theOperationOutcome, String severity, String message, String messageId, String location, String theCode) { 209 IBase issue = addIssue(myCtx, theOperationOutcome, severity, message, location, theCode); 210 BaseRuntimeElementCompositeDefinition<?> issueElement = (BaseRuntimeElementCompositeDefinition<?>) myCtx.getElementDefinition(issue.getClass()); 211 BaseRuntimeChildDefinition detailsChildDef = issueElement.getChildByName("details"); 212 213 IPrimitiveType<?> system = (IPrimitiveType<?>) myCtx.getElementDefinition("uri").newInstance(); 214 system.setValueAsString(Constants.JAVA_VALIDATOR_DETAILS_SYSTEM); 215 IPrimitiveType<?> code = (IPrimitiveType<?>) myCtx.getElementDefinition("code").newInstance(); 216 code.setValueAsString(messageId); 217 218 BaseRuntimeElementCompositeDefinition<?> codingDef = (BaseRuntimeElementCompositeDefinition<?>) myCtx.getElementDefinition("Coding"); 219 ICompositeType coding = (ICompositeType) codingDef.newInstance(); 220 codingDef.getChildByName("system").getMutator().addValue(coding, system); 221 codingDef.getChildByName("code").getMutator().addValue(coding, code); 222 BaseRuntimeElementCompositeDefinition<?> ccDef = (BaseRuntimeElementCompositeDefinition<?>) myCtx.getElementDefinition("CodeableConcept"); 223 ICompositeType codeableConcept = (ICompositeType) ccDef.newInstance(); 224 ccDef.getChildByName("coding").getMutator().addValue(codeableConcept, coding); 225 226 detailsChildDef.getMutator().addValue(issue, codeableConcept); 227 return issue; 228 } 229}