Skip to main content

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 = "crate::error::BuilderError")
65)]
66#[non_exhaustive]
67pub struct StartUploadSession {
68    /// This should never be set manually as this is only for internal use.
69    #[doc(hidden)]
70    #[serde(skip)]
71    #[builder(pattern = "immutable")]
72    #[cfg_attr(feature = "deserializable-endpoint", getset(set = "pub", get = "pub"))]
73    pub http_client: HttpClientRef,
74
75    #[builder(setter(each = "add_group_id"))]
76    #[builder(default)]
77    pub groups: Vec<Uuid>,
78    #[serde(rename = "manga")]
79    pub manga_id: Uuid,
80}
81
82endpoint! {
83    POST "/upload/begin",
84    #[body auth] StartUploadSession,
85    #[rate_limited] UploadSessionData,
86    StartUploadSessionBuilder
87}
88
89#[cfg(test)]
90mod tests {
91    use serde_json::json;
92    use time::OffsetDateTime;
93    use url::Url;
94    use uuid::Uuid;
95    use wiremock::matchers::{body_json, header, method, path};
96    use wiremock::{Mock, MockServer, ResponseTemplate};
97
98    use crate::v5::AuthTokens;
99    use crate::{HttpClient, MangaDexClient};
100    use mangadex_api_types::{MangaDexDateTime, RelationshipType};
101
102    #[tokio::test]
103    async fn start_upload_session_fires_a_request_to_base_url() -> anyhow::Result<()> {
104        let mock_server = MockServer::start().await;
105        let http_client: HttpClient = HttpClient::builder()
106            .base_url(Url::parse(&mock_server.uri())?)
107            .auth_tokens(non_exhaustive::non_exhaustive!(AuthTokens {
108                session: "sessiontoken".to_string(),
109                refresh: "refreshtoken".to_string(),
110            }))
111            .build()?;
112        let mangadex_client = MangaDexClient::new_with_http_client(http_client);
113
114        let group_id = Uuid::new_v4();
115        let manga_id = Uuid::new_v4();
116        let session_id = Uuid::new_v4();
117
118        let datetime = MangaDexDateTime::new(&OffsetDateTime::now_utc());
119
120        let expected_body = json!({
121            "groups": [
122                group_id
123            ],
124            "manga": manga_id
125        });
126        let response_body = json!({
127            "result": "ok",
128            "response": "entity",
129            "data": {
130                "id": session_id,
131                "type": "upload_session",
132                "attributes": {
133                    "isCommitted": false,
134                    "isProcessed": false,
135                    "isDeleted": false,
136                    "version": 1,
137                    "createdAt": datetime.to_string(),
138                    "updatedAt": datetime.to_string(),
139                },
140                "relationships": []
141            }
142        });
143
144        Mock::given(method("POST"))
145            .and(path("/upload/begin"))
146            .and(header("Authorization", "Bearer sessiontoken"))
147            .and(header("Content-Type", "application/json"))
148            .and(body_json(expected_body))
149            .respond_with(
150                ResponseTemplate::new(200)
151                    .insert_header("x-ratelimit-retry-after", "1698723860")
152                    .insert_header("x-ratelimit-limit", "40")
153                    .insert_header("x-ratelimit-remaining", "39")
154                    .set_body_json(response_body),
155            )
156            .expect(1)
157            .mount(&mock_server)
158            .await;
159
160        let res = mangadex_client
161            .upload()
162            .begin()
163            .post()
164            .add_group_id(group_id)
165            .manga_id(manga_id)
166            .send()
167            .await?;
168
169        let res = &res.data;
170        assert_eq!(res.id, session_id);
171        assert_eq!(res.type_, RelationshipType::UploadSession);
172        assert!(!res.attributes.is_committed);
173        assert!(!res.attributes.is_processed);
174        assert!(!res.attributes.is_deleted);
175        assert_eq!(res.attributes.version, 1);
176        assert_eq!(res.attributes.created_at.to_string(), datetime.to_string());
177        assert_eq!(res.attributes.updated_at.to_string(), datetime.to_string());
178
179        Ok(())
180    }
181}