misskey_api/endpoint/admin/
show_users.rs

1use crate::model::{
2    sort::SortOrder,
3    user::{User, UserOrigin, UserSortKey},
4};
5
6use serde::Serialize;
7use thiserror::Error;
8use typed_builder::TypedBuilder;
9
10#[derive(Serialize, PartialEq, Eq, Clone, Debug, Copy)]
11#[serde(rename_all = "camelCase")]
12pub enum UserState {
13    All,
14    Available,
15    // Alive,
16    Admin,
17    Moderator,
18    AdminOrModerator,
19    Silenced,
20    Suspended,
21}
22
23#[derive(Debug, Error, Clone)]
24#[error("invalid user state")]
25pub struct ParseUserStateError {
26    _priv: (),
27}
28
29impl std::str::FromStr for UserState {
30    type Err = ParseUserStateError;
31
32    fn from_str(s: &str) -> Result<UserState, Self::Err> {
33        match s {
34            "all" | "All" => Ok(UserState::All),
35            // "alive" | "Alive" => Ok(UserState::Alive),
36            "available" | "Available" => Ok(UserState::Available),
37            "admin" | "Admin" => Ok(UserState::Admin),
38            "moderator" | "Moderator" => Ok(UserState::Moderator),
39            "adminOrModerator" | "AdminOrModerator" => Ok(UserState::AdminOrModerator),
40            "silenced" | "Silenced" => Ok(UserState::Silenced),
41            "suspended" | "Suspended" => Ok(UserState::Suspended),
42            _ => Err(ParseUserStateError { _priv: () }),
43        }
44    }
45}
46
47#[derive(Serialize, Default, Debug, Clone, TypedBuilder)]
48#[serde(rename_all = "camelCase")]
49#[builder(doc)]
50pub struct Request {
51    /// 1 .. 100
52    #[serde(skip_serializing_if = "Option::is_none")]
53    #[builder(default, setter(strip_option))]
54    pub limit: Option<u8>,
55    #[serde(skip_serializing_if = "Option::is_none")]
56    #[builder(default, setter(strip_option))]
57    pub offset: Option<u64>,
58    #[serde(skip_serializing_if = "Option::is_none")]
59    #[builder(default, setter(strip_option))]
60    pub sort: Option<SortOrder<UserSortKey>>,
61    #[serde(skip_serializing_if = "Option::is_none")]
62    #[builder(default, setter(strip_option))]
63    pub state: Option<UserState>,
64    #[serde(skip_serializing_if = "Option::is_none")]
65    #[builder(default, setter(strip_option))]
66    pub origin: Option<UserOrigin>,
67    #[serde(skip_serializing_if = "Option::is_none")]
68    #[builder(default, setter(strip_option, into))]
69    pub username: Option<String>,
70    #[serde(skip_serializing_if = "Option::is_none")]
71    #[builder(default, setter(strip_option, into))]
72    pub hostname: Option<String>,
73}
74
75impl misskey_core::Request for Request {
76    type Response = Vec<User>;
77    const ENDPOINT: &'static str = "admin/show-users";
78}
79
80impl_offset_pagination!(Request, User);
81
82#[cfg(test)]
83mod tests {
84    use super::{Request, UserState};
85    use crate::test::{ClientExt, TestClient};
86
87    #[tokio::test]
88    async fn request() {
89        let client = TestClient::new();
90        client.admin.test(Request::default()).await;
91    }
92
93    #[tokio::test]
94    async fn request_with_limit() {
95        let client = TestClient::new();
96        client
97            .admin
98            .test(Request {
99                limit: Some(100),
100                offset: None,
101                sort: None,
102                state: None,
103                origin: None,
104                username: None,
105                hostname: None,
106            })
107            .await;
108    }
109
110    #[tokio::test]
111    async fn request_with_offset() {
112        let client = TestClient::new();
113        client
114            .admin
115            .test(Request {
116                limit: None,
117                offset: Some(5),
118                sort: None,
119                state: None,
120                origin: None,
121                username: None,
122                hostname: None,
123            })
124            .await;
125    }
126
127    #[tokio::test]
128    async fn request_with_sort() {
129        use crate::model::{sort::SortOrder, user::UserSortKey};
130
131        let client = TestClient::new();
132
133        client
134            .admin
135            .test(Request {
136                limit: None,
137                offset: None,
138                sort: Some(SortOrder::Ascending(UserSortKey::Follower)),
139                state: None,
140                origin: None,
141                username: None,
142                hostname: None,
143            })
144            .await;
145        client
146            .admin
147            .test(Request {
148                limit: None,
149                offset: None,
150                sort: Some(SortOrder::Ascending(UserSortKey::CreatedAt)),
151                state: None,
152                origin: None,
153                username: None,
154                hostname: None,
155            })
156            .await;
157        client
158            .admin
159            .test(Request {
160                limit: None,
161                offset: None,
162                sort: Some(SortOrder::Descending(UserSortKey::UpdatedAt)),
163                state: None,
164                origin: None,
165                username: None,
166                hostname: None,
167            })
168            .await;
169    }
170
171    #[tokio::test]
172    async fn request_with_state() {
173        let client = TestClient::new();
174
175        client
176            .admin
177            .test(Request {
178                limit: None,
179                offset: None,
180                sort: None,
181                state: Some(UserState::All),
182                origin: None,
183                username: None,
184                hostname: None,
185            })
186            .await;
187        client
188            .admin
189            .test(Request {
190                limit: None,
191                offset: None,
192                sort: None,
193                state: Some(UserState::Admin),
194                origin: None,
195                username: None,
196                hostname: None,
197            })
198            .await;
199        client
200            .admin
201            .test(Request {
202                limit: None,
203                offset: None,
204                sort: None,
205                state: Some(UserState::Available),
206                origin: None,
207                username: None,
208                hostname: None,
209            })
210            .await;
211        // client
212        //     .admin
213        //     .test(Request {
214        //         limit: None,
215        //         offset: None,
216        //         sort: None,
217        //         state: Some(UserState::Alive),
218        //         origin: None,
219        //         username: None,
220        //         hostname: None,
221        //     })
222        //     .await;
223        client
224            .admin
225            .test(Request {
226                limit: None,
227                offset: None,
228                sort: None,
229                state: Some(UserState::Moderator),
230                origin: None,
231                username: None,
232                hostname: None,
233            })
234            .await;
235        // TODO: Uncomment with cfg when `adminOrModerator` value is fixed in Misskey
236        // client
237        //     .admin
238        //     .test(Request {
239        //         limit: None,
240        //         offset: None,
241        //         sort: None,
242        //         state: Some(UserState::AdminOrModerator),
243        //         origin: None,
244        //     })
245        //     .await;
246        client
247            .admin
248            .test(Request {
249                limit: None,
250                offset: None,
251                sort: None,
252                state: Some(UserState::Silenced),
253                origin: None,
254                username: None,
255                hostname: None,
256            })
257            .await;
258        client
259            .admin
260            .test(Request {
261                limit: None,
262                offset: None,
263                sort: None,
264                state: Some(UserState::Suspended),
265                origin: None,
266                username: None,
267                hostname: None,
268            })
269            .await;
270    }
271
272    #[tokio::test]
273    async fn request_with_origin() {
274        use crate::model::user::UserOrigin;
275
276        let client = TestClient::new();
277
278        client
279            .admin
280            .test(Request {
281                limit: None,
282                offset: None,
283                sort: None,
284                state: None,
285                origin: Some(UserOrigin::Local),
286                username: None,
287                hostname: None,
288            })
289            .await;
290        client
291            .admin
292            .test(Request {
293                limit: None,
294                offset: None,
295                sort: None,
296                state: None,
297                origin: Some(UserOrigin::Remote),
298                username: None,
299                hostname: None,
300            })
301            .await;
302        client
303            .admin
304            .test(Request {
305                limit: None,
306                offset: None,
307                sort: None,
308                state: None,
309                origin: Some(UserOrigin::Combined),
310                username: None,
311                hostname: None,
312            })
313            .await;
314    }
315
316    #[tokio::test]
317    async fn request_with_username_hostname() {
318        let client = TestClient::new();
319
320        client
321            .admin
322            .test(Request {
323                limit: None,
324                offset: None,
325                sort: None,
326                state: None,
327                origin: None,
328                username: Some("admin".to_string()),
329                hostname: Some("host".to_string()),
330            })
331            .await;
332    }
333}