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