xmtp_db/encrypted_store/
pending_remove.rs

1use super::ConnectionExt;
2use crate::schema::pending_remove::dsl;
3use crate::{DbConnection, impl_fetch, impl_store_or_ignore, schema::pending_remove};
4use diesel::dsl::exists;
5use diesel::prelude::*;
6use diesel::select;
7use serde::{Deserialize, Serialize};
8
9#[derive(
10    Debug,
11    Clone,
12    Serialize,
13    Deserialize,
14    Insertable,
15    Identifiable,
16    Queryable,
17    Eq,
18    PartialEq,
19    QueryableByName,
20)]
21#[diesel(table_name = pending_remove)]
22#[diesel(primary_key(inbox_id, group_id))]
23pub struct PendingRemove {
24    /// Id of the group this message is tied to.
25    pub group_id: Vec<u8>,
26    /// Id of the inbox user want to leave the group.
27    pub inbox_id: String,
28    /// Id of the LeaveRequest message
29    pub message_id: Vec<u8>,
30}
31
32impl_store_or_ignore!(PendingRemove, pending_remove);
33impl_fetch!(PendingRemove, pending_remove);
34pub trait QueryPendingRemove {
35    fn get_pending_remove_users(
36        &self,
37        group_id: &[u8],
38    ) -> Result<Vec<String>, crate::ConnectionError>;
39    fn get_user_pending_remove_status(
40        &self,
41        group_id: &[u8],
42        inbox_id: &str,
43    ) -> Result<bool, crate::ConnectionError>;
44    fn delete_pending_remove_users(
45        &self,
46        group_id: &[u8],
47        inbox_ids: Vec<String>,
48    ) -> Result<usize, crate::ConnectionError>;
49}
50impl<T> QueryPendingRemove for &T
51where
52    T: QueryPendingRemove,
53{
54    fn get_pending_remove_users(
55        &self,
56        group_id: &[u8],
57    ) -> Result<Vec<String>, crate::ConnectionError> {
58        (**self).get_pending_remove_users(group_id)
59    }
60    fn get_user_pending_remove_status(
61        &self,
62        group_id: &[u8],
63        inbox_id: &str,
64    ) -> Result<bool, crate::ConnectionError> {
65        (**self).get_user_pending_remove_status(group_id, inbox_id)
66    }
67    fn delete_pending_remove_users(
68        &self,
69        group_id: &[u8],
70        inbox_ids: Vec<String>,
71    ) -> Result<usize, crate::ConnectionError> {
72        (**self).delete_pending_remove_users(group_id, inbox_ids)
73    }
74}
75impl<C: ConnectionExt> QueryPendingRemove for DbConnection<C> {
76    fn get_pending_remove_users(
77        &self,
78        group_id: &[u8],
79    ) -> Result<Vec<String>, crate::ConnectionError> {
80        let result = self.raw_query_read(|conn| {
81            dsl::pending_remove
82                .filter(dsl::group_id.eq(group_id))
83                .select(dsl::inbox_id)
84                .load::<String>(conn)
85        })?;
86
87        Ok(result)
88    }
89
90    fn get_user_pending_remove_status(
91        &self,
92        group_id: &[u8],
93        inbox_id: &str,
94    ) -> Result<bool, crate::ConnectionError> {
95        let result: bool = self.raw_query_read(|conn| {
96            select(exists(dsl::pending_remove.filter(
97                dsl::group_id.eq(group_id).and(dsl::inbox_id.eq(inbox_id)),
98            )))
99            .get_result::<bool>(conn)
100        })?;
101        Ok(result)
102    }
103
104    fn delete_pending_remove_users(
105        &self,
106        group_id: &[u8],
107        inbox_ids: Vec<String>,
108    ) -> Result<usize, crate::ConnectionError> {
109        let result = self.raw_query_write(|conn| {
110            diesel::delete(
111                dsl::pending_remove.filter(
112                    dsl::inbox_id
113                        .eq_any(inbox_ids)
114                        .and(dsl::group_id.eq(group_id)),
115                ),
116            )
117            .execute(conn)
118        })?;
119        Ok(result)
120    }
121}
122#[cfg(test)]
123mod tests {
124    use crate::encrypted_store::pending_remove::{PendingRemove, QueryPendingRemove};
125    use crate::{StoreOrIgnore, with_connection};
126
127    #[xmtp_common::test(unwrap_try = true)]
128    fn test_add_pending_remove() {
129        with_connection(|conn| {
130            // Break the chain by unsetting the originator.
131            PendingRemove {
132                inbox_id: "123".to_string(),
133                group_id: vec![1, 2, 3],
134                message_id: vec![1, 2, 3],
135            }
136            .store_or_ignore(conn)?;
137            let users = conn.get_pending_remove_users(&[1, 2, 3]).unwrap();
138            assert_eq!(users.len(), 1);
139            let users = conn.get_pending_remove_users(&[1]).unwrap();
140            assert_eq!(users.len(), 0);
141        })
142    }
143
144    #[xmtp_common::test(unwrap_try = true)]
145    fn test_delete_pending_remove_user() {
146        with_connection(|conn| {
147            // Break the chain by unsetting the originator.
148            PendingRemove {
149                inbox_id: "1".to_string(),
150                group_id: vec![1, 2, 3],
151                message_id: vec![1, 2, 3],
152            }
153            .store_or_ignore(conn)?;
154            PendingRemove {
155                inbox_id: "2".to_string(),
156                group_id: vec![1, 2, 3],
157                message_id: vec![1, 2, 3],
158            }
159            .store_or_ignore(conn)?;
160            PendingRemove {
161                inbox_id: "3".to_string(),
162                group_id: vec![1, 2, 3],
163                message_id: vec![1, 2, 3],
164            }
165            .store_or_ignore(conn)?;
166            let users = conn.get_pending_remove_users(&[1, 2, 3]).unwrap();
167            assert_eq!(users.len(), 3);
168            let deleted_users = conn
169                .delete_pending_remove_users(&[1, 2, 3], vec!["1".to_string(), "2".to_string()])
170                .unwrap();
171            assert_eq!(deleted_users, 2usize);
172            let users = conn.get_pending_remove_users(&[1, 2, 3]).unwrap();
173            assert_eq!(users.len(), 1);
174            let deleted_users = conn
175                .delete_pending_remove_users(&[1], vec!["3".to_string()])
176                .unwrap();
177            assert_eq!(deleted_users, 0usize);
178        })
179    }
180}