View Javadoc
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.ThrowableUtils.*;
22  import static org.apache.juneau.commons.utils.Utils.*;
23  import static org.apache.juneau.internal.ConverterUtils.*;
24  
25  import java.util.*;
26  
27  import org.apache.juneau.commons.collections.*;
28  import org.apache.juneau.marshaller.*;
29  
30  /**
31   * Describes a single operation parameter.
32   *
33   * <p>
34   * The Parameter Object describes a single parameter used in an API operation. Parameters can be passed in various
35   * locations including the path, query string, headers, or cookies. Each parameter has a name, location, and schema
36   * that defines its type and constraints.
37   *
38   * <h5 class='section'>OpenAPI Specification:</h5>
39   * <p>
40   * The Parameter Object is composed of the following fields:
41   * <ul class='spaced-list'>
42   * 	<li><c>name</c> (string, REQUIRED) - The name of the parameter
43   * 	<li><c>in</c> (string, REQUIRED) - The location of the parameter. Possible values: <js>"query"</js>, <js>"header"</js>, <js>"path"</js>, or <js>"cookie"</js>
44   * 	<li><c>description</c> (string) - A brief description of the parameter (CommonMark syntax may be used)
45   * 	<li><c>required</c> (boolean) - Determines whether this parameter is mandatory (must be <jk>true</jk> if <c>in</c> is <js>"path"</js>)
46   * 	<li><c>deprecated</c> (boolean) - Specifies that a parameter is deprecated
47   * 	<li><c>allowEmptyValue</c> (boolean) - Sets the ability to pass empty-valued parameters (valid only for <js>"query"</js> parameters)
48   * 	<li><c>style</c> (string) - Describes how the parameter value will be serialized
49   * 	<li><c>explode</c> (boolean) - When true, parameter values of type array or object generate separate parameters for each value
50   * 	<li><c>allowReserved</c> (boolean) - Determines whether the parameter value should allow reserved characters
51   * 	<li><c>schema</c> ({@link SchemaInfo}) - The schema defining the type used for the parameter
52   * 	<li><c>example</c> (any) - Example of the parameter's potential value
53   * 	<li><c>examples</c> (map of {@link Example}) - Examples of the parameter's potential value
54   * </ul>
55   *
56   * <h5 class='section'>Example:</h5>
57   * <p class='bjava'>
58   * 	<jc>// Create a query parameter</jc>
59   * 	Parameter <jv>param</jv> = <jk>new</jk> Parameter()
60   * 		.setName(<js>"status"</js>)
61   * 		.setIn(<js>"query"</js>)
62   * 		.setDescription(<js>"Status values to filter by"</js>)
63   * 		.setRequired(<jk>false</jk>)
64   * 		.setSchema(
65   * 			<jk>new</jk> SchemaInfo()
66   * 				.setType(<js>"array"</js>)
67   * 				.setItems(
68   * 					<jk>new</jk> Items().setType(<js>"string"</js>)
69   * 				)
70   * 		)
71   * 		.setStyle(<js>"form"</js>)
72   * 		.setExplode(<jk>true</jk>);
73   * </p>
74   *
75   * <h5 class='section'>See Also:</h5><ul>
76   * 	<li class='link'><a class="doclink" href="https://spec.openapis.org/oas/v3.0.0#parameter-object">OpenAPI Specification &gt; Parameter Object</a>
77   * 	<li class='link'><a class="doclink" href="https://swagger.io/docs/specification/describing-parameters/">OpenAPI Describing Parameters</a>
78   * 	<li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanOpenApi3">juneau-bean-openapi-v3</a>
79   * </ul>
80   */
81  public class Parameter extends OpenApiElement {
82  
83  	private static final String[] VALID_IN = { "query", "header", "path", "cookie" };
84  	private static final String[] VALID_STYLES = { "matrix", "label", "form", "simple", "spaceDelimited", "pipeDelimited", "deepObject" };
85  
86  	private String name, in, description, style;
87  	private Boolean required, deprecated, allowEmptyValue, explode, allowReserved;
88  	private SchemaInfo schema;
89  	private Object example;
90  	private Map<String,Example> examples;
91  
92  	/**
93  	 * Default constructor.
94  	 */
95  	public Parameter() {}
96  
97  	/**
98  	 * Copy constructor.
99  	 *
100 	 * @param copyFrom The object to copy.
101 	 */
102 	public Parameter(Parameter copyFrom) {
103 		super(copyFrom);
104 		this.name = copyFrom.name;
105 		this.in = copyFrom.in;
106 		this.description = copyFrom.description;
107 		this.style = copyFrom.style;
108 		this.required = copyFrom.required;
109 		this.deprecated = copyFrom.deprecated;
110 		this.allowEmptyValue = copyFrom.allowEmptyValue;
111 		this.explode = copyFrom.explode;
112 		this.allowReserved = copyFrom.allowReserved;
113 		this.schema = copyFrom.schema;
114 		this.example = copyFrom.example;
115 		this.examples = copyOf(copyFrom.examples);
116 	}
117 
118 	/**
119 	 * Makes a copy of this object.
120 	 *
121 	 * @return A new copy of this object.
122 	 */
123 	public Parameter copy() {
124 		return new Parameter(this);
125 	}
126 
127 	@Override /* Overridden from OpenApiElement */
128 	public <T> T get(String property, Class<T> type) {
129 		assertArgNotNull("property", property);
130 		return switch (property) {
131 			case "name" -> toType(getName(), type);
132 			case "in" -> toType(getIn(), type);
133 			case "description" -> toType(getDescription(), type);
134 			case "required" -> toType(getRequired(), type);
135 			case "deprecated" -> toType(getDeprecated(), type);
136 			case "allowEmptyValue" -> toType(getAllowEmptyValue(), type);
137 			case "style" -> toType(getStyle(), type);
138 			case "explode" -> toType(getExplode(), type);
139 			case "allowReserved" -> toType(getAllowReserved(), type);
140 			case "schema" -> toType(getSchema(), type);
141 			case "example" -> toType(getExample(), type);
142 			case "examples" -> toType(getExamples(), type);
143 			default -> super.get(property, type);
144 		};
145 	}
146 
147 	/**
148 	 * Returns the allow empty value flag.
149 	 *
150 	 * @return The allow empty value flag.
151 	 */
152 	public Boolean getAllowEmptyValue() { return allowEmptyValue; }
153 
154 	/**
155 	 * Returns the allow reserved flag.
156 	 *
157 	 * @return The allow reserved flag.
158 	 */
159 	public Boolean getAllowReserved() { return allowReserved; }
160 
161 	/**
162 	 * Returns the deprecated flag.
163 	 *
164 	 * @return The deprecated flag.
165 	 */
166 	public Boolean getDeprecated() { return deprecated; }
167 
168 	/**
169 	 * Returns the description.
170 	 *
171 	 * @return The description.
172 	 */
173 	public String getDescription() { return description; }
174 
175 	/**
176 	 * Returns the example.
177 	 *
178 	 * @return The example.
179 	 */
180 	public Object getExample() { return example; }
181 
182 	/**
183 	 * Returns the examples map.
184 	 *
185 	 * @return The examples map.
186 	 */
187 	public Map<String,Example> getExamples() { return examples; }
188 
189 	/**
190 	 * Returns the explode flag.
191 	 *
192 	 * @return The explode flag.
193 	 */
194 	public Boolean getExplode() { return explode; }
195 
196 	/**
197 	 * Returns the parameter location.
198 	 *
199 	 * @return The parameter location.
200 	 */
201 	public String getIn() { return in; }
202 
203 	/**
204 	 * Returns the parameter name.
205 	 *
206 	 * @return The parameter name.
207 	 */
208 	public String getName() { return name; }
209 
210 	/**
211 	 * Returns the required flag.
212 	 *
213 	 * @return The required flag.
214 	 */
215 	public Boolean getRequired() { return required; }
216 
217 	/**
218 	 * Returns the schema.
219 	 *
220 	 * @return The schema.
221 	 */
222 	public SchemaInfo getSchema() { return schema; }
223 
224 	/**
225 	 * Returns the style.
226 	 *
227 	 * @return The style.
228 	 */
229 	public String getStyle() { return style; }
230 
231 	@Override /* Overridden from OpenApiElement */
232 	public Set<String> keySet() {
233 		// @formatter:off
234 		var s = setb(String.class)
235 			.addIf(nn(allowEmptyValue), "allowEmptyValue")
236 			.addIf(nn(allowReserved), "allowReserved")
237 			.addIf(nn(deprecated), "deprecated")
238 			.addIf(nn(description), "description")
239 			.addIf(nn(example), "example")
240 			.addIf(nn(examples), "examples")
241 			.addIf(nn(explode), "explode")
242 			.addIf(nn(in), "in")
243 			.addIf(nn(name), "name")
244 			.addIf(nn(required), "required")
245 			.addIf(nn(schema), "schema")
246 			.addIf(nn(style), "style")
247 			.build();
248 		// @formatter:on
249 		return new MultiSet<>(s, super.keySet());
250 	}
251 
252 	@Override /* Overridden from OpenApiElement */
253 	public Parameter set(String property, Object value) {
254 		assertArgNotNull("property", property);
255 		return switch (property) {
256 			case "allowEmptyValue" -> setAllowEmptyValue(toType(value, Boolean.class));
257 			case "allowReserved" -> setAllowReserved(toType(value, Boolean.class));
258 			case "description" -> setDescription(s(value));
259 			case "deprecated" -> setDeprecated(toType(value, Boolean.class));
260 			case "example" -> setExample(value);
261 			case "examples" -> setExamples(toMapBuilder(value, String.class, Example.class).sparse().build());
262 			case "explode" -> setExplode(toType(value, Boolean.class));
263 			case "in" -> setIn(s(value));
264 			case "name" -> setName(s(value));
265 			case "required" -> setRequired(toType(value, Boolean.class));
266 			case "schema" -> setSchema(toType(value, SchemaInfo.class));
267 			case "style" -> setStyle(s(value));
268 			default -> {
269 				super.set(property, value);
270 				yield this;
271 			}
272 		};
273 	}
274 
275 	/**
276 	 * Sets the allow empty value flag.
277 	 *
278 	 * @param value The new value for this property.
279 	 * @return This object.
280 	 */
281 	public Parameter setAllowEmptyValue(Boolean value) {
282 		allowEmptyValue = value;
283 		return this;
284 	}
285 
286 	/**
287 	 * Sets the allow reserved flag.
288 	 *
289 	 * @param value The new value for this property.
290 	 * @return This object.
291 	 */
292 	public Parameter setAllowReserved(Boolean value) {
293 		allowReserved = value;
294 		return this;
295 	}
296 
297 	/**
298 	 * Sets the deprecated flag.
299 	 *
300 	 * @param value The new value for this property.
301 	 * @return This object.
302 	 */
303 	public Parameter setDeprecated(Boolean value) {
304 		deprecated = value;
305 		return this;
306 	}
307 
308 	/**
309 	 * Sets the description.
310 	 *
311 	 * @param value The new value for this property.
312 	 * @return This object.
313 	 */
314 	public Parameter setDescription(String value) {
315 		description = value;
316 		return this;
317 	}
318 
319 	/**
320 	 * Sets the example.
321 	 *
322 	 * @param value The new value for this property.
323 	 * @return This object.
324 	 */
325 	public Parameter setExample(Object value) {
326 		example = value;
327 		return this;
328 	}
329 
330 	/**
331 	 * Sets the examples map.
332 	 *
333 	 * @param value The new value for this property.
334 	 * @return This object.
335 	 */
336 	public Parameter setExamples(Map<String,Example> value) {
337 		examples = value;
338 		return this;
339 	}
340 
341 	/**
342 	 * Sets the explode flag.
343 	 *
344 	 * @param value The new value for this property.
345 	 * @return This object.
346 	 */
347 	public Parameter setExplode(Boolean value) {
348 		explode = value;
349 		return this;
350 	}
351 
352 	/**
353 	 * Sets the parameter location.
354 	 *
355 	 * @param value The new value for this property.
356 	 * @return This object.
357 	 */
358 	public Parameter setIn(String value) {
359 		if (isStrict() && ! contains(value, VALID_IN))
360 			throw rex("Invalid value passed in to setIn(String).  Value=''{0}'', valid values={1}", value, Json5.of(VALID_IN));
361 		in = value;
362 		return this;
363 	}
364 
365 	/**
366 	 * Sets the parameter name.
367 	 *
368 	 * @param value The new value for this property.
369 	 * @return This object.
370 	 */
371 	public Parameter setName(String value) {
372 		name = value;
373 		return this;
374 	}
375 
376 	/**
377 	 * Sets the required flag.
378 	 *
379 	 * @param value The new value for this property.
380 	 * @return This object.
381 	 */
382 	public Parameter setRequired(Boolean value) {
383 		required = value;
384 		return this;
385 	}
386 
387 	/**
388 	 * Sets the schema.
389 	 *
390 	 * @param value The new value for this property.
391 	 * @return This object.
392 	 */
393 	public Parameter setSchema(SchemaInfo value) {
394 		schema = value;
395 		return this;
396 	}
397 
398 	/**
399 	 * Sets the style.
400 	 *
401 	 * @param value The new value for this property.
402 	 * @return This object.
403 	 */
404 	public Parameter setStyle(String value) {
405 		if (isStrict() && ! contains(value, VALID_STYLES))
406 			throw rex("Invalid value passed in to setStyle(String).  Value=''{0}'', valid values={1}", value, Json5.of(VALID_STYLES));
407 		style = value;
408 		return this;
409 	}
410 
411 	@Override /* Overridden from OpenApiElement */
412 	public Parameter strict() {
413 		super.strict();
414 		return this;
415 	}
416 
417 	@Override /* Overridden from OpenApiElement */
418 	public Parameter strict(Object value) {
419 		super.strict(value);
420 		return this;
421 	}
422 }