redesing of a steel usage completely
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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[],
|
||||
|
||||
@@ -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<String, String>,
|
||||
}
|
||||
|
||||
impl Custom for TableAccess {}
|
||||
|
||||
impl TableAccess {
|
||||
pub fn new(
|
||||
db_pool: PgPool,
|
||||
profile_id: i64,
|
||||
current_table: &str,
|
||||
local_data: HashMap<String, String>,
|
||||
) -> 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<SteelVal, SteelError> {
|
||||
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<String> = 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<String>) -> Self {
|
||||
Self(message.into())
|
||||
}
|
||||
}
|
||||
|
||||
fn table_access_module(
|
||||
db_pool: PgPool,
|
||||
profile_id: i64,
|
||||
current_table: String,
|
||||
local_data: HashMap<String, String>,
|
||||
) -> Result<impl Custom, SteelError> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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::*;
|
||||
|
||||
@@ -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<String>) -> 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
|
||||
}
|
||||
@@ -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<String, String> {
|
||||
if SYSTEM_COLUMNS.contains(&target) {
|
||||
return Err(format!("Cannot override system column: {}", target));
|
||||
}
|
||||
@@ -22,28 +18,31 @@ fn validate_target_column(
|
||||
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
|
||||
// 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<TableScriptResponse, Status> {
|
||||
// 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
|
||||
|
||||
Reference in New Issue
Block a user