Skip to content

Commit a92ec21

Browse files
authored
feat: Adapt internal SlackApiScrollableResponse API for more flexible paging (#344)
* feat: Adapt internal SlackApiScrollableResponse API for more flexible paging * chore: Examples update
1 parent af8708f commit a92ec21

6 files changed

Lines changed: 84 additions & 15 deletions

File tree

examples/client.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,26 @@ async fn test_file_upload() -> Result<(), Box<dyn std::error::Error + Send + Syn
117117
&complete_file_upload_resp
118118
);
119119

120+
let all_channels_scroller = SlackApiConversationsListRequest::new().scroller();
121+
let channels: Vec<SlackChannelInfo> = all_channels_scroller
122+
.collect_items_stream(&session, Duration::from_millis(1000))
123+
.await?;
124+
println!("Number of channels: {:#?}", channels.len());
125+
126+
if let Some(general_channel_info) = channels
127+
.into_iter()
128+
.find(|c| c.flags.is_general.unwrap_or(false))
129+
{
130+
println!("General channel info: {:#?}", general_channel_info);
131+
let files_list_scroller = SlackApiFilesListRequest::new()
132+
.with_channel(general_channel_info.id)
133+
.scroller();
134+
let files: Vec<SlackFile> = files_list_scroller
135+
.collect_items_stream(&session, Duration::from_millis(1000))
136+
.await?;
137+
println!("Number of files in general: {:#?}", files.len());
138+
}
139+
120140
Ok(())
121141
}
122142

src/api/chat.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -453,10 +453,11 @@ impl SlackApiScrollableResponse for SlackApiChatScheduledMessagesListResponse {
453453
type CursorType = SlackCursorId;
454454
type ResponseItemType = SlackApiChatScheduledMessageInfo;
455455

456-
fn next_cursor(&self) -> Option<&Self::CursorType> {
456+
fn next_cursor(&self) -> Option<Self::CursorType> {
457457
self.response_metadata
458458
.as_ref()
459459
.and_then(|rm| rm.next_cursor.as_ref())
460+
.cloned()
460461
}
461462

462463
fn scrollable_items<'a>(&'a self) -> Box<dyn Iterator<Item = &'a Self::ResponseItemType> + 'a> {

src/api/conversations.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -426,10 +426,11 @@ impl SlackApiScrollableResponse for SlackApiConversationsHistoryResponse {
426426
type CursorType = SlackCursorId;
427427
type ResponseItemType = SlackHistoryMessage;
428428

429-
fn next_cursor(&self) -> Option<&Self::CursorType> {
429+
fn next_cursor(&self) -> Option<Self::CursorType> {
430430
self.response_metadata
431431
.as_ref()
432432
.and_then(|rm| rm.next_cursor.as_ref())
433+
.cloned()
433434
}
434435

435436
fn scrollable_items<'a>(&'a self) -> Box<dyn Iterator<Item = &'a Self::ResponseItemType> + 'a> {
@@ -540,10 +541,11 @@ impl SlackApiScrollableResponse for SlackApiConversationsListResponse {
540541
type CursorType = SlackCursorId;
541542
type ResponseItemType = SlackChannelInfo;
542543

543-
fn next_cursor(&self) -> Option<&Self::CursorType> {
544+
fn next_cursor(&self) -> Option<Self::CursorType> {
544545
self.response_metadata
545546
.as_ref()
546547
.and_then(|rm| rm.next_cursor.as_ref())
548+
.cloned()
547549
}
548550

549551
fn scrollable_items<'a>(&'a self) -> Box<dyn Iterator<Item = &'a Self::ResponseItemType> + 'a> {
@@ -590,10 +592,11 @@ impl SlackApiScrollableResponse for SlackApiConversationsMembersResponse {
590592
type CursorType = SlackCursorId;
591593
type ResponseItemType = SlackUserId;
592594

593-
fn next_cursor(&self) -> Option<&Self::CursorType> {
595+
fn next_cursor(&self) -> Option<Self::CursorType> {
594596
self.response_metadata
595597
.as_ref()
596598
.and_then(|rm| rm.next_cursor.as_ref())
599+
.cloned()
597600
}
598601

599602
fn scrollable_items<'a>(&'a self) -> Box<dyn Iterator<Item = &'a Self::ResponseItemType> + 'a> {
@@ -677,10 +680,11 @@ impl SlackApiScrollableResponse for SlackApiConversationsRepliesResponse {
677680
type CursorType = SlackCursorId;
678681
type ResponseItemType = SlackHistoryMessage;
679682

680-
fn next_cursor(&self) -> Option<&Self::CursorType> {
683+
fn next_cursor(&self) -> Option<Self::CursorType> {
681684
self.response_metadata
682685
.as_ref()
683686
.and_then(|rm| rm.next_cursor.as_ref())
687+
.cloned()
684688
}
685689

686690
fn scrollable_items<'a>(&'a self) -> Box<dyn Iterator<Item = &'a Self::ResponseItemType> + 'a> {

src/api/files.rs

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,22 @@
22
//! Support for Slack Files API methods
33
//!
44
5-
use rsb_derive::Builder;
6-
use rvstruct::ValueStruct;
7-
use serde::{Deserialize, Serialize, Serializer};
8-
use serde_with::skip_serializing_none;
9-
105
use crate::api::{
116
SlackApiUsersConversationsRequest, SlackApiUsersConversationsResponse,
127
SlackApiUsersProfileSetRequest, SlackApiUsersProfileSetResponse,
138
};
149
use crate::models::*;
1510
use crate::multipart_form::FileMultipartData;
1611
use crate::ratectl::*;
17-
use crate::SlackClientSession;
1812
use crate::{ClientResult, SlackClientHttpConnector};
13+
use crate::{SlackApiScrollableRequest, SlackApiScrollableResponse, SlackClientSession};
14+
use futures_util::future::BoxFuture;
15+
use futures_util::FutureExt;
16+
use rsb_derive::Builder;
17+
use rvstruct::ValueStruct;
18+
use serde::{Deserialize, Serialize, Serializer};
19+
use serde_with::skip_serializing_none;
20+
use tokio_stream::StreamExt;
1921

2022
impl<'a, SCHC> SlackClientSession<'a, SCHC>
2123
where
@@ -223,6 +225,46 @@ pub struct SlackApiFilesListPaging {
223225
pub pages: Option<u32>,
224226
}
225227

228+
impl<SCHC> SlackApiScrollableRequest<SCHC> for SlackApiFilesListRequest
229+
where
230+
SCHC: SlackClientHttpConnector + Send + Sync + Clone + 'static,
231+
{
232+
type ResponseType = SlackApiFilesListResponse;
233+
type CursorType = u32;
234+
type ResponseItemType = SlackFile;
235+
236+
fn with_new_cursor(&self, new_cursor: Option<&Self::CursorType>) -> Self {
237+
self.clone().opt_page(new_cursor.cloned())
238+
}
239+
240+
fn scroll<'a, 's>(
241+
&'a self,
242+
session: &'a SlackClientSession<'s, SCHC>,
243+
) -> BoxFuture<'a, ClientResult<Self::ResponseType>> {
244+
async move { session.files_list(self).await }.boxed()
245+
}
246+
}
247+
248+
impl SlackApiScrollableResponse for SlackApiFilesListResponse {
249+
type CursorType = u32;
250+
type ResponseItemType = SlackFile;
251+
252+
fn next_cursor(&self) -> Option<Self::CursorType> {
253+
self.paging
254+
.as_ref()
255+
.into_iter()
256+
.filter_map(|paging| match (paging.page, paging.pages) {
257+
(Some(page), Some(pages)) if page < pages => Some(page + 1),
258+
_ => None,
259+
})
260+
.next()
261+
}
262+
263+
fn scrollable_items<'a>(&'a self) -> Box<dyn Iterator<Item = &'a Self::ResponseItemType> + 'a> {
264+
Box::new(self.files.iter())
265+
}
266+
}
267+
226268
#[skip_serializing_none]
227269
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Builder)]
228270
pub struct SlackApiFilesUploadRequest {

src/api/users.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -233,10 +233,11 @@ impl SlackApiScrollableResponse for SlackApiUsersConversationsResponse {
233233
type CursorType = SlackCursorId;
234234
type ResponseItemType = SlackChannelInfo;
235235

236-
fn next_cursor(&self) -> Option<&Self::CursorType> {
236+
fn next_cursor(&self) -> Option<Self::CursorType> {
237237
self.response_metadata
238238
.as_ref()
239239
.and_then(|rm| rm.next_cursor.as_ref())
240+
.cloned()
240241
}
241242

242243
fn scrollable_items<'a>(&'a self) -> Box<dyn Iterator<Item = &'a Self::ResponseItemType> + 'a> {
@@ -284,10 +285,11 @@ impl SlackApiScrollableResponse for SlackApiUsersListResponse {
284285
type CursorType = SlackCursorId;
285286
type ResponseItemType = SlackUser;
286287

287-
fn next_cursor(&self) -> Option<&Self::CursorType> {
288+
fn next_cursor(&self) -> Option<Self::CursorType> {
288289
self.response_metadata
289290
.as_ref()
290291
.and_then(|rm| rm.next_cursor.as_ref())
292+
.cloned()
291293
}
292294

293295
fn scrollable_items<'a>(&'a self) -> Box<dyn Iterator<Item = &'a Self::ResponseItemType> + 'a> {

src/scroller.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ pub trait SlackApiScrollableResponse {
8080
type CursorType;
8181
type ResponseItemType;
8282

83-
fn next_cursor(&self) -> Option<&Self::CursorType>;
83+
fn next_cursor(&self) -> Option<Self::CursorType>;
8484
fn scrollable_items<'a>(&'a self) -> Box<dyn Iterator<Item = &'a Self::ResponseItemType> + 'a>;
8585
}
8686

@@ -164,7 +164,7 @@ where
164164
.scroll(session)
165165
.map_ok(|res| {
166166
self.last_response = Some(res.clone());
167-
self.last_cursor = res.next_cursor().cloned();
167+
self.last_cursor = res.next_cursor();
168168
res
169169
})
170170
.await

0 commit comments

Comments
 (0)