mangadex_api/v5/custom_list/id/
get.rs1use derive_builder::Builder;
29use mangadex_api_schema::v5::CustomListData;
30use mangadex_api_types::ReferenceExpansionResource;
31use serde::Serialize;
32use uuid::Uuid;
33
34use crate::HttpClientRef;
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 = "crate::error::BuilderError")
45)]
46#[non_exhaustive]
47pub struct GetCustomList {
48 #[doc(hidden)]
50 #[serde(skip)]
51 #[builder(pattern = "immutable")]
52 #[cfg_attr(feature = "deserializable-endpoint", getset(set = "pub", get = "pub"))]
53 pub http_client: HttpClientRef,
54
55 #[serde(skip_serializing)]
56 pub list_id: Uuid,
57
58 #[serde(skip_serializing)]
59 #[builder(default)]
60 pub with_auth: bool,
61
62 #[serde(skip_serializing_if = "Vec::is_empty")]
63 #[builder(setter(each = "include"), default)]
64 pub includes: Vec<ReferenceExpansionResource>,
65}
66
67endpoint! {
68 GET ("/list/{}", list_id),
69 #[query auth => with_auth] GetCustomList,
70 #[flatten_result] crate::Result<CustomListData>,
71 GetCustomListBuilder
72}
73
74#[cfg(test)]
75mod tests {
76 use fake::Fake;
77 use fake::faker::name::en::Name;
78 use mangadex_api_schema::v5::AuthTokens;
79 use serde_json::json;
80 use url::Url;
81 use uuid::Uuid;
82 use wiremock::matchers::{header, method, path_regex};
83 use wiremock::{Mock, MockServer, ResponseTemplate};
84
85 use crate::error::Error;
86 use crate::{HttpClient, MangaDexClient};
87 use mangadex_api_types::CustomListVisibility;
88
89 #[tokio::test]
90 async fn get_custom_list_fires_a_request_to_base_url() -> anyhow::Result<()> {
91 let mock_server = MockServer::start().await;
92 let http_client = HttpClient::builder()
93 .base_url(Url::parse(&mock_server.uri())?)
94 .build()?;
95 let mangadex_client = MangaDexClient::new_with_http_client(http_client);
96
97 let list_id = Uuid::new_v4();
98 let list_name: String = Name().fake();
99 let response_body = json!({
100 "result": "ok",
101 "response": "entity",
102 "data": {
103 "id": list_id,
104 "type": "custom_list",
105 "attributes": {
106 "name": list_name,
107 "visibility": "private",
108 "version": 1
109 },
110 "relationships": []
111 }
112 });
113
114 Mock::given(method("GET"))
115 .and(path_regex(r"/list/[0-9a-fA-F-]+"))
116 .respond_with(ResponseTemplate::new(200).set_body_json(response_body))
117 .expect(1)
118 .mount(&mock_server)
119 .await;
120
121 let res = mangadex_client
122 .custom_list()
123 .id(list_id)
124 .get()
125 .send()
126 .await?;
127
128 assert_eq!(res.data.id, list_id);
129 assert_eq!(res.data.attributes.name, list_name);
130 assert_eq!(
131 res.data.attributes.visibility,
132 CustomListVisibility::Private
133 );
134 assert_eq!(res.data.attributes.version, 1);
135
136 Ok(())
137 }
138
139 #[tokio::test]
140 async fn get_custom_list_handles_404() -> anyhow::Result<()> {
141 let mock_server = MockServer::start().await;
142 let http_client: HttpClient = HttpClient::builder()
143 .base_url(Url::parse(&mock_server.uri())?)
144 .build()?;
145 let mangadex_client = MangaDexClient::new_with_http_client(http_client);
146
147 let list_id = Uuid::new_v4();
148 let error_id = Uuid::new_v4();
149
150 let response_body = json!({
151 "result": "error",
152 "errors": [{
153 "id": error_id.to_string(),
154 "status": 404,
155 "title": "Not found",
156 "detail": "CustomList could not be found"
157 }]
158 });
159
160 Mock::given(method("GET"))
161 .and(path_regex(r"/list/[0-9a-fA-F-]+"))
162 .respond_with(ResponseTemplate::new(404).set_body_json(response_body))
163 .expect(1)
164 .mount(&mock_server)
165 .await;
166
167 let res = mangadex_client
168 .custom_list()
169 .id(list_id)
170 .get()
171 .send()
172 .await
173 .expect_err("expected error");
174
175 if let Error::Api(errors) = res {
176 assert_eq!(errors.errors.len(), 1);
177
178 assert_eq!(errors.errors[0].id, error_id);
179 assert_eq!(errors.errors[0].status, 404);
180 assert_eq!(errors.errors[0].title, Some("Not found".to_string()));
181 assert_eq!(
182 errors.errors[0].detail,
183 Some("CustomList could not be found".to_string())
184 );
185 }
186
187 Ok(())
188 }
189
190 #[tokio::test]
191 async fn get_custom_list_handles_auth() -> anyhow::Result<()> {
192 let mock_server = MockServer::start().await;
193 let http_client: HttpClient = HttpClient::builder()
194 .base_url(Url::parse(&mock_server.uri())?)
195 .auth_tokens(non_exhaustive::non_exhaustive!(AuthTokens {
196 session: "sessiontoken".into(),
197 refresh: "refresh".into(),
198 }))
199 .build()?;
200 let mangadex_client = MangaDexClient::new_with_http_client(http_client);
201
202 let list_id = Uuid::new_v4();
203 let error_id = Uuid::new_v4();
204
205 let response_body = json!({
206 "result": "error",
207 "errors": [{
208 "id": error_id.to_string(),
209 "status": 404,
210 "title": "Not found",
211 "detail": "CustomList could not be found"
212 }]
213 });
214
215 Mock::given(method("GET"))
216 .and(path_regex(r"/list/[0-9a-fA-F-]+"))
217 .and(header("Authorization", "Bearer sessiontoken"))
218 .respond_with(ResponseTemplate::new(404).set_body_json(response_body))
219 .expect(1)
220 .mount(&mock_server)
221 .await;
222
223 let res = mangadex_client
224 .custom_list()
225 .id(list_id)
226 .get()
227 .with_auth(true)
228 .send()
229 .await
230 .expect_err("expected error");
231
232 if let Error::Api(errors) = res {
233 assert_eq!(errors.errors.len(), 1);
234
235 assert_eq!(errors.errors[0].id, error_id);
236 assert_eq!(errors.errors[0].status, 404);
237 assert_eq!(errors.errors[0].title, Some("Not found".to_string()));
238 assert_eq!(
239 errors.errors[0].detail,
240 Some("CustomList could not be found".to_string())
241 );
242 }
243
244 Ok(())
245 }
246}