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.commons.utils.Shorts.*;
021import static org.apache.juneau.marshall.internal.ConverterUtils.*;
022
023import java.util.*;
024
025import org.apache.juneau.commons.collections.*;
026
027/**
028 * Describes a single request body.
029 *
030 * <p>
031 * The Request Body Object describes a single request body that can be sent to an API operation. It includes
032 * a description, whether the request body is required, and the content (media types) that the request body can contain.
033 *
034 * <h5 class='section'>OpenAPI Specification:</h5>
035 * <p>
036 * The Request Body Object is composed of the following fields:
037 * <ul class='spaced-list'>
038 *    <li><c>description</c> (string) - A brief description of the request body (CommonMark syntax may be used)
039 *    <li><c>content</c> (map of {@link MediaType}, REQUIRED) - The content of the request body (keys are media types)
040 *    <li><c>required</c> (boolean) - Determines if the request body is required in the request (default is <jk>false</jk>)
041 * </ul>
042 *
043 * <h5 class='section'>Example:</h5>
044 * <p class='bjava'>
045 *    <jc>// Create a request body for JSON content</jc>
046 *    RequestBodyInfo <jv>requestBody</jv> = <jk>new</jk> RequestBodyInfo()
047 *       .setDescription(<js>"Pet object that needs to be added to the store"</js>)
048 *       .setRequired(<jk>true</jk>)
049 *       .setContent(
050 *          JsonMap.<jsm>of</jsm>(
051 *             <js>"application/json"</js>, <jk>new</jk> MediaType()
052 *                .setSchema(
053 *                   <jk>new</jk> SchemaInfo().setRef(<js>"#/components/schemas/Pet"</js>)
054 *                )
055 *          )
056 *       );
057 * </p>
058 *
059 * <h5 class='section'>See Also:</h5><ul>
060 *    <li class='link'><a class="doclink" href="https://spec.openapis.org/oas/v3.0.0#request-body-object">OpenAPI Specification &gt; Request Body Object</a>
061 *    <li class='link'><a class="doclink" href="https://swagger.io/docs/specification/describing-request-body/">OpenAPI Describing Request Body</a>
062 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanOpenApi3">juneau-bean-openapi-v3</a>
063 * </ul>
064 */
065@SuppressWarnings({
066   "java:S1192" // Duplicated literals (argument/property names) read more clearly inline than as constants
067})
068public class RequestBodyInfo extends OpenApiElement {
069
070   private String description;
071   private final Map<String,MediaType> content = map();
072   private Boolean required;
073
074   /**
075    * Default constructor.
076    */
077   public RequestBodyInfo() {}
078
079   /**
080    * Copy constructor.
081    *
082    * @param copyFrom The object to copy.
083    */
084   public RequestBodyInfo(RequestBodyInfo copyFrom) {
085      super(copyFrom);
086
087      this.description = copyFrom.description;
088      this.required = copyFrom.required;
089      content.putAll(cp(copyFrom.content, MediaType::copy));
090   }
091
092   /**
093    * Adds one or more values to the <property>content</property> property.
094    *
095    * @param key The mapping key.  Must not be <jk>null</jk>, or an {@link IllegalArgumentException} is thrown.
096    * @param value
097    *    The values to add to this property.
098    *    <br>Must not be <jk>null</jk>, or an {@link IllegalArgumentException} is thrown.
099    *    <br>Ignored if <jk>null</jk>.
100    * @return This object
101    */
102   public RequestBodyInfo addContent(String key, MediaType value) {
103      reqnn("key", key);
104      reqnn("value", value);
105      content.put(key, value);
106      return this;
107   }
108
109   /**
110    * Make a deep copy of this object.
111    *
112    * @return A deep copy of this object.
113    */
114   public RequestBodyInfo copy() {
115      return new RequestBodyInfo(this);
116   }
117
118   @Override /* Overridden from OpenApiElement */
119   public <T> T get(String property, Class<T> type) {
120      reqnn("property", property);
121      return switch (property) {
122         case "description" -> toType(getDescription(), type);
123         case "content" -> toType(getContent(), type);
124         case "required" -> toType(getRequired(), type);
125         default -> super.get(property, type);
126      };
127   }
128
129   /**
130    * Bean property getter:  <property>content</property>.
131    *
132    * @return The property value, or <jk>null</jk> if it is not set.
133    */
134   public Map<String,MediaType> getContent() { return nie(content); }
135
136   /**
137    * Bean property getter:  <property>description</property>.
138    *
139    * <p>
140    * A brief description of the request body.
141    *
142    * @return The property value, or <jk>null</jk> if it is not set.
143    */
144   public String getDescription() { return description; }
145
146   /**
147    * Bean property getter:  <property>required</property>.
148    *
149    * <p>
150    * Determines if the request body is required in the request.
151    *
152    * @return The property value, or <jk>null</jk> if it is not set.
153    */
154   public Boolean getRequired() { return required; }
155
156   @Override /* Overridden from OpenApiElement */
157   public Set<String> keySet() {
158      // @formatter:off
159      var s = stb(String.class)
160         .addIf(ine(content), "content")
161         .addIf(nn(description), "description")
162         .addIf(nn(required), "required")
163         .build();
164      // @formatter:on
165      return new MultiSet<>(s, super.keySet());
166   }
167
168   @Override /* Overridden from OpenApiElement */
169   public RequestBodyInfo set(String property, Object value) {
170      reqnn("property", property);
171      return switch (property) {
172         case "content" -> setContent(toMapBuilder(value, String.class, MediaType.class).sparse().build());
173         case "description" -> setDescription(s(value));
174         case "required" -> setRequired(toBoolean(value));
175         default -> {
176            super.set(property, value);
177            yield this;
178         }
179      };
180   }
181
182   /**
183    * Bean property setter:  <property>content</property>.
184    *
185    * @param value
186    *    The new value for this property.
187    *    <br>Can be <jk>null</jk> to unset the property.
188    * @return This object
189    */
190   public RequestBodyInfo setContent(Map<String,MediaType> value) {
191      content.clear();
192      if (nn(value))
193         content.putAll(value);
194      return this;
195   }
196
197   /**
198    * Bean property setter:  <property>description</property>.
199    *
200    * <p>
201    * A brief description of the request body.
202    *
203    * @param value
204    *    The new value for this property.
205    *    <br>Can be <jk>null</jk> to unset the property.
206    * @return This object
207    */
208   public RequestBodyInfo setDescription(String value) {
209      description = value;
210      return this;
211   }
212
213   /**
214    * Bean property setter:  <property>required</property>.
215    *
216    * <p>
217    * Determines if the request body is required in the request.
218    *
219    * @param value
220    *    The new value for this property.
221    *    <br>Can be <jk>null</jk> to unset the property.
222    * @return This object
223    */
224   public RequestBodyInfo setRequired(Boolean value) {
225      required = value;
226      return this;
227   }
228
229   @Override /* Overridden from OpenApiElement */
230   public RequestBodyInfo strict(Object value) {
231      super.strict(value);
232      return this;
233   }
234
235   @Override /* Overridden from OpenApiElement */
236   protected RequestBodyInfo strict() {
237      super.strict();
238      return this;
239   }
240}