From 698c3307342a8a7cd19fb95a0d82859e7dda9b4f Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Fri, 10 Jul 2026 02:31:37 -0400 Subject: [PATCH] remove old tags code (#1767) --- ...6c3bd7e76e96b0bf603f77f6a9b2d4fcda18d.json | 36 --- ...9389f61f54b871f6361f35529706b98d419ec.json | 12 - ...354baa8d5cd81170dd06d66d7f414505d538b.json | 12 - .../tags/.resources/definition.yaml | 2 - .../ArtCraft API/tags/Edit Tags.request.yaml | 15 -- .../ArtCraft API/tags/List Tags.request.yaml | 6 - .../database/mysql_queries/src/queries/mod.rs | 1 - .../src/queries/tags/create_tag.rs | 27 -- .../src/queries/tags/list_tags_for_entity.rs | 64 ----- .../mysql_queries/src/queries/tags/mod.rs | 4 - .../src/queries/tags/select_matching_tags.rs | 78 ------ .../queries/tags/update_tags_for_entity.rs | 111 -------- .../composite_identifiers/src/by_table/mod.rs | 1 - .../src/by_table/tag_uses/mod.rs | 1 - .../src/by_table/tag_uses/tag_use_entity.rs | 40 --- .../schema/public/enums/src/by_table/mod.rs | 1 - .../public/enums/src/by_table/tag_uses/mod.rs | 1 - .../by_table/tag_uses/tag_use_entity_type.rs | 127 --------- .../web/storyteller_web/src/docs/api_doc.rs | 11 - .../src/http_server/common_responses/mod.rs | 1 - .../http_server/common_responses/tag_info.rs | 8 - .../src/http_server/endpoints/mod.rs | 1 - .../tags/list_tags_for_entity_handler.rs | 101 -------- .../src/http_server/endpoints/tags/mod.rs | 2 - .../tags/set_tags_for_entity_handler.rs | 245 ------------------ .../add_application_routes.rs | 2 - .../routes/application_routes/mod.rs | 1 - .../routes/application_routes/tag_routes.rs | 31 --- 28 files changed, 942 deletions(-) delete mode 100644 .sqlx/query-3929e9fe74e75fad7252681880c6c3bd7e76e96b0bf603f77f6a9b2d4fcda18d.json delete mode 100644 .sqlx/query-41b16ccf04ef1ffb8b48bc59edf9389f61f54b871f6361f35529706b98d419ec.json delete mode 100644 .sqlx/query-c7e848e4c906a34ac5a4ad8cc73354baa8d5cd81170dd06d66d7f414505d538b.json delete mode 100644 _tools/postman/collections/ArtCraft API/tags/.resources/definition.yaml delete mode 100644 _tools/postman/collections/ArtCraft API/tags/Edit Tags.request.yaml delete mode 100644 _tools/postman/collections/ArtCraft API/tags/List Tags.request.yaml delete mode 100644 crates/schema/database/mysql_queries/src/queries/tags/create_tag.rs delete mode 100644 crates/schema/database/mysql_queries/src/queries/tags/list_tags_for_entity.rs delete mode 100644 crates/schema/database/mysql_queries/src/queries/tags/mod.rs delete mode 100644 crates/schema/database/mysql_queries/src/queries/tags/select_matching_tags.rs delete mode 100644 crates/schema/database/mysql_queries/src/queries/tags/update_tags_for_entity.rs delete mode 100644 crates/schema/public/composite_identifiers/src/by_table/tag_uses/mod.rs delete mode 100644 crates/schema/public/composite_identifiers/src/by_table/tag_uses/tag_use_entity.rs delete mode 100644 crates/schema/public/enums/src/by_table/tag_uses/mod.rs delete mode 100644 crates/schema/public/enums/src/by_table/tag_uses/tag_use_entity_type.rs delete mode 100644 crates/service/web/storyteller_web/src/http_server/common_responses/tag_info.rs delete mode 100644 crates/service/web/storyteller_web/src/http_server/endpoints/tags/list_tags_for_entity_handler.rs delete mode 100644 crates/service/web/storyteller_web/src/http_server/endpoints/tags/mod.rs delete mode 100644 crates/service/web/storyteller_web/src/http_server/endpoints/tags/set_tags_for_entity_handler.rs delete mode 100644 crates/service/web/storyteller_web/src/http_server/routes/application_routes/tag_routes.rs diff --git a/.sqlx/query-3929e9fe74e75fad7252681880c6c3bd7e76e96b0bf603f77f6a9b2d4fcda18d.json b/.sqlx/query-3929e9fe74e75fad7252681880c6c3bd7e76e96b0bf603f77f6a9b2d4fcda18d.json deleted file mode 100644 index 7a9a0b0ef6..0000000000 --- a/.sqlx/query-3929e9fe74e75fad7252681880c6c3bd7e76e96b0bf603f77f6a9b2d4fcda18d.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "db_name": "MySQL", - "query": "\nSELECT\n t.token as `token: tokens::tokens::tags::TagToken`,\n t.tag_value\n\nFROM\n tags AS t\nJOIN tag_uses AS u\n ON t.token = u.tag_token\nWHERE\n u.entity_type = ?\n AND u.entity_token = ?\n AND u.maybe_deleted_at IS NULL\nORDER BY t.id DESC\nLIMIT 500\n ", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "token: tokens::tokens::tags::TagToken", - "type_info": { - "type": "VarString", - "flags": "NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE", - "char_set": 224, - "max_size": 128 - } - }, - { - "ordinal": 1, - "name": "tag_value", - "type_info": { - "type": "VarString", - "flags": "NOT_NULL | UNIQUE_KEY | BINARY | NO_DEFAULT_VALUE", - "char_set": 224, - "max_size": 1020 - } - } - ], - "parameters": { - "Right": 2 - }, - "nullable": [ - false, - false - ] - }, - "hash": "3929e9fe74e75fad7252681880c6c3bd7e76e96b0bf603f77f6a9b2d4fcda18d" -} diff --git a/.sqlx/query-41b16ccf04ef1ffb8b48bc59edf9389f61f54b871f6361f35529706b98d419ec.json b/.sqlx/query-41b16ccf04ef1ffb8b48bc59edf9389f61f54b871f6361f35529706b98d419ec.json deleted file mode 100644 index 4da71249a1..0000000000 --- a/.sqlx/query-41b16ccf04ef1ffb8b48bc59edf9389f61f54b871f6361f35529706b98d419ec.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "db_name": "MySQL", - "query": "\nDELETE FROM tag_uses\nWHERE entity_type = ?\nAND entity_token = ?\nLIMIT 1\n ", - "describe": { - "columns": [], - "parameters": { - "Right": 2 - }, - "nullable": [] - }, - "hash": "41b16ccf04ef1ffb8b48bc59edf9389f61f54b871f6361f35529706b98d419ec" -} diff --git a/.sqlx/query-c7e848e4c906a34ac5a4ad8cc73354baa8d5cd81170dd06d66d7f414505d538b.json b/.sqlx/query-c7e848e4c906a34ac5a4ad8cc73354baa8d5cd81170dd06d66d7f414505d538b.json deleted file mode 100644 index 2ce40bca04..0000000000 --- a/.sqlx/query-c7e848e4c906a34ac5a4ad8cc73354baa8d5cd81170dd06d66d7f414505d538b.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "db_name": "MySQL", - "query": "\nINSERT INTO tags\nSET\n token = ?,\n tag_value = ?\n ", - "describe": { - "columns": [], - "parameters": { - "Right": 2 - }, - "nullable": [] - }, - "hash": "c7e848e4c906a34ac5a4ad8cc73354baa8d5cd81170dd06d66d7f414505d538b" -} diff --git a/_tools/postman/collections/ArtCraft API/tags/.resources/definition.yaml b/_tools/postman/collections/ArtCraft API/tags/.resources/definition.yaml deleted file mode 100644 index 7a11f3d93b..0000000000 --- a/_tools/postman/collections/ArtCraft API/tags/.resources/definition.yaml +++ /dev/null @@ -1,2 +0,0 @@ -$kind: collection -order: 8000 diff --git a/_tools/postman/collections/ArtCraft API/tags/Edit Tags.request.yaml b/_tools/postman/collections/ArtCraft API/tags/Edit Tags.request.yaml deleted file mode 100644 index 14b362023f..0000000000 --- a/_tools/postman/collections/ArtCraft API/tags/Edit Tags.request.yaml +++ /dev/null @@ -1,15 +0,0 @@ -$kind: http-request -name: Edit Tags -url: "{{api_host}}/v1/tags/edit/model_weight/weight_84t9zeafn72wkje1x59f9rm81" -method: POST -headers: - Accept: application/json - Content-Type: application/json - Cookie: "" -body: - type: json - content: |- - { - "tags": "video games, legend of zelda" - } -order: 1000 diff --git a/_tools/postman/collections/ArtCraft API/tags/List Tags.request.yaml b/_tools/postman/collections/ArtCraft API/tags/List Tags.request.yaml deleted file mode 100644 index 3cf6b895b8..0000000000 --- a/_tools/postman/collections/ArtCraft API/tags/List Tags.request.yaml +++ /dev/null @@ -1,6 +0,0 @@ -$kind: http-request -url: "{{api_host}}/v1/tags/list/model_weight/weight_84t9zeafn72wkje1x59f9rm81" -method: GET -headers: - Accept: application/json -order: 2000 diff --git a/crates/schema/database/mysql_queries/src/queries/mod.rs b/crates/schema/database/mysql_queries/src/queries/mod.rs index c093cb79fd..7bc40edf7d 100644 --- a/crates/schema/database/mysql_queries/src/queries/mod.rs +++ b/crates/schema/database/mysql_queries/src/queries/mod.rs @@ -32,7 +32,6 @@ pub mod prompts; pub mod public_event_feed; pub mod staff_audit_logs; pub mod stats; -pub mod tags; pub mod trending_model_analytics; pub mod tts; pub mod twitch; diff --git a/crates/schema/database/mysql_queries/src/queries/tags/create_tag.rs b/crates/schema/database/mysql_queries/src/queries/tags/create_tag.rs deleted file mode 100644 index 758b967ff7..0000000000 --- a/crates/schema/database/mysql_queries/src/queries/tags/create_tag.rs +++ /dev/null @@ -1,27 +0,0 @@ -use crate::errors::database_insert_error::DatabaseInsertError; -use crate::utils::transactor::Transactor; -use tokens::tokens::tags::TagToken; - -pub async fn create_tag( - tag: &str, - transactor: Transactor<'_, '_>, -) -> Result -{ - let tag = tag.trim().to_lowercase(); - let token = TagToken::generate(); - - let query = sqlx::query!( - r#" -INSERT INTO tags -SET - token = ?, - tag_value = ? - "#, - token, - tag, - ); - - let _r = transactor.execute(query).await?; - - Ok(token) -} diff --git a/crates/schema/database/mysql_queries/src/queries/tags/list_tags_for_entity.rs b/crates/schema/database/mysql_queries/src/queries/tags/list_tags_for_entity.rs deleted file mode 100644 index 05abc661a8..0000000000 --- a/crates/schema/database/mysql_queries/src/queries/tags/list_tags_for_entity.rs +++ /dev/null @@ -1,64 +0,0 @@ -use composite_identifiers::by_table::tag_uses::tag_use_entity::TagUseEntity; -use errors::AnyhowResult; -use tokens::tokens::tags::TagToken; - -use crate::utils::transactor::Transactor; - -pub struct EntityTag { - pub token: TagToken, - pub tag_value: String, -} - -pub async fn list_tags_for_entity( - tag_entity: TagUseEntity, - transactor: Transactor<'_, '_>, -) -> AnyhowResult> { - - let (entity_type, entity_token) = tag_entity.get_composite_keys(); - - let query = sqlx::query_as!( - RawTag, - r#" -SELECT - t.token as `token: tokens::tokens::tags::TagToken`, - t.tag_value - -FROM - tags AS t -JOIN tag_uses AS u - ON t.token = u.tag_token -WHERE - u.entity_type = ? - AND u.entity_token = ? - AND u.maybe_deleted_at IS NULL -ORDER BY t.id DESC -LIMIT 500 - "#, - entity_type, - entity_token - ); - - let results = match transactor { - Transactor::Pool { pool } => { - query.fetch_all(pool).await? - }, - Transactor::Connection { connection } => { - query.fetch_all(connection).await? - }, - Transactor::Transaction { transaction } => { - query.fetch_all(&mut **transaction).await? - }, - }; - - Ok(results.into_iter() - .map(|record| EntityTag { - token: record.token, - tag_value: record.tag_value, - }) - .collect()) -} - -struct RawTag { - token: TagToken, - tag_value: String, -} diff --git a/crates/schema/database/mysql_queries/src/queries/tags/mod.rs b/crates/schema/database/mysql_queries/src/queries/tags/mod.rs deleted file mode 100644 index 549078afda..0000000000 --- a/crates/schema/database/mysql_queries/src/queries/tags/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod create_tag; -pub mod list_tags_for_entity; -pub mod select_matching_tags; -pub mod update_tags_for_entity; \ No newline at end of file diff --git a/crates/schema/database/mysql_queries/src/queries/tags/select_matching_tags.rs b/crates/schema/database/mysql_queries/src/queries/tags/select_matching_tags.rs deleted file mode 100644 index 75cdf8d0a3..0000000000 --- a/crates/schema/database/mysql_queries/src/queries/tags/select_matching_tags.rs +++ /dev/null @@ -1,78 +0,0 @@ -use crate::utils::transactor::Transactor; -use errors::AnyhowResult; -use sqlx::mysql::MySqlRow; -use sqlx::{FromRow, MySql, QueryBuilder, Row}; -use tokens::tokens::tags::TagToken; - -pub struct MatchingTag { - pub token: TagToken, - pub tag_value: String, -} - -pub async fn select_matching_tags( - tag_values: &[String], - transactor: Transactor<'_, '_>, -) -> AnyhowResult> { - if tag_values.is_empty() { - return Ok(Vec::new()) // This prevents a SQL error when the IN clause is empty. - } - - let tag_values = tag_values.iter() - .map(|tag| tag.trim().to_lowercase()) - .collect::>(); - - let mut query_builder: QueryBuilder = QueryBuilder::new( - r#" -SELECT - token, - tag_value -FROM - tags -WHERE - tag_value IN ( - "#, - ); - - let mut separated = query_builder.separated(", "); - - for tag_value in tag_values.iter() { - separated.push_bind(tag_value); - } - - separated.push_unseparated(") "); - - let query = query_builder.build_query_as::(); - - let results = match transactor { - Transactor::Pool { pool } => { - query.fetch_all(pool).await? - }, - Transactor::Connection { connection } => { - query.fetch_all(connection).await? - }, - Transactor::Transaction { transaction } => { - query.fetch_all(&mut **transaction).await? - }, - }; - - Ok(results.into_iter() - .map(|record| MatchingTag { - token: record.token, - tag_value: record.tag_value, - }) - .collect()) -} - -struct RawTag { - token: TagToken, - tag_value: String, -} - -impl FromRow<'_, MySqlRow> for RawTag { - fn from_row(row: &MySqlRow) -> Result { - Ok(Self { - token: TagToken::new(row.try_get("token")?), - tag_value: row.try_get("tag_value")?, - }) - } -} diff --git a/crates/schema/database/mysql_queries/src/queries/tags/update_tags_for_entity.rs b/crates/schema/database/mysql_queries/src/queries/tags/update_tags_for_entity.rs deleted file mode 100644 index a0d38332dc..0000000000 --- a/crates/schema/database/mysql_queries/src/queries/tags/update_tags_for_entity.rs +++ /dev/null @@ -1,111 +0,0 @@ -use composite_identifiers::by_table::tag_uses::tag_use_entity::TagUseEntity; -use errors::AnyhowResult; -use sqlx::pool::PoolConnection; -use sqlx::{Acquire, Executor, MySql, QueryBuilder, Transaction}; -use tokens::tokens::tags::TagToken; - -pub async fn update_tags_for_entity( - entity: TagUseEntity, - tags: &[TagToken], - mysql_connection: &mut PoolConnection, -) -> AnyhowResult<()> -{ - if tags.is_empty() { - delete_all_query(&entity, mysql_connection).await?; - return Ok(()); - } - - let mut transaction = mysql_connection.begin().await?; - - delete_query(&entity, tags, &mut transaction).await?; - insert_query(&entity, tags, &mut transaction).await?; - - transaction.commit().await?; - - Ok(()) -} - -async fn delete_query( - entity: &TagUseEntity, - tags: &[TagToken], - transaction: &mut Transaction<'_, MySql>, -) -> AnyhowResult<()> { - let mut query_builder: QueryBuilder = QueryBuilder::new("DELETE FROM tag_uses "); - - query_builder.push(" WHERE entity_type = ");; - query_builder.push_bind(entity.get_entity_type().to_str()); - - query_builder.push(" AND entity_token = ");; - query_builder.push_bind(entity.get_entity_token_str()); - - query_builder.push(" AND tag_token NOT IN ( "); - - let mut separated = query_builder.separated(", "); - - for tag in tags.iter() { - separated.push_bind(tag.as_str()); - } - - separated.push_unseparated(") "); - - let query = query_builder.build(); - - transaction.execute(query).await?; - - Ok(()) -} - -async fn delete_all_query( - entity: &TagUseEntity, - mysql_connection: &mut PoolConnection, -) -> AnyhowResult<()> { - let query = sqlx::query!( - r#" -DELETE FROM tag_uses -WHERE entity_type = ? -AND entity_token = ? -LIMIT 1 - "#, - entity.get_entity_type().to_str(), - entity.get_entity_token_str(), - ); - - mysql_connection.execute(query).await?; - - Ok(()) -} - -async fn insert_query( - entity: &TagUseEntity, - tags: &[TagToken], - transaction: &mut Transaction<'_, MySql>, -) -> AnyhowResult<()> { - if tags.is_empty() { - return Ok(()) // This prevents a SQL error when the IN clause is empty. - } - - // NB: Insert ignore will insert non-duplicate rows and silently ignore duplicates - let mut query_builder: QueryBuilder = QueryBuilder::new("INSERT IGNORE INTO tag_uses "); - - query_builder.push(" (entity_type, entity_token, tag_token) VALUES "); - - for (i, tag) in tags.iter().enumerate() { - query_builder.push(" ( ");; - query_builder.push_bind(entity.get_entity_type().to_str()); - query_builder.push(", ");; - query_builder.push_bind(entity.get_entity_token_str()); - query_builder.push(", ");; - query_builder.push_bind(tag.as_str()); - query_builder.push(" ) "); - - if i < tags.len() - 1 { - query_builder.push(", "); - } - } - - let query = query_builder.build(); - - transaction.execute(query).await?; - - Ok(()) -} diff --git a/crates/schema/public/composite_identifiers/src/by_table/mod.rs b/crates/schema/public/composite_identifiers/src/by_table/mod.rs index cbcddcb3a2..1bd660c9bf 100644 --- a/crates/schema/public/composite_identifiers/src/by_table/mod.rs +++ b/crates/schema/public/composite_identifiers/src/by_table/mod.rs @@ -1,4 +1,3 @@ pub mod audit_logs; pub mod batch_generations; pub mod featured_items; -pub mod tag_uses; diff --git a/crates/schema/public/composite_identifiers/src/by_table/tag_uses/mod.rs b/crates/schema/public/composite_identifiers/src/by_table/tag_uses/mod.rs deleted file mode 100644 index 3981bbf3ff..0000000000 --- a/crates/schema/public/composite_identifiers/src/by_table/tag_uses/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod tag_use_entity; \ No newline at end of file diff --git a/crates/schema/public/composite_identifiers/src/by_table/tag_uses/tag_use_entity.rs b/crates/schema/public/composite_identifiers/src/by_table/tag_uses/tag_use_entity.rs deleted file mode 100644 index e3425d2a92..0000000000 --- a/crates/schema/public/composite_identifiers/src/by_table/tag_uses/tag_use_entity.rs +++ /dev/null @@ -1,40 +0,0 @@ -use enums::by_table::tag_uses::tag_use_entity_type::TagUseEntityType; -use tokens::tokens::media_files::MediaFileToken; -use tokens::tokens::model_weights::ModelWeightToken; - -// TODO(bt, 2024-01-16): A ref type with <'a> lifetime of inner data instead of ownership? -pub enum TagUseEntity { - MediaFile(MediaFileToken), - ModelWeight(ModelWeightToken), -} - -// TODO(bt, 2024-01-16): Codegen? Make traits for these? Maybe overkill. -impl TagUseEntity { - pub fn from_entity_type_and_token(entity_type: TagUseEntityType, token: &str) -> Self { - match entity_type { - TagUseEntityType::ModelWeight => Self::ModelWeight(ModelWeightToken::new_from_str(token)), - TagUseEntityType::MediaFile => Self::MediaFile(MediaFileToken::new_from_str(token)), - } - } - - pub fn get_entity_type(&self) -> TagUseEntityType { - match self { - Self::MediaFile(_) => TagUseEntityType::MediaFile, - Self::ModelWeight(_) => TagUseEntityType::ModelWeight, - } - } - - pub fn get_entity_token_str(&self) -> &str { - match self { - Self::MediaFile(token) => token.as_str(), - Self::ModelWeight(token) => token.as_str(), - } - } - - pub fn get_composite_keys(&self) -> (TagUseEntityType, &str) { - match self { - Self::MediaFile(token) => (TagUseEntityType::MediaFile, token.as_str()), - Self::ModelWeight(token) => (TagUseEntityType::ModelWeight, token.as_str()), - } - } -} diff --git a/crates/schema/public/enums/src/by_table/mod.rs b/crates/schema/public/enums/src/by_table/mod.rs index 6cc97be812..57321c921d 100644 --- a/crates/schema/public/enums/src/by_table/mod.rs +++ b/crates/schema/public/enums/src/by_table/mod.rs @@ -18,7 +18,6 @@ pub mod model_weights; pub mod prompt_context_items; pub mod prompts; pub mod staff_audit_logs; -pub mod tag_uses; pub mod trending_model_analytics; pub mod tts_models; pub mod uploaded_video_notes; diff --git a/crates/schema/public/enums/src/by_table/tag_uses/mod.rs b/crates/schema/public/enums/src/by_table/tag_uses/mod.rs deleted file mode 100644 index c41c749b14..0000000000 --- a/crates/schema/public/enums/src/by_table/tag_uses/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod tag_use_entity_type; \ No newline at end of file diff --git a/crates/schema/public/enums/src/by_table/tag_uses/tag_use_entity_type.rs b/crates/schema/public/enums/src/by_table/tag_uses/tag_use_entity_type.rs deleted file mode 100644 index d7902b7a96..0000000000 --- a/crates/schema/public/enums/src/by_table/tag_uses/tag_use_entity_type.rs +++ /dev/null @@ -1,127 +0,0 @@ -use std::collections::BTreeSet; -use serde::Deserialize; -use serde::Serialize; -#[cfg(test)] -use strum::EnumCount; -#[cfg(test)] -use strum::EnumIter; -use utoipa::ToSchema; - -/// Used in the `tag_uses` table in a `VARCHAR(32)` field named `entity_type`. -#[cfg_attr(test, derive(EnumIter, EnumCount))] -#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Deserialize, Serialize, ToSchema)] -pub enum TagUseEntityType { - /// Media files - #[serde(rename = "media_file")] - MediaFile, - - /// Model weights - #[serde(rename = "model_weight")] - ModelWeight, -} - -// TODO(bt, 2023-01-17): This desperately needs MySQL integration tests! -impl_enum_display_and_debug_using_to_str!(TagUseEntityType); -impl_mysql_enum_coders!(TagUseEntityType); - -/// NB: Legacy API for older code. -impl TagUseEntityType { - pub fn to_str(&self) -> &'static str { - match self { - Self::MediaFile => "media_file", - Self::ModelWeight => "model_weight", - } - } - - pub fn from_str(value: &str) -> Result { - match value { - "media_file" => Ok(Self::MediaFile), - "model_weight" => Ok(Self::ModelWeight), - _ => Err(format!("invalid value: {:?}", value)), - } - } - - pub fn all_variants() -> BTreeSet { - // NB: BTreeSet is sorted - // NB: BTreeSet::from() isn't const, but not worth using LazyStatic, etc. - BTreeSet::from([ - Self::MediaFile, - Self::ModelWeight, - ]) - } -} - -#[cfg(test)] -mod tests { - use crate::test_helpers::assert_serialization; - use crate::by_table::tag_uses::tag_use_entity_type::TagUseEntityType; - - mod serde { - use super::*; - - #[test] - fn test_serialization() { - assert_serialization(TagUseEntityType::MediaFile, "media_file"); - assert_serialization(TagUseEntityType::ModelWeight, "model_weight"); - } - } - - mod impl_methods { - use super::*; - - #[test] - fn test_to_str() { - assert_eq!(TagUseEntityType::MediaFile.to_str(), "media_file"); - assert_eq!(TagUseEntityType::ModelWeight.to_str(), "model_weight"); - } - - #[test] - fn test_from_str() { - assert_eq!(TagUseEntityType::from_str("media_file").unwrap(), TagUseEntityType::MediaFile); - assert_eq!(TagUseEntityType::from_str("model_weight").unwrap(), TagUseEntityType::ModelWeight); - assert!(TagUseEntityType::from_str("foo").is_err()); - } - } - - mod manual_variant_checks { - use super::*; - - #[test] - fn all_variants() { - let mut variants = TagUseEntityType::all_variants(); - assert_eq!(variants.len(), 2); - assert_eq!(variants.pop_first(), Some(TagUseEntityType::MediaFile)); - assert_eq!(variants.pop_first(), Some(TagUseEntityType::ModelWeight)); - assert_eq!(variants.pop_first(), None); - } - } - - mod mechanical_checks { - use super::*; - - #[test] - fn variant_length() { - use strum::IntoEnumIterator; - assert_eq!(TagUseEntityType::all_variants().len(), TagUseEntityType::iter().len()); - } - - #[test] - fn round_trip() { - for variant in TagUseEntityType::all_variants() { - assert_eq!(variant, TagUseEntityType::from_str(variant.to_str()).unwrap()); - assert_eq!(variant, TagUseEntityType::from_str(&format!("{}", variant)).unwrap()); - assert_eq!(variant, TagUseEntityType::from_str(&format!("{:?}", variant)).unwrap()); - } - } - - #[test] - fn serialized_length_ok_for_database() { - const MAX_LENGTH : usize = 32; - for variant in TagUseEntityType::all_variants() { - let serialized = variant.to_str(); - assert!(serialized.len() > 0, "variant {:?} is too short", variant); - assert!(serialized.len() <= MAX_LENGTH, "variant {:?} is too long", variant); - } - } - } -} diff --git a/crates/service/web/storyteller_web/src/docs/api_doc.rs b/crates/service/web/storyteller_web/src/docs/api_doc.rs index 556852dfcb..bc19ed3ad8 100644 --- a/crates/service/web/storyteller_web/src/docs/api_doc.rs +++ b/crates/service/web/storyteller_web/src/docs/api_doc.rs @@ -12,7 +12,6 @@ use crate::http_server::common_responses::pagination_cursors::PaginationCursors; use crate::http_server::common_responses::pagination_page::PaginationPage; use crate::http_server::common_responses::simple_entity_stats::SimpleEntityStats; use crate::http_server::common_responses::simple_response::SimpleResponse; -use crate::http_server::common_responses::tag_info::TagInfo; use crate::http_server::common_responses::user_details_lite::{UserDefaultAvatarInfo, UserDetailsLight}; use crate::http_server::deprecated_endpoints::conversion::enqueue_fbx_to_gltf_handler::*; use crate::http_server::deprecated_endpoints::workflows::enqueue::enqueue_face_fusion_workflow_handler::*; @@ -222,8 +221,6 @@ use billing_component::stripe::http_endpoints::checkout::create::stripe_create_c use crate::http_server::endpoints::billing_fakeyou::list_active_user_subscriptions_handler::*; use crate::http_server::endpoints::service::status_alert_handler::*; use crate::http_server::endpoints::stats::get_unified_queue_stats_handler::*; -use crate::http_server::endpoints::tags::list_tags_for_entity_handler::*; -use crate::http_server::endpoints::tags::set_tags_for_entity_handler::*; use crate::http_server::endpoints::tts::enqueue_infer_tts_handler::enqueue_infer_tts_handler::*; use crate::http_server::endpoints::user_bookmarks::batch_get_user_bookmarks_handler::*; use crate::http_server::endpoints::user_bookmarks::create_user_bookmark_handler::*; @@ -512,8 +509,6 @@ use crate::http_server::endpoints::media_files::list::list_batch_generated_redux crate::http_server::endpoints::prompts::get_prompt_handler::get_prompt_handler, crate::http_server::endpoints::service::status_alert_handler::status_alert_handler, crate::http_server::endpoints::stats::get_unified_queue_stats_handler::get_unified_queue_stats_handler, - crate::http_server::endpoints::tags::list_tags_for_entity_handler::list_tags_for_entity_handler, - crate::http_server::endpoints::tags::set_tags_for_entity_handler::set_tags_for_entity_handler, crate::http_server::endpoints::tts::enqueue_infer_tts_handler::enqueue_infer_tts_handler::enqueue_infer_tts_handler, crate::http_server::endpoints::user_bookmarks::batch_get_user_bookmarks_handler::batch_get_user_bookmarks_handler, crate::http_server::endpoints::user_bookmarks::create_user_bookmark_handler::create_user_bookmark_handler, @@ -762,7 +757,6 @@ use crate::http_server::endpoints::media_files::list::list_batch_generated_redux SimpleEntityStats, SimpleGenericJsonSuccess, SimpleResponse, - TagInfo, UserDetailsLight, VideoPreviews, Visibility, @@ -1101,8 +1095,6 @@ use crate::http_server::endpoints::media_files::list::list_batch_generated_redux ListSessionRequestDetailsResponse, ListSessionResultDetailsResponse, ListSessionStatusDetailsResponse, - ListTagsForEntityPathInfo, - ListTagsForEntitySuccessResponse, ListUserBookmarksForEntityError, ListUserBookmarksForEntityPathInfo, ListUserBookmarksForEntitySuccessResponse, @@ -1168,9 +1160,6 @@ use crate::http_server::endpoints::media_files::list::list_batch_generated_redux SetModelWeightCoverImagePathInfo, SetModelWeightCoverImageRequest, SetModelWeightCoverImageResponse, - SetTagsForEntityPathInfo, - SetTagsForEntityRequest, - SetTagsForEntitySuccessResponse, SetUserRatingRequest, SetUserRatingResponse, StatusAlertCategory, diff --git a/crates/service/web/storyteller_web/src/http_server/common_responses/mod.rs b/crates/service/web/storyteller_web/src/http_server/common_responses/mod.rs index f8a72b2347..23bd0286af 100644 --- a/crates/service/web/storyteller_web/src/http_server/common_responses/mod.rs +++ b/crates/service/web/storyteller_web/src/http_server/common_responses/mod.rs @@ -6,7 +6,6 @@ pub mod pagination_cursors; pub mod pagination_page; pub mod simple_entity_stats; pub mod simple_response; -pub mod tag_info; pub mod user_avatars; pub mod user_details_lite; pub mod user_details_lite_builder; diff --git a/crates/service/web/storyteller_web/src/http_server/common_responses/tag_info.rs b/crates/service/web/storyteller_web/src/http_server/common_responses/tag_info.rs deleted file mode 100644 index 7f82972fbf..0000000000 --- a/crates/service/web/storyteller_web/src/http_server/common_responses/tag_info.rs +++ /dev/null @@ -1,8 +0,0 @@ -use tokens::tokens::tags::TagToken; -use utoipa::ToSchema; - -#[derive(Serialize, ToSchema)] -pub struct TagInfo { - pub token: TagToken, - pub value: String, -} diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/mod.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/mod.rs index c27ef73f5c..bcd0690c4b 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/mod.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/mod.rs @@ -23,7 +23,6 @@ pub mod prompts; pub mod service; pub mod stats; pub mod subscriptions; -pub mod tags; pub mod trending; pub mod tts; pub mod user_bookmarks; diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/tags/list_tags_for_entity_handler.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/tags/list_tags_for_entity_handler.rs deleted file mode 100644 index 1ce5476cd8..0000000000 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/tags/list_tags_for_entity_handler.rs +++ /dev/null @@ -1,101 +0,0 @@ -//// NB: Incrementally getting rid of build warnings... -//#![forbid(unused_imports)] -//#![forbid(unused_mut)] -//#![forbid(unused_variables)] - -use std::fmt; -use std::sync::Arc; - -use crate::http_server::common_responses::tag_info::TagInfo; -use crate::http_server::common_responses::common_web_error::CommonWebError; -use crate::http_server::web_utils::response_error_helpers::to_simple_json_error; -use crate::state::server_state::ServerState; -use actix_web::error::ResponseError; -use actix_web::http::StatusCode; -use actix_web::web::{Json, Path}; -use actix_web::{web, HttpRequest, HttpResponse}; -use composite_identifiers::by_table::tag_uses::tag_use_entity::TagUseEntity; -use enums::by_table::tag_uses::tag_use_entity_type::TagUseEntityType; -use log::warn; -use mysql_queries::queries::tags::list_tags_for_entity::list_tags_for_entity; -use mysql_queries::utils::transactor::Transactor; -use tokens::tokens::media_files::MediaFileToken; -use tokens::tokens::model_weights::ModelWeightToken; -use utoipa::ToSchema; - -/// For the URL PathInfo -#[derive(Deserialize, ToSchema)] -pub struct ListTagsForEntityPathInfo { - entity_type: TagUseEntityType, - entity_token: String, -} - -#[derive(Serialize, ToSchema)] -pub struct ListTagsForEntitySuccessResponse { - pub success: bool, - pub tags: Vec, -} -// NB: Not using derive_more::Display since Clion doesn't understand it. -/// List the tags for an entity -#[utoipa::path( - get, - tag = "Tags", - path = "/v1/tags/list/{entity_type}/{entity_token}", - responses( - (status = 200, description = "Success", body = ListTagsForEntitySuccessResponse), - (status = 400, description = "Bad input", body = CommonWebError), - (status = 401, description = "Not authorized", body = CommonWebError), - (status = 500, description = "Server error", body = CommonWebError), - ), - params( - ("request" = ListTagsForEntityPathInfo, description = "Payload for Request"), - ) -)] -pub async fn list_tags_for_entity_handler( - http_request: HttpRequest, - path: Path, - server_state: web::Data>, -) -> Result, CommonWebError> -{ - let token = path.entity_token.as_str(); - let token_prefix_matches = match path.entity_type { - TagUseEntityType::MediaFile => token.starts_with(MediaFileToken::token_prefix()), - TagUseEntityType::ModelWeight => token.starts_with(ModelWeightToken::token_prefix()), - }; - - if !token_prefix_matches { - warn!("invalid token prefix: {:?} for {:?}", path.entity_token, path.entity_type); - return Err(CommonWebError::BadInputWithSimpleMessage("invalid token prefix".to_string())); - } - - let mut mysql_connection = server_state.mysql_pool - .acquire() - .await - .map_err(|err| { - warn!("MySql pool error: {:?}", err); - CommonWebError::from_error(err) - })?; - - let entity = TagUseEntity::from_entity_type_and_token( - path.entity_type, &path.entity_token); - - let mut tags = list_tags_for_entity(entity, Transactor::for_connection(&mut *mysql_connection)) - .await - .map_err(|e| { - warn!("error listing tags: {:?}", e); - CommonWebError::from_anyhow_error(e) - })?; - - tags.sort_by(|a, b| a.tag_value.cmp(&b.tag_value)); - - Ok(Json(ListTagsForEntitySuccessResponse { - success: true, - tags: tags - .into_iter() - .map(|tag| TagInfo { - token: tag.token, - value: tag.tag_value, - }) - .collect(), - })) -} diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/tags/mod.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/tags/mod.rs deleted file mode 100644 index 4fecd6b66a..0000000000 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/tags/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod list_tags_for_entity_handler; -pub mod set_tags_for_entity_handler; \ No newline at end of file diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/tags/set_tags_for_entity_handler.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/tags/set_tags_for_entity_handler.rs deleted file mode 100644 index 4fe1e910d5..0000000000 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/tags/set_tags_for_entity_handler.rs +++ /dev/null @@ -1,245 +0,0 @@ -//// NB: Incrementally getting rid of build warnings... -//#![forbid(unused_imports)] -//#![forbid(unused_mut)] -//#![forbid(unused_variables)] - -use std::fmt; -use std::sync::Arc; - -use crate::http_server::common_responses::tag_info::TagInfo; -use crate::http_server::common_responses::common_web_error::CommonWebError; -use crate::http_server::web_utils::response_error_helpers::to_simple_json_error; -use crate::http_server::user_lookup::user_session::require_user_session_extended::require_user_session_extended; -use crate::state::server_state::ServerState; -use actix_web::error::ResponseError; -use actix_web::http::StatusCode; -use actix_web::web::{Data, Json, Path}; -use actix_web::{HttpRequest, HttpResponse}; -use composite_identifiers::by_table::tag_uses::tag_use_entity::TagUseEntity; -use enums::by_table::tag_uses::tag_use_entity_type::TagUseEntityType; -use log::warn; -use mysql_queries::queries::media_files::get::get_media_file::get_media_file_with_transactor; -use mysql_queries::queries::model_weights::get::get_weight::get_weight_by_token_with_transactor; -use mysql_queries::queries::tags::create_tag::create_tag; -use mysql_queries::queries::tags::select_matching_tags::select_matching_tags; -use mysql_queries::queries::tags::update_tags_for_entity::update_tags_for_entity; -use mysql_queries::utils::transactor::Transactor; -use sqlx::MySqlConnection; -use tokens::tokens::media_files::MediaFileToken; -use tokens::tokens::model_weights::ModelWeightToken; -use tokens::tokens::tags::TagToken; -use tokens::tokens::users::UserToken; -use utoipa::ToSchema; - -/// Maximum number of tags allowed per item. -const MAX_TAGS : usize = 30; - -/// For the URL PathInfo -#[derive(Deserialize, ToSchema)] -pub struct SetTagsForEntityPathInfo { - entity_type: TagUseEntityType, - entity_token: String, -} - -#[derive(Deserialize, ToSchema)] -pub struct SetTagsForEntityRequest { - /// A comma separated list of tags. - tags: String, -} - -#[derive(Serialize, ToSchema)] -pub struct SetTagsForEntitySuccessResponse { - pub success: bool, - pub tags: Vec, -} -// NB: Not using derive_more::Display since Clion doesn't understand it. -/// Edit the tags (in bulk) for an entity. Comma-separated. -#[utoipa::path( - post, - tag = "Tags", - path = "/v1/tags/edit/{entity_type}/{entity_token}", - responses( - (status = 200, description = "Success", body = SetTagsForEntitySuccessResponse), - (status = 400, description = "Bad input", body = CommonWebError), - (status = 401, description = "Not authorized", body = CommonWebError), - (status = 500, description = "Server error", body = CommonWebError), - ), - params( - ("request" = SetTagsForEntityRequest, description = "Payload for Request"), - ) -)] -pub async fn set_tags_for_entity_handler( - http_request: HttpRequest, - request: Json, - path: Path, - server_state: Data>, -) -> Result, CommonWebError> -{ - let token = path.entity_token.as_str(); - let token_prefix_matches = match path.entity_type { - TagUseEntityType::MediaFile => token.starts_with(MediaFileToken::token_prefix()), - TagUseEntityType::ModelWeight => token.starts_with(ModelWeightToken::token_prefix()), - }; - - if !token_prefix_matches { - warn!("invalid token prefix: {:?} for {:?}", path.entity_token, path.entity_type); - return Err(CommonWebError::BadInputWithSimpleMessage("invalid token prefix".to_string())); - } - - let mut mysql_connection = server_state.mysql_pool - .acquire() - .await - .map_err(|err| { - warn!("MySql pool error: {:?}", err); - CommonWebError::from_error(err) - })?; - - let user_session = require_user_session_extended( - &http_request, - &server_state.session_checker, - &mut *mysql_connection).await?; - - if user_session.role.is_banned { - return Err(CommonWebError::NotAuthorized); - } - - let is_mod = user_session.role.can_ban_users; - - let is_creator = get_is_creator( - path.entity_type, - &path.entity_token, - &user_session.user_token_typed, - &mut *mysql_connection - ).await?; - - if !is_creator && !is_mod { - warn!("user is not allowed to modify entity: {:?}", user_session.user_token); - return Err(CommonWebError::NotAuthorized); - } - - let request_tags = to_normalized_tags(&request.tags); - - let matching_tags = select_matching_tags(&request_tags, Transactor::for_connection(&mut *mysql_connection)) - .await - .map(|tags| tags.into_iter() - .map(|tag| TagInfo { - token: tag.token, - value: tag.tag_value.to_string(), - }) - .collect::>()) - .map_err(|e| { - warn!("error selecting tags: {:?}", e); - CommonWebError::from_anyhow_error(e) - })?; - - let matching_tag_values = matching_tags - .iter() - .map(|tag| tag.value.to_string()) - .collect::>(); - - let new_tag_values = request_tags.iter() - .filter(|tag| !matching_tag_values.contains(tag)) - .map(|tag| tag.as_str()) - .collect::>(); - - let mut new_tags = Vec::new(); - - // TODO(bt): Bulk insert instead of insert per tag. - - for new_tag_value in new_tag_values { - let token = create_tag(new_tag_value, Transactor::for_connection(&mut *mysql_connection)) - .await - .map_err(|e| { - warn!("error creating tag: {:?}", e); - CommonWebError::from_error(e) - })?; - new_tags.push(TagInfo { - token, - value: new_tag_value.to_string(), - }); - } - - let final_tag_set = matching_tags.into_iter() - .chain(new_tags.into_iter()) - .collect::>(); - - let final_tag_tokens = final_tag_set.iter() - .map(|tag_info| tag_info.token.clone()) - .collect::>(); - - let entity = TagUseEntity::from_entity_type_and_token( - path.entity_type, &path.entity_token); - - update_tags_for_entity( - entity, - &final_tag_tokens, - &mut mysql_connection) - .await - .map_err(|e| { - warn!("error updating tags: {:?}", e); - CommonWebError::from_anyhow_error(e) - })?; - - Ok(Json(SetTagsForEntitySuccessResponse { - success: true, - tags: final_tag_set, - })) -} - -async fn get_is_creator( - entity_type: TagUseEntityType, - entity_token: &str, - user_token: &UserToken, - mysql_connection: &mut MySqlConnection -) -> Result { - match entity_type { - TagUseEntityType::MediaFile => { - let entity_token = MediaFileToken::new_from_str(entity_token); - let result = get_media_file_with_transactor( - &entity_token, - false, - Transactor::for_connection(mysql_connection), - ).await; - match result { - Ok(Some(media_file)) => { - Ok(media_file.maybe_creator_user_token - .is_some_and(|t| t.as_str() == user_token.as_str())) - }, - Ok(None) => Err(CommonWebError::NotFound), - Err(err) => { - warn!("Error looking up media_file: {:?}", err); - Err(CommonWebError::from_anyhow_error(err)) - } - } - } - TagUseEntityType::ModelWeight => { - let entity_token = ModelWeightToken::new_from_str(entity_token); - let result = get_weight_by_token_with_transactor( - &entity_token, - false, - Transactor::for_connection(mysql_connection), - ).await; - match result { - Ok(Some(model_weight)) => { - Ok(model_weight.creator_user_token.as_str() == user_token.as_str()) - }, - Ok(None) => Err(CommonWebError::NotFound), - Err(err) => { - warn!("Error looking up model_weight: {:?}", err); - Err(CommonWebError::from_anyhow_error(err)) - } - } - } - } -} - -fn to_normalized_tags(tags: &str) -> Vec { - let mut tags : Vec = tags.split(',') - .map(|tag| tag.trim().to_lowercase()) - .filter(|tag| !tag.is_empty()) - .collect(); - - tags.truncate(MAX_TAGS); - - tags -} diff --git a/crates/service/web/storyteller_web/src/http_server/routes/application_routes/add_application_routes.rs b/crates/service/web/storyteller_web/src/http_server/routes/application_routes/add_application_routes.rs index 1699b07ef3..0277894a82 100644 --- a/crates/service/web/storyteller_web/src/http_server/routes/application_routes/add_application_routes.rs +++ b/crates/service/web/storyteller_web/src/http_server/routes/application_routes/add_application_routes.rs @@ -16,7 +16,6 @@ use crate::http_server::routes::application_routes::omni_gen_routes::add_omni_ge use crate::http_server::routes::application_routes::prompts_routes::add_prompts_routes; use crate::http_server::routes::application_routes::stripe_artcraft_routes::add_stripe_artcraft_routes; use crate::http_server::routes::application_routes::subscription_routes::add_subscription_routes; -use crate::http_server::routes::application_routes::tag_routes::add_tag_routes; use crate::http_server::routes::application_routes::user_bookmarks_routes::add_user_bookmarks_routes; use crate::http_server::routes::application_routes::user_rating_routes::add_user_rating_routes; use crate::http_server::routes::application_routes::user_referral_code_routes::add_user_referral_code_routes; @@ -55,7 +54,6 @@ where app = add_webhook_routes(app); // /v1/webhooks/... (fal) // Legacy FakeYou surface area that might be useful again one day - app = add_tag_routes(app); // /v1/tags app = add_weights_routes(app); // v1/weights // Media files routes diff --git a/crates/service/web/storyteller_web/src/http_server/routes/application_routes/mod.rs b/crates/service/web/storyteller_web/src/http_server/routes/application_routes/mod.rs index d67c9c160c..b2fc23d91a 100644 --- a/crates/service/web/storyteller_web/src/http_server/routes/application_routes/mod.rs +++ b/crates/service/web/storyteller_web/src/http_server/routes/application_routes/mod.rs @@ -16,7 +16,6 @@ mod omni_gen_routes; mod prompts_routes; mod stripe_artcraft_routes; mod subscription_routes; -mod tag_routes; mod user_bookmarks_routes; mod user_rating_routes; mod user_referral_code_routes; diff --git a/crates/service/web/storyteller_web/src/http_server/routes/application_routes/tag_routes.rs b/crates/service/web/storyteller_web/src/http_server/routes/application_routes/tag_routes.rs deleted file mode 100644 index 92ed37f8e2..0000000000 --- a/crates/service/web/storyteller_web/src/http_server/routes/application_routes/tag_routes.rs +++ /dev/null @@ -1,31 +0,0 @@ -use crate::http_server::endpoints::tags::list_tags_for_entity_handler::list_tags_for_entity_handler; -use crate::http_server::endpoints::tags::set_tags_for_entity_handler::set_tags_for_entity_handler; -use actix_http::body::MessageBody; -use actix_service::ServiceFactory; -use actix_web::dev::{ServiceRequest, ServiceResponse}; -use actix_web::{web, App, Error, HttpResponse}; - -pub fn add_tag_routes(app: App) -> App -where - B: MessageBody, - T: ServiceFactory< - ServiceRequest, - Config = (), - Response = ServiceResponse, - Error = Error, - InitError = () - > -{ - app.service( - web - ::scope("/v1/tags") - .service(web::resource("/list/{entity_type}/{entity_token}") - .route(web::get().to(list_tags_for_entity_handler)) - .route(web::head().to(|| HttpResponse::Ok())) - ) - .service(web::resource("/edit/{entity_type}/{entity_token}") - .route(web::post().to(set_tags_for_entity_handler)) - .route(web::head().to(|| HttpResponse::Ok())) - ) - ) -}