Skip to content

Commit

Permalink
Use function name as operation verb if operation id is missing (#483)
Browse files Browse the repository at this point in the history
  • Loading branch information
msrd0 authored May 14, 2024
1 parent 70d82ca commit 8aeb371
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 16 deletions.
27 changes: 20 additions & 7 deletions derive/src/endpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -372,18 +372,31 @@ fn expand_operation_verb(_: TokenStream) -> Option<TokenStream> {
}

#[cfg(feature = "openapi")]
fn expand_operation_id(operation_id: Option<LitStr>) -> Option<TokenStream> {
operation_id.map(|operation_id| {
quote! {
fn operation_id() -> ::core::option::Option<::std::string::String> {
::core::option::Option::Some(::std::string::String::from(#operation_id))
fn expand_operation_id(fun_ident: &Ident, operation_id: Option<LitStr>) -> Option<TokenStream> {
let op_id = match operation_id {
Some(operation_id) => quote! {
::gotham_restful::OperationId::Manual(
::std::string::String::from(#operation_id)
)
},
None => {
let verb = fun_ident.to_string();
quote! {
::gotham_restful::OperationId::SemiAuto(
::std::borrow::Cow::Borrowed(#verb)
)
}
}
};
Some(quote! {
fn operation_id() -> ::gotham_restful::OperationId {
#op_id
}
})
}

#[cfg(not(feature = "openapi"))]
fn expand_operation_id(_: Option<LitStr>) -> Option<TokenStream> {
fn expand_operation_id(_: &Ident, _: Option<LitStr>) -> Option<TokenStream> {
None
}

Expand Down Expand Up @@ -736,7 +749,7 @@ fn expand_endpoint_type(
quote!(::gotham_restful::Endpoint)
};
let operation_verb = expand_operation_verb(ty.operation_verb());
let operation_id = expand_operation_id(operation_id);
let operation_id = expand_operation_id(fun_ident, operation_id);
let wants_auth = expand_wants_auth(wants_auth, args.iter().any(|arg| arg.ty.is_auth_status()));
let code = quote! {
#[doc(hidden)]
Expand Down
6 changes: 4 additions & 2 deletions src/endpoint.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#[cfg(feature = "openapi")]
use crate::openapi::operation::OperationId;
use crate::{IntoResponse, RequestBody};
use futures_util::future::BoxFuture;
use gotham::{
Expand Down Expand Up @@ -89,8 +91,8 @@ pub trait Endpoint {
/// Replace the automatically generated operation id with a custom one. Only relevant for the
/// OpenAPI Specification.
#[openapi_only]
fn operation_id() -> Option<String> {
None
fn operation_id() -> OperationId {
OperationId::FullAuto
}

/// Add a description to the openapi specification. Usually taken from the rustdoc comment
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -564,7 +564,7 @@ pub use cors::{handle_cors, CorsConfig, CorsRoute};
#[cfg(feature = "openapi")]
mod openapi;
#[cfg(feature = "openapi")]
pub use openapi::{builder::OpenapiInfo, router::GetOpenapi};
pub use openapi::{builder::OpenapiInfo, operation::OperationId, router::GetOpenapi};

mod endpoint;
#[cfg(feature = "openapi")]
Expand Down
32 changes: 27 additions & 5 deletions src/openapi/operation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use openapi_type::{
},
OpenapiSchema
};
use std::collections::HashMap;
use std::{borrow::Cow, collections::HashMap};

fn new_parameter_data(
name: String,
Expand Down Expand Up @@ -93,6 +93,17 @@ impl OperationParams {
}
}

#[derive(Debug, Default)]
pub enum OperationId {
/// Automatically generate the operation id based on path and operation verb.
#[default]
FullAuto,
/// Automatically generate the operation id based on path and the provided string.
SemiAuto(Cow<'static, str>),
/// Use the provided operation id.
Manual(String)
}

pub(crate) struct OperationDescription {
operation_id: Option<String>,
description: Option<String>,
Expand All @@ -112,10 +123,21 @@ impl OperationDescription {
responses: HashMap<StatusCode, ReferenceOr<Schema>>,
path: &str
) -> Self {
let operation_id = E::operation_id().or_else(|| {
E::operation_verb()
.map(|verb| format!("{verb}_{}", path.replace("/", "_").trim_start_matches('_')))
});
let (mut operation_id, op_id_verb) = match E::operation_id() {
OperationId::FullAuto => (None, E::operation_verb().map(Cow::Borrowed)),
OperationId::SemiAuto(verb) => (None, Some(verb)),
OperationId::Manual(id) => (Some(id), None)
};
if let Some(verb) = op_id_verb {
let op_path = path.replace('/', "_");
let op_path = op_path.trim_start_matches('_');
if verb.starts_with(op_path) || verb.ends_with(op_path) {
operation_id = Some(verb.into_owned());
} else {
operation_id = Some(format!("{verb}_{op_path}"));
}
}

Self {
operation_id,
description: E::description(),
Expand Down
4 changes: 3 additions & 1 deletion tests/openapi_specification.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"paths": {
"/coffee": {
"get": {
"operationId": "read_all_coffee",
"operationId": "coffee_read_all",
"responses": {
"418": {
"content": {
Expand All @@ -64,6 +64,7 @@
},
"/custom": {
"patch": {
"operationId": "custom_patch",
"requestBody": {
"content": {
"application/json": {
Expand All @@ -83,6 +84,7 @@
},
"/custom/read/{from}/with/{id}": {
"get": {
"operationId": "custom_read_with",
"parameters": [
{
"in": "path",
Expand Down

0 comments on commit 8aeb371

Please sign in to comment.