001/* 002 * #%L 003 * HAPI FHIR - Core Library 004 * %% 005 * Copyright (C) 2014 - 2024 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.rest.api; 021 022import ca.uhn.fhir.util.CoverageIgnore; 023import org.apache.commons.lang3.Validate; 024import org.hl7.fhir.instance.model.api.IBaseOperationOutcome; 025import org.hl7.fhir.instance.model.api.IBaseResource; 026import org.hl7.fhir.instance.model.api.IIdType; 027 028import java.util.ArrayList; 029import java.util.Collection; 030import java.util.HashMap; 031import java.util.List; 032import java.util.Map; 033import java.util.Optional; 034 035public class MethodOutcome { 036 037 private Boolean myCreated; 038 private IIdType myId; 039 private IBaseOperationOutcome myOperationOutcome; 040 private IBaseResource myResource; 041 private Map<String, List<String>> myResponseHeaders; 042 private Collection<Runnable> myResourceViewCallbacks; 043 private Integer myResponseStatusCode; 044 045 /** 046 * Constructor 047 */ 048 public MethodOutcome() { 049 super(); 050 } 051 052 /** 053 * Constructor 054 * 055 * @param theId The ID of the created/updated resource 056 * @param theCreated If not null, indicates whether the resource was created (as opposed to being updated). This is generally not needed, since the server can assume based on the method being called 057 * whether the result was a creation or an update. However, it can be useful if you are implementing an update method that does a create if the ID doesn't already exist. 058 */ 059 @CoverageIgnore 060 public MethodOutcome(IIdType theId, Boolean theCreated) { 061 myId = theId; 062 myCreated = theCreated; 063 } 064 065 /** 066 * Constructor 067 * 068 * @param theId The ID of the created/updated resource 069 * @param theBaseOperationOutcome The operation outcome to return with the response (or null for none) 070 */ 071 public MethodOutcome(IIdType theId, IBaseOperationOutcome theBaseOperationOutcome) { 072 myId = theId; 073 myOperationOutcome = theBaseOperationOutcome; 074 } 075 076 /** 077 * Constructor 078 * 079 * @param theId The ID of the created/updated resource 080 * @param theBaseOperationOutcome The operation outcome to return with the response (or null for none) 081 * @param theCreated If not null, indicates whether the resource was created (as opposed to being updated). This is generally not needed, since the server can assume based on the method being called 082 * whether the result was a creation or an update. However, it can be useful if you are implementing an update method that does a create if the ID doesn't already exist. 083 */ 084 public MethodOutcome(IIdType theId, IBaseOperationOutcome theBaseOperationOutcome, Boolean theCreated) { 085 myId = theId; 086 myOperationOutcome = theBaseOperationOutcome; 087 myCreated = theCreated; 088 } 089 090 /** 091 * Constructor 092 * 093 * @param theId The ID of the created/updated resource 094 */ 095 public MethodOutcome(IIdType theId) { 096 myId = theId; 097 } 098 099 /** 100 * Constructor 101 * 102 * @param theOperationOutcome The operation outcome resource to return 103 */ 104 public MethodOutcome(IBaseOperationOutcome theOperationOutcome) { 105 myOperationOutcome = theOperationOutcome; 106 } 107 108 /** 109 * This will be set to {@link Boolean#TRUE} for instance of MethodOutcome which are 110 * returned to client instances, if the server has responded with an HTTP 201 Created. 111 */ 112 public Boolean getCreated() { 113 return myCreated; 114 } 115 116 /** 117 * If not null, indicates whether the resource was created (as opposed to being updated). This is generally not needed, since the server can assume based on the method being called whether the 118 * result was a creation or an update. However, it can be useful if you are implementing an update method that does a create if the ID doesn't already exist. 119 * <p> 120 * Users of HAPI should only interact with this method in Server applications 121 * </p> 122 * 123 * @param theCreated If not null, indicates whether the resource was created (as opposed to being updated). This is generally not needed, since the server can assume based on the method being called 124 * whether the result was a creation or an update. However, it can be useful if you are implementing an update method that does a create if the ID doesn't already exist. 125 * @return Returns a reference to <code>this</code> for easy method chaining 126 */ 127 public MethodOutcome setCreated(Boolean theCreated) { 128 myCreated = theCreated; 129 return this; 130 } 131 132 public IIdType getId() { 133 return myId; 134 } 135 136 /** 137 * @param theId The ID of the created/updated resource 138 * @return Returns a reference to <code>this</code> for easy method chaining 139 */ 140 public MethodOutcome setId(IIdType theId) { 141 myId = theId; 142 return this; 143 } 144 145 /** 146 * Returns the {@link IBaseOperationOutcome} resource to return to the client or <code>null</code> if none. 147 * 148 * @return This method <b>will return null</b>, unlike many methods in the API. 149 */ 150 public IBaseOperationOutcome getOperationOutcome() { 151 return myOperationOutcome; 152 } 153 154 /** 155 * Sets the {@link IBaseOperationOutcome} resource to return to the client. Set to <code>null</code> (which is the default) if none. 156 * 157 * @return Returns a reference to <code>this</code> for easy method chaining 158 */ 159 public MethodOutcome setOperationOutcome(IBaseOperationOutcome theBaseOperationOutcome) { 160 myOperationOutcome = theBaseOperationOutcome; 161 return this; 162 } 163 164 /** 165 * <b>From a client response:</b> If the method returned an actual resource body (e.g. a create/update with 166 * "Prefer: return=representation") this field will be populated with the 167 * resource itself. 168 */ 169 public IBaseResource getResource() { 170 return myResource; 171 } 172 173 /** 174 * <b>In a server response</b>: This field may be populated in server code with the final resource for operations 175 * where a resource body is being created/updated. E.g. for an update method, this field could be populated with 176 * the resource after the update is applied, with the new version ID, lastUpdate time, etc. 177 * <p> 178 * This field is optional, but if it is populated the server will return the resource body if requested to 179 * do so via the HTTP Prefer header. 180 * </p> 181 * 182 * @return Returns a reference to <code>this</code> for easy method chaining 183 * @see #registerResourceViewCallback(Runnable) to register a callback that should be invoked by the framework before the resource is shown/returned to a client 184 */ 185 public MethodOutcome setResource(IBaseResource theResource) { 186 myResource = theResource; 187 return this; 188 } 189 190 /** 191 * Gets the headers for the HTTP response 192 */ 193 public Map<String, List<String>> getResponseHeaders() { 194 if (myResponseHeaders == null) { 195 myResponseHeaders = new HashMap<>(); 196 } 197 return myResponseHeaders; 198 } 199 200 /** 201 * Sets the headers for the HTTP response 202 */ 203 public void setResponseHeaders(Map<String, List<String>> theResponseHeaders) { 204 myResponseHeaders = theResponseHeaders; 205 } 206 207 public Optional<String> getFirstResponseHeader(String theHeader) { 208 List<String> values = getResponseHeaders().get(theHeader); 209 210 if (values == null || values.isEmpty()) { 211 return Optional.empty(); 212 } else { 213 return Optional.of(values.get(0)); 214 } 215 } 216 217 /** 218 * Registers a callback to be invoked before the resource in this object gets 219 * returned to the client. Note that this is an experimental API and may change. 220 * 221 * @param theCallback The callback 222 * @since 4.0.0 223 */ 224 public void registerResourceViewCallback(Runnable theCallback) { 225 Validate.notNull(theCallback, "theCallback must not be null"); 226 227 if (myResourceViewCallbacks == null) { 228 myResourceViewCallbacks = new ArrayList<>(2); 229 } 230 myResourceViewCallbacks.add(theCallback); 231 } 232 233 /** 234 * Fires callbacks registered to {@link #registerResourceViewCallback(Runnable)} and then 235 * clears the list of registered callbacks. 236 * 237 * @since 4.0.0 238 */ 239 public void fireResourceViewCallbacks() { 240 if (myResourceViewCallbacks != null) { 241 myResourceViewCallbacks.forEach(t -> t.run()); 242 myResourceViewCallbacks.clear(); 243 } 244 } 245 246 public void setCreatedUsingStatusCode(int theResponseStatusCode) { 247 if (theResponseStatusCode == Constants.STATUS_HTTP_201_CREATED) { 248 setCreated(true); 249 } 250 } 251 252 protected boolean hasResource() { 253 return myResource != null; 254 } 255 256 public void setResponseStatusCode(int theResponseStatusCode) { 257 myResponseStatusCode = theResponseStatusCode; 258 } 259 260 public int getResponseStatusCode() { 261 return isResponseStatusCodeSet() ? myResponseStatusCode : 0; 262 } 263 264 public boolean isResponseStatusCodeSet() { 265 return myResponseStatusCode != null; 266 } 267}