mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-12 12:53:04 +00:00
add list tts category handler
This commit is contained in:
1 parent
d3435a1f4b
commit
556fa360d3
5 files changed
+165
-14
No files matched your search
+12
-13
@@ -1,6 +1,5 @@
|
||||
use anyhow::anyhow;
|
||||
use chrono::{DateTime, Utc};
|
||||
use config::shared_constants::DEFAULT_MYSQL_QUERY_RESULT_PAGE_SIZE;
|
||||
use crate::database::enums::record_visibility::RecordVisibility;
|
||||
use crate::database::helpers::boolean_converters::{i8_to_bool, nullable_i8_to_optional_bool};
|
||||
use crate::util::anyhow_result::AnyhowResult;
|
||||
@@ -97,7 +96,7 @@ impl ListCategoriesQueryBuilder {
|
||||
}
|
||||
|
||||
/// Perform the query based on the set predicates.
|
||||
pub async fn perform_query_for_page(
|
||||
pub async fn perform_query(
|
||||
&self,
|
||||
mysql_pool: &MySqlPool
|
||||
) -> AnyhowResult<CategoryList> {
|
||||
@@ -134,7 +133,7 @@ impl ListCategoriesQueryBuilder {
|
||||
}
|
||||
|
||||
/// Perform the query based on the set predicates.
|
||||
pub async fn perform_internal_query(
|
||||
async fn perform_internal_query(
|
||||
&self,
|
||||
mysql_pool: &MySqlPool
|
||||
) -> AnyhowResult<Vec<RawInternalCategoryRecord>> {
|
||||
@@ -158,7 +157,7 @@ impl ListCategoriesQueryBuilder {
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
pub fn build_query_string(&self) -> String {
|
||||
fn build_query_string(&self) -> String {
|
||||
// TODO: I haven't figured out how to get field name disambiguation and type coercion working here.
|
||||
// (1) tts_results.creator_set_visibility `creator_set_visibility: crate::database::enums::record_visibility::RecordVisibility`,
|
||||
// Query error: no column found for name: creator_set_visibility
|
||||
@@ -168,7 +167,7 @@ impl ListCategoriesQueryBuilder {
|
||||
// TODO/NB: Unfortunately SQLx can't statically typecheck this query
|
||||
let mut query = r#"
|
||||
SELECT
|
||||
model_categories.token as model_token,
|
||||
model_categories.token as category_token,
|
||||
|
||||
model_categories.model_type,
|
||||
|
||||
@@ -235,10 +234,10 @@ LEFT OUTER JOIN users
|
||||
|
||||
if !self.show_deleted {
|
||||
if !first_predicate_added {
|
||||
query.push_str(" WHERE model_categories.deleted_at IS NOT NULL");
|
||||
query.push_str(" WHERE model_categories.deleted_at IS NULL");
|
||||
first_predicate_added = true;
|
||||
} else {
|
||||
query.push_str(" AND model_categories.deleted_at IS NOT NULL");
|
||||
query.push_str(" AND model_categories.deleted_at IS NULL");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,7 +283,7 @@ mod tests {
|
||||
|
||||
assert_eq!(&query_builder.build_predicates(),
|
||||
" WHERE model_categories.is_mod_approved IS TRUE \
|
||||
AND model_categories.deleted_at IS NOT NULL");
|
||||
AND model_categories.deleted_at IS NULL");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -295,7 +294,7 @@ mod tests {
|
||||
assert_eq!(&query_builder.build_predicates(),
|
||||
" WHERE model_categories.creator_user_token = ? \
|
||||
AND model_categories.is_mod_approved IS TRUE \
|
||||
AND model_categories.deleted_at IS NOT NULL");
|
||||
AND model_categories.deleted_at IS NULL");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -306,7 +305,7 @@ mod tests {
|
||||
assert_eq!(&query_builder.build_predicates(),
|
||||
" WHERE model_categories.model_type = ? \
|
||||
AND model_categories.is_mod_approved IS TRUE \
|
||||
AND model_categories.deleted_at IS NOT NULL");
|
||||
AND model_categories.deleted_at IS NULL");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -316,13 +315,13 @@ mod tests {
|
||||
|
||||
assert_eq!(&query_builder.build_predicates(),
|
||||
" WHERE model_categories.is_mod_approved IS TRUE \
|
||||
AND model_categories.deleted_at IS NOT NULL");
|
||||
AND model_categories.deleted_at IS NULL");
|
||||
|
||||
let query_builder = ListCategoriesQueryBuilder::new()
|
||||
.show_unapproved(true);
|
||||
|
||||
assert_eq!(&query_builder.build_predicates(),
|
||||
" WHERE model_categories.deleted_at IS NOT NULL");
|
||||
" WHERE model_categories.deleted_at IS NULL");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -332,7 +331,7 @@ mod tests {
|
||||
|
||||
assert_eq!(&query_builder.build_predicates(),
|
||||
" WHERE model_categories.is_mod_approved IS TRUE \
|
||||
AND model_categories.deleted_at IS NOT NULL");
|
||||
AND model_categories.deleted_at IS NULL");
|
||||
|
||||
let query_builder = ListCategoriesQueryBuilder::new()
|
||||
.show_deleted(true);
|
||||
|
||||
Whitespace-only changes.
+143
@@ -0,0 +1,143 @@
|
||||
use actix_http::Error;
|
||||
use actix_http::http::header;
|
||||
use actix_web::HttpResponseBuilder;
|
||||
use actix_web::cookie::Cookie;
|
||||
use actix_web::error::ResponseError;
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::web::{Path, Json};
|
||||
use actix_web::{Responder, web, HttpResponse, error, HttpRequest};
|
||||
use crate::database::enums::record_visibility::RecordVisibility;
|
||||
use crate::database::queries::query_w2l_template::select_w2l_template_by_token;
|
||||
use crate::database::query_builders::list_categories_query_builder::ListCategoriesQueryBuilder;
|
||||
use crate::http_server::web_utils::ip_address::get_request_ip;
|
||||
use crate::http_server::web_utils::response_error_helpers::to_simple_json_error;
|
||||
use crate::http_server::web_utils::response_success_helpers::simple_json_success;
|
||||
use crate::server_state::ServerState;
|
||||
use crate::util::email_to_gravatar::email_to_gravatar;
|
||||
use crate::util::markdown_to_html::markdown_to_html;
|
||||
use crate::util::random_crockford_token::random_crockford_token;
|
||||
use crate::util::random_prefix_crockford_token::random_prefix_crockford_token;
|
||||
use crate::validations::check_for_slurs::contains_slurs;
|
||||
use http_server_common::response::serialize_as_json_error::serialize_as_json_error;
|
||||
use log::{info, warn, log};
|
||||
use regex::Regex;
|
||||
use sqlx::MySqlPool;
|
||||
use sqlx::error::DatabaseError;
|
||||
use sqlx::error::Error::Database;
|
||||
use sqlx::mysql::MySqlDatabaseError;
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
|
||||
// =============== Success Response ===============
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct ListTtsCategoriesResponse {
|
||||
pub success: bool,
|
||||
pub categories: Vec<DisplayCategory>,
|
||||
}
|
||||
|
||||
/// Public-facing and optimized (non-irrelevant fields) category list
|
||||
/// Used for the main TTS dropdown as well as the TTS edit/tagging UI
|
||||
#[derive(Serialize)]
|
||||
pub struct DisplayCategory {
|
||||
pub category_token: String,
|
||||
|
||||
pub model_type: String, // TODO: ENUM
|
||||
|
||||
pub maybe_super_category_token: Option<String>,
|
||||
|
||||
pub can_directly_have_models: bool,
|
||||
pub can_have_subcategories: bool,
|
||||
pub can_only_mods_apply: bool,
|
||||
|
||||
pub name: String,
|
||||
pub maybe_dropdown_name: Option<String>,
|
||||
|
||||
//pub creator_user_token: Option<String>,
|
||||
//pub creator_username: Option<String>,
|
||||
//pub creator_display_name: Option<String>,
|
||||
}
|
||||
|
||||
// =============== Error Response ===============
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub enum ListTtsCategoriesError {
|
||||
NotAuthorized,
|
||||
ServerError,
|
||||
}
|
||||
|
||||
impl ResponseError for ListTtsCategoriesError {
|
||||
fn status_code(&self) -> StatusCode {
|
||||
match *self {
|
||||
ListTtsCategoriesError::NotAuthorized => StatusCode::UNAUTHORIZED,
|
||||
ListTtsCategoriesError::ServerError => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
}
|
||||
}
|
||||
|
||||
fn error_response(&self) -> HttpResponse {
|
||||
serialize_as_json_error(self)
|
||||
}
|
||||
}
|
||||
|
||||
// NB: Not using DeriveMore since Clion doesn't understand it.
|
||||
impl fmt::Display for ListTtsCategoriesError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
// =============== Handler ===============
|
||||
|
||||
pub async fn list_tts_categories_handler(
|
||||
http_request: HttpRequest,
|
||||
server_state: web::Data<Arc<ServerState>>) -> Result<HttpResponse, ListTtsCategoriesError>
|
||||
{
|
||||
let query_builder = ListCategoriesQueryBuilder::new()
|
||||
.show_deleted(false)
|
||||
.show_unapproved(false)
|
||||
.scope_model_type(Some("tts"));
|
||||
|
||||
let query_result =
|
||||
query_builder.perform_query(&server_state.mysql_pool).await;
|
||||
|
||||
let results = match query_result {
|
||||
Ok(results) => results,
|
||||
Err(err) => {
|
||||
warn!("DB error: {:?}", err);
|
||||
return Err(ListTtsCategoriesError::ServerError);
|
||||
}
|
||||
};
|
||||
|
||||
warn!("Number of categories: {:?}", results.categories.len());
|
||||
|
||||
let mut categories = results.categories.iter()
|
||||
.map(|c| {
|
||||
DisplayCategory {
|
||||
category_token: c.category_token.clone(),
|
||||
model_type: c.model_type.clone(),
|
||||
maybe_super_category_token: c.maybe_super_category_token.clone(),
|
||||
can_directly_have_models: c.can_directly_have_models,
|
||||
can_have_subcategories: c.can_have_subcategories,
|
||||
can_only_mods_apply: c.can_only_mods_apply,
|
||||
name: c.name.clone(),
|
||||
maybe_dropdown_name:c.maybe_dropdown_name.clone(),
|
||||
}
|
||||
})
|
||||
.collect::<Vec<DisplayCategory>>();
|
||||
|
||||
// TODO: Sort by dropdown name too.
|
||||
categories.sort_by(|c1, c2| c1.name.cmp(&c2.name));
|
||||
|
||||
// TODO: Cache results in Redis w/ TTL.
|
||||
let response = ListTtsCategoriesResponse {
|
||||
success: true,
|
||||
categories,
|
||||
};
|
||||
|
||||
let body = serde_json::to_string(&response)
|
||||
.map_err(|e| ListTtsCategoriesError::ServerError)?;
|
||||
|
||||
Ok(HttpResponse::Ok()
|
||||
.content_type("application/json")
|
||||
.body(body))
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
pub mod create_category;
|
||||
pub mod delete_category;
|
||||
pub mod edit_category;
|
||||
pub mod list_categories;
|
||||
pub mod list_tts_categories;
|
||||
pub mod list_my_categories;
|
||||
@@ -62,6 +62,7 @@ use crate::http_server::endpoints::w2l::list_w2l_templates::list_w2l_templates_h
|
||||
use crate::http_server::endpoints::w2l::set_w2l_template_mod_approval::set_w2l_template_mod_approval_handler;
|
||||
use crate::http_server::endpoints::categories::delete_category::delete_category_handler;
|
||||
use crate::http_server::endpoints::categories::edit_category::edit_category_handler;
|
||||
use crate::http_server::endpoints::categories::list_tts_categories::list_tts_categories_handler;
|
||||
|
||||
pub fn add_routes<T, B> (app: App<T, B>) -> App<T, B>
|
||||
where
|
||||
@@ -426,6 +427,14 @@ fn add_category_routes<T, B> (app: App<T, B>) -> App<T, B>
|
||||
.route(web::post().to(edit_category_handler))
|
||||
.route(web::head().to(|| HttpResponse::Ok()))
|
||||
)
|
||||
.service(
|
||||
web::scope("/list")
|
||||
.service(
|
||||
web::resource("/tts")
|
||||
.route(web::get().to(list_tts_categories_handler))
|
||||
.route(web::head().to(|| HttpResponse::Ok()))
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user