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.commons.utils.Shorts.*; 021import static org.apache.juneau.marshall.internal.ConverterUtils.*; 022 023import java.util.*; 024 025import org.apache.juneau.commons.collections.*; 026 027/** 028 * Describes a single response from an API operation. 029 * 030 * <p> 031 * The Response Object describes a single response from an API operation, including a description, headers, content, and links. 032 * Responses are returned based on the HTTP status code, with the most common being success responses (2xx), redirects (3xx), 033 * client errors (4xx), and server errors (5xx). 034 * 035 * <h5 class='section'>OpenAPI Specification:</h5> 036 * <p> 037 * The Response Object is composed of the following fields: 038 * <ul class='spaced-list'> 039 * <li><c>description</c> (string, REQUIRED) - A short description of the response (CommonMark syntax may be used) 040 * <li><c>headers</c> (map of {@link HeaderInfo}) - Maps a header name to its definition 041 * <li><c>content</c> (map of {@link MediaType}) - A map containing descriptions of potential response payloads (keys are media types) 042 * <li><c>links</c> (map of {@link Link}) - A map of operations links that can be followed from the response 043 * </ul> 044 * 045 * <h5 class='section'>Example:</h5> 046 * <p class='bjava'> 047 * <jc>// Create a successful response with JSON content</jc> 048 * Response <jv>response</jv> = <jk>new</jk> Response() 049 * .setDescription(<js>"A list of pets"</js>) 050 * .setContent( 051 * JsonMap.<jsm>of</jsm>( 052 * <js>"application/json"</js>, <jk>new</jk> MediaType() 053 * .setSchema( 054 * <jk>new</jk> SchemaInfo() 055 * .setType(<js>"array"</js>) 056 * .setItems(<jk>new</jk> Items().setRef(<js>"#/components/schemas/Pet"</js>)) 057 * ) 058 * ) 059 * ) 060 * .setHeaders( 061 * JsonMap.<jsm>of</jsm>( 062 * <js>"X-Rate-Limit"</js>, <jk>new</jk> HeaderInfo() 063 * .setDescription(<js>"Requests per hour allowed by the user"</js>) 064 * .setSchema(<jk>new</jk> SchemaInfo().setType(<js>"integer"</js>)) 065 * ) 066 * ); 067 * </p> 068 * 069 * <h5 class='section'>See Also:</h5><ul> 070 * <li class='link'><a class="doclink" href="https://spec.openapis.org/oas/v3.0.0#response-object">OpenAPI Specification > Response Object</a> 071 * <li class='link'><a class="doclink" href="https://swagger.io/docs/specification/describing-responses/">OpenAPI Describing Responses</a> 072 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanOpenApi3">juneau-bean-openapi-v3</a> 073 * </ul> 074 */ 075@SuppressWarnings({ 076 "java:S1192" // Duplicated literals (argument/property names) read more clearly inline than as constants 077}) 078public class Response extends OpenApiElement { 079 080 private String description; 081 private final Map<String,HeaderInfo> headers = map(); 082 private final Map<String,MediaType> content = map(); 083 private final Map<String,Link> links = map(); 084 085 /** 086 * Default constructor. 087 */ 088 public Response() {} 089 090 /** 091 * Copy constructor. 092 * 093 * @param copyFrom The object to copy. 094 */ 095 public Response(Response copyFrom) { 096 super(copyFrom); 097 098 this.description = copyFrom.description; 099 headers.putAll(cp(copyFrom.headers, HeaderInfo::copy)); 100 content.putAll(cp(copyFrom.content, MediaType::copy)); 101 links.putAll(cp(copyFrom.links, Link::copy)); 102 } 103 104 /** 105 * Adds one or more values to the <property>content</property> property. 106 * 107 * @param key The mapping key. Must not be <jk>null</jk>, or an {@link IllegalArgumentException} is thrown. 108 * @param value The values to add to this property. Must not be <jk>null</jk>, or an {@link IllegalArgumentException} is thrown. 109 * @return This object 110 */ 111 public Response addContent(String key, MediaType value) { 112 reqnn("key", key); 113 reqnn("value", value); 114 content.put(key, value); 115 return this; 116 } 117 118 /** 119 * Adds one or more values to the <property>headers</property> property. 120 * 121 * @param key The mapping key. Must not be <jk>null</jk>, or an {@link IllegalArgumentException} is thrown. 122 * @param value The values to add to this property. Must not be <jk>null</jk>, or an {@link IllegalArgumentException} is thrown. 123 * @return This object 124 */ 125 public Response addHeader(String key, HeaderInfo value) { 126 reqnn("key", key); 127 reqnn("value", value); 128 headers.put(key, value); 129 return this; 130 } 131 132 /** 133 * Adds one or more values to the <property>links</property> property. 134 * 135 * @param key The mapping key. Must not be <jk>null</jk>, or an {@link IllegalArgumentException} is thrown. 136 * @param value The values to add to this property. Must not be <jk>null</jk>, or an {@link IllegalArgumentException} is thrown. 137 * @return This object 138 */ 139 public Response addLink(String key, Link value) { 140 reqnn("key", key); 141 reqnn("value", value); 142 links.put(key, value); 143 return this; 144 } 145 146 /** 147 * Make a deep copy of this object. 148 * 149 * @return A deep copy of this object. 150 */ 151 public Response copy() { 152 return new Response(this); 153 } 154 155 @Override /* Overridden from OpenApiElement */ 156 public <T> T get(String property, Class<T> type) { 157 reqnn("property", property); 158 return switch (property) { 159 case "description" -> toType(getDescription(), type); 160 case "content" -> toType(getContent(), type); 161 case "headers" -> toType(getHeaders(), type); 162 case "links" -> toType(getLinks(), type); 163 default -> super.get(property, type); 164 }; 165 } 166 167 /** 168 * Bean property getter: <property>content</property>. 169 * 170 * @return The property value, or <jk>null</jk> if it is not set. 171 */ 172 public Map<String,MediaType> getContent() { return nie(content); } 173 174 /** 175 * Returns the content with the specified media type. 176 * 177 * @param mediaType The media type. Must not be <jk>null</jk>, or an {@link IllegalArgumentException} is thrown. 178 * @return The media type info, or <jk>null</jk> if not found. 179 */ 180 public MediaType getContent(String mediaType) { 181 reqnn("mediaType", mediaType); 182 return content.get(mediaType); 183 } 184 185 /** 186 * Bean property getter: <property>description</property>. 187 * 188 * <p> 189 * A short description of the response. 190 * 191 * @return The property value, or <jk>null</jk> if it is not set. 192 */ 193 public String getDescription() { return description; } 194 195 /** 196 * Returns the header with the specified name. 197 * 198 * @param name The header name. Must not be <jk>null</jk>, or an {@link IllegalArgumentException} is thrown. 199 * @return The header info, or <jk>null</jk> if not found. 200 */ 201 public HeaderInfo getHeader(String name) { 202 reqnn("name", name); 203 return headers.get(name); 204 } 205 206 /** 207 * Bean property getter: <property>headers</property>. 208 * 209 * @return The property value, or <jk>null</jk> if it is not set. 210 */ 211 public Map<String,HeaderInfo> getHeaders() { return nie(headers); } 212 213 /** 214 * Returns the link with the specified name. 215 * 216 * @param name The link name. Must not be <jk>null</jk>, or an {@link IllegalArgumentException} is thrown. 217 * @return The link info, or <jk>null</jk> if not found. 218 */ 219 public Link getLink(String name) { 220 reqnn("name", name); 221 return links.get(name); 222 } 223 224 /** 225 * Bean property getter: <property>links</property>. 226 * 227 * @return The property value, or <jk>null</jk> if it is not set. 228 */ 229 public Map<String,Link> getLinks() { return nie(links); } 230 231 @Override /* Overridden from OpenApiElement */ 232 public Set<String> keySet() { 233 // @formatter:off 234 var s = stb(String.class) 235 .addIf(ine(content), "content") 236 .addIf(nn(description), "description") 237 .addIf(ine(headers), "headers") 238 .addIf(ine(links), "links") 239 .build(); 240 // @formatter:on 241 return new MultiSet<>(s, super.keySet()); 242 } 243 244 @Override /* Overridden from OpenApiElement */ 245 public Response set(String property, Object value) { 246 reqnn("property", property); 247 return switch (property) { 248 case "content" -> setContent(toMapBuilder(value, String.class, MediaType.class).sparse().build()); 249 case "description" -> setDescription(s(value)); 250 case "headers" -> setHeaders(toMapBuilder(value, String.class, HeaderInfo.class).sparse().build()); 251 case "links" -> setLinks(toMapBuilder(value, String.class, Link.class).sparse().build()); 252 default -> { 253 super.set(property, value); 254 yield this; 255 } 256 }; 257 } 258 259 /** 260 * Bean property setter: <property>content</property>. 261 * 262 * @param value 263 * The new value for this property. 264 * <br>Can be <jk>null</jk> to unset the property. 265 * @return This object 266 */ 267 public Response setContent(Map<String,MediaType> value) { 268 content.clear(); 269 if (nn(value)) 270 content.putAll(value); 271 return this; 272 } 273 274 /** 275 * Bean property setter: <property>description</property>. 276 * 277 * <p> 278 * A short description of the response. 279 * 280 * @param value 281 * The new value for this property. 282 * <br>Can be <jk>null</jk> to unset the property. 283 * @return This object 284 */ 285 public Response setDescription(String value) { 286 description = value; 287 return this; 288 } 289 290 /** 291 * Bean property setter: <property>headers</property>. 292 * 293 * @param value 294 * The new value for this property. 295 * <br>Can be <jk>null</jk> to unset the property. 296 * @return This object 297 */ 298 public Response setHeaders(Map<String,HeaderInfo> value) { 299 headers.clear(); 300 if (nn(value)) 301 headers.putAll(value); 302 return this; 303 } 304 305 /** 306 * Bean property setter: <property>links</property>. 307 * 308 * @param value 309 * The new value for this property. 310 * <br>Can be <jk>null</jk> to unset the property. 311 * @return This object 312 */ 313 public Response setLinks(Map<String,Link> value) { 314 links.clear(); 315 if (nn(value)) 316 links.putAll(value); 317 return this; 318 } 319 320 @Override /* Overridden from OpenApiElement */ 321 public Response strict(Object value) { 322 super.strict(value); 323 return this; 324 } 325 326 @Override /* Overridden from OpenApiElement */ 327 protected Response strict() { 328 super.strict(); 329 return this; 330 } 331}