diff --git a/common/proto/adresar.proto b/common/proto/adresar.proto index 91db0af..33529c1 100644 --- a/common/proto/adresar.proto +++ b/common/proto/adresar.proto @@ -3,7 +3,7 @@ syntax = "proto3"; package multieko2.adresar; import "common.proto"; -import "table_structure.proto"; +// import "table_structure.proto"; service Adresar { rpc PostAdresar (PostAdresarRequest) returns (AdresarResponse); diff --git a/common/proto/table_definition.proto b/common/proto/table_definition.proto index d160eaf..a005be1 100644 --- a/common/proto/table_definition.proto +++ b/common/proto/table_definition.proto @@ -6,6 +6,7 @@ import "common.proto"; service TableDefinition { rpc PostTableDefinition (PostTableDefinitionRequest) returns (TableDefinitionResponse); + rpc GetProfileTree (multieko2.common.Empty) returns (ProfileTreeResponse); } message PostTableDefinitionRequest { @@ -25,3 +26,17 @@ message TableDefinitionResponse { bool success = 1; string sql = 2; } + +message ProfileTreeResponse { + message Table { + string name = 1; + repeated string depends_on = 2; + } + + message Profile { + string name = 1; + repeated Table tables = 2; + } + + repeated Profile profiles = 1; +} diff --git a/common/src/proto/descriptor.bin b/common/src/proto/descriptor.bin index a97f565..8ce08e9 100644 Binary files a/common/src/proto/descriptor.bin and b/common/src/proto/descriptor.bin differ diff --git a/common/src/proto/multieko2.table_definition.rs b/common/src/proto/multieko2.table_definition.rs index 567dc83..a63e65f 100644 --- a/common/src/proto/multieko2.table_definition.rs +++ b/common/src/proto/multieko2.table_definition.rs @@ -26,6 +26,28 @@ pub struct TableDefinitionResponse { #[prost(string, tag = "2")] pub sql: ::prost::alloc::string::String, } +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ProfileTreeResponse { + #[prost(message, repeated, tag = "1")] + pub profiles: ::prost::alloc::vec::Vec, +} +/// Nested message and enum types in `ProfileTreeResponse`. +pub mod profile_tree_response { + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Table { + #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, + #[prost(string, repeated, tag = "2")] + pub depends_on: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + } + #[derive(Clone, PartialEq, ::prost::Message)] + pub struct Profile { + #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "2")] + pub tables: ::prost::alloc::vec::Vec, + } +} /// Generated client implementations. pub mod table_definition_client { #![allow( @@ -146,6 +168,35 @@ pub mod table_definition_client { ); self.inner.unary(req, path, codec).await } + pub async fn get_profile_tree( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic::codec::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/multieko2.table_definition.TableDefinition/GetProfileTree", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new( + "multieko2.table_definition.TableDefinition", + "GetProfileTree", + ), + ); + self.inner.unary(req, path, codec).await + } } } /// Generated server implementations. @@ -168,6 +219,13 @@ pub mod table_definition_server { tonic::Response, tonic::Status, >; + async fn get_profile_tree( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; } #[derive(Debug)] pub struct TableDefinitionServer { @@ -294,6 +352,52 @@ pub mod table_definition_server { }; Box::pin(fut) } + "/multieko2.table_definition.TableDefinition/GetProfileTree" => { + #[allow(non_camel_case_types)] + struct GetProfileTreeSvc(pub Arc); + impl< + T: TableDefinition, + > tonic::server::UnaryService + for GetProfileTreeSvc { + type Response = super::ProfileTreeResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::get_profile_tree(&inner, request) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let method = GetProfileTreeSvc(inner); + let codec = tonic::codec::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } _ => { Box::pin(async move { let mut response = http::Response::new(empty_body()); diff --git a/server/src/server/services/table_definition_service.rs b/server/src/server/services/table_definition_service.rs index 950e90d..d3b4227 100644 --- a/server/src/server/services/table_definition_service.rs +++ b/server/src/server/services/table_definition_service.rs @@ -4,8 +4,10 @@ use common::proto::multieko2::table_definition::{ table_definition_server::TableDefinition, PostTableDefinitionRequest, TableDefinitionResponse }; +use common::proto::multieko2::table_definition::ProfileTreeResponse; use sqlx::PgPool; use crate::table_definition::handlers::post_table_definition; +use crate::table_definition::handlers::get_profile_tree; #[derive(Debug)] pub struct TableDefinitionService { @@ -21,4 +23,10 @@ impl TableDefinition for TableDefinitionService { let response = post_table_definition(&self.db_pool, request.into_inner()).await?; Ok(Response::new(response)) } + async fn get_profile_tree( + &self, + request: Request<()>, + ) -> Result, Status> { + get_profile_tree::get_profile_tree(&self.db_pool, request).await + } } diff --git a/server/src/table_definition/handlers.rs b/server/src/table_definition/handlers.rs index 08b1087..2803921 100644 --- a/server/src/table_definition/handlers.rs +++ b/server/src/table_definition/handlers.rs @@ -1,8 +1,6 @@ // src/table_definition/handlers.rs pub mod post_table_definition; -// pub mod get_table_definition; -// pub mod list_table_definitions; +pub mod get_profile_tree; pub use post_table_definition::post_table_definition; -// pub use get_table_definition::get_table_definition; -// pub use list_table_definitions::list_table_definitions; +pub use get_profile_tree::get_profile_tree; diff --git a/server/src/table_definition/handlers/get_profile_tree.rs b/server/src/table_definition/handlers/get_profile_tree.rs new file mode 100644 index 0000000..1c875f4 --- /dev/null +++ b/server/src/table_definition/handlers/get_profile_tree.rs @@ -0,0 +1,61 @@ +// server/src/table_definition/handlers/get_profile_tree.rs +use tonic::{Request, Response, Status}; +use sqlx::PgPool; +use common::proto::multieko2::table_definition::ProfileTreeResponse; + +pub async fn get_profile_tree( + db_pool: &PgPool, + _request: Request<()>, +) -> Result, Status> { + let mut profiles = Vec::new(); + + // Get all profiles + let profile_records = sqlx::query!("SELECT id, name FROM profiles") + .fetch_all(db_pool) + .await + .map_err(|e| Status::internal(format!("Failed to fetch profiles: {}", e)))?; + + for profile in profile_records { + // Get all tables with their dependencies + let tables = sqlx::query!( + r#" + SELECT + td.table_name, + ltd.table_name as "linked_table_name?" + FROM table_definitions td + LEFT JOIN table_definitions ltd ON td.linked_table_id = ltd.id + WHERE td.profile_id = $1 + "#, + profile.id + ) + .fetch_all(db_pool) + .await + .map_err(|e| Status::internal(format!("Failed to fetch tables: {}", e)))?; + + // Group dependencies per table + let mut table_map = std::collections::HashMap::new(); + for table in tables { + let entry = table_map.entry(table.table_name) + .or_insert(Vec::new()); + + if let Some(linked) = table.linked_table_name { + entry.push(linked); + } + } + + // Convert to protobuf format + let proto_tables = table_map.into_iter() + .map(|(name, depends_on)| ProfileTreeResponse::Table { + name, + depends_on + }) + .collect(); + + profiles.push(ProfileTreeResponse::Profile { + name: profile.name, + tables: proto_tables + }); + } + + Ok(Response::new(ProfileTreeResponse { profiles })) +}