Merge pull request #219 from storytold/dec-18-mithoo

document media files list endpoints, fix public page offset bug
This commit is contained in:
Brandon Thomas authored and GitHub committed 2023-12-21 02:11:19 -05:00
commit cd91a25c3d
14 files changed
+182 -93

No files matched your search

@@ -6,12 +6,13 @@ use serde::Serialize;
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
use utoipa::ToSchema;
/// Used in the `media_files` table in a `VARCHAR` field.
///
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum MediaFileOriginCategory {
/// ML model inference output - uploaded models or zero shot.
@@ -6,12 +6,13 @@ use serde::Serialize;
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
use utoipa::ToSchema;
/// Used in the `media_files` table in a `VARCHAR` field.
///
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum MediaFileOriginModelType {
/// RVC (v2) voice conversion models
@@ -4,6 +4,7 @@ use std::collections::BTreeSet;
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
use utoipa::ToSchema;
/// Used in the `media_files` table in `VARCHAR(16)` field `origin_product_category`.
///
@@ -14,7 +15,7 @@ use strum::EnumIter;
///
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)]
#[derive(Clone, Copy, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize, ToSchema)]
pub enum MediaFileOriginProductCategory {
/// Media files created by (or uploaded for) the Face Animator product.
/// The underlying model could be SadTalker, Wav2Lip, or some future model
@@ -4,12 +4,13 @@ use std::collections::BTreeSet;
use strum::EnumCount;
#[cfg(test)]
use strum::EnumIter;
use utoipa::ToSchema;
/// Used in the `media_files` table in a `VARCHAR` field.
///
/// DO NOT CHANGE VALUES WITHOUT A MIGRATION STRATEGY.
#[cfg_attr(test, derive(EnumIter, EnumCount))]
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Deserialize, Serialize)]
#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Deserialize, Serialize, ToSchema)]
#[serde(rename_all = "lowercase")]
pub enum MediaFileType {
/// Audio files: wav, mp3, etc.
@@ -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 MediaFileToken(pub String);
@@ -44,6 +44,9 @@ pub struct MediaFileListItem {
pub creator_set_visibility: Visibility,
pub comment_count: u64,
pub favorite_count: u64,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
@@ -100,6 +103,8 @@ pub async fn list_media_files(args: ListMediaFilesArgs<'_>) -> AnyhowResult<Medi
maybe_creator_display_name: record.maybe_creator_display_name,
maybe_creator_gravatar_hash: record.maybe_creator_gravatar_hash,
creator_set_visibility: record.creator_set_visibility,
comment_count: record.comment_count as u64,
favorite_count: record.favorite_count as u64,
created_at: record.created_at,
updated_at: record.updated_at,
}
@@ -148,14 +153,22 @@ SELECT
m.creator_set_visibility,
m.created_at,
m.updated_at
m.updated_at,
COUNT(f.id) as favorite_count,
COUNT(c.id) as comment_count
FROM media_files AS m
LEFT OUTER JOIN users AS u
ON m.maybe_creator_user_token = u.token
WHERE m.user_deleted_at IS NULL
AND m.mod_deleted_at IS NULL
LEFT OUTER JOIN favorites as f
ON f.entity_type = 'media_file' AND f.entity_token = m.token
LEFT OUTER JOIN comments as c
ON c.entity_type = 'media_file' AND c.entity_token = c.token
WHERE
m.user_deleted_at IS NULL
AND m.mod_deleted_at IS NULL
"#
);
@@ -186,6 +199,9 @@ AND m.mod_deleted_at IS NULL
}
}
query_builder.push(" GROUP BY m.id");
if cursor_is_reversed {
query_builder.push(" ORDER BY m.id ASC ");
} else {
@@ -218,6 +234,9 @@ struct MediaFileListItemInternal {
creator_set_visibility: Visibility,
comment_count: i64,
favorite_count: i64,
created_at: DateTime<Utc>,
updated_at: DateTime<Utc>,
}
@@ -257,6 +276,8 @@ impl FromRow<'_, MySqlRow> for MediaFileListItemInternal {
creator_set_visibility: Visibility::try_from_mysql_row(row, "creator_set_visibility")?,
created_at: row.try_get("created_at")?,
updated_at: row.try_get("updated_at")?,
comment_count: row.try_get("comment_count")?,
favorite_count: row.try_get("favorite_count")?,
})
}
}
@@ -51,7 +51,7 @@ pub struct ListMediaFileForUserArgs<'a> {
pub maybe_filter_media_type: Option<MediaFileType>,
pub page_size: usize,
pub page_index: usize,
pub cursor_is_reversed: bool,
pub sort_ascending: bool,
pub view_as: ViewAs,
pub mysql_pool: &'a MySqlPool,
}
@@ -66,16 +66,13 @@ pub async fn list_media_files_for_user(args: ListMediaFileForUserArgs<'_>) -> An
false,
0,
0,
args.cursor_is_reversed,
args.sort_ascending,
args.view_as,
count_fields.as_str(),
);
let row_count_query = count_query_builder.build_query_scalar::<i64>();
let row_count_result = row_count_query.fetch_one(args.mysql_pool).await?;
/// Figure out limit start and end based on page size and indexes as requested
let limit_start = args.page_size * args.page_index;
let limit_end = limit_start + args.page_size;
/// Now fetch the actual results with all the fields
let result_fields = select_result_fields();
@@ -83,9 +80,9 @@ pub async fn list_media_files_for_user(args: ListMediaFileForUserArgs<'_>) -> An
args.maybe_filter_media_type,
args.username,
true,
limit_start,
limit_end,
args.cursor_is_reversed,
args.page_index,
args.page_size,
args.sort_ascending,
args.view_as,
result_fields.as_str(),
);
@@ -115,7 +112,7 @@ pub async fn list_media_files_for_user(args: ListMediaFileForUserArgs<'_>) -> An
Ok(MediaFileListPage {
records: results,
sort_ascending: !args.cursor_is_reversed,
sort_ascending: args.sort_ascending,
current_page: args.page_index,
total_page_count: number_of_pages,
})
@@ -157,9 +154,9 @@ fn query_builder<'a>(
maybe_filter_media_type: Option<MediaFileType>,
username: &'a str,
enforce_limits: bool,
limit_start: usize,
limit_end: usize,
cursor_is_reversed: bool,
page_index: usize,
page_size: usize,
sort_ascending: bool,
view_as: ViewAs,
select_fields: &'a str,
) -> QueryBuilder<'a, MySql> {
@@ -201,18 +198,15 @@ WHERE m.user_deleted_at IS NULL
}
}
if cursor_is_reversed {
query_builder.push(" ORDER BY m.id ASC ");
if sort_ascending {
query_builder.push(" ORDER BY m.created_at ASC ");
} else {
query_builder.push(" ORDER BY m.id DESC ");
query_builder.push(" ORDER BY m.created_at DESC ");
}
if enforce_limits {
if cursor_is_reversed {
query_builder.push(format!(" LIMIT {limit_start}, {limit_end} "));
} else {
query_builder.push(format!(" LIMIT {limit_end}, {limit_start} "));
}
let offset = page_index * page_size;
query_builder.push(format!(" LIMIT {page_size} OFFSET {offset} "));
}
query_builder
@@ -1,4 +1,3 @@
use crate::http_server::endpoints::voice_designer::inference::enqueue_tts_request::*;
use crate::http_server::endpoints::weights::list_available_weights::*;
@@ -8,26 +7,36 @@ use crate::http_server::endpoints::weights::get_weight::*;
use crate::http_server::endpoints::weights::update_weight::*;
use crate::http_server::endpoints::weights::list_weights_by_user::*;
use crate::http_server::common_responses::user_details_lite::{DefaultAvatarInfo, UserDetailsLight};
use enums::by_table::model_weights::{
weights_types::WeightsType,
weights_category::WeightsCategory,
};
use crate::http_server::common_responses::media_file_social_meta_lite::MediaFileSocialMetaLight;
use crate::http_server::common_responses::pagination_cursors::PaginationCursors;
use crate::http_server::common_responses::pagination_page::PaginationPage;
use crate::http_server::endpoints::media_files::list_featured_media_files::*;
use crate::http_server::endpoints::media_files::list_media_files::*;
use crate::http_server::endpoints::media_files::list_media_files_for_user::*;
use enums::by_table::model_weights::{weights_types::WeightsType, weights_category::WeightsCategory};
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 tokens::tokens::generic_inference_jobs::*;
use tokens::tokens::model_weights::*;
use tokens::tokens::users::*;
use tokens::tokens::media_files::*;
use enums::common::visibility::Visibility;
use chrono::{DateTime, Utc};
use utoipa::OpenApi;
#[derive(OpenApi)]
#[openapi(paths(crate::http_server::endpoints::voice_designer::inference::enqueue_tts_request::enqueue_tts_request,
crate::http_server::endpoints::weights::list_available_weights::list_available_weights_handler,
crate::http_server::endpoints::weights::delete_weight::delete_weight_handler,
crate::http_server::endpoints::weights::update_weight::update_weight_handler,
crate::http_server::endpoints::weights::get_weight::get_weight_handler,
crate::http_server::endpoints::weights::list_weights_by_user::list_weights_by_user_handler
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
),components(schemas(
// Tokens
UserToken,Visibility,
@@ -40,10 +49,17 @@ use utoipa::OpenApi;
GetWeightPathInfo,GetWeightResponse,GetWeightError,
UpdateWeightRequest,UpdateWeightRequest,UpdateWeightPathInfo,UpdateWeightRequest,UpdateWeightError,
DeleteWeightPathInfo,DeleteWeightRequest,DeleteWeightError,DeleteWeightRequest,DeleteWeightError,
ListWeightError,ModelWeightForList,ListWeightError,
ListWeightError,ModelWeightForList,ListWeightError,
ListAvailableWeightsQuery,ListAvailableWeightsSuccessResponse,ModelWeightForList,
ListWeightsByUserError,ListWeightsByUserSuccessResponse,ListWeightsByUserPathInfo,Weight,
UserDetailsLight,DefaultAvatarInfo
UserDetailsLight,DefaultAvatarInfo,
PaginationCursors,
PaginationPage,
MediaFileOriginModelType,MediaFileOriginProductCategory,MediaFileOriginCategory,
ListFeaturedMediaFilesSuccessResponse,MediaFile,MediaFileToken,MediaFileType, ListFeaturedMediaFilesError,
ListMediaFilesSuccessResponse, ListMediaFilesError, MediaFileListItem, ListMediaFilesQueryParams,
MediaFileSocialMetaLight,
ListMediaFilesForUserSuccessResponse,ListMediaFilesForUserQueryParams,ListMediaFilesForUserError,ListMediaFilesForUserPathInfo,
MediaFileForUserListItem, MediaFileForUserListItem
)))]
pub struct ApiDoc;
@@ -0,0 +1,22 @@
use utoipa::ToSchema;
use tokens::tokens::media_files::MediaFileToken;
/// Fields useful for enriching media file listings
#[derive(Clone, Serialize, ToSchema)]
pub struct MediaFileSocialMetaLight {
pub favorites_count: u64,
pub comments_count: u64,
}
impl MediaFileSocialMetaLight {
pub fn from_db_fields(
favorites_count: u64,
comments_count: u64,
) -> Self {
Self {
favorites_count,
comments_count,
}
}
}
@@ -1,3 +1,4 @@
pub mod pagination_cursors;
pub mod pagination_page;
pub mod user_details_lite;
pub mod user_details_lite;
pub mod media_file_social_meta_lite;
@@ -1,10 +1,12 @@
use utoipa::ToSchema;
/// Pagination by cursors
/// These types of pagination are for "infinite scrolling", which do not reveal a number of pages.
/// This is good so that investors and competitors cannot reveal how many database records we have.
/// This is typically used for "discovery" type pages, not user profiles.
///
/// See `PaginationPage` for the other type of pagination.
#[derive(Serialize)]
#[derive(Serialize, ToSchema)]
pub struct PaginationCursors {
/// The "next" cursor.
@@ -1,3 +1,5 @@
use utoipa::ToSchema;
/// Pagination by page id
/// This type of pagination is by "page id" and "page count".
/// This should never be used to walk the entire public database as competitors and investors
@@ -5,7 +7,7 @@
/// This is best used for user profiles and scoped down results.
///
/// See `PaginationCursors` for the other type of pagination.
#[derive(Serialize)]
#[derive(Serialize, ToSchema)]
pub struct PaginationPage {
/// The current page number
pub current: usize,
@@ -7,6 +7,7 @@ use actix_web::web::{Path, Query};
use chrono::{DateTime, Utc};
use log::{info, warn};
use buckets::public::media_files::bucket_file_path::MediaFileBucketPath;
use utoipa::{IntoParams, path, ToSchema};
use enums::by_table::media_files::media_file_origin_category::MediaFileOriginCategory;
use enums::by_table::media_files::media_file_origin_model_type::MediaFileOriginModelType;
@@ -15,14 +16,15 @@ use enums::by_table::media_files::media_file_type::MediaFileType;
use enums::common::visibility::Visibility;
use mysql_queries::queries::media_files::list_media_files::{list_media_files, ListMediaFilesArgs, ViewAs};
use tokens::tokens::media_files::MediaFileToken;
use crate::http_server::common_responses::media_file_social_meta_lite::MediaFileSocialMetaLight;
use crate::http_server::common_responses::pagination_cursors::PaginationCursors;
use crate::http_server::common_responses::user_details_lite::UserDetailsLight;
use crate::http_server::web_utils::response_error_helpers::to_simple_json_error;
use crate::server_state::ServerState;
#[derive(Deserialize)]
pub struct QueryParams {
#[derive(Deserialize, ToSchema, IntoParams)]
pub struct ListMediaFilesQueryParams {
pub sort_ascending: Option<bool>,
pub per_page: Option<usize>,
pub cursor: Option<String>,
@@ -30,14 +32,14 @@ pub struct QueryParams {
pub filter_media_type: Option<MediaFileType>,
}
#[derive(Serialize)]
pub struct SuccessResponse {
#[derive(Serialize, ToSchema)]
pub struct ListMediaFilesSuccessResponse {
pub success: bool,
pub results: Vec<MediaFileListItem>,
pub pagination: PaginationCursors,
}
#[derive(Serialize)]
#[derive(Serialize, ToSchema)]
pub struct MediaFileListItem {
pub token: MediaFileToken,
@@ -52,6 +54,7 @@ pub struct MediaFileListItem {
pub public_bucket_path: String,
pub maybe_creator: Option<UserDetailsLight>,
pub maybe_social_meta: Option<MediaFileSocialMetaLight>,
pub creator_set_visibility: Visibility,
@@ -59,38 +62,49 @@ pub struct MediaFileListItem {
pub updated_at: DateTime<Utc>,
}
#[derive(Debug)]
pub enum ErrorResponse {
#[derive(Debug, ToSchema)]
pub enum ListMediaFilesError {
ServerError,
}
impl ResponseError for ErrorResponse {
impl ResponseError for ListMediaFilesError {
fn status_code(&self) -> StatusCode {
match *self {
ErrorResponse::ServerError => StatusCode::INTERNAL_SERVER_ERROR,
ListMediaFilesError::ServerError => StatusCode::INTERNAL_SERVER_ERROR,
}
}
fn error_response(&self) -> HttpResponse {
let error_reason = match self {
ErrorResponse::ServerError => "server error".to_string(),
ListMediaFilesError::ServerError => "server error".to_string(),
};
to_simple_json_error(&error_reason, self.status_code())
}
}
impl std::fmt::Display for ErrorResponse {
impl std::fmt::Display for ListMediaFilesError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
#[utoipa::path(
get,
path = "/v1/media_files/list",
params(
ListMediaFilesQueryParams,
),
responses(
(status = 200, description = "List Featured Media Files", body = ListMediaFilesSuccessResponse),
(status = 500, description = "Server error", body = ListMediaFilesError),
),
)]
pub async fn list_media_files_handler(
http_request: HttpRequest,
query: Query<QueryParams>,
server_state: web::Data<Arc<ServerState>>
) -> Result<HttpResponse, ErrorResponse>
http_request: HttpRequest,
query: Query<ListMediaFilesQueryParams>,
server_state: web::Data<Arc<ServerState>>
) -> Result<HttpResponse, ListMediaFilesError>
{
let maybe_user_session = server_state
.session_checker
@@ -98,7 +112,7 @@ pub async fn list_media_files_handler(
.await
.map_err(|e| {
warn!("Session checker error: {:?}", e);
ErrorResponse::ServerError
ListMediaFilesError::ServerError
})?;
let mut is_mod = false;
@@ -120,7 +134,7 @@ pub async fn list_media_files_handler(
let cursor = server_state.sort_key_crypto.decrypt_id(cursor)
.map_err(|e| {
warn!("crypto error: {:?}", e);
ErrorResponse::ServerError
ListMediaFilesError::ServerError
})?;
Some(cursor as usize)
} else {
@@ -147,7 +161,7 @@ pub async fn list_media_files_handler(
Ok(results) => results,
Err(e) => {
warn!("Query error: {:?}", e);
return Err(ErrorResponse::ServerError);
return Err(ListMediaFilesError::ServerError);
}
};
@@ -155,7 +169,7 @@ pub async fn list_media_files_handler(
let cursor = server_state.sort_key_crypto.encrypt_id(id as u64)
.map_err(|e| {
warn!("crypto error: {:?}", e);
ErrorResponse::ServerError
ListMediaFilesError::ServerError
})?;
Some(cursor)
} else {
@@ -166,7 +180,7 @@ pub async fn list_media_files_handler(
let cursor = server_state.sort_key_crypto.encrypt_id(id as u64)
.map_err(|e| {
warn!("crypto error: {:?}", e);
ErrorResponse::ServerError
ListMediaFilesError::ServerError
})?;
Some(cursor)
} else {
@@ -175,7 +189,7 @@ pub async fn list_media_files_handler(
let results = results_page.records.into_iter()
.map(|record| MediaFileListItem {
token: record.token,
token: record.token.clone(),
origin_category: record.origin_category,
origin_product_category: record.origin_product_category,
maybe_origin_model_type: record.maybe_origin_model_type,
@@ -193,13 +207,17 @@ pub async fn list_media_files_handler(
record.maybe_creator_display_name,
record.maybe_creator_gravatar_hash,
),
maybe_social_meta: Option::from(MediaFileSocialMetaLight::from_db_fields(
record.favorite_count,
record.comment_count,
)),
creator_set_visibility: record.creator_set_visibility,
created_at: record.created_at,
updated_at: record.updated_at,
})
.collect::<Vec<_>>();
let response = SuccessResponse {
let response = ListMediaFilesSuccessResponse {
success: true,
results,
pagination: PaginationCursors {
@@ -210,7 +228,7 @@ pub async fn list_media_files_handler(
};
let body = serde_json::to_string(&response)
.map_err(|e| ErrorResponse::ServerError)?;
.map_err(|e| ListMediaFilesError::ServerError)?;
Ok(HttpResponse::Ok()
.content_type("application/json")
@@ -7,6 +7,7 @@ use actix_web::web::{Path, Query};
use chrono::{DateTime, Utc};
use log::{info, warn};
use buckets::public::media_files::bucket_file_path::MediaFileBucketPath;
use utoipa::{IntoParams, path, ToSchema};
use enums::by_table::media_files::media_file_origin_category::MediaFileOriginCategory;
use enums::by_table::media_files::media_file_origin_model_type::MediaFileOriginModelType;
@@ -16,35 +17,32 @@ use enums::common::visibility::Visibility;
use mysql_queries::queries::media_files::list_media_files_for_user::{list_media_files_for_user, ListMediaFileForUserArgs, ViewAs};
use tokens::tokens::media_files::MediaFileToken;
use crate::http_server::common_responses::pagination_cursors::PaginationCursors;
use crate::http_server::common_responses::pagination_page::PaginationPage;
use crate::http_server::web_utils::response_error_helpers::to_simple_json_error;
use crate::server_state::ServerState;
#[derive(Deserialize)]
pub struct PathInfo {
#[derive(Deserialize, ToSchema)]
pub struct ListMediaFilesForUserPathInfo {
username: String,
}
#[derive(Deserialize)]
pub struct QueryParams {
#[derive(Deserialize, ToSchema, IntoParams)]
pub struct ListMediaFilesForUserQueryParams {
pub sort_ascending: Option<bool>,
pub page_size: Option<usize>,
pub page_index: Option<usize>,
pub cursor: Option<String>,
pub cursor_is_reversed: Option<bool>,
pub filter_media_type: Option<MediaFileType>,
}
#[derive(Serialize)]
pub struct SuccessResponse {
#[derive(Serialize, ToSchema)]
pub struct ListMediaFilesForUserSuccessResponse {
pub success: bool,
pub results: Vec<MediaFileListItem>,
pub results: Vec<MediaFileForUserListItem>,
pub pagination: PaginationPage,
}
#[derive(Serialize)]
pub struct MediaFileListItem {
#[derive(Serialize, ToSchema)]
pub struct MediaFileForUserListItem {
pub token: MediaFileToken,
pub origin_category: MediaFileOriginCategory,
@@ -63,39 +61,50 @@ pub struct MediaFileListItem {
pub updated_at: DateTime<Utc>,
}
#[derive(Debug)]
pub enum ErrorResponse {
#[derive(Debug, ToSchema)]
pub enum ListMediaFilesForUserError {
ServerError,
}
impl ResponseError for ErrorResponse {
impl ResponseError for ListMediaFilesForUserError {
fn status_code(&self) -> StatusCode {
match *self {
ErrorResponse::ServerError => StatusCode::INTERNAL_SERVER_ERROR,
ListMediaFilesForUserError::ServerError => StatusCode::INTERNAL_SERVER_ERROR,
}
}
fn error_response(&self) -> HttpResponse {
let error_reason = match self {
ErrorResponse::ServerError => "server error".to_string(),
ListMediaFilesForUserError::ServerError => "server error".to_string(),
};
to_simple_json_error(&error_reason, self.status_code())
}
}
impl std::fmt::Display for ErrorResponse {
impl std::fmt::Display for ListMediaFilesForUserError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
#[utoipa::path(
get,
path = "/v1/media_files/list/user/{username}",
params(
ListMediaFilesForUserQueryParams,
),
responses(
(status = 200, description = "List Featured Media Files", body = ListMediaFilesForUserSuccessResponse),
(status = 500, description = "Server error", body = ListMediaFilesForUserError),
),
)]
pub async fn list_media_files_for_user_handler(
http_request: HttpRequest,
path: Path<PathInfo>,
query: Query<QueryParams>,
path: Path<ListMediaFilesForUserPathInfo>,
query: Query<ListMediaFilesForUserQueryParams>,
server_state: web::Data<Arc<ServerState>>
) -> Result<HttpResponse, ErrorResponse>
) -> Result<HttpResponse, ListMediaFilesForUserError>
{
let maybe_user_session = server_state
.session_checker
@@ -103,7 +112,7 @@ pub async fn list_media_files_for_user_handler(
.await
.map_err(|e| {
warn!("Session checker error: {:?}", e);
ErrorResponse::ServerError
ListMediaFilesForUserError::ServerError
})?;
let mut is_author = false;
@@ -121,7 +130,6 @@ pub async fn list_media_files_for_user_handler(
let limit = query.page_size.unwrap_or(25);
let sort_ascending = query.sort_ascending.unwrap_or(false);
let cursor_is_reversed = query.cursor_is_reversed.unwrap_or(false);
let page_size = query.page_size.unwrap_or_else(|| 25);
let page_index = query.page_index.unwrap_or_else(|| 0);
@@ -140,7 +148,7 @@ pub async fn list_media_files_for_user_handler(
maybe_filter_media_type: query.filter_media_type,
page_size,
page_index,
cursor_is_reversed,
sort_ascending,
view_as,
mysql_pool: &server_state.mysql_pool,
}).await;
@@ -150,13 +158,13 @@ pub async fn list_media_files_for_user_handler(
Ok(results) => results,
Err(e) => {
warn!("Query error: {:?}", e);
return Err(ErrorResponse::ServerError);
return Err(ListMediaFilesForUserError::ServerError);
}
};
let results = results_page.records.into_iter()
.map(|record| MediaFileListItem {
.map(|record| MediaFileForUserListItem {
token: record.token,
origin_category: record.origin_category,
origin_product_category: record.origin_product_category,
@@ -175,7 +183,7 @@ pub async fn list_media_files_for_user_handler(
})
.collect::<Vec<_>>();
let response = SuccessResponse {
let response = ListMediaFilesForUserSuccessResponse {
success: true,
results,
pagination: PaginationPage{
@@ -185,7 +193,7 @@ pub async fn list_media_files_for_user_handler(
};
let body = serde_json::to_string(&response)
.map_err(|e| ErrorResponse::ServerError)?;
.map_err(|e| ListMediaFilesForUserError::ServerError)?;
Ok(HttpResponse::Ok()
.content_type("application/json")