diff --git a/server/Cargo.toml b/server/Cargo.toml index edcac6a..d643cfb 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -18,8 +18,8 @@ tonic = "0.12.3" tonic-reflection = "0.12.3" tracing = "0.1.41" time = { version = "0.3.39", features = ["local-offset"] } -steel-core = { git = "https://github.com/mattwparas/steel.git", branch = "master", package = "steel-core" } steel-derive = { git = "https://github.com/mattwparas/steel.git", branch = "master", package = "steel-derive" } +steel-core = { git = "https://github.com/mattwparas/steel.git", version = "0.6.0", features = ["anyhow", "dylibs", "sync"] } [lib] name = "server" diff --git a/server/migrations/20250306132638_create_table_scripts.sql b/server/migrations/20250306132638_create_table_scripts.sql index 43b0be5..8b76969 100644 --- a/server/migrations/20250306132638_create_table_scripts.sql +++ b/server/migrations/20250306132638_create_table_scripts.sql @@ -3,6 +3,7 @@ CREATE TABLE table_scripts ( id BIGSERIAL PRIMARY KEY, table_definitions_id BIGINT NOT NULL REFERENCES table_definitions(id), target_column TEXT NOT NULL, + target_column_type TEXT NOT NULL, script TEXT NOT NULL, source_tables TEXT[], source_columns TEXT[], diff --git a/server/src/steel/server/data_access.rs b/server/src/steel/server/data_access.rs index 0c68458..258b21c 100644 --- a/server/src/steel/server/data_access.rs +++ b/server/src/steel/server/data_access.rs @@ -1,92 +1,16 @@ // src/steel/server/data_access.rs -use crate::steel::server::{ - rvals::{Custom, SteelVal}, - SteelError, -}; -use sqlx::{PgPool, Row}; -use std::collections::HashMap; +// With this implementation +#[derive(Debug)] +pub struct SteelError(String); -pub struct TableAccess { - db_pool: PgPool, - profile_id: i64, - current_table: String, - local_data: HashMap, -} - -impl Custom for TableAccess {} - -impl TableAccess { - pub fn new( - db_pool: PgPool, - profile_id: i64, - current_table: &str, - local_data: HashMap, - ) -> Self { - Self { - db_pool, - profile_id, - current_table: current_table.to_string(), - local_data, - } - } - - async fn validate_link(&self, table: &str) -> Result<(), SteelError> { - let is_linked: bool = sqlx::query_scalar!( - r#"SELECT EXISTS( - SELECT 1 FROM table_definition_links tdl - JOIN table_definitions src ON tdl.source_table_id = src.id - JOIN table_definitions dst ON tdl.linked_table_id = dst.id - WHERE src.table_name = $1 AND dst.table_name = $2 AND src.profile_id = $3 - )"#, - self.current_table, - table, - self.profile_id - ) - .fetch_one(&self.db_pool) - .await - .map_err(|e| SteelError::new(e.to_string()))? - .unwrap_or(false); - - if !is_linked { - return Err(SteelError::new(format!( - "Table '{}' is not linked to '{}'", - table, self.current_table - ))); - } - - Ok(()) - } - - async fn get_external_value(&self, table: &str, column: &str) -> Result { - self.validate_link(table).await?; - - let firma = self.local_data.get("firma") - .ok_or_else(|| SteelError::new("Missing 'firma' in local data"))?; - - let query = format!( - "SELECT {} FROM \"{}\" WHERE firma = $1 AND profile_id = $2", - column, table - ); - - let value: Option = sqlx::query(&query) - .bind(firma) - .bind(self.profile_id) - .fetch_optional(&self.db_pool) - .await - .map_err(|e| SteelError::new(e.to_string()))? - .and_then(|row| row.try_get(0).ok()); - - value - .map(SteelVal::StringV) - .ok_or_else(|| SteelError::new(format!("No value found for {}.{}", table, column))) +impl SteelError { + pub fn new(message: impl Into) -> Self { + Self(message.into()) } } -fn table_access_module( - db_pool: PgPool, - profile_id: i64, - current_table: String, - local_data: HashMap, -) -> Result { - Ok(TableAccess::new(db_pool, profile_id, ¤t_table, local_data)) +impl std::fmt::Display for SteelError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } } diff --git a/server/src/steel/server/mod.rs b/server/src/steel/server/mod.rs index 366096d..e347288 100644 --- a/server/src/steel/server/mod.rs +++ b/server/src/steel/server/mod.rs @@ -1,5 +1,4 @@ // src/steel/server/mod.rs -pub mod rvals; pub mod data_access; pub mod execution; pub mod script; @@ -7,4 +6,3 @@ pub mod script; pub use data_access::*; pub use execution::*; pub use script::*; -pub use rvals::*; diff --git a/server/src/steel/server/rvals.rs b/server/src/steel/server/rvals.rs deleted file mode 100644 index 8de96a1..0000000 --- a/server/src/steel/server/rvals.rs +++ /dev/null @@ -1,25 +0,0 @@ -// src/steel/server/rvals.rs -use std::fmt; - -#[derive(Debug)] -pub struct SteelError { - pub message: String, -} - -impl SteelError { - pub fn new(message: impl Into) -> Self { - Self { message: message.into() } - } -} - -impl fmt::Display for SteelError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.message) - } -} - -pub trait Custom {} -pub enum SteelVal { - StringV(String), - // Add other variants as needed -} diff --git a/server/src/table_script/handlers/post_table_script.rs b/server/src/table_script/handlers/post_table_script.rs index 580704f..348b44d 100644 --- a/server/src/table_script/handlers/post_table_script.rs +++ b/server/src/table_script/handlers/post_table_script.rs @@ -2,19 +2,15 @@ use tonic::Status; use sqlx::{PgPool, Error as SqlxError}; use common::proto::multieko2::table_script::{PostTableScriptRequest, TableScriptResponse}; -use crate::steel::server::script::validate_script; use serde_json::Value; -// Add these imports for the execution module and ScriptOperation -use crate::steel::server::execution::{self, ScriptOperation}; - const SYSTEM_COLUMNS: &[&str] = &["id", "deleted", "created_at"]; fn validate_target_column( table_name: &str, target: &str, table_columns: &Value, -) -> Result<(), String> { +) -> Result { if SYSTEM_COLUMNS.contains(&target) { return Err(format!("Cannot override system column: {}", target)); } @@ -22,28 +18,31 @@ fn validate_target_column( let columns: Vec = serde_json::from_value(table_columns.clone()) .map_err(|e| format!("Invalid column data: {}", e))?; - // Extract column names from the format "\"column_name\" TYPE" - let column_names: Vec<&str> = columns + // Extract column name and type + let column_info: Vec<(&str, &str)> = columns .iter() .filter_map(|c| { - c.split_whitespace() - .next() - .map(|s| s.trim_matches('"')) + let mut parts = c.split_whitespace(); + let name = parts.next()?.trim_matches('"'); + let data_type = parts.next()?; + Some((name, data_type)) }) .collect(); - if !column_names.contains(&target) { - return Err(format!("Target column {} not defined in table {}", target, table_name)); - } + // Find the target column + let column_type = column_info + .iter() + .find(|(name, _)| *name == target) + .map(|(_, dt)| *dt) + .ok_or_else(|| format!("Target column {} not defined in table {}", target, table_name))?; - Ok(()) + Ok(column_type.to_string()) } pub async fn post_table_script( db_pool: &PgPool, request: PostTableScriptRequest, ) -> Result { - // Basic validation let table_def = sqlx::query!( r#"SELECT id, table_name, columns, profile_id FROM table_definitions WHERE id = $1"#, @@ -54,36 +53,25 @@ pub async fn post_table_script( .map_err(|e| Status::internal(format!("Database error: {}", e)))? .ok_or_else(|| Status::not_found("Table definition not found"))?; - // Use the full path to parse_script - let operation = execution::parse_script(&request.script, &request.target_column) - .map_err(|e| Status::invalid_argument(e.to_string()))?; + // Validate target column and get its type + let column_type = validate_target_column( + &table_def.table_name, + &request.target_column, + &table_def.columns, + ) + .map_err(|e| Status::invalid_argument(e))?; - // Ensure the operation is valid (additional checks if needed) - match operation { - ScriptOperation::SetToLocalColumn { .. } => {}, - ScriptOperation::SetToExternalColumn { .. } => {}, - } - - // Call validation functions - validate_script(&request.script) - .map_err(|e| Status::invalid_argument(e.to_string()))?; - - validate_target_column(&table_def.table_name, &request.target_column, &table_def.columns) - .map_err(|e| Status::invalid_argument(e))?; - - // Handle optional description - let description = request.description; - - // Store script in database + // Store script in database with column type let script_record = sqlx::query!( r#"INSERT INTO table_scripts - (table_definitions_id, target_column, script, description) - VALUES ($1, $2, $3, $4) + (table_definitions_id, target_column, target_column_type, script, description) + VALUES ($1, $2, $3, $4, $5) RETURNING id"#, request.table_definition_id, request.target_column, + column_type, request.script, - description + request.description ) .fetch_one(db_pool) .await