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