001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.bean.swagger; 018 019import static org.apache.juneau.common.utils.Utils.*; 020import static org.apache.juneau.internal.CollectionUtils.*; 021import static org.apache.juneau.internal.ConverterUtils.*; 022 023import java.util.*; 024 025import org.apache.juneau.common.utils.*; 026import org.apache.juneau.internal.*; 027 028/** 029 * Describes a single response from an API operation. 030 * 031 * <p> 032 * The Response Object describes a single response from a Swagger 2.0 API operation, including a description, 033 * schema, headers, and examples. Responses are associated with HTTP status codes (e.g., 200, 404, 500). 034 * 035 * <h5 class='section'>Swagger Specification:</h5> 036 * <p> 037 * The Response Object is composed of the following fields: 038 * <ul class='spaced-list'> 039 * <li><c>description</c> (string, REQUIRED) - A short description of the response 040 * <li><c>schema</c> ({@link SchemaInfo}) - A definition of the response structure 041 * <li><c>headers</c> (map of {@link HeaderInfo}) - Maps a header name to its definition 042 * <li><c>examples</c> (map of any) - An example of the response message (keys are media types) 043 * </ul> 044 * 045 * <h5 class='section'>Example:</h5> 046 * <p class='bjava'> 047 * <jc>// Construct using SwaggerBuilder.</jc> 048 * ResponseInfo <jv>info</jv> = <jsm>responseInfo</jsm>(<js>"A complex object array response"</js>) 049 * .schema( 050 * <jsm>schemaInfo</jsm> 051 * .type(<js>"array"</js>) 052 * .items( 053 * <jsm>items</jsm>() 054 * .set(<js>"$ref"</js>, <js>"#/definitions/VeryComplexType"</js>) 055 * ) 056 * ); 057 * 058 * <jc>// Serialize using JsonSerializer.</jc> 059 * String <jv>json</jv> = Json.<jsm>from</jsm>(<jv>info</jv>); 060 * 061 * <jc>// Or just use toString() which does the same as above.</jc> 062 * <jv>json</jv> = <jv>info</jv>.toString(); 063 * </p> 064 * <p class='bjson'> 065 * <jc>// Output</jc> 066 * { 067 * <js>"description"</js>: <js>"A complex object array response"</js>, 068 * <js>"schema"</js>: { 069 * <js>"type"</js>: <js>"array"</js>, 070 * <js>"items"</js>: { 071 * <js>"$ref"</js>: <js>"#/definitions/VeryComplexType"</js> 072 * } 073 * } 074 * } 075 * </p> 076 * 077 * <h5 class='section'>See Also:</h5><ul> 078 * <li class='link'><a class="doclink" href="https://swagger.io/specification/v2/#response-object">Swagger 2.0 Specification > Response Object</a> 079 * <li class='link'><a class="doclink" href="https://swagger.io/docs/specification/2-0/describing-responses/">Swagger Describing Responses</a> 080 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanSwagger2">juneau-bean-swagger-v2</a> 081 * </ul> 082 */ 083public class ResponseInfo extends SwaggerElement { 084 085 private String description; 086 private SchemaInfo schema; 087 private Map<String,HeaderInfo> headers; 088 private Map<String,Object> examples; 089 090 /** 091 * Default constructor. 092 */ 093 public ResponseInfo() {} 094 095 /** 096 * Copy constructor. 097 * 098 * @param copyFrom The object to copy. 099 */ 100 public ResponseInfo(ResponseInfo copyFrom) { 101 super(copyFrom); 102 103 this.description = copyFrom.description; 104 this.schema = copyFrom.schema == null ? null : copyFrom.schema.copy(); 105 this.examples = copyOf(copyFrom.examples); 106 this.headers = copyOf(copyFrom.headers, HeaderInfo::copy); 107 } 108 109 /** 110 * Make a deep copy of this object. 111 * 112 * @return A deep copy of this object. 113 */ 114 public ResponseInfo copy() { 115 return new ResponseInfo(this); 116 } 117 118 /** 119 * Copies any non-null fields from the specified object to this object. 120 * 121 * @param r 122 * The object to copy fields from. 123 * <br>Can be <jk>null</jk>. 124 * @return This object. 125 */ 126 public ResponseInfo copyFrom(ResponseInfo r) { 127 if (r != null) { 128 if (r.description != null) 129 description = r.description; 130 if (r.schema != null) 131 schema = r.schema; 132 if (r.headers != null) 133 headers = r.headers; 134 if (r.examples != null) 135 examples = r.examples; 136 } 137 return this; 138 } 139 140 //----------------------------------------------------------------------------------------------------------------- 141 // Properties 142 //----------------------------------------------------------------------------------------------------------------- 143 144 /** 145 * Bean property getter: <property>description</property>. 146 * 147 * <p> 148 * A short description of the response. 149 * 150 * @return The property value, or <jk>null</jk> if it is not set. 151 */ 152 public String getDescription() { 153 return description; 154 } 155 156 /** 157 * Bean property setter: <property>description</property>. 158 * 159 * <p> 160 * A short description of the response. 161 * 162 * @param value 163 * The new value for this property. 164 * <br><a class="doclink" href="https://help.github.com/articles/github-flavored-markdown">GFM syntax</a> can be used for rich text representation. 165 * <br>Property value is required. 166 * <br>Can be <jk>null</jk> to unset the property. 167 * @return This object. 168 */ 169 public ResponseInfo setDescription(String value) { 170 description = value; 171 return this; 172 } 173 174 /** 175 * Bean property getter: <property>examples</property>. 176 * 177 * <p> 178 * An example of the response message. 179 * 180 * @return The property value, or <jk>null</jk> if it is not set. 181 */ 182 public Map<String,Object> getExamples() { 183 return examples; 184 } 185 186 /** 187 * Bean property setter: <property>examples</property>. 188 * 189 * <p> 190 * An example of the response message. 191 * 192 * @param value 193 * The new value for this property. 194 * <br>Keys must be MIME-type strings. 195 * <br>Can be <jk>null</jk> to unset the property. 196 * @return This object. 197 */ 198 public ResponseInfo setExamples(Map<String,Object> value) { 199 examples = copyOf(value); 200 return this; 201 } 202 203 /** 204 * Bean property appender: <property>examples</property>. 205 * 206 * <p> 207 * Adds a single value to the <property>examples</property> property. 208 * 209 * @param mimeType The mime-type string. Must not be <jk>null</jk>. 210 * @param example The example. Must not be <jk>null</jk>. 211 * @return This object. 212 */ 213 public ResponseInfo addExample(String mimeType, Object example) { 214 assertArgNotNull("mimeType", mimeType); 215 assertArgNotNull("example", example); 216 examples = mapBuilder(examples).sparse().add(mimeType, example).build(); 217 return this; 218 } 219 220 /** 221 * Bean property getter: <property>headers</property>. 222 * 223 * <p> 224 * A list of headers that are sent with the response. 225 * 226 * @return The property value, or <jk>null</jk> if it is not set. 227 */ 228 public Map<String,HeaderInfo> getHeaders() { 229 return headers; 230 } 231 232 /** 233 * Bean property setter: <property>headers</property>. 234 * 235 * <p> 236 * A list of headers that are sent with the response. 237 * 238 * @param value 239 * The new value for this property. 240 * <br>Can be <jk>null</jk> to unset the property. 241 * @return This object. 242 */ 243 public ResponseInfo setHeaders(Map<String,HeaderInfo> value) { 244 headers = copyOf(value); 245 return this; 246 } 247 248 /** 249 * Bean property appender: <property>headers</property>. 250 * 251 * @param name The header name. Must not be <jk>null</jk>. 252 * @param header The header descriptions Must not be <jk>null</jk>. 253 * @return This object. 254 */ 255 public ResponseInfo addHeader(String name, HeaderInfo header) { 256 assertArgNotNull("name", name); 257 assertArgNotNull("header", header); 258 headers = mapBuilder(headers).add(name, header).build(); 259 return this; 260 } 261 262 /** 263 * Returns the header information with the specified name. 264 * 265 * @param name The header name. Must not be <jk>null</jk>. 266 * @return The header info, or <jk>null</jk> if not found. 267 */ 268 public HeaderInfo getHeader(String name) { 269 assertArgNotNull("name", name); 270 return headers == null ? null : headers.get(name); 271 } 272 273 /** 274 * Bean property getter: <property>schema</property>. 275 * 276 * <p> 277 * A definition of the response structure. 278 * 279 * @return The property value, or <jk>null</jk> if it is not set. 280 */ 281 public SchemaInfo getSchema() { 282 return schema; 283 } 284 285 /** 286 * Bean property setter: <property>schema</property>. 287 * 288 * <p> 289 * A definition of the response structure. 290 * 291 * @param value 292 * The new value for this property. 293 * <br>It can be a primitive, an array or an object. 294 * <br>Can be <jk>null</jk> to unset the property. 295 * @return This object. 296 */ 297 public ResponseInfo setSchema(SchemaInfo value) { 298 schema = value; 299 return this; 300 } 301 302 @Override /* Overridden from SwaggerElement */ 303 public <T> T get(String property, Class<T> type) { 304 assertArgNotNull("property", property); 305 return switch (property) { 306 case "description" -> toType(getDescription(), type); 307 case "examples" -> toType(getExamples(), type); 308 case "headers" -> toType(getHeaders(), type); 309 case "schema" -> toType(getSchema(), type); 310 default -> super.get(property, type); 311 }; 312 } 313 314 @Override /* Overridden from SwaggerElement */ 315 public ResponseInfo set(String property, Object value) { 316 assertArgNotNull("property", property); 317 return switch (property) { 318 case "description" -> setDescription(Utils.s(value)); 319 case "examples" -> setExamples(mapBuilder(String.class,Object.class).sparse().addAny(value).build()); 320 case "headers" -> setHeaders(mapBuilder(String.class,HeaderInfo.class).sparse().addAny(value).build()); 321 case "schema" -> setSchema(toType(value, SchemaInfo.class)); 322 default -> { 323 super.set(property, value); 324 yield this; 325 } 326 }; 327 } 328 329 @Override /* Overridden from SwaggerElement */ 330 public Set<String> keySet() { 331 var s = setBuilder(String.class) 332 .addIf(description != null, "description") 333 .addIf(examples != null, "examples") 334 .addIf(headers != null, "headers") 335 .addIf(schema != null, "schema") 336 .build(); 337 return new MultiSet<>(s, super.keySet()); 338 } 339 340 /** 341 * Resolves any <js>"$ref"</js> attributes in this element. 342 * 343 * @param swagger The swagger document containing the definitions. 344 * @param refStack Keeps track of previously-visited references so that we don't cause recursive loops. 345 * @param maxDepth 346 * The maximum depth to resolve references. 347 * <br>After that level is reached, <c>$ref</c> references will be left alone. 348 * <br>Useful if you have very complex models and you don't want your swagger page to be overly-complex. 349 * @return 350 * This object with references resolved. 351 * <br>May or may not be the same object. 352 */ 353 public ResponseInfo resolveRefs(Swagger swagger, Deque<String> refStack, int maxDepth) { 354 355 if (schema != null) 356 schema = schema.resolveRefs(swagger, refStack, maxDepth); 357 358 if (headers != null) 359 headers.entrySet().forEach(x -> x.setValue(x.getValue().resolveRefs(swagger, refStack, maxDepth))); 360 361 return this; 362 } 363 364 /** 365 * Sets strict mode on this bean. 366 * 367 * @return This object. 368 */ 369 @Override 370 public ResponseInfo strict() { 371 super.strict(); 372 return this; 373 } 374 375 /** 376 * Sets strict mode on this bean. 377 * 378 * @param value 379 * The new value for this property. 380 * <br>Non-boolean values will be converted to boolean using <code>Boolean.<jsm>valueOf</jsm>(value.toString())</code>. 381 * <br>Can be <jk>null</jk> (interpreted as <jk>false</jk>). 382 * @return This object. 383 */ 384 @Override 385 public ResponseInfo strict(Object value) { 386 super.strict(value); 387 return this; 388 } 389 390}