mangadex_api/v5/user/
get.rs1use derive_builder::Builder;
44use serde::Serialize;
45use uuid::Uuid;
46
47use crate::HttpClientRef;
48use mangadex_api_schema::v5::UserListResponse;
49use mangadex_api_types::UserSortOrder;
50
51#[cfg_attr(
52 feature = "deserializable-endpoint",
53 derive(serde::Deserialize, getset::Getters, getset::Setters)
54)]
55#[derive(Debug, Serialize, Clone, Builder, Default)]
56#[serde(rename_all = "camelCase")]
57#[builder(
58 setter(into, strip_option),
59 default,
60 build_fn(error = "mangadex_api_types::error::BuilderError")
61)]
62#[cfg_attr(feature = "non_exhaustive", non_exhaustive)]
63pub struct ListUser {
64 #[doc(hidden)]
65 #[serde(skip)]
66 #[builder(pattern = "immutable")]
67 #[cfg_attr(feature = "deserializable-endpoint", getset(set = "pub", get = "pub"))]
68 pub http_client: HttpClientRef,
69
70 #[serde(skip_serializing_if = "Option::is_none")]
71 pub limit: Option<u32>,
72 #[serde(skip_serializing_if = "Option::is_none")]
73 pub offset: Option<u32>,
74 #[builder(setter(each = "add_user_id"))]
75 #[serde(rename = "ids")]
76 pub user_ids: Vec<Uuid>,
77 #[serde(skip_serializing_if = "Option::is_none")]
78 pub username: Option<String>,
79 #[serde(skip_serializing_if = "Option::is_none")]
80 pub order: Option<UserSortOrder>,
81}
82
83endpoint! {
84 GET "/user",
85 #[query auth] ListUser,
86 #[flatten_result] UserListResponse,
87 ListUserBuilder
88}
89
90#[cfg(test)]
91mod tests {
92 use serde_json::json;
93 use url::Url;
94 use uuid::Uuid;
95 use wiremock::matchers::{header, method, path};
96 use wiremock::{Mock, MockServer, ResponseTemplate};
97
98 use crate::v5::AuthTokens;
99 use crate::{HttpClient, MangaDexClient};
100 use mangadex_api_types::error::Error;
101 use mangadex_api_types::ResponseType;
102
103 #[tokio::test]
104 async fn list_user_fires_a_request_to_base_url() -> anyhow::Result<()> {
105 let mock_server = MockServer::start().await;
106 let http_client = HttpClient::builder()
107 .base_url(Url::parse(&mock_server.uri())?)
108 .auth_tokens(AuthTokens {
109 session: "sessiontoken".to_string(),
110 refresh: "refreshtoken".to_string(),
111 })
112 .build()?;
113 let mangadex_client = MangaDexClient::new_with_http_client(http_client);
114
115 let user_id = Uuid::new_v4();
116 let response_body = json!({
117 "result": "ok",
118 "response": "collection",
119 "data": [
120 {
121 "id": user_id,
122 "type": "user",
123 "attributes": {
124 "username": "myusername",
125 "roles": [
126 "ROLE_MEMBER",
127 "ROLE_GROUP_MEMBER",
128 "ROLE_GROUP_LEADER"
129 ],
130 "version": 1
131 },
132 "relationships": [
133 {
134 "id": "a3219a4f-73c0-4213-8730-05985130539a",
135 "type": "scanlation_group"
136 }
137 ]
138 }
139 ],
140 "limit": 1,
141 "offset": 0,
142 "total": 1
143 });
144
145 Mock::given(method("GET"))
146 .and(path("/user"))
147 .and(header("Authorization", "Bearer sessiontoken"))
148 .respond_with(ResponseTemplate::new(200).set_body_json(response_body))
149 .expect(1)
150 .mount(&mock_server)
151 .await;
152
153 let res = mangadex_client.user().get().limit(1u32).send().await?;
154
155 assert_eq!(res.response, ResponseType::Collection);
156 let user = &res.data[0];
157 assert_eq!(user.id, user_id);
158 assert_eq!(user.attributes.username, "myusername");
159 assert_eq!(user.attributes.version, 1);
160
161 Ok(())
162 }
163
164 #[tokio::test]
165 async fn list_manga_handles_400() -> anyhow::Result<()> {
166 let mock_server = MockServer::start().await;
167 let http_client: HttpClient = HttpClient::builder()
168 .base_url(Url::parse(&mock_server.uri())?)
169 .auth_tokens(AuthTokens {
170 session: "sessiontoken".to_string(),
171 refresh: "refreshtoken".to_string(),
172 })
173 .build()?;
174 let mangadex_client = MangaDexClient::new_with_http_client(http_client);
175
176 let error_id = Uuid::new_v4();
177
178 let response_body = json!({
179 "result": "error",
180 "errors": [{
181 "id": error_id.to_string(),
182 "status": 400,
183 "title": "Invalid limit",
184 "detail": "Limit must be between 1 and 100"
185 }]
186 });
187
188 Mock::given(method("GET"))
189 .and(path("/user"))
190 .respond_with(ResponseTemplate::new(400).set_body_json(response_body))
191 .expect(1)
192 .mount(&mock_server)
193 .await;
194
195 let res = mangadex_client
196 .user()
197 .get()
198 .limit(0u32)
199 .send()
200 .await
201 .expect_err("expected error");
202
203 if let Error::Api(errors) = res {
204 assert_eq!(errors.errors.len(), 1);
205
206 assert_eq!(errors.errors[0].id, error_id);
207 assert_eq!(errors.errors[0].status, 400);
208 assert_eq!(errors.errors[0].title, Some("Invalid limit".to_string()));
209 assert_eq!(
210 errors.errors[0].detail,
211 Some("Limit must be between 1 and 100".to_string())
212 );
213 }
214
215 Ok(())
216 }
217}