mangadex_api/v5/user/follows/list/id/
get.rs1use derive_builder::Builder;
46use mangadex_api_schema::v5::IsFollowingResponse;
47use mangadex_api_schema::{FromResponse, NoData};
48use serde::Serialize;
49use uuid::Uuid;
50
51use crate::HttpClientRef;
52use mangadex_api_types::error::{Error, Result};
53
54#[cfg_attr(
58 feature = "deserializable-endpoint",
59 derive(serde::Deserialize, getset::Getters, getset::Setters)
60)]
61#[derive(Debug, Builder, Serialize, Clone)]
62#[serde(rename_all = "camelCase")]
63#[builder(
64 setter(into, strip_option),
65 build_fn(error = "mangadex_api_types::error::BuilderError")
66)]
67#[cfg_attr(
68 feature = "custom_list_v2",
69 deprecated(
70 since = "3.0.0-rc.1",
71 note = "After the introduction of the Subscription system, this endpoint will be removed in v3"
72 )
73)]
74pub struct IsFollowingCustomList {
75 #[doc(hidden)]
77 #[serde(skip)]
78 #[builder(pattern = "immutable")]
79 #[cfg_attr(feature = "deserializable-endpoint", getset(set = "pub", get = "pub"))]
80 pub http_client: HttpClientRef,
81
82 pub list_id: Uuid,
83}
84
85impl IsFollowingCustomList {
86 pub async fn send(&mut self) -> Result<IsFollowingResponse> {
87 #[cfg(all(
88 not(feature = "multi-thread"),
89 not(feature = "tokio-multi-thread"),
90 not(feature = "rw-multi-thread")
91 ))]
92 let res = self
93 .http_client
94 .try_borrow()?
95 .send_request_without_deserializing(self)
96 .await?;
97 #[cfg(any(feature = "multi-thread", feature = "tokio-multi-thread"))]
98 let res = self
99 .http_client
100 .lock()
101 .await
102 .send_request_without_deserializing(self)
103 .await?;
104 #[cfg(feature = "rw-multi-thread")]
105 let res = self
106 .http_client
107 .read()
108 .await
109 .send_request_without_deserializing(self)
110 .await?;
111
112 match res.status() {
113 reqwest::StatusCode::OK => Ok(IsFollowingResponse { is_following: true }),
114 reqwest::StatusCode::NOT_FOUND => {
115 let result = res
116 .json::<<Result<NoData> as FromResponse>::Response>()
117 .await?;
118 match result.into_result() {
119 Ok(_) => Ok(IsFollowingResponse {
120 is_following: false,
121 }),
122 Err(err) => Err(Error::Api(err)),
123 }
124 }
125 other_status => Err(Error::ServerError(other_status.as_u16(), res.text().await?)),
126 }
127 }
128}
129
130endpoint! {
131 GET ("/user/follows/list/{}", list_id),
132 #[no_data auth] IsFollowingCustomList,
133 #[no_send] Result<IsFollowingResponse>
134}
135
136builder_send! {
137 #[builder] IsFollowingCustomListBuilder,
138 IsFollowingResponse
139}
140
141#[cfg(test)]
142mod tests {
143 use mangadex_api_types::error::Error;
144 use serde_json::json;
145 use url::Url;
146 use uuid::Uuid;
147 use wiremock::matchers::{header, method, path_regex};
148 use wiremock::{Mock, MockServer, ResponseTemplate};
149
150 use crate::v5::AuthTokens;
151 use crate::{HttpClient, MangaDexClient};
152
153 #[tokio::test]
154 async fn user_follows_custom_list() -> anyhow::Result<()> {
155 let mock_server = MockServer::start().await;
156 let http_client: HttpClient = HttpClient::builder()
157 .base_url(Url::parse(&mock_server.uri())?)
158 .auth_tokens(AuthTokens {
159 session: "sessiontoken".to_string(),
160 refresh: "refreshtoken".to_string(),
161 })
162 .build()?;
163 let mangadex_client = MangaDexClient::new_with_http_client(http_client);
164
165 let list_id = Uuid::new_v4();
166 let response_body = json!({
167 "result": "ok"
168 });
169
170 Mock::given(method("GET"))
171 .and(path_regex(r"/user/follows/list/[0-9a-fA-F-]+"))
172 .and(header("Authorization", "Bearer sessiontoken"))
173 .respond_with(ResponseTemplate::new(200).set_body_json(response_body))
174 .expect(1)
175 .mount(&mock_server)
176 .await;
177
178 let res = mangadex_client
179 .user()
180 .follows()
181 .list()
182 .id(list_id)
183 .get()
184 .send()
185 .await?;
186
187 assert!(res.is_following);
188
189 Ok(())
190 }
191
192 #[tokio::test]
193 async fn user_does_not_follow_custom_list() -> anyhow::Result<()> {
194 let mock_server = MockServer::start().await;
195 let http_client: HttpClient = HttpClient::builder()
196 .base_url(Url::parse(&mock_server.uri())?)
197 .auth_tokens(AuthTokens {
198 session: "sessiontoken".to_string(),
199 refresh: "refreshtoken".to_string(),
200 })
201 .build()?;
202 let mangadex_client = MangaDexClient::new_with_http_client(http_client);
203
204 let list_id = Uuid::new_v4();
205 let response_body = json!({
206 "result": "ok"
207 });
208
209 Mock::given(method("GET"))
210 .and(path_regex(r"/user/follows/list/[0-9a-fA-F-]+"))
211 .and(header("Authorization", "Bearer sessiontoken"))
212 .respond_with(ResponseTemplate::new(404).set_body_json(response_body))
213 .expect(1)
214 .mount(&mock_server)
215 .await;
216
217 let res = mangadex_client
218 .user()
219 .follows()
220 .list()
221 .id(list_id)
222 .get()
223 .send()
224 .await?;
225
226 assert!(!res.is_following);
227
228 Ok(())
229 }
230
231 #[tokio::test]
232 async fn custom_list_does_not_exist() -> anyhow::Result<()> {
233 let mock_server = MockServer::start().await;
234 let http_client: HttpClient = HttpClient::builder()
235 .base_url(Url::parse(&mock_server.uri())?)
236 .auth_tokens(AuthTokens {
237 session: "sessiontoken".to_string(),
238 refresh: "refreshtoken".to_string(),
239 })
240 .build()?;
241 let mangadex_client = MangaDexClient::new_with_http_client(http_client);
242
243 let list_id = Uuid::new_v4();
244 let error_id = Uuid::new_v4();
245 let response_body = json!({
246 "result": "error",
247 "errors": [{
248 "id": error_id.to_string(),
249 "status": 404,
250 "title": "Custom list does not exist",
251 "detail": "The provided custom list does not exist or has been deleted"
252 }]
253 });
254
255 Mock::given(method("GET"))
256 .and(path_regex(r"/user/follows/list/[0-9a-fA-F-]+"))
257 .and(header("Authorization", "Bearer sessiontoken"))
258 .respond_with(ResponseTemplate::new(404).set_body_json(response_body))
259 .expect(1)
260 .mount(&mock_server)
261 .await;
262
263 let res = mangadex_client
264 .user()
265 .follows()
266 .list()
267 .id(list_id)
268 .get()
269 .send()
270 .await
271 .unwrap_err();
272
273 match res {
274 Error::Api(error_res) => {
275 assert_eq!(error_res.errors.len(), 1);
276 assert_eq!(error_res.errors[0].status, 404);
277 assert_eq!(
278 error_res.errors[0].title.as_ref().unwrap(),
279 "Custom list does not exist"
280 );
281 }
282 _ => panic!("did not get Error::Api"),
283 }
284
285 Ok(())
286 }
287}