slack_messaging/blocks/elements/multi_select_menus/
external_data_source.rs

1use crate::composition_objects::{ConfirmationDialog, Opt, Plain, Text};
2use crate::validators::*;
3
4use serde::Serialize;
5use slack_messaging_derive::Builder;
6
7/// [Multi select menu of external data source](https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#external_multi_select)
8/// representation
9///
10/// # Fields and Validations
11///
12/// For more details, see the [official
13/// documentation](https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element#external_multi_select).
14///
15/// | Field | Type | Required | Validation |
16/// |-------|------|----------|------------|
17/// | action_id | String | No | Max length 255 characters |
18/// | min_query_length | i64 | No | N/A |
19/// | initial_options | Vec<[Opt]> | No | N/A |
20/// | confirm | [ConfirmationDialog] | No | N/A |
21/// | max_selected_items | i64 | No | Min value 1 |
22/// | focus_on_load | bool | No | N/A |
23/// | placeholder | [Text]<[Plain]> | No | Max length 150 characters |
24///
25/// # Example
26///
27/// ```
28/// use slack_messaging::plain_text;
29/// use slack_messaging::blocks::elements::MultiSelectMenuExternalDataSource;
30/// # use std::error::Error;
31///
32/// # fn try_main() -> Result<(), Box<dyn Error>> {
33/// let menu = MultiSelectMenuExternalDataSource::builder()
34///     .action_id("text1234")
35///     .min_query_length(3)
36///     .placeholder(plain_text!("Select items")?)
37///     .build()?;
38///
39/// let expected = serde_json::json!({
40///     "type": "multi_external_select",
41///     "action_id": "text1234",
42///     "min_query_length": 3,
43///     "placeholder": {
44///         "type": "plain_text",
45///         "text": "Select items"
46///     }
47/// });
48///
49/// let json = serde_json::to_value(menu).unwrap();
50///
51/// assert_eq!(json, expected);
52///
53/// // If your object has any validation errors, the build method returns Result::Err
54/// let menu = MultiSelectMenuExternalDataSource::builder()
55///     .action_id("text1234")
56///     .min_query_length(3)
57///     .placeholder(plain_text!("Select items")?)
58///     .max_selected_items(0)
59///     .build();
60///
61/// assert!(menu.is_err());
62/// #     Ok(())
63/// # }
64/// # fn main() {
65/// #     try_main().unwrap()
66/// # }
67/// ```
68#[derive(Debug, Default, Clone, Serialize, PartialEq, Builder)]
69#[serde(tag = "type", rename = "multi_external_select")]
70pub struct MultiSelectMenuExternalDataSource {
71    #[serde(skip_serializing_if = "Option::is_none")]
72    #[builder(validate("text::max_255"))]
73    pub(crate) action_id: Option<String>,
74
75    #[serde(skip_serializing_if = "Option::is_none")]
76    pub(crate) min_query_length: Option<i64>,
77
78    #[serde(skip_serializing_if = "Option::is_none")]
79    #[builder(push_item = "initial_option")]
80    pub(crate) initial_options: Option<Vec<Opt>>,
81
82    #[serde(skip_serializing_if = "Option::is_none")]
83    pub(crate) confirm: Option<ConfirmationDialog>,
84
85    #[serde(skip_serializing_if = "Option::is_none")]
86    #[builder(validate("integer::min_1"))]
87    pub(crate) max_selected_items: Option<i64>,
88
89    #[serde(skip_serializing_if = "Option::is_none")]
90    pub(crate) focus_on_load: Option<bool>,
91
92    #[serde(skip_serializing_if = "Option::is_none")]
93    #[builder(validate("text_object::max_150"))]
94    pub(crate) placeholder: Option<Text<Plain>>,
95}
96
97#[cfg(test)]
98mod tests {
99    use super::*;
100    use crate::composition_objects::test_helpers::*;
101    use crate::errors::*;
102
103    #[test]
104    fn it_implements_builder() {
105        let expected = MultiSelectMenuExternalDataSource {
106            action_id: Some("multi_select_0".into()),
107            min_query_length: Some(3),
108            initial_options: Some(vec![option("opt0", "val0")]),
109            confirm: Some(confirm()),
110            max_selected_items: Some(2),
111            focus_on_load: Some(true),
112            placeholder: Some(plain_text("Select items")),
113        };
114
115        let val = MultiSelectMenuExternalDataSource::builder()
116            .set_action_id(Some("multi_select_0"))
117            .set_min_query_length(Some(3))
118            .set_initial_options(Some(vec![option("opt0", "val0")]))
119            .set_confirm(Some(confirm()))
120            .set_max_selected_items(Some(2))
121            .set_focus_on_load(Some(true))
122            .set_placeholder(Some(plain_text("Select items")))
123            .build()
124            .unwrap();
125
126        assert_eq!(val, expected);
127
128        let val = MultiSelectMenuExternalDataSource::builder()
129            .action_id("multi_select_0")
130            .min_query_length(3)
131            .initial_options(vec![option("opt0", "val0")])
132            .confirm(confirm())
133            .max_selected_items(2)
134            .focus_on_load(true)
135            .placeholder(plain_text("Select items"))
136            .build()
137            .unwrap();
138
139        assert_eq!(val, expected);
140    }
141
142    #[test]
143    fn it_implements_push_item_method() {
144        let expected = MultiSelectMenuExternalDataSource {
145            action_id: None,
146            min_query_length: None,
147            initial_options: Some(vec![option("opt0", "val0")]),
148            confirm: None,
149            max_selected_items: None,
150            focus_on_load: None,
151            placeholder: None,
152        };
153
154        let val = MultiSelectMenuExternalDataSource::builder()
155            .initial_option(option("opt0", "val0"))
156            .build()
157            .unwrap();
158
159        assert_eq!(val, expected);
160    }
161
162    #[test]
163    fn it_requires_action_id_less_than_255_characters_long() {
164        let err = MultiSelectMenuExternalDataSource::builder()
165            .action_id("a".repeat(256))
166            .build()
167            .unwrap_err();
168        assert_eq!(err.object(), "MultiSelectMenuExternalDataSource");
169
170        let errors = err.field("action_id");
171        assert!(errors.includes(ValidationErrorKind::MaxTextLength(255)));
172    }
173
174    #[test]
175    fn it_requires_max_selected_items_greater_than_1() {
176        let err = MultiSelectMenuExternalDataSource::builder()
177            .max_selected_items(0)
178            .build()
179            .unwrap_err();
180        assert_eq!(err.object(), "MultiSelectMenuExternalDataSource");
181
182        let errors = err.field("max_selected_items");
183        assert!(errors.includes(ValidationErrorKind::MinIntegerValue(1)));
184    }
185
186    #[test]
187    fn it_requires_placeholder_text_less_than_150_characters_long() {
188        let err = MultiSelectMenuExternalDataSource::builder()
189            .placeholder(plain_text("a".repeat(151)))
190            .build()
191            .unwrap_err();
192        assert_eq!(err.object(), "MultiSelectMenuExternalDataSource");
193
194        let errors = err.field("placeholder");
195        assert!(errors.includes(ValidationErrorKind::MaxTextLength(150)));
196    }
197}