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.util.*;
024
025import org.apache.juneau.common.utils.*;
026import org.apache.juneau.internal.*;
027
028/**
029 * The Link object represents a possible design-time link for a response.
030 *
031 * <p>
032 * The Link Object represents a possible design-time link for a response. The presence of a link does not guarantee 
033 * the caller's ability to successfully invoke it, rather it provides a known relationship and traversal mechanism 
034 * between responses and other operations.
035 *
036 * <h5 class='section'>OpenAPI Specification:</h5>
037 * <p>
038 * The Link Object is composed of the following fields:
039 * <ul class='spaced-list'>
040 *    <li><c>operationRef</c> (string) - A relative or absolute reference to an OAS operation (mutually exclusive with <c>operationId</c>)
041 *    <li><c>operationId</c> (string) - The name of an existing, resolvable OAS operation (mutually exclusive with <c>operationRef</c>)
042 *    <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>
043 *    <li><c>requestBody</c> (any) - A literal value or expression to use as a request body when calling the target operation
044 *    <li><c>description</c> (string) - A description of the link (CommonMark syntax may be used)
045 *    <li><c>server</c> ({@link Server}) - A server object to be used by the target operation
046 * </ul>
047 *
048 * <h5 class='section'>Example:</h5>
049 * <p class='bjava'>
050 *    <jc>// Create a link to another operation</jc>
051 *    Link <jv>link</jv> = <jk>new</jk> Link()
052 *       .setOperationId(<js>"getUserById"</js>)
053 *       .setParameters(
054 *          JsonMap.<jsm>of</jsm>(<js>"userId"</js>, <js>"$response.body#/id"</js>)
055 *       )
056 *       .setDescription(<js>"The id value returned in the response can be used as userId parameter in GET /users/{userId}"</js>);
057 * </p>
058 *
059 * <h5 class='section'>See Also:</h5><ul>
060 *    <li class='link'><a class="doclink" href="https://spec.openapis.org/oas/v3.0.0#link-object">OpenAPI Specification &gt; Link Object</a>
061 *    <li class='link'><a class="doclink" href="https://swagger.io/docs/specification/links/">OpenAPI Links</a>
062 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanOpenApi3">juneau-bean-openapi-v3</a>
063 * </ul>
064 */
065public class Link extends OpenApiElement {
066
067   private String operationRef;
068   private String operationId;
069   private String description;
070   private Object requestBody;
071   private Server server;
072   private Map<String,Object> parameters;
073
074   /**
075    * Default constructor.
076    */
077   public Link() {}
078
079   /**
080    * Copy constructor.
081    *
082    * @param copyFrom The object to copy.
083    */
084   public Link(Link copyFrom) {
085      super(copyFrom);
086
087      this.operationRef = copyFrom.operationRef;
088      this.description = copyFrom.description;
089      this.operationId = copyFrom.operationId;
090      this.requestBody = copyFrom.requestBody;
091      this.server = copyFrom.server == null ? null : copyFrom.server.copy();
092      this.parameters = copyOf(copyFrom.parameters);
093   }
094
095   /**
096    * Make a deep copy of this object.
097    *
098    * @return A deep copy of this object.
099    */
100   public Link copy() {
101      return new Link(this);
102   }
103
104   /**
105    * Bean property getter:  <property>operationRef</property>.
106    *
107    * <p>
108    * The identifying name of the contact person/organization.
109    *
110    * @return The property value, or <jk>null</jk> if it is not set.
111    */
112   public String getOperationRef() {
113      return operationRef;
114   }
115
116   /**
117    * Bean property setter:  <property>operationRef</property>.
118    *
119    * <p>
120    * The identifying name of the contact person/organization.
121    *
122    * @param value
123    *    The new value for this property.
124    *    <br>Can be <jk>null</jk> to unset the property.
125    * @return This object
126    */
127   public Link setOperationRef(String value) {
128      operationRef = value;
129      return this;
130   }
131
132   /**
133    * Bean property getter:  <property>description</property>.
134    *
135    * <p>
136    * The URL pointing to the contact information.
137    *
138    * @return The property value, or <jk>null</jk> if it is not set.
139    */
140   public String getDescription() {
141      return description;
142   }
143
144   /**
145    * Bean property setter:  <property>description</property>.
146    * @param value
147    *    The new value for this property.
148    *    <br>Can be <jk>null</jk> to unset the property.
149    * @return This object
150    */
151   public Link setDescription(String value) {
152      description = value;
153      return this;
154   }
155
156   /**
157    * Bean property getter:  <property>externalValue</property>.
158    *
159    * <p>
160    * The email address of the contact person/organization.
161    *
162    * @return The property value, or <jk>null</jk> if it is not set.
163    */
164   public String getOperationId() {
165      return operationId;
166   }
167
168   /**
169    * Bean property setter:  <property>externalValue</property>.
170    *
171    * <p>
172    * The email address of the contact person/organization.
173    *
174    * @param value
175    *    The new value for this property.
176    *    <br>MUST be in the format of an email address.
177    *    <br>Can be <jk>null</jk> to unset the property.
178    * @return This object
179    */
180   public Link setOperationId(String value) {
181      operationId = value;
182      return this;
183   }
184
185   /**
186    * Bean property getter:  <property>default</property>.
187    *
188    * <p>
189    * Declares the value of the parameter that the server will use if none is provided, for example a <js>"count"</js>
190    * to control the number of results per page might default to 100 if not supplied by the client in the request.
191    *
192    * (Note: <js>"value"</js> has no meaning for required parameters.)
193    * Unlike JSON Schema this value MUST conform to the defined <code>type</code> for this parameter.
194    *
195    * @return The property value, or <jk>null</jk> if it is not set.
196    */
197   public Object getRequestBody() {
198      return requestBody;
199   }
200
201   /**
202    * Bean property setter:  <property>value</property>.
203    *
204    * <p>
205    * Declares the value of the parameter that the server will use if none is provided, for example a <js>"count"</js>
206    * to control the number of results per page might default to 100 if not supplied by the client in the request.
207    * (Note: <js>"default"</js> has no meaning for required parameters.)
208    * Unlike JSON Schema this value MUST conform to the defined <code>type</code> for this parameter.
209    *
210    * @param val The new value for this property.
211    *    <br>Can be <jk>null</jk> to unset the property.
212    * @return This object
213    */
214   public Link setRequestBody(Object val) {
215      requestBody = val;
216      return this;
217   }
218
219   /**
220    * Bean property getter:  <property>additionalProperties</property>.
221    *
222    * @return The property value, or <jk>null</jk> if it is not set.
223    */
224   public Server getServer() {
225      return server;
226   }
227
228   /**
229    * Bean property setter:  <property>additionalProperties</property>.
230    *
231    * @param value
232    *    The new value for this property.
233    *    <br>Can be <jk>null</jk> to unset the property.
234    * @return This object
235    */
236   public Link setServer(Server value) {
237      server = value;
238      return this;
239   }
240
241   /**
242    * Bean property getter:  <property>examples</property>.
243    *
244    * <p>
245    * An example of the response message.
246    *
247    * @return The property value, or <jk>null</jk> if it is not set.
248    */
249   public Map<String,Object> getParameters() {
250      return parameters;
251   }
252
253   /**
254    * Bean property setter:  <property>examples</property>.
255    *
256    * <p>
257    * An example of the response message.
258    *
259    * @param value
260    *    The new value for this property.
261    *    <br>Keys must be MIME-type strings.
262    *    <br>Can be <jk>null</jk> to unset the property.
263    * @return This object
264    */
265   public Link setParameters(Map<String,Object> value) {
266      parameters = copyOf(value);
267      return this;
268   }
269
270   /**
271    * Adds a single value to the <property>examples</property> property.
272    *
273    * @param mimeType The mime-type string.  Must not be <jk>null</jk>.
274    * @param parameter The example.  Must not be <jk>null</jk>.
275    * @return This object
276    */
277   public Link addParameter(String mimeType, Object parameter) {
278      assertArgNotNull("mimeType", mimeType);
279      assertArgNotNull("parameter", parameter);
280      parameters = mapBuilder(parameters).sparse().add(mimeType, parameter).build();
281      return this;
282   }
283
284   @Override /* Overridden from OpenApiElement */
285   public <T> T get(String property, Class<T> type) {
286      assertArgNotNull("property", property);
287      return switch (property) {
288         case "description" -> toType(getDescription(), type);
289         case "operationRef" -> toType(getOperationRef(), type);
290         case "operationId" -> toType(getOperationId(), type);
291         case "requestBody" -> toType(getRequestBody(), type);
292         case "parameters" -> toType(getParameters(), type);
293         case "server" -> toType(getServer(), type);
294         default -> super.get(property, type);
295      };
296   }
297
298   @Override /* Overridden from OpenApiElement */
299   public Link set(String property, Object value) {
300      assertArgNotNull("property", property);
301      return switch (property) {
302         case "description" -> setDescription(Utils.s(value));
303         case "operationId" -> setOperationId(Utils.s(value));
304         case "operationRef" -> setOperationRef(Utils.s(value));
305         case "parameters" -> setParameters(mapBuilder(String.class, Object.class).sparse().addAny(value).build());
306         case "requestBody" -> setRequestBody(value);
307         case "server" -> setServer(toType(value, Server.class));
308         default -> {
309            super.set(property, value);
310            yield this;
311         }
312      };
313   }
314
315   @Override /* Overridden from OpenApiElement */
316   public Set<String> keySet() {
317      var s = setBuilder(String.class)
318         .addIf(description != null, "description")
319         .addIf(operationId != null, "operationId")
320         .addIf(operationRef != null, "operationRef")
321         .addIf(parameters != null, "parameters")
322         .addIf(requestBody != null, "requestBody")
323         .addIf(server != null, "server")
324         .build();
325      return new MultiSet<>(s, super.keySet());
326   }
327
328   @Override /* Overridden from OpenApiElement */
329   public Link strict() {
330      super.strict();
331      return this;
332   }
333
334   @Override /* Overridden from OpenApiElement */
335   public Link strict(Object value) {
336      super.strict(value);
337      return this;
338   }
339
340}