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.CollectionUtils.*;
020import static org.apache.juneau.bean.openapi3.OpenApiCopyUtils.*;
021import static org.apache.juneau.commons.utils.Shorts.*;
022import static org.apache.juneau.marshall.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 */
065@SuppressWarnings({
066   "java:S1192" // Duplicated literals (argument/property names) read more clearly inline than as constants
067})
068public class Link extends OpenApiElement {
069
070   private String operationRef;
071   private String operationId;
072   private String description;
073   private Object requestBody;
074   private Server server;
075   private final Map<String,Object> parameters = map();
076
077   /**
078    * Default constructor.
079    */
080   public Link() {}
081
082   /**
083    * Copy constructor.
084    *
085    * @param copyFrom The object to copy.
086    */
087   public Link(Link copyFrom) {
088      super(copyFrom);
089
090      this.operationRef = copyFrom.operationRef;
091      this.description = copyFrom.description;
092      this.operationId = copyFrom.operationId;
093      this.requestBody = copyFrom.requestBody;
094      this.server = copyOf(copyFrom.server);
095      parameters.putAll(copyFrom.parameters);
096   }
097
098   /**
099    * Adds a single value to the <property>parameters</property> property.
100    *
101    * @param mimeType The parameter name.  Must not be <jk>null</jk>, or an {@link IllegalArgumentException} is thrown.
102    * @param parameter The parameter value.  Must not be <jk>null</jk>, or an {@link IllegalArgumentException} is thrown.
103    * @return This object
104    */
105   public Link addParameter(String mimeType, Object parameter) {
106      reqnn("mimeType", mimeType);
107      reqnn("parameter", parameter);
108      parameters.put(mimeType, parameter);
109      return this;
110   }
111
112   /**
113    * Make a deep copy of this object.
114    *
115    * @return A deep copy of this object.
116    */
117   public Link copy() {
118      return new Link(this);
119   }
120
121   @Override /* Overridden from OpenApiElement */
122   public <T> T get(String property, Class<T> type) {
123      reqnn("property", property);
124      return switch (property) {
125         case "description" -> toType(getDescription(), type);
126         case "operationRef" -> toType(getOperationRef(), type);
127         case "operationId" -> toType(getOperationId(), type);
128         case "requestBody" -> toType(getRequestBody(), type);
129         case "parameters" -> toType(getParameters(), type);
130         case "server" -> toType(getServer(), type);
131         default -> super.get(property, type);
132      };
133   }
134
135   /**
136    * Bean property getter:  <property>description</property>.
137    *
138    * <p>
139    * A description of the link.
140    *
141    * @return The property value, or <jk>null</jk> if it is not set.
142    */
143   public String getDescription() { return description; }
144
145   /**
146    * Bean property getter:  <property>operationId</property>.
147    *
148    * <p>
149    * The name of an existing, resolvable OAS operation.
150    *
151    * @return The property value, or <jk>null</jk> if it is not set.
152    */
153   public String getOperationId() { return operationId; }
154
155   /**
156    * Bean property getter:  <property>operationRef</property>.
157    *
158    * <p>
159    * A relative or absolute reference to an OAS operation.
160    *
161    * @return The property value, or <jk>null</jk> if it is not set.
162    */
163   public String getOperationRef() { return operationRef; }
164
165   /**
166    * Bean property getter:  <property>parameters</property>.
167    *
168    * <p>
169    * A map representing parameters to pass to an operation.
170    *
171    * @return The property value, or <jk>null</jk> if it is not set.
172    */
173   public Map<String,Object> getParameters() { return nie(parameters); }
174
175   /**
176    * Bean property getter:  <property>requestBody</property>.
177    *
178    * <p>
179    * A literal value or expression to use as a request body when calling the target operation.
180    *
181    * @return The property value, or <jk>null</jk> if it is not set.
182    */
183   public Object getRequestBody() { return requestBody; }
184
185   /**
186    * Bean property getter:  <property>server</property>.
187    *
188    * @return The property value, or <jk>null</jk> if it is not set.
189    */
190   public Server getServer() { return server; }
191
192   @Override /* Overridden from OpenApiElement */
193   public Set<String> keySet() {
194      // @formatter:off
195      var s = stb(String.class)
196         .addIf(nn(description), "description")
197         .addIf(nn(operationId), "operationId")
198         .addIf(nn(operationRef), "operationRef")
199         .addIf(ine(parameters), "parameters")
200         .addIf(nn(requestBody), "requestBody")
201         .addIf(nn(server), "server")
202         .build();
203      // @formatter:on
204      return new MultiSet<>(s, super.keySet());
205   }
206
207   @Override /* Overridden from OpenApiElement */
208   public Link set(String property, Object value) {
209      reqnn("property", property);
210      return switch (property) {
211         case "description" -> setDescription(s(value));
212         case "operationId" -> setOperationId(s(value));
213         case "operationRef" -> setOperationRef(s(value));
214         case "parameters" -> setParameters(toMapBuilder(value, String.class, Object.class).sparse().build());
215         case "requestBody" -> setRequestBody(value);
216         case "server" -> setServer(toType(value, Server.class));
217         default -> {
218            super.set(property, value);
219            yield this;
220         }
221      };
222   }
223
224   /**
225    * Bean property setter:  <property>description</property>.
226    * @param value
227    *    The new value for this property.
228    *    <br>Can be <jk>null</jk> to unset the property.
229    * @return This object
230    */
231   public Link setDescription(String value) {
232      description = value;
233      return this;
234   }
235
236   /**
237    * Bean property setter:  <property>operationId</property>.
238    *
239    * <p>
240    * The name of an existing, resolvable OAS operation.
241    *
242    * @param value
243    *    The new value for this property.
244    *    <br>Can be <jk>null</jk> to unset the property.
245    * @return This object
246    */
247   public Link setOperationId(String value) {
248      operationId = value;
249      return this;
250   }
251
252   /**
253    * Bean property setter:  <property>operationRef</property>.
254    *
255    * <p>
256    * A relative or absolute reference to an OAS operation.
257    *
258    * @param value
259    *    The new value for this property.
260    *    <br>Can be <jk>null</jk> to unset the property.
261    * @return This object
262    */
263   public Link setOperationRef(String value) {
264      operationRef = value;
265      return this;
266   }
267
268   /**
269    * Bean property setter:  <property>parameters</property>.
270    *
271    * <p>
272    * A map representing parameters to pass to an operation.
273    *
274    * @param value
275    *    The new value for this property.
276    *    <br>Can be <jk>null</jk> to unset the property.
277    * @return This object
278    */
279   public Link setParameters(Map<String,Object> value) {
280      parameters.clear();
281      if (nn(value))
282         parameters.putAll(value);
283      return this;
284   }
285
286   /**
287    * Bean property setter:  <property>requestBody</property>.
288    *
289    * <p>
290    * A literal value or expression to use as a request body when calling the target operation.
291    *
292    * @param val The new value for this property.
293    *    <br>Can be <jk>null</jk> to unset the property.
294    * @return This object
295    */
296   public Link setRequestBody(Object val) {
297      requestBody = val;
298      return this;
299   }
300
301   /**
302    * Bean property setter:  <property>server</property>.
303    *
304    * @param value
305    *    The new value for this property.
306    *    <br>Can be <jk>null</jk> to unset the property.
307    * @return This object
308    */
309   public Link setServer(Server value) {
310      server = value;
311      return this;
312   }
313
314   @Override /* Overridden from OpenApiElement */
315   public Link strict() {
316      super.strict();
317      return this;
318   }
319
320   @Override /* Overridden from OpenApiElement */
321   public Link strict(Object value) {
322      super.strict(value);
323      return this;
324   }
325}