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.util.*;
025
026import org.apache.juneau.commons.collections.*;
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 = map();
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      if (nn(copyFrom.parameters))
093         parameters.putAll(copyFrom.parameters);
094   }
095
096   /**
097    * Adds a single value to the <property>examples</property> property.
098    *
099    * @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}