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.util.*;
25
26 import org.apache.juneau.commons.collections.*;
27
28 /**
29 * The Link object represents a possible design-time link for a response.
30 *
31 * <p>
32 * The Link Object represents a possible design-time link for a response. The presence of a link does not guarantee
33 * the caller's ability to successfully invoke it, rather it provides a known relationship and traversal mechanism
34 * between responses and other operations.
35 *
36 * <h5 class='section'>OpenAPI Specification:</h5>
37 * <p>
38 * The Link Object is composed of the following fields:
39 * <ul class='spaced-list'>
40 * <li><c>operationRef</c> (string) - A relative or absolute reference to an OAS operation (mutually exclusive with <c>operationId</c>)
41 * <li><c>operationId</c> (string) - The name of an existing, resolvable OAS operation (mutually exclusive with <c>operationRef</c>)
42 * <li><c>parameters</c> (map of any) - A map representing parameters to pass to an operation as specified with <c>operationId</c> or identified via <c>operationRef</c>
43 * <li><c>requestBody</c> (any) - A literal value or expression to use as a request body when calling the target operation
44 * <li><c>description</c> (string) - A description of the link (CommonMark syntax may be used)
45 * <li><c>server</c> ({@link Server}) - A server object to be used by the target operation
46 * </ul>
47 *
48 * <h5 class='section'>Example:</h5>
49 * <p class='bjava'>
50 * <jc>// Create a link to another operation</jc>
51 * Link <jv>link</jv> = <jk>new</jk> Link()
52 * .setOperationId(<js>"getUserById"</js>)
53 * .setParameters(
54 * JsonMap.<jsm>of</jsm>(<js>"userId"</js>, <js>"$response.body#/id"</js>)
55 * )
56 * .setDescription(<js>"The id value returned in the response can be used as userId parameter in GET /users/{userId}"</js>);
57 * </p>
58 *
59 * <h5 class='section'>See Also:</h5><ul>
60 * <li class='link'><a class="doclink" href="https://spec.openapis.org/oas/v3.0.0#link-object">OpenAPI Specification > Link Object</a>
61 * <li class='link'><a class="doclink" href="https://swagger.io/docs/specification/links/">OpenAPI Links</a>
62 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanOpenApi3">juneau-bean-openapi-v3</a>
63 * </ul>
64 */
65 public class Link extends OpenApiElement {
66
67 private String operationRef;
68 private String operationId;
69 private String description;
70 private Object requestBody;
71 private Server server;
72 private Map<String,Object> parameters = map();
73
74 /**
75 * Default constructor.
76 */
77 public Link() {}
78
79 /**
80 * Copy constructor.
81 *
82 * @param copyFrom The object to copy.
83 */
84 public Link(Link copyFrom) {
85 super(copyFrom);
86
87 this.operationRef = copyFrom.operationRef;
88 this.description = copyFrom.description;
89 this.operationId = copyFrom.operationId;
90 this.requestBody = copyFrom.requestBody;
91 this.server = copyFrom.server == null ? null : copyFrom.server.copy();
92 if (nn(copyFrom.parameters))
93 parameters.putAll(copyFrom.parameters);
94 }
95
96 /**
97 * Adds a single value to the <property>examples</property> property.
98 *
99 * @param mimeType The mime-type string. Must not be <jk>null</jk>.
100 * @param parameter The example. Must not be <jk>null</jk>.
101 * @return This object
102 */
103 public Link addParameter(String mimeType, Object parameter) {
104 assertArgNotNull("mimeType", mimeType);
105 assertArgNotNull("parameter", parameter);
106 parameters.put(mimeType, parameter);
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 Link copy() {
116 return new Link(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 "operationRef" -> toType(getOperationRef(), type);
125 case "operationId" -> toType(getOperationId(), type);
126 case "requestBody" -> toType(getRequestBody(), type);
127 case "parameters" -> toType(getParameters(), type);
128 case "server" -> toType(getServer(), type);
129 default -> super.get(property, type);
130 };
131 }
132
133 /**
134 * Bean property getter: <property>description</property>.
135 *
136 * <p>
137 * The URL pointing to the contact information.
138 *
139 * @return The property value, or <jk>null</jk> if it is not set.
140 */
141 public String getDescription() { return description; }
142
143 /**
144 * Bean property getter: <property>externalValue</property>.
145 *
146 * <p>
147 * The email address of the contact person/organization.
148 *
149 * @return The property value, or <jk>null</jk> if it is not set.
150 */
151 public String getOperationId() { return operationId; }
152
153 /**
154 * Bean property getter: <property>operationRef</property>.
155 *
156 * <p>
157 * The identifying name of the contact person/organization.
158 *
159 * @return The property value, or <jk>null</jk> if it is not set.
160 */
161 public String getOperationRef() { return operationRef; }
162
163 /**
164 * Bean property getter: <property>examples</property>.
165 *
166 * <p>
167 * An example of the response message.
168 *
169 * @return The property value, or <jk>null</jk> if it is not set.
170 */
171 public Map<String,Object> getParameters() { return nullIfEmpty(parameters); }
172
173 /**
174 * Bean property getter: <property>default</property>.
175 *
176 * <p>
177 * Declares the value of the parameter that the server will use if none is provided, for example a <js>"count"</js>
178 * to control the number of results per page might default to 100 if not supplied by the client in the request.
179 *
180 * (Note: <js>"value"</js> has no meaning for required parameters.)
181 * Unlike JSON Schema this value MUST conform to the defined <code>type</code> for this parameter.
182 *
183 * @return The property value, or <jk>null</jk> if it is not set.
184 */
185 public Object getRequestBody() { return requestBody; }
186
187 /**
188 * Bean property getter: <property>additionalProperties</property>.
189 *
190 * @return The property value, or <jk>null</jk> if it is not set.
191 */
192 public Server getServer() { return server; }
193
194 @Override /* Overridden from OpenApiElement */
195 public Set<String> keySet() {
196 // @formatter:off
197 var s = setb(String.class)
198 .addIf(nn(description), "description")
199 .addIf(nn(operationId), "operationId")
200 .addIf(nn(operationRef), "operationRef")
201 .addIf(ne(parameters), "parameters")
202 .addIf(nn(requestBody), "requestBody")
203 .addIf(nn(server), "server")
204 .build();
205 // @formatter:on
206 return new MultiSet<>(s, super.keySet());
207 }
208
209 @Override /* Overridden from OpenApiElement */
210 public Link set(String property, Object value) {
211 assertArgNotNull("property", property);
212 return switch (property) {
213 case "description" -> setDescription(s(value));
214 case "operationId" -> setOperationId(s(value));
215 case "operationRef" -> setOperationRef(s(value));
216 case "parameters" -> setParameters(toMapBuilder(value, String.class, Object.class).sparse().build());
217 case "requestBody" -> setRequestBody(value);
218 case "server" -> setServer(toType(value, Server.class));
219 default -> {
220 super.set(property, value);
221 yield this;
222 }
223 };
224 }
225
226 /**
227 * Bean property setter: <property>description</property>.
228 * @param value
229 * The new value for this property.
230 * <br>Can be <jk>null</jk> to unset the property.
231 * @return This object
232 */
233 public Link setDescription(String value) {
234 description = value;
235 return this;
236 }
237
238 /**
239 * Bean property setter: <property>externalValue</property>.
240 *
241 * <p>
242 * The email address of the contact person/organization.
243 *
244 * @param value
245 * The new value for this property.
246 * <br>MUST be in the format of an email address.
247 * <br>Can be <jk>null</jk> to unset the property.
248 * @return This object
249 */
250 public Link setOperationId(String value) {
251 operationId = value;
252 return this;
253 }
254
255 /**
256 * Bean property setter: <property>operationRef</property>.
257 *
258 * <p>
259 * The identifying name of the contact person/organization.
260 *
261 * @param value
262 * The new value for this property.
263 * <br>Can be <jk>null</jk> to unset the property.
264 * @return This object
265 */
266 public Link setOperationRef(String value) {
267 operationRef = value;
268 return this;
269 }
270
271 /**
272 * Bean property setter: <property>examples</property>.
273 *
274 * <p>
275 * An example of the response message.
276 *
277 * @param value
278 * The new value for this property.
279 * <br>Keys must be MIME-type strings.
280 * <br>Can be <jk>null</jk> to unset the property.
281 * @return This object
282 */
283 public Link setParameters(Map<String,Object> value) {
284 parameters.clear();
285 if (nn(value))
286 parameters.putAll(value);
287 return this;
288 }
289
290 /**
291 * Bean property setter: <property>value</property>.
292 *
293 * <p>
294 * Declares the value of the parameter that the server will use if none is provided, for example a <js>"count"</js>
295 * to control the number of results per page might default to 100 if not supplied by the client in the request.
296 * (Note: <js>"default"</js> has no meaning for required parameters.)
297 * Unlike JSON Schema this value MUST conform to the defined <code>type</code> for this parameter.
298 *
299 * @param val The new value for this property.
300 * <br>Can be <jk>null</jk> to unset the property.
301 * @return This object
302 */
303 public Link setRequestBody(Object val) {
304 requestBody = val;
305 return this;
306 }
307
308 /**
309 * Bean property setter: <property>additionalProperties</property>.
310 *
311 * @param value
312 * The new value for this property.
313 * <br>Can be <jk>null</jk> to unset the property.
314 * @return This object
315 */
316 public Link setServer(Server value) {
317 server = value;
318 return this;
319 }
320
321 @Override /* Overridden from OpenApiElement */
322 public Link strict() {
323 super.strict();
324 return this;
325 }
326
327 @Override /* Overridden from OpenApiElement */
328 public Link strict(Object value) {
329 super.strict(value);
330 return this;
331 }
332 }