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