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