From 44d9a8b0a67b7dc4573cfd63b991c3afd7acb991 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Wed, 26 Feb 2025 13:42:51 +0100 Subject: [PATCH 1/2] Add OpenAPI documentation for `GET /api/private/crate_owner_invitations` response payload --- src/controllers/crate_owner_invitation.rs | 13 ++- ..._io__openapi__tests__openapi_snapshot.snap | 89 +++++++++++++++++++ src/views.rs | 15 +++- 3 files changed, 113 insertions(+), 4 deletions(-) diff --git a/src/controllers/crate_owner_invitation.rs b/src/controllers/crate_owner_invitation.rs index e3813dc4438..e4ea2b9e8e4 100644 --- a/src/controllers/crate_owner_invitation.rs +++ b/src/controllers/crate_owner_invitation.rs @@ -96,7 +96,7 @@ pub struct ListQueryParams { params(ListQueryParams, PaginationQueryParams), security(("cookie" = [])), tag = "owners", - responses((status = 200, description = "Successful Response")), + responses((status = 200, description = "Successful Response", body = inline(PrivateListResponse))), )] pub async fn list_crate_owner_invitations( app: AppState, @@ -296,15 +296,22 @@ async fn prepare_list( }) } -#[derive(Serialize)] +#[derive(Serialize, utoipa::ToSchema)] pub struct PrivateListResponse { + /// The list of crate owner invitations. invitations: Vec, + + /// The list of users referenced in the crate owner invitations. users: Vec, + + #[schema(inline)] meta: ResponseMeta, } -#[derive(Serialize)] +#[derive(Serialize, utoipa::ToSchema)] struct ResponseMeta { + /// Query parameter string to fetch the next page of results. + #[schema(example = "?seek=c0ffee")] next_page: Option, } diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index cb17f05560b..ffc081c7046 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -72,6 +72,54 @@ expression: response.json() ], "type": "object" }, + "CrateOwnerInvitation": { + "properties": { + "crate_id": { + "description": "The ID of the crate that the user was invited to be an owner of.", + "example": 123, + "format": "int32", + "type": "integer" + }, + "crate_name": { + "description": "The name of the crate that the user was invited to be an owner of.", + "example": "serde", + "type": "string" + }, + "created_at": { + "description": "The date and time this invitation was created.", + "example": "2019-12-13T13:46:41Z", + "format": "date-time", + "type": "string" + }, + "expires_at": { + "description": "The date and time this invitation will expire.", + "example": "2020-01-13T13:46:41Z", + "format": "date-time", + "type": "string" + }, + "invitee_id": { + "description": "The ID of the user who was invited to be a crate owner.", + "example": 42, + "format": "int32", + "type": "integer" + }, + "inviter_id": { + "description": "The ID of the user who sent the invitation.", + "example": 3, + "format": "int32", + "type": "integer" + } + }, + "required": [ + "invitee_id", + "inviter_id", + "crate_id", + "crate_name", + "created_at", + "expires_at" + ], + "type": "object" + }, "Keyword": { "properties": { "crates_cnt": { @@ -261,6 +309,47 @@ expression: response.json() ], "responses": { "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "invitations": { + "description": "The list of crate owner invitations.", + "items": { + "$ref": "#/components/schemas/CrateOwnerInvitation" + }, + "type": "array" + }, + "meta": { + "properties": { + "next_page": { + "description": "Query parameter string to fetch the next page of results.", + "example": "?seek=c0ffee", + "type": [ + "string", + "null" + ] + } + }, + "type": "object" + }, + "users": { + "description": "The list of users referenced in the crate owner invitations.", + "items": { + "$ref": "#/components/schemas/User" + }, + "type": "array" + } + }, + "required": [ + "invitations", + "users", + "meta" + ], + "type": "object" + } + } + }, "description": "Successful Response" } }, diff --git a/src/views.rs b/src/views.rs index 325b597074b..1f0917fb79c 100644 --- a/src/views.rs +++ b/src/views.rs @@ -91,13 +91,26 @@ pub struct EncodableCrateOwnerInvitationV1 { pub expires_at: DateTime, } -#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] +#[derive(Deserialize, Serialize, Debug, PartialEq, Eq, utoipa::ToSchema)] +#[schema(as = CrateOwnerInvitation)] pub struct EncodableCrateOwnerInvitation { + /// The ID of the user who was invited to be a crate owner. + #[schema(example = 42)] pub invitee_id: i32, + /// The ID of the user who sent the invitation. + #[schema(example = 3)] pub inviter_id: i32, + /// The ID of the crate that the user was invited to be an owner of. + #[schema(example = 123)] pub crate_id: i32, + /// The name of the crate that the user was invited to be an owner of. + #[schema(example = "serde")] pub crate_name: String, + /// The date and time this invitation was created. + #[schema(example = "2019-12-13T13:46:41Z")] pub created_at: DateTime, + /// The date and time this invitation will expire. + #[schema(example = "2020-01-13T13:46:41Z")] pub expires_at: DateTime, } From c7a329b5309c7b0eec21d7b540aadd4cb85e3c47 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Wed, 26 Feb 2025 14:35:02 +0100 Subject: [PATCH 2/2] Add OpenAPI documentation for `GET /api/v1/me/crate_owner_invitations` response payload --- src/controllers/crate_owner_invitation.rs | 19 +++-- ..._io__openapi__tests__openapi_snapshot.snap | 81 +++++++++++++++++++ src/views.rs | 18 ++++- 3 files changed, 111 insertions(+), 7 deletions(-) diff --git a/src/controllers/crate_owner_invitation.rs b/src/controllers/crate_owner_invitation.rs index e4ea2b9e8e4..d649f26d8ac 100644 --- a/src/controllers/crate_owner_invitation.rs +++ b/src/controllers/crate_owner_invitation.rs @@ -26,18 +26,27 @@ use http::request::Parts; use indexmap::IndexMap; use std::collections::{HashMap, HashSet}; +#[derive(Serialize, utoipa::ToSchema)] +pub struct LegacyListResponse { + /// The list of crate owner invitations. + crate_owner_invitations: Vec, + + /// The list of users referenced in the crate owner invitations. + users: Vec, +} + /// List all crate owner invitations for the authenticated user. #[utoipa::path( get, path = "/api/v1/me/crate_owner_invitations", security(("cookie" = [])), tag = "owners", - responses((status = 200, description = "Successful Response")), + responses((status = 200, description = "Successful Response", body = inline(LegacyListResponse))), )] pub async fn list_crate_owner_invitations_for_user( app: AppState, req: Parts, -) -> AppResult { +) -> AppResult> { let mut conn = app.db_read().await?; let auth = AuthCheck::only_cookie().check(&req, &mut conn).await?; @@ -68,9 +77,9 @@ pub async fn list_crate_owner_invitations_for_user( }) .collect::>>()?; - Ok(json!({ - "crate_owner_invitations": crate_owner_invitations, - "users": users, + Ok(Json(LegacyListResponse { + crate_owner_invitations, + users, })) } diff --git a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap index ffc081c7046..752c3e1c6fa 100644 --- a/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap +++ b/src/snapshots/crates_io__openapi__tests__openapi_snapshot.snap @@ -153,6 +153,60 @@ expression: response.json() ], "type": "object" }, + "LegacyCrateOwnerInvitation": { + "properties": { + "crate_id": { + "description": "The ID of the crate that the user was invited to be an owner of.", + "example": 123, + "format": "int32", + "type": "integer" + }, + "crate_name": { + "description": "The name of the crate that the user was invited to be an owner of.", + "example": "serde", + "type": "string" + }, + "created_at": { + "description": "The date and time this invitation was created.", + "example": "2019-12-13T13:46:41Z", + "format": "date-time", + "type": "string" + }, + "expires_at": { + "description": "The date and time this invitation will expire.", + "example": "2020-01-13T13:46:41Z", + "format": "date-time", + "type": "string" + }, + "invited_by_username": { + "description": "The username of the user who sent the invitation.", + "example": "ghost", + "type": "string" + }, + "invitee_id": { + "description": "The ID of the user who was invited to be a crate owner.", + "example": 42, + "format": "int32", + "type": "integer" + }, + "inviter_id": { + "description": "The ID of the user who sent the invitation.", + "example": 3, + "format": "int32", + "type": "integer" + } + }, + "required": [ + "invitee_id", + "inviter_id", + "invited_by_username", + "crate_name", + "crate_id", + "created_at", + "expires_at" + ], + "type": "object" + }, "Slug": { "properties": { "description": { @@ -1764,6 +1818,33 @@ expression: response.json() "operationId": "list_crate_owner_invitations_for_user", "responses": { "200": { + "content": { + "application/json": { + "schema": { + "properties": { + "crate_owner_invitations": { + "description": "The list of crate owner invitations.", + "items": { + "$ref": "#/components/schemas/LegacyCrateOwnerInvitation" + }, + "type": "array" + }, + "users": { + "description": "The list of users referenced in the crate owner invitations.", + "items": { + "$ref": "#/components/schemas/User" + }, + "type": "array" + } + }, + "required": [ + "crate_owner_invitations", + "users" + ], + "type": "object" + } + } + }, "description": "Successful Response" } }, diff --git a/src/views.rs b/src/views.rs index 1f0917fb79c..d98be88fe52 100644 --- a/src/views.rs +++ b/src/views.rs @@ -79,15 +79,29 @@ impl From for EncodableCategory { } } -/// The serialization format for the `CrateOwnerInvitation` model. -#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] +#[derive(Deserialize, Serialize, Debug, PartialEq, Eq, utoipa::ToSchema)] +#[schema(as = LegacyCrateOwnerInvitation)] pub struct EncodableCrateOwnerInvitationV1 { + /// The ID of the user who was invited to be a crate owner. + #[schema(example = 42)] pub invitee_id: i32, + /// The ID of the user who sent the invitation. + #[schema(example = 3)] pub inviter_id: i32, + /// The username of the user who sent the invitation. + #[schema(example = "ghost")] pub invited_by_username: String, + /// The name of the crate that the user was invited to be an owner of. + #[schema(example = "serde")] pub crate_name: String, + /// The ID of the crate that the user was invited to be an owner of. + #[schema(example = 123)] pub crate_id: i32, + /// The date and time this invitation was created. + #[schema(example = "2019-12-13T13:46:41Z")] pub created_at: DateTime, + /// The date and time this invitation will expire. + #[schema(example = "2020-01-13T13:46:41Z")] pub expires_at: DateTime, }