slack_morphism/models/blocks/
view.rs

1use crate::blocks::kit::SlackBlock;
2use crate::blocks::{SlackBlockId, SlackBlockPlainText, SlackBlockPlainTextOnly};
3use crate::SlackCallbackId;
4use crate::*;
5use rsb_derive::Builder;
6use serde::{Deserialize, Serialize};
7use serde_with::{serde_as, skip_serializing_none};
8use std::collections::HashMap;
9
10#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
11#[serde(tag = "type")]
12pub enum SlackView {
13    #[serde(rename = "home")]
14    Home(SlackHomeView),
15    #[serde(rename = "modal")]
16    Modal(SlackModalView),
17}
18
19#[serde_as]
20#[skip_serializing_none]
21#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
22pub struct SlackHomeView {
23    pub blocks: Vec<SlackBlock>,
24    #[serde(default)]
25    #[serde_as(as = "serde_with::NoneAsEmptyString")]
26    pub private_metadata: Option<String>,
27    #[serde(default)]
28    #[serde_as(as = "serde_with::NoneAsEmptyString")]
29    pub callback_id: Option<SlackCallbackId>,
30    #[serde(default)]
31    #[serde_as(as = "serde_with::NoneAsEmptyString")]
32    pub external_id: Option<String>,
33}
34
35#[serde_as]
36#[skip_serializing_none]
37#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
38pub struct SlackModalView {
39    pub title: SlackBlockPlainTextOnly,
40    pub blocks: Vec<SlackBlock>,
41    pub close: Option<SlackBlockPlainTextOnly>,
42    pub submit: Option<SlackBlockPlainTextOnly>,
43    #[serde(default)]
44    #[serde_as(as = "serde_with::NoneAsEmptyString")]
45    pub private_metadata: Option<String>,
46    #[serde(default)]
47    #[serde_as(as = "serde_with::NoneAsEmptyString")]
48    pub callback_id: Option<SlackCallbackId>,
49    pub clear_on_close: Option<bool>,
50    pub notify_on_close: Option<bool>,
51    pub hash: Option<String>,
52    #[serde(default)]
53    #[serde_as(as = "serde_with::NoneAsEmptyString")]
54    pub external_id: Option<String>,
55}
56
57#[skip_serializing_none]
58#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
59pub struct SlackStatefulView {
60    #[serde(flatten)]
61    pub state_params: SlackStatefulStateParams,
62    #[serde(flatten)]
63    pub view: SlackView,
64}
65
66#[skip_serializing_none]
67#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
68pub struct SlackStatefulStateParams {
69    pub id: SlackViewId,
70    pub team_id: SlackTeamId,
71    pub state: Option<SlackViewState>,
72    pub hash: String,
73    pub previous_view_id: Option<SlackViewId>,
74    pub root_view_id: Option<SlackViewId>,
75    pub app_id: Option<SlackAppId>,
76    pub bot_id: Option<SlackBotId>,
77}
78
79#[skip_serializing_none]
80#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
81pub struct SlackViewState {
82    pub values: HashMap<SlackBlockId, HashMap<SlackActionId, SlackViewStateValue>>,
83}
84
85#[skip_serializing_none]
86#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
87pub struct SlackViewStateValue {
88    #[serde(rename = "type")]
89    pub action_type: SlackActionType,
90    pub value: Option<String>,
91    pub selected_date: Option<String>,
92    pub selected_time: Option<String>,
93    pub selected_date_time: Option<SlackDateTime>,
94    pub selected_conversation: Option<SlackConversationId>,
95    pub selected_channel: Option<SlackChannelId>,
96    pub selected_user: Option<SlackUserId>,
97    pub selected_option: Option<SlackViewStateValueSelectedOption>,
98    pub selected_conversations: Option<Vec<SlackConversationId>>,
99    pub selected_users: Option<Vec<SlackUserId>>,
100    pub selected_options: Option<Vec<SlackViewStateValueSelectedOption>>,
101}
102
103pub type SlackActionState = SlackViewState;
104pub type SlackActionStateValue = SlackViewStateValue;
105
106#[skip_serializing_none]
107#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
108pub struct SlackViewStateValueSelectedOption {
109    pub text: SlackBlockPlainText,
110    pub value: String,
111}
112
113#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
114#[serde(tag = "response_action")]
115pub enum SlackViewSubmissionResponse {
116    #[serde(rename = "clear")]
117    Clear(SlackViewSubmissionClearResponse),
118    #[serde(rename = "update")]
119    Update(SlackViewSubmissionUpdateResponse),
120    #[serde(rename = "push")]
121    Push(SlackViewSubmissionPushResponse),
122    #[serde(rename = "errors")]
123    Errors(SlackViewSubmissionErrorsResponse),
124}
125
126#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
127pub struct SlackViewSubmissionClearResponse {}
128
129#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
130pub struct SlackViewSubmissionUpdateResponse {
131    pub view: SlackView,
132}
133
134#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
135pub struct SlackViewSubmissionPushResponse {
136    pub view: SlackView,
137}
138
139#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
140pub struct SlackViewSubmissionErrorsResponse {
141    pub errors: HashMap<String, String>,
142}
143
144#[cfg(test)]
145mod test {
146    use super::*;
147
148    #[test]
149    fn test_slack_view_submission_clear_response_serialization() {
150        let output = serde_json::to_string(&SlackViewSubmissionResponse::Clear(
151            SlackViewSubmissionClearResponse::new(),
152        ))
153        .unwrap();
154        assert_eq!(output, r#"{"response_action":"clear"}"#);
155    }
156
157    #[test]
158    fn test_slack_api_apps_manifest_create_request() {
159        let payload = include_str!("./fixtures/slack_home_view.json");
160        let model: SlackHomeView = serde_json::from_str(payload).unwrap();
161        assert!(model.private_metadata.is_none());
162        assert_eq!(
163            model.callback_id,
164            Some(SlackCallbackId::from("test-callback-id"))
165        );
166    }
167}