Files
komp_ac/server/src/table_script/handlers/post_table_script.rs

102 lines
3.3 KiB
Rust

// 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<String> = 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<TableScriptResponse, Status> {
// 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(),
})
}