moved code in steel, time to implement working steel next
This commit is contained in:
@@ -1,4 +0,0 @@
|
|||||||
// src/steel/handlers.rs
|
|
||||||
pub mod execution;
|
|
||||||
|
|
||||||
pub use execution::*;
|
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
// src/steel/mod.rs
|
// src/steel/mod.rs
|
||||||
|
|
||||||
pub mod handlers;
|
pub mod server;
|
||||||
pub mod validation;
|
|
||||||
|
|||||||
92
server/src/steel/server/data_access.rs
Normal file
92
server/src/steel/server/data_access.rs
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
// src/steel/server/data_access.rs
|
||||||
|
use crate::steel::server::{
|
||||||
|
rvals::{Custom, SteelVal},
|
||||||
|
SteelError,
|
||||||
|
};
|
||||||
|
use sqlx::{PgPool, Row};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
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)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// src/steel/handlers/execution.rs
|
// src/steel/server/execution.rs
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use sqlx::{PgPool, Row};
|
use sqlx::{PgPool, Row};
|
||||||
@@ -99,16 +99,21 @@ pub async fn resolve_value(
|
|||||||
let external_table = table.strip_prefix('@')
|
let external_table = table.strip_prefix('@')
|
||||||
.ok_or_else(|| ScriptExecutionError::InvalidReference(format!("Invalid external reference: {}", source)))?;
|
.ok_or_else(|| ScriptExecutionError::InvalidReference(format!("Invalid external reference: {}", source)))?;
|
||||||
|
|
||||||
// Get linking key (assuming firma is the common key)
|
// Get foreign key relationship info
|
||||||
let firma = current_data.get("firma")
|
let (fk_column, referenced_column) = get_relationship_info(db_pool, current_table, external_table).await?;
|
||||||
.ok_or_else(|| ScriptExecutionError::MissingLinkKey("firma".into()))?;
|
|
||||||
|
|
||||||
// Query external table
|
// Get foreign key value from current data
|
||||||
let query = format!("SELECT {} FROM \"{}\" WHERE firma = $1 AND profile_id = $2", column, external_table);
|
let fk_value = current_data.get(&fk_column)
|
||||||
|
.ok_or_else(|| ScriptExecutionError::MissingLinkKey(fk_column.clone()))?;
|
||||||
|
|
||||||
|
// Build and execute query
|
||||||
|
let query = format!(
|
||||||
|
"SELECT {} FROM \"{}\" WHERE {} = $1",
|
||||||
|
column, external_table, referenced_column
|
||||||
|
);
|
||||||
|
|
||||||
let result: Option<String> = sqlx::query(&query)
|
let result: Option<String> = sqlx::query(&query)
|
||||||
.bind(firma)
|
.bind(fk_value)
|
||||||
.bind(profile_id)
|
|
||||||
.fetch_optional(db_pool)
|
.fetch_optional(db_pool)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| ScriptExecutionError::DatabaseError(e.to_string()))?
|
.map_err(|e| ScriptExecutionError::DatabaseError(e.to_string()))?
|
||||||
@@ -118,9 +123,58 @@ pub async fn resolve_value(
|
|||||||
format!("No data found for {} in {}", column, external_table)
|
format!("No data found for {} in {}", column, external_table)
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
// Local column reference
|
// Local column reference remains the same
|
||||||
current_data.get(source)
|
current_data.get(source)
|
||||||
.cloned()
|
.cloned()
|
||||||
.ok_or_else(|| ScriptExecutionError::MissingSourceColumn(source.into()))
|
.ok_or_else(|| ScriptExecutionError::MissingSourceColumn(source.into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn get_relationship_info(
|
||||||
|
db_pool: &PgPool,
|
||||||
|
current_table: &str,
|
||||||
|
external_table: &str,
|
||||||
|
) -> Result<(String, String), ScriptExecutionError> {
|
||||||
|
let query = r#"
|
||||||
|
SELECT
|
||||||
|
kcu.column_name AS fk_column,
|
||||||
|
ccu.column_name AS referenced_column
|
||||||
|
FROM
|
||||||
|
information_schema.table_constraints AS tc
|
||||||
|
JOIN information_schema.key_column_usage AS kcu
|
||||||
|
ON tc.constraint_name = kcu.constraint_name
|
||||||
|
AND tc.table_schema = kcu.table_schema
|
||||||
|
JOIN information_schema.constraint_column_usage AS ccu
|
||||||
|
ON tc.constraint_name = ccu.constraint_name
|
||||||
|
AND tc.table_schema = ccu.table_schema
|
||||||
|
WHERE
|
||||||
|
tc.constraint_type = 'FOREIGN KEY'
|
||||||
|
AND tc.table_name = $1
|
||||||
|
AND ccu.table_name = $2
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let rows = sqlx::query(query)
|
||||||
|
.bind(current_table)
|
||||||
|
.bind(external_table)
|
||||||
|
.fetch_all(db_pool)
|
||||||
|
.await
|
||||||
|
.map_err(|e| ScriptExecutionError::DatabaseError(e.to_string()))?;
|
||||||
|
|
||||||
|
match rows.len() {
|
||||||
|
0 => Err(ScriptExecutionError::InvalidReference(format!(
|
||||||
|
"No foreign key relationship found from {} to {}",
|
||||||
|
current_table, external_table
|
||||||
|
))),
|
||||||
|
1 => {
|
||||||
|
let row = &rows[0];
|
||||||
|
Ok((
|
||||||
|
row.get::<String, _>("fk_column"),
|
||||||
|
row.get::<String, _>("referenced_column"),
|
||||||
|
))
|
||||||
|
},
|
||||||
|
_ => Err(ScriptExecutionError::InvalidReference(format!(
|
||||||
|
"Multiple foreign keys between {} and {} - cannot resolve ambiguity",
|
||||||
|
current_table, external_table
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
10
server/src/steel/server/mod.rs
Normal file
10
server/src/steel/server/mod.rs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
// src/steel/server/mod.rs
|
||||||
|
pub mod rvals;
|
||||||
|
pub mod data_access;
|
||||||
|
pub mod execution;
|
||||||
|
pub mod script;
|
||||||
|
|
||||||
|
pub use data_access::*;
|
||||||
|
pub use execution::*;
|
||||||
|
pub use script::*;
|
||||||
|
pub use rvals::*;
|
||||||
25
server/src/steel/server/rvals.rs
Normal file
25
server/src/steel/server/rvals.rs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
// 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
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// src/steel/validation/script.rs
|
// src/steel/server/script.rs
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
// src/steel/validation.rs
|
|
||||||
pub mod script;
|
|
||||||
@@ -2,11 +2,11 @@
|
|||||||
use tonic::Status;
|
use tonic::Status;
|
||||||
use sqlx::{PgPool, Error as SqlxError};
|
use sqlx::{PgPool, Error as SqlxError};
|
||||||
use common::proto::multieko2::table_script::{PostTableScriptRequest, TableScriptResponse};
|
use common::proto::multieko2::table_script::{PostTableScriptRequest, TableScriptResponse};
|
||||||
use crate::steel::validation::script::validate_script;
|
use crate::steel::server::script::validate_script;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
// Add these imports for the execution module and ScriptOperation
|
// Add these imports for the execution module and ScriptOperation
|
||||||
use crate::steel::handlers::execution::{self, ScriptOperation};
|
use crate::steel::server::execution::{self, ScriptOperation};
|
||||||
|
|
||||||
const SYSTEM_COLUMNS: &[&str] = &["id", "deleted", "created_at"];
|
const SYSTEM_COLUMNS: &[&str] = &["id", "deleted", "created_at"];
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use sqlx::{PgPool, Arguments};
|
|||||||
use sqlx::postgres::PgArguments;
|
use sqlx::postgres::PgArguments;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use common::proto::multieko2::tables_data::{PostTableDataRequest, PostTableDataResponse};
|
use common::proto::multieko2::tables_data::{PostTableDataRequest, PostTableDataResponse};
|
||||||
use crate::steel::handlers::execution::{self, ScriptOperation};
|
use crate::steel::server::execution::{self, ScriptOperation};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
pub async fn post_table_data(
|
pub async fn post_table_data(
|
||||||
|
|||||||
Reference in New Issue
Block a user