Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow consumers to respond with freeform bodies and a particular status code #310

Merged
merged 6 commits into from
Apr 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,13 @@ https://github.com/oxidecomputer/dropshot/compare/v0.6.0\...HEAD[Full list of co
=== Other notable changes

* https://github.com/oxidecomputer/dropshot/pull/198[#198] Responses that used `()` (the unit type) as their `Body` type parameter previously (and inaccurately) were represented in OpenAPI as an empty `responseBody`. They are now more accurately represented as a body whose value is `null` (4 bytes). We encourage those use cases to instead use either `HttpResponseUpdatedNoContent` or `HttpResponseDeleted` both of which have empty response bodies. If there are other situations where you would like a response type with no body, please file an issue.
* https://github.com/oxidecomputer/dropshot/pull/252[#252] Endpoints specified with the `#[endpoint ..]` attribute macro now use the first line of a doc comment as the OpenAPI `summary` and subsequent lines as the `description`. Previously all lines were used as the `description`.
* https://github.com/oxidecomputer/dropshot/pull/252[#252] Endpoints specified with the `##[endpoint ..]` attribute macro now use the first line of a doc comment as the OpenAPI `summary` and subsequent lines as the `description`. Previously all lines were used as the `description`.
* https://github.com/oxidecomputer/dropshot/pull/260[#260] Pulls in a newer serde that changes error messages around parsing NonZeroU32.
* https://github.com/oxidecomputer/dropshot/pull/283[#283] Add support for response headers with the `HttpResponseHeaders` type. Headers may either be defined by a struct type parameter (in which case they appear in the OpenAPI output) or *ad-hoc* added via `HttpResponseHeaders::headers_mut()`.
* https://github.com/oxidecomputer/dropshot/pull/286[#286] OpenAPI output includes descriptions of 4xx and 5xx error responses.
* https://github.com/oxidecomputer/dropshot/pull/296[#296] `ApiDescription` includes a `tag_config` method to specify both predefined tags with descriptions and links as well as a tag policy to ensure that endpoints, for example, only use predefined tags or have at least one tag.
* https://github.com/oxidecomputer/dropshot/pull/317[#317] Allow use of usdt probes with stable Rust. Dropshot consumers can build with USDT probes enabled on stable compilers >= 1.59 (except on MacOS).
* https://github.com/oxidecomputer/dropshot/pull/310[#310] Freeform (and streaming) response bodies may be specified with specific HTTP response codes e.g. by having an endpoint return `Result<HttpResponseOk<FreeformBody>, HttpError>`.

== 0.6.0 (released 2021-11-18)

Expand Down
73 changes: 33 additions & 40 deletions dropshot/src/api_description.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ impl<'a, Context: ServerContext> ApiEndpoint<Context> {
ResponseType: HttpResponse + Send + Sync + 'static,
{
let func_parameters = FuncParams::metadata();
let response = ResponseType::metadata();
let response = ResponseType::response_metadata();
ApiEndpoint {
operation_id,
handler: HttpRouteHandler::new(handler),
Expand Down Expand Up @@ -670,7 +670,7 @@ impl<Context: ServerContext> ApiDescription<Context> {
);
}

if let Some(schema) = &endpoint.response.schema {
let response = if let Some(schema) = &endpoint.response.schema {
let (name, js) = match schema {
ApiSchemaGenerator::Gen { name, schema } => {
(Some(name()), schema(&mut generator))
Expand Down Expand Up @@ -745,36 +745,7 @@ impl<Context: ServerContext> ApiDescription<Context> {
headers,
..Default::default()
};

match &endpoint.response.success {
None => {
// Without knowing the specific status code we use the
// 2xx range.
operation.responses.responses.insert(
openapiv3::StatusCode::Range(2),
openapiv3::ReferenceOr::Item(response),
);
}
Some(code) => {
operation.responses.responses.insert(
openapiv3::StatusCode::Code(code.as_u16()),
openapiv3::ReferenceOr::Item(response),
);
}
}

// 4xx and 5xx responses all use the same error information
let err_ref = openapiv3::ReferenceOr::ref_(
"#/components/responses/Error",
);
operation
.responses
.responses
.insert(openapiv3::StatusCode::Range(4), err_ref.clone());
operation
.responses
.responses
.insert(openapiv3::StatusCode::Range(5), err_ref);
response
} else {
// If no schema was specified, the response is hand-rolled. In
// this case we'll fall back to the default response type which
Expand All @@ -795,15 +766,37 @@ impl<Context: ServerContext> ApiDescription<Context> {
..Default::default()
},
);
openapiv3::Response {
// TODO: perhaps we should require even free-form
// responses to have a description since it's required
// by OpenAPI.
description: "".to_string(),
content,
..Default::default()
}
};

if let Some(code) = &endpoint.response.success {
operation.responses.responses.insert(
openapiv3::StatusCode::Code(code.as_u16()),
openapiv3::ReferenceOr::Item(response),
);

// 4xx and 5xx responses all use the same error information
let err_ref = openapiv3::ReferenceOr::ref_(
"#/components/responses/Error",
);
operation
.responses
.responses
.insert(openapiv3::StatusCode::Range(4), err_ref.clone());
operation
.responses
.responses
.insert(openapiv3::StatusCode::Range(5), err_ref);
} else {
operation.responses.default =
Some(openapiv3::ReferenceOr::Item(openapiv3::Response {
// TODO: perhaps we should require even free-form
// responses to have a description since it's required
// by OpenAPI.
description: "".to_string(),
content,
..Default::default()
}));
Some(openapiv3::ReferenceOr::Item(response))
}

// Drop in the operation.
Expand Down
Loading