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.CollectionUtils.*;
021import static org.apache.juneau.internal.CollectionUtils.map;
022import static org.apache.juneau.internal.ConverterUtils.*;
023
024import java.util.*;
025
026import org.apache.juneau.*;
027import org.apache.juneau.common.utils.*;
028import org.apache.juneau.internal.*;
029
030/**
031 * Describes a single API operation on a path.
032 *
033 * <p>
034 * The Operation Object describes a single operation (such as GET, POST, PUT, DELETE) that can be performed on a path
035 * in Swagger 2.0. Operations define what actions can be taken, what parameters they accept, what they consume/produce,
036 * and what responses they return.
037 *
038 * <h5 class='section'>Swagger Specification:</h5>
039 * <p>
040 * The Operation Object is composed of the following fields:
041 * <ul class='spaced-list'>
042 *    <li><c>tags</c> (array of string) - A list of tags for API documentation control
043 *    <li><c>summary</c> (string) - A short summary of what the operation does
044 *    <li><c>description</c> (string) - A verbose explanation of the operation behavior
045 *    <li><c>externalDocs</c> ({@link ExternalDocumentation}) - Additional external documentation for this operation
046 *    <li><c>operationId</c> (string) - Unique string used to identify the operation
047 *    <li><c>consumes</c> (array of string) - A list of MIME types the operation can consume
048 *    <li><c>produces</c> (array of string) - A list of MIME types the operation can produce
049 *    <li><c>parameters</c> (array of {@link ParameterInfo}) - A list of parameters that are applicable for this operation
050 *    <li><c>responses</c> (map of {@link ResponseInfo}, REQUIRED) - The list of possible responses as they are returned from executing this operation
051 *    <li><c>schemes</c> (array of string) - The transfer protocol for the operation (overrides the Swagger-level schemes)
052 *    <li><c>deprecated</c> (boolean) - Declares this operation to be deprecated
053 *    <li><c>security</c> (array of map) - A declaration of which security schemes are applied for this operation
054 * </ul>
055 *
056 * <h5 class='section'>Example:</h5>
057 * <p class='bjava'>
058 *    <jc>// Construct using SwaggerBuilder.</jc>
059 *    Operation <jv>operation</jv> = <jsm>operation</jsm>()
060 *       .tags(<js>"pet"</js>)
061 *       .summary(<js>"Updates a pet in the store with form data"</js>)
062 *       .description(<js>""</js>)
063 *       .operationId(<js>"updatePetWithForm"</js>)
064 *       .consumes(<js>"application/x-www-form-urlencoded"</js>)
065 *       .produces(<js>"application/json"</js>, <js>"application/xml"</js>)
066 *       .parameters(
067 *          <jsm>parameter</jsm>()
068 *             .name(<js>"petId"</js>)
069 *             .in(<js>"path"</js>)
070 *             .description(<js>"ID of pet that needs to be updated"</js>)
071 *             .required(<jk>true</jk>)
072 *             .type(<js>"string"</js>),
073 *          <jsm>parameter</jsm>()
074 *             .name(<js>"name"</js>)
075 *             .in(<js>"formData"</js>)
076 *             .description(<js>"Updated name of the pet"</js>)
077 *             .required(<jk>false</jk>)
078 *             .type(<js>"string"</js>),
079 *          <jsm>parameter</jsm>()
080 *             .name(<js>"status"</js>)
081 *             .in(<js>"formData"</js>)
082 *             .description(<js>"Updated status of the pet"</js>)
083 *             .required(<jk>false</jk>)
084 *             .type(<js>"string"</js>)
085 *       )
086 *       .response(200, <jsm>responseInfo</jsm>(<js>"Pet updated."</js>))
087 *       .response(405, <jsm>responseInfo</jsm>(<js>"Invalid input."</js>))
088 *       .security(<js>"petstore_auth"</js>, <js>"write:pets"</js>, <js>"read:pets"</js>);
089 *
090 *    <jc>// Serialize using JsonSerializer.</jc>
091 *    String <jv>json</jv> = Json.<jsm>from</jsm>(<jv>operation</jv>);
092 *
093 *    <jc>// Or just use toString() which does the same as above.</jc>
094 *    <jv>json</jv> = <jv>operation</jv>.toString();
095 * </p>
096 * <p class='bjson'>
097 *    <jc>// Output</jc>
098 *    {
099 *       <js>"tags"</js>: [
100 *          <js>"pet"</js>
101 *       ],
102 *       <js>"summary"</js>: <js>"Updates a pet in the store with form data"</js>,
103 *       <js>"description"</js>: <js>""</js>,
104 *       <js>"operationId"</js>: <js>"updatePetWithForm"</js>,
105 *       <js>"consumes"</js>: [
106 *          <js>"application/x-www-form-urlencoded"</js>
107 *       ],
108 *       <js>"produces"</js>: [
109 *          <js>"application/json"</js>,
110 *          <js>"application/xml"</js>
111 *       ],
112 *       <js>"parameters"</js>: [
113 *          {
114 *             <js>"name"</js>: <js>"petId"</js>,
115 *             <js>"in"</js>: <js>"path"</js>,
116 *             <js>"description"</js>: <js>"ID of pet that needs to be updated"</js>,
117 *             <js>"required"</js>: <jk>true</jk>,
118 *             <js>"type"</js>: <js>"string"</js>
119 *          },
120 *          {
121 *             <js>"name"</js>: <js>"name"</js>,
122 *             <js>"in"</js>: <js>"formData"</js>,
123 *             <js>"description"</js>: <js>"Updated name of the pet"</js>,
124 *             <js>"required"</js>: <jk>false</jk>,
125 *             <js>"type"</js>: <js>"string"</js>
126 *          },
127 *          {
128 *             <js>"name"</js>: <js>"status"</js>,
129 *             <js>"in"</js>: <js>"formData"</js>,
130 *             <js>"description"</js>: <js>"Updated status of the pet"</js>,
131 *             <js>"required"</js>: <jk>false</jk>,
132 *             <js>"type"</js>: <js>"string"</js>
133 *          }
134 *       ],
135 *       <js>"responses"</js>: {
136 *          <js>"200"</js>: {
137 *             <js>"description"</js>: <js>"Pet updated."</js>
138 *          },
139 *          <js>"405"</js>: {
140 *             <js>"description"</js>: <js>"Invalid input"</js>
141 *          }
142 *       },
143 *       <js>"security"</js>: [
144 *          {
145 *             <js>"petstore_auth"</js>: [
146 *                <js>"write:pets"</js>,
147 *                <js>"read:pets"</js>
148 *             ]
149 *          }
150 *       ]
151 *    }
152 * </p>
153 *
154 * <h5 class='section'>See Also:</h5><ul>
155 *    <li class='link'><a class="doclink" href="https://swagger.io/specification/v2/#operation-object">Swagger 2.0 Specification &gt; Operation Object</a>
156 *    <li class='link'><a class="doclink" href="https://swagger.io/docs/specification/2-0/paths-and-operations/">Swagger Paths and Operations</a>
157 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanSwagger2">juneau-bean-swagger-v2</a>
158 * </ul>
159 */
160public class Operation extends SwaggerElement {
161
162   private String
163      summary,
164      description,
165      operationId;
166   private Boolean deprecated;
167   private ExternalDocumentation externalDocs;
168   private Set<String>
169      tags,
170      schemes;
171   private Set<MediaType>
172      consumes,
173      produces;
174   private List<ParameterInfo> parameters;
175   private List<Map<String,List<String>>> security;
176   private Map<String,ResponseInfo> responses;
177
178   /**
179    * Default constructor.
180    */
181   public Operation() {}
182
183   /**
184    * Copy constructor.
185    *
186    * @param copyFrom The object to copy.
187    */
188   public Operation(Operation copyFrom) {
189      super(copyFrom);
190
191      this.consumes = copyOf(copyFrom.consumes);
192      this.deprecated = copyFrom.deprecated;
193      this.description = copyFrom.description;
194      this.externalDocs = copyFrom.externalDocs == null ? null : copyFrom.externalDocs.copy();
195      this.operationId = copyFrom.operationId;
196      this.produces = copyOf(copyFrom.produces);
197      this.schemes = copyOf(copyFrom.schemes);
198      this.summary = copyFrom.summary;
199      this.tags = copyOf(copyFrom.tags);
200
201      if (copyFrom.parameters == null) {
202         this.parameters = null;
203      } else {
204         this.parameters = list();
205         copyFrom.parameters.forEach(x -> this.parameters.add(x.copy()));
206      }
207
208      if (copyFrom.responses == null) {
209         this.responses = null;
210      } else {
211         this.responses = map();
212         copyFrom.responses.forEach((k,v) -> this.responses.put(k, v.copy()));
213      }
214
215      if (copyFrom.security == null) {
216         this.security = null;
217      } else {
218         this.security = list();
219         copyFrom.security.forEach(x -> {
220            Map<String,List<String>> m2 = map();
221            x.forEach((k,v) -> m2.put(k, copyOf(v)));
222            this.security.add(m2);
223         });
224      }
225   }
226
227   /**
228    * Make a deep copy of this object.
229    *
230    * @return A deep copy of this object.
231    */
232   public Operation copy() {
233      return new Operation(this);
234   }
235
236   //-----------------------------------------------------------------------------------------------------------------
237   // Properties
238   //-----------------------------------------------------------------------------------------------------------------
239
240   /**
241    * Bean property getter:  <property>consumes</property>.
242    *
243    * <p>
244    * A list of MIME types the operation can consume.
245    *
246    * @return The property value, or <jk>null</jk> if it is not set.
247    */
248   public Set<MediaType> getConsumes() {
249      return consumes;
250   }
251
252   /**
253    * Bean property setter:  <property>consumes</property>.
254    *
255    * <p>
256    * A list of MIME types the operation can consume.
257    *
258    * @param value
259    *    The new value for this property.
260    *    <br>Values MUST be as described under <a class="doclink" href="https://swagger.io/specification#mimeTypes">Swagger Mime Types</a>.
261    *    <br>Can be <jk>null</jk> to unset the property.
262    * @return This object.
263    */
264   public Operation setConsumes(Collection<MediaType> value) {
265      consumes = setFrom(value);
266      return this;
267   }
268
269   /**
270    * Bean property setter:  <property>consumes</property>.
271    *
272    * <p>
273    * A list of MIME types the operation can consume.
274    *
275    * @param value
276    *    The new value for this property.
277    *    <br>Values MUST be as described under <a class="doclink" href="https://swagger.io/specification#mimeTypes">Swagger Mime Types</a>.
278    *    <br>Can be <jk>null</jk> to unset the property.
279    * @return This object.
280    */
281   public Operation setConsumes(MediaType...value) {
282      return setConsumes(Arrays.asList(value));
283   }
284
285   /**
286    * Bean property fluent setter:  <property>consumes</property>.
287    *
288    * <p>
289    * A list of MIME types the operation can consume.
290    *
291    * @param value
292    *    The new value for this property.
293    * @return This object.
294    */
295   public Operation addConsumes(MediaType...value) {
296      consumes = setBuilder(consumes).sparse().add(value).build();
297      return this;
298   }
299
300   /**
301    * Bean property appender:  <property>consumes</property>.
302    *
303    * <p>
304    * A list of MIME types the operation can consume.
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 addConsumes(Collection<MediaType> values) {
312      consumes = setBuilder(consumes).sparse().addAll(values).build();
313      return this;
314   }
315
316   /**
317    * Bean property getter:  <property>deprecated</property>.
318    *
319    * <p>
320    * Declares this operation to be deprecated.
321    *
322    * @return The property value, or <jk>null</jk> if it is not set.
323    */
324   public Boolean getDeprecated() {
325      return deprecated;
326   }
327
328   /**
329    * Bean property getter:  <property>deprecated</property>.
330    *
331    * <p>
332    * Declares this operation to be deprecated.
333    *
334    * @return The property value, or <jk>false</jk> if it is not set.
335    */
336   public boolean isDeprecated() {
337      return deprecated != null && deprecated;
338   }
339
340   /**
341    * Bean property setter:  <property>deprecated</property>.
342    *
343    * <p>
344    * Declares this operation to be deprecated.
345    *
346    * @param value The new value for this property.
347    *    <br>Can be <jk>null</jk> to unset the property.
348    * @return This object.
349    */
350   public Operation setDeprecated(Boolean value) {
351      deprecated = value;
352      return this;
353   }
354
355   /**
356    * Bean property getter:  <property>description</property>.
357    *
358    * <p>
359    * A verbose explanation of the operation behavior.
360    *
361    * @return The property value, or <jk>null</jk> if it is not set.
362    */
363   public String getDescription() {
364      return description;
365   }
366
367   /**
368    * Bean property setter:  <property>description</property>.
369    *
370    * <p>
371    * A verbose explanation of the operation behavior.
372    *
373    * @param value
374    *    The new value for this property.
375    *    <br><a class="doclink" href="https://help.github.com/articles/github-flavored-markdown">GFM syntax</a> can be used for rich text representation.
376    *    <br>Can be <jk>null</jk> to unset the property.
377    * @return This object.
378    */
379   public Operation setDescription(String value) {
380      description = value;
381      return this;
382   }
383
384   /**
385    * Bean property getter:  <property>externalDocs</property>.
386    *
387    * <p>
388    * Additional external documentation for this operation.
389    *
390    * @return The property value, or <jk>null</jk> if it is not set.
391    */
392   public ExternalDocumentation getExternalDocs() {
393      return externalDocs;
394   }
395
396   /**
397    * Bean property setter:  <property>externalDocs</property>.
398    *
399    * <p>
400    * Additional external documentation for this operation.
401    *
402    * @param value
403    *    The values to add to this property.
404    *    <br>Can be <jk>null</jk> to unset the property.
405    * @return This object.
406    */
407   public Operation setExternalDocs(ExternalDocumentation value) {
408      externalDocs = value;
409      return this;
410   }
411
412   /**
413    * Bean property getter:  <property>operationId</property>.
414    *
415    * <p>
416    * Unique string used to identify the operation.
417    *
418    * @return The property value, or <jk>null</jk> if it is not set.
419    */
420   public String getOperationId() {
421      return operationId;
422   }
423
424   /**
425    * Bean property setter:  <property>operationId</property>.
426    *
427    * <p>
428    * Unique string used to identify the operation.
429    *
430    * @param value
431    *    The new value for this property.
432    *    <br>The id MUST be unique among all operations described in the API.
433    *    <br>Tools and libraries MAY use the operationId to uniquely identify an operation, therefore, it is recommended to
434    *    follow common programming naming conventions.
435    *    <br>Can be <jk>null</jk> to unset the property.
436    * @return This object.
437    */
438   public Operation setOperationId(String value) {
439      operationId = value;
440      return this;
441   }
442
443   /**
444    * Bean property getter:  <property>parameters</property>.
445    *
446    * <p>
447    * A list of parameters that are applicable for this operation.
448    *
449    * <h5 class='section'>Notes:</h5><ul>
450    *    <li class='note'>
451    *       If a parameter is already defined at the <a class="doclink" href="https://swagger.io/specification#pathItemObject">Path Item</a>,
452    *       the new definition will override it, but can never remove it.
453    *    <li class='note'>
454    *       The list MUST NOT include duplicated parameters.
455    *    <li class='note'>
456    *       A unique parameter is defined by a combination of a <c>name</c> and <c>location</c>.
457    *    <li class='note'>
458    *       The list can use the <a class="doclink" href="https://swagger.io/specification#referenceObject">Swagger Reference Object</a>
459    *       to link to parameters that are defined at the <a class='doclink' href='https://swagger.io/specification/v2#parameterObject'>Swagger Object's parameters</a>.
460    *    <li class='note'>
461    *       There can be one <js>"body"</js> parameter at most.
462    * </ul>
463    *
464    * @return The property value, or <jk>null</jk> if it is not set.
465    */
466   public List<ParameterInfo> getParameters() {
467      return parameters;
468   }
469
470   /**
471    * Returns the parameter with the specified type and name.
472    *
473    * @param in The parameter in.  Must not be <jk>null</jk>.
474    * @param name The parameter name.  Can be <jk>null</jk> for parameter type <c>body</c>.
475    * @return The matching parameter info, or <jk>null</jk> if not found.
476    */
477   public ParameterInfo getParameter(String in, String name) {
478      assertArgNotNull("in", in);
479      // Note: name can be null for "body" parameters
480      if (parameters != null)
481         for (var pi : parameters)
482            if (eq(pi.getIn(), in) && (eq(pi.getName(), name) || "body".equals(pi.getIn())))
483               return pi;
484      return null;
485   }
486
487   /**
488    * Bean property setter:  <property>parameters</property>.
489    *
490    * <p>
491    * A list of parameters that are applicable for this operation.
492    *
493    * @param value
494    *    The new value for this property.
495    *    <br>Can be <jk>null</jk> to unset the property.
496    * @return This object.
497    */
498   public Operation setParameters(Collection<ParameterInfo> value) {
499      parameters = listFrom(value);
500      return this;
501   }
502
503   /**
504    * Bean property setter:  <property>parameters</property>.
505    *
506    * <p>
507    * A list of parameters that are applicable for this operation.
508    *
509    * @param value
510    *    The new value for this property.
511    *    <br>Can be <jk>null</jk> to unset the property.
512    * @return This object.
513    */
514   public Operation setParameters(ParameterInfo...value) {
515      return setParameters(Arrays.asList(value));
516   }
517
518   /**
519    * Bean property fluent setter:  <property>parameters</property>.
520    *
521    * <p>
522    * A list of parameters that are applicable for this operation.
523    *
524    * @param value
525    *    The new value for this property.
526    * @return This object.
527    */
528   public Operation addParameters(ParameterInfo...value) {
529      parameters = listBuilder(parameters).sparse().add(value).build();
530      return this;
531   }
532
533   /**
534    * Bean property fluent setter:  <property>parameters</property>.
535    *
536    * <p>
537    * The parameters needed to send a valid API call.
538    *
539    * @param values
540    *    The values to add to this property.
541    *    <br>Ignored if <jk>null</jk>.
542    * @return This object.
543    */
544   public Operation addParameters(Collection<ParameterInfo> values) {
545      parameters = listBuilder(parameters).sparse().addAll(values).build();
546      return this;
547   }
548
549   //-----------------------------------------------------------------------------------------------------------------
550   // produces
551   //-----------------------------------------------------------------------------------------------------------------
552
553   /**
554    * Bean property getter:  <property>produces</property>.
555    *
556    * <p>
557    * A list of MIME types the operation can produce.
558    *
559    * @return The property value, or <jk>null</jk> if it is not set.
560    */
561   public Set<MediaType> getProduces() {
562      return produces;
563   }
564
565   /**
566    * Bean property setter:  <property>produces</property>.
567    *
568    * <p>
569    * A list of MIME types the operation can produce.
570    *
571    * @param value
572    *    The new value for this property.
573    *    <br>Value MUST be as described under <a class="doclink" href="https://swagger.io/specification#mimeTypes">Swagger Mime Types</a>.
574    *    <br>Can be <jk>null</jk> to unset the property.
575    * @return This object.
576    */
577   public Operation setProduces(Collection<MediaType> value) {
578      produces = setFrom(value);
579      return this;
580   }
581
582   /**
583    * Bean property setter:  <property>produces</property>.
584    *
585    * <p>
586    * A list of MIME types the operation can produce.
587    *
588    * @param value
589    *    The new value for this property.
590    *    <br>Value MUST be as described under <a class="doclink" href="https://swagger.io/specification#mimeTypes">Swagger Mime Types</a>.
591    *    <br>Can be <jk>null</jk> to unset the property.
592    * @return This object.
593    */
594   public Operation setProduces(MediaType...value) {
595      return setProduces(Arrays.asList(value));
596   }
597
598   /**
599    * Bean property fluent setter:  <property>produces</property>.
600    *
601    * <p>
602    * A list of MIME types the operation can produce.
603    *
604    * @param value
605    *    The new value for this property.
606    * @return This object.
607    */
608   public Operation addProduces(MediaType...value) {
609      produces = setBuilder(produces).sparse().add(value).build();
610      return this;
611   }
612
613   /**
614    * Bean property setter:  <property>produces</property>.
615    *
616    * <p>
617    * A list of MIME types the operation can produce.
618    *
619    * @param values
620    *    The values to add to this property.
621    *    <br>Ignored if <jk>null</jk>.
622    * @return This object.
623    */
624   public Operation addProduces(Collection<MediaType> values) {
625      produces = setBuilder(produces).sparse().addAll(values).build();
626      return this;
627   }
628
629   /**
630    * Bean property getter:  <property>responses</property>.
631    *
632    * <p>
633    * The list of possible responses as they are returned from executing this operation.
634    *
635    * @return The property value, or <jk>null</jk> if it is not set.
636    */
637   public Map<String,ResponseInfo> getResponses() {
638      return responses;
639   }
640
641   /**
642    * Returns the response info with the given status code.
643    *
644    * @param status The HTTP status code.  Must not be <jk>null</jk>.
645    * @return The response info, or <jk>null</jk> if not found.
646    */
647   public ResponseInfo getResponse(String status) {
648      assertArgNotNull("status", status);
649      return responses == null ? null : responses.get(status);
650   }
651
652   /**
653    * Returns the response info with the given status code.
654    *
655    * @param status The HTTP status code.
656    * @return The response info, or <jk>null</jk> if not found.
657    */
658   public ResponseInfo getResponse(int status) {
659      return getResponse(String.valueOf(status));
660   }
661
662   /**
663    * Bean property setter:  <property>responses</property>.
664    *
665    * <p>
666    * The list of possible responses as they are returned from executing this operation.
667    *
668    * @param value
669    *    The new value for this property.
670    *    <br>Property value is required.
671    *    <br>Can be <jk>null</jk> to unset the property.
672    * @return This object.
673    */
674   public Operation setResponses(Map<String,ResponseInfo> value) {
675      responses = copyOf(value);
676      return this;
677   }
678
679   /**
680    * Adds a single value to the <property>responses</property> property.
681    *
682    * @param statusCode The HTTP status code.  Must not be <jk>null</jk>.
683    * @param response The response description.  Must not be <jk>null</jk>.
684    * @return This object.
685    */
686   public Operation addResponse(String statusCode, ResponseInfo response) {
687      assertArgNotNull("statusCode", statusCode);
688      assertArgNotNull("response", response);
689      responses = mapBuilder(responses).add(statusCode, response).build();
690      return this;
691   }
692
693   /**
694    * Bean property getter:  <property>schemes</property>.
695    *
696    * <p>
697    * The transfer protocol for the operation.
698    *
699    * @return The property value, or <jk>null</jk> if it is not set.
700    */
701   public Set<String> getSchemes() {
702      return schemes;
703   }
704
705   /**
706    * Bean property setter:  <property>schemes</property>.
707    *
708    * <p>
709    * The transfer protocol for the operation.
710    *
711    * @param value
712    *    The new value for this property.
713    *    <br>Valid values:
714    *    <ul>
715    *       <li><js>"http"</js>
716    *       <li><js>"https"</js>
717    *       <li><js>"ws"</js>
718    *       <li><js>"wss"</js>
719    *    </ul>
720    *    <br>Can be <jk>null</jk> to unset the property.
721    * @return This object.
722    */
723   public Operation setSchemes(Collection<String> value) {
724      schemes = setFrom(value);
725      return this;
726   }
727
728   /**
729    * Bean property setter:  <property>schemes</property>.
730    *
731    * <p>
732    * The transfer protocol for the operation.
733    *
734    * @param value
735    *    The new value for this property.
736    *    <br>Can be <jk>null</jk> to unset the property.
737    * @return This object.
738    */
739   public Operation setSchemes(String...value) {
740      setSchemes(setBuilder(String.class).sparse().add(value).build());
741      return this;
742   }
743
744   /**
745    * Bean property fluent setter:  <property>schemes</property>.
746    *
747    * <p>
748    * The transfer protocol for the operation.
749    *
750    * @param value
751    *    The new value for this property.
752    *    <br>String values can also be JSON arrays.
753    * @return This object.
754    */
755   public Operation addSchemes(String...value) {
756      schemes = setBuilder(schemes).sparse().add(value).build();
757      return this;
758   }
759
760   /**
761    * Bean property setter:  <property>schemes</property>.
762    *
763    * <p>
764    * The transfer protocol for the operation.
765    *
766    * @param values
767    *    The values to add to this property.
768    *    <br>Ignored if <jk>null</jk>.
769    * @return This object.
770    */
771   public Operation addSchemes(Collection<String> values) {
772      schemes = setBuilder(schemes).sparse().addAll(values).build();
773      return this;
774   }
775
776   /**
777    * Bean property getter:  <property>security</property>.
778    *
779    * <p>
780    * A declaration of which security schemes are applied for this operation.
781    *
782    * @return The property value, or <jk>null</jk> if it is not set.
783    */
784   public List<Map<String,List<String>>> getSecurity() {
785      return security;
786   }
787
788   /**
789    * Bean property setter:  <property>security</property>.
790    *
791    * <p>
792    * A declaration of which security schemes are applied for this operation.
793    *
794    * @param value
795    *    The new value for this property.
796    *    <br>Can be <jk>null</jk> to unset the property.
797    * @return This object.
798    */
799   public Operation setSecurity(Collection<Map<String,List<String>>> value) {
800      security = listFrom(value);
801      return this;
802   }
803
804   /**
805    * Bean property setter:  <property>security</property>.
806    *
807    * <p>
808    * A declaration of which security schemes are applied for this operation.
809    *
810    * @param value
811    *    The new value for this property.
812    *    <br>Can be <jk>null</jk> to unset the property.
813    * @return This object.
814    */
815   @SafeVarargs
816   public final Operation setSecurity(Map<String,List<String>>...value) {
817      security = alist(value);
818      return this;
819   }
820
821   /**
822    * Same as {@link #addSecurity(String, String...)}.
823    *
824    * @param scheme
825    *    The scheme name.
826    *    <br>Must not be <jk>null</jk>.
827    * @param alternatives
828    *    The list of values describes alternative security schemes that can be used (that is, there is a logical OR
829    *    between the security requirements).
830    * @return This object.
831    */
832   public Operation addSecurity(String scheme, String...alternatives) {
833      assertArgNotNull("scheme", scheme);
834      Map<String,List<String>> m = map();
835      m.put(scheme, alist(alternatives));
836      security = listBuilder(security).add(m).build();
837      return this;
838   }
839
840   /**
841    * Bean property adder:  <property>security</property>.
842    *
843    * <p>
844    * A declaration of which security schemes are applied for this operation.
845    *
846    * @param value
847    *    The values to add to this property.
848    *    <br>Must not be <jk>null</jk>.
849    * @return This object.
850    */
851   @SuppressWarnings("unchecked")
852   public Operation addSecurity(Collection<Map<String,List<String>>> value) {
853      assertArgNotNull("value", value);
854      security = listBuilder(security).addAll(value).build();
855      return this;
856   }
857
858   /**
859    * Bean property getter:  <property>summary</property>.
860    *
861    * <p>
862    * A short summary of what the operation does.
863    *
864    * @return The property value, or <jk>null</jk> if it is not set.
865    */
866   public String getSummary() {
867      return summary;
868   }
869
870   /**
871    * Bean property setter:  <property>summary</property>.
872    *
873    * <p>
874    * A short summary of what the operation does.
875    *
876    * @param value
877    *    The new value for this property.
878    *    <br>Can be <jk>null</jk> to unset the property.
879    * @return This object.
880    */
881   public Operation setSummary(String value) {
882      summary = value;
883      return this;
884   }
885
886   /**
887    * Bean property getter:  <property>tags</property>.
888    *
889    * <p>
890    * A list of tags for API documentation control.
891    * <br>Tags can be used for logical grouping of operations by resources or any other qualifier.
892    *
893    * @return The property value, or <jk>null</jk> if it is not set.
894    */
895   public Set<String> getTags() {
896      return tags;
897   }
898
899   /**
900    * Bean property setter:  <property>tags</property>.
901    *
902    * <p>
903    * A list of tags for API documentation control.
904    * <br>Tags can be used for logical grouping of operations by resources or any other qualifier.
905    *
906    * @param value
907    *    The new value for this property.
908    *    <br>Can be <jk>null</jk> to unset the property.
909    * @return This object.
910    */
911   public Operation setTags(Collection<String> value) {
912      tags = setFrom(value);
913      return this;
914   }
915
916   /**
917    * Bean property fluent setter:  <property>tags</property>.
918    *
919    * <p>
920    * A list of tags for API documentation control.
921    * <br>Tags can be used for logical grouping of operations by resources or any other qualifier.
922    *
923    * @param value
924    *    The new value for this property.
925    * @return This object.
926    */
927   public Operation setTags(String...value) {
928      setTags(setBuilder(String.class).sparse().add(value).build());
929      return this;
930   }
931
932   /**
933    * Bean property fluent adder:  <property>tags</property>.
934    *
935    * <p>
936    * A list of tags for API documentation control.
937    * <br>Tags can be used for logical grouping of operations by resources or any other qualifier.
938    *
939    * @param value
940    *    The values to add to this property.
941    * @return This object.
942    */
943   public Operation addTags(String...value) {
944      tags = setBuilder(tags).sparse().add(value).build();
945      return this;
946   }
947
948   /**
949    * Bean property appender:  <property>tags</property>.
950    *
951    * <p>
952    * A list of tags for API documentation control.
953    *
954    * @param values
955    *    The values to add to this property.
956    *    <br>Ignored if <jk>null</jk>.
957    * @return This object.
958    */
959   public Operation addTags(Collection<String> values) {
960      tags = setBuilder(tags).sparse().addAll(values).build();
961      return this;
962   }
963
964   @Override /* Overridden from SwaggerElement */
965   public <T> T get(String property, Class<T> type) {
966      assertArgNotNull("property", property);
967      return switch (property) {
968         case "consumes" -> toType(getConsumes(), type);
969         case "deprecated" -> toType(getDeprecated(), type);
970         case "description" -> toType(getDescription(), type);
971         case "externalDocs" -> toType(getExternalDocs(), type);
972         case "operationId" -> toType(getOperationId(), type);
973         case "parameters" -> toType(getParameters(), type);
974         case "produces" -> toType(getProduces(), type);
975         case "responses" -> toType(getResponses(), type);
976         case "schemes" -> toType(getSchemes(), type);
977         case "security" -> toType(getSecurity(), type);
978         case "summary" -> toType(getSummary(), type);
979         case "tags" -> toType(getTags(), type);
980         default -> super.get(property, type);
981      };
982   }
983
984   @SuppressWarnings("rawtypes")
985   @Override /* Overridden from SwaggerElement */
986   public Operation set(String property, Object value) {
987      assertArgNotNull("property", property);
988      return switch (property) {
989         case "consumes" -> setConsumes(listBuilder(MediaType.class).sparse().addAny(value).build());
990         case "deprecated" -> setDeprecated(toBoolean(value));
991         case "description" -> setDescription(Utils.s(value));
992         case "externalDocs" -> setExternalDocs(toType(value, ExternalDocumentation.class));
993         case "operationId" -> setOperationId(Utils.s(value));
994         case "parameters" -> setParameters(listBuilder(ParameterInfo.class).sparse().addAny(value).build());
995         case "produces" -> setProduces(listBuilder(MediaType.class).sparse().addAny(value).build());
996         case "responses" -> setResponses(mapBuilder(String.class,ResponseInfo.class).sparse().addAny(value).build());
997         case "schemes" -> setSchemes(listBuilder(String.class).sparse().addAny(value).build());
998         case "security" -> setSecurity((List)listBuilder(Map.class,String.class,List.class,String.class).sparse().addAny(value).build());
999         case "summary" -> setSummary(Utils.s(value));
1000         case "tags" -> setTags(listBuilder(String.class).sparse().addAny(value).build());
1001         default -> {
1002            super.set(property, value);
1003            yield this;
1004         }
1005      };
1006   }
1007
1008   @Override /* Overridden from SwaggerElement */
1009   public Set<String> keySet() {
1010      var s = setBuilder(String.class)
1011         .addIf(consumes != null, "consumes")
1012         .addIf(deprecated != null, "deprecated")
1013         .addIf(description != null, "description")
1014         .addIf(externalDocs != null, "externalDocs")
1015         .addIf(operationId != null, "operationId")
1016         .addIf(parameters != null, "parameters")
1017         .addIf(produces != null, "produces")
1018         .addIf(responses != null, "responses")
1019         .addIf(schemes != null, "schemes")
1020         .addIf(security != null, "security")
1021         .addIf(summary != null, "summary")
1022         .addIf(tags != null, "tags")
1023         .build();
1024      return new MultiSet<>(s, super.keySet());
1025   }
1026
1027   /**
1028    * Sets strict mode on this bean.
1029    *
1030    * @return This object.
1031    */
1032   @Override
1033   public Operation strict() {
1034      super.strict();
1035      return this;
1036   }
1037
1038   /**
1039    * Sets strict mode on this bean.
1040    *
1041    * @param value
1042    *    The new value for this property.
1043    *    <br>Non-boolean values will be converted to boolean using <code>Boolean.<jsm>valueOf</jsm>(value.toString())</code>.
1044    *    <br>Can be <jk>null</jk> (interpreted as <jk>false</jk>).
1045    * @return This object.
1046    */
1047   @Override
1048   public Operation strict(Object value) {
1049      super.strict(value);
1050      return this;
1051   }
1052
1053}