From 82aa90f3640ef1d31909e1b52b16a034f15a8922 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 10:23:54 +0100 Subject: [PATCH 01/43] utoipa: Add annotations to `/crates/new` endpoints --- src/controllers/krate/metadata.rs | 12 +++++++- src/controllers/krate/publish.rs | 10 ++++++- src/router.rs | 13 +++------ ..._io__openapi__tests__openapi_snapshot.snap | 28 +++++++++++++++++++ 4 files changed, 52 insertions(+), 11 deletions(-) diff --git a/src/controllers/krate/metadata.rs b/src/controllers/krate/metadata.rs index 8be893ba887..0aabfe60094 100644 --- a/src/controllers/krate/metadata.rs +++ b/src/controllers/krate/metadata.rs @@ -26,7 +26,17 @@ use http::request::Parts; use std::cmp::Reverse; use std::str::FromStr; -/// Handles the `GET /crates/new` special case. +/// Get crate metadata (for the `new` crate). +/// +/// This endpoint works around a small limitation in `axum` and is delegating +/// to the `GET /api/v1/crates/{name}` endpoint internally. +#[utoipa::path( + get, + path = "/api/v1/crates/new", + operation_id = "crates_show_new", + tag = "crates", + responses((status = 200, description = "Successful Response")), +)] pub async fn show_new(app: AppState, req: Parts) -> AppResult { show(app, Path("new".to_string()), req).await } diff --git a/src/controllers/krate/publish.rs b/src/controllers/krate/publish.rs index fe938360d23..82a30e6ff00 100644 --- a/src/controllers/krate/publish.rs +++ b/src/controllers/krate/publish.rs @@ -49,9 +49,17 @@ const MISSING_RIGHTS_ERROR_MESSAGE: &str = "this crate exists but you don't seem const MAX_DESCRIPTION_LENGTH: usize = 1000; -/// Handles the `PUT /crates/new` route. +/// Publish a new crate/version. +/// /// Used by `cargo publish` to publish a new crate or to publish a new version of an /// existing crate. +#[utoipa::path( + put, + path = "/api/v1/crates/new", + operation_id = "publish", + tag = "publish", + responses((status = 200, description = "Successful Response")), +)] pub async fn publish(app: AppState, req: Parts, body: Body) -> AppResult> { let stream = body.into_data_stream(); let stream = stream.map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err)); diff --git a/src/router.rs b/src/router.rs index 1da715e7103..05da5167237 100644 --- a/src/router.rs +++ b/src/router.rs @@ -13,18 +13,13 @@ use crate::Env; pub fn build_axum_router(state: AppState) -> Router<()> { let (router, openapi) = BaseOpenApi::router() - .routes(routes!( - // Route used by both `cargo search` and the frontend - krate::search::search - )) + // Route used by both `cargo search` and the frontend + .routes(routes!(krate::search::search)) + // Routes used by `cargo` + .routes(routes!(krate::publish::publish, krate::metadata::show_new)) .split_for_parts(); let mut router = router - // Routes used by `cargo` - .route( - "/api/v1/crates/new", - put(krate::publish::publish).get(krate::metadata::show_new), - ) .route( "/api/v1/crates/:crate_id/owners", get(krate::owners::owners) diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index bb539c39650..f6afb17fb08 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -34,6 +34,34 @@ snapshot_kind: text "crates" ] } + }, + "/api/v1/crates/new": { + "get": { + "description": "This endpoint works around a small limitation in `axum` and is delegating\nto the `GET /api/v1/crates/{name}` endpoint internally.", + "operationId": "crates_show_new", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "Get crate metadata (for the `new` crate).", + "tags": [ + "crates" + ] + }, + "put": { + "description": "Used by `cargo publish` to publish a new crate or to publish a new version of an\nexisting crate.", + "operationId": "publish", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "Publish a new crate/version.", + "tags": [ + "publish" + ] + } } }, "servers": [ From 08c6e4bdbc2694c1932f7935a923afbebd93c35b Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 10:36:55 +0100 Subject: [PATCH 02/43] utoipa: Add annotations to `/crates/{name}/owners` endpoints --- src/controllers/krate/owners.rs | 27 +++++++++++-- src/router.rs | 11 +++--- ..._io__openapi__tests__openapi_snapshot.snap | 38 +++++++++++++++++++ 3 files changed, 67 insertions(+), 9 deletions(-) diff --git a/src/controllers/krate/owners.rs b/src/controllers/krate/owners.rs index 05a3451a5a7..bf50d05a94b 100644 --- a/src/controllers/krate/owners.rs +++ b/src/controllers/krate/owners.rs @@ -17,7 +17,14 @@ use http::request::Parts; use http::StatusCode; use secrecy::{ExposeSecret, SecretString}; -/// Handles the `GET /crates/:crate_id/owners` route. +/// List crate owners. +#[utoipa::path( + get, + path = "/api/v1/crates/{name}/owners", + operation_id = "list_owners", + tag = "owners", + responses((status = 200, description = "Successful Response")), +)] pub async fn owners(state: AppState, Path(crate_name): Path) -> AppResult { let mut conn = state.db_read().await?; @@ -74,7 +81,14 @@ pub async fn owner_user(state: AppState, Path(crate_name): Path) -> AppR Ok(json!({ "users": owners })) } -/// Handles the `PUT /crates/:crate_id/owners` route. +/// Add crate owners. +#[utoipa::path( + put, + path = "/api/v1/crates/{name}/owners", + operation_id = "add_owners", + tag = "owners", + responses((status = 200, description = "Successful Response")), +)] pub async fn add_owners( app: AppState, Path(crate_name): Path, @@ -84,7 +98,14 @@ pub async fn add_owners( modify_owners(app, crate_name, parts, body, true).await } -/// Handles the `DELETE /crates/:crate_id/owners` route. +/// Remove crate owners. +#[utoipa::path( + delete, + path = "/api/v1/crates/{name}/owners", + operation_id = "delete_owners", + tag = "owners", + responses((status = 200, description = "Successful Response")), +)] pub async fn remove_owners( app: AppState, Path(crate_name): Path, diff --git a/src/router.rs b/src/router.rs index 05da5167237..cffc590c2b9 100644 --- a/src/router.rs +++ b/src/router.rs @@ -17,15 +17,14 @@ pub fn build_axum_router(state: AppState) -> Router<()> { .routes(routes!(krate::search::search)) // Routes used by `cargo` .routes(routes!(krate::publish::publish, krate::metadata::show_new)) + .routes(routes!( + krate::owners::owners, + krate::owners::add_owners, + krate::owners::remove_owners + )) .split_for_parts(); let mut router = router - .route( - "/api/v1/crates/:crate_id/owners", - get(krate::owners::owners) - .put(krate::owners::add_owners) - .delete(krate::owners::remove_owners), - ) .route( "/api/v1/crates/:crate_id/:version/yank", delete(version::yank::yank), diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index f6afb17fb08..0c046fb233a 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -62,6 +62,44 @@ snapshot_kind: text "publish" ] } + }, + "/api/v1/crates/{name}/owners": { + "delete": { + "operationId": "delete_owners", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "Remove crate owners.", + "tags": [ + "owners" + ] + }, + "get": { + "operationId": "list_owners", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "List crate owners.", + "tags": [ + "owners" + ] + }, + "put": { + "operationId": "add_owners", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "Add crate owners.", + "tags": [ + "owners" + ] + } } }, "servers": [ From 4acb47fccc6a1dfc075f68b02f33f0991b797169 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 10:41:21 +0100 Subject: [PATCH 03/43] utoipa: Add annotations to `/crates/{name}/{version}/yank` endpoints --- src/controllers/version/yank.rs | 13 +++++++++++-- src/router.rs | 5 +---- ...ates_io__openapi__tests__openapi_snapshot.snap | 15 +++++++++++++++ 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/controllers/version/yank.rs b/src/controllers/version/yank.rs index c9e18f65074..8498c58e623 100644 --- a/src/controllers/version/yank.rs +++ b/src/controllers/version/yank.rs @@ -10,15 +10,24 @@ use axum::extract::Path; use axum::response::Response; use http::request::Parts; -/// Handles the `DELETE /crates/:crate_id/:version/yank` route. +/// Yank a crate version. +/// /// This does not delete a crate version, it makes the crate /// version accessible only to crates that already have a /// `Cargo.lock` containing this version. /// /// Notes: -/// Crate deletion is not implemented to avoid breaking builds, +/// +/// Version deletion is not implemented to avoid breaking builds, /// and the goal of yanking a crate is to prevent crates /// beginning to depend on the yanked crate version. +#[utoipa::path( + delete, + path = "/api/v1/crates/{name}/{version}/yank", + operation_id = "yank_version", + tag = "versions", + responses((status = 200, description = "Successful Response")), +)] pub async fn yank( app: AppState, Path((crate_name, version)): Path<(String, String)>, diff --git a/src/router.rs b/src/router.rs index cffc590c2b9..efcb2d8b415 100644 --- a/src/router.rs +++ b/src/router.rs @@ -22,13 +22,10 @@ pub fn build_axum_router(state: AppState) -> Router<()> { krate::owners::add_owners, krate::owners::remove_owners )) + .routes(routes!(version::yank::yank)) .split_for_parts(); let mut router = router - .route( - "/api/v1/crates/:crate_id/:version/yank", - delete(version::yank::yank), - ) .route( "/api/v1/crates/:crate_id/:version/unyank", put(version::yank::unyank), diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index 0c046fb233a..9c99af26ee5 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -100,6 +100,21 @@ snapshot_kind: text "owners" ] } + }, + "/api/v1/crates/{name}/{version}/yank": { + "delete": { + "description": "This does not delete a crate version, it makes the crate\nversion accessible only to crates that already have a\n`Cargo.lock` containing this version.\n\nNotes:\n\nVersion deletion is not implemented to avoid breaking builds,\nand the goal of yanking a crate is to prevent crates\nbeginning to depend on the yanked crate version.", + "operationId": "yank_version", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "Yank a crate version.", + "tags": [ + "versions" + ] + } } }, "servers": [ From fc5f2ce2a9c0600f3439b4f208c9cfc4eb2ba680 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 10:42:30 +0100 Subject: [PATCH 04/43] utoipa: Add annotations to `/crates/{name}/{version}/unyank` endpoints --- src/controllers/version/yank.rs | 9 ++++++++- src/router.rs | 5 +---- ...rates_io__openapi__tests__openapi_snapshot.snap | 14 ++++++++++++++ 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/controllers/version/yank.rs b/src/controllers/version/yank.rs index 8498c58e623..06b8cac5cb8 100644 --- a/src/controllers/version/yank.rs +++ b/src/controllers/version/yank.rs @@ -36,7 +36,14 @@ pub async fn yank( modify_yank(crate_name, version, app, req, true).await } -/// Handles the `PUT /crates/:crate_id/:version/unyank` route. +/// Unyank a crate version. +#[utoipa::path( + put, + path = "/api/v1/crates/{name}/{version}/unyank", + operation_id = "unyank_version", + tag = "versions", + responses((status = 200, description = "Successful Response")), +)] pub async fn unyank( app: AppState, Path((crate_name, version)): Path<(String, String)>, diff --git a/src/router.rs b/src/router.rs index efcb2d8b415..fe9abb6570e 100644 --- a/src/router.rs +++ b/src/router.rs @@ -23,13 +23,10 @@ pub fn build_axum_router(state: AppState) -> Router<()> { krate::owners::remove_owners )) .routes(routes!(version::yank::yank)) + .routes(routes!(version::yank::unyank)) .split_for_parts(); let mut router = router - .route( - "/api/v1/crates/:crate_id/:version/unyank", - put(version::yank::unyank), - ) .route( "/api/v1/crates/:crate_id/:version/download", get(version::downloads::download), diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index 9c99af26ee5..4a01b2c697f 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -101,6 +101,20 @@ snapshot_kind: text ] } }, + "/api/v1/crates/{name}/{version}/unyank": { + "put": { + "operationId": "unyank_version", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "Unyank a crate version.", + "tags": [ + "versions" + ] + } + }, "/api/v1/crates/{name}/{version}/yank": { "delete": { "description": "This does not delete a crate version, it makes the crate\nversion accessible only to crates that already have a\n`Cargo.lock` containing this version.\n\nNotes:\n\nVersion deletion is not implemented to avoid breaking builds,\nand the goal of yanking a crate is to prevent crates\nbeginning to depend on the yanked crate version.", From 9000960009bcfd89221c233a7cb4a96f8e7a9bb7 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 10:44:15 +0100 Subject: [PATCH 05/43] utoipa: Add annotations to `/crates/{name}/{version}/download` endpoints --- src/controllers/version/downloads.rs | 10 +++++++++- src/router.rs | 5 +---- ...ates_io__openapi__tests__openapi_snapshot.snap | 15 +++++++++++++++ src/tests/blocked_routes.rs | 2 +- 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/controllers/version/downloads.rs b/src/controllers/version/downloads.rs index 28390e2b908..80a302c9f37 100644 --- a/src/controllers/version/downloads.rs +++ b/src/controllers/version/downloads.rs @@ -18,8 +18,16 @@ use diesel::prelude::*; use diesel_async::RunQueryDsl; use http::request::Parts; -/// Handles the `GET /crates/:crate_id/:version/download` route. +/// Download a crate version. +/// /// This returns a URL to the location where the crate is stored. +#[utoipa::path( + get, + path = "/api/v1/crates/{name}/{version}/download", + operation_id = "download_version", + tag = "versions", + responses((status = 200, description = "Successful Response")), +)] pub async fn download( app: AppState, Path((crate_name, version)): Path<(String, String)>, diff --git a/src/router.rs b/src/router.rs index fe9abb6570e..3abafa285b1 100644 --- a/src/router.rs +++ b/src/router.rs @@ -24,13 +24,10 @@ pub fn build_axum_router(state: AppState) -> Router<()> { )) .routes(routes!(version::yank::yank)) .routes(routes!(version::yank::unyank)) + .routes(routes!(version::downloads::download)) .split_for_parts(); let mut router = router - .route( - "/api/v1/crates/:crate_id/:version/download", - get(version::downloads::download), - ) // Routes used by the frontend .route( "/api/v1/crates/:crate_id", diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index 4a01b2c697f..0e39e460600 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -101,6 +101,21 @@ snapshot_kind: text ] } }, + "/api/v1/crates/{name}/{version}/download": { + "get": { + "description": "This returns a URL to the location where the crate is stored.", + "operationId": "download_version", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "Download a crate version.", + "tags": [ + "versions" + ] + } + }, "/api/v1/crates/{name}/{version}/unyank": { "put": { "operationId": "unyank_version", diff --git a/src/tests/blocked_routes.rs b/src/tests/blocked_routes.rs index 7c1e31efddb..bca35c93607 100644 --- a/src/tests/blocked_routes.rs +++ b/src/tests/blocked_routes.rs @@ -32,7 +32,7 @@ async fn test_blocked_download_route() { config.blocked_routes.clear(); config .blocked_routes - .insert("/api/v1/crates/:crate_id/:version/download".into()); + .insert("/api/v1/crates/:name/:version/download".into()); }) .with_user() .await; From ea9b50e07f8b7c848a2f7815a136197c1dc377c0 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 10:50:00 +0100 Subject: [PATCH 06/43] utoipa: Add annotations to `/crates/{name}` endpoints --- src/controllers/krate.rs | 4 +-- src/controllers/krate/delete.rs | 12 ++++++++- src/controllers/krate/metadata.rs | 9 ++++++- src/router.rs | 7 ++--- ..._io__openapi__tests__openapi_snapshot.snap | 27 +++++++++++++++++++ 5 files changed, 49 insertions(+), 10 deletions(-) diff --git a/src/controllers/krate.rs b/src/controllers/krate.rs index 93ee59d4798..ca1cd37cddb 100644 --- a/src/controllers/krate.rs +++ b/src/controllers/krate.rs @@ -1,4 +1,4 @@ -mod delete; +pub mod delete; pub mod downloads; pub mod follow; pub mod metadata; @@ -6,5 +6,3 @@ pub mod owners; pub mod publish; pub mod search; pub mod versions; - -pub use delete::delete; diff --git a/src/controllers/krate/delete.rs b/src/controllers/krate/delete.rs index 55bd9db4b3c..b08e4642061 100644 --- a/src/controllers/krate/delete.rs +++ b/src/controllers/krate/delete.rs @@ -18,12 +18,22 @@ use http::StatusCode; const DOWNLOADS_PER_MONTH_LIMIT: u64 = 100; const AVAILABLE_AFTER: TimeDelta = TimeDelta::hours(24); -/// Deletes a crate from the database, index and storage. +/// Delete a crate. +/// +/// The crate is immediately deleted from the database, and with a small delay +/// from the git and sparse index, and the crate file storage. /// /// The crate can only be deleted by the owner of the crate, and only if the /// crate has been published for less than 72 hours, or if the crate has a /// single owner, has been downloaded less than 100 times for each month it has /// been published, and is not depended upon by any other crate on crates.io. +#[utoipa::path( + delete, + path = "/api/v1/crates/{name}", + operation_id = "delete_crate", + tag = "crates", + responses((status = 200, description = "Successful Response")), +)] pub async fn delete( Path(name): Path, parts: Parts, diff --git a/src/controllers/krate/metadata.rs b/src/controllers/krate/metadata.rs index 0aabfe60094..9f86e4f5839 100644 --- a/src/controllers/krate/metadata.rs +++ b/src/controllers/krate/metadata.rs @@ -41,7 +41,14 @@ pub async fn show_new(app: AppState, req: Parts) -> AppResult { show(app, Path("new".to_string()), req).await } -/// Handles the `GET /crates/:crate_id` route. +/// Get crate metadata. +#[utoipa::path( + get, + path = "/api/v1/crates/{name}", + operation_id = "get_crate", + tag = "crates", + responses((status = 200, description = "Successful Response")), +)] pub async fn show(app: AppState, Path(name): Path, req: Parts) -> AppResult { let mut conn = app.db_read().await?; diff --git a/src/router.rs b/src/router.rs index 3abafa285b1..826fc472732 100644 --- a/src/router.rs +++ b/src/router.rs @@ -25,14 +25,11 @@ pub fn build_axum_router(state: AppState) -> Router<()> { .routes(routes!(version::yank::yank)) .routes(routes!(version::yank::unyank)) .routes(routes!(version::downloads::download)) + // Routes used by the frontend + .routes(routes!(krate::metadata::show, krate::delete::delete)) .split_for_parts(); let mut router = router - // Routes used by the frontend - .route( - "/api/v1/crates/:crate_id", - get(krate::metadata::show).delete(krate::delete), - ) .route( "/api/v1/crates/:crate_id/:version", get(version::metadata::show).patch(version::metadata::update), diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index 0e39e460600..99ecdf12ccb 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -63,6 +63,33 @@ snapshot_kind: text ] } }, + "/api/v1/crates/{name}": { + "delete": { + "description": "The crate is immediately deleted from the database, and with a small delay\nfrom the git and sparse index, and the crate file storage.\n\nThe crate can only be deleted by the owner of the crate, and only if the\ncrate has been published for less than 72 hours, or if the crate has a\nsingle owner, has been downloaded less than 100 times for each month it has\nbeen published, and is not depended upon by any other crate on crates.io.", + "operationId": "delete_crate", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "Delete a crate.", + "tags": [ + "crates" + ] + }, + "get": { + "operationId": "get_crate", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "Get crate metadata.", + "tags": [ + "crates" + ] + } + }, "/api/v1/crates/{name}/owners": { "delete": { "operationId": "delete_owners", From 9d0a123342bbc8dcf8b6df1926c1a6bbf4610dc0 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 10:56:42 +0100 Subject: [PATCH 07/43] utoipa: Add annotations to `/crates/{name}/{version}` endpoints --- src/controllers/version/metadata.rs | 23 +++++++++++----- src/router.rs | 5 +--- ..._io__openapi__tests__openapi_snapshot.snap | 27 +++++++++++++++++++ 3 files changed, 45 insertions(+), 10 deletions(-) diff --git a/src/controllers/version/metadata.rs b/src/controllers/version/metadata.rs index 61130b80bf0..76b87687ffd 100644 --- a/src/controllers/version/metadata.rs +++ b/src/controllers/version/metadata.rs @@ -82,10 +82,14 @@ pub async fn authors() -> ErasedJson { }) } -/// Handles the `GET /crates/:crate/:version` route. -/// -/// The frontend doesn't appear to hit this endpoint, but our tests do, and it seems to be a useful -/// API route to have. +/// Get crate version metadata. +#[utoipa::path( + get, + path = "/api/v1/crates/{name}/{version}", + operation_id = "get_version", + tag = "versions", + responses((status = 200, description = "Successful Response")), +)] pub async fn show( state: AppState, Path((crate_name, version)): Path<(String, String)>, @@ -103,9 +107,16 @@ pub async fn show( Ok(json!({ "version": version })) } -/// Handles the `PATCH /crates/:crate/:version` route. +/// Update a crate version. /// -/// This endpoint allows updating the yanked state of a version, including a yank message. +/// This endpoint allows updating the `yanked` state of a version, including a yank message. +#[utoipa::path( + patch, + path = "/api/v1/crates/{name}/{version}", + operation_id = "update_version", + tag = "versions", + responses((status = 200, description = "Successful Response")), +)] pub async fn update( state: AppState, Path((crate_name, version)): Path<(String, String)>, diff --git a/src/router.rs b/src/router.rs index 826fc472732..ab42daa138e 100644 --- a/src/router.rs +++ b/src/router.rs @@ -27,13 +27,10 @@ pub fn build_axum_router(state: AppState) -> Router<()> { .routes(routes!(version::downloads::download)) // Routes used by the frontend .routes(routes!(krate::metadata::show, krate::delete::delete)) + .routes(routes!(version::metadata::show, version::metadata::update)) .split_for_parts(); let mut router = router - .route( - "/api/v1/crates/:crate_id/:version", - get(version::metadata::show).patch(version::metadata::update), - ) .route( "/api/v1/crates/:crate_id/:version/readme", get(krate::metadata::readme), diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index 99ecdf12ccb..628103ed841 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -128,6 +128,33 @@ snapshot_kind: text ] } }, + "/api/v1/crates/{name}/{version}": { + "get": { + "operationId": "get_version", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "Get crate version metadata.", + "tags": [ + "versions" + ] + }, + "patch": { + "description": "This endpoint allows updating the `yanked` state of a version, including a yank message.", + "operationId": "update_version", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "Update a crate version.", + "tags": [ + "versions" + ] + } + }, "/api/v1/crates/{name}/{version}/download": { "get": { "description": "This returns a URL to the location where the crate is stored.", From f79f4d52a38109186e5d9e13ec8ea129d03dba59 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 10:58:51 +0100 Subject: [PATCH 08/43] utoipa: Add annotations to `/crates/{name}/{version}/readme` endpoints --- src/controllers/krate/metadata.rs | 9 ++++++++- src/router.rs | 5 +---- ...rates_io__openapi__tests__openapi_snapshot.snap | 14 ++++++++++++++ 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/controllers/krate/metadata.rs b/src/controllers/krate/metadata.rs index 9f86e4f5839..ee38beb769f 100644 --- a/src/controllers/krate/metadata.rs +++ b/src/controllers/krate/metadata.rs @@ -244,7 +244,14 @@ impl FromStr for ShowIncludeMode { } } -/// Handles the `GET /crates/:crate_id/:version/readme` route. +/// Get the readme of a crate version. +#[utoipa::path( + get, + path = "/api/v1/crates/{name}/{version}/readme", + operation_id = "get_version_readme", + tag = "versions", + responses((status = 200, description = "Successful Response")), +)] pub async fn readme( app: AppState, Path((crate_name, version)): Path<(String, String)>, diff --git a/src/router.rs b/src/router.rs index ab42daa138e..fa136a20220 100644 --- a/src/router.rs +++ b/src/router.rs @@ -28,13 +28,10 @@ pub fn build_axum_router(state: AppState) -> Router<()> { // Routes used by the frontend .routes(routes!(krate::metadata::show, krate::delete::delete)) .routes(routes!(version::metadata::show, version::metadata::update)) + .routes(routes!(krate::metadata::readme)) .split_for_parts(); let mut router = router - .route( - "/api/v1/crates/:crate_id/:version/readme", - get(krate::metadata::readme), - ) .route( "/api/v1/crates/:crate_id/:version/dependencies", get(version::metadata::dependencies), diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index 628103ed841..182fdad19e7 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -170,6 +170,20 @@ snapshot_kind: text ] } }, + "/api/v1/crates/{name}/{version}/readme": { + "get": { + "operationId": "get_version_readme", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "Get the readme of a crate version.", + "tags": [ + "versions" + ] + } + }, "/api/v1/crates/{name}/{version}/unyank": { "put": { "operationId": "unyank_version", From 5eb53e37a8b530bac93f487068651823dcd4a22b Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 11:01:12 +0100 Subject: [PATCH 09/43] utoipa: Add annotations to `/crates/{name}/{version}/dependencies` endpoints --- src/controllers/version/metadata.rs | 11 +++++++++-- src/router.rs | 5 +---- ...ates_io__openapi__tests__openapi_snapshot.snap | 15 +++++++++++++++ 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/controllers/version/metadata.rs b/src/controllers/version/metadata.rs index 76b87687ffd..2b2a2fd1461 100644 --- a/src/controllers/version/metadata.rs +++ b/src/controllers/version/metadata.rs @@ -40,13 +40,20 @@ pub struct VersionUpdateRequest { version: VersionUpdate, } -/// Handles the `GET /crates/:crate_id/:version/dependencies` route. +/// Get crate version dependencies. /// -/// This information can be obtained directly from the index. +/// This information can also be obtained directly from the index. /// /// In addition to returning cached data from the index, this returns /// fields for `id`, `version_id`, and `downloads` (which appears to always /// be 0) +#[utoipa::path( + get, + path = "/api/v1/crates/{name}/{version}/dependencies", + operation_id = "get_version_dependencies", + tag = "versions", + responses((status = 200, description = "Successful Response")), +)] pub async fn dependencies( state: AppState, Path((crate_name, version)): Path<(String, String)>, diff --git a/src/router.rs b/src/router.rs index fa136a20220..cc9dd4655f2 100644 --- a/src/router.rs +++ b/src/router.rs @@ -29,13 +29,10 @@ pub fn build_axum_router(state: AppState) -> Router<()> { .routes(routes!(krate::metadata::show, krate::delete::delete)) .routes(routes!(version::metadata::show, version::metadata::update)) .routes(routes!(krate::metadata::readme)) + .routes(routes!(version::metadata::dependencies)) .split_for_parts(); let mut router = router - .route( - "/api/v1/crates/:crate_id/:version/dependencies", - get(version::metadata::dependencies), - ) .route( "/api/v1/crates/:crate_id/:version/downloads", get(version::downloads::downloads), diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index 182fdad19e7..a56b21b4741 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -155,6 +155,21 @@ snapshot_kind: text ] } }, + "/api/v1/crates/{name}/{version}/dependencies": { + "get": { + "description": "This information can also be obtained directly from the index.\n\nIn addition to returning cached data from the index, this returns\nfields for `id`, `version_id`, and `downloads` (which appears to always\nbe 0)", + "operationId": "get_version_dependencies", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "Get crate version dependencies.", + "tags": [ + "versions" + ] + } + }, "/api/v1/crates/{name}/{version}/download": { "get": { "description": "This returns a URL to the location where the crate is stored.", From 96ff1663a856c878e24829f75303b8afc60ac150 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 11:03:09 +0100 Subject: [PATCH 10/43] utoipa: Add annotations to `/crates/{name}/{version}/downloads` endpoints --- src/controllers/version/downloads.rs | 11 ++++++++++- src/router.rs | 5 +---- ...ates_io__openapi__tests__openapi_snapshot.snap | 15 +++++++++++++++ 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/controllers/version/downloads.rs b/src/controllers/version/downloads.rs index 80a302c9f37..92841325a5d 100644 --- a/src/controllers/version/downloads.rs +++ b/src/controllers/version/downloads.rs @@ -42,7 +42,16 @@ pub async fn download( } } -/// Handles the `GET /crates/:crate_id/:version/downloads` route. +/// Get the download counts for a crate version. +/// +/// This includes the per-day downloads for the last 90 days. +#[utoipa::path( + get, + path = "/api/v1/crates/{name}/{version}/downloads", + operation_id = "get_version_downloads", + tag = "versions", + responses((status = 200, description = "Successful Response")), +)] pub async fn downloads( app: AppState, Path((crate_name, version)): Path<(String, String)>, diff --git a/src/router.rs b/src/router.rs index cc9dd4655f2..45fdaf6a0df 100644 --- a/src/router.rs +++ b/src/router.rs @@ -30,13 +30,10 @@ pub fn build_axum_router(state: AppState) -> Router<()> { .routes(routes!(version::metadata::show, version::metadata::update)) .routes(routes!(krate::metadata::readme)) .routes(routes!(version::metadata::dependencies)) + .routes(routes!(version::downloads::downloads)) .split_for_parts(); let mut router = router - .route( - "/api/v1/crates/:crate_id/:version/downloads", - get(version::downloads::downloads), - ) .route( "/api/v1/crates/:crate_id/:version/authors", get(version::metadata::authors), diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index a56b21b4741..fe757a81728 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -185,6 +185,21 @@ snapshot_kind: text ] } }, + "/api/v1/crates/{name}/{version}/downloads": { + "get": { + "description": "This includes the per-day downloads for the last 90 days.", + "operationId": "get_version_downloads", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "Get the download counts for a crate version.", + "tags": [ + "versions" + ] + } + }, "/api/v1/crates/{name}/{version}/readme": { "get": { "operationId": "get_version_readme", From f19c6f471bba72a64c5809dbbfa7a8683ba3aaf4 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 16:49:00 +0100 Subject: [PATCH 11/43] utoipa: Add annotations to `/crates/{name}/{version}/authors` endpoints --- src/controllers/version/metadata.rs | 16 ++++++++++++---- src/router.rs | 6 ++---- ...tes_io__openapi__tests__openapi_snapshot.snap | 16 ++++++++++++++++ 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/controllers/version/metadata.rs b/src/controllers/version/metadata.rs index 2b2a2fd1461..01f15fa7885 100644 --- a/src/controllers/version/metadata.rs +++ b/src/controllers/version/metadata.rs @@ -78,11 +78,19 @@ pub async fn dependencies( Ok(json!({ "dependencies": deps })) } -/// Handles the `GET /crates/:crate_id/:version/authors` route. +/// Get crate version authors. +/// +/// This endpoint was deprecated by [RFC #3052](https://github.com/rust-lang/rfcs/pull/3052) +/// and returns an empty list for backwards compatibility reasons. +#[utoipa::path( + get, + path = "/api/v1/crates/{name}/{version}/authors", + operation_id = "get_version_authors", + tag = "versions", + responses((status = 200, description = "Successful Response")), +)] +#[deprecated] pub async fn authors() -> ErasedJson { - // Currently we return the empty list. - // Because the API is not used anymore after RFC https://github.com/rust-lang/rfcs/pull/3052. - json!({ "users": [], "meta": { "names": [] }, diff --git a/src/router.rs b/src/router.rs index 45fdaf6a0df..7608f369780 100644 --- a/src/router.rs +++ b/src/router.rs @@ -11,6 +11,7 @@ use crate::openapi::BaseOpenApi; use crate::util::errors::not_found; use crate::Env; +#[allow(deprecated)] pub fn build_axum_router(state: AppState) -> Router<()> { let (router, openapi) = BaseOpenApi::router() // Route used by both `cargo search` and the frontend @@ -31,13 +32,10 @@ pub fn build_axum_router(state: AppState) -> Router<()> { .routes(routes!(krate::metadata::readme)) .routes(routes!(version::metadata::dependencies)) .routes(routes!(version::downloads::downloads)) + .routes(routes!(version::metadata::authors)) .split_for_parts(); let mut router = router - .route( - "/api/v1/crates/:crate_id/:version/authors", - get(version::metadata::authors), - ) .route( "/api/v1/crates/:crate_id/downloads", get(krate::downloads::downloads), diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index fe757a81728..f1c488fd4d3 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -155,6 +155,22 @@ snapshot_kind: text ] } }, + "/api/v1/crates/{name}/{version}/authors": { + "get": { + "deprecated": true, + "description": "This endpoint was deprecated by [RFC #3052](https://github.com/rust-lang/rfcs/pull/3052)\nand returns an empty list for backwards compatibility reasons.", + "operationId": "get_version_authors", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "Get crate version authors.", + "tags": [ + "versions" + ] + } + }, "/api/v1/crates/{name}/{version}/dependencies": { "get": { "description": "This information can also be obtained directly from the index.\n\nIn addition to returning cached data from the index, this returns\nfields for `id`, `version_id`, and `downloads` (which appears to always\nbe 0)", From 24090fd50048e0d7bdba0256e59f539194499d65 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 16:52:13 +0100 Subject: [PATCH 12/43] utoipa: Add annotations to `/crates/{name}/downloads` endpoints --- src/controllers/krate/downloads.rs | 13 ++++++++++++- src/router.rs | 5 +---- ...ates_io__openapi__tests__openapi_snapshot.snap | 15 +++++++++++++++ 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/controllers/krate/downloads.rs b/src/controllers/krate/downloads.rs index 3ff2dbba87e..3d731aa0588 100644 --- a/src/controllers/krate/downloads.rs +++ b/src/controllers/krate/downloads.rs @@ -16,7 +16,18 @@ use diesel::prelude::*; use diesel_async::RunQueryDsl; use std::cmp; -/// Handles the `GET /crates/:crate_id/downloads` route. +/// Get the download counts for a crate. +/// +/// This includes the per-day downloads for the last 90 days and for the +/// latest 5 versions plus the sum of the rest. +#[utoipa::path( + get, + path = "/api/v1/crates/{name}/downloads", + operation_id = "get_crate_downloads", + tag = "crates", + responses((status = 200, description = "Successful Response")), +)] + pub async fn downloads(state: AppState, Path(crate_name): Path) -> AppResult { let mut conn = state.db_read().await?; diff --git a/src/router.rs b/src/router.rs index 7608f369780..5a52c931cf8 100644 --- a/src/router.rs +++ b/src/router.rs @@ -33,13 +33,10 @@ pub fn build_axum_router(state: AppState) -> Router<()> { .routes(routes!(version::metadata::dependencies)) .routes(routes!(version::downloads::downloads)) .routes(routes!(version::metadata::authors)) + .routes(routes!(krate::downloads::downloads)) .split_for_parts(); let mut router = router - .route( - "/api/v1/crates/:crate_id/downloads", - get(krate::downloads::downloads), - ) .route( "/api/v1/crates/:crate_id/versions", get(krate::versions::versions), diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index f1c488fd4d3..9da83f927d9 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -90,6 +90,21 @@ snapshot_kind: text ] } }, + "/api/v1/crates/{name}/downloads": { + "get": { + "description": "This includes the per-day downloads for the last 90 days and for the\nlatest 5 versions plus the sum of the rest.", + "operationId": "get_crate_downloads", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "Get the download counts for a crate.", + "tags": [ + "crates" + ] + } + }, "/api/v1/crates/{name}/owners": { "delete": { "operationId": "delete_owners", From 5c1d82db572b02fbdbaa8d1c799fc503ab516835 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 16:54:45 +0100 Subject: [PATCH 13/43] utoipa: Add annotations to `/crates/{name}/versions` endpoints --- src/controllers/krate/versions.rs | 9 ++++++++- src/router.rs | 5 +---- ...rates_io__openapi__tests__openapi_snapshot.snap | 14 ++++++++++++++ 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/controllers/krate/versions.rs b/src/controllers/krate/versions.rs index 43761016122..5dd370a2418 100644 --- a/src/controllers/krate/versions.rs +++ b/src/controllers/krate/versions.rs @@ -20,7 +20,14 @@ use crate::util::errors::{bad_request, crate_not_found, AppResult, BoxedAppError use crate::util::RequestUtils; use crate::views::EncodableVersion; -/// Handles the `GET /crates/:crate_id/versions` route. +/// List all versions of a crate. +#[utoipa::path( + get, + path = "/api/v1/crates/{name}/versions", + operation_id = "list_crate_versions", + tag = "versions", + responses((status = 200, description = "Successful Response")), +)] pub async fn versions( state: AppState, Path(crate_name): Path, diff --git a/src/router.rs b/src/router.rs index 5a52c931cf8..5dc61cbf6cf 100644 --- a/src/router.rs +++ b/src/router.rs @@ -34,13 +34,10 @@ pub fn build_axum_router(state: AppState) -> Router<()> { .routes(routes!(version::downloads::downloads)) .routes(routes!(version::metadata::authors)) .routes(routes!(krate::downloads::downloads)) + .routes(routes!(krate::versions::versions)) .split_for_parts(); let mut router = router - .route( - "/api/v1/crates/:crate_id/versions", - get(krate::versions::versions), - ) .route( "/api/v1/crates/:crate_id/follow", put(krate::follow::follow).delete(krate::follow::unfollow), diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index 9da83f927d9..51c49744cbc 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -143,6 +143,20 @@ snapshot_kind: text ] } }, + "/api/v1/crates/{name}/versions": { + "get": { + "operationId": "list_crate_versions", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "List all versions of a crate.", + "tags": [ + "versions" + ] + } + }, "/api/v1/crates/{name}/{version}": { "get": { "operationId": "get_version", From c9d9c29b5f0c9d5fa67e8ceb8abcb2dc917fb22c Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 16:57:31 +0100 Subject: [PATCH 14/43] utoipa: Add annotations to `/crates/{name}/follow` endpoints --- src/controllers/krate/follow.rs | 18 +++++++++++-- src/router.rs | 5 +--- ..._io__openapi__tests__openapi_snapshot.snap | 26 +++++++++++++++++++ 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/src/controllers/krate/follow.rs b/src/controllers/krate/follow.rs index d000a10c532..0c081544e10 100644 --- a/src/controllers/krate/follow.rs +++ b/src/controllers/krate/follow.rs @@ -29,7 +29,14 @@ async fn follow_target( Ok(Follow { user_id, crate_id }) } -/// Handles the `PUT /crates/:crate_id/follow` route. +/// Follow a crate. +#[utoipa::path( + put, + path = "/api/v1/crates/{name}/follow", + operation_id = "follow_crate", + tag = "crates", + responses((status = 200, description = "Successful Response")), +)] pub async fn follow( app: AppState, Path(crate_name): Path, @@ -47,7 +54,14 @@ pub async fn follow( ok_true() } -/// Handles the `DELETE /crates/:crate_id/follow` route. +/// Unfollow a crate. +#[utoipa::path( + delete, + path = "/api/v1/crates/{name}/follow", + operation_id = "unfollow_crate", + tag = "crates", + responses((status = 200, description = "Successful Response")), +)] pub async fn unfollow( app: AppState, Path(crate_name): Path, diff --git a/src/router.rs b/src/router.rs index 5dc61cbf6cf..c3159670d39 100644 --- a/src/router.rs +++ b/src/router.rs @@ -35,13 +35,10 @@ pub fn build_axum_router(state: AppState) -> Router<()> { .routes(routes!(version::metadata::authors)) .routes(routes!(krate::downloads::downloads)) .routes(routes!(krate::versions::versions)) + .routes(routes!(krate::follow::follow, krate::follow::unfollow)) .split_for_parts(); let mut router = router - .route( - "/api/v1/crates/:crate_id/follow", - put(krate::follow::follow).delete(krate::follow::unfollow), - ) .route( "/api/v1/crates/:crate_id/following", get(krate::follow::following), diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index 51c49744cbc..f4b5ec07dee 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -105,6 +105,32 @@ snapshot_kind: text ] } }, + "/api/v1/crates/{name}/follow": { + "delete": { + "operationId": "unfollow_crate", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "Unfollow a crate.", + "tags": [ + "crates" + ] + }, + "put": { + "operationId": "follow_crate", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "Follow a crate.", + "tags": [ + "crates" + ] + } + }, "/api/v1/crates/{name}/owners": { "delete": { "operationId": "delete_owners", From b740a465452c4ff4ad48484af170687e6e40ee40 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 17:00:46 +0100 Subject: [PATCH 15/43] utoipa: Add annotations to `/crates/{name}/following` endpoints --- src/controllers/krate/follow.rs | 9 ++++++++- src/router.rs | 5 +---- ...rates_io__openapi__tests__openapi_snapshot.snap | 14 ++++++++++++++ 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/controllers/krate/follow.rs b/src/controllers/krate/follow.rs index 0c081544e10..c0e2dc2985b 100644 --- a/src/controllers/krate/follow.rs +++ b/src/controllers/krate/follow.rs @@ -75,7 +75,14 @@ pub async fn unfollow( ok_true() } -/// Handles the `GET /crates/:crate_id/following` route. +/// Check if a crate is followed. +#[utoipa::path( + get, + path = "/api/v1/crates/{name}/following", + operation_id = "get_following_crate", + tag = "crates", + responses((status = 200, description = "Successful Response")), +)] pub async fn following( app: AppState, Path(crate_name): Path, diff --git a/src/router.rs b/src/router.rs index c3159670d39..3ab4900ad23 100644 --- a/src/router.rs +++ b/src/router.rs @@ -36,13 +36,10 @@ pub fn build_axum_router(state: AppState) -> Router<()> { .routes(routes!(krate::downloads::downloads)) .routes(routes!(krate::versions::versions)) .routes(routes!(krate::follow::follow, krate::follow::unfollow)) + .routes(routes!(krate::follow::following)) .split_for_parts(); let mut router = router - .route( - "/api/v1/crates/:crate_id/following", - get(krate::follow::following), - ) .route( "/api/v1/crates/:crate_id/owner_team", get(krate::owners::owner_team), diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index f4b5ec07dee..b3282175ba4 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -131,6 +131,20 @@ snapshot_kind: text ] } }, + "/api/v1/crates/{name}/following": { + "get": { + "operationId": "get_following_crate", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "Check if a crate is followed.", + "tags": [ + "crates" + ] + } + }, "/api/v1/crates/{name}/owners": { "delete": { "operationId": "delete_owners", From 5fa4b1feb77509c1cc470a6324134440bf8ae61b Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 17:05:38 +0100 Subject: [PATCH 16/43] utoipa: Add annotations to `/crates/{name}/owner_team` endpoints --- src/controllers/krate/owners.rs | 9 ++++++++- src/router.rs | 5 +---- ...rates_io__openapi__tests__openapi_snapshot.snap | 14 ++++++++++++++ 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/controllers/krate/owners.rs b/src/controllers/krate/owners.rs index bf50d05a94b..1ecc07423ab 100644 --- a/src/controllers/krate/owners.rs +++ b/src/controllers/krate/owners.rs @@ -44,7 +44,14 @@ pub async fn owners(state: AppState, Path(crate_name): Path) -> AppResul Ok(json!({ "users": owners })) } -/// Handles the `GET /crates/:crate_id/owner_team` route. +/// List team owners of a crate. +#[utoipa::path( + get, + path = "/api/v1/crates/{name}/owner_team", + operation_id = "get_team_owners", + tag = "owners", + responses((status = 200, description = "Successful Response")), +)] pub async fn owner_team(state: AppState, Path(crate_name): Path) -> AppResult { let mut conn = state.db_read().await?; let krate: Crate = Crate::by_name(&crate_name) diff --git a/src/router.rs b/src/router.rs index 3ab4900ad23..6c802bd9011 100644 --- a/src/router.rs +++ b/src/router.rs @@ -37,13 +37,10 @@ pub fn build_axum_router(state: AppState) -> Router<()> { .routes(routes!(krate::versions::versions)) .routes(routes!(krate::follow::follow, krate::follow::unfollow)) .routes(routes!(krate::follow::following)) + .routes(routes!(krate::owners::owner_team)) .split_for_parts(); let mut router = router - .route( - "/api/v1/crates/:crate_id/owner_team", - get(krate::owners::owner_team), - ) .route( "/api/v1/crates/:crate_id/owner_user", get(krate::owners::owner_user), diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index b3282175ba4..24762a08a1f 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -145,6 +145,20 @@ snapshot_kind: text ] } }, + "/api/v1/crates/{name}/owner_team": { + "get": { + "operationId": "get_team_owners", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "List team owners of a crate.", + "tags": [ + "owners" + ] + } + }, "/api/v1/crates/{name}/owners": { "delete": { "operationId": "delete_owners", From b8f8fef2219cfa3baa8550d714528ee63adf7217 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 17:07:15 +0100 Subject: [PATCH 17/43] utoipa: Add annotations to `/crates/{name}/owner_user` endpoints --- src/controllers/krate/owners.rs | 9 ++++++++- src/router.rs | 5 +---- ...rates_io__openapi__tests__openapi_snapshot.snap | 14 ++++++++++++++ 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/controllers/krate/owners.rs b/src/controllers/krate/owners.rs index 1ecc07423ab..6fdf2584f37 100644 --- a/src/controllers/krate/owners.rs +++ b/src/controllers/krate/owners.rs @@ -69,7 +69,14 @@ pub async fn owner_team(state: AppState, Path(crate_name): Path) -> AppR Ok(json!({ "teams": owners })) } -/// Handles the `GET /crates/:crate_id/owner_user` route. +/// List user owners of a crate. +#[utoipa::path( + get, + path = "/api/v1/crates/{name}/owner_user", + operation_id = "get_user_owners", + tag = "owners", + responses((status = 200, description = "Successful Response")), +)] pub async fn owner_user(state: AppState, Path(crate_name): Path) -> AppResult { let mut conn = state.db_read().await?; diff --git a/src/router.rs b/src/router.rs index 6c802bd9011..d95ccab478e 100644 --- a/src/router.rs +++ b/src/router.rs @@ -38,13 +38,10 @@ pub fn build_axum_router(state: AppState) -> Router<()> { .routes(routes!(krate::follow::follow, krate::follow::unfollow)) .routes(routes!(krate::follow::following)) .routes(routes!(krate::owners::owner_team)) + .routes(routes!(krate::owners::owner_user)) .split_for_parts(); let mut router = router - .route( - "/api/v1/crates/:crate_id/owner_user", - get(krate::owners::owner_user), - ) .route( "/api/v1/crates/:crate_id/reverse_dependencies", get(krate::metadata::reverse_dependencies), diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index 24762a08a1f..06f55bd8e5c 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -159,6 +159,20 @@ snapshot_kind: text ] } }, + "/api/v1/crates/{name}/owner_user": { + "get": { + "operationId": "get_user_owners", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "List user owners of a crate.", + "tags": [ + "owners" + ] + } + }, "/api/v1/crates/{name}/owners": { "delete": { "operationId": "delete_owners", From 5777f08afbcc4187831dfc02d3fb0ff06b8babea Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 17:08:59 +0100 Subject: [PATCH 18/43] utoipa: Add annotations to `/crates/{name}/reverse_dependencies` endpoints --- src/controllers/krate/metadata.rs | 9 ++++++++- src/router.rs | 5 +---- ...rates_io__openapi__tests__openapi_snapshot.snap | 14 ++++++++++++++ 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/controllers/krate/metadata.rs b/src/controllers/krate/metadata.rs index ee38beb769f..f0befa72aa7 100644 --- a/src/controllers/krate/metadata.rs +++ b/src/controllers/krate/metadata.rs @@ -265,7 +265,14 @@ pub async fn readme( } } -/// Handles the `GET /crates/:crate_id/reverse_dependencies` route. +/// List reverse dependencies of a crate. +#[utoipa::path( + get, + path = "/api/v1/crates/{name}/reverse_dependencies", + operation_id = "list_reverse_dependencies", + tag = "crates", + responses((status = 200, description = "Successful Response")), +)] pub async fn reverse_dependencies( app: AppState, Path(name): Path, diff --git a/src/router.rs b/src/router.rs index d95ccab478e..c87dd252c52 100644 --- a/src/router.rs +++ b/src/router.rs @@ -39,13 +39,10 @@ pub fn build_axum_router(state: AppState) -> Router<()> { .routes(routes!(krate::follow::following)) .routes(routes!(krate::owners::owner_team)) .routes(routes!(krate::owners::owner_user)) + .routes(routes!(krate::metadata::reverse_dependencies)) .split_for_parts(); let mut router = router - .route( - "/api/v1/crates/:crate_id/reverse_dependencies", - get(krate::metadata::reverse_dependencies), - ) .route("/api/v1/keywords", get(keyword::index)) .route("/api/v1/keywords/:keyword_id", get(keyword::show)) .route("/api/v1/categories", get(category::index)) diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index 06f55bd8e5c..00d35b5f506 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -211,6 +211,20 @@ snapshot_kind: text ] } }, + "/api/v1/crates/{name}/reverse_dependencies": { + "get": { + "operationId": "list_reverse_dependencies", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "List reverse dependencies of a crate.", + "tags": [ + "crates" + ] + } + }, "/api/v1/crates/{name}/versions": { "get": { "operationId": "list_crate_versions", From 8cf41e09bcde9c1291ff4f4a83839cb1de9b5bf7 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 17:10:08 +0100 Subject: [PATCH 19/43] utoipa: Add annotations to `/keywords` endpoints --- src/controllers/keyword.rs | 9 ++++++++- src/router.rs | 2 +- ...rates_io__openapi__tests__openapi_snapshot.snap | 14 ++++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/controllers/keyword.rs b/src/controllers/keyword.rs index 6843b72461a..7ec5c36bd96 100644 --- a/src/controllers/keyword.rs +++ b/src/controllers/keyword.rs @@ -15,7 +15,14 @@ pub struct IndexQuery { sort: Option, } -/// Handles the `GET /keywords` route. +/// List all keywords. +#[utoipa::path( + get, + path = "/api/v1/keywords", + operation_id = "list_keywords", + tag = "keywords", + responses((status = 200, description = "Successful Response")), +)] pub async fn index(state: AppState, qp: Query, req: Parts) -> AppResult { use crate::schema::keywords; diff --git a/src/router.rs b/src/router.rs index c87dd252c52..686c334520d 100644 --- a/src/router.rs +++ b/src/router.rs @@ -40,10 +40,10 @@ pub fn build_axum_router(state: AppState) -> Router<()> { .routes(routes!(krate::owners::owner_team)) .routes(routes!(krate::owners::owner_user)) .routes(routes!(krate::metadata::reverse_dependencies)) + .routes(routes!(keyword::index)) .split_for_parts(); let mut router = router - .route("/api/v1/keywords", get(keyword::index)) .route("/api/v1/keywords/:keyword_id", get(keyword::show)) .route("/api/v1/categories", get(category::index)) .route("/api/v1/categories/:category_id", get(category::show)) diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index 00d35b5f506..0c14a45fe48 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -369,6 +369,20 @@ snapshot_kind: text "versions" ] } + }, + "/api/v1/keywords": { + "get": { + "operationId": "list_keywords", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "List all keywords.", + "tags": [ + "keywords" + ] + } } }, "servers": [ From 51150531cd9097427b975130411113ff6a0021a4 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 17:12:33 +0100 Subject: [PATCH 20/43] utoipa: Add annotations to `/keywords/{keyword}` endpoints --- src/controllers/keyword.rs | 9 ++++++++- src/router.rs | 2 +- ...rates_io__openapi__tests__openapi_snapshot.snap | 14 ++++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/controllers/keyword.rs b/src/controllers/keyword.rs index 7ec5c36bd96..c7c4efbfab3 100644 --- a/src/controllers/keyword.rs +++ b/src/controllers/keyword.rs @@ -49,7 +49,14 @@ pub async fn index(state: AppState, qp: Query, req: Parts) -> AppRes })) } -/// Handles the `GET /keywords/:keyword_id` route. +/// Get keyword metadata. +#[utoipa::path( + get, + path = "/api/v1/keywords/{keyword}", + operation_id = "get_keyword", + tag = "keywords", + responses((status = 200, description = "Successful Response")), +)] pub async fn show(Path(name): Path, state: AppState) -> AppResult { let mut conn = state.db_read().await?; let kw = Keyword::find_by_keyword(&mut conn, &name).await?; diff --git a/src/router.rs b/src/router.rs index 686c334520d..8d614d82c2f 100644 --- a/src/router.rs +++ b/src/router.rs @@ -41,10 +41,10 @@ pub fn build_axum_router(state: AppState) -> Router<()> { .routes(routes!(krate::owners::owner_user)) .routes(routes!(krate::metadata::reverse_dependencies)) .routes(routes!(keyword::index)) + .routes(routes!(keyword::show)) .split_for_parts(); let mut router = router - .route("/api/v1/keywords/:keyword_id", get(keyword::show)) .route("/api/v1/categories", get(category::index)) .route("/api/v1/categories/:category_id", get(category::show)) .route("/api/v1/category_slugs", get(category::slugs)) diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index 0c14a45fe48..879af7b63df 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -383,6 +383,20 @@ snapshot_kind: text "keywords" ] } + }, + "/api/v1/keywords/{keyword}": { + "get": { + "operationId": "get_keyword", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "Get keyword metadata.", + "tags": [ + "keywords" + ] + } } }, "servers": [ From cb7c6869b7e9211dc2f91b5fc5fa69f2f2686931 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 17:19:25 +0100 Subject: [PATCH 21/43] utoipa: Add annotations to `/categories` endpoints --- src/controllers/category.rs | 9 ++++++++- src/router.rs | 2 +- ...rates_io__openapi__tests__openapi_snapshot.snap | 14 ++++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/controllers/category.rs b/src/controllers/category.rs index 6384fcf880c..dc791275795 100644 --- a/src/controllers/category.rs +++ b/src/controllers/category.rs @@ -12,7 +12,14 @@ use diesel::QueryDsl; use diesel_async::RunQueryDsl; use http::request::Parts; -/// Handles the `GET /categories` route. +/// List all categories. +#[utoipa::path( + get, + path = "/api/v1/categories", + operation_id = "list_categories", + tag = "categories", + responses((status = 200, description = "Successful Response")), +)] pub async fn index(app: AppState, req: Parts) -> AppResult { // FIXME: There are 69 categories, 47 top level. This isn't going to // grow by an OoM. We need a limit for /summary, but we don't need diff --git a/src/router.rs b/src/router.rs index 8d614d82c2f..46c3d9799f5 100644 --- a/src/router.rs +++ b/src/router.rs @@ -42,10 +42,10 @@ pub fn build_axum_router(state: AppState) -> Router<()> { .routes(routes!(krate::metadata::reverse_dependencies)) .routes(routes!(keyword::index)) .routes(routes!(keyword::show)) + .routes(routes!(category::index)) .split_for_parts(); let mut router = router - .route("/api/v1/categories", get(category::index)) .route("/api/v1/categories/:category_id", get(category::show)) .route("/api/v1/category_slugs", get(category::slugs)) .route( diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index 879af7b63df..0fa44a02e2e 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -20,6 +20,20 @@ snapshot_kind: text }, "openapi": "3.1.0", "paths": { + "/api/v1/categories": { + "get": { + "operationId": "list_categories", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "List all categories.", + "tags": [ + "categories" + ] + } + }, "/api/v1/crates": { "get": { "description": "Called in a variety of scenarios in the front end, including:\n- Alphabetical listing of crates\n- List of crates under a specific owner\n- Listing a user's followed crates", From ec22ad15194547d06d63f73cd04361ba80bc779c Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 17:20:55 +0100 Subject: [PATCH 22/43] utoipa: Add annotations to `/categories/{category}` endpoints --- src/controllers/category.rs | 9 ++++++++- src/router.rs | 2 +- ...rates_io__openapi__tests__openapi_snapshot.snap | 14 ++++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/controllers/category.rs b/src/controllers/category.rs index dc791275795..c87030fa350 100644 --- a/src/controllers/category.rs +++ b/src/controllers/category.rs @@ -48,7 +48,14 @@ pub async fn index(app: AppState, req: Parts) -> AppResult { })) } -/// Handles the `GET /categories/:category_id` route. +/// Get category metadata. +#[utoipa::path( + get, + path = "/api/v1/categories/{category}", + operation_id = "get_category", + tag = "categories", + responses((status = 200, description = "Successful Response")), +)] pub async fn show(state: AppState, Path(slug): Path) -> AppResult { let mut conn = state.db_read().await?; diff --git a/src/router.rs b/src/router.rs index 46c3d9799f5..1ebe7028e51 100644 --- a/src/router.rs +++ b/src/router.rs @@ -43,10 +43,10 @@ pub fn build_axum_router(state: AppState) -> Router<()> { .routes(routes!(keyword::index)) .routes(routes!(keyword::show)) .routes(routes!(category::index)) + .routes(routes!(category::show)) .split_for_parts(); let mut router = router - .route("/api/v1/categories/:category_id", get(category::show)) .route("/api/v1/category_slugs", get(category::slugs)) .route( "/api/v1/users/:user_id", diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index 0fa44a02e2e..8a2323916a0 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -34,6 +34,20 @@ snapshot_kind: text ] } }, + "/api/v1/categories/{category}": { + "get": { + "operationId": "get_category", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "Get category metadata.", + "tags": [ + "categories" + ] + } + }, "/api/v1/crates": { "get": { "description": "Called in a variety of scenarios in the front end, including:\n- Alphabetical listing of crates\n- List of crates under a specific owner\n- Listing a user's followed crates", From 42f64e42b39ee7325359d2d282973dab320f419a Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 17:22:15 +0100 Subject: [PATCH 23/43] utoipa: Add annotations to `/category_slugs` endpoints --- src/controllers/category.rs | 9 ++++++++- src/router.rs | 2 +- ...rates_io__openapi__tests__openapi_snapshot.snap | 14 ++++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/controllers/category.rs b/src/controllers/category.rs index c87030fa350..b7ea29d18b5 100644 --- a/src/controllers/category.rs +++ b/src/controllers/category.rs @@ -88,7 +88,14 @@ pub async fn show(state: AppState, Path(slug): Path) -> AppResult AppResult { let mut conn = state.db_read().await?; diff --git a/src/router.rs b/src/router.rs index 1ebe7028e51..f7516355c7c 100644 --- a/src/router.rs +++ b/src/router.rs @@ -44,10 +44,10 @@ pub fn build_axum_router(state: AppState) -> Router<()> { .routes(routes!(keyword::show)) .routes(routes!(category::index)) .routes(routes!(category::show)) + .routes(routes!(category::slugs)) .split_for_parts(); let mut router = router - .route("/api/v1/category_slugs", get(category::slugs)) .route( "/api/v1/users/:user_id", get(user::other::show).put(update_user), diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index 8a2323916a0..259108b771c 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -48,6 +48,20 @@ snapshot_kind: text ] } }, + "/api/v1/category_slugs": { + "get": { + "operationId": "list_category_slugs", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "List all available category slugs.", + "tags": [ + "categories" + ] + } + }, "/api/v1/crates": { "get": { "description": "Called in a variety of scenarios in the front end, including:\n- Alphabetical listing of crates\n- List of crates under a specific owner\n- Listing a user's followed crates", From ccba727653970ee68bd4083c2d7b6a4a056fea7a Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 17:28:36 +0100 Subject: [PATCH 24/43] utoipa: Add annotations to `/users/{user}` endpoints --- src/controllers/user/other.rs | 9 ++++++- src/controllers/user/update.rs | 13 ++++++++- src/router.rs | 6 +---- ..._io__openapi__tests__openapi_snapshot.snap | 27 +++++++++++++++++++ 4 files changed, 48 insertions(+), 7 deletions(-) diff --git a/src/controllers/user/other.rs b/src/controllers/user/other.rs index 8ba6459abb3..d653c6414fb 100644 --- a/src/controllers/user/other.rs +++ b/src/controllers/user/other.rs @@ -12,7 +12,14 @@ use crate::sql::lower; use crate::util::errors::AppResult; use crate::views::EncodablePublicUser; -/// Handles the `GET /users/:user_id` route. +/// Find user by login. +#[utoipa::path( + get, + path = "/api/v1/users/{user}", + operation_id = "get_user", + tag = "users", + responses((status = 200, description = "Successful Response")), +)] pub async fn show(state: AppState, Path(user_name): Path) -> AppResult { let mut conn = state.db_read_prefer_primary().await?; diff --git a/src/controllers/user/update.rs b/src/controllers/user/update.rs index 9c14a308e2c..06e934ee7c9 100644 --- a/src/controllers/user/update.rs +++ b/src/controllers/user/update.rs @@ -24,7 +24,18 @@ pub struct User { publish_notifications: Option, } -/// Handles the `PUT /users/:user_id` route. +/// Update user settings. +/// +/// This endpoint allows users to update their email address and publish notifications settings. +/// +/// The `id` parameter needs to match the ID of the currently authenticated user. +#[utoipa::path( + put, + path = "/api/v1/users/{user}", + operation_id = "update_user", + tag = "users", + responses((status = 200, description = "Successful Response")), +)] pub async fn update_user( state: AppState, Path(param_user_id): Path, diff --git a/src/router.rs b/src/router.rs index f7516355c7c..639d81a0cd9 100644 --- a/src/router.rs +++ b/src/router.rs @@ -5,7 +5,6 @@ use http::{Method, StatusCode}; use utoipa_axum::routes; use crate::app::AppState; -use crate::controllers::user::update_user; use crate::controllers::*; use crate::openapi::BaseOpenApi; use crate::util::errors::not_found; @@ -45,13 +44,10 @@ pub fn build_axum_router(state: AppState) -> Router<()> { .routes(routes!(category::index)) .routes(routes!(category::show)) .routes(routes!(category::slugs)) + .routes(routes!(user::other::show, user::update::update_user)) .split_for_parts(); let mut router = router - .route( - "/api/v1/users/:user_id", - get(user::other::show).put(update_user), - ) .route("/api/v1/users/:user_id/stats", get(user::other::stats)) .route("/api/v1/teams/:team_id", get(team::show_team)) .route("/api/v1/me", get(user::me::me)) diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index 259108b771c..84d8b580524 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -439,6 +439,33 @@ snapshot_kind: text "keywords" ] } + }, + "/api/v1/users/{user}": { + "get": { + "operationId": "get_user", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "Find user by login.", + "tags": [ + "users" + ] + }, + "put": { + "description": "This endpoint allows users to update their email address and publish notifications settings.\n\nThe `id` parameter needs to match the ID of the currently authenticated user.", + "operationId": "update_user", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "Update user settings.", + "tags": [ + "users" + ] + } } }, "servers": [ From 949634b8f94301a87cd21e9b1eeba44cd307f079 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 17:35:53 +0100 Subject: [PATCH 25/43] utoipa: Add annotations to `/users/{id}/stats` endpoints --- src/controllers/user/other.rs | 12 +++++++++++- src/router.rs | 2 +- ...ates_io__openapi__tests__openapi_snapshot.snap | 15 +++++++++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/controllers/user/other.rs b/src/controllers/user/other.rs index d653c6414fb..05602a415b2 100644 --- a/src/controllers/user/other.rs +++ b/src/controllers/user/other.rs @@ -35,7 +35,17 @@ pub async fn show(state: AppState, Path(user_name): Path) -> AppResult) -> AppResult { let mut conn = state.db_read_prefer_primary().await?; diff --git a/src/router.rs b/src/router.rs index 639d81a0cd9..ac8a157c660 100644 --- a/src/router.rs +++ b/src/router.rs @@ -45,10 +45,10 @@ pub fn build_axum_router(state: AppState) -> Router<()> { .routes(routes!(category::show)) .routes(routes!(category::slugs)) .routes(routes!(user::other::show, user::update::update_user)) + .routes(routes!(user::other::stats)) .split_for_parts(); let mut router = router - .route("/api/v1/users/:user_id/stats", get(user::other::stats)) .route("/api/v1/teams/:team_id", get(team::show_team)) .route("/api/v1/me", get(user::me::me)) .route("/api/v1/me/updates", get(user::me::updates)) diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index 84d8b580524..5cf744c1fea 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -440,6 +440,21 @@ snapshot_kind: text ] } }, + "/api/v1/users/{id}/stats": { + "get": { + "description": "This currently only returns the total number of downloads for crates owned\nby the user.", + "operationId": "get_user_stats", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "Get user stats.", + "tags": [ + "users" + ] + } + }, "/api/v1/users/{user}": { "get": { "operationId": "get_user", From 3767cef3cfc8d1a1c39aec58d6da35ef6cd5fdbd Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 17:37:16 +0100 Subject: [PATCH 26/43] utoipa: Add annotations to `/teams/{team}` endpoints --- src/controllers/team.rs | 9 ++++++++- src/router.rs | 2 +- ...rates_io__openapi__tests__openapi_snapshot.snap | 14 ++++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/controllers/team.rs b/src/controllers/team.rs index b1263ae463d..ccfc482e3b8 100644 --- a/src/controllers/team.rs +++ b/src/controllers/team.rs @@ -8,7 +8,14 @@ use axum_extra::response::ErasedJson; use diesel::prelude::*; use diesel_async::RunQueryDsl; -/// Handles the `GET /teams/:team_id` route. +/// Find team by login. +#[utoipa::path( + get, + path = "/api/v1/teams/{team}", + operation_id = "get_team", + tag = "teams", + responses((status = 200, description = "Successful Response")), +)] pub async fn show_team(state: AppState, Path(name): Path) -> AppResult { use crate::schema::teams::dsl::{login, teams}; diff --git a/src/router.rs b/src/router.rs index ac8a157c660..3d14274b408 100644 --- a/src/router.rs +++ b/src/router.rs @@ -46,10 +46,10 @@ pub fn build_axum_router(state: AppState) -> Router<()> { .routes(routes!(category::slugs)) .routes(routes!(user::other::show, user::update::update_user)) .routes(routes!(user::other::stats)) + .routes(routes!(team::show_team)) .split_for_parts(); let mut router = router - .route("/api/v1/teams/:team_id", get(team::show_team)) .route("/api/v1/me", get(user::me::me)) .route("/api/v1/me/updates", get(user::me::updates)) .route("/api/v1/me/tokens", get(token::list).put(token::new)) diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index 5cf744c1fea..97b7bcaad4e 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -440,6 +440,20 @@ snapshot_kind: text ] } }, + "/api/v1/teams/{team}": { + "get": { + "operationId": "get_team", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "Find team by login.", + "tags": [ + "teams" + ] + } + }, "/api/v1/users/{id}/stats": { "get": { "description": "This currently only returns the total number of downloads for crates owned\nby the user.", From 67d13f70d9ccf9548b5aab4c77b261451bc95bad Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 17:39:10 +0100 Subject: [PATCH 27/43] utoipa: Add annotations to `/me` endpoints --- src/controllers/user/me.rs | 9 ++++++++- src/router.rs | 2 +- ...rates_io__openapi__tests__openapi_snapshot.snap | 14 ++++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/controllers/user/me.rs b/src/controllers/user/me.rs index 210db02e4c3..00699c7778e 100644 --- a/src/controllers/user/me.rs +++ b/src/controllers/user/me.rs @@ -19,7 +19,14 @@ use crate::util::errors::{bad_request, AppResult}; use crate::util::BytesRequest; use crate::views::{EncodableMe, EncodablePrivateUser, EncodableVersion, OwnedCrate}; -/// Handles the `GET /me` route. +/// Get the currently authenticated user. +#[utoipa::path( + get, + path = "/api/v1/me", + operation_id = "get_authenticated_user", + tag = "users", + responses((status = 200, description = "Successful Response")), +)] pub async fn me(app: AppState, req: Parts) -> AppResult> { let mut conn = app.db_read_prefer_primary().await?; let user_id = AuthCheck::only_cookie() diff --git a/src/router.rs b/src/router.rs index 3d14274b408..a80751ddf89 100644 --- a/src/router.rs +++ b/src/router.rs @@ -47,10 +47,10 @@ pub fn build_axum_router(state: AppState) -> Router<()> { .routes(routes!(user::other::show, user::update::update_user)) .routes(routes!(user::other::stats)) .routes(routes!(team::show_team)) + .routes(routes!(user::me::me)) .split_for_parts(); let mut router = router - .route("/api/v1/me", get(user::me::me)) .route("/api/v1/me/updates", get(user::me::updates)) .route("/api/v1/me/tokens", get(token::list).put(token::new)) .route( diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index 97b7bcaad4e..37dee131e3c 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -440,6 +440,20 @@ snapshot_kind: text ] } }, + "/api/v1/me": { + "get": { + "operationId": "get_authenticated_user", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "Get the currently authenticated user.", + "tags": [ + "users" + ] + } + }, "/api/v1/teams/{team}": { "get": { "operationId": "get_team", From dd4f19227ed2b764c669055765db3aecf9983985 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 17:42:02 +0100 Subject: [PATCH 28/43] utoipa: Add annotations to `/me/updates` endpoints --- src/controllers/user/me.rs | 9 ++++++++- src/router.rs | 2 +- ...rates_io__openapi__tests__openapi_snapshot.snap | 14 ++++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/controllers/user/me.rs b/src/controllers/user/me.rs index 00699c7778e..761158b6b94 100644 --- a/src/controllers/user/me.rs +++ b/src/controllers/user/me.rs @@ -69,7 +69,14 @@ pub async fn me(app: AppState, req: Parts) -> AppResult> { })) } -/// Handles the `GET /me/updates` route. +/// List versions of crates that the authenticated user follows. +#[utoipa::path( + get, + path = "/api/v1/me/updates", + operation_id = "get_authenticated_user_updates", + tag = "versions", + responses((status = 200, description = "Successful Response")), +)] pub async fn updates(app: AppState, req: Parts) -> AppResult { let mut conn = app.db_read_prefer_primary().await?; let auth = AuthCheck::only_cookie().check(&req, &mut conn).await?; diff --git a/src/router.rs b/src/router.rs index a80751ddf89..a14fa8dbedf 100644 --- a/src/router.rs +++ b/src/router.rs @@ -48,10 +48,10 @@ pub fn build_axum_router(state: AppState) -> Router<()> { .routes(routes!(user::other::stats)) .routes(routes!(team::show_team)) .routes(routes!(user::me::me)) + .routes(routes!(user::me::updates)) .split_for_parts(); let mut router = router - .route("/api/v1/me/updates", get(user::me::updates)) .route("/api/v1/me/tokens", get(token::list).put(token::new)) .route( "/api/v1/me/tokens/:id", diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index 37dee131e3c..7ef37b45aa2 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -454,6 +454,20 @@ snapshot_kind: text ] } }, + "/api/v1/me/updates": { + "get": { + "operationId": "get_authenticated_user_updates", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "List versions of crates that the authenticated user follows.", + "tags": [ + "versions" + ] + } + }, "/api/v1/teams/{team}": { "get": { "operationId": "get_team", From c5987ae5c23e8a57e5f70ab452b4b94c5f26793f Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 17:45:33 +0100 Subject: [PATCH 29/43] utoipa: Add annotations to `/me/tokens` endpoints --- src/controllers/token.rs | 18 +++++++++++-- src/router.rs | 2 +- ..._io__openapi__tests__openapi_snapshot.snap | 26 +++++++++++++++++++ 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/src/controllers/token.rs b/src/controllers/token.rs index 8f6a4d2c301..a6994d81033 100644 --- a/src/controllers/token.rs +++ b/src/controllers/token.rs @@ -35,7 +35,14 @@ impl GetParams { } } -/// Handles the `GET /me/tokens` route. +/// List all API tokens of the authenticated user. +#[utoipa::path( + get, + path = "/api/v1/me/tokens", + operation_id = "list_api_tokens", + tag = "api_tokens", + responses((status = 200, description = "Successful Response")), +)] pub async fn list( app: AppState, Query(params): Query, @@ -76,7 +83,14 @@ pub struct NewApiTokenRequest { api_token: NewApiToken, } -/// Handles the `PUT /me/tokens` route. +/// Create a new API token. +#[utoipa::path( + put, + path = "/api/v1/me/tokens", + operation_id = "create_api_token", + tag = "api_tokens", + responses((status = 200, description = "Successful Response")), +)] pub async fn new( app: AppState, parts: Parts, diff --git a/src/router.rs b/src/router.rs index a14fa8dbedf..99825681c84 100644 --- a/src/router.rs +++ b/src/router.rs @@ -49,10 +49,10 @@ pub fn build_axum_router(state: AppState) -> Router<()> { .routes(routes!(team::show_team)) .routes(routes!(user::me::me)) .routes(routes!(user::me::updates)) + .routes(routes!(token::list, token::new)) .split_for_parts(); let mut router = router - .route("/api/v1/me/tokens", get(token::list).put(token::new)) .route( "/api/v1/me/tokens/:id", get(token::show).delete(token::revoke), diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index 7ef37b45aa2..6ac8fd3d50c 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -454,6 +454,32 @@ snapshot_kind: text ] } }, + "/api/v1/me/tokens": { + "get": { + "operationId": "list_api_tokens", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "List all API tokens of the authenticated user.", + "tags": [ + "api_tokens" + ] + }, + "put": { + "operationId": "create_api_token", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "Create a new API token.", + "tags": [ + "api_tokens" + ] + } + }, "/api/v1/me/updates": { "get": { "operationId": "get_authenticated_user_updates", From 2730095a1ac4acd33da92fc983a64e77791ba8f3 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 17:53:39 +0100 Subject: [PATCH 30/43] utoipa: Add annotations to `/me/tokens/{id}` endpoints --- src/controllers/token.rs | 18 +++++++++++-- src/router.rs | 5 +--- ..._io__openapi__tests__openapi_snapshot.snap | 26 +++++++++++++++++++ 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/src/controllers/token.rs b/src/controllers/token.rs index a6994d81033..86a3cbaa073 100644 --- a/src/controllers/token.rs +++ b/src/controllers/token.rs @@ -179,7 +179,14 @@ pub async fn new( Ok(json!({ "api_token": api_token })) } -/// Handles the `GET /me/tokens/:id` route. +/// Find API token by id. +#[utoipa::path( + get, + path = "/api/v1/me/tokens/{id}", + operation_id = "get_api_token", + tag = "api_tokens", + responses((status = 200, description = "Successful Response")), +)] pub async fn show(app: AppState, Path(id): Path, req: Parts) -> AppResult { let mut conn = app.db_write().await?; let auth = AuthCheck::default().check(&req, &mut conn).await?; @@ -193,7 +200,14 @@ pub async fn show(app: AppState, Path(id): Path, req: Parts) -> AppResult, req: Parts) -> AppResult { let mut conn = app.db_write().await?; let auth = AuthCheck::default().check(&req, &mut conn).await?; diff --git a/src/router.rs b/src/router.rs index 99825681c84..00067debd76 100644 --- a/src/router.rs +++ b/src/router.rs @@ -50,13 +50,10 @@ pub fn build_axum_router(state: AppState) -> Router<()> { .routes(routes!(user::me::me)) .routes(routes!(user::me::updates)) .routes(routes!(token::list, token::new)) + .routes(routes!(token::show, token::revoke)) .split_for_parts(); let mut router = router - .route( - "/api/v1/me/tokens/:id", - get(token::show).delete(token::revoke), - ) .route("/api/v1/tokens/current", delete(token::revoke_current)) .route( "/api/v1/me/crate_owner_invitations", diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index 6ac8fd3d50c..6031bd2af7e 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -480,6 +480,32 @@ snapshot_kind: text ] } }, + "/api/v1/me/tokens/{id}": { + "delete": { + "operationId": "revoke_api_token", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "Revoke API token.", + "tags": [ + "api_tokens" + ] + }, + "get": { + "operationId": "get_api_token", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "Find API token by id.", + "tags": [ + "api_tokens" + ] + } + }, "/api/v1/me/updates": { "get": { "operationId": "get_authenticated_user_updates", From 868d2dae548d84bd5e05b22f76c441ead2c2442b Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 17:54:12 +0100 Subject: [PATCH 31/43] utoipa: Add annotations to `/tokens/current` endpoints --- src/controllers/token.rs | 12 +++++++++++- src/router.rs | 2 +- ...ates_io__openapi__tests__openapi_snapshot.snap | 15 +++++++++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/controllers/token.rs b/src/controllers/token.rs index 86a3cbaa073..14f0a06626d 100644 --- a/src/controllers/token.rs +++ b/src/controllers/token.rs @@ -220,7 +220,17 @@ pub async fn revoke(app: AppState, Path(id): Path, req: Parts) -> AppResult Ok(json!({})) } -/// Handles the `DELETE /tokens/current` route. +/// Revoke the current API token. +/// +/// This endpoint revokes the API token that is used to authenticate +/// the request. +#[utoipa::path( + delete, + path = "/api/v1/tokens/current", + operation_id = "revoke_current_api_token", + tag = "api_tokens", + responses((status = 200, description = "Successful Response")), +)] pub async fn revoke_current(app: AppState, req: Parts) -> AppResult { let mut conn = app.db_write().await?; let auth = AuthCheck::default().check(&req, &mut conn).await?; diff --git a/src/router.rs b/src/router.rs index 00067debd76..dcaa9b2e095 100644 --- a/src/router.rs +++ b/src/router.rs @@ -51,10 +51,10 @@ pub fn build_axum_router(state: AppState) -> Router<()> { .routes(routes!(user::me::updates)) .routes(routes!(token::list, token::new)) .routes(routes!(token::show, token::revoke)) + .routes(routes!(token::revoke_current)) .split_for_parts(); let mut router = router - .route("/api/v1/tokens/current", delete(token::revoke_current)) .route( "/api/v1/me/crate_owner_invitations", get(crate_owner_invitation::list), diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index 6031bd2af7e..69a168cb81c 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -534,6 +534,21 @@ snapshot_kind: text ] } }, + "/api/v1/tokens/current": { + "delete": { + "description": "This endpoint revokes the API token that is used to authenticate\nthe request.", + "operationId": "revoke_current_api_token", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "Revoke the current API token.", + "tags": [ + "api_tokens" + ] + } + }, "/api/v1/users/{id}/stats": { "get": { "description": "This currently only returns the total number of downloads for crates owned\nby the user.", From 6dafec27daa636ce5f12afc294cdf0e6ae591ccb Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 17:56:04 +0100 Subject: [PATCH 32/43] utoipa: Add annotations to `/me/crate_owner_invitations` endpoints --- src/controllers/crate_owner_invitation.rs | 9 ++++++++- src/router.rs | 5 +---- ...rates_io__openapi__tests__openapi_snapshot.snap | 14 ++++++++++++++ 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/controllers/crate_owner_invitation.rs b/src/controllers/crate_owner_invitation.rs index b9b7383faf8..96fdab464c7 100644 --- a/src/controllers/crate_owner_invitation.rs +++ b/src/controllers/crate_owner_invitation.rs @@ -23,7 +23,14 @@ use http::request::Parts; use indexmap::IndexMap; use std::collections::{HashMap, HashSet}; -/// Handles the `GET /api/v1/me/crate_owner_invitations` route. +/// List all crate owner invitations for the authenticated user. +#[utoipa::path( + get, + path = "/api/v1/me/crate_owner_invitations", + operation_id = "list_crate_owner_invitations", + tag = "owners", + responses((status = 200, description = "Successful Response")), +)] pub async fn list(app: AppState, req: Parts) -> AppResult { let mut conn = app.db_read().await?; let auth = AuthCheck::only_cookie().check(&req, &mut conn).await?; diff --git a/src/router.rs b/src/router.rs index dcaa9b2e095..093e490e71e 100644 --- a/src/router.rs +++ b/src/router.rs @@ -52,13 +52,10 @@ pub fn build_axum_router(state: AppState) -> Router<()> { .routes(routes!(token::list, token::new)) .routes(routes!(token::show, token::revoke)) .routes(routes!(token::revoke_current)) + .routes(routes!(crate_owner_invitation::list)) .split_for_parts(); let mut router = router - .route( - "/api/v1/me/crate_owner_invitations", - get(crate_owner_invitation::list), - ) .route( "/api/v1/me/crate_owner_invitations/:crate_id", put(crate_owner_invitation::handle_invite), diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index 69a168cb81c..8766eccebb0 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -454,6 +454,20 @@ snapshot_kind: text ] } }, + "/api/v1/me/crate_owner_invitations": { + "get": { + "operationId": "list_crate_owner_invitations", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "List all crate owner invitations for the authenticated user.", + "tags": [ + "owners" + ] + } + }, "/api/v1/me/tokens": { "get": { "operationId": "list_api_tokens", From b68719b02fef13de073c6c2dbdc4a3b8af67f91d Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 18:03:45 +0100 Subject: [PATCH 33/43] utoipa: Add annotations to `/me/crate_owner_invitations/{crate_id}` endpoints --- src/controllers/crate_owner_invitation.rs | 9 ++++++++- src/router.rs | 5 +---- ...rates_io__openapi__tests__openapi_snapshot.snap | 14 ++++++++++++++ 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/controllers/crate_owner_invitation.rs b/src/controllers/crate_owner_invitation.rs index 96fdab464c7..34368a6deb8 100644 --- a/src/controllers/crate_owner_invitation.rs +++ b/src/controllers/crate_owner_invitation.rs @@ -272,7 +272,14 @@ struct OwnerInvitation { crate_owner_invite: InvitationResponse, } -/// Handles the `PUT /api/v1/me/crate_owner_invitations/:crate_id` route. +/// Accept or decline a crate owner invitation. +#[utoipa::path( + put, + path = "/api/v1/me/crate_owner_invitations/{crate_id}", + operation_id = "handle_crate_owner_invitation", + tag = "owners", + responses((status = 200, description = "Successful Response")), +)] pub async fn handle_invite(state: AppState, req: BytesRequest) -> AppResult { let (parts, body) = req.0.into_parts(); diff --git a/src/router.rs b/src/router.rs index 093e490e71e..db6df5ea2be 100644 --- a/src/router.rs +++ b/src/router.rs @@ -53,13 +53,10 @@ pub fn build_axum_router(state: AppState) -> Router<()> { .routes(routes!(token::show, token::revoke)) .routes(routes!(token::revoke_current)) .routes(routes!(crate_owner_invitation::list)) + .routes(routes!(crate_owner_invitation::handle_invite)) .split_for_parts(); let mut router = router - .route( - "/api/v1/me/crate_owner_invitations/:crate_id", - put(crate_owner_invitation::handle_invite), - ) .route( "/api/v1/me/crate_owner_invitations/accept/:token", put(crate_owner_invitation::handle_invite_with_token), diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index 8766eccebb0..8806104a645 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -468,6 +468,20 @@ snapshot_kind: text ] } }, + "/api/v1/me/crate_owner_invitations/{crate_id}": { + "put": { + "operationId": "handle_crate_owner_invitation", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "Accept or decline a crate owner invitation.", + "tags": [ + "owners" + ] + } + }, "/api/v1/me/tokens": { "get": { "operationId": "list_api_tokens", From 29ca13c96bb89828e7032436fbde161d8eb24378 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 18:05:15 +0100 Subject: [PATCH 34/43] utoipa: Add annotations to `/me/crate_owner_invitations/accept/{token}` endpoints --- src/controllers/crate_owner_invitation.rs | 9 ++++++++- src/router.rs | 5 +---- ...rates_io__openapi__tests__openapi_snapshot.snap | 14 ++++++++++++++ 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/controllers/crate_owner_invitation.rs b/src/controllers/crate_owner_invitation.rs index 34368a6deb8..8d8653c2ac3 100644 --- a/src/controllers/crate_owner_invitation.rs +++ b/src/controllers/crate_owner_invitation.rs @@ -307,7 +307,14 @@ pub async fn handle_invite(state: AppState, req: BytesRequest) -> AppResult, diff --git a/src/router.rs b/src/router.rs index db6df5ea2be..dc8681f7c65 100644 --- a/src/router.rs +++ b/src/router.rs @@ -54,13 +54,10 @@ pub fn build_axum_router(state: AppState) -> Router<()> { .routes(routes!(token::revoke_current)) .routes(routes!(crate_owner_invitation::list)) .routes(routes!(crate_owner_invitation::handle_invite)) + .routes(routes!(crate_owner_invitation::handle_invite_with_token)) .split_for_parts(); let mut router = router - .route( - "/api/v1/me/crate_owner_invitations/accept/:token", - put(crate_owner_invitation::handle_invite_with_token), - ) .route( "/api/v1/me/email_notifications", put(user::me::update_email_notifications), diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index 8806104a645..76602548a71 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -468,6 +468,20 @@ snapshot_kind: text ] } }, + "/api/v1/me/crate_owner_invitations/accept/{token}": { + "put": { + "operationId": "accept_crate_owner_invitation_with_token", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "Accept a crate owner invitation with a token.", + "tags": [ + "owners" + ] + } + }, "/api/v1/me/crate_owner_invitations/{crate_id}": { "put": { "operationId": "handle_crate_owner_invitation", From 834624676e1a3c322106a40b735d0e8d4c1bbe0c Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 18:13:57 +0100 Subject: [PATCH 35/43] utoipa: Add annotations to `/me/email_notifications` endpoints --- src/controllers/user/me.rs | 13 ++++++++++++- src/router.rs | 5 +---- ...tes_io__openapi__tests__openapi_snapshot.snap | 16 ++++++++++++++++ 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/controllers/user/me.rs b/src/controllers/user/me.rs index 761158b6b94..e501be8366a 100644 --- a/src/controllers/user/me.rs +++ b/src/controllers/user/me.rs @@ -133,7 +133,18 @@ pub async fn confirm_user_email(state: AppState, Path(token): Path) -> A ok_true() } -/// Handles `PUT /me/email_notifications` route +/// Update email notification settings for the authenticated user. +/// +/// This endpoint was implemented for an experimental feature that was never +/// fully implemented. It is now deprecated and will be removed in the future. +#[utoipa::path( + put, + path = "/api/v1/me/email_notifications", + operation_id = "update_email_notifications", + tag = "users", + responses((status = 200, description = "Successful Response")), +)] +#[deprecated] pub async fn update_email_notifications(app: AppState, req: BytesRequest) -> AppResult { use diesel::pg::upsert::excluded; diff --git a/src/router.rs b/src/router.rs index dc8681f7c65..4ec4d980acf 100644 --- a/src/router.rs +++ b/src/router.rs @@ -55,13 +55,10 @@ pub fn build_axum_router(state: AppState) -> Router<()> { .routes(routes!(crate_owner_invitation::list)) .routes(routes!(crate_owner_invitation::handle_invite)) .routes(routes!(crate_owner_invitation::handle_invite_with_token)) + .routes(routes!(user::me::update_email_notifications)) .split_for_parts(); let mut router = router - .route( - "/api/v1/me/email_notifications", - put(user::me::update_email_notifications), - ) .route("/api/v1/summary", get(summary::summary)) .route( "/api/v1/confirm/:email_token", diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index 76602548a71..ec9906bb371 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -496,6 +496,22 @@ snapshot_kind: text ] } }, + "/api/v1/me/email_notifications": { + "put": { + "deprecated": true, + "description": "This endpoint was implemented for an experimental feature that was never\nfully implemented. It is now deprecated and will be removed in the future.", + "operationId": "update_email_notifications", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "Update email notification settings for the authenticated user.", + "tags": [ + "users" + ] + } + }, "/api/v1/me/tokens": { "get": { "operationId": "list_api_tokens", From d0bb30ba1cd6550bb9103a9b564ddce8ee2baae2 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 18:16:56 +0100 Subject: [PATCH 36/43] utoipa: Add annotations to `/summary` endpoints --- src/controllers/summary.rs | 12 +++++++++++- src/router.rs | 2 +- ...ates_io__openapi__tests__openapi_snapshot.snap | 15 +++++++++++++++ 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/controllers/summary.rs b/src/controllers/summary.rs index 452cb7d5c87..c803d892422 100644 --- a/src/controllers/summary.rs +++ b/src/controllers/summary.rs @@ -12,7 +12,17 @@ use diesel_async::{AsyncPgConnection, RunQueryDsl}; use futures_util::FutureExt; use std::future::Future; -/// Handles the `GET /summary` route. +/// Get front page data. +/// +/// This endpoint returns a summary of the most important data for the front +/// page of crates.io. +#[utoipa::path( + get, + path = "/api/v1/summary", + operation_id = "get_summary", + tag = "other", + responses((status = 200, description = "Successful Response")), +)] pub async fn summary(state: AppState) -> AppResult { let mut conn = state.db_read().await?; diff --git a/src/router.rs b/src/router.rs index 4ec4d980acf..1810811aa8d 100644 --- a/src/router.rs +++ b/src/router.rs @@ -56,10 +56,10 @@ pub fn build_axum_router(state: AppState) -> Router<()> { .routes(routes!(crate_owner_invitation::handle_invite)) .routes(routes!(crate_owner_invitation::handle_invite_with_token)) .routes(routes!(user::me::update_email_notifications)) + .routes(routes!(summary::summary)) .split_for_parts(); let mut router = router - .route("/api/v1/summary", get(summary::summary)) .route( "/api/v1/confirm/:email_token", put(user::me::confirm_user_email), diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index ec9906bb371..4fa3bd87e78 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -578,6 +578,21 @@ snapshot_kind: text ] } }, + "/api/v1/summary": { + "get": { + "description": "This endpoint returns a summary of the most important data for the front\npage of crates.io.", + "operationId": "get_summary", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "Get front page data.", + "tags": [ + "other" + ] + } + }, "/api/v1/teams/{team}": { "get": { "operationId": "get_team", From b2a388812043e3e19adbf19af7234b78a889569c Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 18:18:44 +0100 Subject: [PATCH 37/43] utoipa: Add annotations to `/confirm/{email_token}` endpoints --- src/controllers/user/me.rs | 9 ++++++++- src/router.rs | 5 +---- ...rates_io__openapi__tests__openapi_snapshot.snap | 14 ++++++++++++++ 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/controllers/user/me.rs b/src/controllers/user/me.rs index e501be8366a..a4c0d3bf00c 100644 --- a/src/controllers/user/me.rs +++ b/src/controllers/user/me.rs @@ -115,7 +115,14 @@ pub async fn updates(app: AppState, req: Parts) -> AppResult { })) } -/// Handles the `PUT /confirm/:email_token` route +/// Marks the email belonging to the given token as verified. +#[utoipa::path( + put, + path = "/api/v1/confirm/{email_token}", + operation_id = "confirm_user_email", + tag = "users", + responses((status = 200, description = "Successful Response")), +)] pub async fn confirm_user_email(state: AppState, Path(token): Path) -> AppResult { use diesel::update; diff --git a/src/router.rs b/src/router.rs index 1810811aa8d..8919c2de610 100644 --- a/src/router.rs +++ b/src/router.rs @@ -57,13 +57,10 @@ pub fn build_axum_router(state: AppState) -> Router<()> { .routes(routes!(crate_owner_invitation::handle_invite_with_token)) .routes(routes!(user::me::update_email_notifications)) .routes(routes!(summary::summary)) + .routes(routes!(user::me::confirm_user_email)) .split_for_parts(); let mut router = router - .route( - "/api/v1/confirm/:email_token", - put(user::me::confirm_user_email), - ) .route( "/api/v1/users/:user_id/resend", put(user::regenerate_token_and_send), diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index 4fa3bd87e78..5c70b558aeb 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -62,6 +62,20 @@ snapshot_kind: text ] } }, + "/api/v1/confirm/{email_token}": { + "put": { + "operationId": "confirm_user_email", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "Marks the email belonging to the given token as verified.", + "tags": [ + "users" + ] + } + }, "/api/v1/crates": { "get": { "description": "Called in a variety of scenarios in the front end, including:\n- Alphabetical listing of crates\n- List of crates under a specific owner\n- Listing a user's followed crates", From 4a7d68ebee95de63b9b8da49c1c5e289bf4bdf0b Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 18:25:35 +0100 Subject: [PATCH 38/43] utoipa: Add annotations to `/users/{id}/resend` endpoints --- src/controllers/user.rs | 2 +- src/controllers/user/resend.rs | 9 ++++++++- src/router.rs | 7 ++----- ...rates_io__openapi__tests__openapi_snapshot.snap | 14 ++++++++++++++ 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/controllers/user.rs b/src/controllers/user.rs index 4226f0f3a0e..250a658be14 100644 --- a/src/controllers/user.rs +++ b/src/controllers/user.rs @@ -1,6 +1,6 @@ pub mod me; pub mod other; -mod resend; +pub mod resend; pub mod session; pub mod update; diff --git a/src/controllers/user/resend.rs b/src/controllers/user/resend.rs index d73f47697e9..3a743848a9d 100644 --- a/src/controllers/user/resend.rs +++ b/src/controllers/user/resend.rs @@ -14,7 +14,14 @@ use diesel_async::scoped_futures::ScopedFutureExt; use diesel_async::{AsyncConnection, RunQueryDsl}; use http::request::Parts; -/// Handles `PUT /user/:user_id/resend` route +/// Regenerate and send an email verification token. +#[utoipa::path( + put, + path = "/api/v1/users/{id}/resend", + operation_id = "resend_email_verification", + tag = "users", + responses((status = 200, description = "Successful Response")), +)] pub async fn regenerate_token_and_send( state: AppState, Path(param_user_id): Path, diff --git a/src/router.rs b/src/router.rs index 8919c2de610..e77fa5396f0 100644 --- a/src/router.rs +++ b/src/router.rs @@ -1,5 +1,5 @@ use axum::response::IntoResponse; -use axum::routing::{delete, get, post, put}; +use axum::routing::{delete, get, post}; use axum::{Json, Router}; use http::{Method, StatusCode}; use utoipa_axum::routes; @@ -58,13 +58,10 @@ pub fn build_axum_router(state: AppState) -> Router<()> { .routes(routes!(user::me::update_email_notifications)) .routes(routes!(summary::summary)) .routes(routes!(user::me::confirm_user_email)) + .routes(routes!(user::resend::regenerate_token_and_send)) .split_for_parts(); let mut router = router - .route( - "/api/v1/users/:user_id/resend", - put(user::regenerate_token_and_send), - ) .route( "/api/v1/site_metadata", get(site_metadata::show_deployed_sha), diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index 5c70b558aeb..41874ef745b 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -636,6 +636,20 @@ snapshot_kind: text ] } }, + "/api/v1/users/{id}/resend": { + "put": { + "operationId": "resend_email_verification", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "Regenerate and send an email verification token.", + "tags": [ + "users" + ] + } + }, "/api/v1/users/{id}/stats": { "get": { "description": "This currently only returns the total number of downloads for crates owned\nby the user.", From d7b2d4e706910128b2ca41c39bf2aa286fdfd95f Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 18:28:53 +0100 Subject: [PATCH 39/43] utoipa: Add annotations to `/site_metadata` endpoints --- src/controllers/site_metadata.rs | 13 ++++++++++--- src/router.rs | 5 +---- ...ates_io__openapi__tests__openapi_snapshot.snap | 15 +++++++++++++++ 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/controllers/site_metadata.rs b/src/controllers/site_metadata.rs index 910055458e5..7d4515f2d5f 100644 --- a/src/controllers/site_metadata.rs +++ b/src/controllers/site_metadata.rs @@ -2,10 +2,17 @@ use crate::app::AppState; use axum::response::IntoResponse; use axum_extra::json; -/// Returns the JSON representation of the current deployed commit sha. +/// Get crates.io metadata. /// -/// The sha is contained within the `HEROKU_SLUG_COMMIT` environment variable. -/// If `HEROKU_SLUG_COMMIT` is not set, returns `"unknown"`. +/// Returns the current deployed commit SHA1 (or `unknown`), and whether the +/// system is in read-only mode. +#[utoipa::path( + get, + path = "/api/v1/site_metadata", + operation_id = "get_site_metadata", + tag = "other", + responses((status = 200, description = "Successful Response")), +)] pub async fn show_deployed_sha(state: AppState) -> impl IntoResponse { let read_only = state.config.db.are_all_read_only(); diff --git a/src/router.rs b/src/router.rs index e77fa5396f0..fc36622c9c8 100644 --- a/src/router.rs +++ b/src/router.rs @@ -59,13 +59,10 @@ pub fn build_axum_router(state: AppState) -> Router<()> { .routes(routes!(summary::summary)) .routes(routes!(user::me::confirm_user_email)) .routes(routes!(user::resend::regenerate_token_and_send)) + .routes(routes!(site_metadata::show_deployed_sha)) .split_for_parts(); let mut router = router - .route( - "/api/v1/site_metadata", - get(site_metadata::show_deployed_sha), - ) // Session management .route("/api/private/session/begin", get(user::session::begin)) .route( diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index 41874ef745b..3bbacf05707 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -592,6 +592,21 @@ snapshot_kind: text ] } }, + "/api/v1/site_metadata": { + "get": { + "description": "Returns the current deployed commit SHA1 (or `unknown`), and whether the\nsystem is in read-only mode.", + "operationId": "get_site_metadata", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "Get crates.io metadata.", + "tags": [ + "other" + ] + } + }, "/api/v1/summary": { "get": { "description": "This endpoint returns a summary of the most important data for the front\npage of crates.io.", From b03579c1a602addc27f44ebd86f452593116db82 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 18:32:33 +0100 Subject: [PATCH 40/43] utoipa: Add annotations to `/private/session/begin` endpoints --- src/controllers/user/session.rs | 9 ++++++++- src/router.rs | 4 ++-- ...ates_io__openapi__tests__openapi_snapshot.snap | 15 +++++++++++++++ 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/controllers/user/session.rs b/src/controllers/user/session.rs index e555573a1e4..1f6e0ffbd38 100644 --- a/src/controllers/user/session.rs +++ b/src/controllers/user/session.rs @@ -19,7 +19,7 @@ use crate::util::errors::{bad_request, server_error, AppResult}; use crate::views::EncodableMe; use crates_io_github::GithubUser; -/// Handles the `GET /api/private/session/begin` route. +/// Begin authentication flow. /// /// This route will return an authorization URL for the GitHub OAuth flow including the crates.io /// `client_id` and a randomly generated `state` secret. @@ -34,6 +34,13 @@ use crates_io_github::GithubUser; /// "url": "https://github.com/login/oauth/authorize?client_id=...&state=...&scope=read%3Aorg" /// } /// ``` +#[utoipa::path( + get, + path = "/api/private/session/begin", + operation_id = "begin_session", + tag = "session", + responses((status = 200, description = "Successful Response")), +)] pub async fn begin(app: AppState, session: SessionExtension) -> ErasedJson { let (url, state) = app .github_oauth diff --git a/src/router.rs b/src/router.rs index fc36622c9c8..ee20722f494 100644 --- a/src/router.rs +++ b/src/router.rs @@ -60,11 +60,11 @@ pub fn build_axum_router(state: AppState) -> Router<()> { .routes(routes!(user::me::confirm_user_email)) .routes(routes!(user::resend::regenerate_token_and_send)) .routes(routes!(site_metadata::show_deployed_sha)) + // Session management + .routes(routes!(user::session::begin)) .split_for_parts(); let mut router = router - // Session management - .route("/api/private/session/begin", get(user::session::begin)) .route( "/api/private/session/authorize", get(user::session::authorize), diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index 3bbacf05707..5df7302aa84 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -20,6 +20,21 @@ snapshot_kind: text }, "openapi": "3.1.0", "paths": { + "/api/private/session/begin": { + "get": { + "description": "This route will return an authorization URL for the GitHub OAuth flow including the crates.io\n`client_id` and a randomly generated `state` secret.\n\nsee \n\n## Response Body Example\n\n```json\n{\n \"state\": \"b84a63c4ea3fcb4ac84\",\n \"url\": \"https://github.com/login/oauth/authorize?client_id=...&state=...&scope=read%3Aorg\"\n}\n```", + "operationId": "begin_session", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "Begin authentication flow.", + "tags": [ + "session" + ] + } + }, "/api/v1/categories": { "get": { "operationId": "list_categories", From 4c0ebe9eb792d2e220f28de5abaacd63baca68cd Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 18:35:09 +0100 Subject: [PATCH 41/43] utoipa: Add annotations to `/private/session/authorize` endpoints --- src/controllers/user/session.rs | 10 ++++++++-- src/router.rs | 5 +---- ...ates_io__openapi__tests__openapi_snapshot.snap | 15 +++++++++++++++ 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/controllers/user/session.rs b/src/controllers/user/session.rs index 1f6e0ffbd38..3bd20c633c0 100644 --- a/src/controllers/user/session.rs +++ b/src/controllers/user/session.rs @@ -61,7 +61,7 @@ pub struct AuthorizeQuery { state: CsrfToken, } -/// Handles the `GET /api/private/session/authorize` route. +/// Complete authentication flow. /// /// This route is called from the GitHub API OAuth flow after the user accepted or rejected /// the data access permissions. It will check the `state` parameter and then call the GitHub API @@ -79,7 +79,6 @@ pub struct AuthorizeQuery { /// /// ```json /// { -/// "api_token": "b84a63c4ea3fcb4ac84", /// "user": { /// "email": "foo@bar.org", /// "name": "Foo Bar", @@ -89,6 +88,13 @@ pub struct AuthorizeQuery { /// } /// } /// ``` +#[utoipa::path( + get, + path = "/api/private/session/authorize", + operation_id = "authorize_session", + tag = "session", + responses((status = 200, description = "Successful Response")), +)] pub async fn authorize( query: AuthorizeQuery, app: AppState, diff --git a/src/router.rs b/src/router.rs index ee20722f494..ccc164cb0f4 100644 --- a/src/router.rs +++ b/src/router.rs @@ -62,13 +62,10 @@ pub fn build_axum_router(state: AppState) -> Router<()> { .routes(routes!(site_metadata::show_deployed_sha)) // Session management .routes(routes!(user::session::begin)) + .routes(routes!(user::session::authorize)) .split_for_parts(); let mut router = router - .route( - "/api/private/session/authorize", - get(user::session::authorize), - ) .route("/api/private/session", delete(user::session::logout)) // Metrics .route("/api/private/metrics/:kind", get(metrics::prometheus)) diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index 5df7302aa84..bafc5be1498 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -20,6 +20,21 @@ snapshot_kind: text }, "openapi": "3.1.0", "paths": { + "/api/private/session/authorize": { + "get": { + "description": "This route is called from the GitHub API OAuth flow after the user accepted or rejected\nthe data access permissions. It will check the `state` parameter and then call the GitHub API\nto exchange the temporary `code` for an API token. The API token is returned together with\nthe corresponding user information.\n\nsee \n\n## Query Parameters\n\n- `code` – temporary code received from the GitHub API **(Required)**\n- `state` – state parameter received from the GitHub API **(Required)**\n\n## Response Body Example\n\n```json\n{\n \"user\": {\n \"email\": \"foo@bar.org\",\n \"name\": \"Foo Bar\",\n \"login\": \"foobar\",\n \"avatar\": \"https://avatars.githubusercontent.com/u/1234\",\n \"url\": null\n }\n}\n```", + "operationId": "authorize_session", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "Complete authentication flow.", + "tags": [ + "session" + ] + } + }, "/api/private/session/begin": { "get": { "description": "This route will return an authorization URL for the GitHub OAuth flow including the crates.io\n`client_id` and a randomly generated `state` secret.\n\nsee \n\n## Response Body Example\n\n```json\n{\n \"state\": \"b84a63c4ea3fcb4ac84\",\n \"url\": \"https://github.com/login/oauth/authorize?client_id=...&state=...&scope=read%3Aorg\"\n}\n```", From 3fec3e82bbfee904595cc854f7aef95ed66ecf8c Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 18:36:22 +0100 Subject: [PATCH 42/43] utoipa: Add annotations to `/private/session` endpoints --- src/controllers/user/session.rs | 9 ++++++++- src/router.rs | 2 +- ...rates_io__openapi__tests__openapi_snapshot.snap | 14 ++++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/controllers/user/session.rs b/src/controllers/user/session.rs index 3bd20c633c0..84566e701ad 100644 --- a/src/controllers/user/session.rs +++ b/src/controllers/user/session.rs @@ -171,7 +171,14 @@ async fn find_user_by_gh_id(conn: &mut AsyncPgConnection, gh_id: i32) -> QueryRe .optional() } -/// Handles the `DELETE /api/private/session` route. +/// End the current session. +#[utoipa::path( + delete, + path = "/api/private/session", + operation_id = "end_session", + tag = "session", + responses((status = 200, description = "Successful Response")), +)] pub async fn logout(session: SessionExtension) -> Json { session.remove("user_id"); Json(true) diff --git a/src/router.rs b/src/router.rs index ccc164cb0f4..9559e3ea0f5 100644 --- a/src/router.rs +++ b/src/router.rs @@ -63,10 +63,10 @@ pub fn build_axum_router(state: AppState) -> Router<()> { // Session management .routes(routes!(user::session::begin)) .routes(routes!(user::session::authorize)) + .routes(routes!(user::session::logout)) .split_for_parts(); let mut router = router - .route("/api/private/session", delete(user::session::logout)) // Metrics .route("/api/private/metrics/:kind", get(metrics::prometheus)) // Crate ownership invitations management in the frontend diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index bafc5be1498..607b324c699 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -20,6 +20,20 @@ snapshot_kind: text }, "openapi": "3.1.0", "paths": { + "/api/private/session": { + "delete": { + "operationId": "end_session", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "End the current session.", + "tags": [ + "session" + ] + } + }, "/api/private/session/authorize": { "get": { "description": "This route is called from the GitHub API OAuth flow after the user accepted or rejected\nthe data access permissions. It will check the `state` parameter and then call the GitHub API\nto exchange the temporary `code` for an API token. The API token is returned together with\nthe corresponding user information.\n\nsee \n\n## Query Parameters\n\n- `code` – temporary code received from the GitHub API **(Required)**\n- `state` – state parameter received from the GitHub API **(Required)**\n\n## Response Body Example\n\n```json\n{\n \"user\": {\n \"email\": \"foo@bar.org\",\n \"name\": \"Foo Bar\",\n \"login\": \"foobar\",\n \"avatar\": \"https://avatars.githubusercontent.com/u/1234\",\n \"url\": null\n }\n}\n```", From dd6973958b0d6949532b97b83adc5b9c47104bdb Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 13 Dec 2024 18:39:39 +0100 Subject: [PATCH 43/43] utoipa: Add annotations to `/private/crate_owner_invitations` endpoints --- src/controllers/crate_owner_invitation.rs | 11 +++++++++-- src/router.rs | 8 ++------ ...tes_io__openapi__tests__openapi_snapshot.snap | 16 +++++++++++++++- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/controllers/crate_owner_invitation.rs b/src/controllers/crate_owner_invitation.rs index 8d8653c2ac3..4bfd68d52b7 100644 --- a/src/controllers/crate_owner_invitation.rs +++ b/src/controllers/crate_owner_invitation.rs @@ -27,7 +27,7 @@ use std::collections::{HashMap, HashSet}; #[utoipa::path( get, path = "/api/v1/me/crate_owner_invitations", - operation_id = "list_crate_owner_invitations", + operation_id = "list_crate_owner_invitations_for_user", tag = "owners", responses((status = 200, description = "Successful Response")), )] @@ -68,7 +68,14 @@ pub async fn list(app: AppState, req: Parts) -> AppResult { })) } -/// Handles the `GET /api/private/crate_owner_invitations` route. +/// List all crate owner invitations for a crate or user. +#[utoipa::path( + get, + path = "/api/private/crate_owner_invitations", + operation_id = "list_crate_owner_invitations", + tag = "owners", + responses((status = 200, description = "Successful Response")), +)] pub async fn private_list(app: AppState, req: Parts) -> AppResult> { let mut conn = app.db_read().await?; let auth = AuthCheck::only_cookie().check(&req, &mut conn).await?; diff --git a/src/router.rs b/src/router.rs index 9559e3ea0f5..24758d92405 100644 --- a/src/router.rs +++ b/src/router.rs @@ -1,5 +1,5 @@ use axum::response::IntoResponse; -use axum::routing::{delete, get, post}; +use axum::routing::{get, post}; use axum::{Json, Router}; use http::{Method, StatusCode}; use utoipa_axum::routes; @@ -53,6 +53,7 @@ pub fn build_axum_router(state: AppState) -> Router<()> { .routes(routes!(token::show, token::revoke)) .routes(routes!(token::revoke_current)) .routes(routes!(crate_owner_invitation::list)) + .routes(routes!(crate_owner_invitation::private_list)) .routes(routes!(crate_owner_invitation::handle_invite)) .routes(routes!(crate_owner_invitation::handle_invite_with_token)) .routes(routes!(user::me::update_email_notifications)) @@ -69,11 +70,6 @@ pub fn build_axum_router(state: AppState) -> Router<()> { let mut router = router // Metrics .route("/api/private/metrics/:kind", get(metrics::prometheus)) - // Crate ownership invitations management in the frontend - .route( - "/api/private/crate_owner_invitations", - get(crate_owner_invitation::private_list), - ) // Alerts from GitHub scanning for exposed API tokens .route( "/api/github/secret-scanning/verify", diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index 607b324c699..325db33001b 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -20,6 +20,20 @@ snapshot_kind: text }, "openapi": "3.1.0", "paths": { + "/api/private/crate_owner_invitations": { + "get": { + "operationId": "list_crate_owner_invitations", + "responses": { + "200": { + "description": "Successful Response" + } + }, + "summary": "List all crate owner invitations for a crate or user.", + "tags": [ + "owners" + ] + } + }, "/api/private/session": { "delete": { "operationId": "end_session", @@ -514,7 +528,7 @@ snapshot_kind: text }, "/api/v1/me/crate_owner_invitations": { "get": { - "operationId": "list_crate_owner_invitations", + "operationId": "list_crate_owner_invitations_for_user", "responses": { "200": { "description": "Successful Response"