From 6715224d4011734407bb279879ce8aeb7c231bdf Mon Sep 17 00:00:00 2001 From: Nikolay Marchuk Date: Mon, 29 Sep 2025 20:33:02 +0700 Subject: [PATCH 01/11] Undo static typing for find_* api --- src/services/transactor/document.rs | 17 +++++++++++------ src/services/transactor/subscription.rs | 4 +++- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/services/transactor/document.rs b/src/services/transactor/document.rs index 3ab6c64..fbc2ce6 100644 --- a/src/services/transactor/document.rs +++ b/src/services/transactor/document.rs @@ -319,14 +319,16 @@ impl FindOptionsBuilder { pub trait DocumentClient { fn get_account(&self) -> impl Future>; - fn find_all( + fn find_all( &self, + class: &str, query: Q, options: &FindOptions, ) -> impl Future>>; - fn find_one( + fn find_one( &self, + class: &str, query: Q, options: &FindOptions, ) -> impl Future>>; @@ -337,8 +339,9 @@ impl DocumentClient for super::TransactorClient { self.get(Method::Account, []).await } - async fn find_all( + async fn find_all( &self, + class: &str, query: Q, options: &FindOptions, ) -> Result> { @@ -354,7 +357,7 @@ impl DocumentClient for super::TransactorClient { .get( Method::FindAll, [ - (String::from("class"), C::CLASS.into()), + (String::from("class"), class.into()), (String::from("query"), json::to_value(query)?), (String::from("options"), json::to_value(options)?), ], @@ -387,7 +390,7 @@ impl DocumentClient for super::TransactorClient { for entry in result.value.iter_mut() { let object = entry.as_object_mut().unwrap(); if !object.contains_key("_class") { - object.insert("_class".into(), Value::String(C::CLASS.into())); + object.insert("_class".into(), Value::String(class.into())); } for (k, v) in query.iter() { @@ -427,13 +430,15 @@ impl DocumentClient for super::TransactorClient { Ok(result) } - async fn find_one( + async fn find_one( &self, + class: &str, query: Q, options: &FindOptions, ) -> Result> { Ok(self .find_all::( + class, query, &FindOptions { limit: Some(1), diff --git a/src/services/transactor/subscription.rs b/src/services/transactor/subscription.rs index 6840051..adc822d 100644 --- a/src/services/transactor/subscription.rs +++ b/src/services/transactor/subscription.rs @@ -120,7 +120,9 @@ pub(super) fn live_query< ) -> impl Stream>> + Send { let client_clone = client.clone(); let initial_fetch = async move { - let results = client_clone.find_all::(query, &options).await?; + let results = client_clone + .find_all::(C::CLASS, query, &options) + .await?; Ok(LiveQueryEvent::Initial(results.value)) }; From 5afe2ba1f20e99b9d6bd25524349210bd3ef1be6 Mon Sep 17 00:00:00 2001 From: Nikolay Marchuk Date: Mon, 29 Sep 2025 20:51:21 +0700 Subject: [PATCH 02/11] Fix bounds on TxDomainEvent --- src/services/core/tx.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/services/core/tx.rs b/src/services/core/tx.rs index 709aae2..31f08f3 100644 --- a/src/services/core/tx.rs +++ b/src/services/core/tx.rs @@ -1,6 +1,6 @@ use super::classes::OperationDomain; use crate::services::core::classes::Ref; -use crate::services::event::{Class, Event, HasId, QueryClass}; +use crate::services::event::{Class, Event, HasId}; use crate::services::transactor::tx::Doc; use derive_builder::Builder; use serde::{Deserialize, Deserializer, Serialize}; @@ -107,12 +107,9 @@ impl Class for TxDomainEvent { const CLASS: &'static str = crate::services::core::class::TxDomainEvent; } -impl Event for TxDomainEvent { +impl Event for TxDomainEvent { fn matches(value: &Value) -> bool { - if value.get("_class").and_then(|v| v.as_str()) != Some(Self::CLASS) { - return false; - } - value.get("objectClass").and_then(|v| v.as_str()) == Some(T::CLASS) + value.get("_class").and_then(|v| v.as_str()) == Some(Self::CLASS) } } From 260ebd807af0aa5a0c3ff1b3e86822c8a824f1ef Mon Sep 17 00:00:00 2001 From: Nikolay Marchuk Date: Mon, 29 Sep 2025 20:57:14 +0700 Subject: [PATCH 03/11] Fix event matching for TxWorkspaceEvent --- src/services/core/tx.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/services/core/tx.rs b/src/services/core/tx.rs index 31f08f3..72a6476 100644 --- a/src/services/core/tx.rs +++ b/src/services/core/tx.rs @@ -87,10 +87,7 @@ impl HasId for TxWorkspaceEvent { impl Event for TxWorkspaceEvent { fn matches(value: &Value) -> bool { - if value.get("_class").and_then(|v| v.as_str()) != Some(Self::CLASS) { - return false; - } - value.get("objectClass").and_then(|v| v.as_str()) == Some(T::CLASS) + value.get("_class").and_then(|v| v.as_str()) == Some(Self::CLASS) } } From 3a69a8ba08be80fe19383329c9049667a1840b30 Mon Sep 17 00:00:00 2001 From: Nikolay Marchuk Date: Mon, 29 Sep 2025 21:00:33 +0700 Subject: [PATCH 04/11] Use serde_json::Value for TxWorkspaceEvent and TxDomainEvent There are no other meaningful types to deserialize into because inner type depends on other event params --- src/services/core/tx.rs | 18 +++++++++--------- src/services/transactor/comm/mod.rs | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/services/core/tx.rs b/src/services/core/tx.rs index 72a6476..eface7a 100644 --- a/src/services/core/tx.rs +++ b/src/services/core/tx.rs @@ -68,24 +68,24 @@ impl<'de> Deserialize<'de> for WorkspaceEvent { #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] -pub struct TxWorkspaceEvent { +pub struct TxWorkspaceEvent { #[serde(flatten)] pub tx: Tx, pub domain: WorkspaceEvent, - pub event: T, + pub event: serde_json::Value, } -impl Class for TxWorkspaceEvent { +impl Class for TxWorkspaceEvent { const CLASS: &'static str = crate::services::core::class::TxWorkspaceEvent; } -impl HasId for TxWorkspaceEvent { +impl HasId for TxWorkspaceEvent { fn id(&self) -> &str { &self.tx.doc.id } } -impl Event for TxWorkspaceEvent { +impl Event for TxWorkspaceEvent { fn matches(value: &Value) -> bool { value.get("_class").and_then(|v| v.as_str()) == Some(Self::CLASS) } @@ -93,18 +93,18 @@ impl Event for TxWorkspaceEvent { #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] -pub struct TxDomainEvent { +pub struct TxDomainEvent { #[serde(flatten)] pub tx: Tx, pub domain: OperationDomain, - pub event: T, + pub event: serde_json::Value, } -impl Class for TxDomainEvent { +impl Class for TxDomainEvent { const CLASS: &'static str = crate::services::core::class::TxDomainEvent; } -impl Event for TxDomainEvent { +impl Event for TxDomainEvent { fn matches(value: &Value) -> bool { value.get("_class").and_then(|v| v.as_str()) == Some(Self::CLASS) } diff --git a/src/services/transactor/comm/mod.rs b/src/services/transactor/comm/mod.rs index 7807b4e..792cb19 100644 --- a/src/services/transactor/comm/mod.rs +++ b/src/services/transactor/comm/mod.rs @@ -96,7 +96,7 @@ impl super::Transaction for Envelope { }, domain: "communication".to_string(), - event: self, + event: json::to_value(&self)?, }; Ok(json::to_value(&event)?) } From 6d25ccd39aebcec485b4c8bda9b5edd749c57603 Mon Sep 17 00:00:00 2001 From: Nikolay Marchuk Date: Mon, 29 Sep 2025 21:40:41 +0700 Subject: [PATCH 05/11] Return old order of types in find_* methods --- src/services/transactor/document.rs | 10 +++++----- src/services/transactor/subscription.rs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/services/transactor/document.rs b/src/services/transactor/document.rs index fbc2ce6..6738ea0 100644 --- a/src/services/transactor/document.rs +++ b/src/services/transactor/document.rs @@ -319,14 +319,14 @@ impl FindOptionsBuilder { pub trait DocumentClient { fn get_account(&self) -> impl Future>; - fn find_all( + fn find_all( &self, class: &str, query: Q, options: &FindOptions, ) -> impl Future>>; - fn find_one( + fn find_one( &self, class: &str, query: Q, @@ -339,7 +339,7 @@ impl DocumentClient for super::TransactorClient { self.get(Method::Account, []).await } - async fn find_all( + async fn find_all( &self, class: &str, query: Q, @@ -430,14 +430,14 @@ impl DocumentClient for super::TransactorClient { Ok(result) } - async fn find_one( + async fn find_one( &self, class: &str, query: Q, options: &FindOptions, ) -> Result> { Ok(self - .find_all::( + .find_all::<_, C>( class, query, &FindOptions { diff --git a/src/services/transactor/subscription.rs b/src/services/transactor/subscription.rs index adc822d..0ffcfc0 100644 --- a/src/services/transactor/subscription.rs +++ b/src/services/transactor/subscription.rs @@ -121,7 +121,7 @@ pub(super) fn live_query< let client_clone = client.clone(); let initial_fetch = async move { let results = client_clone - .find_all::(C::CLASS, query, &options) + .find_all::(C::CLASS, query, &options) .await?; Ok(LiveQueryEvent::Initial(results.value)) }; From 23ea166a32fd1b3cb6420cfb478d72f370922e4c Mon Sep 17 00:00:00 2001 From: Nikolay Marchuk Date: Tue, 30 Sep 2025 12:47:13 +0700 Subject: [PATCH 06/11] Fix !Sync issue when using transactor Rust sometimes can't detect that query_pairs_mut dropped before .await and makes whole feature !Send, using additional inner scope fixes it --- src/services/transactor/backend/http.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/services/transactor/backend/http.rs b/src/services/transactor/backend/http.rs index ca27a9b..8363348 100644 --- a/src/services/transactor/backend/http.rs +++ b/src/services/transactor/backend/http.rs @@ -50,15 +50,16 @@ impl HttpBackend { params: impl IntoIterator, ) -> Result { let mut url = self.base().join(path)?; - let mut qp = url.query_pairs_mut(); - for (name, value) in params { - if let Value::String(string) = &value { - qp.append_pair(&name, string); - } else { - qp.append_pair(&name, &value.to_string()); + { + let mut qp = url.query_pairs_mut(); + for (name, value) in params { + if let Value::String(string) = &value { + qp.append_pair(&name, string); + } else { + qp.append_pair(&name, &value.to_string()); + } } } - drop(qp); ::get(&self.inner.client, self, url).await } From 7d335a2f7005e865c06fda6bb871eb40278a874a Mon Sep 17 00:00:00 2001 From: Nikolay Marchuk Date: Tue, 30 Sep 2025 13:14:17 +0700 Subject: [PATCH 07/11] Revert "Use serde_json::Value for TxWorkspaceEvent and TxDomainEvent" This reverts commit 3a69a8ba08be80fe19383329c9049667a1840b30. --- src/services/core/tx.rs | 18 +++++++++--------- src/services/transactor/comm/mod.rs | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/services/core/tx.rs b/src/services/core/tx.rs index eface7a..72a6476 100644 --- a/src/services/core/tx.rs +++ b/src/services/core/tx.rs @@ -68,24 +68,24 @@ impl<'de> Deserialize<'de> for WorkspaceEvent { #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] -pub struct TxWorkspaceEvent { +pub struct TxWorkspaceEvent { #[serde(flatten)] pub tx: Tx, pub domain: WorkspaceEvent, - pub event: serde_json::Value, + pub event: T, } -impl Class for TxWorkspaceEvent { +impl Class for TxWorkspaceEvent { const CLASS: &'static str = crate::services::core::class::TxWorkspaceEvent; } -impl HasId for TxWorkspaceEvent { +impl HasId for TxWorkspaceEvent { fn id(&self) -> &str { &self.tx.doc.id } } -impl Event for TxWorkspaceEvent { +impl Event for TxWorkspaceEvent { fn matches(value: &Value) -> bool { value.get("_class").and_then(|v| v.as_str()) == Some(Self::CLASS) } @@ -93,18 +93,18 @@ impl Event for TxWorkspaceEvent { #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] -pub struct TxDomainEvent { +pub struct TxDomainEvent { #[serde(flatten)] pub tx: Tx, pub domain: OperationDomain, - pub event: serde_json::Value, + pub event: T, } -impl Class for TxDomainEvent { +impl Class for TxDomainEvent { const CLASS: &'static str = crate::services::core::class::TxDomainEvent; } -impl Event for TxDomainEvent { +impl Event for TxDomainEvent { fn matches(value: &Value) -> bool { value.get("_class").and_then(|v| v.as_str()) == Some(Self::CLASS) } diff --git a/src/services/transactor/comm/mod.rs b/src/services/transactor/comm/mod.rs index 792cb19..7807b4e 100644 --- a/src/services/transactor/comm/mod.rs +++ b/src/services/transactor/comm/mod.rs @@ -96,7 +96,7 @@ impl super::Transaction for Envelope { }, domain: "communication".to_string(), - event: json::to_value(&self)?, + event: self, }; Ok(json::to_value(&event)?) } From d20e6314ef35026bd203ebc14f718d87ef9a058e Mon Sep 17 00:00:00 2001 From: Nikolay Marchuk Date: Tue, 30 Sep 2025 13:15:19 +0700 Subject: [PATCH 08/11] Do not require Class for inner data for TxWorkspaceEvent --- src/services/core/tx.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/core/tx.rs b/src/services/core/tx.rs index 72a6476..74a153e 100644 --- a/src/services/core/tx.rs +++ b/src/services/core/tx.rs @@ -85,7 +85,7 @@ impl HasId for TxWorkspaceEvent { } } -impl Event for TxWorkspaceEvent { +impl Event for TxWorkspaceEvent { fn matches(value: &Value) -> bool { value.get("_class").and_then(|v| v.as_str()) == Some(Self::CLASS) } From 23f2891616826bf92ad12fbba32a80a5816976bd Mon Sep 17 00:00:00 2001 From: Alexey Aristov Date: Tue, 23 Sep 2025 12:47:27 +0200 Subject: [PATCH 09/11] adjust MessageType enum Signed-off-by: Alexey Aristov --- src/services/transactor/comm/message.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/transactor/comm/message.rs b/src/services/transactor/comm/message.rs index ef74314..cb16fa6 100644 --- a/src/services/transactor/comm/message.rs +++ b/src/services/transactor/comm/message.rs @@ -49,7 +49,7 @@ pub type MessageExtra = HashMap; #[serde(rename_all = "lowercase")] pub enum MessageType { #[default] - Message, + Text, Activity, } From 34ce3c75cd042b8eea2c8d26fba8c078e7811c62 Mon Sep 17 00:00:00 2001 From: Nikolay Marchuk Date: Tue, 30 Sep 2025 13:33:04 +0700 Subject: [PATCH 10/11] Drop unnecessary class trait requirements --- src/services/core/tx.rs | 21 ++++++----------- src/services/transactor/document.rs | 30 ++++++++++++------------- src/services/transactor/mod.rs | 3 ++- src/services/transactor/subscription.rs | 13 +++++------ 4 files changed, 30 insertions(+), 37 deletions(-) diff --git a/src/services/core/tx.rs b/src/services/core/tx.rs index 74a153e..5be276a 100644 --- a/src/services/core/tx.rs +++ b/src/services/core/tx.rs @@ -218,36 +218,29 @@ impl Event for TxUpdateDoc { #[derive(Clone, Debug, Serialize, Deserialize, Builder)] #[serde(rename_all = "camelCase")] -pub struct TxRemoveDoc { +pub struct TxRemoveDoc { #[serde(flatten)] pub txcud: TxCUD, - - #[serde(skip)] - #[builder(setter(skip), default)] - pub(crate) _phantom: PhantomData, } -impl TxRemoveDoc { - pub fn builder() -> TxRemoveDocBuilder { +impl TxRemoveDoc { + pub fn builder() -> TxRemoveDocBuilder { TxRemoveDocBuilder::default() } } -impl Class for TxRemoveDoc { +impl Class for TxRemoveDoc { const CLASS: &'static str = crate::services::core::class::TxRemoveDoc; } -impl HasId for TxRemoveDoc { +impl HasId for TxRemoveDoc { fn id(&self) -> &str { &self.txcud.object_id } } -impl Event for TxRemoveDoc { +impl Event for TxRemoveDoc { fn matches(value: &Value) -> bool { - if value.get("_class").and_then(|v| v.as_str()) != Some(Self::CLASS) { - return false; - } - value.get("objectClass").and_then(|v| v.as_str()) == Some(C::CLASS) + value.get("_class").and_then(|v| v.as_str()) == Some(Self::CLASS) } } diff --git a/src/services/transactor/document.rs b/src/services/transactor/document.rs index 6738ea0..26de831 100644 --- a/src/services/transactor/document.rs +++ b/src/services/transactor/document.rs @@ -17,7 +17,6 @@ use derive_builder::Builder; use serde::{Deserialize, Serialize, de::DeserializeOwned}; use serde_json::{self as json, Value}; use std::collections::HashMap; -use std::marker::PhantomData; use std::sync::LazyLock; use std::sync::atomic::AtomicUsize; @@ -55,10 +54,13 @@ pub(crate) fn generate_object_id() -> Ref { } #[derive(Default, Debug, derive_builder::Builder, Clone)] -pub struct CreateDocument { +pub struct CreateDocument { #[builder(setter(into), default = generate_object_id())] object_id: Ref, + #[builder(setter(into))] + object_class: String, + #[builder(setter(into), default = Utc::now())] modified_on: Timestamp, @@ -77,7 +79,7 @@ pub struct CreateDocument { attributes: C, } -impl CreateDocument { +impl CreateDocument { pub fn builder() -> CreateDocumentBuilder { CreateDocumentBuilder::default() } @@ -103,7 +105,7 @@ impl Transaction for CreateDocument { object_space: self.object_space, }, object_id: self.object_id, - object_class: C::CLASS.into(), + object_class: self.object_class, attached_to: None, attached_to_class: None, collection: None, @@ -117,10 +119,13 @@ impl Transaction for CreateDocument { } #[derive(Default, Debug, derive_builder::Builder, Clone, Serialize, Deserialize)] -pub struct RemoveDocument { +pub struct RemoveDocument { #[builder(setter(into))] object_id: Ref, + #[builder(setter(into))] + object_class: String, + #[builder(setter(into), default)] modified_on: Option, @@ -135,21 +140,17 @@ pub struct RemoveDocument { #[builder(setter(into))] object_space: String, - - #[serde(skip)] - #[builder(setter(skip), default)] - _phantom: PhantomData, } -impl RemoveDocument { - pub fn builder() -> RemoveDocumentBuilder { +impl RemoveDocument { + pub fn builder() -> RemoveDocumentBuilder { RemoveDocumentBuilder::default() } } -impl Transaction for RemoveDocument { +impl Transaction for RemoveDocument { fn to_value(self) -> Result { - let doc = TxRemoveDoc:: { + let doc = TxRemoveDoc { txcud: TxCUD { tx: Tx { doc: Doc { @@ -167,12 +168,11 @@ impl Transaction for RemoveDocument { object_space: self.object_space, }, object_id: self.object_id, - object_class: C::CLASS.into(), + object_class: self.object_class, attached_to: None, attached_to_class: None, collection: None, }, - _phantom: Default::default(), }; Ok(json::to_value(&doc)?) diff --git a/src/services/transactor/mod.rs b/src/services/transactor/mod.rs index a6d5c4c..4316951 100644 --- a/src/services/transactor/mod.rs +++ b/src/services/transactor/mod.rs @@ -121,7 +121,8 @@ impl TransactorClient { } pub async fn remove(&self, doc: &T) -> Result<()> { - let tx = RemoveDocument::::builder() + let tx = RemoveDocument::builder() + .object_class(&doc.doc().obj.class) .object_id(doc.id()) .object_space(&doc.doc().space) .build() diff --git a/src/services/transactor/subscription.rs b/src/services/transactor/subscription.rs index 0ffcfc0..c4c20df 100644 --- a/src/services/transactor/subscription.rs +++ b/src/services/transactor/subscription.rs @@ -37,7 +37,7 @@ impl SubscribedQuery { pub enum TxEvent { Created(Box>), Updated(Box>), - Deleted(Box>), + Deleted(Box), } impl TxEvent> { @@ -53,10 +53,7 @@ impl TxEvent> { retrieve: tx.retrieve, _phantom: Default::default(), })), - TxEvent::Deleted(tx) => TxEvent::Deleted(Box::new(TxRemoveDoc { - txcud: tx.txcud, - _phantom: Default::default(), - })), + TxEvent::Deleted(tx) => TxEvent::Deleted(Box::new(TxRemoveDoc { txcud: tx.txcud })), } } } @@ -74,8 +71,10 @@ impl Stream for Subscribed } else if TxUpdateDoc::::matches(&value) { let tx: TxUpdateDoc = serde_json::from_value(value)?; return Poll::Ready(Some(Ok(TxEvent::Updated(Box::new(tx))))); - } else if TxRemoveDoc::::matches(&value) { - let tx: TxRemoveDoc = serde_json::from_value(value)?; + } else if TxRemoveDoc::matches(&value) + && value.get("objectClass").and_then(|v| v.as_str()) == Some(C::CLASS) + { + let tx: TxRemoveDoc = serde_json::from_value(value)?; return Poll::Ready(Some(Ok(TxEvent::Deleted(Box::new(tx))))); } From 69a46f2ce9b24f9d4be85c6709fdf10c79ea0390 Mon Sep 17 00:00:00 2001 From: Nikolay Marchuk Date: Tue, 30 Sep 2025 13:38:24 +0700 Subject: [PATCH 11/11] Drop Class requirement for CreateDocument --- src/services/transactor/document.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/services/transactor/document.rs b/src/services/transactor/document.rs index 26de831..f955976 100644 --- a/src/services/transactor/document.rs +++ b/src/services/transactor/document.rs @@ -29,7 +29,6 @@ use crate::services::core::classes::{Ref, Timestamp}; use crate::services::core::ser::Data; use crate::services::core::tx::{Tx, TxCUD, TxCreateDoc, TxRemoveDoc}; use crate::services::core::{Account, FindResult, PersonId}; -use crate::services::event::Class; use crate::services::transactor::backend::Backend; use crate::services::transactor::methods::Method; use crate::{Error, Result}; @@ -85,7 +84,7 @@ impl CreateDocument { } } -impl Transaction for CreateDocument { +impl Transaction for CreateDocument { fn to_value(self) -> Result { let doc = TxCreateDoc { txcud: TxCUD {