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.openapi3; 018 019import static org.apache.juneau.commons.utils.AssertionUtils.*; 020import static org.apache.juneau.commons.utils.CollectionUtils.*; 021import static org.apache.juneau.commons.utils.Utils.*; 022import static org.apache.juneau.internal.ConverterUtils.*; 023 024import java.util.*; 025 026import org.apache.juneau.annotation.*; 027import org.apache.juneau.commons.collections.*; 028 029/** 030 * Describes a single HTTP header. 031 * 032 * <p> 033 * The Header Object follows the structure of the Parameter Object with the following changes: it does not have a 034 * <c>name</c> field since the header name is specified in the key, and it does not have a <c>required</c> field 035 * since headers are always optional in HTTP. 036 * 037 * <h5 class='section'>OpenAPI Specification:</h5> 038 * <p> 039 * The Header Object is composed of the following fields: 040 * <ul class='spaced-list'> 041 * <li><c>description</c> (string) - A brief description of the header (CommonMark syntax may be used) 042 * <li><c>required</c> (boolean) - Determines whether this header is mandatory (default is <jk>false</jk>) 043 * <li><c>deprecated</c> (boolean) - Specifies that a header is deprecated 044 * <li><c>allowEmptyValue</c> (boolean) - Sets the ability to pass empty-valued headers 045 * <li><c>style</c> (string) - Describes how the header value will be serialized 046 * <li><c>explode</c> (boolean) - When true, header values of type array or object generate separate headers for each value 047 * <li><c>allowReserved</c> (boolean) - Determines whether the header value should allow reserved characters 048 * <li><c>schema</c> ({@link SchemaInfo}) - The schema defining the type used for the header 049 * <li><c>example</c> (any) - Example of the header's potential value 050 * <li><c>examples</c> (map of {@link Example}) - Examples of the header's potential value 051 * </ul> 052 * 053 * <h5 class='section'>Example:</h5> 054 * <p class='bcode'> 055 * <jc>// Construct using SwaggerBuilder.</jc> 056 * HeaderInfo <jv>x</jv> = <jsm>headerInfo</jsm>(<js>"integer"</js>).description(<js>"The number of allowed requests in the current period"</js>); 057 * 058 * <jc>// Serialize using JsonSerializer.</jc> 059 * String <jv>json</jv> = Json.<jsm>from</jsm>(<jv>x</jv>); 060 * 061 * <jc>// Or just use toString() which does the same as above.</jc> 062 * String <jv>json</jv> = <jv>x</jv>.toString(); 063 * </p> 064 * <p class='bcode'> 065 * <jc>// Output</jc> 066 * { 067 * <js>"description"</js>: <js>"The number of allowed requests in the current period"</js>, 068 * <js>"type"</js>: <js>"integer"</js> 069 * } 070 * </p> 071 * 072 * <h5 class='section'>See Also:</h5><ul> 073 * <li class='link'><a class="doclink" href="https://spec.openapis.org/oas/v3.0.0#header-object">OpenAPI Specification > Header Object</a> 074 * <li class='link'><a class="doclink" href="https://swagger.io/docs/specification/describing-parameters/">OpenAPI Describing Parameters</a> 075 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanOpenApi3">juneau-bean-openapi-v3</a> 076 * </ul> 077 */ 078public class HeaderInfo extends OpenApiElement { 079 080 private String description, ref; 081 private Boolean required, explode, deprecated, allowEmptyValue, allowReserved; 082 private SchemaInfo schema; 083 private Object example; 084 private Map<String,Example> examples = map(); 085 086 /** 087 * Default constructor. 088 */ 089 public HeaderInfo() {} 090 091 /** 092 * Copy constructor. 093 * 094 * @param copyFrom The object to copy. 095 */ 096 public HeaderInfo(HeaderInfo copyFrom) { 097 super(copyFrom); 098 099 this.description = copyFrom.description; 100 this.example = copyFrom.example; 101 this.allowEmptyValue = copyFrom.allowEmptyValue; 102 this.schema = copyFrom.schema; 103 this.allowReserved = copyFrom.allowReserved; 104 this.required = copyFrom.required; 105 this.ref = copyFrom.ref; 106 this.explode = copyFrom.explode; 107 this.deprecated = copyFrom.deprecated; 108 if (nn(copyFrom.examples)) 109 examples.putAll(copyOf(copyFrom.examples, Example::copy)); 110 } 111 112 /** 113 * Adds a single value to the <property>examples</property> property. 114 * 115 * @param name The example name. Must not be <jk>null</jk>. 116 * @param example The example. Must not be <jk>null</jk>. 117 * @return This object 118 */ 119 public HeaderInfo addExample(String name, Example example) { 120 assertArgNotNull("name", name); 121 assertArgNotNull("example", example); 122 examples.put(name, example); 123 return this; 124 } 125 126 /** 127 * Make a deep copy of this object. 128 * 129 * @return A deep copy of this object. 130 */ 131 public HeaderInfo copy() { 132 return new HeaderInfo(this); 133 } 134 135 @Override /* Overridden from OpenApiElement */ 136 public <T> T get(String property, Class<T> type) { 137 assertArgNotNull("property", property); 138 return switch (property) { 139 case "description" -> toType(getDescription(), type); 140 case "required" -> toType(getRequired(), type); 141 case "explode" -> toType(getExplode(), type); 142 case "deprecated" -> toType(getDeprecated(), type); 143 case "allowEmptyValue" -> toType(getAllowEmptyValue(), type); 144 case "allowReserved" -> toType(getAllowReserved(), type); 145 case "$ref" -> toType(getRef(), type); 146 case "schema" -> toType(getSchema(), type); 147 case "x-example" -> toType(getExample(), type); 148 case "examples" -> toType(getExamples(), type); 149 default -> super.get(property, type); 150 }; 151 } 152 153 /** 154 * Bean property getter: <property>allowEmptyValue</property>. 155 * 156 * <p> 157 * The type of the object. 158 * 159 * @return The property value, or <jk>null</jk> if it is not set. 160 */ 161 public Boolean getAllowEmptyValue() { return allowEmptyValue; } 162 163 /** 164 * Bean property getter: <property>allowReserved</property>. 165 * 166 * <p> 167 * The type of the object. 168 * 169 * @return The property value, or <jk>null</jk> if it is not set. 170 */ 171 public Boolean getAllowReserved() { return allowReserved; } 172 173 /** 174 * Bean property getter: <property>deprecated</property>. 175 * 176 * <p> 177 * The type of the object. 178 * 179 * @return The property value, or <jk>null</jk> if it is not set. 180 */ 181 public Boolean getDeprecated() { return deprecated; } 182 183 /** 184 * Bean property getter: <property>description</property>. 185 * 186 * <p> 187 * A short description of the header. 188 * 189 * @return The property value, or <jk>null</jk> if it is not set. 190 */ 191 public String getDescription() { return description; } 192 193 /** 194 * Bean property getter: <property>x-example</property>. 195 * 196 * @return The property value, or <jk>null</jk> if it is not set. 197 */ 198 @Beanp("x-example") 199 public Object getExample() { return example; } 200 201 /** 202 * Bean property getter: <property>examples</property>. 203 * 204 * <p> 205 * The list of possible responses as they are returned from executing this operation. 206 * 207 * @return The property value, or <jk>null</jk> if it is not set. 208 */ 209 public Map<String,Example> getExamples() { return nullIfEmpty(examples); } 210 211 /** 212 * Bean property getter: <property>required</property>. 213 * 214 * <p> 215 * The type of the object. 216 * 217 * @return The property value, or <jk>null</jk> if it is not set. 218 */ 219 public Boolean getExplode() { return explode; } 220 221 /** 222 * Bean property getter: <property>$ref</property>. 223 * 224 * @return The property value, or <jk>null</jk> if it is not set. 225 */ 226 @Beanp("$ref") 227 public String getRef() { return ref; } 228 229 /** 230 * Bean property getter: <property>required</property>. 231 * 232 * <p> 233 * The type of the object. 234 * 235 * @return The property value, or <jk>null</jk> if it is not set. 236 */ 237 public Boolean getRequired() { return required; } 238 239 /** 240 * Bean property getter: <property>schema</property>. 241 * 242 * @return The property value, or <jk>null</jk> if it is not set. 243 */ 244 public SchemaInfo getSchema() { return schema; } 245 246 @Override /* Overridden from SwaggerElement */ 247 public Set<String> keySet() { 248 // @formatter:off 249 var s = setb(String.class) 250 .addIf(nn(ref), "$ref") 251 .addIf(nn(allowEmptyValue), "allowEmptyValue") 252 .addIf(nn(allowReserved), "allowReserved") 253 .addIf(nn(deprecated), "deprecated") 254 .addIf(nn(description), "description") 255 .addIf(ne(examples), "examples") 256 .addIf(nn(explode), "explode") 257 .addIf(nn(required), "required") 258 .addIf(nn(schema), "schema") 259 .addIf(nn(example), "x-example") 260 .build(); 261 // @formatter:on 262 return new MultiSet<>(s, super.keySet()); 263 } 264 265 /** 266 * Resolves any <js>"$ref"</js> attributes in this element. 267 * 268 * @param openApi The swagger document containing the definitions. 269 * @param refStack Keeps track of previously-visited references so that we don't cause recursive loops. 270 * @param maxDepth 271 * The maximum depth to resolve references. 272 * <br>After that level is reached, <code>$ref</code> references will be left alone. 273 * <br>Useful if you have very complex models and you don't want your swagger page to be overly-complex. 274 * @return 275 * This object with references resolved. 276 * <br>May or may not be the same object. 277 */ 278 public HeaderInfo resolveRefs(OpenApi openApi, Deque<String> refStack, int maxDepth) { 279 280 if (nn(ref)) { 281 if (refStack.contains(ref) || refStack.size() >= maxDepth) 282 return this; 283 refStack.addLast(ref); 284 var r = openApi.findRef(ref, HeaderInfo.class); 285 r = r.resolveRefs(openApi, refStack, maxDepth); 286 refStack.removeLast(); 287 return r; 288 } 289 return this; 290 } 291 292 @Override /* Overridden from OpenApiElement */ 293 public HeaderInfo set(String property, Object value) { 294 assertArgNotNull("property", property); 295 return switch (property) { 296 case "$ref" -> setRef(s(value)); 297 case "allowEmptyValue" -> setAllowEmptyValue(toBoolean(value)); 298 case "allowReserved" -> setAllowReserved(toBoolean(value)); 299 case "deprecated" -> setDeprecated(toBoolean(value)); 300 case "description" -> setDescription(s(value)); 301 case "examples" -> setExamples(toMapBuilder(value, String.class, Example.class).sparse().build()); 302 case "explode" -> setExplode(toBoolean(value)); 303 case "required" -> setRequired(toBoolean(value)); 304 case "schema" -> setSchema(toType(value, SchemaInfo.class)); 305 case "x-example" -> setExample(value); 306 default -> { 307 super.set(property, value); 308 yield this; 309 } 310 }; 311 } 312 313 /** 314 * Bean property setter: <property>allowEmptyValue</property>. 315 * 316 * <p> 317 * The type of the object. 318 * 319 * @param value 320 * The new value for this property. 321 * <br>Can be <jk>null</jk> to unset the property. 322 * @return This object 323 */ 324 public HeaderInfo setAllowEmptyValue(Boolean value) { 325 allowEmptyValue = value; 326 return this; 327 } 328 329 /** 330 * Bean property setter: <property>allowReserved</property>. 331 * 332 * <p> 333 * The type of the object. 334 * 335 * @param value 336 * The new value for this property. 337 * <br>Can be <jk>null</jk> to unset the property. 338 * @return This object 339 */ 340 public HeaderInfo setAllowReserved(Boolean value) { 341 allowReserved = value; 342 return this; 343 } 344 345 /** 346 * Bean property setter: <property>deprecated</property>. 347 * 348 * <p> 349 * The type of the object. 350 * 351 * @param value 352 * The new value for this property. 353 * <br>Can be <jk>null</jk> to unset the property. 354 * @return This object 355 */ 356 public HeaderInfo setDeprecated(Boolean value) { 357 deprecated = value; 358 return this; 359 } 360 361 /** 362 * Bean property setter: <property>description</property>. 363 * 364 * <p> 365 * A short description of the header. 366 * 367 * @param value 368 * The new value for this property. 369 * <br>Can be <jk>null</jk> to unset the property. 370 * @return This object 371 */ 372 public HeaderInfo setDescription(String value) { 373 description = value; 374 return this; 375 } 376 377 /** 378 * Bean property setter: <property>examples</property>. 379 * 380 * @param value 381 * The new value for this property. 382 * <br>Can be <jk>null</jk> to unset the property. 383 * @return This object 384 */ 385 @Beanp("x-example") 386 public HeaderInfo setExample(Object value) { 387 example = value; 388 return this; 389 } 390 391 /** 392 * Bean property setter: <property>headers</property>. 393 * 394 * <p> 395 * A list of examples that are sent with the response. 396 * 397 * @param value 398 * The new value for this property. 399 * <br>Can be <jk>null</jk> to unset the property. 400 * @return This object 401 */ 402 public HeaderInfo setExamples(Map<String,Example> value) { 403 examples.clear(); 404 if (nn(value)) 405 examples.putAll(value); 406 return this; 407 } 408 409 /** 410 * Bean property setter: <property>explode</property>. 411 * 412 * <p> 413 * The type of the object. 414 * 415 * @param value 416 * The new value for this property. 417 * <br>Can be <jk>null</jk> to unset the property. 418 * @return This object 419 */ 420 public HeaderInfo setExplode(Boolean value) { 421 explode = value; 422 return this; 423 } 424 425 /** 426 * Bean property setter: <property>$ref</property>. 427 * 428 * @param value 429 * The new value for this property. 430 * <br>Can be <jk>null</jk> to unset the property. 431 * @return This object 432 */ 433 @Beanp("$ref") 434 public HeaderInfo setRef(String value) { 435 ref = value; 436 return this; 437 } 438 439 /** 440 * Bean property setter: <property>required</property>. 441 * 442 * <p> 443 * The type of the object. 444 * 445 * @param value 446 * The new value for this property. 447 * <br>Property value is required. 448 * <br>Valid values: 449 * <ul> 450 * <li><js>"string"</js> 451 * <li><js>"number"</js> 452 * <li><js>"integer"</js> 453 * <li><js>"boolean"</js> 454 * <li><js>"array"</js> 455 * </ul> 456 * <br>Can be <jk>null</jk> to unset the property. 457 * @return This object 458 */ 459 public HeaderInfo setRequired(Boolean value) { 460 required = value; 461 return this; 462 } 463 464 /** 465 * Bean property setter: <property>schema</property>. 466 * 467 * @param value 468 * The new value for this property. 469 * <br>Can be <jk>null</jk> to unset the property. 470 * @return This object 471 */ 472 public HeaderInfo setSchema(SchemaInfo value) { 473 schema = value; 474 return this; 475 } 476 477 @Override /* Overridden from OpenApiElement */ 478 public HeaderInfo strict(Object value) { 479 super.strict(value); 480 return this; 481 } 482 483 @Override /* Overridden from OpenApiElement */ 484 protected HeaderInfo strict() { 485 super.strict(); 486 return this; 487 } 488}