diff --git a/server/src/steel/server/execution.rs b/server/src/steel/server/execution.rs index ac77583..f8fd1d2 100644 --- a/server/src/steel/server/execution.rs +++ b/server/src/steel/server/execution.rs @@ -2,179 +2,3 @@ use std::fmt; use std::collections::HashMap; use sqlx::{PgPool, Row}; - -#[derive(Debug)] -pub enum ScriptOperation { - SetToLocalColumn { source: String }, - SetToExternalColumn { table: String, column: String }, -} - -#[derive(Debug)] -pub enum ScriptExecutionError { - ParseError(String), - MissingSourceColumn(String), - Mismatch { expected: String, actual: String }, - InvalidReference(String), - MissingLinkKey(String), - DatabaseError(String), - MissingExternalData(String), -} - -impl fmt::Display for ScriptExecutionError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::ParseError(msg) => write!(f, "Parse error: {}", msg), - Self::MissingSourceColumn(col) => write!(f, "Missing source column: {}", col), - Self::Mismatch { expected, actual } => write!( - f, - "Value does not match script expectation. Expected: {}, Actual: {}", - expected, actual - ), - Self::InvalidReference(msg) => write!(f, "Invalid reference: {}", msg), - Self::MissingLinkKey(key) => write!(f, "Missing link key: {}", key), - Self::DatabaseError(msg) => write!(f, "Database error: {}", msg), - Self::MissingExternalData(msg) => write!(f, "External data not found: {}", msg), - } - } -} - -pub fn parse_script(script: &str, expected_target: &str) -> Result { - let script = script.trim(); - - if !script.starts_with("(set! ") || !script.ends_with(')') { - return Err(ScriptExecutionError::ParseError( - "Script must be in the form (set! target source)".to_string(), - )); - } - - let content = &script[6..script.len() - 1].trim(); - let mut parts = content.split_whitespace(); - - let target = parts.next().ok_or_else(|| { - ScriptExecutionError::ParseError("Missing target in set! expression".to_string()) - })?; - let source = parts.next().ok_or_else(|| { - ScriptExecutionError::ParseError("Missing source in set! expression".to_string()) - })?; - - if parts.next().is_some() { - return Err(ScriptExecutionError::ParseError( - "Too many arguments in set! expression".to_string(), - )); - } - - if target != expected_target { - return Err(ScriptExecutionError::ParseError(format!( - "Script target '{}' does not match expected '{}'", - target, expected_target - ))); - } - - // Check if source is an external reference - if source.starts_with('@') { - let (table, column) = source.split_once('.') - .ok_or_else(|| ScriptExecutionError::InvalidReference( - format!("Invalid external reference format: {}", source) - ))?; - Ok(ScriptOperation::SetToExternalColumn { - table: table.to_string(), - column: column.to_string(), - }) - } else { - Ok(ScriptOperation::SetToLocalColumn { - source: source.to_string(), - }) - } -} - -pub async fn resolve_value( - db_pool: &PgPool, - profile_id: i64, - current_table: &str, - current_data: &HashMap, - source: &str, -) -> Result { - if let Some((table, column)) = source.split_once('.') { - // External table reference - let external_table = table.strip_prefix('@') - .ok_or_else(|| ScriptExecutionError::InvalidReference(format!("Invalid external reference: {}", source)))?; - - // Get foreign key relationship info - let (fk_column, referenced_column) = get_relationship_info(db_pool, current_table, external_table).await?; - - // Get foreign key value from current data - let fk_value = current_data.get(&fk_column) - .ok_or_else(|| ScriptExecutionError::MissingLinkKey(fk_column.clone()))?; - - // Build and execute query - let query = format!( - "SELECT {} FROM \"{}\" WHERE {} = $1", - column, external_table, referenced_column - ); - - let result: Option = sqlx::query(&query) - .bind(fk_value) - .fetch_optional(db_pool) - .await - .map_err(|e| ScriptExecutionError::DatabaseError(e.to_string()))? - .and_then(|row| row.try_get(0).ok()); - - result.ok_or_else(|| ScriptExecutionError::MissingExternalData( - format!("No data found for {} in {}", column, external_table) - )) - } else { - // Local column reference remains the same - current_data.get(source) - .cloned() - .ok_or_else(|| ScriptExecutionError::MissingSourceColumn(source.into())) - } -} - -async fn get_relationship_info( - db_pool: &PgPool, - current_table: &str, - external_table: &str, -) -> Result<(String, String), ScriptExecutionError> { - let query = r#" - SELECT - kcu.column_name AS fk_column, - ccu.column_name AS referenced_column - FROM - information_schema.table_constraints AS tc - JOIN information_schema.key_column_usage AS kcu - ON tc.constraint_name = kcu.constraint_name - AND tc.table_schema = kcu.table_schema - JOIN information_schema.constraint_column_usage AS ccu - ON tc.constraint_name = ccu.constraint_name - AND tc.table_schema = ccu.table_schema - WHERE - tc.constraint_type = 'FOREIGN KEY' - AND tc.table_name = $1 - AND ccu.table_name = $2 - "#; - - let rows = sqlx::query(query) - .bind(current_table) - .bind(external_table) - .fetch_all(db_pool) - .await - .map_err(|e| ScriptExecutionError::DatabaseError(e.to_string()))?; - - match rows.len() { - 0 => Err(ScriptExecutionError::InvalidReference(format!( - "No foreign key relationship found from {} to {}", - current_table, external_table - ))), - 1 => { - let row = &rows[0]; - Ok(( - row.get::("fk_column"), - row.get::("referenced_column"), - )) - }, - _ => Err(ScriptExecutionError::InvalidReference(format!( - "Multiple foreign keys between {} and {} - cannot resolve ambiguity", - current_table, external_table - ))) - } -} diff --git a/server/src/steel/server/mod.rs b/server/src/steel/server/mod.rs index 6f71416..09f4648 100644 --- a/server/src/steel/server/mod.rs +++ b/server/src/steel/server/mod.rs @@ -1,4 +1,4 @@ // src/steel/server/mod.rs -pub mod execution; +// pub mod execution; -pub use execution::*; +// pub use execution::*; diff --git a/server/src/tables_data/handlers/post_table_data.rs b/server/src/tables_data/handlers/post_table_data.rs index fd3202b..0183321 100644 --- a/server/src/tables_data/handlers/post_table_data.rs +++ b/server/src/tables_data/handlers/post_table_data.rs @@ -4,7 +4,6 @@ use sqlx::{PgPool, Arguments}; use sqlx::postgres::PgArguments; use chrono::{DateTime, Utc}; use common::proto::multieko2::tables_data::{PostTableDataRequest, PostTableDataResponse}; -use crate::steel::server::execution::{self, ScriptOperation}; use std::collections::HashMap; pub async fn post_table_data( @@ -19,13 +18,13 @@ pub async fn post_table_data( for (key, value) in request.data { let trimmed = value.trim().to_string(); - // Handle firma specially - it cannot be empty - if key == "firma" && trimmed.is_empty() { + // Handle specially - it cannot be empty + if trimmed.is_empty() { return Err(Status::invalid_argument("Firma cannot be empty")); } // Add trimmed non-empty values to data map - if !trimmed.is_empty() || key == "firma" { + if !trimmed.is_empty(){ data.insert(key, trimmed); } } @@ -82,7 +81,7 @@ pub async fn post_table_data( .map_err(|e| Status::internal(format!("Link lookup error: {}", e)))?; // Build required columns list - let mut required_columns = vec!["firma".to_string()]; + let mut required_columns = vec![]; for link in required_links { let base_name = link.table_name.split('_').last().unwrap_or(&link.table_name); required_columns.push(format!("{}_id", base_name)); @@ -96,7 +95,7 @@ pub async fn post_table_data( } // Validate all data columns - let system_columns = ["firma", "deleted"]; + let system_columns = ["deleted"]; let user_columns: Vec<&String> = columns.iter().map(|(name, _)| name).collect(); for key in data.keys() { if !system_columns.contains(&key.as_str()) && !user_columns.contains(&key) { @@ -113,6 +112,8 @@ pub async fn post_table_data( .await .map_err(|e| Status::internal(format!("Failed to fetch scripts: {}", e)))?; + // TODO: Re-implement script validation logic + /* for script_record in scripts { let target_column = script_record.target_column; @@ -158,6 +159,7 @@ pub async fn post_table_data( )); } } + */ // Prepare SQL parameters let mut params = PgArguments::default(); @@ -168,7 +170,6 @@ pub async fn post_table_data( for (col, value) in data { let sql_type = if system_columns.contains(&col.as_str()) { match col.as_str() { - "firma" => "TEXT", "deleted" => "BOOLEAN", _ => return Err(Status::invalid_argument("Invalid system column")), }