diff --git a/crates/_types/enums/src/by_table/user_bookmarks/user_bookmark_entity_type.rs b/crates/_types/enums/src/by_table/user_bookmarks/user_bookmark_entity_type.rs index 1e3fa6692c..31475b24ef 100644 --- a/crates/_types/enums/src/by_table/user_bookmarks/user_bookmark_entity_type.rs +++ b/crates/_types/enums/src/by_table/user_bookmarks/user_bookmark_entity_type.rs @@ -4,10 +4,11 @@ use serde::Serialize; use strum::EnumCount; #[cfg(test)] use strum::EnumIter; +use utoipa::ToSchema; /// Used in the `user_bookmarks` table in a `VARCHAR(32)` field named `entity_type`. #[cfg_attr(test, derive(EnumIter, EnumCount))] -#[derive(Clone, Copy, Eq, PartialEq, Hash, Deserialize, Serialize)] +#[derive(Clone, Copy, Eq, PartialEq, Hash, Deserialize, Serialize, ToSchema)] pub enum UserBookmarkEntityType { /// User #[serde(rename = "user")] diff --git a/crates/_types/tokens/src/tokens/user_bookmarks.rs b/crates/_types/tokens/src/tokens/user_bookmarks.rs index ac91dc079d..94ec6f99e5 100644 --- a/crates/_types/tokens/src/tokens/user_bookmarks.rs +++ b/crates/_types/tokens/src/tokens/user_bookmarks.rs @@ -2,11 +2,12 @@ use std::fmt::Debug; use serde::Deserialize; use serde::Serialize; +use utoipa::ToSchema; use crate::prefixes::TokenPrefix; /// The primary key for Audit Logs. -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, sqlx::Type, Debug, Serialize, Deserialize)] +#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, sqlx::Type, Debug, Serialize, Deserialize, ToSchema)] #[sqlx(transparent)] pub struct UserBookmarkToken(pub String); diff --git a/crates/service/web/storyteller_web/src/api_doc.rs b/crates/service/web/storyteller_web/src/api_doc.rs index e6808517b7..3e0604480c 100644 --- a/crates/service/web/storyteller_web/src/api_doc.rs +++ b/crates/service/web/storyteller_web/src/api_doc.rs @@ -18,11 +18,21 @@ use enums::by_table::media_files::media_file_type::MediaFileType; use enums::by_table::media_files::media_file_origin_category::MediaFileOriginCategory; use enums::by_table::media_files::media_file_origin_model_type::MediaFileOriginModelType; use enums::by_table::media_files::media_file_origin_product_category::MediaFileOriginProductCategory; +use enums::by_table::user_bookmarks::user_bookmark_entity_type::UserBookmarkEntityType; + +use crate::http_server::endpoints::user_bookmarks::create_user_bookmark_handler::*; +use crate::http_server::endpoints::user_bookmarks::delete_user_bookmark_handler::*; +use crate::http_server::endpoints::user_bookmarks::list_user_bookmarks_for_entity_handler::*; +use crate::http_server::endpoints::user_bookmarks::list_user_bookmarks_for_session_handler::*; +use crate::http_server::endpoints::user_bookmarks::list_user_bookmarks_for_user_handler::*; +use crate::http_server::endpoints::user_ratings::get_user_rating_handler::*; +use crate::http_server::endpoints::user_ratings::set_user_rating_handler::*; use tokens::tokens::generic_inference_jobs::*; use tokens::tokens::model_weights::*; use tokens::tokens::users::*; use tokens::tokens::media_files::*; +use tokens::tokens::user_bookmarks::*; use enums::common::visibility::Visibility; use chrono::{DateTime, Utc}; use utoipa::OpenApi; @@ -36,7 +46,11 @@ use utoipa::OpenApi; crate::http_server::endpoints::weights::list_weights_by_user::list_weights_by_user_handler, crate::http_server::endpoints::media_files::list_featured_media_files::list_featured_media_files_handler, crate::http_server::endpoints::media_files::list_media_files::list_media_files_handler, - crate::http_server::endpoints::media_files::list_media_files_for_user::list_media_files_for_user_handler + crate::http_server::endpoints::media_files::list_media_files_for_user::list_media_files_for_user_handler, + crate::http_server::endpoints::user_bookmarks::create_user_bookmark_handler::create_user_bookmark_handler, + crate::http_server::endpoints::user_bookmarks::delete_user_bookmark_handler::delete_user_bookmark_handler, + crate::http_server::endpoints::user_bookmarks::list_user_bookmarks_for_user_handler::list_user_bookmarks_for_user_handler, + crate::http_server::endpoints::user_bookmarks::list_user_bookmarks_for_entity_handler::list_user_bookmarks_for_entity_handler, ),components(schemas( // Tokens UserToken,Visibility, @@ -60,6 +74,11 @@ use utoipa::OpenApi; ListMediaFilesSuccessResponse, ListMediaFilesError, MediaFileListItem, ListMediaFilesQueryParams, MediaFileSocialMetaLight, ListMediaFilesForUserSuccessResponse,ListMediaFilesForUserQueryParams,ListMediaFilesForUserError,ListMediaFilesForUserPathInfo, - MediaFileForUserListItem, MediaFileForUserListItem + MediaFileForUserListItem, MediaFileForUserListItem, + UserBookmarkToken,UserBookmarkDetailsForUserList,UserBookmarkEntityType, + CreateUserBookmarkRequest,CreateUserBookmarkError,CreateUserBookmarkSuccessResponse, + DeleteUserBookmarkPathInfo,DeleteUserBookmarkError,DeleteUserBookmarkRequest, + ListUserBookmarksPathInfo,ListUserBookmarksForUserError,ListUserBookmarksForUserSuccessResponse,UserBookmarkListItem, + ListUserBookmarksForEntityPathInfo,ListUserBookmarksForEntityError,ListUserBookmarksForEntitySuccessResponse,UserBookmarkForEntityListItem, )))] pub struct ApiDoc; diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/user_bookmarks/create_user_bookmark_handler.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/user_bookmarks/create_user_bookmark_handler.rs index 43f0fe6ac1..f720c601c7 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/user_bookmarks/create_user_bookmark_handler.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/user_bookmarks/create_user_bookmark_handler.rs @@ -10,6 +10,7 @@ use actix_web::{HttpRequest, HttpResponse, web}; use actix_web::error::ResponseError; use actix_web::http::StatusCode; use log::warn; +use utoipa::ToSchema; use enums::by_table::user_bookmarks::user_bookmark_entity_type::UserBookmarkEntityType; use mysql_queries::queries::user_bookmarks::create_user_bookmark::{create_user_bookmark, CreateUserBookmarkArgs}; @@ -28,19 +29,19 @@ use tokens::tokens::zs_voices::ZsVoiceToken; use crate::http_server::web_utils::response_error_helpers::to_simple_json_error; use crate::server_state::ServerState; -#[derive(Deserialize)] +#[derive(Deserialize, ToSchema)] pub struct CreateUserBookmarkRequest { entity_token: String, entity_type: UserBookmarkEntityType, } -#[derive(Serialize)] +#[derive(Serialize, ToSchema)] pub struct CreateUserBookmarkSuccessResponse { pub success: bool, pub user_bookmark_token: UserBookmarkToken, } -#[derive(Debug)] +#[derive(Debug, ToSchema)] pub enum CreateUserBookmarkError { BadInput(String), NotAuthorized, @@ -74,6 +75,15 @@ impl fmt::Display for CreateUserBookmarkError { } } +#[utoipa::path( +post, +path = "/v1/user_bookmarks/create", +request_body = CreateUserBookmarkRequest, +responses( +(status = 200, body = CreateUserBookmarkSuccessResponse), +(status = 400, body = CreateUserBookmarkError), +) +)] pub async fn create_user_bookmark_handler( http_request: HttpRequest, request: web::Json, diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/user_bookmarks/delete_user_bookmark_handler.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/user_bookmarks/delete_user_bookmark_handler.rs index d68fc29e75..d449bb78f5 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/user_bookmarks/delete_user_bookmark_handler.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/user_bookmarks/delete_user_bookmark_handler.rs @@ -11,6 +11,7 @@ use actix_web::error::ResponseError; use actix_web::http::StatusCode; use actix_web::web::Path; use log::warn; +use utoipa::ToSchema; use mysql_queries::queries::user_bookmarks::delete_user_bookmark::delete_user_bookmark; use tokens::tokens::user_bookmarks::UserBookmarkToken; @@ -20,18 +21,18 @@ use crate::http_server::web_utils::response_success_helpers::simple_json_success use crate::server_state::ServerState; /// For the URL PathInfo -#[derive(Deserialize)] +#[derive(Deserialize, ToSchema)] pub struct DeleteUserBookmarkPathInfo { user_bookmark_token: UserBookmarkToken, } -#[derive(Deserialize)] +#[derive(Deserialize, ToSchema)] pub struct DeleteUserBookmarkRequest { /// NB: this is only to disambiguate when a user is both a mod and an author. as_mod: Option, } -#[derive(Debug)] +#[derive(Debug, ToSchema)] pub enum DeleteUserBookmarkError { BadInput(String), NotAuthorized, @@ -68,6 +69,20 @@ impl fmt::Display for DeleteUserBookmarkError { } } +#[utoipa::path( + delete, + path = "/v1/user_bookmarks/delete/{user_bookmark_token}", + params( + ("user_bookmark_token", description = "UserBookmarkToken"), + ), + responses( + (status = 200, description = "Delete User Bookmark", body = SimpleGenericJsonSuccess), + (status = 400, description = "Bad input", body = DeleteUserBookmarkError), + (status = 401, description = "Not authorized", body = DeleteUserBookmarkError), + (status = 404, description = "Not found", body = DeleteUserBookmarkError), + (status = 500, description = "Server error", body = DeleteUserBookmarkError), + ), +)] pub async fn delete_user_bookmark_handler( http_request: HttpRequest, path: Path, diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/user_bookmarks/list_user_bookmarks_for_entity_handler.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/user_bookmarks/list_user_bookmarks_for_entity_handler.rs index 0fa28887fd..abeb7fa0ca 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/user_bookmarks/list_user_bookmarks_for_entity_handler.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/user_bookmarks/list_user_bookmarks_for_entity_handler.rs @@ -12,6 +12,7 @@ use actix_web::http::StatusCode; use actix_web::web::Path; use chrono::{DateTime, Utc}; use log::warn; +use utoipa::ToSchema; use enums::by_table::user_bookmarks::user_bookmark_entity_type::UserBookmarkEntityType; use mysql_queries::queries::user_bookmarks::user_bookmark_entity_token::UserBookmarkEntityToken; @@ -23,20 +24,20 @@ use crate::http_server::web_utils::response_error_helpers::to_simple_json_error; use crate::server_state::ServerState; /// For the URL PathInfo -#[derive(Deserialize)] -pub struct ListUserBookmarkPathInfo { +#[derive(Deserialize, ToSchema)] +pub struct ListUserBookmarksForEntityPathInfo { entity_type: UserBookmarkEntityType, entity_token: String, } -#[derive(Serialize)] -pub struct ListUserBookmarksSuccessResponse { +#[derive(Serialize, ToSchema)] +pub struct ListUserBookmarksForEntitySuccessResponse { pub success: bool, - pub user_bookmarks: Vec, + pub user_bookmarks: Vec, } -#[derive(Serialize)] -pub struct UserBookmark { +#[derive(Serialize, ToSchema)] +pub struct UserBookmarkForEntityListItem { pub token: UserBookmarkToken, pub user: UserDetailsLight, @@ -45,21 +46,21 @@ pub struct UserBookmark { pub updated_at: DateTime, } -#[derive(Debug)] -pub enum ListUserBookmarksError { +#[derive(Debug, ToSchema)] +pub enum ListUserBookmarksForEntityError { ServerError, } -impl ResponseError for ListUserBookmarksError { +impl ResponseError for ListUserBookmarksForEntityError { fn status_code(&self) -> StatusCode { match *self { - ListUserBookmarksError::ServerError=> StatusCode::INTERNAL_SERVER_ERROR, + ListUserBookmarksForEntityError::ServerError=> StatusCode::INTERNAL_SERVER_ERROR, } } fn error_response(&self) -> HttpResponse { let error_reason = match self { - ListUserBookmarksError::ServerError => "server error".to_string(), + ListUserBookmarksForEntityError::ServerError => "server error".to_string(), }; to_simple_json_error(&error_reason, self.status_code()) @@ -67,17 +68,30 @@ impl ResponseError for ListUserBookmarksError { } // NB: Not using derive_more::Display since Clion doesn't understand it. -impl fmt::Display for ListUserBookmarksError { +impl fmt::Display for ListUserBookmarksForEntityError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", self) } } + +#[utoipa::path( + get, + path = "/v1/user_bookmarks/list/{entity_type}/{entity_token}", + params( + ("entity_type", description="The type of entity to list bookmarks for"), + ("entity_token", description="The token of the entity to list bookmarks for"), + ), + responses( + (status = 200, body = ListUserBookmarksForEntitySuccessResponse), + (status = 500, body = ListUserBookmarksForEntityError), + ), +)] pub async fn list_user_bookmarks_for_entity_handler( _http_request: HttpRequest, - path: Path, + path: Path, server_state: web::Data> -) -> Result +) -> Result { let entity_token = UserBookmarkEntityToken::from_entity_type_and_token( path.entity_type, &path.entity_token); @@ -91,14 +105,14 @@ pub async fn list_user_bookmarks_for_entity_handler( Ok(results) => results, Err(e) => { warn!("Query error: {:?}", e); - return Err(ListUserBookmarksError::ServerError); + return Err(ListUserBookmarksForEntityError::ServerError); } }; - let response = ListUserBookmarksSuccessResponse { + let response = ListUserBookmarksForEntitySuccessResponse { success: true, user_bookmarks: user_bookmarks.into_iter() - .map(|user_bookmark| UserBookmark { + .map(|user_bookmark| UserBookmarkForEntityListItem { token: user_bookmark.token, user: UserDetailsLight { user_token: user_bookmark.user_token.clone(), @@ -114,7 +128,7 @@ pub async fn list_user_bookmarks_for_entity_handler( }; let body = serde_json::to_string(&response) - .map_err(|_e| ListUserBookmarksError::ServerError)?; + .map_err(|_e| ListUserBookmarksForEntityError::ServerError)?; Ok(HttpResponse::Ok() .content_type("application/json") diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/user_bookmarks/list_user_bookmarks_for_user_handler.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/user_bookmarks/list_user_bookmarks_for_user_handler.rs index ccaf8b61f4..3118d490d1 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/user_bookmarks/list_user_bookmarks_for_user_handler.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/user_bookmarks/list_user_bookmarks_for_user_handler.rs @@ -12,6 +12,7 @@ use actix_web::http::StatusCode; use actix_web::web::{Path, Query}; use chrono::{DateTime, Utc}; use log::warn; +use utoipa::{IntoParams, ToSchema}; use enums::by_table::user_bookmarks::user_bookmark_entity_type::UserBookmarkEntityType; use mysql_queries::queries::user_bookmarks::list_user_bookmarks::{list_user_bookmarks, list_user_user_bookmarks_by_entity_type}; @@ -20,34 +21,34 @@ use tokens::tokens::user_bookmarks::UserBookmarkToken; use crate::http_server::web_utils::response_error_helpers::to_simple_json_error; use crate::server_state::ServerState; -#[derive(Deserialize)] +#[derive(Deserialize, ToSchema)] pub struct ListUserBookmarksPathInfo { username: String, } -#[derive(Deserialize)] +#[derive(Deserialize, ToSchema, IntoParams)] pub struct ListUserBookmarksQueryData { maybe_scoped_entity_type: Option, } -#[derive(Serialize)] -pub struct ListUserBookmarksSuccessResponse { +#[derive(Serialize, ToSchema)] +pub struct ListUserBookmarksForUserSuccessResponse { pub success: bool, - pub user_bookmarks: Vec, + pub user_bookmarks: Vec, } -#[derive(Serialize)] -pub struct UserBookmark { +#[derive(Serialize, ToSchema)] +pub struct UserBookmarkListItem { pub token: UserBookmarkToken, - pub details: UserBookmarkDetails, + pub details: UserBookmarkDetailsForUserList, pub created_at: DateTime, pub updated_at: DateTime, } -#[derive(Serialize)] -pub struct UserBookmarkDetails { +#[derive(Serialize, ToSchema)] +pub struct UserBookmarkDetailsForUserList { // TODO: This needs titles or some other summary metadata. pub entity_type: UserBookmarkEntityType, pub entity_token: String, @@ -59,21 +60,21 @@ pub struct UserBookmarkDetails { pub maybe_thumbnail_url: Option, } -#[derive(Debug)] -pub enum ListUserBookmarksError { +#[derive(Debug, ToSchema)] +pub enum ListUserBookmarksForUserError { ServerError, } -impl ResponseError for ListUserBookmarksError { +impl ResponseError for ListUserBookmarksForUserError { fn status_code(&self) -> StatusCode { match *self { - ListUserBookmarksError::ServerError=> StatusCode::INTERNAL_SERVER_ERROR, + ListUserBookmarksForUserError::ServerError=> StatusCode::INTERNAL_SERVER_ERROR, } } fn error_response(&self) -> HttpResponse { let error_reason = match self { - ListUserBookmarksError::ServerError => "server error".to_string(), + ListUserBookmarksForUserError::ServerError => "server error".to_string(), }; to_simple_json_error(&error_reason, self.status_code()) @@ -81,18 +82,30 @@ impl ResponseError for ListUserBookmarksError { } // NB: Not using derive_more::Display since Clion doesn't understand it. -impl fmt::Display for ListUserBookmarksError { +impl fmt::Display for ListUserBookmarksForUserError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", self) } } +#[utoipa::path( + get, + path = "/v1/user_bookmarks/list/user/{username}", + params( + ("username", description = "The username of the user whose bookmarks to list."), + ListUserBookmarksQueryData + ), +responses( + (status = 200, description = "List User Bookmarks", body = ListUserBookmarksForUserSuccessResponse), + (status = 500, description = "Server error", body = ListUserBookmarksForUserError), +), +)] pub async fn list_user_bookmarks_for_user_handler( _http_request: HttpRequest, path: Path, query: Query, server_state: web::Data> -) -> Result +) -> Result { let query_results = match query.maybe_scoped_entity_type { @@ -106,16 +119,16 @@ pub async fn list_user_bookmarks_for_user_handler( Ok(results) => results, Err(e) => { warn!("Query error: {:?}", e); - return Err(ListUserBookmarksError::ServerError); + return Err(ListUserBookmarksForUserError::ServerError); } }; - let response = ListUserBookmarksSuccessResponse { + let response = ListUserBookmarksForUserSuccessResponse { success: true, user_bookmarks: user_bookmarks.into_iter() - .map(|user_bookmark| UserBookmark { + .map(|user_bookmark| UserBookmarkListItem { token: user_bookmark.token, - details: UserBookmarkDetails { + details: UserBookmarkDetailsForUserList { entity_type: user_bookmark.entity_type, entity_token: user_bookmark.entity_token, maybe_summary_text: user_bookmark.maybe_entity_descriptive_text, @@ -130,7 +143,7 @@ pub async fn list_user_bookmarks_for_user_handler( }; let body = serde_json::to_string(&response) - .map_err(|_e| ListUserBookmarksError::ServerError)?; + .map_err(|_e| ListUserBookmarksForUserError::ServerError)?; Ok(HttpResponse::Ok() .content_type("application/json")