slack_messaging/blocks/
video.rs

1use crate::composition_objects::{Plain, Text};
2use crate::validators::*;
3
4use serde::Serialize;
5use slack_messaging_derive::Builder;
6
7/// [Video block](https://docs.slack.dev/reference/block-kit/blocks/video-block)
8/// representation.
9///
10/// # Fields and Validations
11///
12/// For more details, see the [official
13/// documentation](https://docs.slack.dev/reference/block-kit/blocks/video-block).
14///
15/// | Field | Type | Required | Validation |
16/// |-------|------|----------|------------|
17/// | alt_text | String | Yes | N/A |
18/// | author_name | String | No | Maximum 50 characters |
19/// | block_id | String | No | Maximum 255 characters |
20/// | description | [Text]<[Plain]> | No | Maximum 200 characters |
21/// | provider_icon_url | String | No | N/A |
22/// | provider_name | String | No | N/A |
23/// | title | [Text]<[Plain]> | Yes | Maximum 200 characters |
24/// | title_url | String | No | N/A |
25/// | thumbnail_url | String | Yes | N/A |
26/// | video_url | String | Yes | N/A |
27///
28/// # Example
29///
30/// ```
31/// use slack_messaging::plain_text;
32/// use slack_messaging::blocks::Video;
33/// # use std::error::Error;
34///
35/// # fn try_main() -> Result<(), Box<dyn Error>> {
36/// let video = Video::builder()
37///     .title(plain_text!("How to use Slack.")?)
38///     .description(plain_text!("Slack is a new way to communicate with your team. It's faster, better organized and more secure than email.")?)
39///     .title_url("https://www.youtube.com/watch?v=RRxQQxiM7AA")
40///     .video_url("https://www.youtube.com/embed/RRxQQxiM7AA?feature=oembed&autoplay=1")
41///     .thumbnail_url("https://i.ytimg.com/vi/RRxQQxiM7AA/hqdefault.jpg")
42///     .alt_text("How to use Slack?")
43///     .author_name("Arcado Buendia")
44///     .provider_name("YouTube")
45///     .provider_icon_url("https://a.slack-edge.com/80588/img/unfurl_icons/youtube.png")
46///     .build()?;
47///
48/// let expected = serde_json::json!({
49///     "type": "video",
50///     "title": {
51///         "type": "plain_text",
52///         "text": "How to use Slack."
53///     },
54///     "description": {
55///         "type": "plain_text",
56///         "text": "Slack is a new way to communicate with your team. It's faster, better organized and more secure than email."
57///     },
58///     "title_url": "https://www.youtube.com/watch?v=RRxQQxiM7AA",
59///     "video_url": "https://www.youtube.com/embed/RRxQQxiM7AA?feature=oembed&autoplay=1",
60///     "thumbnail_url": "https://i.ytimg.com/vi/RRxQQxiM7AA/hqdefault.jpg",
61///     "alt_text": "How to use Slack?",
62///     "author_name": "Arcado Buendia",
63///     "provider_name": "YouTube",
64///     "provider_icon_url": "https://a.slack-edge.com/80588/img/unfurl_icons/youtube.png"
65/// });
66///
67/// let json = serde_json::to_value(video).unwrap();
68///
69/// assert_eq!(json, expected);
70/// #     Ok(())
71/// # }
72/// # fn main() {
73/// #     try_main().unwrap()
74/// # }
75/// ```
76#[derive(Debug, Clone, Serialize, PartialEq, Builder)]
77#[serde(tag = "type", rename = "video")]
78pub struct Video {
79    #[builder(validate("required"))]
80    pub(crate) alt_text: Option<String>,
81
82    #[serde(skip_serializing_if = "Option::is_none")]
83    #[builder(validate("text::max_50"))]
84    pub(crate) author_name: Option<String>,
85
86    #[serde(skip_serializing_if = "Option::is_none")]
87    #[builder(validate("text::max_255"))]
88    pub(crate) block_id: Option<String>,
89
90    #[serde(skip_serializing_if = "Option::is_none")]
91    #[builder(validate("text_object::max_200"))]
92    pub(crate) description: Option<Text<Plain>>,
93
94    #[serde(skip_serializing_if = "Option::is_none")]
95    pub(crate) provider_icon_url: Option<String>,
96
97    #[serde(skip_serializing_if = "Option::is_none")]
98    pub(crate) provider_name: Option<String>,
99
100    #[builder(validate("required", "text_object::max_200"))]
101    pub(crate) title: Option<Text<Plain>>,
102
103    #[serde(skip_serializing_if = "Option::is_none")]
104    pub(crate) title_url: Option<String>,
105
106    #[builder(validate("required"))]
107    pub(crate) thumbnail_url: Option<String>,
108
109    #[builder(validate("required"))]
110    pub(crate) video_url: Option<String>,
111}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116    use crate::composition_objects::test_helpers::*;
117    use crate::errors::*;
118
119    #[test]
120    fn it_implements_builder() {
121        let expected = Video {
122            alt_text: Some("Use the Events API to create a dynamic App Home".into()),
123            author_name: Some("foo".into()),
124            block_id: Some("video_0".into()),
125            description: Some(plain_text("Slack sure is nifty!")),
126            provider_icon_url: Some("bar".into()),
127            provider_name: Some("baz".into()),
128            title: Some(plain_text(
129                "Use the Events API to create a dynamic App Home",
130            )),
131            title_url: Some("https://www.youtube.com/watch?v=8876OZV_Yy0".into()),
132            thumbnail_url: Some("https://i.ytimg.com/vi/8876OZV_Yy0/hqdefault.jpg".into()),
133            video_url: Some(
134                "https://www.youtube.com/embed/8876OZV_Yy0?feature=oembed&autoplay=1".into(),
135            ),
136        };
137
138        let val = Video::builder()
139            .set_alt_text(Some("Use the Events API to create a dynamic App Home"))
140            .set_author_name(Some("foo"))
141            .set_block_id(Some("video_0"))
142            .set_description(Some(plain_text("Slack sure is nifty!")))
143            .set_provider_icon_url(Some("bar"))
144            .set_provider_name(Some("baz"))
145            .set_title(Some(plain_text(
146                "Use the Events API to create a dynamic App Home",
147            )))
148            .set_title_url(Some("https://www.youtube.com/watch?v=8876OZV_Yy0"))
149            .set_thumbnail_url(Some("https://i.ytimg.com/vi/8876OZV_Yy0/hqdefault.jpg"))
150            .set_video_url(Some(
151                "https://www.youtube.com/embed/8876OZV_Yy0?feature=oembed&autoplay=1",
152            ))
153            .build()
154            .unwrap();
155
156        assert_eq!(val, expected);
157
158        let val = Video::builder()
159            .alt_text("Use the Events API to create a dynamic App Home")
160            .author_name("foo")
161            .block_id("video_0")
162            .description(plain_text("Slack sure is nifty!"))
163            .provider_icon_url("bar")
164            .provider_name("baz")
165            .title(plain_text(
166                "Use the Events API to create a dynamic App Home",
167            ))
168            .title_url("https://www.youtube.com/watch?v=8876OZV_Yy0")
169            .thumbnail_url("https://i.ytimg.com/vi/8876OZV_Yy0/hqdefault.jpg")
170            .video_url("https://www.youtube.com/embed/8876OZV_Yy0?feature=oembed&autoplay=1")
171            .build()
172            .unwrap();
173
174        assert_eq!(val, expected);
175    }
176
177    #[test]
178    fn it_requires_alt_text_field() {
179        let err = Video::builder()
180            .title(plain_text("foo"))
181            .thumbnail_url("bar")
182            .video_url("baz")
183            .build()
184            .unwrap_err();
185        assert_eq!(err.object(), "Video");
186
187        let errors = err.field("alt_text");
188        assert!(errors.includes(ValidationErrorKind::Required));
189    }
190
191    #[test]
192    fn it_requires_author_name_less_than_50_characters_long() {
193        let err = Video::builder()
194            .alt_text("foobar")
195            .title(plain_text("foo"))
196            .thumbnail_url("bar")
197            .video_url("baz")
198            .author_name("a".repeat(51))
199            .build()
200            .unwrap_err();
201        assert_eq!(err.object(), "Video");
202
203        let errors = err.field("author_name");
204        assert!(errors.includes(ValidationErrorKind::MaxTextLength(50)));
205    }
206
207    #[test]
208    fn it_requires_block_id_less_than_255_characters_long() {
209        let err = Video::builder()
210            .alt_text("foobar")
211            .title(plain_text("foo"))
212            .thumbnail_url("bar")
213            .video_url("baz")
214            .block_id("a".repeat(256))
215            .build()
216            .unwrap_err();
217        assert_eq!(err.object(), "Video");
218
219        let errors = err.field("block_id");
220        assert!(errors.includes(ValidationErrorKind::MaxTextLength(255)));
221    }
222
223    #[test]
224    fn it_requires_description_less_than_200_characters_long() {
225        let err = Video::builder()
226            .alt_text("foobar")
227            .title(plain_text("foo"))
228            .thumbnail_url("bar")
229            .video_url("baz")
230            .description(plain_text("a".repeat(201)))
231            .build()
232            .unwrap_err();
233        assert_eq!(err.object(), "Video");
234
235        let errors = err.field("description");
236        assert!(errors.includes(ValidationErrorKind::MaxTextLength(200)));
237    }
238
239    #[test]
240    fn it_requires_title_field() {
241        let err = Video::builder()
242            .alt_text("foobar")
243            .thumbnail_url("bar")
244            .video_url("baz")
245            .build()
246            .unwrap_err();
247        assert_eq!(err.object(), "Video");
248
249        let errors = err.field("title");
250        assert!(errors.includes(ValidationErrorKind::Required));
251    }
252
253    #[test]
254    fn it_requires_title_less_than_200_characters_long() {
255        let err = Video::builder()
256            .alt_text("foobar")
257            .title(plain_text("a".repeat(201)))
258            .thumbnail_url("bar")
259            .video_url("baz")
260            .build()
261            .unwrap_err();
262        assert_eq!(err.object(), "Video");
263
264        let errors = err.field("title");
265        assert!(errors.includes(ValidationErrorKind::MaxTextLength(200)));
266    }
267
268    #[test]
269    fn it_requires_thumbnail_url_field() {
270        let err = Video::builder()
271            .alt_text("foobar")
272            .title(plain_text("foo"))
273            .video_url("baz")
274            .build()
275            .unwrap_err();
276        assert_eq!(err.object(), "Video");
277
278        let errors = err.field("thumbnail_url");
279        assert!(errors.includes(ValidationErrorKind::Required));
280    }
281
282    #[test]
283    fn it_requires_video_url_field() {
284        let err = Video::builder()
285            .alt_text("foobar")
286            .title(plain_text("foo"))
287            .thumbnail_url("bar")
288            .build()
289            .unwrap_err();
290        assert_eq!(err.object(), "Video");
291
292        let errors = err.field("video_url");
293        assert!(errors.includes(ValidationErrorKind::Required));
294    }
295}