Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

utoipa: Add annotations to (almost) all endpoints #10201

Merged
merged 43 commits into from
Dec 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
82aa90f
utoipa: Add annotations to `/crates/new` endpoints
Turbo87 Dec 13, 2024
08c6e4b
utoipa: Add annotations to `/crates/{name}/owners` endpoints
Turbo87 Dec 13, 2024
4acb47f
utoipa: Add annotations to `/crates/{name}/{version}/yank` endpoints
Turbo87 Dec 13, 2024
fc5f2ce
utoipa: Add annotations to `/crates/{name}/{version}/unyank` endpoints
Turbo87 Dec 13, 2024
9000960
utoipa: Add annotations to `/crates/{name}/{version}/download` endpoints
Turbo87 Dec 13, 2024
ea9b50e
utoipa: Add annotations to `/crates/{name}` endpoints
Turbo87 Dec 13, 2024
9d0a123
utoipa: Add annotations to `/crates/{name}/{version}` endpoints
Turbo87 Dec 13, 2024
f79f4d5
utoipa: Add annotations to `/crates/{name}/{version}/readme` endpoints
Turbo87 Dec 13, 2024
5eb53e3
utoipa: Add annotations to `/crates/{name}/{version}/dependencies` en…
Turbo87 Dec 13, 2024
96ff166
utoipa: Add annotations to `/crates/{name}/{version}/downloads` endpo…
Turbo87 Dec 13, 2024
f19c6f4
utoipa: Add annotations to `/crates/{name}/{version}/authors` endpoints
Turbo87 Dec 13, 2024
24090fd
utoipa: Add annotations to `/crates/{name}/downloads` endpoints
Turbo87 Dec 13, 2024
5c1d82d
utoipa: Add annotations to `/crates/{name}/versions` endpoints
Turbo87 Dec 13, 2024
c9d9c29
utoipa: Add annotations to `/crates/{name}/follow` endpoints
Turbo87 Dec 13, 2024
b740a46
utoipa: Add annotations to `/crates/{name}/following` endpoints
Turbo87 Dec 13, 2024
5fa4b1f
utoipa: Add annotations to `/crates/{name}/owner_team` endpoints
Turbo87 Dec 13, 2024
b8f8fef
utoipa: Add annotations to `/crates/{name}/owner_user` endpoints
Turbo87 Dec 13, 2024
5777f08
utoipa: Add annotations to `/crates/{name}/reverse_dependencies` endp…
Turbo87 Dec 13, 2024
8cf41e0
utoipa: Add annotations to `/keywords` endpoints
Turbo87 Dec 13, 2024
5115053
utoipa: Add annotations to `/keywords/{keyword}` endpoints
Turbo87 Dec 13, 2024
cb7c686
utoipa: Add annotations to `/categories` endpoints
Turbo87 Dec 13, 2024
ec22ad1
utoipa: Add annotations to `/categories/{category}` endpoints
Turbo87 Dec 13, 2024
42f64e4
utoipa: Add annotations to `/category_slugs` endpoints
Turbo87 Dec 13, 2024
ccba727
utoipa: Add annotations to `/users/{user}` endpoints
Turbo87 Dec 13, 2024
949634b
utoipa: Add annotations to `/users/{id}/stats` endpoints
Turbo87 Dec 13, 2024
3767cef
utoipa: Add annotations to `/teams/{team}` endpoints
Turbo87 Dec 13, 2024
67d13f7
utoipa: Add annotations to `/me` endpoints
Turbo87 Dec 13, 2024
dd4f192
utoipa: Add annotations to `/me/updates` endpoints
Turbo87 Dec 13, 2024
c5987ae
utoipa: Add annotations to `/me/tokens` endpoints
Turbo87 Dec 13, 2024
2730095
utoipa: Add annotations to `/me/tokens/{id}` endpoints
Turbo87 Dec 13, 2024
868d2da
utoipa: Add annotations to `/tokens/current` endpoints
Turbo87 Dec 13, 2024
6dafec2
utoipa: Add annotations to `/me/crate_owner_invitations` endpoints
Turbo87 Dec 13, 2024
b68719b
utoipa: Add annotations to `/me/crate_owner_invitations/{crate_id}` e…
Turbo87 Dec 13, 2024
29ca13c
utoipa: Add annotations to `/me/crate_owner_invitations/accept/{token…
Turbo87 Dec 13, 2024
8346246
utoipa: Add annotations to `/me/email_notifications` endpoints
Turbo87 Dec 13, 2024
d0bb30b
utoipa: Add annotations to `/summary` endpoints
Turbo87 Dec 13, 2024
b2a3888
utoipa: Add annotations to `/confirm/{email_token}` endpoints
Turbo87 Dec 13, 2024
4a7d68e
utoipa: Add annotations to `/users/{id}/resend` endpoints
Turbo87 Dec 13, 2024
d7b2d4e
utoipa: Add annotations to `/site_metadata` endpoints
Turbo87 Dec 13, 2024
b03579c
utoipa: Add annotations to `/private/session/begin` endpoints
Turbo87 Dec 13, 2024
4c0ebe9
utoipa: Add annotations to `/private/session/authorize` endpoints
Turbo87 Dec 13, 2024
3fec3e8
utoipa: Add annotations to `/private/session` endpoints
Turbo87 Dec 13, 2024
dd69739
utoipa: Add annotations to `/private/crate_owner_invitations` endpoints
Turbo87 Dec 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 24 additions & 3 deletions src/controllers/category.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<ErasedJson> {
// 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
Expand Down Expand Up @@ -41,7 +48,14 @@ pub async fn index(app: AppState, req: Parts) -> AppResult<ErasedJson> {
}))
}

/// 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<String>) -> AppResult<ErasedJson> {
let mut conn = state.db_read().await?;

Expand Down Expand Up @@ -74,7 +88,14 @@ pub async fn show(state: AppState, Path(slug): Path<String>) -> AppResult<Erased
Ok(json!({ "category": cat_with_subcats }))
}

/// Handles the `GET /category_slugs` route.
/// List all available category slugs.
#[utoipa::path(
get,
path = "/api/v1/category_slugs",
operation_id = "list_category_slugs",
tag = "categories",
responses((status = 200, description = "Successful Response")),
)]
pub async fn slugs(state: AppState) -> AppResult<ErasedJson> {
let mut conn = state.db_read().await?;

Expand Down
36 changes: 32 additions & 4 deletions src/controllers/crate_owner_invitation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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_for_user",
tag = "owners",
responses((status = 200, description = "Successful Response")),
)]
pub async fn list(app: AppState, req: Parts) -> AppResult<ErasedJson> {
let mut conn = app.db_read().await?;
let auth = AuthCheck::only_cookie().check(&req, &mut conn).await?;
Expand Down Expand Up @@ -61,7 +68,14 @@ pub async fn list(app: AppState, req: Parts) -> AppResult<ErasedJson> {
}))
}

/// 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<Json<PrivateListResponse>> {
let mut conn = app.db_read().await?;
let auth = AuthCheck::only_cookie().check(&req, &mut conn).await?;
Expand Down Expand Up @@ -265,7 +279,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<ErasedJson> {
let (parts, body) = req.0.into_parts();

Expand Down Expand Up @@ -293,7 +314,14 @@ pub async fn handle_invite(state: AppState, req: BytesRequest) -> AppResult<Eras
Ok(json!({ "crate_owner_invitation": crate_invite }))
}

/// Handles the `PUT /api/v1/me/crate_owner_invitations/accept/:token` route.
/// Accept a crate owner invitation with a token.
#[utoipa::path(
put,
path = "/api/v1/me/crate_owner_invitations/accept/{token}",
operation_id = "accept_crate_owner_invitation_with_token",
tag = "owners",
responses((status = 200, description = "Successful Response")),
)]
pub async fn handle_invite_with_token(
state: AppState,
Path(token): Path<String>,
Expand Down
18 changes: 16 additions & 2 deletions src/controllers/keyword.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,14 @@ pub struct IndexQuery {
sort: Option<String>,
}

/// 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<IndexQuery>, req: Parts) -> AppResult<ErasedJson> {
use crate::schema::keywords;

Expand All @@ -42,7 +49,14 @@ pub async fn index(state: AppState, qp: Query<IndexQuery>, 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<String>, state: AppState) -> AppResult<ErasedJson> {
let mut conn = state.db_read().await?;
let kw = Keyword::find_by_keyword(&mut conn, &name).await?;
Expand Down
4 changes: 1 addition & 3 deletions src/controllers/krate.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
mod delete;
pub mod delete;
pub mod downloads;
pub mod follow;
pub mod metadata;
pub mod owners;
pub mod publish;
pub mod search;
pub mod versions;

pub use delete::delete;
12 changes: 11 additions & 1 deletion src/controllers/krate/delete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>,
parts: Parts,
Expand Down
13 changes: 12 additions & 1 deletion src/controllers/krate/downloads.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>) -> AppResult<ErasedJson> {
let mut conn = state.db_read().await?;

Expand Down
27 changes: 24 additions & 3 deletions src/controllers/krate/follow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>,
Expand All @@ -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<String>,
Expand All @@ -61,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<String>,
Expand Down
39 changes: 35 additions & 4 deletions src/controllers/krate/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,29 @@ 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<ErasedJson> {
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<String>, req: Parts) -> AppResult<ErasedJson> {
let mut conn = app.db_read().await?;

Expand Down Expand Up @@ -227,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)>,
Expand All @@ -241,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<String>,
Expand Down
45 changes: 40 additions & 5 deletions src/controllers/krate/owners.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>) -> AppResult<ErasedJson> {
let mut conn = state.db_read().await?;

Expand All @@ -37,7 +44,14 @@ pub async fn owners(state: AppState, Path(crate_name): Path<String>) -> 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<String>) -> AppResult<ErasedJson> {
let mut conn = state.db_read().await?;
let krate: Crate = Crate::by_name(&crate_name)
Expand All @@ -55,7 +69,14 @@ pub async fn owner_team(state: AppState, Path(crate_name): Path<String>) -> 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<String>) -> AppResult<ErasedJson> {
let mut conn = state.db_read().await?;

Expand All @@ -74,7 +95,14 @@ pub async fn owner_user(state: AppState, Path(crate_name): Path<String>) -> 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<String>,
Expand All @@ -84,7 +112,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<String>,
Expand Down
Loading
Loading