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.ClassUtils.*; 021import static org.apache.juneau.internal.CollectionUtils.*; 022import static org.apache.juneau.internal.CollectionUtils.map; 023import static org.apache.juneau.internal.ConverterUtils.*; 024 025import java.util.*; 026 027import org.apache.juneau.*; 028import org.apache.juneau.collections.*; 029import org.apache.juneau.common.utils.*; 030import org.apache.juneau.internal.*; 031import org.apache.juneau.json.*; 032import org.apache.juneau.objecttools.*; 033 034/** 035 * This is the root document object for the Swagger 2.0 API specification. 036 * 037 * <p> 038 * The Swagger Object is the root document that describes an entire API. It contains metadata about the API, 039 * available paths and operations, parameters, responses, security definitions, and other information. This is 040 * the Swagger 2.0 specification (predecessor to OpenAPI 3.0). 041 * 042 * <h5 class='section'>Swagger Specification:</h5> 043 * <p> 044 * The Swagger Object is composed of the following fields: 045 * <ul class='spaced-list'> 046 * <li><c>swagger</c> (string, REQUIRED) - The Swagger Specification version (must be <js>"2.0"</js>) 047 * <li><c>info</c> ({@link Info}, REQUIRED) - Provides metadata about the API 048 * <li><c>host</c> (string) - The host (name or IP) serving the API 049 * <li><c>basePath</c> (string) - The base path on which the API is served (relative to host) 050 * <li><c>schemes</c> (array of string) - The transfer protocols of the API (e.g., <js>"http"</js>, <js>"https"</js>) 051 * <li><c>consumes</c> (array of string) - A list of MIME types the APIs can consume 052 * <li><c>produces</c> (array of string) - A list of MIME types the APIs can produce 053 * <li><c>paths</c> (map of {@link OperationMap}, REQUIRED) - The available paths and operations for the API 054 * <li><c>definitions</c> (map of {@link SchemaInfo}) - Schema definitions that can be referenced 055 * <li><c>parameters</c> (map of {@link ParameterInfo}) - Parameters definitions that can be referenced 056 * <li><c>responses</c> (map of {@link ResponseInfo}) - Response definitions that can be referenced 057 * <li><c>securityDefinitions</c> (map of {@link SecurityScheme}) - Security scheme definitions 058 * <li><c>security</c> (array of map) - Security requirements applied to all operations 059 * <li><c>tags</c> (array of {@link Tag}) - A list of tags used by the specification with additional metadata 060 * <li><c>externalDocs</c> ({@link ExternalDocumentation}) - Additional external documentation 061 * </ul> 062 * 063 * <h5 class='section'>Example:</h5> 064 * <p class='bjava'> 065 * <jc>// Create a Swagger document</jc> 066 * Swagger <jv>doc</jv> = <jk>new</jk> Swagger() 067 * .setSwagger(<js>"2.0"</js>) 068 * .setInfo( 069 * <jk>new</jk> Info() 070 * .setTitle(<js>"Pet Store API"</js>) 071 * .setVersion(<js>"1.0.0"</js>) 072 * ) 073 * .setHost(<js>"petstore.swagger.io"</js>) 074 * .setBasePath(<js>"/v2"</js>) 075 * .setSchemes(<js>"https"</js>) 076 * .addPath(<js>"/pets"</js>, <js>"get"</js>, 077 * <jk>new</jk> Operation() 078 * .setSummary(<js>"List all pets"</js>) 079 * .addResponse(<js>"200"</js>, <jk>new</jk> ResponseInfo(<js>"Success"</js>)) 080 * ); 081 * </p> 082 * 083 * <h5 class='section'>See Also:</h5><ul> 084 * <li class='link'><a class="doclink" href="https://swagger.io/specification/v2/">Swagger 2.0 Specification</a> 085 * <li class='link'><a class="doclink" href="https://swagger.io/docs/specification/2-0/basic-structure/">Swagger Basic Structure</a> 086 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanSwagger2">juneau-bean-swagger-v2</a> 087 * </ul> 088 */ 089public class Swagger extends SwaggerElement { 090 091 /** Represents a null swagger */ 092 public static final Swagger NULL = new Swagger(); 093 094 private static final Comparator<String> PATH_COMPARATOR = (o1, o2) -> o1.replace('{', '@').compareTo(o2.replace('{', '@')); 095 096 private String 097 swagger = "2.0", // NOSONAR - Intentional naming. 098 host, 099 basePath; 100 private Info info; 101 private ExternalDocumentation externalDocs; 102 private Set<String> schemes; 103 private Set<MediaType> 104 consumes, 105 produces; 106 private Set<Tag> tags; 107 private List<Map<String,List<String>>> security; 108 private Map<String,JsonMap> definitions; 109 private Map<String,ParameterInfo> parameters; 110 private Map<String,ResponseInfo> responses; 111 private Map<String,SecurityScheme> securityDefinitions; 112 private Map<String,OperationMap> paths; 113 114 /** 115 * Default constructor. 116 */ 117 public Swagger() {} 118 119 /** 120 * Copy constructor. 121 * 122 * @param copyFrom The object to copy. 123 */ 124 public Swagger(Swagger copyFrom) { 125 super(copyFrom); 126 127 this.basePath = copyFrom.basePath; 128 this.consumes = copyOf(copyFrom.consumes); 129 this.externalDocs = copyFrom.externalDocs == null ? null : copyFrom.externalDocs.copy(); 130 this.host = copyFrom.host; 131 this.info = copyFrom.info == null ? null : copyFrom.info.copy(); 132 this.produces = copyOf(copyFrom.produces); 133 this.schemes = copyOf(copyFrom.schemes); 134 this.swagger = copyFrom.swagger; 135 136 // TODO - Definitions are not deep copied, so they should not contain references. 137 this.definitions = copyOf(copyFrom.definitions, JsonMap::new); 138 139 this.paths = copyOf(copyFrom.paths, v -> { 140 var m = new OperationMap(); 141 v.forEach((k2,v2) -> m.put(k2, v2.copy())); 142 return m; 143 }); 144 145 this.parameters = copyOf(copyFrom.parameters, ParameterInfo::copy); 146 this.responses = copyOf(copyFrom.responses, ResponseInfo::copy); 147 this.securityDefinitions = copyOf(copyFrom.securityDefinitions, SecurityScheme::copy); 148 149 this.security = copyOf(copyFrom.security, x -> { 150 Map<String,List<String>> m2 = map(); 151 x.forEach((k,v) -> m2.put(k, copyOf(v))); 152 return m2; 153 }); 154 155 this.tags = copyOf(copyFrom.tags, x -> x.copy()); 156 } 157 158 /** 159 * Make a deep copy of this object. 160 * 161 * @return A deep copy of this object. 162 */ 163 public Swagger copy() { 164 return new Swagger(this); 165 } 166 167 //----------------------------------------------------------------------------------------------------------------- 168 // Properties 169 //----------------------------------------------------------------------------------------------------------------- 170 171 /** 172 * Bean property getter: <property>basePath</property>. 173 * 174 * <p> 175 * The base path on which the API is served, which is relative to the <c>host</c>. 176 * 177 * @return The property value, or <jk>null</jk> if it is not set. 178 */ 179 public String getBasePath() { 180 return basePath; 181 } 182 183 /** 184 * Bean property setter: <property>basePath</property>. 185 * 186 * <p> 187 * The base path on which the API is served, which is relative to the <c>host</c>. 188 * 189 * @param value 190 * The new value for this property. 191 * <br>If it is not included, the API is served directly under the <c>host</c>. 192 * <br>The value MUST start with a leading slash (/). 193 * <br>The <c>basePath</c> does not support <a class="doclink" href="https://swagger.io/specification/v2#pathTemplating">path templating</a>. 194 * <br>Can be <jk>null</jk> to unset the property. 195 * @return This object. 196 */ 197 public Swagger setBasePath(String value) { 198 basePath = value; 199 return this; 200 } 201 202 /** 203 * Bean property getter: <property>consumes</property>. 204 * 205 * <p> 206 * A list of MIME types the APIs can consume. 207 * 208 * @return The property value, or <jk>null</jk> if it is not set. 209 */ 210 public Set<MediaType> getConsumes() { 211 return consumes; 212 } 213 214 /** 215 * Bean property setter: <property>consumes</property>. 216 * 217 * <p> 218 * A list of MIME types the APIs can consume. 219 * 220 * @param value 221 * The new value for this property. 222 * <br>Value MUST be as described under <a class="doclink" href="https://swagger.io/specification#mimeTypes">Swagger Mime Types</a>. 223 * <br>Can be <jk>null</jk> to unset the property. 224 * @return This object. 225 */ 226 public Swagger setConsumes(Collection<MediaType> value) { 227 consumes = setFrom(value); 228 return this; 229 } 230 231 /** 232 * Bean property appender: <property>consumes</property>. 233 * 234 * <p> 235 * A list of MIME types the APIs can consume. 236 * 237 * @param values 238 * The values to add to this property. 239 * <br>Values MUST be as described under <a class="doclink" href="https://swagger.io/specification#mimeTypes">Swagger Mime Types</a>. 240 * <br>Ignored if <jk>null</jk>. 241 * @return This object. 242 */ 243 public Swagger addConsumes(MediaType...values) { 244 consumes = setBuilder(consumes).sparse().add(values).build(); 245 return this; 246 } 247 248 /** 249 * Bean property appender: <property>consumes</property>. 250 * 251 * <p> 252 * A list of MIME types the APIs can consume. 253 * 254 * @param values 255 * The values to add to this property. 256 * <br>Ignored if <jk>null</jk>. 257 * @return This object. 258 */ 259 public Swagger addConsumes(Collection<MediaType> values) { 260 consumes = setBuilder(consumes).sparse().addAll(values).build(); 261 return this; 262 } 263 264 /** 265 * Bean property fluent setter: <property>consumes</property>. 266 * 267 * <p> 268 * A list of MIME types the APIs can consume. 269 * 270 * @param value 271 * The values to set on this property. 272 * @return This object. 273 */ 274 public Swagger setConsumes(MediaType...value) { 275 setConsumes(setBuilder(MediaType.class).sparse().add(value).build()); 276 return this; 277 } 278 279 /** 280 * Bean property getter: <property>definitions</property>. 281 * 282 * <p> 283 * An object to hold data types produced and consumed by operations. 284 * 285 * @return The property value, or <jk>null</jk> if it is not set. 286 */ 287 public Map<String,JsonMap> getDefinitions() { 288 return definitions; 289 } 290 291 /** 292 * Bean property setter: <property>definitions</property>. 293 * 294 * <p> 295 * An object to hold data types produced and consumed by operations. 296 * 297 * @param value 298 * The new value for this property. 299 * <br>Can be <jk>null</jk> to unset the property. 300 * @return This object. 301 */ 302 public Swagger setDefinitions(Map<String,JsonMap> value) { 303 definitions = copyOf(value); 304 return this; 305 } 306 307 /** 308 * Bean property appender: <property>definitions</property>. 309 * 310 * <p> 311 * Adds a single value to the <property>definitions</property> property. 312 * 313 * @param name A definition name. Must not be <jk>null</jk>. 314 * @param schema The schema that the name defines. Must not be <jk>null</jk>. 315 * @return This object. 316 */ 317 public Swagger addDefinition(String name, JsonMap schema) { 318 assertArgNotNull("name", name); 319 assertArgNotNull("schema", schema); 320 definitions = mapBuilder(definitions).sparse().add(name, schema).build(); 321 return this; 322 } 323 324 /** 325 * Bean property getter: <property>externalDocs</property>. 326 * 327 * <p> 328 * Additional external documentation. 329 * 330 * @return The property value, or <jk>null</jk> if it is not set. 331 */ 332 public ExternalDocumentation getExternalDocs() { 333 return externalDocs; 334 } 335 336 /** 337 * Bean property setter: <property>externalDocs</property>. 338 * 339 * <p> 340 * Additional external documentation. 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 Swagger setExternalDocs(ExternalDocumentation value) { 348 externalDocs = value; 349 return this; 350 } 351 352 /** 353 * Bean property getter: <property>host</property>. 354 * 355 * <p> 356 * The host (name or IP) serving the API. 357 * 358 * @return The property value, or <jk>null</jk> if it is not set. 359 */ 360 public String getHost() { 361 return host; 362 } 363 364 /** 365 * Bean property setter: <property>host</property>. 366 * 367 * <p> 368 * The host (name or IP) serving the API. 369 * 370 * @param value 371 * The new value for this property. 372 * <br>This MUST be the host only and does not include the scheme nor sub-paths. 373 * <br>It MAY include a port. 374 * <br>If the host is not included, the host serving the documentation is to be used (including the port). 375 * <br>The host does not support <a class="doclink" href="https://swagger.io/specification/v2#pathTemplating">path templating</a> 376 * <br>Can be <jk>null</jk> to unset the property. 377 * @return This object. 378 */ 379 public Swagger setHost(String value) { 380 host = value; 381 return this; 382 } 383 384 /** 385 * Bean property getter: <property>info</property>. 386 * 387 * <p> 388 * Provides metadata about the API. 389 * 390 * @return The property value, or <jk>null</jk> if it is not set. 391 */ 392 public Info getInfo() { 393 return info; 394 } 395 396 /** 397 * Bean property setter: <property>info</property>. 398 * 399 * <p> 400 * Provides metadata about the API. 401 * 402 * @param value 403 * The new value for this property. 404 * <br>Property value is required. 405 * <br>Can be <jk>null</jk> to unset the property. 406 * @return This object. 407 */ 408 public Swagger setInfo(Info value) { 409 info = value; 410 return this; 411 } 412 413 /** 414 * Bean property getter: <property>parameters</property>. 415 * 416 * <p> 417 * An object to hold parameters that can be used across operations. 418 * 419 * @return The property value, or <jk>null</jk> if it is not set. 420 */ 421 public Map<String,ParameterInfo> getParameters() { 422 return parameters; 423 } 424 425 /** 426 * Bean property setter: <property>parameters</property>. 427 * 428 * <p> 429 * An object to hold parameters that can be used across operations. 430 * 431 * @param value 432 * The new value for this property. 433 * <br>Can be <jk>null</jk> to unset the property. 434 * @return This object. 435 */ 436 public Swagger setParameters(Map<String,ParameterInfo> value) { 437 parameters = copyOf(value); 438 return this; 439 } 440 441 /** 442 * Bean property appender: <property>parameters</property>. 443 * 444 * <p> 445 * Adds a single value to the <property>parameter</property> property. 446 * 447 * @param name The parameter name. Must not be <jk>null</jk>. 448 * @param parameter The parameter definition. Must not be <jk>null</jk>. 449 * @return This object. 450 */ 451 public Swagger addParameter(String name, ParameterInfo parameter) { 452 assertArgNotNull("name", name); 453 assertArgNotNull("parameter", parameter); 454 parameters = mapBuilder(parameters).sparse().add(name, parameter).build(); 455 return this; 456 } 457 458 /** 459 * Bean property getter: <property>paths</property>. 460 * 461 * <p> 462 * The available paths and operations for the API. 463 * 464 * @return The property value, or <jk>null</jk> if it is not set. 465 */ 466 public Map<String,OperationMap> getPaths() { 467 return paths; 468 } 469 470 /** 471 * Bean property setter: <property>paths</property>. 472 * 473 * <p> 474 * The available paths and operations for the API. 475 * 476 * @param value 477 * The new value for this property. 478 * <br>Property value is required. 479 * <br>Can be <jk>null</jk> to unset the property. 480 * @return This object. 481 */ 482 public Swagger setPaths(Map<String,OperationMap> value) { 483 paths = mapBuilder(String.class,OperationMap.class).sparse().sorted(PATH_COMPARATOR).addAll(value).build(); 484 return this; 485 } 486 487 /** 488 * Bean property appender: <property>paths</property>. 489 * 490 * <p> 491 * Adds a single value to the <property>paths</property> property. 492 * 493 * @param path The path template. Must not be <jk>null</jk>. 494 * @param methodName The HTTP method name. Must not be <jk>null</jk>. 495 * @param operation The operation that describes the path. Must not be <jk>null</jk>. 496 * @return This object. 497 */ 498 public Swagger addPath(String path, String methodName, Operation operation) { 499 assertArgNotNull("path", path); 500 assertArgNotNull("methodName", methodName); 501 assertArgNotNull("operation", operation); 502 if (paths == null) 503 paths = new TreeMap<>(PATH_COMPARATOR); 504 getPaths().computeIfAbsent(path, k -> new OperationMap()).put(methodName, operation); 505 return this; 506 } 507 508 /** 509 * Bean property getter: <property>produces</property>. 510 * 511 * <p> 512 * A list of MIME types the APIs can produce. 513 * 514 * @return The property value, or <jk>null</jk> if it is not set. 515 */ 516 public Set<MediaType> getProduces() { 517 return produces; 518 } 519 520 /** 521 * Bean property setter: <property>produces</property>. 522 * 523 * <p> 524 * A list of MIME types the APIs can produce. 525 * 526 * @param value 527 * The new value for this property. 528 * <br>Value MUST be as described under <a class="doclink" href="https://swagger.io/specification#mimeTypes">Swagger Mime Types</a>. 529 * <br>Can be <jk>null</jk> to unset the property. 530 * @return This object. 531 */ 532 public Swagger setProduces(Collection<MediaType> value) { 533 produces = setFrom(value); 534 return this; 535 } 536 537 /** 538 * Adds one or more values to the <property>produces</property> property. 539 * 540 * <p> 541 * A list of MIME types the APIs can produce. 542 * 543 * @param values 544 * The values to add to this property. 545 * <br>Value MUST be as described under <a class="doclink" href="https://swagger.io/specification#mimeTypes">Swagger Mime Types</a>. 546 * <br>Can be <jk>null</jk> to unset the property. 547 * @return This object. 548 */ 549 public Swagger addProduces(MediaType...values) { 550 produces = setBuilder(produces).sparse().add(values).build(); 551 return this; 552 } 553 554 /** 555 * Bean property appender: <property>produces</property>. 556 * 557 * <p> 558 * A list of MIME types the APIs can produce. 559 * 560 * @param values 561 * The values to add to this property. 562 * <br>Value MUST be as described under <a class="doclink" href="https://swagger.io/specification#mimeTypes">Swagger Mime Types</a>. 563 * <br>Ignored if <jk>null</jk>. 564 * @return This object. 565 */ 566 public Swagger addProduces(Collection<MediaType> values) { 567 produces = setBuilder(produces).sparse().addAll(values).build(); 568 return this; 569 } 570 571 /** 572 * Bean property fluent setter: <property>produces</property>. 573 * 574 * <p> 575 * A list of MIME types the APIs can produce. 576 * 577 * @param value 578 * The new value for this property. 579 * @return This object. 580 */ 581 public Swagger setProduces(MediaType...value) { 582 setProduces(setBuilder(MediaType.class).sparse().add(value).build()); 583 return this; 584 } 585 586 /** 587 * Bean property getter: <property>responses</property>. 588 * 589 * <p> 590 * An object to hold responses that can be used across operations. 591 * 592 * @return The property value, or <jk>null</jk> if it is not set. 593 */ 594 public Map<String,ResponseInfo> getResponses() { 595 return responses; 596 } 597 598 /** 599 * Bean property setter: <property>responses</property>. 600 * 601 * <p> 602 * An object to hold responses that can be used across operations. 603 * 604 * @param value 605 * The new value for this property. 606 * <br>Can be <jk>null</jk> to unset the property. 607 * @return This object. 608 */ 609 public Swagger setResponses(Map<String,ResponseInfo> value) { 610 responses = copyOf(value); 611 return this; 612 } 613 614 /** 615 * Bean property appender: <property>responses</property>. 616 * 617 * <p> 618 * Adds a single value to the <property>responses</property> property. 619 * 620 * @param name The response name. Must not be <jk>null</jk>. 621 * @param response The response definition. Must not be <jk>null</jk>. 622 * @return This object. 623 */ 624 public Swagger addResponse(String name, ResponseInfo response) { 625 assertArgNotNull("name", name); 626 assertArgNotNull("response", response); 627 responses = mapBuilder(responses).sparse().add(name, response).build(); 628 return this; 629 } 630 631 /** 632 * Bean property getter: <property>schemes</property>. 633 * 634 * <p> 635 * The transfer protocol of the API. 636 * 637 * @return The property value, or <jk>null</jk> if it is not set. 638 */ 639 public Set<String> getSchemes() { 640 return schemes; 641 } 642 643 /** 644 * Bean property setter: <property>schemes</property>. 645 * 646 * <p> 647 * The transfer protocol of the API. 648 * 649 * @param value 650 * The new value for this property. 651 * <br>Valid values: 652 * <ul> 653 * <li><js>"http"</js> 654 * <li><js>"https"</js> 655 * <li><js>"ws"</js> 656 * <li><js>"wss"</js> 657 * </ul> 658 * <br>Can be <jk>null</jk> to unset the property. 659 * @return This object. 660 */ 661 public Swagger setSchemes(Collection<String> value) { 662 schemes = setFrom(value); 663 return this; 664 } 665 666 /** 667 * Bean property appender: <property>schemes</property>. 668 * 669 * <p> 670 * The transfer protocol of the API. 671 * 672 * @param values 673 * The values to add to this property. 674 * <br>Valid values: 675 * <ul> 676 * <li><js>"http"</js> 677 * <li><js>"https"</js> 678 * <li><js>"ws"</js> 679 * <li><js>"wss"</js> 680 * </ul> 681 * <br>Ignored if <jk>null</jk>. 682 * @return This object. 683 */ 684 public Swagger addSchemes(String...values) { 685 schemes = setBuilder(schemes).sparse().add(values).build(); 686 return this; 687 } 688 689 /** 690 * Bean property appender: <property>schemes</property>. 691 * 692 * <p> 693 * The transfer protocol of the API. 694 * 695 * @param values 696 * The values to add to this property. 697 * <br>Valid values: 698 * <ul> 699 * <li><js>"http"</js> 700 * <li><js>"https"</js> 701 * <li><js>"ws"</js> 702 * <li><js>"wss"</js> 703 * </ul> 704 * <br>Ignored if <jk>null</jk>. 705 * @return This object. 706 */ 707 public Swagger addSchemes(Collection<String> values) { 708 schemes = setBuilder(schemes).sparse().addAll(values).build(); 709 return this; 710 } 711 712 /** 713 * Bean property fluent setter: <property>schemes</property>. 714 * 715 * <p> 716 * The transfer protocol of the API. 717 * 718 * @param value 719 * The new value for this property. 720 * <br>Strings can be JSON arrays. 721 * @return This object. 722 */ 723 public Swagger setSchemes(String...value) { 724 setSchemes(setBuilder(String.class).sparse().addJson(value).build()); 725 return this; 726 } 727 728 /** 729 * Bean property getter: <property>security</property>. 730 * 731 * <p> 732 * A declaration of which security schemes are applied for the API as a whole. 733 * 734 * @return The property value, or <jk>null</jk> if it is not set. 735 */ 736 public List<Map<String,List<String>>> getSecurity() { 737 return security; 738 } 739 740 /** 741 * Bean property setter: <property>security</property>. 742 * 743 * <p> 744 * A declaration of which security schemes are applied for the API as a whole. 745 * 746 * @param value 747 * The new value for this property. 748 * <br>Can be <jk>null</jk> to unset the property. 749 * @return This object. 750 */ 751 public Swagger setSecurity(Collection<Map<String,List<String>>> value) { 752 security = listFrom(value); 753 return this; 754 } 755 756 /** 757 * Bean property appender: <property>security</property>. 758 * 759 * <p> 760 * Adds a single value to the <property>securityDefinitions</property> property. 761 * 762 * @param scheme The security scheme that applies to this operation Must not be <jk>null</jk>. 763 * @param alternatives 764 * The list of values describes alternative security schemes that can be used (that is, there is a logical OR between the security requirements). 765 * @return This object. 766 */ 767 public Swagger addSecurity(String scheme, String...alternatives) { 768 assertArgNotNull("scheme", scheme); 769 Map<String,List<String>> m = map(); 770 m.put(scheme, alist(alternatives)); 771 security = listBuilder(security).sparse().addAll(Collections.singleton(m)).build(); 772 return this; 773 } 774 775 /** 776 * Bean property fluent setter: <property>security</property>. 777 * 778 * <p> 779 * A declaration of which security schemes are applied for the API as a whole. 780 * 781 * @param values 782 * The values to add to this property. 783 * <br>Ignored if <jk>null</jk>. 784 * @return This object. 785 */ 786 public Swagger addSecurity(Collection<Map<String,List<String>>> values) { 787 security = listBuilder(security).sparse().addAll(values).build(); 788 return this; 789 } 790 791 /** 792 * Bean property getter: <property>securityDefinitions</property>. 793 * 794 * <p> 795 * Security scheme definitions that can be used across the specification. 796 * 797 * @return The property value, or <jk>null</jk> if it is not set. 798 */ 799 public Map<String,SecurityScheme> getSecurityDefinitions() { 800 return securityDefinitions; 801 } 802 803 /** 804 * Bean property setter: <property>securityDefinitions</property>. 805 * 806 * <p> 807 * Security scheme definitions that can be used across the specification. 808 * 809 * @param value 810 * The new value for this property. 811 * <br>Can be <jk>null</jk> to unset the property. 812 * @return This object. 813 */ 814 public Swagger setSecurityDefinitions(Map<String,SecurityScheme> value) { 815 securityDefinitions = copyOf(value); 816 return this; 817 } 818 819 /** 820 * Bean property appender: <property>securityDefinitions</property>. 821 * 822 * <p> 823 * Adds a single value to the <property>securityDefinitions</property> property. 824 * 825 * @param name A security name. Must not be <jk>null</jk>. 826 * @param securityScheme A security schema. Must not be <jk>null</jk>. 827 * @return This object. 828 */ 829 public Swagger addSecurityDefinition(String name, SecurityScheme securityScheme) { 830 assertArgNotNull("name", name); 831 assertArgNotNull("securityScheme", securityScheme); 832 securityDefinitions = mapBuilder(securityDefinitions).sparse().add(name, securityScheme).build(); 833 return this; 834 } 835 836 /** 837 * Bean property getter: <property>swagger</property>. 838 * 839 * <p> 840 * Specifies the Swagger Specification version being used. 841 * 842 * @return The property value, or <jk>null</jk> if it is not set. 843 */ 844 public String getSwagger() { 845 return swagger; 846 } 847 848 /** 849 * Bean property setter: <property>swagger</property>. 850 * 851 * <p> 852 * Specifies the Swagger Specification version being used. 853 * 854 * @param value 855 * The new value for this property. 856 * <br>Property value is required. 857 * <br>Can be <jk>null</jk> to unset the property. 858 * @return This object. 859 */ 860 public Swagger setSwagger(String value) { 861 swagger = value; 862 return this; 863 } 864 865 /** 866 * Bean property getter: <property>tags</property>. 867 * 868 * <p> 869 * A list of tags used by the specification with additional metadata. 870 * 871 * @return The property value, or <jk>null</jk> if it is not set. 872 */ 873 public Set<Tag> getTags() { 874 return tags; 875 } 876 877 /** 878 * Bean property setter: <property>tags</property>. 879 * 880 * <p> 881 * A list of tags used by the specification with additional metadata. 882 * 883 * @param value 884 * The new value for this property. 885 * <br>The order of the tags can be used to reflect on their order by the parsing tools. 886 * <br>Not all tags that are used by the <a class="doclink" href="https://swagger.io/specification/v2#operationObject">Operation Object</a> must be declared. 887 * <br>The tags that are not declared may be organized randomly or based on the tools' logic. 888 * <br>Each tag name in the list MUST be unique. 889 * <br>Can be <jk>null</jk> to unset the property. 890 * @return This object. 891 */ 892 public Swagger setTags(Collection<Tag> value) { 893 tags = setFrom(value); 894 return this; 895 } 896 897 /** 898 * Bean property setter: <property>tags</property>. 899 * 900 * <p> 901 * A list of tags used by the specification with additional metadata. 902 * 903 * @param value 904 * The new value for this property. 905 * <br>The order of the tags can be used to reflect on their order by the parsing tools. 906 * <br>Not all tags that are used by the <a class="doclink" href="https://swagger.io/specification/v2#operationObject">Operation Object</a> must be declared. 907 * <br>The tags that are not declared may be organized randomly or based on the tools' logic. 908 * <br>Each tag name in the list MUST be unique. 909 * <br>Ignored if <jk>null</jk>. 910 * @return This object. 911 */ 912 public Swagger setTags(Tag...value) { 913 setTags(setBuilder(Tag.class).sparse().add(value).build()); 914 return this; 915 } 916 917 /** 918 * Bean property appender: <property>tags</property>. 919 * 920 * <p> 921 * A list of tags used by the specification with additional metadata. 922 * 923 * @param values 924 * The values to add to this property. 925 * <br>The order of the tags can be used to reflect on their order by the parsing tools. 926 * <br>Not all tags that are used by the <a class="doclink" href="https://swagger.io/specification/v2#operationObject">Operation Object</a> must be declared. 927 * <br>The tags that are not declared may be organized randomly or based on the tools' logic. 928 * <br>Each tag name in the list MUST be unique. 929 * <br>Ignored if <jk>null</jk>. 930 * @return This object. 931 */ 932 public Swagger addTags(Tag...values) { 933 tags = setBuilder(tags).sparse().add(values).build(); 934 return this; 935 } 936 937 /** 938 * Bean property appender: <property>tags</property>. 939 * 940 * <p> 941 * A list of tags used by the specification with additional metadata. 942 * 943 * @param values 944 * The values to add to this property. 945 * <br>The order of the tags can be used to reflect on their order by the parsing tools. 946 * <br>Not all tags that are used by the <a class="doclink" href="https://swagger.io/specification/v2#operationObject">Operation Object</a> must be declared. 947 * <br>The tags that are not declared may be organized randomly or based on the tools' logic. 948 * <br>Each tag name in the list MUST be unique. 949 * <br>Ignored if <jk>null</jk>. 950 * @return This object. 951 */ 952 public Swagger addTags(Collection<Tag> values) { 953 tags = setBuilder(tags).sparse().addAll(values).build(); 954 return this; 955 } 956 957 //----------------------------------------------------------------------------------------------------------------- 958 // Convenience methods 959 //----------------------------------------------------------------------------------------------------------------- 960 961 /** 962 * Shortcut for calling <c>getPaths().get(path);</c> 963 * 964 * @param path The path (e.g. <js>"/foo"</js>). Must not be <jk>null</jk>. 965 * @return The operation map for the specified path, or <jk>null</jk> if it doesn't exist. 966 */ 967 public OperationMap getPath(String path) { 968 assertArgNotNull("path", path); 969 return opt(getPaths()).map(x -> x.get(path)).orElse(null); 970 } 971 972 /** 973 * Shortcut for calling <c>getPaths().get(path).get(operation);</c> 974 * 975 * @param path The path (e.g. <js>"/foo"</js>). Must not be <jk>null</jk>. 976 * @param operation The HTTP operation (e.g. <js>"get"</js>). Must not be <jk>null</jk>. 977 * @return The operation for the specified path and operation id, or <jk>null</jk> if it doesn't exist. 978 */ 979 public Operation getOperation(String path, String operation) { 980 assertArgNotNull("path", path); 981 assertArgNotNull("operation", operation); 982 return opt(getPath(path)).map(x -> x.get(operation)).orElse(null); 983 } 984 985 /** 986 * Shortcut for calling <c>getPaths().get(path).get(operation).getResponse(status);</c> 987 * 988 * @param path The path (e.g. <js>"/foo"</js>). Must not be <jk>null</jk>. 989 * @param operation The HTTP operation (e.g. <js>"get"</js>). Must not be <jk>null</jk>. 990 * @param status The HTTP response status (e.g. <js>"200"</js>). Must not be <jk>null</jk>. 991 * @return The operation for the specified path and operation id, or <jk>null</jk> if it doesn't exist. 992 */ 993 public ResponseInfo getResponseInfo(String path, String operation, String status) { 994 assertArgNotNull("path", path); 995 assertArgNotNull("operation", operation); 996 assertArgNotNull("status", status); 997 return opt(getPath(path)).map(x -> x.get(operation)).map(x -> x.getResponse(status)).orElse(null); 998 } 999 1000 /** 1001 * Shortcut for calling <c>getPaths().get(path).get(operation).getResponse(status);</c> 1002 * 1003 * @param path The path (e.g. <js>"/foo"</js>). 1004 * @param operation The HTTP operation (e.g. <js>"get"</js>). 1005 * @param status The HTTP response status (e.g. <js>"200"</js>). 1006 * @return The operation for the specified path and operation id, or <jk>null</jk> if it doesn't exist. 1007 */ 1008 public ResponseInfo getResponseInfo(String path, String operation, int status) { 1009 return getResponseInfo(path, operation, String.valueOf(status)); 1010 } 1011 1012 /** 1013 * Convenience method for calling <c>getPath(path).get(method).getParameter(in,name);</c> 1014 * 1015 * @param path The HTTP path. Must not be <jk>null</jk>. 1016 * @param method The HTTP method. Must not be <jk>null</jk>. 1017 * @param in The parameter type. Must not be <jk>null</jk>. 1018 * @param name The parameter name. Can be <jk>null</jk> for parameter type <c>body</c>. 1019 * @return The parameter information or <jk>null</jk> if not found. 1020 */ 1021 public ParameterInfo getParameterInfo(String path, String method, String in, String name) { 1022 assertArgNotNull("path", path); 1023 assertArgNotNull("method", method); 1024 assertArgNotNull("in", in); 1025 return opt(getPath(path)).map(x -> x.get(method)).map(x -> x.getParameter(in, name)).orElse(null); 1026 } 1027 1028 @Override /* Overridden from SwaggerElement */ 1029 public <T> T get(String property, Class<T> type) { 1030 assertArgNotNull("property", property); 1031 return switch (property) { 1032 case "basePath" -> toType(getBasePath(), type); 1033 case "consumes" -> toType(getConsumes(), type); 1034 case "definitions" -> toType(getDefinitions(), type); 1035 case "externalDocs" -> toType(getExternalDocs(), type); 1036 case "host" -> toType(getHost(), type); 1037 case "info" -> toType(getInfo(), type); 1038 case "parameters" -> toType(getParameters(), type); 1039 case "paths" -> toType(getPaths(), type); 1040 case "produces" -> toType(getProduces(), type); 1041 case "responses" -> toType(getResponses(), type); 1042 case "schemes" -> toType(getSchemes(), type); 1043 case "security" -> toType(getSecurity(), type); 1044 case "securityDefinitions" -> toType(getSecurityDefinitions(), type); 1045 case "swagger" -> toType(getSwagger(), type); 1046 case "tags" -> toType(getTags(), type); 1047 default -> super.get(property, type); 1048 }; 1049 } 1050 1051 @SuppressWarnings("rawtypes") 1052 @Override /* Overridden from SwaggerElement */ 1053 public Swagger set(String property, Object value) { 1054 assertArgNotNull("property", property); 1055 return switch (property) { 1056 case "basePath" -> setBasePath(Utils.s(value)); 1057 case "consumes" -> setConsumes(listBuilder(MediaType.class).sparse().addAny(value).build()); 1058 case "definitions" -> setDefinitions(mapBuilder(String.class,JsonMap.class).sparse().addAny(value).build()); 1059 case "externalDocs" -> setExternalDocs(toType(value, ExternalDocumentation.class)); 1060 case "host" -> setHost(Utils.s(value)); 1061 case "info" -> setInfo(toType(value, Info.class)); 1062 case "parameters" -> setParameters(mapBuilder(String.class,ParameterInfo.class).sparse().addAny(value).build()); 1063 case "paths" -> setPaths(mapBuilder(String.class,OperationMap.class).sparse().addAny(value).build()); 1064 case "produces" -> setProduces(listBuilder(MediaType.class).sparse().addAny(value).build()); 1065 case "responses" -> setResponses(mapBuilder(String.class,ResponseInfo.class).sparse().addAny(value).build()); 1066 case "schemes" -> setSchemes(listBuilder(String.class).sparse().addAny(value).build()); 1067 case "security" -> setSecurity((List)listBuilder(Map.class,String.class,List.class,String.class).sparse().addAny(value).build()); 1068 case "securityDefinitions" -> setSecurityDefinitions(mapBuilder(String.class,SecurityScheme.class).sparse().addAny(value).build()); 1069 case "swagger" -> setSwagger(Utils.s(value)); 1070 case "tags" -> setTags(listBuilder(Tag.class).sparse().addAny(value).build()); 1071 default -> { 1072 super.set(property, value); 1073 yield this; 1074 } 1075 }; 1076 } 1077 1078 @Override /* Overridden from SwaggerElement */ 1079 public Set<String> keySet() { 1080 var s = setBuilder(String.class) 1081 .addIf(basePath != null, "basePath") 1082 .addIf(consumes != null, "consumes") 1083 .addIf(definitions != null, "definitions") 1084 .addIf(externalDocs != null, "externalDocs") 1085 .addIf(host != null, "host") 1086 .addIf(info != null, "info") 1087 .addIf(parameters != null, "parameters") 1088 .addIf(paths != null, "paths") 1089 .addIf(produces != null, "produces") 1090 .addIf(responses != null, "responses") 1091 .addIf(schemes != null, "schemes") 1092 .addIf(security != null, "security") 1093 .addIf(securityDefinitions != null, "securityDefinitions") 1094 .addIf(swagger != null, "swagger") 1095 .addIf(tags != null, "tags") 1096 .build(); 1097 return new MultiSet<>(s, super.keySet()); 1098 } 1099 1100 /** 1101 * A synonym of {@link #toString()}. 1102 * @return This object serialized as JSON. 1103 */ 1104 public String asJson() { 1105 return toString(); 1106 } 1107 1108 @Override /* Object */ 1109 public String toString() { 1110 return JsonSerializer.DEFAULT.toString(this); 1111 } 1112 1113 /** 1114 * Resolves a <js>"$ref"</js> tags to nodes in this swagger document. 1115 * 1116 * @param <T> The class to convert the reference to. 1117 * @param ref The ref tag value. Must not be <jk>null</jk> or blank. 1118 * @param c The class to convert the reference to. Must not be <jk>null</jk>. 1119 * @return The referenced node, or <jk>null</jk> if not found. 1120 */ 1121 public <T> T findRef(String ref, Class<T> c) { 1122 assertArgNotNullOrBlank("ref", ref); 1123 assertArgNotNull("c", c); 1124 if (! ref.startsWith("#/")) 1125 throw new BasicRuntimeException("Unsupported reference: ''{0}''", ref); 1126 try { 1127 return new ObjectRest(this).get(ref.substring(1), c); 1128 } catch (Exception e) { 1129 throw new BeanRuntimeException(e, c, "Reference ''{0}'' could not be converted to type ''{1}''.", ref, className(c)); 1130 } 1131 } 1132 1133 /** 1134 * Sets strict mode on this bean. 1135 * 1136 * @return This object. 1137 */ 1138 @Override 1139 public Swagger strict() { 1140 super.strict(); 1141 return this; 1142 } 1143 1144 /** 1145 * Sets strict mode on this bean. 1146 * 1147 * @param value 1148 * The new value for this property. 1149 * <br>Non-boolean values will be converted to boolean using <code>Boolean.<jsm>valueOf</jsm>(value.toString())</code>. 1150 * <br>Can be <jk>null</jk> (interpreted as <jk>false</jk>). 1151 * @return This object. 1152 */ 1153 @Override 1154 public Swagger strict(Object value) { 1155 super.strict(value); 1156 return this; 1157 } 1158 1159}