working creation of profiles and tables for each profile
This commit is contained in:
12
Cargo.lock
generated
12
Cargo.lock
generated
@@ -1381,6 +1381,15 @@ dependencies = [
|
|||||||
"libm",
|
"libm",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num_threads"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "object"
|
name = "object"
|
||||||
version = "0.36.7"
|
version = "0.36.7"
|
||||||
@@ -1975,6 +1984,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
|
"time",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tonic",
|
"tonic",
|
||||||
"tonic-reflection",
|
"tonic-reflection",
|
||||||
@@ -2419,7 +2429,9 @@ checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"deranged",
|
"deranged",
|
||||||
"itoa",
|
"itoa",
|
||||||
|
"libc",
|
||||||
"num-conv",
|
"num-conv",
|
||||||
|
"num_threads",
|
||||||
"powerfmt",
|
"powerfmt",
|
||||||
"serde",
|
"serde",
|
||||||
"time-core",
|
"time-core",
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ tokio = { version = "1.43.0", features = ["full", "macros"] }
|
|||||||
tonic = "0.12.3"
|
tonic = "0.12.3"
|
||||||
tonic-reflection = "0.12.3"
|
tonic-reflection = "0.12.3"
|
||||||
tracing = "0.1.41"
|
tracing = "0.1.41"
|
||||||
|
time = { version = "0.3.37", features = ["local-offset"] }
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "server"
|
name = "server"
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
use tonic::Status;
|
use tonic::Status;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
use time::OffsetDateTime;
|
||||||
use common::proto::multieko2::table_definition::{PostTableDefinitionRequest, TableDefinitionResponse};
|
use common::proto::multieko2::table_definition::{PostTableDefinitionRequest, TableDefinitionResponse};
|
||||||
|
|
||||||
const VALID_DATA_TYPES: &[&str] = &["TEXT", "INTEGER", "BIGINT", "BOOLEAN", "TIMESTAMPTZ", "NUMERIC"];
|
const VALID_DATA_TYPES: &[&str] = &["TEXT", "INTEGER", "BIGINT", "BOOLEAN", "TIMESTAMPTZ", "NUMERIC"];
|
||||||
@@ -11,16 +12,23 @@ fn is_valid_data_type(dt: &str) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn is_valid_identifier(s: &str) -> bool {
|
fn is_valid_identifier(s: &str) -> bool {
|
||||||
!s.is_empty() &&
|
let parts: Vec<&str> = s.split('_').collect();
|
||||||
s.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') &&
|
|
||||||
!s.starts_with('_') &&
|
parts.len() >= 3 &&
|
||||||
!s.chars().next().unwrap().is_ascii_digit()
|
parts[0] == "ud" &&
|
||||||
|
parts[1].len() == 4 &&
|
||||||
|
parts[1].chars().all(|c| c.is_ascii_digit()) &&
|
||||||
|
parts[2..].iter().all(|p| !p.is_empty()) &&
|
||||||
|
s.chars().all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_')
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sanitize_identifier(s: &str) -> String {
|
fn sanitize_identifier(s: &str) -> String {
|
||||||
s.replace(|c: char| !c.is_ascii_alphanumeric() && c != '_', "")
|
let year = OffsetDateTime::now_utc().year();
|
||||||
|
let cleaned = s.replace(|c: char| !c.is_ascii_alphanumeric() && c != '_', "")
|
||||||
.trim()
|
.trim()
|
||||||
.to_lowercase()
|
.to_lowercase();
|
||||||
|
|
||||||
|
format!("ud_{}_{}", year, cleaned)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn post_table_definition(
|
pub async fn post_table_definition(
|
||||||
@@ -49,10 +57,10 @@ pub async fn post_table_definition(
|
|||||||
let linked_table_name = request.linked_table_name
|
let linked_table_name = request.linked_table_name
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|lt| sanitize_identifier(lt));
|
.map(|lt| sanitize_identifier(lt));
|
||||||
|
|
||||||
if let Some(lt_name) = &linked_table_name {
|
if let Some(lt_name) = &linked_table_name {
|
||||||
let lt_record = sqlx::query!(
|
let lt_record = sqlx::query!(
|
||||||
"SELECT id FROM table_definitions
|
"SELECT id FROM table_definitions
|
||||||
WHERE profile_id = $1 AND table_name = $2",
|
WHERE profile_id = $1 AND table_name = $2",
|
||||||
profile.id,
|
profile.id,
|
||||||
lt_name
|
lt_name
|
||||||
@@ -153,8 +161,15 @@ fn generate_table_sql(
|
|||||||
];
|
];
|
||||||
|
|
||||||
if let Some(linked) = linked_table {
|
if let Some(linked) = linked_table {
|
||||||
|
// Extract base name without prefix for relationship
|
||||||
|
let parts: Vec<&str> = linked.splitn(3, '_').collect();
|
||||||
|
let base_name = parts.get(2).unwrap_or(&linked);
|
||||||
|
|
||||||
system_columns.push(
|
system_columns.push(
|
||||||
format!("\"{}_id\" BIGINT NOT NULL REFERENCES \"{}\"(id)", linked, linked)
|
format!("\"{}_id\" BIGINT NOT NULL REFERENCES \"{}\"(id)",
|
||||||
|
base_name,
|
||||||
|
linked
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,7 +199,7 @@ fn generate_table_sql(
|
|||||||
let all_indexes = system_indexes
|
let all_indexes = system_indexes
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.chain(indexes.iter().map(|idx| {
|
.chain(indexes.iter().map(|idx| {
|
||||||
format!("CREATE INDEX idx_{}_{} ON \"{}\" (\"{}\")",
|
format!("CREATE INDEX idx_{}_{} ON \"{}\" (\"{}\")",
|
||||||
table_name, idx, table_name, idx)
|
table_name, idx, table_name, idx)
|
||||||
}))
|
}))
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|||||||
Reference in New Issue
Block a user