Skip to content

Commit

Permalink
Fix diverging axum route and openapi spec (#1199)
Browse files Browse the repository at this point in the history
Unify the `axum::Router::nest` functionality with `utoipa_axum::Router::nest` functionality while keeping the base implementation same as before, default to `utoipa`.

---------

Co-authored-by: Benjamin Mollier <[email protected]>
Co-authored-by: Juha Kukkonen <[email protected]>
  • Loading branch information
3 people authored Nov 21, 2024
1 parent b5a0b11 commit 7cf06e6
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 5 deletions.
6 changes: 6 additions & 0 deletions utoipa-axum/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog - utoipa-axum

## Unreleased

### Fixed

* Fix diverging axum route and openapi spec (https://github.com/juhaku/utoipa/pull/1199)

## 0.1.2 - Oct 29 2024

### Changed
Expand Down
2 changes: 1 addition & 1 deletion utoipa-axum/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ mod tests {
let paths = router.to_openapi().paths;
let expected_paths = utoipa::openapi::path::PathsBuilder::new()
.path(
"/api/customer/",
"/api/customer",
utoipa::openapi::PathItem::new(
utoipa::openapi::path::HttpMethod::Get,
utoipa::openapi::path::OperationBuilder::new()
Expand Down
22 changes: 20 additions & 2 deletions utoipa-axum/src/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,8 +320,26 @@ where
/// .nest("/api", search_router);
/// ```
pub fn nest(self, path: &str, router: OpenApiRouter<S>) -> Self {
let api = self.1.nest(path_template(path), router.1);
let path = if path.is_empty() { "/" } else { path };
// from axum::routing::path_router::path_for_nested_route
// method is private, so we need to replicate it here
fn path_for_nested_route<'a>(prefix: &'a str, path: &'a str) -> String {
debug_assert!(prefix.starts_with('/'));
debug_assert!(path.starts_with('/'));

if prefix.ends_with('/') {
format!("{prefix}{}", path.trim_start_matches('/')).into()
} else if path == "/" {
prefix.into()
} else {
format!("{prefix}{path}").into()
}
}

let api = self.1.nest_with_path_composer(
path_for_nested_route(path, "/"),
router.1,
|a: &str, b: &str| path_for_nested_route(a, b),
);
let router = self.0.nest(&colonized_params(path), router.0);

Self(router, api)
Expand Down
6 changes: 6 additions & 0 deletions utoipa/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
**`utoipa`** is in direct correlation with **`utoipa-gen`** ([CHANGELOG.md](../utoipa-gen/CHANGELOG.md)). You might want
to look into changes introduced to **`utoipa-gen`**.

## Unreleased

### Fixed

* Fix diverging axum route and openapi spec (https://github.com/juhaku/utoipa/pull/1199)

## 5.2.0 - Nov 2024

### Changed
Expand Down
23 changes: 21 additions & 2 deletions utoipa/src/openapi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,26 @@ impl OpenApi {
/// .build();
/// let nested = api.nest("/api/v1/user", user_api);
/// ```
pub fn nest<P: Into<String>, O: Into<OpenApi>>(mut self, path: P, other: O) -> Self {
pub fn nest<P: Into<String>, O: Into<OpenApi>>(self, path: P, other: O) -> Self {
self.nest_with_path_composer(path, other, |base, path| format!("{base}{path}"))
}

/// Nest `other` [`OpenApi`] with custom path composer.
///
/// In most cases you should use [`OpenApi::nest`] instead.
/// Only use this method if you need custom path composition for a specific use case.
///
/// `composer` is a function that takes two strings, the base path and the path to nest, and returns the composed path for the API Specification.
pub fn nest_with_path_composer<
P: Into<String>,
O: Into<OpenApi>,
F: Fn(&str, &str) -> String,
>(
mut self,
path: P,
other: O,
composer: F,
) -> Self {
let path: String = path.into();
let mut other_api: OpenApi = other.into();

Expand All @@ -288,7 +307,7 @@ impl OpenApi {
.paths
.into_iter()
.map(|(item_path, item)| {
let path = format!("{path}{item_path}");
let path = composer(&path, &item_path);
(path, item)
})
.collect::<PathsMap<_, _>>();
Expand Down

0 comments on commit 7cf06e6

Please sign in to comment.