Skip to content

Commit

Permalink
#297 - Allow using default and range matched status responses (#298)
Browse files Browse the repository at this point in the history
Originally only explisit status codes were allowed. This commit addresses this by adding 
a capability to allow using status code ranges like `"4XX"` or `"2XX"` or to define a 
`"default"` response for an operation. See more details https://swagger.io/specification/#responses-object. 
Update tests and docs about changed behaviour.
  • Loading branch information
jacob-pro committed Sep 25, 2022
1 parent 84f969e commit f3f1186
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 6 deletions.
3 changes: 2 additions & 1 deletion utoipa-gen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,7 @@ pub fn derive_to_schema(input: TokenStream) -> TokenStream {
///
/// # Responses Attributes
///
/// * `status = ...` Is valid http status code. E.g. _`200`_
/// * `status = ...` Is either a valid http status code integer. E.g. _`200`_ or a string value representing a range such as `"4XX"` or `"default"`.
/// * `description = "..."` Define description for the response as str.
/// * `body = ...` Optional response body object type. When left empty response does not expect to send any
/// response body. Should be an identifier or slice. E.g _`Pet`_ or _`[Pet]`_. Where the type implments [`ToSchema`][to_schema],
Expand All @@ -524,6 +524,7 @@ pub fn derive_to_schema(input: TokenStream) -> TokenStream {
/// responses(
/// (status = 200, description = "success response"),
/// (status = 404, description = "resource missing"),
/// (status = "5XX", description = "server error"),
/// )
/// ```
///
Expand Down
26 changes: 22 additions & 4 deletions utoipa-gen/src/path/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ impl Parse for Response<'_> {
#[derive(Default)]
#[cfg_attr(feature = "debug", derive(Debug))]
pub struct ResponseValue<'r> {
status_code: i32,
status_code: String,
description: String,
response_type: Option<Type<'r>>,
content_type: Option<Vec<String>>,
Expand All @@ -48,6 +48,9 @@ pub struct ResponseValue<'r> {
impl Parse for ResponseValue<'_> {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
const EXPECTED_ATTRIBUTE_MESSAGE: &str = "unexpected attribute, expected any of: status, description, body, content_type, headers";
const VALID_STATUS_RANGES: &[&str] = &["default", "1XX", "2XX", "3XX", "4XX", "5XX"];
const INVALID_STATUS_RANGE_MESSAGE: &str = "Invalid status range, expected one of:";

let mut response = ResponseValue::default();

while !input.is_empty() {
Expand All @@ -62,8 +65,23 @@ impl Parse for ResponseValue<'_> {
match attribute_name {
"status" => {
response.status_code =
parse_utils::parse_next(input, || input.parse::<LitInt>())?
.base10_parse()?;
parse_utils::parse_next(input, || {
let lookahead = input.lookahead1();
if lookahead.peek(LitInt) {
input.parse::<LitInt>()?.base10_parse()
} else if lookahead.peek(LitStr) {
let value = input.parse::<LitStr>()?.value();
if !VALID_STATUS_RANGES.contains(&value.as_str()) {
return Err(Error::new(
input.span(),
format!("{} {}", INVALID_STATUS_RANGE_MESSAGE, VALID_STATUS_RANGES.join(", ")),
))
}
Ok(value)
} else {
Err(lookahead.error())
}
})?
}
"description" => {
response.description = parse_utils::parse_next_literal_str(input)?;
Expand Down Expand Up @@ -176,7 +194,7 @@ impl ToTokens for Responses<'_> {
})
}
Response::Value(response) => {
let code = &response.status_code.to_string();
let code = &response.status_code;
acc.extend(quote! { .response(#code, #response) });
}
}
Expand Down
10 changes: 9 additions & 1 deletion utoipa-gen/tests/path_response_derive_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@ test_fn! {
(status = 200, description = "success"),
(status = 401, description = "unauthorized"),
(status = 404, description = "not found"),
(status = 500, description = "server error")
(status = 500, description = "server error"),
(status = "5XX", description = "all other server errors"),
(status = "default", description = "default")
)
}

Expand All @@ -79,6 +81,12 @@ fn derive_path_with_multiple_simple_responses() {
"responses.500.description" = r#""server error""#, "Response description"
"responses.500.content" = r#"null"#, "Response content"
"responses.500.headers" = r#"null"#, "Response headers"
"responses.5XX.description" = r#""all other server errors""#, "Response description"
"responses.5XX.content" = r#"null"#, "Response content"
"responses.5XX.headers" = r#"null"#, "Response headers"
"responses.default.description" = r#""default""#, "Response description"
"responses.default.content" = r#"null"#, "Response content"
"responses.default.headers" = r#"null"#, "Response headers"
}
}

Expand Down

0 comments on commit f3f1186

Please sign in to comment.