mangadex_api/v5/forums/thread/
post.rs

1//! Builder for the Forum Thread creation endpoint.
2//!
3//! <https://api.mangadex.org/docs/swagger.html#/Forums/forums-thread-create>
4//!
5//! # Examples
6//!
7//! ```rust
8//! use uuid::Uuid;
9//!
10//! use mangadex_api::v5::MangaDexClient;
11//! use mangadex_api_types::ForumThreadType;
12//! // use mangadex_api_types::{Password, Username};
13//!
14//! # async fn run() -> anyhow::Result<()> {
15//! let client = MangaDexClient::default();
16//!
17//! /*
18//!     let _login_res = client
19//!      .auth()
20//!      .login()
21//!      .username(Username::parse("myusername")?)
22//!      .password(Password::parse("hunter23")?)
23//!      .send()
24//!      .await?;
25//! */
26//!
27//! let manga_id = Uuid::new_v4();
28//! let res = client
29//!     .forums()
30//!     .thread()
31//!     .post()
32//!     .id(manga_id)
33//!     .type_(ForumThreadType::Manga)
34//!     .send()
35//!     .await?;
36//!
37//! println!("custom list create: {:?}", res);
38//! # Ok(())
39//! # }
40//! ```
41
42use crate::HttpClientRef;
43use derive_builder::Builder;
44use mangadex_api_schema::v5::ForumThreadResponseData;
45use mangadex_api_types::ForumThreadType;
46use serde::Serialize;
47use uuid::Uuid;
48
49#[cfg_attr(
50    feature = "deserializable-endpoint",
51    derive(serde::Deserialize, getset::Getters, getset::Setters)
52)]
53#[derive(Debug, Serialize, Clone, Builder)]
54#[serde(rename_all = "camelCase")]
55#[builder(
56    setter(into, strip_option),
57    build_fn(error = "mangadex_api_types::error::BuilderError")
58)]
59#[cfg_attr(feature = "non_exhaustive", non_exhaustive)]
60pub struct CreateForumThread {
61    /// This should never be set manually as this is only for internal use.
62    #[doc(hidden)]
63    #[serde(skip)]
64    #[builder(pattern = "immutable")]
65    #[cfg_attr(feature = "deserializable-endpoint", getset(set = "pub", get = "pub"))]
66    pub http_client: HttpClientRef,
67
68    pub type_: ForumThreadType,
69    pub id: Uuid,
70}
71
72endpoint! {
73    POST "/forums/thread",
74    #[body auth] CreateForumThread,
75    #[rate_limited] ForumThreadResponseData,
76    CreateForumThreadBuilder
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::ForumThreadType;
90
91    #[tokio::test]
92    async fn create_a_forums_thread_handle_ok() -> 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 body_id = Uuid::new_v4();
104        let response_body = json!({
105            "result": "ok",
106            "response": "entity",
107            "data": {
108                "type": "thread",
109                "id": 0,
110                "attributes": {
111                    "repliesCount": 0
112                }
113            }
114        });
115
116        Mock::given(method("POST"))
117            .and(path_regex("/forums/thread"))
118            .and(header("Authorization", "Bearer sessiontoken"))
119            .and(header("Content-Type", "application/json"))
120            .and(body_json(json!({
121                "type": "manga",
122                "id": body_id
123            })))
124            .respond_with(
125                ResponseTemplate::new(200)
126                    .insert_header("x-ratelimit-retry-after", "1698723860")
127                    .insert_header("x-ratelimit-limit", "40")
128                    .insert_header("x-ratelimit-remaining", "39")
129                    .set_body_json(response_body),
130            )
131            .expect(1)
132            .mount(&mock_server)
133            .await;
134
135        mangadex_client
136            .forums()
137            .thread()
138            .post()
139            .id(body_id)
140            .type_(ForumThreadType::Manga)
141            .send()
142            .await?;
143
144        Ok(())
145    }
146
147    #[tokio::test]
148    async fn create_a_forums_thread_handle_403_error() -> anyhow::Result<()> {
149        let mock_server = MockServer::start().await;
150        let http_client = HttpClient::builder()
151            .base_url(Url::parse(&mock_server.uri())?)
152            .auth_tokens(AuthTokens {
153                session: "sessiontoken".to_string(),
154                refresh: "refreshtoken".to_string(),
155            })
156            .build()?;
157        let mangadex_client = MangaDexClient::new_with_http_client(http_client);
158
159        let body_id = Uuid::new_v4();
160        let response_body = json!({
161            "result": "error",
162            "errors": [
163                {
164                    "id": "string",
165                    "status": 0,
166                    "title": "string",
167                    "detail": "string"
168                }
169            ]
170        });
171
172        Mock::given(method("POST"))
173            .and(path_regex("/forums/thread"))
174            .and(header("Authorization", "Bearer sessiontoken"))
175            .and(header("Content-Type", "application/json"))
176            .and(body_json(json!({
177                "type": "manga",
178                "id": body_id
179            })))
180            .respond_with(
181                ResponseTemplate::new(403)
182                    .insert_header("x-ratelimit-retry-after", "1698723860")
183                    .insert_header("x-ratelimit-limit", "40")
184                    .insert_header("x-ratelimit-remaining", "39")
185                    .set_body_json(response_body),
186            )
187            .expect(1)
188            .mount(&mock_server)
189            .await;
190
191        mangadex_client
192            .forums()
193            .thread()
194            .post()
195            .id(body_id)
196            .type_(ForumThreadType::Manga)
197            .send()
198            .await
199            .expect_err("an error should be received");
200
201        Ok(())
202    }
203
204    #[tokio::test]
205    async fn create_a_forums_thread_handle_404_error() -> anyhow::Result<()> {
206        let mock_server = MockServer::start().await;
207        let http_client = HttpClient::builder()
208            .base_url(Url::parse(&mock_server.uri())?)
209            .auth_tokens(AuthTokens {
210                session: "sessiontoken".to_string(),
211                refresh: "refreshtoken".to_string(),
212            })
213            .build()?;
214        let mangadex_client = MangaDexClient::new_with_http_client(http_client);
215
216        let body_id = Uuid::new_v4();
217        let response_body = json!({
218            "result": "error",
219            "errors": [
220                {
221                    "id": "string",
222                    "status": 0,
223                    "title": "string",
224                    "detail": "string"
225                }
226            ]
227        });
228
229        Mock::given(method("POST"))
230            .and(path_regex("/forums/thread"))
231            .and(header("Authorization", "Bearer sessiontoken"))
232            .and(header("Content-Type", "application/json"))
233            .and(body_json(json!({
234                "type": "manga",
235                "id": body_id
236            })))
237            .respond_with(
238                ResponseTemplate::new(404)
239                    .insert_header("x-ratelimit-retry-after", "1698723860")
240                    .insert_header("x-ratelimit-limit", "40")
241                    .insert_header("x-ratelimit-remaining", "39")
242                    .set_body_json(response_body),
243            )
244            .expect(1)
245            .mount(&mock_server)
246            .await;
247
248        mangadex_client
249            .forums()
250            .thread()
251            .post()
252            .id(body_id)
253            .type_(ForumThreadType::Manga)
254            .send()
255            .await
256            .expect_err("an error should be received");
257
258        Ok(())
259    }
260}