feat: Introduce surrealdb_helpers for query building and execution utilities
This commit is contained in:
@@ -6,10 +6,12 @@ use super::{
|
|||||||
use imphnen_libs::{
|
use imphnen_libs::{
|
||||||
AppState, MetaRequestDto, ResourceEnum, ResponseListSuccessDto
|
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 surrealdb::sql::Thing;
|
||||||
use anyhow::{Result, bail};
|
use anyhow::{Result, bail};
|
||||||
use imphnen_utils::{DetailQueryBuilder, QueryListBuilder, make_thing_from_enum};
|
|
||||||
use serde_json;
|
use serde_json;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
@@ -184,12 +186,9 @@ impl<'a> TeamsRepository<'a> {
|
|||||||
pub async fn query_team_members(&self, team_id: &Thing) -> Result<Vec<TeamMembersQueryDto>> {
|
pub async fn query_team_members(&self, team_id: &Thing) -> Result<Vec<TeamMembersQueryDto>> {
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
let db = &self.state.surrealdb_ws;
|
let db = &self.state.surrealdb_ws;
|
||||||
let sql = format!(
|
|
||||||
"SELECT * FROM {} WHERE team_id = type::thing('{}', '{}') AND is_active = true",
|
let condition = format!("{} AND is_active = true", build_thing_condition("team_id", team_id));
|
||||||
ResourceEnum::TeamMembers,
|
let sql = format!("SELECT * FROM {} WHERE {}", ResourceEnum::TeamMembers, condition);
|
||||||
team_id.tb,
|
|
||||||
team_id.id.to_raw()
|
|
||||||
);
|
|
||||||
let mut result = db.query(sql).await?;
|
let mut result = db.query(sql).await?;
|
||||||
|
|
||||||
let members: Vec<TeamMembersQueryDto> = match result.take(0) {
|
let members: Vec<TeamMembersQueryDto> = match result.take(0) {
|
||||||
@@ -241,38 +240,16 @@ impl<'a> TeamsRepository<'a> {
|
|||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
let db = &self.state.surrealdb_ws;
|
let db = &self.state.surrealdb_ws;
|
||||||
|
|
||||||
let sql = format!(
|
let conditions = format!(
|
||||||
"SELECT COUNT() AS member_count FROM {} WHERE team_id = type::thing('{}', '{}') AND user_id = type::thing('{}', '{}') AND is_active = true",
|
"{} AND is_active = true",
|
||||||
ResourceEnum::TeamMembers,
|
build_multi_thing_condition(&[("team_id", team_id), ("user_id", user_id)])
|
||||||
team_id.tb,
|
|
||||||
team_id.id.to_raw(),
|
|
||||||
user_id.tb,
|
|
||||||
user_id.id.to_raw()
|
|
||||||
);
|
);
|
||||||
let mut result = db.query(sql).await?;
|
|
||||||
|
|
||||||
// Use COUNT query to avoid serialization issues with enum values
|
let member_count = execute_safe_count_query(
|
||||||
let count_result: Vec<serde_json::Value> = match result.take(0) {
|
db,
|
||||||
Ok(count_result) => count_result,
|
&ResourceEnum::TeamMembers.to_string(),
|
||||||
Err(e) => {
|
&conditions,
|
||||||
if std::env::var("RUST_ENV").unwrap_or_else(|_| "development".to_string())
|
).await.unwrap_or(0);
|
||||||
== "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 elapsed = now.elapsed();
|
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<String> {
|
pub async fn query_remove_team_member(&self, team_id: &Thing, user_id: &Thing) -> Result<String> {
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
let db = &self.state.surrealdb_ws;
|
let db = &self.state.surrealdb_ws;
|
||||||
|
|
||||||
|
let conditions = build_multi_thing_condition(&[("team_id", team_id), ("user_id", user_id)]);
|
||||||
let sql = format!(
|
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,
|
ResourceEnum::TeamMembers,
|
||||||
team_id.tb,
|
conditions
|
||||||
team_id.id.to_raw(),
|
|
||||||
user_id.tb,
|
|
||||||
user_id.id.to_raw()
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Execute the query but don't try to parse the result as it can contain complex enum values
|
execute_safe_update_query(db, sql).await?;
|
||||||
let mut result = db.query(sql).await?;
|
|
||||||
|
|
||||||
// Just consume the result without trying to deserialize it to avoid serialization errors
|
|
||||||
let _: Result<Vec<serde_json::Value>, _> = result.take(0);
|
|
||||||
|
|
||||||
let elapsed = now.elapsed();
|
let elapsed = now.elapsed();
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ pub mod response_format;
|
|||||||
pub mod serde_helpers;
|
pub mod serde_helpers;
|
||||||
pub mod validator;
|
pub mod validator;
|
||||||
pub mod csrf_token;
|
pub mod csrf_token;
|
||||||
|
pub mod surrealdb_helpers;
|
||||||
|
|
||||||
pub use logger::init_logger;
|
pub use logger::init_logger;
|
||||||
pub use bind_filter::*;
|
pub use bind_filter::*;
|
||||||
@@ -30,3 +31,4 @@ pub use serde_helpers::{
|
|||||||
};
|
};
|
||||||
pub use validator::*;
|
pub use validator::*;
|
||||||
pub use csrf_token::*;
|
pub use csrf_token::*;
|
||||||
|
pub use surrealdb_helpers::*;
|
||||||
|
|||||||
@@ -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::<Vec<_>>()
|
||||||
|
.join(" AND ")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn execute_safe_update_query(
|
||||||
|
db: &surrealdb::Surreal<surrealdb::engine::any::Any>,
|
||||||
|
query: String,
|
||||||
|
) -> Result<()> {
|
||||||
|
let mut result = db.query(query).await?;
|
||||||
|
let _: Result<Vec<serde_json::Value>, _> = result.take(0);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn execute_safe_count_query(
|
||||||
|
db: &surrealdb::Surreal<surrealdb::engine::any::Any>,
|
||||||
|
table: &str,
|
||||||
|
conditions: &str,
|
||||||
|
) -> Result<u64> {
|
||||||
|
let query = format!("SELECT COUNT() AS member_count FROM {} WHERE {}", table, conditions);
|
||||||
|
let mut result = db.query(query).await?;
|
||||||
|
|
||||||
|
let count_result: Vec<serde_json::Value> = 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')"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user