mangadex_api/v5/chapter/id/
get.rs1use derive_builder::Builder;
29use serde::Serialize;
30use uuid::Uuid;
31
32use crate::HttpClientRef;
33use mangadex_api_schema::v5::ChapterResponse;
34use mangadex_api_types::ReferenceExpansionResource;
35
36#[cfg_attr(
37 feature = "deserializable-endpoint",
38 derive(serde::Deserialize, getset::Getters, getset::Setters)
39)]
40#[derive(Debug, Serialize, Clone, Builder)]
41#[serde(rename_all = "camelCase")]
42#[builder(
43 setter(into, strip_option),
44 build_fn(error = "mangadex_api_types::error::BuilderError")
45)]
46pub struct GetChapter {
47 #[doc(hidden)]
49 #[serde(skip)]
50 #[builder(pattern = "immutable")]
51 #[cfg_attr(feature = "deserializable-endpoint", getset(set = "pub", get = "pub"))]
52 pub http_client: HttpClientRef,
53
54 #[serde(skip_serializing)]
55 pub chapter_id: Uuid,
56
57 #[builder(setter(each = "include"), default)]
58 pub includes: Vec<ReferenceExpansionResource>,
59}
60
61endpoint! {
62 GET ("/chapter/{}", chapter_id),
63 #[query] GetChapter,
64 #[flatten_result] ChapterResponse,
65 GetChapterBuilder
66}
67
68#[cfg(test)]
69mod tests {
70 use fake::faker::name::en::Name;
71 use fake::Fake;
72 use serde_json::json;
73 use time::OffsetDateTime;
74 use url::Url;
75 use uuid::Uuid;
76 use wiremock::matchers::{method, path_regex};
77 use wiremock::{Mock, MockServer, ResponseTemplate};
78
79 use crate::{HttpClient, MangaDexClient};
80 use mangadex_api_types::error::Error;
81 use mangadex_api_types::{Language, MangaDexDateTime, ResponseType};
82
83 #[tokio::test]
84 async fn get_chapter_fires_a_request_to_base_url() -> anyhow::Result<()> {
85 let mock_server = MockServer::start().await;
86 let http_client = HttpClient::builder()
87 .base_url(Url::parse(&mock_server.uri())?)
88 .build()?;
89 let mangadex_client = MangaDexClient::new_with_http_client(http_client);
90
91 let chapter_id = Uuid::new_v4();
92 let uploader_id = Uuid::new_v4();
93 let chapter_title: String = Name().fake();
94
95 let datetime = MangaDexDateTime::new(&OffsetDateTime::now_utc());
96
97 let response_body = json!({
98 "result": "ok",
99 "response": "entity",
100 "data": {
101 "id": chapter_id,
102 "type": "chapter",
103 "attributes": {
104 "title": chapter_title,
105 "volume": "1",
106 "chapter": "1.5",
107 "pages": 4,
108 "translatedLanguage": "en",
109 "uploader": uploader_id,
110 "version": 1,
111 "createdAt": datetime.to_string(),
112 "updatedAt": datetime.to_string(),
113 "publishAt": datetime.to_string(),
114 "readableAt": datetime.to_string(),
115 },
116 "relationships": []
117 }
118 });
119
120 Mock::given(method("GET"))
121 .and(path_regex(r"/chapter/[0-9a-fA-F-]+"))
122 .respond_with(ResponseTemplate::new(200).set_body_json(response_body))
123 .expect(1)
124 .mount(&mock_server)
125 .await;
126
127 let res = mangadex_client
128 .chapter()
129 .id(chapter_id)
130 .get()
131 .build()?
132 .send()
133 .await?;
134
135 assert_eq!(res.response, ResponseType::Entity);
136 assert_eq!(res.data.id, chapter_id);
137 assert_eq!(res.data.attributes.title, Some(chapter_title));
138 assert_eq!(res.data.attributes.volume, Some("1".to_string()));
139 assert_eq!(res.data.attributes.chapter, Some("1.5".to_string()));
140 assert_eq!(res.data.attributes.pages, 4);
141 assert_eq!(res.data.attributes.translated_language, Language::English);
142 assert_eq!(res.data.attributes.version, 1);
143 assert_eq!(
144 res.data.attributes.created_at.to_string(),
145 datetime.to_string()
146 );
147 assert_eq!(
148 res.data.attributes.updated_at.as_ref().unwrap().to_string(),
149 datetime.to_string()
150 );
151 assert_eq!(
152 res.data.attributes.publish_at.unwrap().to_string(),
153 datetime.to_string()
154 );
155
156 Ok(())
157 }
158
159 #[tokio::test]
160 async fn get_chapter_handles_404() -> anyhow::Result<()> {
161 let mock_server = MockServer::start().await;
162 let http_client: HttpClient = HttpClient::builder()
163 .base_url(Url::parse(&mock_server.uri())?)
164 .build()?;
165 let mangadex_client = MangaDexClient::new_with_http_client(http_client);
166
167 let chapter_id = Uuid::new_v4();
168 let error_id = Uuid::new_v4();
169
170 let response_body = json!({
171 "result": "error",
172 "errors": [{
173 "id": error_id.to_string(),
174 "status": 404,
175 "title": "Not found",
176 "detail": "Chapter could not be found"
177 }]
178 });
179
180 Mock::given(method("GET"))
181 .and(path_regex(r"/chapter/[0-9a-fA-F-]+"))
182 .respond_with(ResponseTemplate::new(404).set_body_json(response_body))
183 .expect(1)
184 .mount(&mock_server)
185 .await;
186
187 let res = mangadex_client
188 .chapter()
189 .id(chapter_id)
190 .get()
191 .build()?
192 .send()
193 .await
194 .expect_err("expected error");
195
196 if let Error::Api(errors) = res {
197 assert_eq!(errors.errors.len(), 1);
198
199 assert_eq!(errors.errors[0].id, error_id);
200 assert_eq!(errors.errors[0].status, 404);
201 assert_eq!(errors.errors[0].title, Some("Not found".to_string()));
202 assert_eq!(
203 errors.errors[0].detail,
204 Some("Chapter could not be found".to_string())
205 );
206 }
207
208 Ok(())
209 }
210}