From 22ddf35bc6e93e855b7fa28784d0b298fcb758de Mon Sep 17 00:00:00 2001 From: cameronvoell Date: Wed, 18 Dec 2024 10:31:15 -0800 Subject: [PATCH 01/10] save group message content types --- .../2024-12-18-175338_messages_content_type/down.sql | 11 +++++++++++ .../2024-12-18-175338_messages_content_type/up.sql | 11 +++++++++++ xmtp_mls/src/storage/encrypted_store/schema.rs | 4 ++++ 3 files changed, 26 insertions(+) create mode 100644 xmtp_mls/migrations/2024-12-18-175338_messages_content_type/down.sql create mode 100644 xmtp_mls/migrations/2024-12-18-175338_messages_content_type/up.sql diff --git a/xmtp_mls/migrations/2024-12-18-175338_messages_content_type/down.sql b/xmtp_mls/migrations/2024-12-18-175338_messages_content_type/down.sql new file mode 100644 index 000000000..3c9d1a4ec --- /dev/null +++ b/xmtp_mls/migrations/2024-12-18-175338_messages_content_type/down.sql @@ -0,0 +1,11 @@ +ALTER TABLE group_messages + DROP COLUMN authority_id; + +ALTER TABLE group_messages + DROP COLUMN version_major; + +ALTER TABLE group_messages + DROP COLUMN version_minor; + +ALTER TABLE group_messages + DROP COLUMN content_type; diff --git a/xmtp_mls/migrations/2024-12-18-175338_messages_content_type/up.sql b/xmtp_mls/migrations/2024-12-18-175338_messages_content_type/up.sql new file mode 100644 index 000000000..fc3ace48f --- /dev/null +++ b/xmtp_mls/migrations/2024-12-18-175338_messages_content_type/up.sql @@ -0,0 +1,11 @@ +ALTER TABLE group_messages + ADD COLUMN content_type INTEGER NOT NULL DEFAULT 0; + +ALTER TABLE group_messages + ADD COLUMN version_minor INTEGER NOT NULL DEFAULT 0; + +ALTER TABLE group_messages + ADD COLUMN version_major INTEGER NOT NULL DEFAULT 0; + +ALTER TABLE group_messages + ADD COLUMN authority_id TEXT NOT NULL DEFAULT ''; diff --git a/xmtp_mls/src/storage/encrypted_store/schema.rs b/xmtp_mls/src/storage/encrypted_store/schema.rs index c82818161..9dba148f6 100644 --- a/xmtp_mls/src/storage/encrypted_store/schema.rs +++ b/xmtp_mls/src/storage/encrypted_store/schema.rs @@ -41,6 +41,10 @@ diesel::table! { sender_installation_id -> Binary, sender_inbox_id -> Text, delivery_status -> Integer, + content_type -> Integer, + version_minor -> Integer, + version_major -> Integer, + authority_id -> Text, } } From a39c98032808df2a953e1a8a665fabcc4750fd90 Mon Sep 17 00:00:00 2001 From: cameronvoell Date: Wed, 18 Dec 2024 16:21:04 -0800 Subject: [PATCH 02/10] Saving content types to db --- Cargo.lock | 2 + xmtp_content_types/Cargo.toml | 2 + xmtp_content_types/src/lib.rs | 67 ++++++++++++++++++- xmtp_mls/src/groups/mls_sync.rs | 37 ++++++++-- xmtp_mls/src/groups/mod.rs | 46 ++++++++++++- .../storage/encrypted_store/group_message.rs | 38 ++++++++--- 6 files changed, 173 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cf5f4a245..0908ef7f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7319,8 +7319,10 @@ dependencies = [ name = "xmtp_content_types" version = "0.1.0" dependencies = [ + "diesel", "prost", "rand", + "serde", "thiserror 2.0.6", "tonic", "xmtp_common", diff --git a/xmtp_content_types/Cargo.toml b/xmtp_content_types/Cargo.toml index 2b7c506d1..52a4599d5 100644 --- a/xmtp_content_types/Cargo.toml +++ b/xmtp_content_types/Cargo.toml @@ -8,6 +8,8 @@ license.workspace = true thiserror = { workspace = true } prost = { workspace = true, features = ["prost-derive"] } rand = { workspace = true } +diesel = { workspace = true } +serde = { workspace = true } # XMTP/Local xmtp_proto = { workspace = true, features = ["convert"] } diff --git a/xmtp_content_types/src/lib.rs b/xmtp_content_types/src/lib.rs index 04a7a3fb9..ce89ed9f7 100644 --- a/xmtp_content_types/src/lib.rs +++ b/xmtp_content_types/src/lib.rs @@ -2,13 +2,74 @@ pub mod group_updated; pub mod membership_change; pub mod text; +use diesel::{ + backend::Backend, + deserialize::{self, FromSql, FromSqlRow}, + expression::AsExpression, + serialize::{self, IsNull, Output, ToSql}, + sql_types::Integer, + sqlite::Sqlite, +}; +use serde::{Deserialize, Serialize}; use thiserror::Error; use xmtp_proto::xmtp::mls::message_contents::{ContentTypeId, EncodedContent}; +/// ContentType and their corresponding string representation +/// are derived from the `ContentTypeId` enum in the xmtp-proto crate +/// that each content type in this crate establishes for itself +#[repr(i32)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, FromSqlRow, AsExpression)] +#[diesel(sql_type = diesel::sql_types::Integer)] pub enum ContentType { - GroupMembershipChange, - GroupUpdated, - Text, + Unknown = 0, + Text = 1, + GroupMembershipChange = 2, + GroupUpdated = 3, +} + +impl ContentType { + pub fn from_string(type_id: &str) -> Self { + match type_id { + "text" => Self::Text, + "group_membership_change" => Self::GroupMembershipChange, + "group_updated" => Self::GroupUpdated, + _ => Self::Unknown, + } + } + + pub fn to_string(&self) -> &'static str { + match self { + Self::Unknown => "unknown", + Self::Text => "text", + Self::GroupMembershipChange => "group_membership_change", + Self::GroupUpdated => "group_updated", + } + } +} + +impl ToSql for ContentType +where + i32: ToSql, +{ + fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result { + out.set_value(*self as i32); + Ok(IsNull::No) + } +} + +impl FromSql for ContentType +where + i32: FromSql, +{ + fn from_sql(bytes: ::RawValue<'_>) -> deserialize::Result { + match i32::from_sql(bytes)? { + 0 => Ok(ContentType::Unknown), + 1 => Ok(ContentType::Text), + 2 => Ok(ContentType::GroupMembershipChange), + 3 => Ok(ContentType::GroupUpdated), + x => Err(format!("Unrecognized variant {}", x).into()), + } + } } #[derive(Debug, Error)] diff --git a/xmtp_mls/src/groups/mls_sync.rs b/xmtp_mls/src/groups/mls_sync.rs index 87109e81a..044e164ac 100644 --- a/xmtp_mls/src/groups/mls_sync.rs +++ b/xmtp_mls/src/groups/mls_sync.rs @@ -44,7 +44,7 @@ use hmac::{Hmac, Mac}; use openmls::{ credentials::BasicCredential, extensions::Extensions, - framing::{ContentType, ProtocolMessage}, + framing::{ContentType as MlsContentType, ProtocolMessage}, group::{GroupEpoch, StagedCommit}, key_packages::KeyPackage, prelude::{ @@ -67,7 +67,7 @@ use std::{ use thiserror::Error; use tracing::debug; use xmtp_common::{retry_async, Retry, RetryableError}; -use xmtp_content_types::{group_updated::GroupUpdatedCodec, CodecError, ContentCodec}; +use xmtp_content_types::{group_updated::GroupUpdatedCodec, CodecError, ContentCodec, ContentType}; use xmtp_id::{InboxId, InboxIdRef}; use xmtp_proto::xmtp::mls::{ api::v1::{ @@ -546,6 +546,7 @@ where })) => { let message_id = calculate_message_id(&self.group_id, &content, &idempotency_key); + let queryable_content_fields = Self::extract_queryable_content_fields(&content); StoredGroupMessage { id: message_id, group_id: self.group_id.clone(), @@ -555,6 +556,10 @@ where sender_installation_id, sender_inbox_id, delivery_status: DeliveryStatus::Published, + content_type: queryable_content_fields.content_type, + version_major: queryable_content_fields.version_major, + version_minor: queryable_content_fields.version_minor, + authority_id: queryable_content_fields.authority_id, } .store_or_ignore(provider.conn_ref())? } @@ -583,6 +588,10 @@ where sender_installation_id, sender_inbox_id: sender_inbox_id.clone(), delivery_status: DeliveryStatus::Published, + content_type: ContentType::Unknown, + version_major: 0, + version_minor: 0, + authority_id: "unknown".to_string(), } .store_or_ignore(provider.conn_ref())?; @@ -612,6 +621,10 @@ where sender_installation_id, sender_inbox_id, delivery_status: DeliveryStatus::Published, + content_type: ContentType::Unknown, + version_major: 0, + version_minor: 0, + authority_id: "unknown".to_string(), } .store_or_ignore(provider.conn_ref())?; @@ -712,7 +725,7 @@ where discriminant(&other), )), }?; - if !allow_epoch_increment && message.content_type() == ContentType::Commit { + if !allow_epoch_increment && message.content_type() == MlsContentType::Commit { return Err(GroupMessageProcessingError::EpochIncrementNotAllowed); } @@ -933,7 +946,19 @@ where encoded_payload_bytes.as_slice(), ×tamp_ns.to_string(), ); - + let content_type = match encoded_payload.r#type { + Some(ct) => ct, + None => { + tracing::warn!("Missing content type in encoded payload, using default values"); + // Default content type values + xmtp_proto::xmtp::mls::message_contents::ContentTypeId { + authority_id: "unknown".to_string(), + type_id: "unknown".to_string(), + version_major: 0, + version_minor: 0, + } + } + }; let msg = StoredGroupMessage { id: message_id, group_id: group_id.to_vec(), @@ -943,6 +968,10 @@ where sender_installation_id, sender_inbox_id, delivery_status: DeliveryStatus::Published, + content_type: ContentType::from_string(&content_type.type_id), + version_major: content_type.version_major as i32, + version_minor: content_type.version_minor as i32, + authority_id: content_type.authority_id.to_string(), }; msg.store_or_ignore(conn)?; diff --git a/xmtp_mls/src/groups/mod.rs b/xmtp_mls/src/groups/mod.rs index 35662bc7d..e23362c08 100644 --- a/xmtp_mls/src/groups/mod.rs +++ b/xmtp_mls/src/groups/mod.rs @@ -35,6 +35,7 @@ use openmls_traits::OpenMlsProvider; use prost::Message; use thiserror::Error; use tokio::sync::Mutex; +use xmtp_content_types::ContentType; use self::device_sync::DeviceSyncError; pub use self::group_permissions::PreconfiguredPolicies; @@ -67,7 +68,7 @@ use xmtp_proto::xmtp::mls::{ }, message_contents::{ plaintext_envelope::{Content, V1}, - PlaintextEnvelope, + EncodedContent, PlaintextEnvelope, }, }; @@ -309,6 +310,14 @@ pub enum UpdateAdminListType { RemoveSuper, } +/// Fields extracted from content of a message that should be stored in the DB +pub struct QueryableContentFields { + pub content_type: ContentType, + pub version_major: i32, + pub version_minor: i32, + pub authority_id: String, +} + /// Represents a group, which can contain anywhere from 1 to MAX_GROUP_SIZE inboxes. /// /// This is a wrapper around OpenMLS's `MlsGroup` that handles our application-level configuration @@ -706,6 +715,36 @@ impl MlsGroup { Ok(message_id) } + /// Helper function to extract queryable content fields from a message + fn extract_queryable_content_fields(message: &[u8]) -> QueryableContentFields { + let default = QueryableContentFields { + content_type: ContentType::Unknown, + version_major: 0, + version_minor: 0, + authority_id: "unknown".to_string(), + }; + + // Return early with default if decoding fails or type is missing + let content_type_id = match EncodedContent::decode(message) + .map_err(|e| tracing::debug!("Failed to decode message as EncodedContent: {}", e)) + .ok() + .and_then(|content| content.r#type) + { + Some(type_id) => type_id, + None => { + tracing::debug!("Message content type is missing"); + return default; + } + }; + + QueryableContentFields { + content_type: ContentType::from_string(&content_type_id.type_id), + version_major: content_type_id.version_major as i32, + version_minor: content_type_id.version_minor as i32, + authority_id: content_type_id.authority_id.to_string(), + } + } + /// Prepare a [`IntentKind::SendMessage`] intent, and [`StoredGroupMessage`] on this users XMTP [`Client`]. /// /// # Arguments @@ -734,6 +773,7 @@ impl MlsGroup { // store this unpublished message locally before sending let message_id = calculate_message_id(&self.group_id, message, &now.to_string()); + let queryable_content_fields = Self::extract_queryable_content_fields(message); let group_message = StoredGroupMessage { id: message_id.clone(), group_id: self.group_id.clone(), @@ -743,6 +783,10 @@ impl MlsGroup { sender_installation_id: self.context().installation_public_key().into(), sender_inbox_id: self.context().inbox_id().to_string(), delivery_status: DeliveryStatus::Unpublished, + content_type: queryable_content_fields.content_type, + version_major: queryable_content_fields.version_major, + version_minor: queryable_content_fields.version_minor, + authority_id: queryable_content_fields.authority_id, }; group_message.store(provider.conn_ref())?; diff --git a/xmtp_mls/src/storage/encrypted_store/group_message.rs b/xmtp_mls/src/storage/encrypted_store/group_message.rs index 743800d79..75ee41213 100644 --- a/xmtp_mls/src/storage/encrypted_store/group_message.rs +++ b/xmtp_mls/src/storage/encrypted_store/group_message.rs @@ -7,6 +7,7 @@ use diesel::{ sql_types::Integer, }; use serde::{Deserialize, Serialize}; +use xmtp_content_types::ContentType; use super::{ db_connection::DbConnection, @@ -38,6 +39,14 @@ pub struct StoredGroupMessage { pub sender_inbox_id: String, /// We optimistically store messages before sending. pub delivery_status: DeliveryStatus, + /// The Content Type of the message + pub content_type: ContentType, + /// The content type version major + pub version_major: i32, + /// The content type version minor + pub version_minor: i32, + /// The ID of the authority defining the content type + pub authority_id: String, } #[derive(Clone, Debug, PartialEq)] @@ -294,6 +303,7 @@ pub(crate) mod tests { kind: Option, group_id: Option<&[u8]>, sent_at_ns: Option, + content_type: Option, ) -> StoredGroupMessage { StoredGroupMessage { id: rand_vec::<24>(), @@ -304,6 +314,10 @@ pub(crate) mod tests { sender_inbox_id: "0x0".to_string(), kind: kind.unwrap_or(GroupMessageKind::Application), delivery_status: DeliveryStatus::Unpublished, + content_type: content_type.unwrap_or(ContentType::Unknown), + version_major: 0, + version_minor: 0, + authority_id: "unknown".to_string(), } } @@ -320,7 +334,7 @@ pub(crate) mod tests { async fn it_gets_messages() { with_connection(|conn| { let group = generate_group(None); - let message = generate_message(None, Some(&group.id), None); + let message = generate_message(None, Some(&group.id), None, None); group.store(conn).unwrap(); let id = message.id.clone(); @@ -337,7 +351,7 @@ pub(crate) mod tests { use diesel::result::{DatabaseErrorKind::ForeignKeyViolation, Error::DatabaseError}; with_connection(|conn| { - let message = generate_message(None, None, None); + let message = generate_message(None, None, None, None); assert_err!( message.store(conn), StorageError::DieselResult(DatabaseError(ForeignKeyViolation, _)) @@ -355,7 +369,7 @@ pub(crate) mod tests { group.store(conn).unwrap(); for idx in 0..50 { - let msg = generate_message(None, Some(&group.id), Some(idx)); + let msg = generate_message(None, Some(&group.id), Some(idx), None); assert_ok!(msg.store(conn)); } @@ -388,10 +402,10 @@ pub(crate) mod tests { group.store(conn).unwrap(); let messages = vec![ - generate_message(None, Some(&group.id), Some(1_000)), - generate_message(None, Some(&group.id), Some(100_000)), - generate_message(None, Some(&group.id), Some(10_000)), - generate_message(None, Some(&group.id), Some(1_000_000)), + generate_message(None, Some(&group.id), Some(1_000), None), + generate_message(None, Some(&group.id), Some(100_000), None), + generate_message(None, Some(&group.id), Some(10_000), None), + generate_message(None, Some(&group.id), Some(1_000_000), None), ]; assert_ok!(messages.store(conn)); let message = conn @@ -432,6 +446,7 @@ pub(crate) mod tests { Some(GroupMessageKind::Application), Some(&group.id), None, + Some(ContentType::Text), ); msg.store(conn).unwrap(); } @@ -440,6 +455,7 @@ pub(crate) mod tests { Some(GroupMessageKind::MembershipChange), Some(&group.id), None, + Some(ContentType::GroupMembershipChange), ); msg.store(conn).unwrap(); } @@ -472,10 +488,10 @@ pub(crate) mod tests { group.store(conn).unwrap(); let messages = vec![ - generate_message(None, Some(&group.id), Some(10_000)), - generate_message(None, Some(&group.id), Some(1_000)), - generate_message(None, Some(&group.id), Some(100_000)), - generate_message(None, Some(&group.id), Some(1_000_000)), + generate_message(None, Some(&group.id), Some(10_000), None), + generate_message(None, Some(&group.id), Some(1_000), None), + generate_message(None, Some(&group.id), Some(100_000), None), + generate_message(None, Some(&group.id), Some(1_000_000), None), ]; assert_ok!(messages.store(conn)); From a20f5da8377b66c5b961fb03371ee18b7603039b Mon Sep 17 00:00:00 2001 From: cameronvoell Date: Thu, 19 Dec 2024 09:45:35 -0800 Subject: [PATCH 03/10] get content id strings straight from codecs --- xmtp_content_types/src/group_updated.rs | 2 +- xmtp_content_types/src/lib.rs | 12 ++++++------ xmtp_content_types/src/membership_change.rs | 2 +- xmtp_content_types/src/text.rs | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/xmtp_content_types/src/group_updated.rs b/xmtp_content_types/src/group_updated.rs index 2ab08917a..6ef0e33f8 100644 --- a/xmtp_content_types/src/group_updated.rs +++ b/xmtp_content_types/src/group_updated.rs @@ -10,7 +10,7 @@ pub struct GroupUpdatedCodec {} impl GroupUpdatedCodec { const AUTHORITY_ID: &'static str = "xmtp.org"; - const TYPE_ID: &'static str = "group_updated"; + pub const TYPE_ID: &'static str = "group_updated"; } impl ContentCodec for GroupUpdatedCodec { diff --git a/xmtp_content_types/src/lib.rs b/xmtp_content_types/src/lib.rs index ce89ed9f7..c3453908a 100644 --- a/xmtp_content_types/src/lib.rs +++ b/xmtp_content_types/src/lib.rs @@ -30,9 +30,9 @@ pub enum ContentType { impl ContentType { pub fn from_string(type_id: &str) -> Self { match type_id { - "text" => Self::Text, - "group_membership_change" => Self::GroupMembershipChange, - "group_updated" => Self::GroupUpdated, + text::TextCodec::TYPE_ID => Self::Text, + membership_change::GroupMembershipChangeCodec::TYPE_ID => Self::GroupMembershipChange, + group_updated::GroupUpdatedCodec::TYPE_ID => Self::GroupUpdated, _ => Self::Unknown, } } @@ -40,9 +40,9 @@ impl ContentType { pub fn to_string(&self) -> &'static str { match self { Self::Unknown => "unknown", - Self::Text => "text", - Self::GroupMembershipChange => "group_membership_change", - Self::GroupUpdated => "group_updated", + Self::Text => text::TextCodec::TYPE_ID, + Self::GroupMembershipChange => membership_change::GroupMembershipChangeCodec::TYPE_ID, + Self::GroupUpdated => group_updated::GroupUpdatedCodec::TYPE_ID, } } } diff --git a/xmtp_content_types/src/membership_change.rs b/xmtp_content_types/src/membership_change.rs index 14401bbb8..bfb65fea7 100644 --- a/xmtp_content_types/src/membership_change.rs +++ b/xmtp_content_types/src/membership_change.rs @@ -12,7 +12,7 @@ pub struct GroupMembershipChangeCodec {} impl GroupMembershipChangeCodec { const AUTHORITY_ID: &'static str = "xmtp.org"; - const TYPE_ID: &'static str = "group_membership_change"; + pub const TYPE_ID: &'static str = "group_membership_change"; } impl ContentCodec for GroupMembershipChangeCodec { diff --git a/xmtp_content_types/src/text.rs b/xmtp_content_types/src/text.rs index 124fb7831..64bf0c93a 100644 --- a/xmtp_content_types/src/text.rs +++ b/xmtp_content_types/src/text.rs @@ -8,7 +8,7 @@ pub struct TextCodec {} impl TextCodec { const AUTHORITY_ID: &'static str = "xmtp.org"; - const TYPE_ID: &'static str = "text"; + pub const TYPE_ID: &'static str = "text"; const ENCODING_KEY: &'static str = "encoding"; const ENCODING_UTF8: &'static str = "UTF-8"; } From c03d7b458508ee0ded05ad9b93cb6c36ccad8640 Mon Sep 17 00:00:00 2001 From: cameronvoell Date: Thu, 19 Dec 2024 10:59:12 -0800 Subject: [PATCH 04/10] fix wasm dependencies --- xmtp_content_types/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xmtp_content_types/Cargo.toml b/xmtp_content_types/Cargo.toml index 52a4599d5..52fa200b5 100644 --- a/xmtp_content_types/Cargo.toml +++ b/xmtp_content_types/Cargo.toml @@ -8,7 +8,6 @@ license.workspace = true thiserror = { workspace = true } prost = { workspace = true, features = ["prost-derive"] } rand = { workspace = true } -diesel = { workspace = true } serde = { workspace = true } # XMTP/Local @@ -20,3 +19,4 @@ xmtp_common = { workspace = true, features = ['test-utils'] } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] tonic = { version = "0.12", features = ["transport"] } +diesel = { workspace = true } From 8d650fa744c15fdcc8aafa926c953f7976ad25d5 Mon Sep 17 00:00:00 2001 From: cameronvoell Date: Thu, 19 Dec 2024 11:04:08 -0800 Subject: [PATCH 05/10] move content type enum to encrypted_store --- Cargo.lock | 2 - xmtp_content_types/Cargo.toml | 2 - xmtp_content_types/src/lib.rs | 67 ------------------- xmtp_mls/src/groups/mls_sync.rs | 11 ++- xmtp_mls/src/groups/mod.rs | 3 +- .../storage/encrypted_store/group_message.rs | 57 +++++++++++++++- 6 files changed, 62 insertions(+), 80 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0908ef7f8..cf5f4a245 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7319,10 +7319,8 @@ dependencies = [ name = "xmtp_content_types" version = "0.1.0" dependencies = [ - "diesel", "prost", "rand", - "serde", "thiserror 2.0.6", "tonic", "xmtp_common", diff --git a/xmtp_content_types/Cargo.toml b/xmtp_content_types/Cargo.toml index 52fa200b5..2b7c506d1 100644 --- a/xmtp_content_types/Cargo.toml +++ b/xmtp_content_types/Cargo.toml @@ -8,7 +8,6 @@ license.workspace = true thiserror = { workspace = true } prost = { workspace = true, features = ["prost-derive"] } rand = { workspace = true } -serde = { workspace = true } # XMTP/Local xmtp_proto = { workspace = true, features = ["convert"] } @@ -19,4 +18,3 @@ xmtp_common = { workspace = true, features = ['test-utils'] } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] tonic = { version = "0.12", features = ["transport"] } -diesel = { workspace = true } diff --git a/xmtp_content_types/src/lib.rs b/xmtp_content_types/src/lib.rs index c3453908a..3bafb2a03 100644 --- a/xmtp_content_types/src/lib.rs +++ b/xmtp_content_types/src/lib.rs @@ -2,76 +2,9 @@ pub mod group_updated; pub mod membership_change; pub mod text; -use diesel::{ - backend::Backend, - deserialize::{self, FromSql, FromSqlRow}, - expression::AsExpression, - serialize::{self, IsNull, Output, ToSql}, - sql_types::Integer, - sqlite::Sqlite, -}; -use serde::{Deserialize, Serialize}; use thiserror::Error; use xmtp_proto::xmtp::mls::message_contents::{ContentTypeId, EncodedContent}; -/// ContentType and their corresponding string representation -/// are derived from the `ContentTypeId` enum in the xmtp-proto crate -/// that each content type in this crate establishes for itself -#[repr(i32)] -#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, FromSqlRow, AsExpression)] -#[diesel(sql_type = diesel::sql_types::Integer)] -pub enum ContentType { - Unknown = 0, - Text = 1, - GroupMembershipChange = 2, - GroupUpdated = 3, -} - -impl ContentType { - pub fn from_string(type_id: &str) -> Self { - match type_id { - text::TextCodec::TYPE_ID => Self::Text, - membership_change::GroupMembershipChangeCodec::TYPE_ID => Self::GroupMembershipChange, - group_updated::GroupUpdatedCodec::TYPE_ID => Self::GroupUpdated, - _ => Self::Unknown, - } - } - - pub fn to_string(&self) -> &'static str { - match self { - Self::Unknown => "unknown", - Self::Text => text::TextCodec::TYPE_ID, - Self::GroupMembershipChange => membership_change::GroupMembershipChangeCodec::TYPE_ID, - Self::GroupUpdated => group_updated::GroupUpdatedCodec::TYPE_ID, - } - } -} - -impl ToSql for ContentType -where - i32: ToSql, -{ - fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result { - out.set_value(*self as i32); - Ok(IsNull::No) - } -} - -impl FromSql for ContentType -where - i32: FromSql, -{ - fn from_sql(bytes: ::RawValue<'_>) -> deserialize::Result { - match i32::from_sql(bytes)? { - 0 => Ok(ContentType::Unknown), - 1 => Ok(ContentType::Text), - 2 => Ok(ContentType::GroupMembershipChange), - 3 => Ok(ContentType::GroupUpdated), - x => Err(format!("Unrecognized variant {}", x).into()), - } - } -} - #[derive(Debug, Error)] pub enum CodecError { #[error("encode error {0}")] diff --git a/xmtp_mls/src/groups/mls_sync.rs b/xmtp_mls/src/groups/mls_sync.rs index 044e164ac..b1a60ec87 100644 --- a/xmtp_mls/src/groups/mls_sync.rs +++ b/xmtp_mls/src/groups/mls_sync.rs @@ -13,9 +13,9 @@ use crate::{ GRPC_DATA_LIMIT, HMAC_SALT, MAX_GROUP_SIZE, MAX_INTENT_PUBLISH_ATTEMPTS, MAX_PAST_EPOCHS, SYNC_UPDATE_INSTALLATIONS_INTERVAL_NS, }, - groups::device_sync::DeviceSyncContent, groups::{ - device_sync::preference_sync::UserPreferenceUpdate, intents::UpdateMetadataIntentData, + device_sync::{preference_sync::UserPreferenceUpdate, DeviceSyncContent}, + intents::UpdateMetadataIntentData, validated_commit::ValidatedCommit, }, hpke::{encrypt_welcome, HpkeError}, @@ -25,15 +25,14 @@ use crate::{ storage::{ db_connection::DbConnection, group_intent::{IntentKind, IntentState, StoredGroupIntent, ID}, - group_message::{DeliveryStatus, GroupMessageKind, StoredGroupMessage}, + group_message::{ContentType, DeliveryStatus, GroupMessageKind, StoredGroupMessage}, refresh_state::EntityKind, serialization::{db_deserialize, db_serialize}, sql_key_store, user_preferences::StoredUserPreferences, StorageError, }, - subscriptions::LocalEvents, - subscriptions::SyncMessage, + subscriptions::{LocalEvents, SyncMessage}, utils::{hash::sha256, id::calculate_message_id, time::hmac_epoch}, xmtp_openmls_provider::XmtpOpenMlsProvider, Delete, Fetch, StoreOrIgnore, @@ -67,7 +66,7 @@ use std::{ use thiserror::Error; use tracing::debug; use xmtp_common::{retry_async, Retry, RetryableError}; -use xmtp_content_types::{group_updated::GroupUpdatedCodec, CodecError, ContentCodec, ContentType}; +use xmtp_content_types::{group_updated::GroupUpdatedCodec, CodecError, ContentCodec}; use xmtp_id::{InboxId, InboxIdRef}; use xmtp_proto::xmtp::mls::{ api::v1::{ diff --git a/xmtp_mls/src/groups/mod.rs b/xmtp_mls/src/groups/mod.rs index e23362c08..a72c0d02c 100644 --- a/xmtp_mls/src/groups/mod.rs +++ b/xmtp_mls/src/groups/mod.rs @@ -35,7 +35,6 @@ use openmls_traits::OpenMlsProvider; use prost::Message; use thiserror::Error; use tokio::sync::Mutex; -use xmtp_content_types::ContentType; use self::device_sync::DeviceSyncError; pub use self::group_permissions::PreconfiguredPolicies; @@ -59,7 +58,7 @@ use self::{ intents::IntentError, validated_commit::CommitValidationError, }; -use crate::storage::StorageError; +use crate::storage::{group_message::ContentType, StorageError}; use xmtp_common::time::now_ns; use xmtp_proto::xmtp::mls::{ api::v1::{ diff --git a/xmtp_mls/src/storage/encrypted_store/group_message.rs b/xmtp_mls/src/storage/encrypted_store/group_message.rs index 75ee41213..9d4178ea2 100644 --- a/xmtp_mls/src/storage/encrypted_store/group_message.rs +++ b/xmtp_mls/src/storage/encrypted_store/group_message.rs @@ -7,7 +7,7 @@ use diesel::{ sql_types::Integer, }; use serde::{Deserialize, Serialize}; -use xmtp_content_types::ContentType; +use xmtp_content_types::{group_updated, membership_change, text}; use super::{ db_connection::DbConnection, @@ -86,6 +86,61 @@ where } } +#[repr(i32)] +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, FromSqlRow, AsExpression)] +#[diesel(sql_type = diesel::sql_types::Integer)] +pub enum ContentType { + Unknown = 0, + Text = 1, + GroupMembershipChange = 2, + GroupUpdated = 3, +} + +impl ContentType { + pub fn from_string(type_id: &str) -> Self { + match type_id { + text::TextCodec::TYPE_ID => Self::Text, + membership_change::GroupMembershipChangeCodec::TYPE_ID => Self::GroupMembershipChange, + group_updated::GroupUpdatedCodec::TYPE_ID => Self::GroupUpdated, + _ => Self::Unknown, + } + } + + pub fn to_string(&self) -> &'static str { + match self { + Self::Unknown => "unknown", + Self::Text => text::TextCodec::TYPE_ID, + Self::GroupMembershipChange => membership_change::GroupMembershipChangeCodec::TYPE_ID, + Self::GroupUpdated => group_updated::GroupUpdatedCodec::TYPE_ID, + } + } +} + +impl ToSql for ContentType +where + i32: ToSql, +{ + fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result { + out.set_value(*self as i32); + Ok(IsNull::No) + } +} + +impl FromSql for ContentType +where + i32: FromSql, +{ + fn from_sql(bytes: ::RawValue<'_>) -> deserialize::Result { + match i32::from_sql(bytes)? { + 0 => Ok(ContentType::Unknown), + 1 => Ok(ContentType::Text), + 2 => Ok(ContentType::GroupMembershipChange), + 3 => Ok(ContentType::GroupUpdated), + x => Err(format!("Unrecognized variant {}", x).into()), + } + } +} + #[repr(i32)] #[derive(Debug, Copy, Clone, Serialize, Deserialize, Eq, PartialEq, FromSqlRow, AsExpression)] #[diesel(sql_type = Integer)] From d06d62b607181cad450aa6befa1b34b568283508 Mon Sep 17 00:00:00 2001 From: cameronvoell Date: Thu, 19 Dec 2024 12:25:42 -0800 Subject: [PATCH 06/10] add codec files and type ids --- xmtp_content_types/src/lib.rs | 5 +++++ xmtp_content_types/src/reaction.rs | 6 ++++++ xmtp_content_types/src/read_receipt.rs | 6 ++++++ xmtp_content_types/src/remote_attachment.rs | 6 ++++++ xmtp_content_types/src/reply.rs | 6 ++++++ xmtp_content_types/src/transaction_reference.rs | 6 ++++++ 6 files changed, 35 insertions(+) create mode 100644 xmtp_content_types/src/reaction.rs create mode 100644 xmtp_content_types/src/read_receipt.rs create mode 100644 xmtp_content_types/src/remote_attachment.rs create mode 100644 xmtp_content_types/src/reply.rs create mode 100644 xmtp_content_types/src/transaction_reference.rs diff --git a/xmtp_content_types/src/lib.rs b/xmtp_content_types/src/lib.rs index 3bafb2a03..c0fe8fb42 100644 --- a/xmtp_content_types/src/lib.rs +++ b/xmtp_content_types/src/lib.rs @@ -1,6 +1,11 @@ pub mod group_updated; pub mod membership_change; +pub mod reaction; +pub mod reply; +pub mod read_receipt; +pub mod remote_attachment; pub mod text; +pub mod transaction_reference; use thiserror::Error; use xmtp_proto::xmtp::mls::message_contents::{ContentTypeId, EncodedContent}; diff --git a/xmtp_content_types/src/reaction.rs b/xmtp_content_types/src/reaction.rs new file mode 100644 index 000000000..50a89ebce --- /dev/null +++ b/xmtp_content_types/src/reaction.rs @@ -0,0 +1,6 @@ +pub struct ReactionCodec {} + +impl ReactionCodec { + const AUTHORITY_ID: &'static str = "xmtp.org"; + pub const TYPE_ID: &'static str = "reaction"; +} diff --git a/xmtp_content_types/src/read_receipt.rs b/xmtp_content_types/src/read_receipt.rs new file mode 100644 index 000000000..a81a2b7ad --- /dev/null +++ b/xmtp_content_types/src/read_receipt.rs @@ -0,0 +1,6 @@ +pub struct ReadReceiptCodec {} + +impl ReadReceiptCodec { + const AUTHORITY_ID: &'static str = "xmtp.org"; + pub const TYPE_ID: &'static str = "read_receipt"; +} diff --git a/xmtp_content_types/src/remote_attachment.rs b/xmtp_content_types/src/remote_attachment.rs new file mode 100644 index 000000000..77680aefe --- /dev/null +++ b/xmtp_content_types/src/remote_attachment.rs @@ -0,0 +1,6 @@ +pub struct RemoteAttachmentCodec {} + +impl RemoteAttachmentCodec { + const AUTHORITY_ID: &'static str = "xmtp.org"; + pub const TYPE_ID: &'static str = "remote_attachment"; +} diff --git a/xmtp_content_types/src/reply.rs b/xmtp_content_types/src/reply.rs new file mode 100644 index 000000000..6f8ba32f3 --- /dev/null +++ b/xmtp_content_types/src/reply.rs @@ -0,0 +1,6 @@ +pub struct ReplyCodec {} + +impl ReplyCodec { + const AUTHORITY_ID: &'static str = "xmtp.org"; + pub const TYPE_ID: &'static str = "reply"; +} diff --git a/xmtp_content_types/src/transaction_reference.rs b/xmtp_content_types/src/transaction_reference.rs new file mode 100644 index 000000000..c7060dc88 --- /dev/null +++ b/xmtp_content_types/src/transaction_reference.rs @@ -0,0 +1,6 @@ +pub struct TransactionReferenceCodec {} + +impl TransactionReferenceCodec { + const AUTHORITY_ID: &'static str = "xmtp.org"; + pub const TYPE_ID: &'static str = "transaction_reference"; +} From be6e327e7d10c4e2cd91ad2a8bab18f02ff15160 Mon Sep 17 00:00:00 2001 From: cameronvoell Date: Thu, 19 Dec 2024 12:36:30 -0800 Subject: [PATCH 07/10] add test --- xmtp_content_types/src/lib.rs | 2 +- xmtp_content_types/src/reaction.rs | 1 - xmtp_content_types/src/read_receipt.rs | 1 - xmtp_content_types/src/remote_attachment.rs | 1 - xmtp_content_types/src/reply.rs | 1 - .../src/transaction_reference.rs | 1 - .../storage/encrypted_store/group_message.rs | 78 +++++++++++++++++++ 7 files changed, 79 insertions(+), 6 deletions(-) diff --git a/xmtp_content_types/src/lib.rs b/xmtp_content_types/src/lib.rs index c0fe8fb42..727a5374f 100644 --- a/xmtp_content_types/src/lib.rs +++ b/xmtp_content_types/src/lib.rs @@ -1,9 +1,9 @@ pub mod group_updated; pub mod membership_change; pub mod reaction; -pub mod reply; pub mod read_receipt; pub mod remote_attachment; +pub mod reply; pub mod text; pub mod transaction_reference; diff --git a/xmtp_content_types/src/reaction.rs b/xmtp_content_types/src/reaction.rs index 50a89ebce..be03f5465 100644 --- a/xmtp_content_types/src/reaction.rs +++ b/xmtp_content_types/src/reaction.rs @@ -1,6 +1,5 @@ pub struct ReactionCodec {} impl ReactionCodec { - const AUTHORITY_ID: &'static str = "xmtp.org"; pub const TYPE_ID: &'static str = "reaction"; } diff --git a/xmtp_content_types/src/read_receipt.rs b/xmtp_content_types/src/read_receipt.rs index a81a2b7ad..bd72e02cf 100644 --- a/xmtp_content_types/src/read_receipt.rs +++ b/xmtp_content_types/src/read_receipt.rs @@ -1,6 +1,5 @@ pub struct ReadReceiptCodec {} impl ReadReceiptCodec { - const AUTHORITY_ID: &'static str = "xmtp.org"; pub const TYPE_ID: &'static str = "read_receipt"; } diff --git a/xmtp_content_types/src/remote_attachment.rs b/xmtp_content_types/src/remote_attachment.rs index 77680aefe..6cd7ac38d 100644 --- a/xmtp_content_types/src/remote_attachment.rs +++ b/xmtp_content_types/src/remote_attachment.rs @@ -1,6 +1,5 @@ pub struct RemoteAttachmentCodec {} impl RemoteAttachmentCodec { - const AUTHORITY_ID: &'static str = "xmtp.org"; pub const TYPE_ID: &'static str = "remote_attachment"; } diff --git a/xmtp_content_types/src/reply.rs b/xmtp_content_types/src/reply.rs index 6f8ba32f3..bdc0c5add 100644 --- a/xmtp_content_types/src/reply.rs +++ b/xmtp_content_types/src/reply.rs @@ -1,6 +1,5 @@ pub struct ReplyCodec {} impl ReplyCodec { - const AUTHORITY_ID: &'static str = "xmtp.org"; pub const TYPE_ID: &'static str = "reply"; } diff --git a/xmtp_content_types/src/transaction_reference.rs b/xmtp_content_types/src/transaction_reference.rs index c7060dc88..0f7898dcf 100644 --- a/xmtp_content_types/src/transaction_reference.rs +++ b/xmtp_content_types/src/transaction_reference.rs @@ -1,6 +1,5 @@ pub struct TransactionReferenceCodec {} impl TransactionReferenceCodec { - const AUTHORITY_ID: &'static str = "xmtp.org"; pub const TYPE_ID: &'static str = "transaction_reference"; } diff --git a/xmtp_mls/src/storage/encrypted_store/group_message.rs b/xmtp_mls/src/storage/encrypted_store/group_message.rs index 9d4178ea2..a452ecc3c 100644 --- a/xmtp_mls/src/storage/encrypted_store/group_message.rs +++ b/xmtp_mls/src/storage/encrypted_store/group_message.rs @@ -186,6 +186,7 @@ pub struct MsgQueryArgs { delivery_status: Option, limit: Option, direction: Option, + content_types: Option>, } impl MsgQueryArgs { @@ -243,6 +244,16 @@ impl MsgQueryArgs { self.limit = limit; self } + + pub fn content_types(mut self, content_types: Vec) -> Self { + self.content_types = Some(content_types); + self + } + + pub fn maybe_content_types(mut self, content_types: Option>) -> Self { + self.content_types = content_types; + self + } } impl DbConnection { @@ -273,6 +284,10 @@ impl DbConnection { query = query.filter(dsl::delivery_status.eq(status)); } + if let Some(content_types) = &args.content_types { + query = query.filter(dsl::content_type.eq_any(content_types)); + } + query = match args.direction.as_ref().unwrap_or(&SortDirection::Ascending) { SortDirection::Ascending => query.order(dsl::sent_at_ns.asc()), SortDirection::Descending => query.order(dsl::sent_at_ns.desc()), @@ -577,4 +592,67 @@ pub(crate) mod tests { }) .await } + + #[wasm_bindgen_test(unsupported = tokio::test)] + async fn it_gets_messages_by_content_type() { + with_connection(|conn| { + let group = generate_group(None); + group.store(conn).unwrap(); + + let messages = vec![ + generate_message(None, Some(&group.id), Some(1_000), Some(ContentType::Text)), + generate_message( + None, + Some(&group.id), + Some(2_000), + Some(ContentType::GroupMembershipChange), + ), + generate_message( + None, + Some(&group.id), + Some(3_000), + Some(ContentType::GroupUpdated), + ), + ]; + assert_ok!(messages.store(conn)); + + // Query for text messages + let text_messages = conn + .get_group_messages( + &group.id, + &MsgQueryArgs::default().content_types(vec![ContentType::Text]), + ) + .unwrap(); + assert_eq!(text_messages.len(), 1); + assert_eq!(text_messages[0].content_type, ContentType::Text); + assert_eq!(text_messages[0].sent_at_ns, 1_000); + + // Query for membership change messages + let membership_messages = conn + .get_group_messages( + &group.id, + &MsgQueryArgs::default() + .content_types(vec![ContentType::GroupMembershipChange]), + ) + .unwrap(); + assert_eq!(membership_messages.len(), 1); + assert_eq!( + membership_messages[0].content_type, + ContentType::GroupMembershipChange + ); + assert_eq!(membership_messages[0].sent_at_ns, 2_000); + + // Query for group updated messages + let updated_messages = conn + .get_group_messages( + &group.id, + &MsgQueryArgs::default().content_types(vec![ContentType::GroupUpdated]), + ) + .unwrap(); + assert_eq!(updated_messages.len(), 1); + assert_eq!(updated_messages[0].content_type, ContentType::GroupUpdated); + assert_eq!(updated_messages[0].sent_at_ns, 3_000); + }) + .await + } } From ce7eaa8a20c4f30984a78f15cd3a446fc93ed3c1 Mon Sep 17 00:00:00 2001 From: cameronvoell Date: Thu, 19 Dec 2024 14:43:40 -0800 Subject: [PATCH 08/10] implement default for QueryableContentFields --- xmtp_mls/src/groups/mod.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/xmtp_mls/src/groups/mod.rs b/xmtp_mls/src/groups/mod.rs index a72c0d02c..1431925b5 100644 --- a/xmtp_mls/src/groups/mod.rs +++ b/xmtp_mls/src/groups/mod.rs @@ -317,6 +317,17 @@ pub struct QueryableContentFields { pub authority_id: String, } +impl Default for QueryableContentFields { + fn default() -> Self { + Self { + content_type: ContentType::Unknown, // Or whatever the appropriate default is + version_major: 0, + version_minor: 0, + authority_id: String::new(), + } + } +} + /// Represents a group, which can contain anywhere from 1 to MAX_GROUP_SIZE inboxes. /// /// This is a wrapper around OpenMLS's `MlsGroup` that handles our application-level configuration From 9e252344fadc8ac3ba88dc6dad807674a296853f Mon Sep 17 00:00:00 2001 From: cameronvoell Date: Thu, 19 Dec 2024 14:51:52 -0800 Subject: [PATCH 09/10] impl from encoded content for queryable content fields --- xmtp_mls/src/groups/mls_sync.rs | 2 +- xmtp_mls/src/groups/mod.rs | 42 ++++++++----------- .../storage/encrypted_store/group_message.rs | 28 +++++++------ 3 files changed, 34 insertions(+), 38 deletions(-) diff --git a/xmtp_mls/src/groups/mls_sync.rs b/xmtp_mls/src/groups/mls_sync.rs index b1a60ec87..4550abfc3 100644 --- a/xmtp_mls/src/groups/mls_sync.rs +++ b/xmtp_mls/src/groups/mls_sync.rs @@ -967,7 +967,7 @@ where sender_installation_id, sender_inbox_id, delivery_status: DeliveryStatus::Published, - content_type: ContentType::from_string(&content_type.type_id), + content_type: content_type.type_id.into(), version_major: content_type.version_major as i32, version_minor: content_type.version_minor as i32, authority_id: content_type.authority_id.to_string(), diff --git a/xmtp_mls/src/groups/mod.rs b/xmtp_mls/src/groups/mod.rs index 1431925b5..3c1320f6c 100644 --- a/xmtp_mls/src/groups/mod.rs +++ b/xmtp_mls/src/groups/mod.rs @@ -328,6 +328,19 @@ impl Default for QueryableContentFields { } } +impl From for QueryableContentFields { + fn from(content: EncodedContent) -> Self { + let content_type_id = content.r#type.unwrap_or_default(); + + QueryableContentFields { + content_type: content_type_id.type_id.into(), + version_major: content_type_id.version_major as i32, + version_minor: content_type_id.version_minor as i32, + authority_id: content_type_id.authority_id.to_string(), + } + } +} + /// Represents a group, which can contain anywhere from 1 to MAX_GROUP_SIZE inboxes. /// /// This is a wrapper around OpenMLS's `MlsGroup` that handles our application-level configuration @@ -727,32 +740,11 @@ impl MlsGroup { /// Helper function to extract queryable content fields from a message fn extract_queryable_content_fields(message: &[u8]) -> QueryableContentFields { - let default = QueryableContentFields { - content_type: ContentType::Unknown, - version_major: 0, - version_minor: 0, - authority_id: "unknown".to_string(), - }; - // Return early with default if decoding fails or type is missing - let content_type_id = match EncodedContent::decode(message) - .map_err(|e| tracing::debug!("Failed to decode message as EncodedContent: {}", e)) - .ok() - .and_then(|content| content.r#type) - { - Some(type_id) => type_id, - None => { - tracing::debug!("Message content type is missing"); - return default; - } - }; - - QueryableContentFields { - content_type: ContentType::from_string(&content_type_id.type_id), - version_major: content_type_id.version_major as i32, - version_minor: content_type_id.version_minor as i32, - authority_id: content_type_id.authority_id.to_string(), - } + EncodedContent::decode(message) + .inspect_err(|e| tracing::debug!("Failed to decode message as EncodedContent: {}", e)) + .map(QueryableContentFields::from) + .unwrap_or_default() } /// Prepare a [`IntentKind::SendMessage`] intent, and [`StoredGroupMessage`] on this users XMTP [`Client`]. diff --git a/xmtp_mls/src/storage/encrypted_store/group_message.rs b/xmtp_mls/src/storage/encrypted_store/group_message.rs index a452ecc3c..5c75c3a79 100644 --- a/xmtp_mls/src/storage/encrypted_store/group_message.rs +++ b/xmtp_mls/src/storage/encrypted_store/group_message.rs @@ -96,22 +96,26 @@ pub enum ContentType { GroupUpdated = 3, } -impl ContentType { - pub fn from_string(type_id: &str) -> Self { - match type_id { - text::TextCodec::TYPE_ID => Self::Text, - membership_change::GroupMembershipChangeCodec::TYPE_ID => Self::GroupMembershipChange, - group_updated::GroupUpdatedCodec::TYPE_ID => Self::GroupUpdated, - _ => Self::Unknown, - } - } - - pub fn to_string(&self) -> &'static str { - match self { +impl std::fmt::Display for ContentType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let as_string = match self { Self::Unknown => "unknown", Self::Text => text::TextCodec::TYPE_ID, Self::GroupMembershipChange => membership_change::GroupMembershipChangeCodec::TYPE_ID, Self::GroupUpdated => group_updated::GroupUpdatedCodec::TYPE_ID, + }; + + write!(f, "{}", as_string) + } +} + +impl From for ContentType { + fn from(type_id: String) -> Self { + match type_id.as_str() { + text::TextCodec::TYPE_ID => Self::Text, + membership_change::GroupMembershipChangeCodec::TYPE_ID => Self::GroupMembershipChange, + group_updated::GroupUpdatedCodec::TYPE_ID => Self::GroupUpdated, + _ => Self::Unknown, } } } From 247a7e148973838858a3da6695d227d92e14465f Mon Sep 17 00:00:00 2001 From: cameronvoell Date: Thu, 19 Dec 2024 15:51:33 -0800 Subject: [PATCH 10/10] adds existing content type identifiers for saving to db --- xmtp_content_types/src/attachment.rs | 6 ++++ xmtp_content_types/src/lib.rs | 1 + xmtp_content_types/src/reaction.rs | 1 + xmtp_content_types/src/read_receipt.rs | 3 +- xmtp_content_types/src/remote_attachment.rs | 3 +- xmtp_content_types/src/reply.rs | 1 + .../src/transaction_reference.rs | 3 +- .../storage/encrypted_store/group_message.rs | 30 ++++++++++++++++++- 8 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 xmtp_content_types/src/attachment.rs diff --git a/xmtp_content_types/src/attachment.rs b/xmtp_content_types/src/attachment.rs new file mode 100644 index 000000000..fcd908944 --- /dev/null +++ b/xmtp_content_types/src/attachment.rs @@ -0,0 +1,6 @@ +pub struct AttachmentCodec {} + +//. Legacy content type id at https://github.com/xmtp/xmtp-js/blob/main/content-types/content-type-remote-attachment/src/Attachment.ts +impl AttachmentCodec { + pub const TYPE_ID: &'static str = "attachment"; +} diff --git a/xmtp_content_types/src/lib.rs b/xmtp_content_types/src/lib.rs index 727a5374f..e6e8d4397 100644 --- a/xmtp_content_types/src/lib.rs +++ b/xmtp_content_types/src/lib.rs @@ -1,3 +1,4 @@ +pub mod attachment; pub mod group_updated; pub mod membership_change; pub mod reaction; diff --git a/xmtp_content_types/src/reaction.rs b/xmtp_content_types/src/reaction.rs index be03f5465..771f03119 100644 --- a/xmtp_content_types/src/reaction.rs +++ b/xmtp_content_types/src/reaction.rs @@ -1,5 +1,6 @@ pub struct ReactionCodec {} +/// Legacy content type id at https://github.com/xmtp/xmtp-js/blob/main/content-types/content-type-reaction/src/Reaction.ts impl ReactionCodec { pub const TYPE_ID: &'static str = "reaction"; } diff --git a/xmtp_content_types/src/read_receipt.rs b/xmtp_content_types/src/read_receipt.rs index bd72e02cf..7c0f34e06 100644 --- a/xmtp_content_types/src/read_receipt.rs +++ b/xmtp_content_types/src/read_receipt.rs @@ -1,5 +1,6 @@ pub struct ReadReceiptCodec {} +/// Legacy content type id at https://github.com/xmtp/xmtp-js/blob/main/content-types/content-type-read-receipt/src/ReadReceipt.ts impl ReadReceiptCodec { - pub const TYPE_ID: &'static str = "read_receipt"; + pub const TYPE_ID: &'static str = "readReceipt"; } diff --git a/xmtp_content_types/src/remote_attachment.rs b/xmtp_content_types/src/remote_attachment.rs index 6cd7ac38d..7d190d32b 100644 --- a/xmtp_content_types/src/remote_attachment.rs +++ b/xmtp_content_types/src/remote_attachment.rs @@ -1,5 +1,6 @@ pub struct RemoteAttachmentCodec {} +//. Legacy content type id at https://github.com/xmtp/xmtp-js/blob/main/content-types/content-type-remote-attachment/src/RemoteAttachment.ts impl RemoteAttachmentCodec { - pub const TYPE_ID: &'static str = "remote_attachment"; + pub const TYPE_ID: &'static str = "remoteStaticAttachment"; } diff --git a/xmtp_content_types/src/reply.rs b/xmtp_content_types/src/reply.rs index bdc0c5add..513effe9d 100644 --- a/xmtp_content_types/src/reply.rs +++ b/xmtp_content_types/src/reply.rs @@ -1,5 +1,6 @@ pub struct ReplyCodec {} +/// Legacy content type id at https://github.com/xmtp/xmtp-js/blob/main/content-types/content-type-reply/src/Reply.ts impl ReplyCodec { pub const TYPE_ID: &'static str = "reply"; } diff --git a/xmtp_content_types/src/transaction_reference.rs b/xmtp_content_types/src/transaction_reference.rs index 0f7898dcf..44df54d6c 100644 --- a/xmtp_content_types/src/transaction_reference.rs +++ b/xmtp_content_types/src/transaction_reference.rs @@ -1,5 +1,6 @@ pub struct TransactionReferenceCodec {} +/// Legacy content type id at https://github.com/xmtp/xmtp-js/blob/main/content-types/content-type-transaction-reference/src/TransactionReference.ts impl TransactionReferenceCodec { - pub const TYPE_ID: &'static str = "transaction_reference"; + pub const TYPE_ID: &'static str = "transactionReference"; } diff --git a/xmtp_mls/src/storage/encrypted_store/group_message.rs b/xmtp_mls/src/storage/encrypted_store/group_message.rs index 5c75c3a79..cfc3d599c 100644 --- a/xmtp_mls/src/storage/encrypted_store/group_message.rs +++ b/xmtp_mls/src/storage/encrypted_store/group_message.rs @@ -7,7 +7,10 @@ use diesel::{ sql_types::Integer, }; use serde::{Deserialize, Serialize}; -use xmtp_content_types::{group_updated, membership_change, text}; +use xmtp_content_types::{ + attachment, group_updated, membership_change, reaction, read_receipt, remote_attachment, reply, + text, transaction_reference, +}; use super::{ db_connection::DbConnection, @@ -86,6 +89,7 @@ where } } +//Legacy content types found at https://github.com/xmtp/xmtp-js/tree/main/content-types #[repr(i32)] #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, FromSqlRow, AsExpression)] #[diesel(sql_type = diesel::sql_types::Integer)] @@ -94,6 +98,12 @@ pub enum ContentType { Text = 1, GroupMembershipChange = 2, GroupUpdated = 3, + Reaction = 4, + ReadReceipt = 5, + Reply = 6, + Attachment = 7, + RemoteAttachment = 8, + TransactionReference = 9, } impl std::fmt::Display for ContentType { @@ -103,6 +113,12 @@ impl std::fmt::Display for ContentType { Self::Text => text::TextCodec::TYPE_ID, Self::GroupMembershipChange => membership_change::GroupMembershipChangeCodec::TYPE_ID, Self::GroupUpdated => group_updated::GroupUpdatedCodec::TYPE_ID, + Self::Reaction => reaction::ReactionCodec::TYPE_ID, + Self::ReadReceipt => read_receipt::ReadReceiptCodec::TYPE_ID, + Self::Attachment => attachment::AttachmentCodec::TYPE_ID, + Self::RemoteAttachment => remote_attachment::RemoteAttachmentCodec::TYPE_ID, + Self::Reply => reply::ReplyCodec::TYPE_ID, + Self::TransactionReference => transaction_reference::TransactionReferenceCodec::TYPE_ID, }; write!(f, "{}", as_string) @@ -115,6 +131,12 @@ impl From for ContentType { text::TextCodec::TYPE_ID => Self::Text, membership_change::GroupMembershipChangeCodec::TYPE_ID => Self::GroupMembershipChange, group_updated::GroupUpdatedCodec::TYPE_ID => Self::GroupUpdated, + reaction::ReactionCodec::TYPE_ID => Self::Reaction, + read_receipt::ReadReceiptCodec::TYPE_ID => Self::ReadReceipt, + reply::ReplyCodec::TYPE_ID => Self::Reply, + attachment::AttachmentCodec::TYPE_ID => Self::Attachment, + remote_attachment::RemoteAttachmentCodec::TYPE_ID => Self::RemoteAttachment, + transaction_reference::TransactionReferenceCodec::TYPE_ID => Self::TransactionReference, _ => Self::Unknown, } } @@ -140,6 +162,12 @@ where 1 => Ok(ContentType::Text), 2 => Ok(ContentType::GroupMembershipChange), 3 => Ok(ContentType::GroupUpdated), + 4 => Ok(ContentType::Reaction), + 5 => Ok(ContentType::ReadReceipt), + 6 => Ok(ContentType::Reply), + 7 => Ok(ContentType::Attachment), + 8 => Ok(ContentType::RemoteAttachment), + 9 => Ok(ContentType::TransactionReference), x => Err(format!("Unrecognized variant {}", x).into()), } }