Simplify notification serialization
This commit is contained in:
parent
83fb8d20b7
commit
5a0afcc835
4 changed files with 29 additions and 40 deletions
|
@ -13,6 +13,7 @@ use anyhow::anyhow;
|
||||||
use collections::{BTreeMap, HashMap, HashSet};
|
use collections::{BTreeMap, HashMap, HashSet};
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
|
use queries::channels::ChannelGraph;
|
||||||
use rand::{prelude::StdRng, Rng, SeedableRng};
|
use rand::{prelude::StdRng, Rng, SeedableRng};
|
||||||
use rpc::{
|
use rpc::{
|
||||||
proto::{self},
|
proto::{self},
|
||||||
|
@ -47,8 +48,6 @@ pub use ids::*;
|
||||||
pub use sea_orm::ConnectOptions;
|
pub use sea_orm::ConnectOptions;
|
||||||
pub use tables::user::Model as User;
|
pub use tables::user::Model as User;
|
||||||
|
|
||||||
use self::queries::channels::ChannelGraph;
|
|
||||||
|
|
||||||
pub struct Database {
|
pub struct Database {
|
||||||
options: ConnectOptions,
|
options: ConnectOptions,
|
||||||
pool: DatabaseConnection,
|
pool: DatabaseConnection,
|
||||||
|
|
|
@ -76,10 +76,10 @@ impl Database {
|
||||||
notification: Notification,
|
notification: Notification,
|
||||||
tx: &DatabaseTransaction,
|
tx: &DatabaseTransaction,
|
||||||
) -> Result<proto::Notification> {
|
) -> Result<proto::Notification> {
|
||||||
let notification = notification.to_any();
|
let notification = notification.to_proto();
|
||||||
let kind = *self
|
let kind = *self
|
||||||
.notification_kinds_by_name
|
.notification_kinds_by_name
|
||||||
.get(notification.kind.as_ref())
|
.get(¬ification.kind)
|
||||||
.ok_or_else(|| anyhow!("invalid notification kind {:?}", notification.kind))?;
|
.ok_or_else(|| anyhow!("invalid notification kind {:?}", notification.kind))?;
|
||||||
|
|
||||||
let model = notification::ActiveModel {
|
let model = notification::ActiveModel {
|
||||||
|
@ -110,10 +110,10 @@ impl Database {
|
||||||
notification: Notification,
|
notification: Notification,
|
||||||
tx: &DatabaseTransaction,
|
tx: &DatabaseTransaction,
|
||||||
) -> Result<Option<NotificationId>> {
|
) -> Result<Option<NotificationId>> {
|
||||||
let notification = notification.to_any();
|
let notification = notification.to_proto();
|
||||||
let kind = *self
|
let kind = *self
|
||||||
.notification_kinds_by_name
|
.notification_kinds_by_name
|
||||||
.get(notification.kind.as_ref())
|
.get(¬ification.kind)
|
||||||
.ok_or_else(|| anyhow!("invalid notification kind {:?}", notification.kind))?;
|
.ok_or_else(|| anyhow!("invalid notification kind {:?}", notification.kind))?;
|
||||||
let actor_id = notification.actor_id.map(|id| UserId::from_proto(id));
|
let actor_id = notification.actor_id.map(|id| UserId::from_proto(id));
|
||||||
let notification = notification::Entity::find()
|
let notification = notification::Entity::find()
|
||||||
|
|
|
@ -4,7 +4,7 @@ use client::{Client, UserStore};
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use db::smol::stream::StreamExt;
|
use db::smol::stream::StreamExt;
|
||||||
use gpui::{AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, Task};
|
use gpui::{AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, Task};
|
||||||
use rpc::{proto, AnyNotification, Notification, TypedEnvelope};
|
use rpc::{proto, Notification, TypedEnvelope};
|
||||||
use std::{ops::Range, sync::Arc};
|
use std::{ops::Range, sync::Arc};
|
||||||
use sum_tree::{Bias, SumTree};
|
use sum_tree::{Bias, SumTree};
|
||||||
use time::OffsetDateTime;
|
use time::OffsetDateTime;
|
||||||
|
@ -185,11 +185,7 @@ impl NotificationStore {
|
||||||
is_read: message.is_read,
|
is_read: message.is_read,
|
||||||
timestamp: OffsetDateTime::from_unix_timestamp(message.timestamp as i64)
|
timestamp: OffsetDateTime::from_unix_timestamp(message.timestamp as i64)
|
||||||
.ok()?,
|
.ok()?,
|
||||||
notification: Notification::from_any(&AnyNotification {
|
notification: Notification::from_proto(&message)?,
|
||||||
actor_id: message.actor_id,
|
|
||||||
kind: message.kind.into(),
|
|
||||||
content: message.content,
|
|
||||||
})?,
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
use crate::proto;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::{map, Value};
|
use serde_json::{map, Value};
|
||||||
use std::borrow::Cow;
|
use strum::{EnumVariantNames, VariantNames as _};
|
||||||
use strum::{EnumVariantNames, IntoStaticStr, VariantNames as _};
|
|
||||||
|
|
||||||
const KIND: &'static str = "kind";
|
const KIND: &'static str = "kind";
|
||||||
const ACTOR_ID: &'static str = "actor_id";
|
const ACTOR_ID: &'static str = "actor_id";
|
||||||
|
@ -9,10 +9,12 @@ const ACTOR_ID: &'static str = "actor_id";
|
||||||
/// A notification that can be stored, associated with a given user.
|
/// A notification that can be stored, associated with a given user.
|
||||||
///
|
///
|
||||||
/// This struct is stored in the collab database as JSON, so it shouldn't be
|
/// This struct is stored in the collab database as JSON, so it shouldn't be
|
||||||
/// changed in a backward-incompatible way.
|
/// changed in a backward-incompatible way. For example, when renaming a
|
||||||
|
/// variant, add a serde alias for the old name.
|
||||||
///
|
///
|
||||||
/// For example, when renaming a variant, add a serde alias for the old name.
|
/// When a notification is initiated by a user, use the `actor_id` field
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, EnumVariantNames, IntoStaticStr, Serialize, Deserialize)]
|
/// to store the user's id.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, EnumVariantNames, Serialize, Deserialize)]
|
||||||
#[serde(tag = "kind")]
|
#[serde(tag = "kind")]
|
||||||
pub enum Notification {
|
pub enum Notification {
|
||||||
ContactRequest {
|
ContactRequest {
|
||||||
|
@ -32,36 +34,28 @@ pub enum Notification {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The representation of a notification that is stored in the database and
|
|
||||||
/// sent over the wire.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct AnyNotification {
|
|
||||||
pub kind: Cow<'static, str>,
|
|
||||||
pub actor_id: Option<u64>,
|
|
||||||
pub content: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Notification {
|
impl Notification {
|
||||||
pub fn to_any(&self) -> AnyNotification {
|
pub fn to_proto(&self) -> proto::Notification {
|
||||||
let kind: &'static str = self.into();
|
|
||||||
let mut value = serde_json::to_value(self).unwrap();
|
let mut value = serde_json::to_value(self).unwrap();
|
||||||
let mut actor_id = None;
|
let mut actor_id = None;
|
||||||
if let Some(value) = value.as_object_mut() {
|
let value = value.as_object_mut().unwrap();
|
||||||
value.remove(KIND);
|
let Some(Value::String(kind)) = value.remove(KIND) else {
|
||||||
if let map::Entry::Occupied(e) = value.entry(ACTOR_ID) {
|
unreachable!()
|
||||||
if e.get().is_u64() {
|
};
|
||||||
actor_id = e.remove().as_u64();
|
if let map::Entry::Occupied(e) = value.entry(ACTOR_ID) {
|
||||||
}
|
if e.get().is_u64() {
|
||||||
|
actor_id = e.remove().as_u64();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AnyNotification {
|
proto::Notification {
|
||||||
kind: Cow::Borrowed(kind),
|
kind,
|
||||||
actor_id,
|
actor_id,
|
||||||
content: serde_json::to_string(&value).unwrap(),
|
content: serde_json::to_string(&value).unwrap(),
|
||||||
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_any(notification: &AnyNotification) -> Option<Self> {
|
pub fn from_proto(notification: &proto::Notification) -> Option<Self> {
|
||||||
let mut value = serde_json::from_str::<Value>(¬ification.content).ok()?;
|
let mut value = serde_json::from_str::<Value>(¬ification.content).ok()?;
|
||||||
let object = value.as_object_mut()?;
|
let object = value.as_object_mut()?;
|
||||||
object.insert(KIND.into(), notification.kind.to_string().into());
|
object.insert(KIND.into(), notification.kind.to_string().into());
|
||||||
|
@ -92,13 +86,13 @@ fn test_notification() {
|
||||||
message_id: 1,
|
message_id: 1,
|
||||||
},
|
},
|
||||||
] {
|
] {
|
||||||
let serialized = notification.to_any();
|
let message = notification.to_proto();
|
||||||
let deserialized = Notification::from_any(&serialized).unwrap();
|
let deserialized = Notification::from_proto(&message).unwrap();
|
||||||
assert_eq!(deserialized, notification);
|
assert_eq!(deserialized, notification);
|
||||||
}
|
}
|
||||||
|
|
||||||
// When notifications are serialized, the `kind` and `actor_id` fields are
|
// When notifications are serialized, the `kind` and `actor_id` fields are
|
||||||
// stored separately, and do not appear redundantly in the JSON.
|
// stored separately, and do not appear redundantly in the JSON.
|
||||||
let notification = Notification::ContactRequest { actor_id: 1 };
|
let notification = Notification::ContactRequest { actor_id: 1 };
|
||||||
assert_eq!(notification.to_any().content, "{}");
|
assert_eq!(notification.to_proto().content, "{}");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue