From 8f981572cd0751d880800bec5c99072ce936bfc4 Mon Sep 17 00:00:00 2001 From: MythEclipse Date: Mon, 22 Sep 2025 15:06:37 +0700 Subject: [PATCH] feat: Introduce surrealdb_helpers for query building and execution utilities --- imphnen-iam/src/v1/teams/teams_repository.rs | 68 ++++++------------ imphnen-utils/src/lib.rs | 2 + imphnen-utils/src/surrealdb_helpers.rs | 76 ++++++++++++++++++++ 3 files changed, 98 insertions(+), 48 deletions(-) create mode 100644 imphnen-utils/src/surrealdb_helpers.rs diff --git a/imphnen-iam/src/v1/teams/teams_repository.rs b/imphnen-iam/src/v1/teams/teams_repository.rs index cedb889..041e31e 100644 --- a/imphnen-iam/src/v1/teams/teams_repository.rs +++ b/imphnen-iam/src/v1/teams/teams_repository.rs @@ -6,10 +6,12 @@ use super::{ use imphnen_libs::{ AppState, MetaRequestDto, ResourceEnum, ResponseListSuccessDto }; -use imphnen_utils::get_id; +use imphnen_utils::{ + get_id, DetailQueryBuilder, QueryListBuilder, make_thing_from_enum, + build_thing_condition, build_multi_thing_condition, execute_safe_update_query, execute_safe_count_query +}; use surrealdb::sql::Thing; use anyhow::{Result, bail}; -use imphnen_utils::{DetailQueryBuilder, QueryListBuilder, make_thing_from_enum}; use serde_json; use std::time::Instant; @@ -184,12 +186,9 @@ impl<'a> TeamsRepository<'a> { pub async fn query_team_members(&self, team_id: &Thing) -> Result> { let now = Instant::now(); let db = &self.state.surrealdb_ws; - let sql = format!( - "SELECT * FROM {} WHERE team_id = type::thing('{}', '{}') AND is_active = true", - ResourceEnum::TeamMembers, - team_id.tb, - team_id.id.to_raw() - ); + + let condition = format!("{} AND is_active = true", build_thing_condition("team_id", team_id)); + let sql = format!("SELECT * FROM {} WHERE {}", ResourceEnum::TeamMembers, condition); let mut result = db.query(sql).await?; let members: Vec = match result.take(0) { @@ -241,38 +240,16 @@ impl<'a> TeamsRepository<'a> { let now = Instant::now(); let db = &self.state.surrealdb_ws; - let sql = format!( - "SELECT COUNT() AS member_count FROM {} WHERE team_id = type::thing('{}', '{}') AND user_id = type::thing('{}', '{}') AND is_active = true", - ResourceEnum::TeamMembers, - team_id.tb, - team_id.id.to_raw(), - user_id.tb, - user_id.id.to_raw() + let conditions = format!( + "{} AND is_active = true", + build_multi_thing_condition(&[("team_id", team_id), ("user_id", user_id)]) ); - let mut result = db.query(sql).await?; - // Use COUNT query to avoid serialization issues with enum values - let count_result: Vec = match result.take(0) { - Ok(count_result) => count_result, - Err(e) => { - if std::env::var("RUST_ENV").unwrap_or_else(|_| "development".to_string()) - == "development" - { - println!("Error checking team membership: {:?}", e); - } - vec![] - } - }; - - let member_count = if let Some(first_result) = count_result.first() { - if let Some(count_val) = first_result.get("member_count") { - count_val.as_u64().unwrap_or(0) - } else { - 0 - } - } else { - 0 - }; + let member_count = execute_safe_count_query( + db, + &ResourceEnum::TeamMembers.to_string(), + &conditions, + ).await.unwrap_or(0); let elapsed = now.elapsed(); @@ -419,20 +396,15 @@ impl<'a> TeamsRepository<'a> { pub async fn query_remove_team_member(&self, team_id: &Thing, user_id: &Thing) -> Result { let now = Instant::now(); let db = &self.state.surrealdb_ws; + + let conditions = build_multi_thing_condition(&[("team_id", team_id), ("user_id", user_id)]); let sql = format!( - "UPDATE {} SET is_active = false WHERE team_id = type::thing('{}', '{}') AND user_id = type::thing('{}', '{}')", + "UPDATE {} SET is_active = false WHERE {}", ResourceEnum::TeamMembers, - team_id.tb, - team_id.id.to_raw(), - user_id.tb, - user_id.id.to_raw() + conditions ); - // Execute the query but don't try to parse the result as it can contain complex enum values - let mut result = db.query(sql).await?; - - // Just consume the result without trying to deserialize it to avoid serialization errors - let _: Result, _> = result.take(0); + execute_safe_update_query(db, sql).await?; let elapsed = now.elapsed(); diff --git a/imphnen-utils/src/lib.rs b/imphnen-utils/src/lib.rs index 29b68f6..78dacd4 100644 --- a/imphnen-utils/src/lib.rs +++ b/imphnen-utils/src/lib.rs @@ -11,6 +11,7 @@ pub mod response_format; pub mod serde_helpers; pub mod validator; pub mod csrf_token; +pub mod surrealdb_helpers; pub use logger::init_logger; pub use bind_filter::*; @@ -30,3 +31,4 @@ pub use serde_helpers::{ }; pub use validator::*; pub use csrf_token::*; +pub use surrealdb_helpers::*; diff --git a/imphnen-utils/src/surrealdb_helpers.rs b/imphnen-utils/src/surrealdb_helpers.rs new file mode 100644 index 0000000..f0d40e4 --- /dev/null +++ b/imphnen-utils/src/surrealdb_helpers.rs @@ -0,0 +1,76 @@ +use surrealdb::sql::Thing; +use anyhow::Result; + +pub fn build_thing_condition(field: &str, thing: &Thing) -> String { + format!("{} = type::thing('{}', '{}')", field, thing.tb, thing.id.to_raw()) +} + +pub fn build_multi_thing_condition(conditions: &[(&str, &Thing)]) -> String { + conditions + .iter() + .map(|(field, thing)| build_thing_condition(field, thing)) + .collect::>() + .join(" AND ") +} + +pub async fn execute_safe_update_query( + db: &surrealdb::Surreal, + query: String, +) -> Result<()> { + let mut result = db.query(query).await?; + let _: Result, _> = result.take(0); + Ok(()) +} + +pub async fn execute_safe_count_query( + db: &surrealdb::Surreal, + table: &str, + conditions: &str, +) -> Result { + let query = format!("SELECT COUNT() AS member_count FROM {} WHERE {}", table, conditions); + let mut result = db.query(query).await?; + + let count_result: Vec = result.take(0).unwrap_or_default(); + + let count = if let Some(first_result) = count_result.first() { + if let Some(count_val) = first_result.get("member_count") { + count_val.as_u64().unwrap_or(0) + } else { + 0 + } + } else { + 0 + }; + + Ok(count) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::make_thing_from_enum; + use imphnen_libs::ResourceEnum; + + #[test] + fn test_build_thing_condition() { + let team_thing = make_thing_from_enum(ResourceEnum::Teams, "test-id"); + let condition = build_thing_condition("team_id", &team_thing); + assert_eq!(condition, "team_id = type::thing('app_teams', 'test-id')"); + } + + #[test] + fn test_build_multi_thing_condition() { + let team_thing = make_thing_from_enum(ResourceEnum::Teams, "team-id"); + let user_thing = make_thing_from_enum(ResourceEnum::Users, "user-id"); + + let conditions = build_multi_thing_condition(&[ + ("team_id", &team_thing), + ("user_id", &user_thing), + ]); + + assert_eq!( + conditions, + "team_id = type::thing('app_teams', 'team-id') AND user_id = type::thing('app_users', 'user-id')" + ); + } +} \ No newline at end of file