Skip to content
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
1 change: 1 addition & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ https://github.com/oxidecomputer/dropshot/compare/v0.8.0\...HEAD[Full list of co

* https://github.com/oxidecomputer/dropshot/pull/452[#452] Dropshot no longer enables the `slog` cargo features `max_level_trace` and `release_max_level_debug`. Previously, clients were unable to set a release log level of `trace`; now they can. However, clients that did not select their own max log levels will see behavior change from the levels Dropshot was choosing to the default levels of `slog` itself (`debug` for debug builds and `info` for release builds).
* https://github.com/oxidecomputer/dropshot/pull/451[#451] There are now response types to support 302 ("Found"), 303 ("See Other"), and 307 ("Temporary Redirect") HTTP response codes. See `HttpResponseFound`, `HttpResponseSeeOther`, and `HttpResponseTemporaryRedirect`.
* https://github.com/oxidecomputer/dropshot/pull/503[#503] Add an optional `deprecated` field to the `#[endpoint]` macro.

== 0.8.0 (released 2022-09-09)

Expand Down
8 changes: 8 additions & 0 deletions dropshot/src/api_description.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ pub struct ApiEndpoint<Context: ServerContext> {
pub tags: Vec<String>,
pub extension_mode: ExtensionMode,
pub visible: bool,
pub deprecated: bool,
}

impl<'a, Context: ServerContext> ApiEndpoint<Context> {
Expand Down Expand Up @@ -80,6 +81,7 @@ impl<'a, Context: ServerContext> ApiEndpoint<Context> {
tags: vec![],
extension_mode: func_parameters.extension_mode,
visible: true,
deprecated: false,
}
}

Expand All @@ -102,6 +104,11 @@ impl<'a, Context: ServerContext> ApiEndpoint<Context> {
self.visible = visible;
self
}

pub fn deprecated(mut self, deprecated: bool) -> Self {
self.deprecated = deprecated;
self
}
}

/**
Expand Down Expand Up @@ -588,6 +595,7 @@ impl<Context: ServerContext> ApiDescription<Context> {
operation.summary = endpoint.summary.clone();
operation.description = endpoint.description.clone();
operation.tags = endpoint.tags.clone();
operation.deprecated = endpoint.deprecated;

operation.parameters = endpoint
.parameters
Expand Down
1 change: 1 addition & 0 deletions dropshot/src/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -824,6 +824,7 @@ mod test {
tags: vec![],
extension_mode: Default::default(),
visible: true,
deprecated: false,
}
}

Expand Down
30 changes: 30 additions & 0 deletions dropshot/tests/test_openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,36 @@
}
}
},
"/test/deprecated": {
"get": {
"tags": [
"it"
],
"operationId": "handler24",
"responses": {
"307": {
"description": "redirect (temporary redirect)",
"headers": {
"location": {
"description": "HTTP \"Location\" header",
"style": "simple",
"required": true,
"schema": {
"type": "string"
}
}
}
},
"4XX": {
"$ref": "#/components/responses/Error"
},
"5XX": {
"$ref": "#/components/responses/Error"
}
},
"deprecated": true
}
},
"/test/man/{x}": {
"delete": {
"tags": [
Expand Down
13 changes: 13 additions & 0 deletions dropshot/tests/test_openapi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,18 @@ async fn handler23(
Ok(http_response_temporary_redirect(String::from("/path3")).unwrap())
}

#[endpoint {
method = GET,
path = "/test/deprecated",
tags = [ "it"],
deprecated = true,
}]
async fn handler24(
_rqctx: Arc<RequestContext<()>>,
) -> Result<HttpResponseTemporaryRedirect, HttpError> {
unimplemented!()
}

fn make_api(
maybe_tag_config: Option<TagConfig>,
) -> Result<ApiDescription<()>, String> {
Expand Down Expand Up @@ -487,6 +499,7 @@ fn make_api(
api.register(handler21)?;
api.register(handler22)?;
api.register(handler23)?;
api.register(handler24)?;
Ok(api)
}

Expand Down
30 changes: 30 additions & 0 deletions dropshot/tests/test_openapi_fuller.json
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,36 @@
}
}
},
"/test/deprecated": {
"get": {
"tags": [
"it"
],
"operationId": "handler24",
"responses": {
"307": {
"description": "redirect (temporary redirect)",
"headers": {
"location": {
"description": "HTTP \"Location\" header",
"style": "simple",
"required": true,
"schema": {
"type": "string"
}
}
}
},
"4XX": {
"$ref": "#/components/responses/Error"
},
"5XX": {
"$ref": "#/components/responses/Error"
}
},
"deprecated": true
}
},
"/test/man/{x}": {
"delete": {
"tags": [
Expand Down
63 changes: 43 additions & 20 deletions dropshot_endpoint/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,12 @@ impl MethodType {
struct EndpointMetadata {
method: MethodType,
path: String,
tags: Option<Vec<String>>,
unpublished: Option<bool>,
#[serde(default)]
tags: Vec<String>,
#[serde(default)]
unpublished: bool,
#[serde(default)]
deprecated: bool,
content_type: Option<String>,
_dropshot_crate: Option<String>,
}
Expand All @@ -67,8 +71,12 @@ enum ChannelProtocol {
struct ChannelMetadata {
protocol: ChannelProtocol,
path: String,
tags: Option<Vec<String>>,
unpublished: Option<bool>,
#[serde(default)]
tags: Vec<String>,
#[serde(default)]
unpublished: bool,
#[serde(default)]
deprecated: bool,
_dropshot_crate: Option<String>,
}

Expand All @@ -94,12 +102,14 @@ const USAGE: &str = "Endpoint handlers must have the following signature:
/// method = { DELETE | GET | OPTIONS | PATCH | POST | PUT },
/// path = "/path/name/with/{named}/{variables}",
///
/// // Optional tags for the API description
/// // Optional tags for the operation's description
/// tags = [ "all", "your", "OpenAPI", "tags" ],
/// // A value of `true` causes the API to be omitted from the API description
/// unpublished = { true | false },
/// // Specifies the media type used to encode the request body
/// content_type = { "application/json" | "application/x-www-form-urlencoded" }
/// // A value of `true` marks the operation as deprecated
/// deprecated = { true | false },
/// // A value of `true` causes the operation to be omitted from the API description
/// unpublished = { true | false },
/// }]
/// ```
///
Expand Down Expand Up @@ -153,8 +163,14 @@ fn do_channel(
attr: proc_macro2::TokenStream,
item: proc_macro2::TokenStream,
) -> Result<(proc_macro2::TokenStream, Vec<Error>), Error> {
let ChannelMetadata { protocol, path, tags, unpublished, _dropshot_crate } =
from_tokenstream(&attr)?;
let ChannelMetadata {
protocol,
path,
tags,
unpublished,
deprecated,
_dropshot_crate,
} = from_tokenstream(&attr)?;
match protocol {
ChannelProtocol::WEBSOCKETS => {
// here we construct a wrapper function and mutate the arguments a bit
Expand Down Expand Up @@ -225,6 +241,7 @@ fn do_channel(
path,
tags,
unpublished,
deprecated,
content_type: Some("application/json".to_string()),
_dropshot_crate,
};
Expand Down Expand Up @@ -347,25 +364,30 @@ fn do_endpoint_inner(

let tags = metadata
.tags
.map(|v| {
v.iter()
.map(|tag| {
quote! {
.tag(#tag)
}
})
.collect::<Vec<_>>()
.iter()
.map(|tag| {
quote! {
.tag(#tag)
}
})
.unwrap_or_default();
.collect::<Vec<_>>();

let visible = if let Some(true) = metadata.unpublished {
let visible = if metadata.unpublished {
quote! {
.visible(false)
}
} else {
quote! {}
};

let deprecated = if metadata.deprecated {
quote! {
.deprecated(true)
}
} else {
quote! {}
};

let dropshot = get_crate(metadata._dropshot_crate);

let first_arg = match ast.sig.inputs.first() {
Expand Down Expand Up @@ -553,6 +575,7 @@ fn do_endpoint_inner(
#description
#(#tags)*
#visible
#deprecated
}
} else {
quote! {
Expand Down Expand Up @@ -599,7 +622,7 @@ fn do_endpoint_inner(
errors.insert(0, Error::new_spanned(&ast.sig, USAGE));
}

if path.contains(":.*}") && metadata.unpublished != Some(true) {
if path.contains(":.*}") && !metadata.unpublished {
errors.push(Error::new_spanned(
&attr,
"paths that contain a wildcard match must include 'unpublished = \
Expand Down