mangadex_api/v5/rating/manga_id/
post.rs1use derive_builder::Builder;
47use serde::Serialize;
48use uuid::Uuid;
49
50use crate::HttpClientRef;
51use mangadex_api_schema::NoData;
52use mangadex_api_types::error::Result;
53
54#[cfg_attr(
55 feature = "deserializable-endpoint",
56 derive(serde::Deserialize, getset::Getters, getset::Setters)
57)]
58#[derive(Debug, Serialize, Clone, Builder)]
59#[serde(rename_all = "camelCase")]
60#[builder(
61 setter(into, strip_option),
62 build_fn(error = "mangadex_api_types::error::BuilderError")
63)]
64pub struct CreateUpdateMangaRating {
65 #[doc(hidden)]
67 #[serde(skip)]
68 #[builder(pattern = "immutable")]
69 #[cfg_attr(feature = "deserializable-endpoint", getset(set = "pub", get = "pub"))]
70 pub http_client: HttpClientRef,
71
72 #[serde(skip_serializing)]
73 pub manga_id: Uuid,
74
75 pub rating: u8,
79}
80
81impl CreateUpdateMangaRating {
82 pub async fn send(&mut self) -> Result<NoData> {
83 self.rating = self.rating.clamp(1, 10);
84
85 #[cfg(all(
86 not(feature = "multi-thread"),
87 not(feature = "tokio-multi-thread"),
88 not(feature = "rw-multi-thread")
89 ))]
90 let res = self.http_client.try_borrow()?.send_request(self).await??;
91 #[cfg(any(feature = "multi-thread", feature = "tokio-multi-thread"))]
92 let res = self.http_client.lock().await.send_request(self).await??;
93
94 #[cfg(feature = "rw-multi-thread")]
95 let res = self.http_client.read().await.send_request(self).await??;
96
97 Ok(res)
98 }
99}
100
101endpoint! {
102 POST ("/rating/{}", manga_id),
103 #[body auth] CreateUpdateMangaRating,
104 #[no_send] Result<NoData>
105}
106
107builder_send! {
108 #[builder] CreateUpdateMangaRatingBuilder,
109 NoData
110}
111
112#[cfg(test)]
113mod tests {
114 use serde_json::json;
115 use url::Url;
116 use uuid::Uuid;
117 use wiremock::matchers::{body_json, header, method, path_regex};
118 use wiremock::{Mock, MockServer, ResponseTemplate};
119
120 use crate::v5::AuthTokens;
121 use crate::{HttpClient, MangaDexClient};
122 use mangadex_api_types::error::Error;
123
124 #[tokio::test]
125 async fn create_update_manga_rating_fires_a_request_to_base_url() -> anyhow::Result<()> {
126 let mock_server = MockServer::start().await;
127 let http_client = HttpClient::builder()
128 .base_url(Url::parse(&mock_server.uri())?)
129 .auth_tokens(AuthTokens {
130 session: "sessiontoken".to_string(),
131 refresh: "refreshtoken".to_string(),
132 })
133 .build()?;
134 let mangadex_client = MangaDexClient::new_with_http_client(http_client);
135
136 let expected_body = json!({
137 "rating": 9
138 });
139
140 let manga_id = Uuid::new_v4();
141 let response_body = json!({
142 "result": "ok",
143 });
144
145 Mock::given(method("POST"))
146 .and(path_regex(r"/rating/[0-9a-fA-F-]+"))
147 .and(header("Content-Type", "application/json"))
148 .and(body_json(expected_body))
149 .respond_with(ResponseTemplate::new(200).set_body_json(response_body))
150 .expect(1)
151 .mount(&mock_server)
152 .await;
153
154 let _res = mangadex_client
155 .rating()
156 .manga_id(manga_id)
157 .post()
158 .rating(9)
159 .send()
160 .await?;
161
162 Ok(())
163 }
164
165 #[tokio::test]
166 async fn create_update_manga_rating_sets_rating_below_min_to_1() -> anyhow::Result<()> {
167 let mock_server = MockServer::start().await;
168 let http_client = HttpClient::builder()
169 .base_url(Url::parse(&mock_server.uri())?)
170 .auth_tokens(AuthTokens {
171 session: "sessiontoken".to_string(),
172 refresh: "refreshtoken".to_string(),
173 })
174 .build()?;
175 let mangadex_client = MangaDexClient::new_with_http_client(http_client);
176
177 let expected_body = json!({
178 "rating": 1
179 });
180
181 let manga_id = Uuid::new_v4();
182 let response_body = json!({
183 "result": "ok",
184 });
185
186 Mock::given(method("POST"))
187 .and(path_regex(r"/rating/[0-9a-fA-F-]+"))
188 .and(header("Content-Type", "application/json"))
189 .and(body_json(expected_body))
190 .respond_with(ResponseTemplate::new(200).set_body_json(response_body))
191 .expect(1)
192 .mount(&mock_server)
193 .await;
194
195 let _res = mangadex_client
196 .rating()
197 .manga_id(manga_id)
198 .post()
199 .rating(0)
200 .send()
201 .await?;
202
203 Ok(())
204 }
205
206 #[tokio::test]
207 async fn create_update_manga_rating_sets_rating_above_max_to_10() -> anyhow::Result<()> {
208 let mock_server = MockServer::start().await;
209 let http_client = HttpClient::builder()
210 .base_url(Url::parse(&mock_server.uri())?)
211 .auth_tokens(AuthTokens {
212 session: "sessiontoken".to_string(),
213 refresh: "refreshtoken".to_string(),
214 })
215 .build()?;
216 let mangadex_client = MangaDexClient::new_with_http_client(http_client);
217
218 let expected_body = json!({
219 "rating": 10
220 });
221
222 let manga_id = Uuid::new_v4();
223 let response_body = json!({
224 "result": "ok",
225 });
226
227 Mock::given(method("POST"))
228 .and(path_regex(r"/rating/[0-9a-fA-F-]+"))
229 .and(header("Content-Type", "application/json"))
230 .and(body_json(expected_body))
231 .respond_with(ResponseTemplate::new(200).set_body_json(response_body))
232 .expect(1)
233 .mount(&mock_server)
234 .await;
235
236 let _res = mangadex_client
237 .rating()
238 .manga_id(manga_id)
239 .post()
240 .rating(11)
241 .send()
242 .await?;
243
244 Ok(())
245 }
246
247 #[tokio::test]
248 async fn create_update_manga_rating_requires_auth() -> anyhow::Result<()> {
249 let mock_server = MockServer::start().await;
250 let http_client: HttpClient = HttpClient::builder()
251 .base_url(Url::parse(&mock_server.uri())?)
252 .build()?;
253 let mangadex_client = MangaDexClient::new_with_http_client(http_client);
254
255 let manga_id = Uuid::new_v4();
256 let error_id = Uuid::new_v4();
257 let response_body = json!({
258 "result": "error",
259 "errors": [{
260 "id": error_id.to_string(),
261 "status": 403,
262 "title": "Forbidden",
263 "detail": "You must be logged in to continue."
264 }]
265 });
266
267 Mock::given(method("POST"))
268 .and(path_regex(r"/rating/[0-9a-fA-F-]+"))
269 .and(header("Content-Type", "application/json"))
270 .respond_with(ResponseTemplate::new(403).set_body_json(response_body))
271 .expect(0)
272 .mount(&mock_server)
273 .await;
274
275 let res = mangadex_client
276 .rating()
277 .manga_id(manga_id)
278 .post()
279 .rating(7)
280 .send()
281 .await
282 .expect_err("expected error");
283
284 match res {
285 Error::MissingTokens => {}
286 _ => panic!("unexpected error: {:#?}", res),
287 }
288
289 Ok(())
290 }
291}