diff --git a/src/controllers/helpers.rs b/src/controllers/helpers.rs index dff8702d5d6..9a664656ba9 100644 --- a/src/controllers/helpers.rs +++ b/src/controllers/helpers.rs @@ -1,13 +1,31 @@ -use crate::util::errors::AppResult; +use axum::Json; use axum::response::{IntoResponse, Response}; -use axum_extra::json; pub mod authorization; pub(crate) mod pagination; pub(crate) use self::pagination::Paginate; -pub fn ok_true() -> AppResult { - let json = json!({ "ok": true }); - Ok(json.into_response()) +#[derive(Debug, Serialize, utoipa::ToSchema)] +pub struct OkResponse { + #[schema(example = true)] + ok: bool, +} + +impl Default for OkResponse { + fn default() -> Self { + Self::new() + } +} + +impl OkResponse { + pub fn new() -> Self { + Self { ok: true } + } +} + +impl IntoResponse for OkResponse { + fn into_response(self) -> Response { + Json(self).into_response() + } } diff --git a/src/controllers/krate/follow.rs b/src/controllers/krate/follow.rs index fb2cb29a7dc..7475dc1f4b3 100644 --- a/src/controllers/krate/follow.rs +++ b/src/controllers/krate/follow.rs @@ -2,12 +2,11 @@ use crate::app::AppState; use crate::auth::AuthCheck; -use crate::controllers::helpers::ok_true; +use crate::controllers::helpers::OkResponse; use crate::controllers::krate::CratePath; use crate::models::{Crate, Follow}; use crate::schema::*; use crate::util::errors::{AppResult, crate_not_found}; -use axum::response::Response; use axum_extra::json; use axum_extra::response::ErasedJson; use diesel::prelude::*; @@ -39,9 +38,9 @@ async fn follow_target( ("cookie" = []), ), tag = "crates", - responses((status = 200, description = "Successful Response")), + responses((status = 200, description = "Successful Response", body = inline(OkResponse))), )] -pub async fn follow_crate(app: AppState, path: CratePath, req: Parts) -> AppResult { +pub async fn follow_crate(app: AppState, path: CratePath, req: Parts) -> AppResult { let mut conn = app.db_write().await?; let user_id = AuthCheck::default().check(&req, &mut conn).await?.user_id(); let follow = follow_target(&path.name, &mut conn, user_id).await?; @@ -51,7 +50,7 @@ pub async fn follow_crate(app: AppState, path: CratePath, req: Parts) -> AppResu .execute(&mut conn) .await?; - ok_true() + Ok(OkResponse::new()) } /// Unfollow a crate. @@ -64,15 +63,15 @@ pub async fn follow_crate(app: AppState, path: CratePath, req: Parts) -> AppResu ("cookie" = []), ), tag = "crates", - responses((status = 200, description = "Successful Response")), + responses((status = 200, description = "Successful Response", body = inline(OkResponse))), )] -pub async fn unfollow_crate(app: AppState, path: CratePath, req: Parts) -> AppResult { +pub async fn unfollow_crate(app: AppState, path: CratePath, req: Parts) -> AppResult { let mut conn = app.db_write().await?; let user_id = AuthCheck::default().check(&req, &mut conn).await?.user_id(); let follow = follow_target(&path.name, &mut conn, user_id).await?; diesel::delete(&follow).execute(&mut conn).await?; - ok_true() + Ok(OkResponse::new()) } /// Check if a crate is followed. diff --git a/src/controllers/user/email_notifications.rs b/src/controllers/user/email_notifications.rs index c069d1cfd18..baf6e760c6e 100644 --- a/src/controllers/user/email_notifications.rs +++ b/src/controllers/user/email_notifications.rs @@ -1,11 +1,10 @@ use crate::app::AppState; use crate::auth::AuthCheck; -use crate::controllers::helpers::ok_true; +use crate::controllers::helpers::OkResponse; use crate::models::{CrateOwner, OwnerKind}; use crate::schema::crate_owners; use crate::util::errors::AppResult; use axum::Json; -use axum::response::Response; use diesel::prelude::*; use diesel_async::RunQueryDsl; use http::request::Parts; @@ -29,14 +28,14 @@ pub struct CrateEmailNotifications { ("cookie" = []), ), tag = "users", - responses((status = 200, description = "Successful Response")), + responses((status = 200, description = "Successful Response", body = inline(OkResponse))), )] #[deprecated] pub async fn update_email_notifications( app: AppState, parts: Parts, Json(updates): Json>, -) -> AppResult { +) -> AppResult { use diesel::pg::upsert::excluded; let updates: HashMap = updates @@ -89,5 +88,5 @@ pub async fn update_email_notifications( .execute(&mut conn) .await?; - ok_true() + Ok(OkResponse::new()) } diff --git a/src/controllers/user/email_verification.rs b/src/controllers/user/email_verification.rs index 4c035815f01..55cc7248c8e 100644 --- a/src/controllers/user/email_verification.rs +++ b/src/controllers/user/email_verification.rs @@ -1,12 +1,11 @@ use super::update::UserConfirmEmail; use crate::app::AppState; use crate::auth::AuthCheck; -use crate::controllers::helpers::ok_true; +use crate::controllers::helpers::OkResponse; use crate::models::Email; use crate::util::errors::AppResult; use crate::util::errors::{BoxedAppError, bad_request}; use axum::extract::Path; -use axum::response::Response; use crates_io_database::schema::emails; use diesel::dsl::sql; use diesel::prelude::*; @@ -22,9 +21,12 @@ use http::request::Parts; ("email_token" = String, Path, description = "Secret verification token sent to the user's email address"), ), tag = "users", - responses((status = 200, description = "Successful Response")), + responses((status = 200, description = "Successful Response", body = inline(OkResponse))), )] -pub async fn confirm_user_email(state: AppState, Path(token): Path) -> AppResult { +pub async fn confirm_user_email( + state: AppState, + Path(token): Path, +) -> AppResult { let mut conn = state.db_write().await?; let updated_rows = diesel::update(emails::table.filter(emails::token.eq(&token))) @@ -36,7 +38,7 @@ pub async fn confirm_user_email(state: AppState, Path(token): Path) -> A return Err(bad_request("Email belonging to token not found.")); } - ok_true() + Ok(OkResponse::new()) } /// Regenerate and send an email verification token. @@ -51,13 +53,13 @@ pub async fn confirm_user_email(state: AppState, Path(token): Path) -> A ("cookie" = []), ), tag = "users", - responses((status = 200, description = "Successful Response")), + responses((status = 200, description = "Successful Response", body = inline(OkResponse))), )] pub async fn resend_email_verification( state: AppState, Path(param_user_id): Path, req: Parts, -) -> AppResult { +) -> AppResult { let mut conn = state.db_write().await?; let auth = AuthCheck::default().check(&req, &mut conn).await?; @@ -91,7 +93,7 @@ pub async fn resend_email_verification( }) .await?; - ok_true() + Ok(OkResponse::new()) } #[cfg(test)] diff --git a/src/controllers/user/update.rs b/src/controllers/user/update.rs index 5174692acca..eb956c6b556 100644 --- a/src/controllers/user/update.rs +++ b/src/controllers/user/update.rs @@ -1,12 +1,11 @@ use crate::app::AppState; use crate::auth::AuthCheck; -use crate::controllers::helpers::ok_true; +use crate::controllers::helpers::OkResponse; use crate::models::NewEmail; use crate::schema::users; use crate::util::errors::{AppResult, bad_request, server_error}; use axum::Json; use axum::extract::Path; -use axum::response::Response; use diesel::prelude::*; use diesel_async::RunQueryDsl; use http::request::Parts; @@ -40,14 +39,14 @@ pub struct User { ("cookie" = []), ), tag = "users", - responses((status = 200, description = "Successful Response")), + responses((status = 200, description = "Successful Response", body = inline(OkResponse))), )] pub async fn update_user( state: AppState, Path(param_user_id): Path, req: Parts, Json(user_update): Json, -) -> AppResult { +) -> AppResult { let mut conn = state.db_write().await?; let auth = AuthCheck::default().check(&req, &mut conn).await?; @@ -116,7 +115,7 @@ pub async fn update_user( let _ = state.emails.send(user_email, email).await; } - ok_true() + Ok(OkResponse::new()) } pub struct UserConfirmEmail<'a> { diff --git a/src/controllers/version/yank.rs b/src/controllers/version/yank.rs index 7f7b3d367fc..c18531606ee 100644 --- a/src/controllers/version/yank.rs +++ b/src/controllers/version/yank.rs @@ -3,10 +3,9 @@ use super::CrateVersionPath; use super::update::{authenticate, perform_version_yank_update}; use crate::app::AppState; -use crate::controllers::helpers::ok_true; +use crate::controllers::helpers::OkResponse; use crate::rate_limiter::LimitedAction; use crate::util::errors::AppResult; -use axum::response::Response; use http::request::Parts; /// Yank a crate version. @@ -29,13 +28,13 @@ use http::request::Parts; ("cookie" = []), ), tag = "versions", - responses((status = 200, description = "Successful Response")), + responses((status = 200, description = "Successful Response", body = inline(OkResponse))), )] pub async fn yank_version( app: AppState, path: CrateVersionPath, req: Parts, -) -> AppResult { +) -> AppResult { modify_yank(path, app, req, true).await } @@ -49,13 +48,13 @@ pub async fn yank_version( ("cookie" = []), ), tag = "versions", - responses((status = 200, description = "Successful Response")), + responses((status = 200, description = "Successful Response", body = inline(OkResponse))), )] pub async fn unyank_version( app: AppState, path: CrateVersionPath, req: Parts, -) -> AppResult { +) -> AppResult { modify_yank(path, app, req, false).await } @@ -65,7 +64,7 @@ async fn modify_yank( state: AppState, req: Parts, yanked: bool, -) -> AppResult { +) -> AppResult { // FIXME: Should reject bad requests before authentication, but can't due to // lifetime issues with `req`. @@ -89,5 +88,5 @@ async fn modify_yank( ) .await?; - ok_true() + Ok(OkResponse::new()) } diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index 752c3e1c6fa..08a2e4adf30 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -646,6 +646,22 @@ expression: response.json() ], "responses": { "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "ok": { + "example": true, + "type": "boolean" + } + }, + "required": [ + "ok" + ], + "type": "object" + } + } + }, "description": "Successful Response" } }, @@ -982,6 +998,22 @@ expression: response.json() ], "responses": { "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "ok": { + "example": true, + "type": "boolean" + } + }, + "required": [ + "ok" + ], + "type": "object" + } + } + }, "description": "Successful Response" } }, @@ -1013,6 +1045,22 @@ expression: response.json() ], "responses": { "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "ok": { + "example": true, + "type": "boolean" + } + }, + "required": [ + "ok" + ], + "type": "object" + } + } + }, "description": "Successful Response" } }, @@ -1603,6 +1651,22 @@ expression: response.json() ], "responses": { "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "ok": { + "example": true, + "type": "boolean" + } + }, + "required": [ + "ok" + ], + "type": "object" + } + } + }, "description": "Successful Response" } }, @@ -1647,6 +1711,22 @@ expression: response.json() ], "responses": { "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "ok": { + "example": true, + "type": "boolean" + } + }, + "required": [ + "ok" + ], + "type": "object" + } + } + }, "description": "Successful Response" } }, @@ -1925,6 +2005,22 @@ expression: response.json() "operationId": "update_email_notifications", "responses": { "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "ok": { + "example": true, + "type": "boolean" + } + }, + "required": [ + "ok" + ], + "type": "object" + } + } + }, "description": "Successful Response" } }, @@ -2156,6 +2252,22 @@ expression: response.json() ], "responses": { "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "ok": { + "example": true, + "type": "boolean" + } + }, + "required": [ + "ok" + ], + "type": "object" + } + } + }, "description": "Successful Response" } }, @@ -2275,6 +2387,22 @@ expression: response.json() ], "responses": { "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "ok": { + "example": true, + "type": "boolean" + } + }, + "required": [ + "ok" + ], + "type": "object" + } + } + }, "description": "Successful Response" } },