summaryrefslogblamecommitdiff
path: root/src/mattermost/model.rs
blob: b8090c8a60a8743a3c6d339c95680fb55a24e9df (plain) (tree)
1
2
3
4
5
6
7
8
9


                              

                       
                     
                     
                        
 
                                                   




                            
                                                   



                           

                                                   
                      













                                                     
 

                                                   


                             
                                                   

                            
                                     


                       
                                                   

                                         

                              





                                     





                                       



                                                 

 

                                                   



                                     

                                                    
                              
                                                    

                   
 
                                                   
                 
                   




                        

                              



                              
                               
                         
                                   






                             





























                                                   








                                   
                                                                                          
































































































                                                                                                                                                                                                                   








                                                     
































                                                                            

                                                 






























                                                                                                                                                                                                                   

                                                 











                                                         



                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        


                                                                                                                                                                                                                                                     
     
 
use serde_with::serde_as;
use std::collections::HashMap;

use serde::Deserialize;
type PostId = String;
type TeamId = String;
type UserId = String;
type ChannelId = String;

#[derive(Deserialize, Debug, PartialEq, Eq, Clone)]
#[serde(untagged)]
pub enum WebsocketMessage {
    Update(WebsocketUpdate),
    Reply(WebsocketReply),
}
#[derive(Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct WebsocketReply {
    pub status: String,
    pub seq_reply: u32,
}
#[serde_as]
#[derive(Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct Broadcast {
    #[serde_as(as = "serde_with::NoneAsEmptyString")]
    pub channel_id: Option<ChannelId>,
    #[serde_as(as = "serde_with::NoneAsEmptyString")]
    pub connection_id: Option<String>,
    #[serde_as(as = "serde_with::NoneAsEmptyString")]
    pub omit_connection_id: Option<String>,
    #[serde(default)]
    pub omit_users: Option<HashMap<UserId, bool>>,
    #[serde_as(as = "serde_with::NoneAsEmptyString")]
    pub team_id: Option<TeamId>,
    #[serde_as(as = "serde_with::NoneAsEmptyString")]
    pub user_id: Option<UserId>,
    #[serde(default)]
    pub contains_sanitized_data: bool,
}
#[derive(Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct WebsocketUpdateFields {
    pub broadcast: Broadcast,
    pub seq: u32,
}
#[derive(Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct WebsocketUpdate {
    #[serde(flatten)]
    pub stuff: WebsocketUpdateFields,
    #[serde(flatten)]
    pub update: Update,
}
#[derive(Deserialize, Debug, PartialEq, Eq, Clone)]
#[serde(tag = "event", content = "data")]
pub enum Update {
    #[serde(rename = "hello")]
    Hello(Hello),
    #[serde(rename = "new_user")]
    NewUser { user_id: UserId },
    #[serde(rename = "user_updated")]
    UserUpdated(UserUpdated),
    #[serde(rename = "posted")]
    Posted(Posted),
    #[serde(rename = "status_change")]
    StatusChange {},
    #[serde(rename = "typing")]
    Typing {},
    #[serde(rename = "channel_viewed")]
    ChannelViewed {},
    #[serde(rename = "multiple_channels_viewed")]
    MultipleChannelViewed {},
    // #[serde(other)]
    // Other,
}

#[serde_as]
#[derive(Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct Posted {
    pub channel_display_name: String,
    pub channel_name: String,
    pub channel_type: String,
    #[serde_as(as = "serde_with::json::JsonString")]
    #[serde(default)]
    pub mentions: Vec<String>,
    #[serde_as(as = "serde_with::json::JsonString")]
    pub post: Post,
}

#[derive(Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct Post {
    pub id: PostId,
    pub create_at: u64,
    pub update_at: u64,
    pub edit_at: u64,
    pub delete_at: u64,
    pub is_pinned: bool,
    pub user_id: UserId,
    pub channel_id: ChannelId,
    // pub hashtags: String,
    // pub last_reply_at: u64,
    pub message: String,
    // pub metadata:,
    // pub original_id: PostId,
    // pub participants:,
    // pub pending_post_id: PostId,
    // pub preops
    // pub reply_count: u64,
    pub root_id: String,
    #[serde(rename = "type")]
    pub ttype: String,
}

#[derive(Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct UserUpdated {
    pub id: UserId,
    pub create_at: u64,
    pub update_at: u64,
    pub delete_at: u64,
    pub username: String,
    pub authdata: String,
    pub auth_service: String,
    pub email: String,
    pub nickname: String,
    pub first_name: String,
    pub last_name: String,
    pub position: String,
    pub roles: String,
    pub locale: String,
    pub timezone: Timezone,
    pub disable_welcome_email: bool,
}

#[derive(Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct Timezone {
    #[serde(rename = "automaticTimezone")]
    pub automatic_timezone: String,
    #[serde(rename = "manualTimezone")]
    pub manual_timezone: String,
    #[serde(rename = "useAutomaticTimezone")]
    pub use_automatic_timezone: String,
}
#[derive(Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct Hello {
    pub connection_id: String,
    pub server_version: String,
}
#[cfg(test)]
mod tests {
    use serde_json::Value;

    use crate::mattermost::model::{
        Broadcast, Post, Update, WebsocketMessage, WebsocketUpdate, WebsocketUpdateFields,
    };
    #[test]
    fn parse_post() {
        let post = Post {
            id: "a".to_owned(),
            create_at: 1,
            update_at: 2,
            edit_at: 3,
            delete_at: 4,
            is_pinned: true,
            user_id: "b".to_owned(),
            channel_id: "c".to_owned(),
            message: "d".to_owned(),
            root_id: "e".to_owned(),
            ttype: "f".to_owned(),
        };
        let post_str = r#"{
                    "id":"a",
                    "create_at":1,
                    "update_at":2,
                    "edit_at":3,
                    "delete_at":4,
                    "is_pinned":true,
                    "user_id":"b",
                    "channel_id":"c",
                    "message":"d",
                    "root_id":"e",
                    "type":"f"
                }"#;
        let d: Result<Post, _> = serde_json::from_str(post_str);
        assert_eq!(d.unwrap(), post);
        let data = format!(
            "\"{}\"",
            post_str.replace('\n', "\\n").replace('\"', "\\\"")
        );
        let x: Value = serde_json::from_str(&data).unwrap();
        let s = x.as_str().unwrap();
        let d: Result<Post, _> = serde_json::from_str(s);
        assert_eq!(d.unwrap(), post);
    }

    #[test]
    fn parse_update() {
        let post = Post {
            id: "a".to_owned(),
            create_at: 1,
            update_at: 2,
            edit_at: 3,
            delete_at: 4,
            is_pinned: true,
            user_id: "b".to_owned(),
            channel_id: "c".to_owned(),
            message: "d".to_owned(),
            root_id: "e".to_owned(),
            ttype: "f".to_owned(),
        };
        let data = r#"
        {
            "data":{
                "connection_id":  "aaaa",
                "server_version": "2137"
            },
            "event":"hello"
        }"#;
        let update: Result<Update, _> = serde_json::from_str(data);
        assert_eq!(
            update.unwrap(),
            Update::Hello(super::Hello {
                connection_id: "aaaa".to_owned(),
                server_version: "2137".to_owned()
            })
        );
        let data = r#"
        {
            "data":{
                "channel_display_name":"a",
                "channel_name":"b",
                "channel_type":"c",
                "mentions":"[\"d\"]",
                "post": "{\"id\":\"a\",\"create_at\":1,\"update_at\":2,\"edit_at\":3,\"delete_at\":4,\"is_pinned\":true,\"user_id\":\"b\",\"channel_id\":\"c\",\"message\":\"d\",\"root_id\":\"e\",\"type\":\"f\"}"
            },
            "event":"posted"
        }"#;
        let update: Result<Update, _> = serde_json::from_str(&data);
        assert_eq!(
            update.unwrap(),
            Update::Posted(super::Posted {
                channel_display_name: "a".to_owned(),
                channel_name: "b".to_owned(),
                channel_type: "c".to_owned(),
                mentions: vec!["d".to_owned()],
                post: post
            })
        );
    }
    #[test]
    fn parse_websocketupdate() {
        let broadcast = Broadcast {
            channel_id: Some("a".to_owned()),
            connection_id: Some("b".to_owned()),
            omit_connection_id: Some("c".to_owned()),
            team_id: Some("d".to_owned()),
            user_id: Some("e".to_owned()),
            omit_users: None,
            contains_sanitized_data: false,
        };
        let post = Post {
            id: "a".to_owned(),
            create_at: 1,
            update_at: 2,
            edit_at: 3,
            delete_at: 4,
            is_pinned: true,
            user_id: "b".to_owned(),
            channel_id: "c".to_owned(),
            message: "d".to_owned(),
            root_id: "e".to_owned(),
            ttype: "f".to_owned(),
        };
        let data = r#"
        {
            "broadcast":{
                "channel_id":"a",
                "connection_id":"b",
                "omit_connection_id":"c",
                "team_id":"d",
                "user_id":"e"
            },
            "data":{
                "connection_id":  "aaaa",
                "server_version": "2137"
            },
            "event":"hello",
            "seq":2137
        }"#;
        let update: Result<WebsocketUpdate, _> = serde_json::from_str(data);
        assert_eq!(
            update.unwrap(),
            WebsocketUpdate {
                stuff: WebsocketUpdateFields {
                    broadcast: broadcast.clone(),
                    seq: 2137,
                },
                update: Update::Hello(super::Hello {
                    connection_id: "aaaa".to_owned(),
                    server_version: "2137".to_owned()
                })
            }
        );
        let data = r#"
        {
            "broadcast":{
                "channel_id":"a",
                "connection_id":"b",
                "omit_connection_id":"c",
                "team_id":"d",
                "user_id":"e"
            },
            "data":{
                "channel_display_name":"a",
                "channel_name":"b",
                "channel_type":"c",
                "mentions":"[\"d\"]",
                "post": "{\"id\":\"a\",\"create_at\":1,\"update_at\":2,\"edit_at\":3,\"delete_at\":4,\"is_pinned\":true,\"user_id\":\"b\",\"channel_id\":\"c\",\"message\":\"d\",\"root_id\":\"e\",\"type\":\"f\"}"
            },
            "event":"posted",
            "seq": 2137
        }"#;
        let update: Result<WebsocketUpdate, _> = serde_json::from_str(&data);
        assert_eq!(
            update.unwrap(),
            WebsocketUpdate {
                stuff: WebsocketUpdateFields {
                    broadcast: broadcast.clone(),
                    seq: 2137,
                },
                update: Update::Posted(super::Posted {
                    channel_display_name: "a".to_owned(),
                    channel_name: "b".to_owned(),
                    channel_type: "c".to_owned(),
                    mentions: vec!["d".to_owned()],
                    post: post
                })
            }
        );
    }
    #[test]
    fn parse_realexamples() {
        let data = r#"{"event":"posted","data":{"channel_display_name":"a","channel_name":"a","channel_type":"O","post":"{\"id\":\"b\",\"create_at\":1,\"update_at\":2,\"edit_at\":0,\"delete_at\":0,\"is_pinned\":false,\"user_id\":\"b\",\"channel_id\":\"c\",\"root_id\":\"\",\"original_id\":\"\",\"message\":\"blah\",\"type\":\"\",\"props\":{\"disable_group_highlight\":true},\"hashtags\":\"\",\"pending_post_id\":\"d\",\"reply_count\":0,\"last_reply_at\":0,\"participants\":null,\"metadata\":{}}","sender_name":"@blah","set_online":true,"team_id":"e"},"broadcast":{"omit_users":null,"user_id":"","channel_id":"f","team_id":"","connection_id":"","omit_connection_id":""},"seq":3}"#;

        serde_json::from_str::<WebsocketMessage>(data).unwrap();
        let data = r#"{"event": "multiple_channels_viewed", "data": {"channel_times":{"aaaa":1706319279901}}, "broadcast": {"omit_users":null,"user_id":"bbbb","channel_id":"","team_id":"","connection_id":"","omit_connection_id":""}, "seq": 6}"#;
        serde_json::from_str::<WebsocketMessage>(data).unwrap();
    }
}