mangadex_api/v5/manga/id/status/
post.rs

1//! Builder for the update manga reading status endpoint.
2//!
3//! <https://api.mangadex.org/docs/swagger.html#/Manga/post-manga-id-status>
4//!
5//! # Examples
6//!
7//! ```rust
8//! use uuid::Uuid;
9//!
10//! use mangadex_api_types::ReadingStatus;
11//! use mangadex_api::v5::MangaDexClient;
12//!
13//! # async fn run() -> anyhow::Result<()> {
14//! let client = MangaDexClient::default();
15//!
16//! let manga_id = Uuid::new_v4();
17//! let res = client
18//!     .manga()
19//!     .id(manga_id)
20//!     .status()
21//!     .post()
22//!     .status(Some(ReadingStatus::Reading))
23//!     .send()
24//!     .await?;
25//!
26//! println!("status: {:?}", res);
27//! # Ok(())
28//! # }
29//! ```
30
31use derive_builder::Builder;
32use serde::Serialize;
33use uuid::Uuid;
34
35use crate::HttpClientRef;
36use mangadex_api_schema::NoData;
37use mangadex_api_types::error::Result;
38use mangadex_api_types::ReadingStatus;
39
40#[cfg_attr(
41    feature = "deserializable-endpoint",
42    derive(serde::Deserialize, getset::Getters, getset::Setters)
43)]
44#[derive(Debug, Serialize, Clone, Builder)]
45#[serde(rename_all = "camelCase")]
46#[builder(
47    setter(into),
48    build_fn(error = "mangadex_api_types::error::BuilderError")
49)]
50#[cfg_attr(
51    feature = "custom_list_v2",
52    deprecated(
53        since = "3.0.0-rc.1",
54        note = "After the introduction of the Subscription system, this endpoint will be removed in v3"
55    )
56)]
57pub struct UpdateMangaReadingStatus {
58    /// This should never be set manually as this is only for internal use.
59    #[doc(hidden)]
60    #[serde(skip)]
61    #[builder(pattern = "immutable")]
62    #[cfg_attr(feature = "deserializable-endpoint", getset(set = "pub", get = "pub"))]
63    pub http_client: HttpClientRef,
64
65    #[serde(skip_serializing)]
66    pub manga_id: Uuid,
67
68    /// Using a `None` (`null`) value will remove the reading status.
69    pub status: Option<ReadingStatus>,
70}
71
72endpoint! {
73    POST ("/manga/{}/status", manga_id),
74    #[body auth] UpdateMangaReadingStatus,
75    #[discard_result] Result<NoData>,
76    UpdateMangaReadingStatusBuilder
77}
78
79#[cfg(test)]
80mod tests {
81    use serde_json::json;
82    use url::Url;
83    use uuid::Uuid;
84    use wiremock::matchers::{body_json, header, method, path_regex};
85    use wiremock::{Mock, MockServer, ResponseTemplate};
86
87    use crate::v5::AuthTokens;
88    use crate::{HttpClient, MangaDexClient};
89    use mangadex_api_types::ReadingStatus;
90
91    #[tokio::test]
92    async fn update_manga_reading_status_fires_a_request_to_base_url() -> anyhow::Result<()> {
93        let mock_server = MockServer::start().await;
94        let http_client = HttpClient::builder()
95            .base_url(Url::parse(&mock_server.uri())?)
96            .auth_tokens(AuthTokens {
97                session: "sessiontoken".to_string(),
98                refresh: "refreshtoken".to_string(),
99            })
100            .build()?;
101        let mangadex_client = MangaDexClient::new_with_http_client(http_client);
102
103        let manga_id = Uuid::new_v4();
104        let expected_body = json!({
105            "status": "reading",
106        });
107        let response_body = json!({
108            "result": "ok",
109        });
110
111        Mock::given(method("POST"))
112            .and(path_regex(r"/manga/[0-9a-fA-F-]+/status"))
113            .and(header("Authorization", "Bearer sessiontoken"))
114            .and(header("Content-Type", "application/json"))
115            .and(body_json(expected_body))
116            .respond_with(ResponseTemplate::new(200).set_body_json(response_body))
117            .expect(1)
118            .mount(&mock_server)
119            .await;
120
121        mangadex_client
122            .manga()
123            .id(manga_id)
124            .status()
125            .post()
126            .status(Some(ReadingStatus::Reading))
127            .send()
128            .await?;
129
130        Ok(())
131    }
132
133    #[tokio::test]
134    async fn update_manga_reading_status_sets_status_to_null() -> anyhow::Result<()> {
135        let mock_server = MockServer::start().await;
136        let http_client = HttpClient::builder()
137            .base_url(Url::parse(&mock_server.uri())?)
138            .auth_tokens(AuthTokens {
139                session: "sessiontoken".to_string(),
140                refresh: "refreshtoken".to_string(),
141            })
142            .build()?;
143        let mangadex_client = MangaDexClient::new_with_http_client(http_client);
144
145        let manga_id = Uuid::new_v4();
146        let expected_body = json!({
147            "status": null,
148        });
149        let response_body = json!({
150            "result": "ok",
151        });
152
153        Mock::given(method("POST"))
154            .and(path_regex(r"/manga/[0-9a-fA-F-]+/status"))
155            .and(header("Authorization", "Bearer sessiontoken"))
156            .and(header("Content-Type", "application/json"))
157            .and(body_json(expected_body))
158            .respond_with(ResponseTemplate::new(200).set_body_json(response_body))
159            .expect(1)
160            .mount(&mock_server)
161            .await;
162
163        mangadex_client
164            .manga()
165            .id(manga_id)
166            .status()
167            .post()
168            .status(None)
169            .send()
170            .await?;
171
172        Ok(())
173    }
174}