From 3ecfcfee0235f4a66fb4fe2019f044b6b3692837 Mon Sep 17 00:00:00 2001 From: filipriec Date: Sun, 16 Mar 2025 17:47:02 +0100 Subject: [PATCH] post table data validating now steel --- server/src/steel/server/execution.rs | 5 ++- server/src/steel/server/functions.rs | 17 ++++--- .../tables_data/handlers/post_table_data.rs | 45 ++++++++++++++----- 3 files changed, 47 insertions(+), 20 deletions(-) diff --git a/server/src/steel/server/execution.rs b/server/src/steel/server/execution.rs index abb8701..a957a87 100644 --- a/server/src/steel/server/execution.rs +++ b/server/src/steel/server/execution.rs @@ -28,8 +28,11 @@ pub fn execute_script( script: String, target_type: &str, _db_pool: Arc, // Passed to the SteelContext - context: SteelContext, // Contains row_data, profile_id, etc. + mut context: SteelContext, // Make mutable to inject runtime ) -> Result { + // Inject the current runtime handle + context.runtime = tokio::runtime::Handle::current(); + let mut vm = Engine::new(); let context = Arc::new(context); diff --git a/server/src/steel/server/functions.rs b/server/src/steel/server/functions.rs index 04c3ef2..eb9d6c1 100644 --- a/server/src/steel/server/functions.rs +++ b/server/src/steel/server/functions.rs @@ -5,6 +5,7 @@ use std::collections::HashMap; use std::sync::Arc; use thiserror::Error; use sqlx::Row; +use tokio::runtime::Handle; #[derive(Debug, Error)] pub enum FunctionError { @@ -18,11 +19,13 @@ pub enum FunctionError { DatabaseError(String), } +#[derive(Clone)] pub struct SteelContext { pub current_table: String, pub profile_id: i64, pub row_data: HashMap, pub db_pool: Arc, + pub runtime: Handle, // Add runtime handle } impl SteelContext { @@ -51,20 +54,20 @@ impl SteelContext { let base_name = table.split_once('_') .map(|(_, rest)| rest) .unwrap_or(table); - + let fk_column = format!("{}_id", base_name); let fk_value = self.row_data.get(&fk_column) .ok_or_else(|| SteelVal::StringV(format!("Foreign key {} not found", fk_column).into()))?; - // Convert to async block for database access - let result = tokio::runtime::Runtime::new().unwrap().block_on(async { + // Use the injected runtime handle + let result = self.runtime.block_on(async { let actual_table = self.get_related_table_name(base_name).await .map_err(|e| SteelVal::StringV(e.to_string().into()))?; sqlx::query_scalar::<_, String>( &format!("SELECT {} FROM {} WHERE id = $1", column, actual_table) ) - .bind(fk_value.parse::().map_err(|_| + .bind(fk_value.parse::().map_err(|_| SteelVal::StringV("Invalid foreign key format".into()))?) .fetch_one(&*self.db_pool) .await @@ -100,7 +103,7 @@ impl SteelContext { } let pool = self.db_pool.clone(); - let result = tokio::runtime::Runtime::new().unwrap().block_on(async { + let result = self.runtime.block_on(async { // Execute and get first column of all rows as strings let rows = sqlx::query(query) .fetch_all(&*pool) @@ -113,10 +116,10 @@ impl SteelContext { .map_err(|e| SteelVal::StringV(e.to_string().into()))?; results.push(val); } - + Ok(results.join(",")) }); - + result.map(|s| SteelVal::StringV(s.into())) } } diff --git a/server/src/tables_data/handlers/post_table_data.rs b/server/src/tables_data/handlers/post_table_data.rs index 42cae2e..9665da5 100644 --- a/server/src/tables_data/handlers/post_table_data.rs +++ b/server/src/tables_data/handlers/post_table_data.rs @@ -5,8 +5,11 @@ use sqlx::postgres::PgArguments; use chrono::{DateTime, Utc}; use common::proto::multieko2::tables_data::{PostTableDataRequest, PostTableDataResponse}; use std::collections::HashMap; +use std::sync::Arc; +use tokio::runtime::Handle; use crate::steel::server::execution::{self, Value}; +use crate::steel::server::functions::SteelContext; pub async fn post_table_data( db_pool: &PgPool, @@ -26,7 +29,7 @@ pub async fn post_table_data( } // Add trimmed non-empty values to data map - if !trimmed.is_empty(){ + if !trimmed.is_empty() { data.insert(key, trimmed); } } @@ -72,7 +75,7 @@ pub async fn post_table_data( // Get required relationships let required_links = sqlx::query!( - r#"SELECT ltd.table_name + r#"SELECT ltd.table_name FROM table_definition_links tdl JOIN table_definitions ltd ON tdl.linked_table_id = ltd.id WHERE tdl.source_table_id = $1 AND tdl.is_required = true"#, @@ -105,7 +108,6 @@ pub async fn post_table_data( } } - /* // Validate Steel scripts let scripts = sqlx::query!( "SELECT target_column, script FROM table_scripts WHERE table_definitions_id = $1", @@ -124,22 +126,41 @@ pub async fn post_table_data( format!("Script target column '{}' is required", target_column) ))?; - // Execute the script using the owned String - let script_result = execution::execute_script(script_record.script.clone(), "STRING") // Changed here - .map_err(|e| Status::invalid_argument( - format!("Script execution failed for '{}': {}", target_column, e) - ))?; + // Create execution context + let context = SteelContext { + current_table: table_name.clone(), + profile_id, + row_data: data.clone(), + db_pool: Arc::new(db_pool.clone()), + runtime: Handle::current(), + }; + + // Execute validation script + let script_result = execution::execute_script( + script_record.script, + "STRINGS", + Arc::new(db_pool.clone()), + context, + ) + .map_err(|e| Status::invalid_argument( + format!("Script execution failed for '{}': {}", target_column, e) + ))?; + + // Validate script output + let Value::Strings(mut script_output) = script_result else { + return Err(Status::internal("Script must return string values")); + }; + + let expected_value = script_output.pop() + .ok_or_else(|| Status::internal("Script returned no values"))?; - // Compare script result with user input - let Value::String(expected_value) = script_result; if user_value != &expected_value { return Err(Status::invalid_argument(format!( - "Validation failed for '{}'. Expected: '{}', Received: '{}'", + "Validation failed for column '{}': Expected '{}', Got '{}'", target_column, expected_value, user_value ))); } } - */ // Prepare SQL parameters let mut params = PgArguments::default();