// src/table_script/handlers/post_table_script.rs 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> { if SYSTEM_COLUMNS.contains(&target) { return Err(format!("Cannot override system column: {}", target)); } 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 .iter() .filter_map(|c| { c.split_whitespace() .next() .map(|s| s.trim_matches('"')) }) .collect(); if !column_names.contains(&target) { return Err(format!("Target column {} not defined in table {}", target, table_name)); } Ok(()) } 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"#, request.table_definition_id ) .fetch_optional(db_pool) .await .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()))?; // 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 let script_record = sqlx::query!( r#"INSERT INTO table_scripts (table_definitions_id, target_column, script, description) VALUES ($1, $2, $3, $4) RETURNING id"#, request.table_definition_id, request.target_column, request.script, description ) .fetch_one(db_pool) .await .map_err(|e| match e { SqlxError::Database(db_err) if db_err.constraint() == Some("table_scripts_table_definitions_id_target_column_key") => { Status::already_exists("Script already exists for this column") } _ => Status::internal(format!("Database error: {}", e)), })?; Ok(TableScriptResponse { id: script_record.id, warnings: String::new(), }) }