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 > 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}