Merge pull request #237 from storytold/dec-21-mithoo

Document user bookmarks endpoints
This commit is contained in:
ArEnSc authored and GitHub committed 2023-12-21 12:34:48 -05:00
commit 62c5d86584
7 files changed
+124 -51

No files matched your search

@@ -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")]
@@ -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);
@@ -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;
@@ -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<CreateUserBookmarkRequest>,
@@ -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<bool>,
}
#[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<DeleteUserBookmarkPathInfo>,
@@ -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<UserBookmark>,
pub user_bookmarks: Vec<UserBookmarkForEntityListItem>,
}
#[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<Utc>,
}
#[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<ListUserBookmarkPathInfo>,
path: Path<ListUserBookmarksForEntityPathInfo>,
server_state: web::Data<Arc<ServerState>>
) -> Result<HttpResponse, ListUserBookmarksError>
) -> Result<HttpResponse, ListUserBookmarksForEntityError>
{
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")
@@ -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<UserBookmarkEntityType>,
}
#[derive(Serialize)]
pub struct ListUserBookmarksSuccessResponse {
#[derive(Serialize, ToSchema)]
pub struct ListUserBookmarksForUserSuccessResponse {
pub success: bool,
pub user_bookmarks: Vec<UserBookmark>,
pub user_bookmarks: Vec<UserBookmarkListItem>,
}
#[derive(Serialize)]
pub struct UserBookmark {
#[derive(Serialize, ToSchema)]
pub struct UserBookmarkListItem {
pub token: UserBookmarkToken,
pub details: UserBookmarkDetails,
pub details: UserBookmarkDetailsForUserList,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
#[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<String>,
}
#[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<ListUserBookmarksPathInfo>,
query: Query<ListUserBookmarksQueryData>,
server_state: web::Data<Arc<ServerState>>
) -> Result<HttpResponse, ListUserBookmarksError>
) -> Result<HttpResponse, ListUserBookmarksForUserError>
{
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")