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.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.annotation.*; 026import org.apache.juneau.common.utils.*; 027import org.apache.juneau.internal.*; 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 081 description, 082 ref; 083 private Boolean 084 required, 085 explode, 086 deprecated, 087 allowEmptyValue, 088 allowReserved; 089 private SchemaInfo schema; 090 private Object example; 091 private Map<String,Example> examples; 092 093 /** 094 * Default constructor. 095 */ 096 public HeaderInfo() {} 097 098 /** 099 * Copy constructor. 100 * 101 * @param copyFrom The object to copy. 102 */ 103 public HeaderInfo(HeaderInfo copyFrom) { 104 super(copyFrom); 105 106 this.description = copyFrom.description; 107 this.example = copyFrom.example; 108 this.allowEmptyValue = copyFrom.allowEmptyValue; 109 this.schema = copyFrom.schema; 110 this.allowReserved = copyFrom.allowReserved; 111 this.required = copyFrom.required; 112 this.ref = copyFrom.ref; 113 this.explode = copyFrom.explode; 114 this.deprecated = copyFrom.deprecated; 115 this.examples = copyOf(copyFrom.examples, Example::copy); 116 } 117 118 /** 119 * Make a deep copy of this object. 120 * 121 * @return A deep copy of this object. 122 */ 123 public HeaderInfo copy() { 124 return new HeaderInfo(this); 125 } 126 127 @Override /* Overridden from OpenApiElement */ 128 protected HeaderInfo strict() { 129 super.strict(); 130 return this; 131 } 132 133 @Override /* Overridden from OpenApiElement */ 134 public HeaderInfo strict(Object value) { 135 super.strict(value); 136 return this; 137 } 138 139 /** 140 * Bean property getter: <property>description</property>. 141 * 142 * <p> 143 * A short description of the header. 144 * 145 * @return The property value, or <jk>null</jk> if it is not set. 146 */ 147 public String getDescription() { 148 return description; 149 } 150 151 /** 152 * Bean property setter: <property>description</property>. 153 * 154 * <p> 155 * A short description of the header. 156 * 157 * @param value 158 * The new value for this property. 159 * <br>Can be <jk>null</jk> to unset the property. 160 * @return This object 161 */ 162 public HeaderInfo setDescription(String value) { 163 description = value; 164 return this; 165 } 166 167 /** 168 * Bean property getter: <property>required</property>. 169 * 170 * <p> 171 * The type of the object. 172 * 173 * @return The property value, or <jk>null</jk> if it is not set. 174 */ 175 public Boolean getRequired() { 176 return required; 177 } 178 179 /** 180 * Bean property setter: <property>required</property>. 181 * 182 * <p> 183 * The type of the object. 184 * 185 * @param value 186 * The new value for this property. 187 * <br>Property value is required. 188 * <br>Valid values: 189 * <ul> 190 * <li><js>"string"</js> 191 * <li><js>"number"</js> 192 * <li><js>"integer"</js> 193 * <li><js>"boolean"</js> 194 * <li><js>"array"</js> 195 * </ul> 196 * <br>Can be <jk>null</jk> to unset the property. 197 * @return This object 198 */ 199 public HeaderInfo setRequired(Boolean value) { 200 required = value; 201 return this; 202 } 203 204 /** 205 * Bean property getter: <property>required</property>. 206 * 207 * <p> 208 * The type of the object. 209 * 210 * @return The property value, or <jk>null</jk> if it is not set. 211 */ 212 public Boolean getExplode() { 213 return explode; 214 } 215 216 /** 217 * Bean property setter: <property>explode</property>. 218 * 219 * <p> 220 * The type of the object. 221 * 222 * @param value 223 * The new value for this property. 224 * <br>Can be <jk>null</jk> to unset the property. 225 * @return This object 226 */ 227 public HeaderInfo setExplode(Boolean value) { 228 explode = value; 229 return this; 230 } 231 232 /** 233 * Bean property getter: <property>deprecated</property>. 234 * 235 * <p> 236 * The type of the object. 237 * 238 * @return The property value, or <jk>null</jk> if it is not set. 239 */ 240 public Boolean getDeprecated() { 241 return deprecated; 242 } 243 244 /** 245 * Bean property setter: <property>deprecated</property>. 246 * 247 * <p> 248 * The type of the object. 249 * 250 * @param value 251 * The new value for this property. 252 * <br>Can be <jk>null</jk> to unset the property. 253 * @return This object 254 */ 255 public HeaderInfo setDeprecated(Boolean value) { 256 deprecated = value; 257 return this; 258 } 259 260 /** 261 * Bean property getter: <property>allowEmptyValue</property>. 262 * 263 * <p> 264 * The type of the object. 265 * 266 * @return The property value, or <jk>null</jk> if it is not set. 267 */ 268 public Boolean getAllowEmptyValue() { 269 return allowEmptyValue; 270 } 271 272 /** 273 * Bean property setter: <property>allowEmptyValue</property>. 274 * 275 * <p> 276 * The type of the object. 277 * 278 * @param value 279 * The new value for this property. 280 * <br>Can be <jk>null</jk> to unset the property. 281 * @return This object 282 */ 283 public HeaderInfo setAllowEmptyValue(Boolean value) { 284 allowEmptyValue = value; 285 return this; 286 } 287 288 /** 289 * Bean property getter: <property>allowReserved</property>. 290 * 291 * <p> 292 * The type of the object. 293 * 294 * @return The property value, or <jk>null</jk> if it is not set. 295 */ 296 public Boolean getAllowReserved() { 297 return allowReserved; 298 } 299 300 /** 301 * Bean property setter: <property>allowReserved</property>. 302 * 303 * <p> 304 * The type of the object. 305 * 306 * @param value 307 * The new value for this property. 308 * <br>Can be <jk>null</jk> to unset the property. 309 * @return This object 310 */ 311 public HeaderInfo setAllowReserved(Boolean value) { 312 allowReserved = value; 313 return this; 314 } 315 316 /** 317 * Bean property getter: <property>schema</property>. 318 * 319 * @return The property value, or <jk>null</jk> if it is not set. 320 */ 321 public SchemaInfo getSchema() { 322 return schema; 323 } 324 325 /** 326 * Bean property setter: <property>schema</property>. 327 * 328 * @param value 329 * The new value for this property. 330 * <br>Can be <jk>null</jk> to unset the property. 331 * @return This object 332 */ 333 public HeaderInfo setSchema(SchemaInfo value) { 334 schema = value; 335 return this; 336 } 337 338 /** 339 * Bean property getter: <property>$ref</property>. 340 * 341 * @return The property value, or <jk>null</jk> if it is not set. 342 */ 343 @Beanp("$ref") 344 public String getRef() { 345 return ref; 346 } 347 348 /** 349 * Bean property setter: <property>$ref</property>. 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 @Beanp("$ref") 357 public HeaderInfo setRef(String value) { 358 ref = value; 359 return this; 360 } 361 362 /** 363 * Bean property getter: <property>x-example</property>. 364 * 365 * @return The property value, or <jk>null</jk> if it is not set. 366 */ 367 @Beanp("x-example") 368 public Object getExample() { 369 return example; 370 } 371 372 /** 373 * Bean property setter: <property>examples</property>. 374 * 375 * @param value 376 * The new value for this property. 377 * <br>Can be <jk>null</jk> to unset the property. 378 * @return This object 379 */ 380 @Beanp("x-example") 381 public HeaderInfo setExample(Object value) { 382 example = value; 383 return this; 384 } 385 386 /** 387 * Bean property getter: <property>examples</property>. 388 * 389 * <p> 390 * The list of possible responses as they are returned from executing this operation. 391 * 392 * @return The property value, or <jk>null</jk> if it is not set. 393 */ 394 public Map<String,Example> getExamples() { 395 return examples; 396 } 397 398 /** 399 * Bean property setter: <property>headers</property>. 400 * 401 * <p> 402 * A list of examples that are sent with the response. 403 * 404 * @param value 405 * The new value for this property. 406 * <br>Can be <jk>null</jk> to unset the property. 407 * @return This object 408 */ 409 public HeaderInfo setExamples(Map<String,Example> value) { 410 examples = copyOf(value); 411 return this; 412 } 413 414 /** 415 * Adds a single value to the <property>examples</property> property. 416 * 417 * @param name The example name. Must not be <jk>null</jk>. 418 * @param example The example. Must not be <jk>null</jk>. 419 * @return This object 420 */ 421 public HeaderInfo addExample(String name, Example example) { 422 assertArgNotNull("name", name); 423 assertArgNotNull("example", example); 424 examples = mapBuilder(examples).sparse().add(name, example).build(); 425 return this; 426 } 427 428 @Override /* Overridden from OpenApiElement */ 429 public <T> T get(String property, Class<T> type) { 430 assertArgNotNull("property", property); 431 return switch (property) { 432 case "description" -> (T)getDescription(); 433 case "required" -> toType(getRequired(), type); 434 case "explode" -> toType(getExplode(), type); 435 case "deprecated" -> toType(getDeprecated(), type); 436 case "allowEmptyValue" -> toType(getAllowEmptyValue(), type); 437 case "allowReserved" -> toType(getAllowReserved(), type); 438 case "$ref" -> toType(getRef(), type); 439 case "schema" -> toType(getSchema(), type); 440 case "x-example" -> toType(getExample(), type); 441 case "examples" -> toType(getExamples(), type); 442 default -> super.get(property, type); 443 }; 444 } 445 446 @Override /* Overridden from OpenApiElement */ 447 public HeaderInfo set(String property, Object value) { 448 assertArgNotNull("property", property); 449 return switch (property) { 450 case "$ref" -> setRef(Utils.s(value)); 451 case "allowEmptyValue" -> setAllowEmptyValue(toBoolean(value)); 452 case "allowReserved" -> setAllowReserved(toBoolean(value)); 453 case "deprecated" -> setDeprecated(toBoolean(value)); 454 case "description" -> setDescription(Utils.s(value)); 455 case "examples" -> setExamples(mapBuilder(String.class,Example.class).sparse().addAny(value).build()); 456 case "explode" -> setExplode(toBoolean(value)); 457 case "required" -> setRequired(toBoolean(value)); 458 case "schema" -> setSchema(toType(value, SchemaInfo.class)); 459 case "x-example" -> setExample(value); 460 default -> { 461 super.set(property, value); 462 yield this; 463 } 464 }; 465 } 466 467 @Override /* SwaggerElement */ 468 public Set<String> keySet() { 469 var s = setBuilder(String.class) 470 .addIf(ref != null, "$ref") 471 .addIf(allowEmptyValue != null, "allowEmptyValue") 472 .addIf(allowReserved != null, "allowReserved") 473 .addIf(deprecated != null, "deprecated") 474 .addIf(description != null, "description") 475 .addIf(examples != null, "examples") 476 .addIf(explode != null, "explode") 477 .addIf(required != null, "required") 478 .addIf(schema != null, "schema") 479 .addIf(example != null, "x-example") 480 .build(); 481 return new MultiSet<>(s, super.keySet()); 482 483 } 484 485 /** 486 * Resolves any <js>"$ref"</js> attributes in this element. 487 * 488 * @param openApi The swagger document containing the definitions. 489 * @param refStack Keeps track of previously-visited references so that we don't cause recursive loops. 490 * @param maxDepth 491 * The maximum depth to resolve references. 492 * <br>After that level is reached, <code>$ref</code> references will be left alone. 493 * <br>Useful if you have very complex models and you don't want your swagger page to be overly-complex. 494 * @return 495 * This object with references resolved. 496 * <br>May or may not be the same object. 497 */ 498 public HeaderInfo resolveRefs(OpenApi openApi, Deque<String> refStack, int maxDepth) { 499 500 if (ref != null) { 501 if (refStack.contains(ref) || refStack.size() >= maxDepth) 502 return this; 503 refStack.addLast(ref); 504 var r = openApi.findRef(ref, HeaderInfo.class); 505 r = r.resolveRefs(openApi, refStack, maxDepth); 506 refStack.removeLast(); 507 return r; 508 } 509 return this; 510 } 511}