mangadex_api/v5/scanlation_group/
get.rs1use derive_builder::Builder;
27use serde::Serialize;
28use uuid::Uuid;
29
30use crate::HttpClientRef;
31use mangadex_api_schema::v5::GroupListResponse;
32use mangadex_api_types::{GroupSortOrder, Language, ReferenceExpansionResource};
33
34#[cfg_attr(
35 feature = "deserializable-endpoint",
36 derive(serde::Deserialize, getset::Getters, getset::Setters)
37)]
38#[derive(Debug, Serialize, Clone, Builder, Default)]
39#[serde(rename_all = "camelCase")]
40#[builder(
41 setter(into, strip_option),
42 default,
43 build_fn(error = "mangadex_api_types::error::BuilderError")
44)]
45#[cfg_attr(feature = "non_exhaustive", non_exhaustive)]
46pub struct ListGroup {
47 #[doc(hidden)]
48 #[serde(skip)]
49 #[builder(pattern = "immutable")]
50 #[cfg_attr(feature = "deserializable-endpoint", getset(set = "pub", get = "pub"))]
51 pub http_client: HttpClientRef,
52
53 pub limit: Option<u32>,
54 pub offset: Option<u32>,
55 #[builder(setter(each = "add_group_id"))]
56 #[serde(rename = "ids")]
57 pub group_ids: Vec<Uuid>,
58 pub name: Option<String>,
59 pub focused_language: Option<Language>,
62 #[builder(setter(each = "include"))]
63 pub includes: Vec<ReferenceExpansionResource>,
64 pub order: Option<GroupSortOrder>,
65}
66
67endpoint! {
68 GET "/group",
69 #[query] ListGroup,
70 #[flatten_result] GroupListResponse,
71 ListGroupBuilder
72}
73
74#[cfg(test)]
75mod tests {
76 use serde_json::json;
77 use time::OffsetDateTime;
78 use url::Url;
79 use uuid::Uuid;
80 use wiremock::matchers::{method, path};
81 use wiremock::{Mock, MockServer, ResponseTemplate};
82
83 use crate::{HttpClient, MangaDexClient};
84 use mangadex_api_types::error::Error;
85 use mangadex_api_types::{MangaDexDateTime, ResponseType};
86
87 #[tokio::test]
88 async fn list_scanlation_groups_fires_a_request_to_base_url() -> anyhow::Result<()> {
89 let mock_server = MockServer::start().await;
90 let http_client = HttpClient::builder()
91 .base_url(Url::parse(&mock_server.uri())?)
92 .build()?;
93 let mangadex_client = MangaDexClient::new_with_http_client(http_client);
94
95 let group_id = Uuid::new_v4();
96
97 let datetime = MangaDexDateTime::new(&OffsetDateTime::now_utc());
98
99 let response_body = json!({
100 "result": "ok",
101 "response": "collection",
102 "data": [
103 {
104 "id": group_id,
105 "type": "scanlation_group",
106 "attributes": {
107 "name": "Scanlation Group",
108 "altNames": [
109 {
110 "en": "Alternative Name"
111 }
112 ],
113 "website": "https://example.org",
114 "ircServer": null,
115 "ircChannel": null,
116 "discord": null,
117 "contactEmail": null,
118 "description": null,
119 "twitter": null,
120 "focusedLanguages": ["en"],
121 "locked": false,
122 "official": false,
123 "verified": false,
124 "inactive": false,
125 "publishDelay": "P6WT5M",
126 "version": 1,
127 "createdAt": datetime.to_string(),
128 "updatedAt": datetime.to_string(),
129 },
130 "relationships": []
131 }
132 ],
133 "limit": 1,
134 "offset": 0,
135 "total": 1
136 });
137
138 Mock::given(method("GET"))
139 .and(path("/group"))
140 .respond_with(ResponseTemplate::new(200).set_body_json(response_body))
141 .expect(1)
142 .mount(&mock_server)
143 .await;
144
145 let res = mangadex_client
146 .scanlation_group()
147 .get()
148 .limit(1u32)
149 .send()
150 .await?;
151
152 assert_eq!(res.response, ResponseType::Collection);
153 let group = &res.data[0];
154 assert_eq!(group.id, group_id);
155 assert_eq!(group.attributes.name, "Scanlation Group");
156 assert_eq!(
157 group.attributes.website,
158 Some("https://example.org".to_string())
159 );
160 assert_eq!(group.attributes.irc_server, None);
161 assert_eq!(group.attributes.irc_channel, None);
162 assert_eq!(group.attributes.discord, None);
163 assert_eq!(group.attributes.contact_email, None);
164 assert_eq!(group.attributes.description, None);
165 assert!(group.attributes.twitter.is_none());
166 assert!(!group.attributes.locked);
167 assert_eq!(group.attributes.version, 1);
168 assert_eq!(
169 group.attributes.created_at.to_string(),
170 datetime.to_string()
171 );
172 assert_eq!(
173 group.attributes.updated_at.to_string(),
174 datetime.to_string()
175 );
176
177 Ok(())
178 }
179
180 #[tokio::test]
181 async fn list_scanlation_groups_handles_400() -> anyhow::Result<()> {
182 let mock_server = MockServer::start().await;
183 let http_client: HttpClient = HttpClient::builder()
184 .base_url(Url::parse(&mock_server.uri())?)
185 .build()?;
186 let mangadex_client = MangaDexClient::new_with_http_client(http_client);
187
188 let error_id = Uuid::new_v4();
189
190 let response_body = json!({
191 "result": "error",
192 "errors": [{
193 "id": error_id.to_string(),
194 "status": 400,
195 "title": "Invalid limit",
196 "detail": "Limit must be between 1 and 100"
197 }]
198 });
199
200 Mock::given(method("GET"))
201 .and(path("/group"))
202 .respond_with(ResponseTemplate::new(400).set_body_json(response_body))
203 .expect(1)
204 .mount(&mock_server)
205 .await;
206
207 let res = mangadex_client
208 .scanlation_group()
209 .get()
210 .limit(0u32)
211 .send()
212 .await
213 .expect_err("expected error");
214
215 if let Error::Api(errors) = res {
216 assert_eq!(errors.errors.len(), 1);
217
218 assert_eq!(errors.errors[0].id, error_id);
219 assert_eq!(errors.errors[0].status, 400);
220 assert_eq!(errors.errors[0].title, Some("Invalid limit".to_string()));
221 assert_eq!(
222 errors.errors[0].detail,
223 Some("Limit must be between 1 and 100".to_string())
224 );
225 }
226
227 Ok(())
228 }
229}