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.AssertionUtils.*;
020import static org.apache.juneau.commons.utils.CollectionUtils.*;
021import static org.apache.juneau.commons.utils.Utils.*;
022import static org.apache.juneau.internal.ConverterUtils.*;
023
024import java.net.*;
025import java.util.*;
026
027import org.apache.juneau.*;
028import org.apache.juneau.commons.collections.*;
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 = 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      if (nn(copyFrom.content))
090         content.putAll(copyOf(copyFrom.content, MediaType::copy));
091   }
092
093   /**
094    * Adds one or more values to the <property>content</property> property.
095    *
096    * @param key The mapping key.  Must not be <jk>null</jk>.
097    * @param value
098    *    The values to add to this property.
099    *    <br>Must not be <jk>null</jk>.
100    *    <br>Ignored if <jk>null</jk>.
101    * @return This object
102    */
103   public RequestBodyInfo addContent(String key, MediaType value) {
104      assertArgNotNull("key", key);
105      assertArgNotNull("value", value);
106      content.put(key, value);
107      return this;
108   }
109
110   /**
111    * Make a deep copy of this object.
112    *
113    * @return A deep copy of this object.
114    */
115   public RequestBodyInfo copy() {
116      return new RequestBodyInfo(this);
117   }
118
119   @Override /* Overridden from OpenApiElement */
120   public <T> T get(String property, Class<T> type) {
121      assertArgNotNull("property", property);
122      return switch (property) {
123         case "description" -> toType(getDescription(), type);
124         case "content" -> toType(getContent(), type);
125         case "required" -> toType(getRequired(), type);
126         default -> super.get(property, type);
127      };
128   }
129
130   /**
131    * Bean property getter:  <property>content</property>.
132    *
133    * @return The property value, or <jk>null</jk> if it is not set.
134    */
135   public Map<String,MediaType> getContent() { return nullIfEmpty(content); }
136
137   /**
138    * Bean property getter:  <property>contentType</property>.
139    *
140    * <p>
141    * The URL pointing to the contact information.
142    *
143    * @return The property value, or <jk>null</jk> if it is not set.
144    */
145   public String getDescription() { return description; }
146
147   /**
148    * Bean property getter:  <property>required</property>.
149    *
150    * <p>
151    * The type of the object.
152    *
153    * @return The property value, or <jk>null</jk> if it is not set.
154    */
155   public Boolean getRequired() { return required; }
156
157   @Override /* Overridden from OpenApiElement */
158   public Set<String> keySet() {
159      // @formatter:off
160      var s = setb(String.class)
161         .addIf(ne(content), "content")
162         .addIf(nn(description), "description")
163         .addIf(nn(required), "required")
164         .build();
165      // @formatter:on
166      return new MultiSet<>(s, super.keySet());
167   }
168
169   @Override /* Overridden from OpenApiElement */
170   public RequestBodyInfo set(String property, Object value) {
171      assertArgNotNull("property", property);
172      return switch (property) {
173         case "content" -> setContent(toMapBuilder(value, String.class, MediaType.class).sparse().build());
174         case "description" -> setDescription(s(value));
175         case "required" -> setRequired(toBoolean(value));
176         default -> {
177            super.set(property, value);
178            yield this;
179         }
180      };
181   }
182
183   /**
184    * Bean property setter:  <property>content</property>.
185    *
186    * @param value
187    *    The new value for this property.
188    *    <br>Can be <jk>null</jk> to unset the property.
189    * @return This object
190    */
191   public RequestBodyInfo setContent(Map<String,MediaType> value) {
192      content.clear();
193      if (nn(value))
194         content.putAll(value);
195      return this;
196   }
197
198   /**
199    * Bean property setter:  <property>url</property>.
200    *
201    * <p>
202    * The value can be of any of the following types: {@link URI}, {@link URL}, {@link String}.
203    * <br>Strings must be valid URIs.
204    *
205    * <p>
206    * URIs defined by {@link UriResolver} can be used for values.
207    *
208    * @param value
209    *    The new value for this property.
210    *    <br>Can be <jk>null</jk> to unset the property.
211    * @return This object
212    */
213   public RequestBodyInfo setDescription(String value) {
214      description = value;
215      return this;
216   }
217
218   /**
219    * Bean property setter:  <property>explode</property>.
220    *
221    * <p>
222    * The type of the object.
223    *
224    * @param value
225    *    The new value for this property.
226    *    <br>Property value is required.
227    *    <br>Can be <jk>null</jk> to unset the property.
228    * @return This object
229    */
230   public RequestBodyInfo setRequired(Boolean value) {
231      required = value;
232      return this;
233   }
234
235   @Override /* Overridden from OpenApiElement */
236   public RequestBodyInfo strict(Object value) {
237      super.strict(value);
238      return this;
239   }
240
241   @Override /* Overridden from OpenApiElement */
242   protected RequestBodyInfo strict() {
243      super.strict();
244      return this;
245   }
246}