1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.juneau.bean.openapi3;
18
19 import static org.apache.juneau.commons.utils.AssertionUtils.*;
20 import static org.apache.juneau.commons.utils.CollectionUtils.*;
21 import static org.apache.juneau.commons.utils.Utils.*;
22 import static org.apache.juneau.internal.ConverterUtils.*;
23
24 import java.net.*;
25 import java.util.*;
26
27 import org.apache.juneau.*;
28 import org.apache.juneau.commons.collections.*;
29
30 /**
31 * Describes a single request body.
32 *
33 * <p>
34 * The Request Body Object describes a single request body that can be sent to an API operation. It includes
35 * a description, whether the request body is required, and the content (media types) that the request body can contain.
36 *
37 * <h5 class='section'>OpenAPI Specification:</h5>
38 * <p>
39 * The Request Body Object is composed of the following fields:
40 * <ul class='spaced-list'>
41 * <li><c>description</c> (string) - A brief description of the request body (CommonMark syntax may be used)
42 * <li><c>content</c> (map of {@link MediaType}, REQUIRED) - The content of the request body (keys are media types)
43 * <li><c>required</c> (boolean) - Determines if the request body is required in the request (default is <jk>false</jk>)
44 * </ul>
45 *
46 * <h5 class='section'>Example:</h5>
47 * <p class='bjava'>
48 * <jc>// Create a request body for JSON content</jc>
49 * RequestBodyInfo <jv>requestBody</jv> = <jk>new</jk> RequestBodyInfo()
50 * .setDescription(<js>"Pet object that needs to be added to the store"</js>)
51 * .setRequired(<jk>true</jk>)
52 * .setContent(
53 * JsonMap.<jsm>of</jsm>(
54 * <js>"application/json"</js>, <jk>new</jk> MediaType()
55 * .setSchema(
56 * <jk>new</jk> SchemaInfo().setRef(<js>"#/components/schemas/Pet"</js>)
57 * )
58 * )
59 * );
60 * </p>
61 *
62 * <h5 class='section'>See Also:</h5><ul>
63 * <li class='link'><a class="doclink" href="https://spec.openapis.org/oas/v3.0.0#request-body-object">OpenAPI Specification > Request Body Object</a>
64 * <li class='link'><a class="doclink" href="https://swagger.io/docs/specification/describing-request-body/">OpenAPI Describing Request Body</a>
65 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanOpenApi3">juneau-bean-openapi-v3</a>
66 * </ul>
67 */
68 public class RequestBodyInfo extends OpenApiElement {
69
70 private String description;
71 private Map<String,MediaType> content = map();
72 private Boolean required;
73
74 /**
75 * Default constructor.
76 */
77 public RequestBodyInfo() {}
78
79 /**
80 * Copy constructor.
81 *
82 * @param copyFrom The object to copy.
83 */
84 public RequestBodyInfo(RequestBodyInfo copyFrom) {
85 super(copyFrom);
86
87 this.description = copyFrom.description;
88 this.required = copyFrom.required;
89 if (nn(copyFrom.content))
90 content.putAll(copyOf(copyFrom.content, MediaType::copy));
91 }
92
93 /**
94 * Adds one or more values to the <property>content</property> property.
95 *
96 * @param key The mapping key. Must not be <jk>null</jk>.
97 * @param value
98 * The values to add to this property.
99 * <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 }