mangadex_api/v5/upload/begin/
post.rs

1//! Builder for starting an upload session.
2//!
3//! <https://api.mangadex.org/docs/swagger.html#/Upload/begin-upload-session>
4//!
5//! ```rust
6//! use uuid::Uuid;
7//!
8//! use mangadex_api::MangaDexClient;
9//! // use mangadex_api_types::{Password, Username};
10//! use mangadex_api_types::Language;
11//!
12//! # async fn run() -> anyhow::Result<()> {
13//! let client = MangaDexClient::default();
14//!
15//! /*
16//!
17//!     let _login_res = client
18//!         .auth()
19//!         .login()
20//!         .post()
21//!         .username(Username::parse("myusername")?)
22//!         .password(Password::parse("hunter23")?)
23//!         .send()
24//!         .await?;
25//!
26//!  */
27//!
28//! let group_id = Uuid::new_v4();
29//! let manga_id = Uuid::new_v4();
30//! let res = client
31//!     .upload()
32//!     .begin()
33//!     .post()
34//!     .add_group_id(&group_id)
35//!     .manga_id(manga_id)
36//!     .send()
37//!     .await?;
38//!
39//! println!("session start: {:?}", res);
40//! # Ok(())
41//! # }
42//! ```
43
44use derive_builder::Builder;
45use mangadex_api_schema::v5::UploadSessionData;
46use serde::Serialize;
47use uuid::Uuid;
48
49use crate::HttpClientRef;
50
51/// Start an upload session.
52///
53/// This requires authentication.
54///
55/// Makes a request to `POST /upload/begin`.
56#[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)]
66pub struct StartUploadSession {
67    /// This should never be set manually as this is only for internal use.
68    #[doc(hidden)]
69    #[serde(skip)]
70    #[builder(pattern = "immutable")]
71    #[cfg_attr(feature = "deserializable-endpoint", getset(set = "pub", get = "pub"))]
72    pub http_client: HttpClientRef,
73
74    #[builder(setter(each = "add_group_id"))]
75    #[builder(default)]
76    pub groups: Vec<Uuid>,
77    #[serde(rename = "manga")]
78    pub manga_id: Uuid,
79}
80
81endpoint! {
82    POST "/upload/begin",
83    #[body auth] StartUploadSession,
84    #[rate_limited] UploadSessionData,
85    StartUploadSessionBuilder
86}
87
88#[cfg(test)]
89mod tests {
90    use serde_json::json;
91    use time::OffsetDateTime;
92    use url::Url;
93    use uuid::Uuid;
94    use wiremock::matchers::{body_json, header, method, path};
95    use wiremock::{Mock, MockServer, ResponseTemplate};
96
97    use crate::v5::AuthTokens;
98    use crate::{HttpClient, MangaDexClient};
99    use mangadex_api_types::{MangaDexDateTime, RelationshipType};
100
101    #[tokio::test]
102    async fn start_upload_session_fires_a_request_to_base_url() -> anyhow::Result<()> {
103        let mock_server = MockServer::start().await;
104        let http_client: HttpClient = HttpClient::builder()
105            .base_url(Url::parse(&mock_server.uri())?)
106            .auth_tokens(AuthTokens {
107                session: "sessiontoken".to_string(),
108                refresh: "refreshtoken".to_string(),
109            })
110            .build()?;
111        let mangadex_client = MangaDexClient::new_with_http_client(http_client);
112
113        let group_id = Uuid::new_v4();
114        let manga_id = Uuid::new_v4();
115        let session_id = Uuid::new_v4();
116
117        let datetime = MangaDexDateTime::new(&OffsetDateTime::now_utc());
118
119        let expected_body = json!({
120            "groups": [
121                group_id
122            ],
123            "manga": manga_id
124        });
125        let response_body = json!({
126            "result": "ok",
127            "response": "entity",
128            "data": {
129                "id": session_id,
130                "type": "upload_session",
131                "attributes": {
132                    "isCommitted": false,
133                    "isProcessed": false,
134                    "isDeleted": false,
135                    "version": 1,
136                    "createdAt": datetime.to_string(),
137                    "updatedAt": datetime.to_string(),
138                },
139                "relationships": []
140            }
141        });
142
143        Mock::given(method("POST"))
144            .and(path("/upload/begin"))
145            .and(header("Authorization", "Bearer sessiontoken"))
146            .and(header("Content-Type", "application/json"))
147            .and(body_json(expected_body))
148            .respond_with(
149                ResponseTemplate::new(200)
150                    .insert_header("x-ratelimit-retry-after", "1698723860")
151                    .insert_header("x-ratelimit-limit", "40")
152                    .insert_header("x-ratelimit-remaining", "39")
153                    .set_body_json(response_body),
154            )
155            .expect(1)
156            .mount(&mock_server)
157            .await;
158
159        let res = mangadex_client
160            .upload()
161            .begin()
162            .post()
163            .add_group_id(group_id)
164            .manga_id(manga_id)
165            .send()
166            .await?;
167
168        let res = &res.data;
169        assert_eq!(res.id, session_id);
170        assert_eq!(res.type_, RelationshipType::UploadSession);
171        assert!(!res.attributes.is_committed);
172        assert!(!res.attributes.is_processed);
173        assert!(!res.attributes.is_deleted);
174        assert_eq!(res.attributes.version, 1);
175        assert_eq!(res.attributes.created_at.to_string(), datetime.to_string());
176        assert_eq!(res.attributes.updated_at.to_string(), datetime.to_string());
177
178        Ok(())
179    }
180}