// src/tables_data/handlers/get_table_data.rs use tonic::Status; use sqlx::{PgPool, Row}; use std::collections::HashMap; use common::proto::multieko2::tables_data::{GetTableDataRequest, GetTableDataResponse}; pub async fn get_table_data( db_pool: &PgPool, request: GetTableDataRequest, ) -> Result { let profile_name = request.profile_name; let table_name = request.table_name; let record_id = request.id; // Lookup profile let profile = sqlx::query!( "SELECT id FROM profiles WHERE name = $1", profile_name ) .fetch_optional(db_pool) .await .map_err(|e| Status::internal(format!("Profile lookup error: {}", e)))?; let profile_id = profile.ok_or_else(|| Status::not_found("Profile not found"))?.id; // Lookup table_definition let table_def = sqlx::query!( r#"SELECT id, columns FROM table_definitions WHERE profile_id = $1 AND table_name = $2"#, profile_id, table_name ) .fetch_optional(db_pool) .await .map_err(|e| Status::internal(format!("Table lookup error: {}", e)))?; let table_def = table_def.ok_or_else(|| Status::not_found("Table not found"))?; // Parse user-defined columns from JSON let columns_json: Vec = serde_json::from_value(table_def.columns.clone()) .map_err(|e| Status::internal(format!("Column parsing error: {}", e)))?; let mut user_columns = Vec::new(); for col_def in columns_json { let parts: Vec<&str> = col_def.splitn(2, ' ').collect(); if parts.len() != 2 { return Err(Status::internal("Invalid column format")); } let name = parts[0].trim_matches('"').to_string(); let sql_type = parts[1].to_string(); user_columns.push((name, sql_type)); } // Prepare all columns (system + user-defined) let system_columns = vec![ ("id".to_string(), "BIGINT".to_string()), ("deleted".to_string(), "BOOLEAN".to_string()), ("firma".to_string(), "TEXT".to_string()), ]; let all_columns: Vec<(String, String)> = system_columns .into_iter() .chain(user_columns.into_iter()) .collect(); // Build SELECT clause with COALESCE and type casting let columns_clause = all_columns .iter() .map(|(name, _)| format!("COALESCE(\"{0}\"::TEXT, '') AS \"{0}\"", name)) .collect::>() .join(", "); let sql = format!( "SELECT {} FROM \"{}\" WHERE id = $1 AND deleted = false", columns_clause, table_name ); // Execute query let row = sqlx::query(&sql) .bind(record_id) .fetch_one(db_pool) .await .map_err(|e| match e { sqlx::Error::RowNotFound => Status::not_found("Record not found"), _ => Status::internal(format!("Database error: {}", e)), })?; // Build response data let mut data = HashMap::new(); for (column_name, _) in &all_columns { let value: String = row .try_get(column_name.as_str()) .map_err(|e| Status::internal(format!("Failed to get column {}: {}", column_name, e)))?; data.insert(column_name.clone(), value); } Ok(GetTableDataResponse { data }) }