diff --git a/src/controllers/user/other.rs b/src/controllers/user/other.rs index bc70b7cdc66..c7dce0a7ba5 100644 --- a/src/controllers/user/other.rs +++ b/src/controllers/user/other.rs @@ -1,3 +1,4 @@ +use axum::Json; use axum::extract::Path; use axum_extra::json; use axum_extra::response::ErasedJson; @@ -12,6 +13,11 @@ use crate::util::errors::AppResult; use crate::views::EncodablePublicUser; use crates_io_diesel_helpers::lower; +#[derive(Debug, Serialize, utoipa::ToSchema)] +pub struct GetResponse { + pub user: EncodablePublicUser, +} + /// Find user by login. #[utoipa::path( get, @@ -20,9 +26,12 @@ use crates_io_diesel_helpers::lower; ("user" = String, Path, description = "Login name of the user"), ), tag = "users", - responses((status = 200, description = "Successful Response")), + responses((status = 200, description = "Successful Response", body = inline(GetResponse))), )] -pub async fn find_user(state: AppState, Path(user_name): Path) -> AppResult { +pub async fn find_user( + state: AppState, + Path(user_name): Path, +) -> AppResult> { let mut conn = state.db_read_prefer_primary().await?; use crate::schema::users::dsl::{gh_login, id, users}; @@ -34,7 +43,7 @@ pub async fn find_user(state: AppState, Path(user_name): Path) -> AppRes .first(&mut conn) .await?; - Ok(json!({ "user": EncodablePublicUser::from(user) })) + Ok(Json(GetResponse { user: user.into() })) } /// Get user stats. diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index 114679eff07..44bb6366345 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -129,6 +129,48 @@ expression: response.json() "description" ], "type": "object" + }, + "User": { + "properties": { + "avatar": { + "description": "The user's avatar URL, if set.", + "example": "https://avatars2.githubusercontent.com/u/1234567?v=4", + "type": [ + "string", + "null" + ] + }, + "id": { + "description": "An opaque identifier for the user.", + "example": 42, + "format": "int32", + "type": "integer" + }, + "login": { + "description": "The user's login name.", + "example": "ghost", + "type": "string" + }, + "name": { + "description": "The user's display name, if set.", + "example": "Kate Morgan", + "type": [ + "string", + "null" + ] + }, + "url": { + "description": "The user's GitHub profile URL.", + "example": "https://github.com/ghost", + "type": "string" + } + }, + "required": [ + "id", + "login", + "url" + ], + "type": "object" } }, "securitySchemes": { @@ -2004,6 +2046,21 @@ expression: response.json() ], "responses": { "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "user": { + "$ref": "#/components/schemas/User" + } + }, + "required": [ + "user" + ], + "type": "object" + } + } + }, "description": "Successful Response" } }, diff --git a/src/views.rs b/src/views.rs index 5b101752649..325b597074b 100644 --- a/src/views.rs +++ b/src/views.rs @@ -522,14 +522,27 @@ impl EncodablePrivateUser { } } -/// The serialization format for the `User` model. -/// Same as private user, except no email field -#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] +#[derive(Deserialize, Serialize, Debug, PartialEq, Eq, utoipa::ToSchema)] +#[schema(as = User)] pub struct EncodablePublicUser { + /// An opaque identifier for the user. + #[schema(example = 42)] pub id: i32, + + /// The user's login name. + #[schema(example = "ghost")] pub login: String, + + /// The user's display name, if set. + #[schema(example = "Kate Morgan")] pub name: Option, + + /// The user's avatar URL, if set. + #[schema(example = "https://avatars2.githubusercontent.com/u/1234567?v=4")] pub avatar: Option, + + /// The user's GitHub profile URL. + #[schema(example = "https://github.com/ghost")] pub url: String, }