Feat: Include record ID in live queries notifications. ()

Co-authored-by: Tobie Morgan Hitchcock <tobie@surrealdb.com>
This commit is contained in:
Roman Heinrich 2024-09-16 01:48:57 +02:00 committed by GitHub
parent 218fa83c10
commit be24734048
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 21 additions and 10 deletions
core/src
dbs
doc
sql/statements
sdk/tests
tests/common

View file

@ -33,6 +33,8 @@ pub struct Notification {
pub id: Uuid, pub id: Uuid,
/// The CREATE / UPDATE / DELETE action which caused this notification /// The CREATE / UPDATE / DELETE action which caused this notification
pub action: Action, pub action: Action,
/// The id of the document to which this notification has been made
pub record: Value,
/// The resulting notification content, usually the altered record content /// The resulting notification content, usually the altered record content
pub result: Value, pub result: Value,
} }
@ -42,6 +44,7 @@ impl Display for Notification {
let obj: Object = map! { let obj: Object = map! {
"id".to_string() => self.id.to_string().into(), "id".to_string() => self.id.to_string().into(),
"action".to_string() => self.action.to_string().into(), "action".to_string() => self.action.to_string().into(),
"record".to_string() => self.record.clone(),
"result".to_string() => self.result.clone(), "result".to_string() => self.result.clone(),
} }
.into(); .into();
@ -51,10 +54,11 @@ impl Display for Notification {
impl Notification { impl Notification {
/// Construct a new notification /// Construct a new notification
pub const fn new(id: Uuid, action: Action, result: Value) -> Self { pub const fn new(id: Uuid, action: Action, record: Value, result: Value) -> Self {
Self { Self {
id, id,
action, action,
record,
result, result,
} }
} }
@ -63,6 +67,6 @@ impl Notification {
#[cfg(test)] #[cfg(test)]
impl FuzzyEq for Notification { impl FuzzyEq for Notification {
fn fuzzy_eq(&self, other: &Self) -> bool { fn fuzzy_eq(&self, other: &Self) -> bool {
self.action == other.action && self.result == other.result self.action == other.action && self.record == other.record && self.result == other.result
} }
} }

View file

@ -47,6 +47,9 @@ impl Document {
} else { } else {
Value::from("UPDATE") Value::from("UPDATE")
}; };
// Get the record if of this docunent
let rid = self.id.as_ref().unwrap();
// Get the current and initial docs
let current = self.current.doc.as_arc(); let current = self.current.doc.as_arc();
let initial = self.initial.doc.as_arc(); let initial = self.initial.doc.as_arc();
// Check if this is a delete statement // Check if this is a delete statement
@ -119,6 +122,7 @@ impl Document {
chn.send(Notification { chn.send(Notification {
id: lv.id, id: lv.id,
action: Action::Delete, action: Action::Delete,
record: Value::Thing(rid.as_ref().clone()),
result: { result: {
// Ensure futures are run // Ensure futures are run
let lqopt: &Options = &lqopt.new_with_futures(true); let lqopt: &Options = &lqopt.new_with_futures(true);
@ -141,6 +145,7 @@ impl Document {
chn.send(Notification { chn.send(Notification {
id: lv.id, id: lv.id,
action: Action::Create, action: Action::Create,
record: Value::Thing(rid.as_ref().clone()),
result: self.pluck(stk, &lqctx, &lqopt, &lq).await?, result: self.pluck(stk, &lqctx, &lqopt, &lq).await?,
}) })
.await?; .await?;
@ -153,6 +158,7 @@ impl Document {
chn.send(Notification { chn.send(Notification {
id: lv.id, id: lv.id,
action: Action::Update, action: Action::Update,
record: Value::Thing(rid.as_ref().clone()),
result: self.pluck(stk, &lqctx, &lqopt, &lq).await?, result: self.pluck(stk, &lqctx, &lqopt, &lq).await?,
}) })
.await?; .await?;

View file

@ -162,6 +162,7 @@ mod tests {
use crate::kvs::Datastore; use crate::kvs::Datastore;
use crate::kvs::LockType::Optimistic; use crate::kvs::LockType::Optimistic;
use crate::kvs::TransactionType::Write; use crate::kvs::TransactionType::Write;
use crate::sql::Thing;
use crate::sql::Value; use crate::sql::Value;
use crate::syn::Parse; use crate::syn::Parse;
@ -202,15 +203,14 @@ mod tests {
tx.cancel().await.unwrap(); tx.cancel().await.unwrap();
// Initiate a Create record // Initiate a Create record
let create_statement = format!("CREATE {}:test_true SET condition = true", tb); let create_statement = format!("CREATE {tb}:test_true SET condition = true");
let create_response = &mut dbs.execute(&create_statement, &ses, None).await.unwrap(); let create_response = &mut dbs.execute(&create_statement, &ses, None).await.unwrap();
assert_eq!(create_response.len(), 1); assert_eq!(create_response.len(), 1);
let expected_record = Value::parse(&format!( let expected_record = Value::parse(&format!(
"[{{ "[{{
id: {}:test_true, id: {tb}:test_true,
condition: true, condition: true,
}}]", }}]"
tb
)); ));
let tmp = create_response.remove(0).result.unwrap(); let tmp = create_response.remove(0).result.unwrap();
@ -231,12 +231,12 @@ mod tests {
Notification::new( Notification::new(
live_id, live_id,
Action::Create, Action::Create,
Value::Thing(Thing::from((tb, "test_true"))),
Value::parse(&format!( Value::parse(&format!(
"{{ "{{
id: {}:test_true, id: {tb}:test_true,
condition: true, condition: true,
}}", }}"
tb
),), ),),
) )
); );

View file

@ -416,6 +416,7 @@ async fn delete_filtered_live_notification() -> Result<(), Error> {
Notification::new( Notification::new(
live_id, live_id,
Action::Delete, Action::Delete,
Value::Thing(Thing::from(("person", "test_true"))),
Value::parse( Value::parse(
"{ "{
id: person:test_true, id: person:test_true,

View file

@ -24,7 +24,7 @@ pub fn is_notification(msg: &serde_json::Value) -> bool {
.as_object() .as_object()
.unwrap() .unwrap()
.keys() .keys()
.all(|k| ["id", "action", "result"].contains(&k.as_str())) .all(|k| ["id", "action", "record", "result"].contains(&k.as_str()))
} }
/// Check if the given message is a notification from LQ and comes from the given LQ ID. /// Check if the given message is a notification from LQ and comes from the given LQ ID.