// src/services/grpc_client.rs use common::proto::komp_ac::common::Empty; use common::proto::komp_ac::table_structure::table_structure_service_client::TableStructureServiceClient; use common::proto::komp_ac::table_structure::{ GetTableStructureRequest, TableStructureResponse, }; use common::proto::komp_ac::table_definition::{ table_definition_client::TableDefinitionClient, PostTableDefinitionRequest, ProfileTreeResponse, TableDefinitionResponse, }; use common::proto::komp_ac::table_script::{ table_script_client::TableScriptClient, PostTableScriptRequest, TableScriptResponse, }; use common::proto::komp_ac::tables_data::{ tables_data_client::TablesDataClient, GetTableDataByPositionRequest, GetTableDataRequest, // ADD THIS GetTableDataResponse, DeleteTableDataRequest, // ADD THIS DeleteTableDataResponse, // ADD THIS GetTableDataCountRequest, PostTableDataRequest, PostTableDataResponse, PutTableDataRequest, PutTableDataResponse, }; use crate::search::SearchGrpc; use common::proto::komp_ac::search::SearchResponse; use common::proto::komp_ac::table_validation::{ table_validation_service_client::TableValidationServiceClient, GetTableValidationRequest, TableValidationResponse, CountMode as PbCountMode, FieldValidation as PbFieldValidation, CharacterLimits as PbCharacterLimits, }; use anyhow::{Context, Result}; use std::collections::HashMap; use tonic::transport::{Channel, Endpoint}; use prost_types::Value; use std::time::Duration; #[derive(Clone)] pub struct GrpcClient { channel: Channel, table_structure_client: TableStructureServiceClient, table_definition_client: TableDefinitionClient, table_script_client: TableScriptClient, tables_data_client: TablesDataClient, search_client: SearchGrpc, table_validation_client: TableValidationServiceClient, } impl GrpcClient { pub async fn new() -> Result { let endpoint = Endpoint::from_static("http://[::1]:50051") .connect_timeout(Duration::from_secs(5)) .tcp_keepalive(Some(Duration::from_secs(30))) .keep_alive_while_idle(true) .http2_keep_alive_interval(Duration::from_secs(15)) .keep_alive_timeout(Duration::from_secs(5)); let channel = endpoint .connect() .await .context("Failed to create gRPC channel")?; let table_structure_client = TableStructureServiceClient::new(channel.clone()); let table_definition_client = TableDefinitionClient::new(channel.clone()); let table_script_client = TableScriptClient::new(channel.clone()); let tables_data_client = TablesDataClient::new(channel.clone()); let search_client = SearchGrpc::new(channel.clone()); let table_validation_client = TableValidationServiceClient::new(channel.clone()); Ok(Self { channel, table_structure_client, table_definition_client, table_script_client, tables_data_client, search_client, table_validation_client, }) } // Expose the shared channel so other typed clients can reuse it. pub fn channel(&self) -> Channel { self.channel.clone() } // Fetch validation rules for a table. Absence of a field in response = no validation. pub async fn get_table_validation( &mut self, profile_name: String, table_name: String, ) -> Result { let req = GetTableValidationRequest { profile_name, table_name, }; let resp = self .table_validation_client .get_table_validation(tonic::Request::new(req)) .await .context("gRPC GetTableValidation call failed")?; Ok(resp.into_inner()) } pub async fn get_table_structure( &mut self, profile_name: String, table_name: String, ) -> Result { let grpc_request = GetTableStructureRequest { profile_name, table_name, }; let request = tonic::Request::new(grpc_request); let response = self .table_structure_client .get_table_structure(request) .await .context("gRPC GetTableStructure call failed")?; Ok(response.into_inner()) } pub async fn get_profile_tree( &mut self, ) -> Result { let request = tonic::Request::new(Empty::default()); let response = self .table_definition_client .get_profile_tree(request) .await .context("gRPC GetProfileTree call failed")?; Ok(response.into_inner()) } pub async fn post_table_definition( &mut self, request: PostTableDefinitionRequest, ) -> Result { let tonic_request = tonic::Request::new(request); let response = self .table_definition_client .post_table_definition(tonic_request) .await .context("gRPC PostTableDefinition call failed")?; Ok(response.into_inner()) } pub async fn post_table_script( &mut self, request: PostTableScriptRequest, ) -> Result { let tonic_request = tonic::Request::new(request); let response = self .table_script_client .post_table_script(tonic_request) .await .context("gRPC PostTableScript call failed")?; Ok(response.into_inner()) } // Existing TablesData methods pub async fn get_table_data_count( &mut self, profile_name: String, table_name: String, ) -> Result { let grpc_request = GetTableDataCountRequest { profile_name, table_name, }; let request = tonic::Request::new(grpc_request); let response = self .tables_data_client .get_table_data_count(request) .await .context("gRPC GetTableDataCount call failed")?; Ok(response.into_inner().count as u64) } pub async fn get_table_data_by_position( &mut self, profile_name: String, table_name: String, position: i32, ) -> Result { let grpc_request = GetTableDataByPositionRequest { profile_name, table_name, position, }; let request = tonic::Request::new(grpc_request); let response = self .tables_data_client .get_table_data_by_position(request) .await .context("gRPC GetTableDataByPosition call failed")?; Ok(response.into_inner()) } // ADD THIS: Missing get_table_data method pub async fn get_table_data( &mut self, profile_name: String, table_name: String, id: i64, ) -> Result { let grpc_request = GetTableDataRequest { profile_name, table_name, id, }; let request = tonic::Request::new(grpc_request); let response = self .tables_data_client .get_table_data(request) .await .context("gRPC GetTableData call failed")?; Ok(response.into_inner()) } // ADD THIS: Missing delete_table_data method pub async fn delete_table_data( &mut self, profile_name: String, table_name: String, record_id: i64, ) -> Result { let grpc_request = DeleteTableDataRequest { profile_name, table_name, record_id, }; let request = tonic::Request::new(grpc_request); let response = self .tables_data_client .delete_table_data(request) .await .context("gRPC DeleteTableData call failed")?; Ok(response.into_inner()) } pub async fn post_table_data( &mut self, profile_name: String, table_name: String, data: HashMap, ) -> Result { let grpc_request = PostTableDataRequest { profile_name, table_name, data, }; let request = tonic::Request::new(grpc_request); let response = self .tables_data_client .post_table_data(request) .await .context("gRPC PostTableData call failed")?; Ok(response.into_inner()) } pub async fn put_table_data( &mut self, profile_name: String, table_name: String, id: i64, data: HashMap, ) -> Result { let grpc_request = PutTableDataRequest { profile_name, table_name, id, data, }; let request = tonic::Request::new(grpc_request); let response = self .tables_data_client .put_table_data(request) .await .context("gRPC PutTableData call failed")?; Ok(response.into_inner()) } pub async fn search_table( &mut self, table_name: String, query: String, ) -> Result { self.search_client.search_table(table_name, query).await } }