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.net.*;
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 request body.
032 *
033 * <p>
034 * The Request Body Object describes a single request body that can be sent to an API operation. It includes 
035 * a description, whether the request body is required, and the content (media types) that the request body can contain.
036 *
037 * <h5 class='section'>OpenAPI Specification:</h5>
038 * <p>
039 * The Request Body Object is composed of the following fields:
040 * <ul class='spaced-list'>
041 *    <li><c>description</c> (string) - A brief description of the request body (CommonMark syntax may be used)
042 *    <li><c>content</c> (map of {@link MediaType}, REQUIRED) - The content of the request body (keys are media types)
043 *    <li><c>required</c> (boolean) - Determines if the request body is required in the request (default is <jk>false</jk>)
044 * </ul>
045 *
046 * <h5 class='section'>Example:</h5>
047 * <p class='bjava'>
048 *    <jc>// Create a request body for JSON content</jc>
049 *    RequestBodyInfo <jv>requestBody</jv> = <jk>new</jk> RequestBodyInfo()
050 *       .setDescription(<js>"Pet object that needs to be added to the store"</js>)
051 *       .setRequired(<jk>true</jk>)
052 *       .setContent(
053 *          JsonMap.<jsm>of</jsm>(
054 *             <js>"application/json"</js>, <jk>new</jk> MediaType()
055 *                .setSchema(
056 *                   <jk>new</jk> SchemaInfo().setRef(<js>"#/components/schemas/Pet"</js>)
057 *                )
058 *          )
059 *       );
060 * </p>
061 *
062 * <h5 class='section'>See Also:</h5><ul>
063 *    <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>
064 *    <li class='link'><a class="doclink" href="https://swagger.io/docs/specification/describing-request-body/">OpenAPI Describing Request Body</a>
065 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanOpenApi3">juneau-bean-openapi-v3</a>
066 * </ul>
067 */
068public class RequestBodyInfo extends OpenApiElement{
069
070   private String description;
071   private Map<String,MediaType> content;
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      this.content = copyOf(copyFrom.content, MediaType::copy);
090   }
091
092   /**
093    * Make a deep copy of this object.
094    *
095    * @return A deep copy of this object.
096    */
097   public RequestBodyInfo copy() {
098      return new RequestBodyInfo(this);
099   }
100
101   @Override /* Overridden from OpenApiElement */
102   protected RequestBodyInfo strict() {
103      super.strict();
104      return this;
105   }
106
107   @Override /* Overridden from OpenApiElement */
108   public RequestBodyInfo strict(Object value) {
109      super.strict(value);
110      return this;
111   }
112
113   /**
114    * Bean property getter:  <property>contentType</property>.
115    *
116    * <p>
117    * The URL pointing to the contact information.
118    *
119    * @return The property value, or <jk>null</jk> if it is not set.
120    */
121   public String getDescription() {
122      return description;
123   }
124
125   /**
126    * Bean property setter:  <property>url</property>.
127    *
128    * <p>
129    * The value can be of any of the following types: {@link URI}, {@link URL}, {@link String}.
130    * <br>Strings must be valid URIs.
131    *
132    * <p>
133    * URIs defined by {@link UriResolver} can be used for values.
134    *
135    * @param value
136    *    The new value for this property.
137    *    <br>Can be <jk>null</jk> to unset the property.
138    * @return This object
139    */
140   public RequestBodyInfo setDescription(String value) {
141      description = value;
142      return this;
143   }
144
145   /**
146    * Bean property getter:  <property>content</property>.
147    *
148    * @return The property value, or <jk>null</jk> if it is not set.
149    */
150   public Map<String, MediaType> getContent() {
151      return content;
152   }
153
154   /**
155    * Bean property setter:  <property>content</property>.
156    *
157    * @param value
158    *    The new value for this property.
159    *    <br>Can be <jk>null</jk> to unset the property.
160    * @return This object
161    */
162   public RequestBodyInfo setContent(Map<String, MediaType> value) {
163      content = copyOf(value);
164      return this;
165   }
166
167   /**
168    * Adds one or more values to the <property>content</property> property.
169    *
170    * @param key The mapping key.  Must not be <jk>null</jk>.
171    * @param value
172    *    The values to add to this property.
173    *    <br>Must not be <jk>null</jk>.
174    *    <br>Ignored if <jk>null</jk>.
175    * @return This object
176    */
177   public RequestBodyInfo addContent(String key, MediaType value) {
178      assertArgNotNull("key", key);
179      assertArgNotNull("value", value);
180      content = mapBuilder(content).sparse().add(key, value).build();
181      return this;
182   }
183
184   /**
185    * Bean property getter:  <property>required</property>.
186    *
187    * <p>
188    * The type of the object.
189    *
190    * @return The property value, or <jk>null</jk> if it is not set.
191    */
192   public Boolean getRequired() {
193      return required;
194   }
195
196   /**
197    * Bean property setter:  <property>explode</property>.
198    *
199    * <p>
200    * The type of the object.
201    *
202    * @param value
203    *    The new value for this property.
204    *    <br>Property value is required.
205    *    <br>Can be <jk>null</jk> to unset the property.
206    * @return This object
207    */
208   public RequestBodyInfo setRequired(Boolean value) {
209      required = value;
210      return this;
211   }
212
213   @Override /* Overridden from OpenApiElement */
214   public <T> T get(String property, Class<T> type) {
215      assertArgNotNull("property", property);
216      return switch (property) {
217         case "description" -> toType(getDescription(), type);
218         case "content" -> toType(getContent(), type);
219         case "required" -> toType(getRequired(), type);
220         default -> super.get(property, type);
221      };
222   }
223
224   @Override /* Overridden from OpenApiElement */
225   public RequestBodyInfo set(String property, Object value) {
226      assertArgNotNull("property", property);
227      return switch (property) {
228         case "content" -> setContent(mapBuilder(String.class,MediaType.class).sparse().addAny(value).build());
229         case "description" -> setDescription(Utils.s(value));
230         case "required" -> setRequired(toBoolean(value));
231         default -> {
232            super.set(property, value);
233            yield this;
234         }
235      };
236   }
237
238   @Override /* Overridden from OpenApiElement */
239   public Set<String> keySet() {
240      var s = setBuilder(String.class)
241         .addIf(content != null, "content")
242         .addIf(description != null, "description")
243         .addIf(required != null, "required")
244         .build();
245      return new MultiSet<>(s, super.keySet());
246   }
247}