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