mangadex_api/v5/upload/begin/
post.rs1use derive_builder::Builder;
45use mangadex_api_schema::v5::UploadSessionData;
46use serde::Serialize;
47use uuid::Uuid;
48
49use crate::HttpClientRef;
50
51#[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 #[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}