Skip to content

Commit 189e496

Browse files
committed
Use utoipa crate to generate and serve basic OpenAPI description
1 parent a746f03 commit 189e496

File tree

6 files changed

+99
-2
lines changed

6 files changed

+99
-2
lines changed

Cargo.lock

Lines changed: 38 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ tracing-subscriber = { version = "=0.3.19", features = ["env-filter", "json"] }
125125
typomania = { version = "=0.1.2", default-features = false }
126126
url = "=2.5.4"
127127
unicode-xid = "=0.2.6"
128+
utoipa = "=5.2.0"
129+
utoipa-axum = "=0.1.2"
128130

129131
[dev-dependencies]
130132
bytes = "=1.9.0"

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ mod licenses;
4444
pub mod metrics;
4545
pub mod middleware;
4646
pub mod models;
47+
pub mod openapi;
4748
pub mod rate_limiter;
4849
mod real_ip;
4950
mod router;

src/openapi.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
use utoipa::OpenApi;
2+
use utoipa_axum::router::OpenApiRouter;
3+
4+
#[derive(OpenApi)]
5+
pub struct BaseOpenApi;
6+
7+
impl BaseOpenApi {
8+
pub fn router<S>() -> OpenApiRouter<S>
9+
where
10+
S: Send + Sync + Clone + 'static,
11+
{
12+
OpenApiRouter::with_openapi(Self::openapi())
13+
}
14+
}
15+
16+
#[cfg(test)]
17+
mod tests {
18+
use crate::tests::util::{RequestHelper, TestApp};
19+
use http::StatusCode;
20+
use insta::assert_json_snapshot;
21+
22+
#[tokio::test(flavor = "multi_thread")]
23+
async fn test_openapi_snapshot() {
24+
let (_app, anon) = TestApp::init().empty().await;
25+
26+
let response = anon.get::<()>("/api/openapi.json").await;
27+
assert_eq!(response.status(), StatusCode::OK);
28+
assert_json_snapshot!(response.json());
29+
}
30+
}

src/router.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
11
use axum::extract::DefaultBodyLimit;
22
use axum::response::IntoResponse;
33
use axum::routing::{delete, get, post, put};
4-
use axum::Router;
4+
use axum::{Json, Router};
55
use http::{Method, StatusCode};
66

77
use crate::app::AppState;
88
use crate::controllers::user::update_user;
99
use crate::controllers::*;
10+
use crate::openapi::BaseOpenApi;
1011
use crate::util::errors::not_found;
1112
use crate::Env;
1213

1314
const MAX_PUBLISH_CONTENT_LENGTH: usize = 128 * 1024 * 1024; // 128 MB
1415

1516
pub fn build_axum_router(state: AppState) -> Router<()> {
16-
let mut router = Router::new()
17+
let (router, openapi) = BaseOpenApi::router().split_for_parts();
18+
19+
let mut router = router
1720
// Route used by both `cargo search` and the frontend
1821
.route("/api/v1/crates", get(krate::search::search))
1922
// Routes used by `cargo`
@@ -174,6 +177,7 @@ pub fn build_axum_router(state: AppState) -> Router<()> {
174177
}
175178

176179
router
180+
.route("/api/openapi.json", get(|| async { Json(openapi) }))
177181
.fallback(|method: Method| async move {
178182
match method {
179183
Method::HEAD => StatusCode::NOT_FOUND.into_response(),
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
---
2+
source: src/openapi.rs
3+
expression: response.json()
4+
snapshot_kind: text
5+
---
6+
{
7+
"components": {},
8+
"info": {
9+
"contact": {
10+
"email": "[email protected]",
11+
"name": "Alex Crichton"
12+
},
13+
"description": "Backend of crates.io",
14+
"license": {
15+
"name": "MIT OR Apache-2.0"
16+
},
17+
"title": "crates_io",
18+
"version": "0.0.0"
19+
},
20+
"openapi": "3.1.0",
21+
"paths": {}
22+
}

0 commit comments

Comments
 (0)