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