Skip to main content

slack_morphism/api/
files.rs

1//!
2//! Support for Slack Files API methods
3//!
4
5use crate::api::{
6    SlackApiUsersConversationsRequest, SlackApiUsersConversationsResponse,
7    SlackApiUsersProfileSetRequest, SlackApiUsersProfileSetResponse,
8};
9use crate::models::*;
10use crate::multipart_form::FileMultipartData;
11use crate::ratectl::*;
12use crate::{ClientResult, SlackClientHttpConnector};
13use crate::{SlackApiScrollableRequest, SlackApiScrollableResponse, SlackClientSession};
14use futures_util::future::BoxFuture;
15use futures_util::FutureExt;
16use rsb_derive::Builder;
17use rvstruct::ValueStruct;
18use serde::{Deserialize, Serialize, Serializer};
19use serde_with::skip_serializing_none;
20
21impl<'a, SCHC> SlackClientSession<'a, SCHC>
22where
23    SCHC: SlackClientHttpConnector + Send,
24{
25    ///
26    /// https://api.slack.com/methods/files.info
27    ///
28    pub async fn files_info(
29        &self,
30        req: &SlackApiFilesInfoRequest,
31    ) -> ClientResult<SlackApiFilesInfoResponse> {
32        self.http_session_api
33            .http_get(
34                "files.info",
35                &vec![("file", Some(req.file.value()))],
36                Some(&SLACK_TIER4_METHOD_CONFIG),
37            )
38            .await
39    }
40
41    ///
42    /// https://api.slack.com/methods/files.list
43    ///
44    pub async fn files_list(
45        &self,
46        req: &SlackApiFilesListRequest,
47    ) -> ClientResult<SlackApiFilesListResponse> {
48        self.http_session_api
49            .http_get(
50                "files.list",
51                &vec![
52                    ("channel", req.channel.as_ref().map(|x| x.value())),
53                    ("user", req.user.as_ref().map(|x| x.value())),
54                    ("types", req.types.as_ref()),
55                    ("count", req.count.map(|x| x.to_string()).as_ref()),
56                    ("page", req.page.map(|x| x.to_string()).as_ref()),
57                    ("ts_from", req.ts_from.map(|x| x.to_string()).as_ref()),
58                    ("ts_to", req.ts_to.map(|x| x.to_string()).as_ref()),
59                    (
60                        "show_files_hidden_by_limit",
61                        req.show_files_hidden_by_limit
62                            .map(|x| x.to_string())
63                            .as_ref(),
64                    ),
65                ],
66                Some(&SLACK_TIER3_METHOD_CONFIG),
67            )
68            .await
69    }
70
71    ///
72    /// https://api.slack.com/methods/files.upload
73    ///
74    #[deprecated(
75        note = "Deprecated by Slack. Use `getUploadURLExternal/files_upload_via_url/completeUploadExternal` instead."
76    )]
77    pub async fn files_upload(
78        &self,
79        req: &SlackApiFilesUploadRequest,
80    ) -> ClientResult<SlackApiFilesUploadResponse> {
81        let maybe_file = req.binary_content.as_ref().map(|file_data| {
82            let filename = req.filename.clone().unwrap_or("file".to_string());
83            let file_content_type = req.file_content_type.clone().unwrap_or_else(|| {
84                let file_mime = mime_guess::MimeGuess::from_path(&filename).first_or_octet_stream();
85                file_mime.to_string()
86            });
87            FileMultipartData {
88                name: filename,
89                content_type: file_content_type,
90                data: file_data.as_slice(),
91            }
92        });
93        self.http_session_api
94            .http_post_multipart_form(
95                "files.upload",
96                maybe_file,
97                &vec![
98                    (
99                        "channels",
100                        req.channels
101                            .as_ref()
102                            .map(|xs| {
103                                xs.iter()
104                                    .map(|x| x.to_string())
105                                    .collect::<Vec<String>>()
106                                    .join(",")
107                            })
108                            .as_ref(),
109                    ),
110                    ("content", req.content.as_ref()),
111                    ("filename", req.filename.as_ref()),
112                    ("filetype", req.filetype.as_ref().map(|x| x.value())),
113                    ("initial_comment", req.initial_comment.as_ref()),
114                    ("thread_ts", req.thread_ts.as_ref().map(|x| x.value())),
115                    ("title", req.title.as_ref()),
116                ],
117                Some(&SLACK_TIER2_METHOD_CONFIG),
118            )
119            .await
120    }
121
122    ///
123    /// https://api.slack.com/methods/files.getUploadURLExternal
124    ///
125    pub async fn get_upload_url_external(
126        &self,
127        req: &SlackApiFilesGetUploadUrlExternalRequest,
128    ) -> ClientResult<SlackApiFilesGetUploadUrlExternalResponse> {
129        self.http_session_api
130            .http_get(
131                "files.getUploadURLExternal",
132                &vec![
133                    ("filename", Some(&req.filename)),
134                    ("length", Some(&req.length.to_string())),
135                    ("alt_txt", req.alt_txt.as_ref()),
136                    ("snippet_type", req.snippet_type.as_ref().map(|v| v.value())),
137                ],
138                Some(&SLACK_TIER4_METHOD_CONFIG),
139            )
140            .await
141    }
142
143    pub async fn files_upload_via_url(
144        &self,
145        req: &SlackApiFilesUploadViaUrlRequest,
146    ) -> ClientResult<SlackApiFilesUploadViaUrlResponse> {
147        self.http_session_api
148            .http_post_uri_binary(
149                req.upload_url.value().clone(),
150                req.content_type.clone(),
151                &req.content,
152                Some(&SLACK_TIER4_METHOD_CONFIG),
153            )
154            .await
155    }
156
157    ///
158    /// https://api.slack.com/methods/files.completeUploadExternal
159    ///
160    pub async fn files_complete_upload_external(
161        &self,
162        req: &SlackApiFilesCompleteUploadExternalRequest,
163    ) -> ClientResult<SlackApiFilesCompleteUploadExternalResponse> {
164        self.http_session_api
165            .http_post(
166                "files.completeUploadExternal",
167                req,
168                Some(&SLACK_TIER4_METHOD_CONFIG),
169            )
170            .await
171    }
172
173    ///
174    /// https://api.slack.com/methods/files.delete
175    ///
176    pub async fn files_delete(
177        &self,
178        req: &SlackApiFilesDeleteRequest,
179    ) -> ClientResult<SlackApiFilesDeleteResponse> {
180        self.http_session_api
181            .http_post("files.delete", req, Some(&SLACK_TIER3_METHOD_CONFIG))
182            .await
183    }
184}
185
186#[skip_serializing_none]
187#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
188pub struct SlackApiFilesInfoRequest {
189    pub file: SlackFileId,
190}
191
192#[skip_serializing_none]
193#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
194pub struct SlackApiFilesInfoResponse {
195    pub file: SlackFile,
196}
197
198#[skip_serializing_none]
199#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
200pub struct SlackApiFilesListRequest {
201    pub channel: Option<SlackChannelId>,
202    pub user: Option<SlackUserId>,
203    pub types: Option<String>,
204    pub count: Option<u32>,
205    pub page: Option<u32>,
206    pub ts_from: Option<i64>,
207    pub ts_to: Option<i64>,
208    pub show_files_hidden_by_limit: Option<bool>,
209}
210
211#[skip_serializing_none]
212#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
213pub struct SlackApiFilesListResponse {
214    pub files: Vec<SlackFile>,
215    pub paging: Option<SlackApiFilesListPaging>,
216}
217
218#[skip_serializing_none]
219#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
220pub struct SlackApiFilesListPaging {
221    pub count: Option<u32>,
222    pub total: Option<u32>,
223    pub page: Option<u32>,
224    pub pages: Option<u32>,
225}
226
227impl<SCHC> SlackApiScrollableRequest<SCHC> for SlackApiFilesListRequest
228where
229    SCHC: SlackClientHttpConnector + Send + Sync + Clone + 'static,
230{
231    type ResponseType = SlackApiFilesListResponse;
232    type CursorType = u32;
233    type ResponseItemType = SlackFile;
234
235    fn with_new_cursor(&self, new_cursor: Option<&Self::CursorType>) -> Self {
236        self.clone().opt_page(new_cursor.cloned())
237    }
238
239    fn scroll<'a, 's>(
240        &'a self,
241        session: &'a SlackClientSession<'s, SCHC>,
242    ) -> BoxFuture<'a, ClientResult<Self::ResponseType>> {
243        async move { session.files_list(self).await }.boxed()
244    }
245}
246
247impl SlackApiScrollableResponse for SlackApiFilesListResponse {
248    type CursorType = u32;
249    type ResponseItemType = SlackFile;
250
251    fn next_cursor(&self) -> Option<Self::CursorType> {
252        self.paging
253            .as_ref()
254            .into_iter()
255            .filter_map(|paging| match (paging.page, paging.pages) {
256                (Some(page), Some(pages)) if page < pages => Some(page + 1),
257                _ => None,
258            })
259            .next()
260    }
261
262    fn scrollable_items<'a>(&'a self) -> Box<dyn Iterator<Item = &'a Self::ResponseItemType> + 'a> {
263        Box::new(self.files.iter())
264    }
265}
266
267#[skip_serializing_none]
268#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
269pub struct SlackApiFilesUploadRequest {
270    #[serde(serialize_with = "to_csv")]
271    pub channels: Option<Vec<SlackChannelId>>,
272    pub content: Option<String>,
273    pub binary_content: Option<Vec<u8>>,
274    pub filename: Option<String>,
275    pub filetype: Option<SlackFileType>,
276    pub initial_comment: Option<String>,
277    pub thread_ts: Option<SlackTs>,
278    pub title: Option<String>,
279    pub file_content_type: Option<String>,
280}
281
282#[skip_serializing_none]
283#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
284pub struct SlackApiFilesUploadResponse {
285    pub file: SlackFile,
286}
287
288#[skip_serializing_none]
289#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
290pub struct SlackApiFilesGetUploadUrlExternalRequest {
291    pub filename: String,
292    pub length: usize,
293    pub alt_txt: Option<String>,
294    pub snippet_type: Option<SlackFileSnippetType>,
295}
296
297#[skip_serializing_none]
298#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
299pub struct SlackApiFilesGetUploadUrlExternalResponse {
300    pub upload_url: SlackFileUploadUrl,
301    pub file_id: SlackFileId,
302}
303
304#[skip_serializing_none]
305#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
306pub struct SlackApiFilesUploadViaUrlRequest {
307    pub upload_url: SlackFileUploadUrl,
308    pub content: Vec<u8>,
309    pub content_type: String,
310}
311
312#[skip_serializing_none]
313#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
314pub struct SlackApiFilesUploadViaUrlResponse {}
315
316#[skip_serializing_none]
317#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
318pub struct SlackApiFilesCompleteUploadExternalRequest {
319    pub files: Vec<SlackApiFilesComplete>,
320    pub channel_id: Option<SlackChannelId>,
321    pub initial_comment: Option<String>,
322    pub thread_ts: Option<SlackTs>,
323}
324
325#[skip_serializing_none]
326#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
327pub struct SlackApiFilesCompleteUploadExternalResponse {
328    pub files: Vec<SlackFile>,
329}
330
331#[skip_serializing_none]
332#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
333pub struct SlackApiFilesComplete {
334    pub id: SlackFileId,
335    pub title: Option<String>,
336}
337
338#[skip_serializing_none]
339#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
340pub struct SlackApiFilesDeleteRequest {
341    pub file: SlackFileId,
342}
343
344#[skip_serializing_none]
345#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
346pub struct SlackApiFilesDeleteResponse {}
347
348fn to_csv<S: Serializer>(x: &Option<Vec<SlackChannelId>>, s: S) -> Result<S::Ok, S::Error> {
349    match x {
350        None => s.serialize_none(),
351        Some(ids) => {
352            let y: Vec<String> = ids.iter().map(|v| v.0.clone()).collect();
353            y.join(",").serialize(s)
354        }
355    }
356}