001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.bean.openapi3; 018 019import static org.apache.juneau.common.utils.Utils.*; 020import static org.apache.juneau.internal.CollectionUtils.*; 021import static org.apache.juneau.internal.ConverterUtils.*; 022 023import java.util.*; 024 025import org.apache.juneau.common.utils.*; 026import org.apache.juneau.internal.*; 027 028/** 029 * Describes a single API operation on a path. 030 * 031 * <p> 032 * The Operation Object describes a single operation (such as GET, POST, PUT, DELETE) that can be performed on a path. 033 * Operations are the core of the API specification, defining what actions can be taken, what parameters they accept, 034 * and what responses they return. 035 * 036 * <h5 class='section'>OpenAPI Specification:</h5> 037 * <p> 038 * The Operation Object is composed of the following fields: 039 * <ul class='spaced-list'> 040 * <li><c>tags</c> (array of string) - A list of tags for API documentation control 041 * <li><c>summary</c> (string) - A short summary of what the operation does 042 * <li><c>description</c> (string) - A verbose explanation of the operation behavior (CommonMark syntax may be used) 043 * <li><c>externalDocs</c> ({@link ExternalDocumentation}) - Additional external documentation for this operation 044 * <li><c>operationId</c> (string) - Unique string used to identify the operation 045 * <li><c>parameters</c> (array of {@link Parameter}) - A list of parameters that are applicable for this operation 046 * <li><c>requestBody</c> ({@link RequestBodyInfo}) - The request body applicable for this operation 047 * <li><c>responses</c> (map of {@link Response}, REQUIRED) - The list of possible responses as they are returned from executing this operation 048 * <li><c>callbacks</c> (map of {@link Callback}) - A map of possible out-of band callbacks related to the parent operation 049 * <li><c>deprecated</c> (boolean) - Declares this operation to be deprecated 050 * <li><c>security</c> (array of {@link SecurityRequirement}) - A declaration of which security mechanisms can be used for this operation 051 * <li><c>servers</c> (array of {@link Server}) - An alternative server array to service this operation 052 * </ul> 053 * 054 * <h5 class='section'>Example:</h5> 055 * <p class='bjava'> 056 * <jc>// Create an Operation object for a GET endpoint</jc> 057 * Operation <jv>operation</jv> = <jk>new</jk> Operation() 058 * .setTags(<js>"pet"</js>) 059 * .setSummary(<js>"Find pets by status"</js>) 060 * .setDescription(<js>"Multiple status values can be provided with comma separated strings"</js>) 061 * .setOperationId(<js>"findPetsByStatus"</js>) 062 * .setParameters( 063 * <jk>new</jk> Parameter() 064 * .setName(<js>"status"</js>) 065 * .setIn(<js>"query"</js>) 066 * .setDescription(<js>"Status values that need to be considered for filter"</js>) 067 * .setRequired(<jk>true</jk>) 068 * .setSchema( 069 * <jk>new</jk> SchemaInfo().setType(<js>"string"</js>) 070 * ) 071 * ) 072 * .setResponses( 073 * JsonMap.<jsm>of</jsm>( 074 * <js>"200"</js>, <jk>new</jk> Response() 075 * .setDescription(<js>"successful operation"</js>) 076 * .setContent( 077 * JsonMap.<jsm>of</jsm>( 078 * <js>"application/json"</js>, <jk>new</jk> MediaType() 079 * .setSchema(<jk>new</jk> SchemaInfo().<jsm>setType</jsm>(<js>"array"</js>)) 080 * ) 081 * ) 082 * ) 083 * ); 084 * </p> 085 * 086 * <h5 class='section'>See Also:</h5><ul> 087 * <li class='link'><a class="doclink" href="https://spec.openapis.org/oas/v3.0.0#operation-object">OpenAPI Specification > Operation Object</a> 088 * <li class='link'><a class="doclink" href="https://swagger.io/docs/specification/paths-and-operations/">OpenAPI Paths and Operations</a> 089 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanOpenApi3">juneau-bean-openapi-v3</a> 090 * </ul> 091 */ 092public class Operation extends OpenApiElement { 093 094 private List<String> tags; 095 private String summary, description, operationId; 096 private ExternalDocumentation externalDocs; 097 private List<Parameter> parameters; 098 private RequestBodyInfo requestBody; 099 private Map<String,Response> responses; 100 private Map<String,Callback> callbacks; 101 private Boolean deprecated; 102 private List<SecurityRequirement> security; 103 private List<Server> servers; 104 105 /** 106 * Default constructor. 107 */ 108 public Operation() {} 109 110 /** 111 * Copy constructor. 112 * 113 * @param copyFrom The object to copy. 114 */ 115 public Operation(Operation copyFrom) { 116 super(copyFrom); 117 this.tags = copyOf(copyFrom.tags); 118 this.summary = copyFrom.summary; 119 this.description = copyFrom.description; 120 this.operationId = copyFrom.operationId; 121 this.externalDocs = copyFrom.externalDocs; 122 this.parameters = copyOf(copyFrom.parameters); 123 this.requestBody = copyFrom.requestBody; 124 this.responses = copyOf(copyFrom.responses); 125 this.callbacks = copyOf(copyFrom.callbacks); 126 this.deprecated = copyFrom.deprecated; 127 this.security = copyOf(copyFrom.security); 128 this.servers = copyOf(copyFrom.servers); 129 } 130 131 /** 132 * Returns the tags list. 133 * 134 * @return The tags list. 135 */ 136 public List<String> getTags() { 137 return tags; 138 } 139 140 /** 141 * Sets the tags list. 142 * 143 * @param value The new value for this property. 144 * @return This object. 145 */ 146 public Operation setTags(List<String> value) { 147 this.tags = value; 148 return this; 149 } 150 151 /** 152 * Sets the tags list. 153 * 154 * @param value The new value for this property. 155 * @return This object. 156 */ 157 public Operation setTags(String...value) { 158 setTags(listBuilder(String.class).sparse().add(value).build()); 159 return this; 160 } 161 162 /** 163 * Bean property appender: <property>tags</property>. 164 * 165 * <p> 166 * A list of tags for API documentation control. 167 * 168 * @param values 169 * The values to add to this property. 170 * <br>Ignored if <jk>null</jk>. 171 * @return This object. 172 */ 173 public Operation addTags(String...values) { 174 tags = listBuilder(tags).sparse().add(values).build(); 175 return this; 176 } 177 178 /** 179 * Bean property appender: <property>tags</property>. 180 * 181 * <p> 182 * A list of tags for API documentation control. 183 * 184 * @param values 185 * The values to add to this property. 186 * <br>Ignored if <jk>null</jk>. 187 * @return This object. 188 */ 189 public Operation addTags(Collection<String> values) { 190 tags = listBuilder(tags).sparse().addAll(values).build(); 191 return this; 192 } 193 194 /** 195 * Bean property fluent setter: <property>parameters</property>. 196 * 197 * <p> 198 * A list of parameters that are applicable for this operation. 199 * 200 * @param values 201 * The values to add to this property. 202 * <br>Ignored if <jk>null</jk>. 203 * @return This object. 204 */ 205 public Operation addParameters(Parameter...values) { 206 parameters = listBuilder(parameters).sparse().add(values).build(); 207 return this; 208 } 209 210 /** 211 * Bean property fluent setter: <property>parameters</property>. 212 * 213 * <p> 214 * A list of parameters that are applicable for this operation. 215 * 216 * @param values 217 * The values to add to this property. 218 * <br>Ignored if <jk>null</jk>. 219 * @return This object. 220 */ 221 public Operation addParameters(Collection<Parameter> values) { 222 parameters = listBuilder(parameters).sparse().addAll(values).build(); 223 return this; 224 } 225 226 /** 227 * Bean property fluent setter: <property>responses</property>. 228 * 229 * <p> 230 * The list of possible responses as they are returned from executing this operation. 231 * 232 * @param statusCode 233 * The status code for the response. 234 * <br>Must not be <jk>null</jk>. 235 * @param response 236 * The response object. 237 * <br>Must not be <jk>null</jk>. 238 * @return This object. 239 */ 240 public Operation addResponse(String statusCode, Response response) { 241 assertArgNotNull("statusCode", statusCode); 242 assertArgNotNull("response", response); 243 responses = mapBuilder(responses).sparse().add(statusCode, response).build(); 244 return this; 245 } 246 247 /** 248 * Bean property fluent setter: <property>callbacks</property>. 249 * 250 * <p> 251 * A map of possible out-of band callbacks related to the parent operation. 252 * 253 * @param name 254 * The name of the callback. 255 * <br>Must not be <jk>null</jk>. 256 * @param callback 257 * The callback object. 258 * <br>Must not be <jk>null</jk>. 259 * @return This object. 260 */ 261 public Operation addCallback(String name, Callback callback) { 262 assertArgNotNull("name", name); 263 assertArgNotNull("callback", callback); 264 callbacks = mapBuilder(callbacks).sparse().add(name, callback).build(); 265 return this; 266 } 267 268 /** 269 * Bean property fluent setter: <property>security</property>. 270 * 271 * <p> 272 * A declaration of which security mechanisms can be used for this operation. 273 * 274 * @param values 275 * The values to add to this property. 276 * <br>Ignored if <jk>null</jk>. 277 * @return This object. 278 */ 279 public Operation addSecurity(SecurityRequirement...values) { 280 security = listBuilder(security).sparse().add(values).build(); 281 return this; 282 } 283 284 /** 285 * Bean property fluent setter: <property>security</property>. 286 * 287 * <p> 288 * A declaration of which security mechanisms can be used for this operation. 289 * 290 * @param values 291 * The values to add to this property. 292 * <br>Ignored if <jk>null</jk>. 293 * @return This object. 294 */ 295 public Operation addSecurity(Collection<SecurityRequirement> values) { 296 security = listBuilder(security).sparse().addAll(values).build(); 297 return this; 298 } 299 300 /** 301 * Bean property fluent setter: <property>servers</property>. 302 * 303 * <p> 304 * An alternative server array to service this operation. 305 * 306 * @param values 307 * The values to add to this property. 308 * <br>Ignored if <jk>null</jk>. 309 * @return This object. 310 */ 311 public Operation addServers(Server...values) { 312 servers = listBuilder(servers).sparse().add(values).build(); 313 return this; 314 } 315 316 /** 317 * Bean property fluent setter: <property>servers</property>. 318 * 319 * <p> 320 * An alternative server array to service this operation. 321 * 322 * @param values 323 * The values to add to this property. 324 * <br>Ignored if <jk>null</jk>. 325 * @return This object. 326 */ 327 public Operation addServers(Collection<Server> values) { 328 servers = listBuilder(servers).sparse().addAll(values).build(); 329 return this; 330 } 331 332 /** 333 * Returns the summary. 334 * 335 * @return The summary. 336 */ 337 public String getSummary() { 338 return summary; 339 } 340 341 /** 342 * Sets the summary. 343 * 344 * @param value The new value for this property. 345 * @return This object. 346 */ 347 public Operation setSummary(String value) { 348 this.summary = value; 349 return this; 350 } 351 352 /** 353 * Returns the description. 354 * 355 * @return The description. 356 */ 357 public String getDescription() { 358 return description; 359 } 360 361 /** 362 * Sets the description. 363 * 364 * @param value The new value for this property. 365 * @return This object. 366 */ 367 public Operation setDescription(String value) { 368 this.description = value; 369 return this; 370 } 371 372 /** 373 * Returns the operation ID. 374 * 375 * @return The operation ID. 376 */ 377 public String getOperationId() { 378 return operationId; 379 } 380 381 /** 382 * Sets the operation ID. 383 * 384 * @param value The new value for this property. 385 * @return This object. 386 */ 387 public Operation setOperationId(String value) { 388 this.operationId = value; 389 return this; 390 } 391 392 /** 393 * Returns the external documentation. 394 * 395 * @return The external documentation. 396 */ 397 public ExternalDocumentation getExternalDocs() { 398 return externalDocs; 399 } 400 401 /** 402 * Sets the external documentation. 403 * 404 * @param value The new value for this property. 405 * @return This object. 406 */ 407 public Operation setExternalDocs(ExternalDocumentation value) { 408 this.externalDocs = value; 409 return this; 410 } 411 412 /** 413 * Returns the parameters list. 414 * 415 * @return The parameters list. 416 */ 417 public List<Parameter> getParameters() { 418 return parameters; 419 } 420 421 /** 422 * Returns the parameter with the specified type and name. 423 * 424 * @param in The parameter in. Must not be <jk>null</jk>. 425 * @param name The parameter name. Must not be <jk>null</jk>. 426 * @return The matching parameter, or <jk>null</jk> if not found. 427 */ 428 public Parameter getParameter(String in, String name) { 429 assertArgNotNull("in", in); 430 assertArgNotNull("name", name); 431 if (parameters != null) 432 for (var p : parameters) 433 if (eq(p.getIn(), in) && eq(p.getName(), name)) 434 return p; 435 return null; 436 } 437 438 /** 439 * Sets the parameters list. 440 * 441 * @param value The new value for this property. 442 * @return This object. 443 */ 444 public Operation setParameters(List<Parameter> value) { 445 this.parameters = value; 446 return this; 447 } 448 449 /** 450 * Sets the parameters list. 451 * 452 * @param value The new value for this property. 453 * @return This object. 454 */ 455 public Operation setParameters(Parameter...value) { 456 setParameters(listBuilder(Parameter.class).sparse().add(value).build()); 457 return this; 458 } 459 460 /** 461 * Returns the request body. 462 * 463 * @return The request body. 464 */ 465 public RequestBodyInfo getRequestBody() { 466 return requestBody; 467 } 468 469 /** 470 * Sets the request body. 471 * 472 * @param value The new value for this property. 473 * @return This object. 474 */ 475 public Operation setRequestBody(RequestBodyInfo value) { 476 this.requestBody = value; 477 return this; 478 } 479 480 /** 481 * Returns the responses map. 482 * 483 * @return The responses map. 484 */ 485 public Map<String,Response> getResponses() { 486 return responses; 487 } 488 489 /** 490 * Returns the response with the given status code. 491 * 492 * @param status The HTTP status code. Must not be <jk>null</jk>. 493 * @return The response, or <jk>null</jk> if not found. 494 */ 495 public Response getResponse(String status) { 496 assertArgNotNull("status", status); 497 return responses == null ? null : responses.get(status); 498 } 499 500 /** 501 * Returns the response with the given status code. 502 * 503 * @param status The HTTP status code. 504 * @return The response, or <jk>null</jk> if not found. 505 */ 506 public Response getResponse(int status) { 507 return getResponse(String.valueOf(status)); 508 } 509 510 /** 511 * Sets the responses map. 512 * 513 * @param value The new value for this property. 514 * @return This object. 515 */ 516 public Operation setResponses(Map<String,Response> value) { 517 this.responses = value; 518 return this; 519 } 520 521 /** 522 * Returns the callbacks map. 523 * 524 * @return The callbacks map. 525 */ 526 public Map<String,Callback> getCallbacks() { 527 return callbacks; 528 } 529 530 /** 531 * Sets the callbacks map. 532 * 533 * @param value The new value for this property. 534 * @return This object. 535 */ 536 public Operation setCallbacks(Map<String,Callback> value) { 537 this.callbacks = value; 538 return this; 539 } 540 541 /** 542 * Returns the deprecated flag. 543 * 544 * @return The deprecated flag. 545 */ 546 public Boolean getDeprecated() { 547 return deprecated; 548 } 549 550 /** 551 * Sets the deprecated flag. 552 * 553 * @param value The new value for this property. 554 * @return This object. 555 */ 556 public Operation setDeprecated(Boolean value) { 557 this.deprecated = value; 558 return this; 559 } 560 561 /** 562 * Returns the security requirements list. 563 * 564 * @return The security requirements list. 565 */ 566 public List<SecurityRequirement> getSecurity() { 567 return security; 568 } 569 570 /** 571 * Sets the security requirements list. 572 * 573 * @param value The new value for this property. 574 * @return This object. 575 */ 576 public Operation setSecurity(List<SecurityRequirement> value) { 577 this.security = value; 578 return this; 579 } 580 581 /** 582 * Sets the security requirements list. 583 * 584 * @param value The new value for this property. 585 * @return This object. 586 */ 587 public Operation setSecurity(SecurityRequirement...value) { 588 setSecurity(listBuilder(SecurityRequirement.class).sparse().add(value).build()); 589 return this; 590 } 591 592 /** 593 * Returns the servers list. 594 * 595 * @return The servers list. 596 */ 597 public List<Server> getServers() { 598 return servers; 599 } 600 601 /** 602 * Sets the servers list. 603 * 604 * @param value The new value for this property. 605 * @return This object. 606 */ 607 public Operation setServers(List<Server> value) { 608 this.servers = value; 609 return this; 610 } 611 612 /** 613 * Sets the servers list. 614 * 615 * @param value The new value for this property. 616 * @return This object. 617 */ 618 public Operation setServers(Server...value) { 619 setServers(listBuilder(Server.class).sparse().add(value).build()); 620 return this; 621 } 622 623 /** 624 * Creates a copy of this object. 625 * 626 * @return A copy of this object. 627 */ 628 public Operation copy() { 629 return new Operation(this); 630 } 631 632 @Override /* Overridden from OpenApiElement */ 633 public <T> T get(String property, Class<T> type) { 634 assertArgNotNull("property", property); 635 return switch (property) { 636 case "tags" -> toType(getTags(), type); 637 case "summary" -> toType(getSummary(), type); 638 case "description" -> toType(getDescription(), type); 639 case "operationId" -> toType(getOperationId(), type); 640 case "externalDocs" -> toType(getExternalDocs(), type); 641 case "parameters" -> toType(getParameters(), type); 642 case "requestBody" -> toType(getRequestBody(), type); 643 case "responses" -> toType(getResponses(), type); 644 case "callbacks" -> toType(getCallbacks(), type); 645 case "deprecated" -> toType(getDeprecated(), type); 646 case "security" -> toType(getSecurity(), type); 647 case "servers" -> toType(getServers(), type); 648 default -> super.get(property, type); 649 }; 650 } 651 652 @Override /* Overridden from OpenApiElement */ 653 public Operation set(String property, Object value) { 654 assertArgNotNull("property", property); 655 return switch (property) { 656 case "callbacks" -> setCallbacks(mapBuilder(String.class, Callback.class).sparse().addAny(value).build()); 657 case "deprecated" -> setDeprecated(toType(value, Boolean.class)); 658 case "description" -> setDescription(Utils.s(value)); 659 case "externalDocs" -> setExternalDocs(toType(value, ExternalDocumentation.class)); 660 case "operationId" -> setOperationId(Utils.s(value)); 661 case "parameters" -> setParameters(listBuilder(Parameter.class).sparse().addAny(value).build()); 662 case "requestBody" -> setRequestBody(toType(value, RequestBodyInfo.class)); 663 case "responses" -> setResponses(mapBuilder(String.class, Response.class).sparse().addAny(value).build()); 664 case "security" -> setSecurity(listBuilder(SecurityRequirement.class).sparse().addAny(value).build()); 665 case "servers" -> setServers(listBuilder(Server.class).sparse().addAny(value).build()); 666 case "summary" -> setSummary(Utils.s(value)); 667 case "tags" -> setTags(listBuilder(String.class).sparse().addAny(value).build()); 668 default -> { 669 super.set(property, value); 670 yield this; 671 } 672 }; 673 } 674 675 @Override /* Overridden from OpenApiElement */ 676 public Set<String> keySet() { 677 var s = setBuilder(String.class) 678 .addIf(callbacks != null, "callbacks") 679 .addIf(deprecated != null, "deprecated") 680 .addIf(description != null, "description") 681 .addIf(externalDocs != null, "externalDocs") 682 .addIf(operationId != null, "operationId") 683 .addIf(parameters != null, "parameters") 684 .addIf(requestBody != null, "requestBody") 685 .addIf(responses != null, "responses") 686 .addIf(security != null, "security") 687 .addIf(servers != null, "servers") 688 .addIf(summary != null, "summary") 689 .addIf(tags != null, "tags") 690 .build(); 691 return new MultiSet<>(s, super.keySet()); 692 } 693 694 @Override /* Overridden from OpenApiElement */ 695 public Operation strict() { 696 super.strict(); 697 return this; 698 } 699 700 @Override /* Overridden from OpenApiElement */ 701 public Operation strict(Object value) { 702 super.strict(value); 703 return this; 704 } 705 706}