add list tts category handler

This commit is contained in:
Brandon Thomas committed 2021-12-28 03:14:37 -05:00
1 parent d3435a1f4b
commit 556fa360d3
5 files changed
+165 -14

No files matched your search

@@ -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);
@@ -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;
+9
View File
@@ -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()))
)
)
)
}