From 5256279b652231570bd2dc1628ebfb47060fb54b Mon Sep 17 00:00:00 2001 From: filipriec Date: Tue, 4 Mar 2025 18:47:17 +0100 Subject: [PATCH] general endpoint delete --- common/proto/tables_data.proto | 11 ++ common/src/proto/descriptor.bin | Bin 15555 -> 16166 bytes common/src/proto/multieko2.tables_data.rs | 95 ++++++++++++++++++ .../server/services/tables_data_service.rs | 14 ++- server/src/tables_data/handlers.rs | 2 + .../tables_data/handlers/delete_table_data.rs | 57 +++++++++++ .../handlers/put_table_data_test.rs | 60 ----------- 7 files changed, 177 insertions(+), 62 deletions(-) create mode 100644 server/src/tables_data/handlers/delete_table_data.rs diff --git a/common/proto/tables_data.proto b/common/proto/tables_data.proto index a13557a..14511e3 100644 --- a/common/proto/tables_data.proto +++ b/common/proto/tables_data.proto @@ -7,6 +7,7 @@ import "common.proto"; service TablesData { rpc PostTableData (PostTableDataRequest) returns (PostTableDataResponse); rpc PutTableData (PutTableDataRequest) returns (PutTableDataResponse); + rpc DeleteTableData (DeleteTableDataRequest) returns (DeleteTableDataResponse); } message PostTableDataRequest { @@ -33,3 +34,13 @@ message PutTableDataResponse { string message = 2; int64 updated_id = 3; } + +message DeleteTableDataRequest { + string profile_name = 1; + string table_name = 2; + int64 record_id = 3; +} + +message DeleteTableDataResponse { + bool success = 1; +} diff --git a/common/src/proto/descriptor.bin b/common/src/proto/descriptor.bin index 8a27db29e0318c39ccd469204828f3d529bd367a..d47b5b087e1601e03b052ca43a881f53c91e8f6b 100644 GIT binary patch delta 1330 zcmZXSy>1gx5QTT=UOVecY{%DL|0n)Iv5jm~kOV?Ql?H@_2=pi+TOdW)*hUBm*oW{7 zP$AJFQB%>-Q>WlL5E5tZ&bolk-_D%5XXg0(+fN_9{63`5m9KveugLV@nCeM%8LguA zy`v|CmdP!m=d;Ddvlkb2)%8c}G)O+&zvv&BnzXiWkJYW8w7$CYE3|V{Og=w7JHL!h z&Q@o3y8d`ntN-)$GkE~5wEjLBNAtWd3q3=WIbkrHK@(g%2#Vl}lwkQI0L zoO|DD>s8~#K2Nv7uAAqx{W2v_0brv{SvMVo#`0DnCCn?AtjJKR$iqId`-->+aV@ta zG9R)^xx%to`+WRRwgx~K(w20x{V+(au>BBQ^YO{*y0@FS=dOCtayE$FH{|#LjBAJu z5E>hDd=SRP4LQC`y(u;T&^0$Yw%?TFqXu15j!*2qMMdWq09i}igCJ`KCapo%$``~X zppDF8{*MqTgw~eZfuL(kXdrHt(3I63?|QN|cXi}1r+DkGWCnnZu4D#6V^=Z*VO&=- zQxv1tlZTbtx+hzMxF(^2pz9@}X~kRjspd8V;JLnR4T7v6*t7;&KdidUwAK6Gjl{i1 z*M6`UyK`u^KakJ>m^Tm`Aao8SG!W(uBs0xV4J9)GWW$Y&-47)*)F2y5X1KRiBdW5) z_ZkG&?Hvj+t delta 869 zcmX|5Jh)qR}!_7sAx$^BxQ@DN{Ai6NeMx!NCPsATml4P0XeFNoDH~y0O`U9 z-&}(Y1O7vO>i978XaCIKzrX+eynnrx|MQ2p>ql`PpX~9^%~SaLG77`!t=S)+KYz31 z=qF}wLkP|r8{H(i2$SHw5fj~1sxS=$g@IDi6B?)EWls)Nl$4<~lY%7+ z+3YN{S{rA5t%Yv3T2m*{rBbsdL#6%!fi7M3Rs^#2atp`eZS)JXJh|8Di{!E!XVra_ z(^CN0h%)SkgU}d9X=rsdAm+tIqz<)=4_i_D8SR6#HvYgV7P2hMDTDi*TU!yxav8Bv z-RDVfMcwCE8tz*!(XY+L$-cI&lT9~I)PBL^12C_k4G, + ) -> 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.tables_data.TablesData/DeleteTableData", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new( + "multieko2.tables_data.TablesData", + "DeleteTableData", + ), + ); + self.inner.unary(req, path, codec).await + } } } /// Generated server implementations. @@ -215,6 +258,13 @@ pub mod tables_data_server { tonic::Response, tonic::Status, >; + async fn delete_table_data( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; } #[derive(Debug)] pub struct TablesDataServer { @@ -382,6 +432,51 @@ pub mod tables_data_server { }; Box::pin(fut) } + "/multieko2.tables_data.TablesData/DeleteTableData" => { + #[allow(non_camel_case_types)] + struct DeleteTableDataSvc(pub Arc); + impl< + T: TablesData, + > tonic::server::UnaryService + for DeleteTableDataSvc { + type Response = super::DeleteTableDataResponse; + 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 { + ::delete_table_data(&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 = DeleteTableDataSvc(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/tables_data_service.rs b/server/src/server/services/tables_data_service.rs index d119e96..7cb0e34 100644 --- a/server/src/server/services/tables_data_service.rs +++ b/server/src/server/services/tables_data_service.rs @@ -3,9 +3,10 @@ use tonic::{Request, Response, Status}; use common::proto::multieko2::tables_data::tables_data_server::TablesData; use common::proto::multieko2::tables_data::{ PostTableDataRequest, PostTableDataResponse, - PutTableDataRequest, PutTableDataResponse // Add this import + PutTableDataRequest, PutTableDataResponse, + DeleteTableDataRequest, DeleteTableDataResponse, }; -use crate::tables_data::handlers::{post_table_data, put_table_data}; // Add put_table_data +use crate::tables_data::handlers::{post_table_data, put_table_data, delete_table_data,}; use sqlx::PgPool; #[derive(Debug)] @@ -33,4 +34,13 @@ impl TablesData for TablesDataService { let response = put_table_data(&self.db_pool, request).await?; Ok(Response::new(response)) } + + async fn delete_table_data( + &self, + request: Request, + ) -> Result, Status> { + let request = request.into_inner(); + let response = delete_table_data(&self.db_pool, request).await?; + Ok(Response::new(response)) + } } diff --git a/server/src/tables_data/handlers.rs b/server/src/tables_data/handlers.rs index 7da9364..eea735a 100644 --- a/server/src/tables_data/handlers.rs +++ b/server/src/tables_data/handlers.rs @@ -1,6 +1,8 @@ // server/src/tables_data/handlers.rs pub mod post_table_data; pub mod put_table_data; +pub mod delete_table_data; pub use post_table_data::post_table_data; pub use put_table_data::put_table_data; +pub use delete_table_data::delete_table_data; diff --git a/server/src/tables_data/handlers/delete_table_data.rs b/server/src/tables_data/handlers/delete_table_data.rs new file mode 100644 index 0000000..7d94e0c --- /dev/null +++ b/server/src/tables_data/handlers/delete_table_data.rs @@ -0,0 +1,57 @@ +// src/tables_data/handlers/delete_table_data.rs +use tonic::Status; +use sqlx::PgPool; +use common::proto::multieko2::tables_data::{DeleteTableDataRequest, DeleteTableDataResponse}; + +pub async fn delete_table_data( + db_pool: &PgPool, + request: DeleteTableDataRequest, +) -> Result { + // Lookup profile + let profile = sqlx::query!( + "SELECT id FROM profiles WHERE name = $1", + request.profile_name + ) + .fetch_optional(db_pool) + .await + .map_err(|e| Status::internal(format!("Profile lookup error: {}", e)))?; + + let profile_id = match profile { + Some(p) => p.id, + None => return Err(Status::not_found("Profile not found")), + }; + + // Verify table exists in profile + let table_exists = sqlx::query!( + "SELECT 1 AS exists FROM table_definitions + WHERE profile_id = $1 AND table_name = $2", + profile_id, + request.table_name + ) + .fetch_optional(db_pool) + .await + .map_err(|e| Status::internal(format!("Table verification error: {}", e)))?; + + if table_exists.is_none() { + return Err(Status::not_found("Table not found in profile")); + } + + // Perform soft delete + let query = format!( + "UPDATE \"{}\" + SET deleted = true + WHERE id = $1 AND deleted = false", + request.table_name + ); + + let rows_affected = sqlx::query(&query) + .bind(request.record_id) + .execute(db_pool) + .await + .map_err(|e| Status::internal(format!("Delete operation failed: {}", e)))? + .rows_affected(); + + Ok(DeleteTableDataResponse { + success: rows_affected > 0, + }) +} diff --git a/server/tests/tables_data/handlers/put_table_data_test.rs b/server/tests/tables_data/handlers/put_table_data_test.rs index 9d93a19..a918ecc 100644 --- a/server/tests/tables_data/handlers/put_table_data_test.rs +++ b/server/tests/tables_data/handlers/put_table_data_test.rs @@ -252,63 +252,3 @@ async fn test_update_table_data_clear_optional_fields( assert!(db_record.telefon.is_none()); assert!(db_record.ulica.is_none()); } - -#[rstest] -#[tokio::test] -async fn test_update_table_data_max_length_fields( - #[future] existing_record: (PgPool, i64), - valid_request_template: HashMap, -) { - let (pool, id) = existing_record.await; - - let mut data = valid_request_template; - data.insert("firma".into(), "a".repeat(255)); - data.insert("telefon".into(), "1".repeat(20)); - - let request = PutTableDataRequest { - profile_name: "default".into(), - table_name: "2025_adresar".into(), - id, - data, - }; - - let _response = put_table_data(&pool, request).await.unwrap(); - - let db_record = sqlx::query!(r#"SELECT firma, telefon FROM "2025_adresar" WHERE id = $1"#, id) - .fetch_one(&pool) - .await - .unwrap(); - - assert_eq!(db_record.firma.len(), 255); - assert_eq!(db_record.telefon.unwrap().len(), 20); -} - -#[rstest] -#[tokio::test] -async fn test_update_table_data_special_characters( - #[future] existing_record: (PgPool, i64), - valid_request_template: HashMap, -) { - let (pool, id) = existing_record.await; - - let mut data = valid_request_template; - data.insert("ulica".into(), "Náměstí 28. října".into()); - data.insert("telefon".into(), "+420 123-456.789".into()); - - let request = PutTableDataRequest { - profile_name: "default".into(), - table_name: "2025_adresar".into(), - id, - data, - }; - - let _response = put_table_data(&pool, request).await.unwrap(); - - let db_record = sqlx::query!(r#"SELECT ulica, telefon FROM "2025_adresar" WHERE id = $1"#, id) - .fetch_one(&pool) - .await - .unwrap(); - - assert_eq!(db_record.ulica.unwrap(), "Náměstí 28. října"); - assert_eq!(db_record.telefon.unwrap(), "+420 123-456.789"); -}