1use crate::event_api::event::Event;
2use crate::payloads::interactive::{InteractivePayload, SlashPayload};
3use serde::{Deserialize, Serialize};
4use serde_with::skip_serializing_none;
5
6#[derive(Deserialize, Serialize, Debug, PartialEq)]
7#[serde(tag = "type")]
8pub enum SocketModeEvent {
9 #[serde(rename = "hello")]
10 HelloEvent(HelloEvent),
11 #[serde(rename = "disconnect")]
12 DisconnectEvent(DisconnectEvent),
13 #[serde(rename = "events_api")]
14 EventsAPI(EventsAPI),
15 #[serde(rename = "interactive")]
16 InteractiveEvent(InteractiveEvent),
17 #[serde(rename = "slash_commands")]
18 SlashCommandsEvent(SlashCommandsEvent),
19}
20
21impl SocketModeEvent {
22 pub fn event_type(&self) -> SocketModeEventType {
23 match self {
24 SocketModeEvent::HelloEvent(HelloEvent { .. }) => SocketModeEventType::Hello,
25 SocketModeEvent::DisconnectEvent(DisconnectEvent { .. }) => {
26 SocketModeEventType::Disconnect
27 }
28 SocketModeEvent::EventsAPI(EventsAPI { .. }) => SocketModeEventType::EventsAPI,
29 SocketModeEvent::InteractiveEvent(InteractiveEvent { .. }) => {
30 SocketModeEventType::Interactive
31 }
32 SocketModeEvent::SlashCommandsEvent(SlashCommandsEvent { .. }) => {
33 SocketModeEventType::SlashCommands
34 }
35 }
36 }
37}
38
39#[derive(Deserialize, Serialize, Debug, PartialEq)]
40#[serde(rename_all = "snake_case")]
41pub enum SocketModeEventType {
42 Hello,
43 Disconnect,
44 EventsAPI,
45 Interactive,
46 SlashCommands,
47}
48
49#[skip_serializing_none]
50#[derive(Deserialize, Serialize, Debug, Default, PartialEq)]
51pub struct DebugInfo {
52 pub host: Option<String>,
53 pub started: Option<String>,
54 pub build_number: Option<i32>,
55 pub approximate_connection_time: Option<i32>,
56}
57
58#[skip_serializing_none]
59#[derive(Deserialize, Serialize, Debug, PartialEq)]
60pub struct HelloEvent {
61 pub connection_info: Option<ConnectionInfo>,
62 pub num_connections: Option<i32>,
63 pub debug_info: Option<DebugInfo>,
64}
65
66#[skip_serializing_none]
67#[derive(Deserialize, Serialize, Debug, Default, PartialEq)]
68pub struct ConnectionInfo {
69 pub app_id: Option<String>,
70}
71
72#[skip_serializing_none]
73#[derive(Deserialize, Serialize, Debug, PartialEq)]
74pub struct DisconnectEvent {
75 pub reason: DisconnectReason,
76 pub debug_info: Option<DebugInfo>,
77}
78
79#[derive(Deserialize, Serialize, Debug, PartialEq)]
80#[serde(rename_all = "snake_case")]
81pub enum DisconnectReason {
82 LinkDisabled,
83 Warning,
84 RefreshRequested,
85}
86
87#[skip_serializing_none]
88#[derive(Deserialize, Serialize, Debug, PartialEq)]
89pub struct EventsAPI {
90 pub envelope_id: String,
91 pub accepts_response_payload: bool,
92 pub payload: Event,
93}
94
95#[skip_serializing_none]
96#[derive(Deserialize, Serialize, Debug, PartialEq)]
97pub struct InteractiveEvent {
98 pub envelope_id: String,
99 pub accepts_response_payload: bool,
100 pub payload: InteractivePayload,
101}
102
103#[skip_serializing_none]
104#[derive(Deserialize, Serialize, Debug, PartialEq)]
105pub struct SlashCommandsEvent {
106 pub envelope_id: String,
107 pub accepts_response_payload: bool,
108 pub payload: SlashPayload,
109}
110
111#[skip_serializing_none]
112#[derive(Deserialize, Serialize, Debug, PartialEq)]
113pub struct AcknowledgeMessage<'s> {
114 pub envelope_id: &'s str,
115}
116
117#[cfg(test)]
118mod test {
119 use super::*;
120 use crate::payloads::interactive::InteractiveEventType;
121
122 #[test]
123 fn deserialize_hello_event() {
124 let json = r##"{
125 "type": "hello",
126 "connection_info": {
127 "app_id": "app_id"
128 },
129 "num_connections": 1,
130 "debug_info": {
131 "host": "host"
132 }
133}"##;
134 let event = serde_json::from_str::<SocketModeEvent>(json).unwrap();
135 match event {
136 SocketModeEvent::HelloEvent(HelloEvent {
137 connection_info,
138 num_connections,
139 debug_info,
140 }) => {
141 assert_eq!(connection_info.unwrap().app_id.unwrap(), "app_id");
142 assert_eq!(num_connections.unwrap(), 1);
143 assert_eq!(debug_info.unwrap().host.unwrap(), "host");
144 }
145 _ => panic!("Event deserialize into incorrect variant"),
146 }
147 }
148
149 #[test]
150 fn deserialize_disconnect_event() {
151 let json = r##"{
152 "type": "disconnect",
153 "reason": "link_disabled",
154 "debug_info": {
155 "host": "wss-111.slack.com"
156 }
157}"##;
158 let event = serde_json::from_str::<SocketModeEvent>(json).unwrap();
159 match event {
160 SocketModeEvent::DisconnectEvent(DisconnectEvent { reason, debug_info }) => {
161 assert_eq!(reason, DisconnectReason::LinkDisabled);
162 assert_eq!(debug_info.unwrap().host.unwrap(), "wss-111.slack.com");
163 }
164 _ => panic!("Event deserialize into incorrect variant"),
165 }
166 }
167
168 #[test]
169 fn deserialize_events_api() {
170 let json = r##"{
171 "type": "events_api",
172 "envelope_id": "dbdd0ef3-1543-4f94-bfb4-133d0e6c1545",
173 "accepts_response_payload": false,
174 "payload": {
175 "type": "app_home_opened",
176 "user": "U061F7AUR",
177 "channel": "D0LAN2Q65",
178 "event_ts": "1515449522000016",
179 "tab": "home",
180 "view": {
181 "id": "VPASKP233"
182 }
183 }
184}"##;
185 let event = serde_json::from_str::<SocketModeEvent>(json).unwrap();
186 match event {
187 SocketModeEvent::EventsAPI(EventsAPI {
188 envelope_id,
189 accepts_response_payload,
190 payload,
191 }) => {
192 assert_eq!(envelope_id, "dbdd0ef3-1543-4f94-bfb4-133d0e6c1545");
193 assert!(!accepts_response_payload, "false");
194
195 match payload {
196 Event::AppHomeOpened { user, .. } => {
197 assert_eq!(user, "U061F7AUR");
198 }
199 _ => panic!("Payload deserialize into incorrect variant"),
200 }
201 }
202 _ => panic!("Event deserialize into incorrect variant"),
203 }
204 }
205
206 #[test]
207 fn deserialize_interactive_event() {
208 let json = r##"{
209 "type": "interactive",
210 "envelope_id": "dbdd0ef3-1543-4f94-bfb4-133d0e6c1545",
211 "accepts_response_payload": true,
212 "payload": {
213 "type": "view_submission"
214 }
215}"##;
216 let event = serde_json::from_str::<SocketModeEvent>(json).unwrap();
217 match event {
218 SocketModeEvent::InteractiveEvent(InteractiveEvent {
219 envelope_id,
220 accepts_response_payload,
221 payload,
222 }) => {
223 assert_eq!(envelope_id, "dbdd0ef3-1543-4f94-bfb4-133d0e6c1545");
224 assert!(accepts_response_payload, "true");
225 assert_eq!(payload.type_filed, InteractiveEventType::ViewSubmission);
226 }
227 _ => panic!("Event deserialize into incorrect variant"),
228 }
229 }
230
231 #[test]
232 fn deserialize_slash_commands_event() {
233 let json = r##"{
234 "type": "slash_commands",
235 "envelope_id": "dbdd0ef3-1543-4f94-bfb4-133d0e6c1545",
236 "accepts_response_payload": true,
237 "payload": {
238 "token": "bHKJ2n9AW6Ju3MjciOHfbA1b"
239 }
240}"##;
241 let event = serde_json::from_str::<SocketModeEvent>(json).unwrap();
242 match event {
243 SocketModeEvent::SlashCommandsEvent(SlashCommandsEvent {
244 envelope_id,
245 accepts_response_payload,
246 payload,
247 }) => {
248 assert_eq!(envelope_id, "dbdd0ef3-1543-4f94-bfb4-133d0e6c1545");
249 assert!(accepts_response_payload, "true");
250 assert_eq!(payload.token.unwrap(), "bHKJ2n9AW6Ju3MjciOHfbA1b");
251 }
252 _ => panic!("Event deserialize into incorrect variant"),
253 }
254 }
255
256 #[test]
257 fn serialize_acknowledge_message() {
258 let json = serde_json::to_string_pretty(&AcknowledgeMessage {
259 envelope_id: "xxxxxxxxxxxxxxxxxxxxx",
260 })
261 .unwrap();
262
263 let expect = r##"{
264 "envelope_id": "xxxxxxxxxxxxxxxxxxxxx"
265}"##;
266
267 assert_eq!(expect, json);
268 }
269}