mangadex_api/v5/scanlation_group/
post.rs1use derive_builder::Builder;
42use serde::Serialize;
43use url::Url;
44
45use crate::HttpClientRef;
46use mangadex_api_schema::v5::GroupData;
47use mangadex_api_types::MangaDexDuration;
48
49#[cfg_attr(
50 feature = "deserializable-endpoint",
51 derive(serde::Deserialize, getset::Getters, getset::Setters)
52)]
53#[derive(Debug, Serialize, Clone, Builder)]
54#[serde(rename_all = "camelCase")]
55#[builder(
56 setter(into, strip_option),
57 build_fn(error = "mangadex_api_types::error::BuilderError")
58)]
59#[cfg_attr(feature = "non_exhaustive", non_exhaustive)]
60pub struct CreateGroup {
61 #[doc(hidden)]
63 #[serde(skip)]
64 #[builder(pattern = "immutable")]
65 #[cfg_attr(feature = "deserializable-endpoint", getset(set = "pub", get = "pub"))]
66 pub http_client: HttpClientRef,
67
68 pub name: String,
69 #[serde(skip_serializing_if = "Option::is_none")]
71 #[builder(default)]
72 pub website: Option<String>,
73 #[serde(skip_serializing_if = "Option::is_none")]
75 #[builder(default)]
76 pub irc_server: Option<String>,
77 #[serde(skip_serializing_if = "Option::is_none")]
79 #[builder(default)]
80 pub irc_channel: Option<String>,
81 #[serde(skip_serializing_if = "Option::is_none")]
83 #[builder(default)]
84 pub discord: Option<String>,
85 #[serde(skip_serializing_if = "Option::is_none")]
87 #[builder(default)]
88 pub contact_email: Option<String>,
89 #[serde(skip_serializing_if = "Option::is_none")]
91 #[builder(default)]
92 pub description: Option<String>,
93 #[serde(skip_serializing_if = "Option::is_none")]
95 #[builder(default)]
96 pub twitter: Option<Url>,
97 #[serde(skip_serializing_if = "Option::is_none")]
101 #[builder(default)]
102 pub manga_updates: Option<Url>,
103 #[serde(skip_serializing_if = "Option::is_none")]
104 #[builder(default)]
105 pub inactive: Option<bool>,
106 #[serde(skip_serializing_if = "Option::is_none")]
108 #[builder(default)]
109 pub publish_delay: Option<MangaDexDuration>,
110}
111
112endpoint! {
113 POST ("/group"),
114 #[body auth] CreateGroup,
115 #[rate_limited] GroupData,
116 CreateGroupBuilder
117}
118
119#[cfg(test)]
120mod tests {
121 use fake::faker::lorem::en::Sentence;
122 use fake::faker::name::en::Name;
123 use fake::Fake;
124 use serde_json::json;
125 use time::OffsetDateTime;
126 use url::Url;
127 use uuid::Uuid;
128 use wiremock::matchers::{body_json, header, method, path};
129 use wiremock::{Mock, MockServer, ResponseTemplate};
130
131 use crate::v5::AuthTokens;
132 use crate::{HttpClient, MangaDexClient};
133 use mangadex_api_types::MangaDexDateTime;
134
135 #[tokio::test]
136 async fn create_scanlation_group_fires_a_request_to_base_url() -> anyhow::Result<()> {
137 let mock_server = MockServer::start().await;
138 let http_client = HttpClient::builder()
139 .base_url(Url::parse(&mock_server.uri())?)
140 .auth_tokens(AuthTokens {
141 session: "sessiontoken".to_string(),
142 refresh: "refreshtoken".to_string(),
143 })
144 .build()?;
145 let mangadex_client = MangaDexClient::new_with_http_client(http_client);
146
147 let group_id = Uuid::new_v4();
148 let group_name: String = Name().fake();
149 let group_description: String = Sentence(1..2).fake();
150
151 let datetime = MangaDexDateTime::new(&OffsetDateTime::now_utc());
152
153 let expected_body = json!({
154 "name": group_name,
155 "description": group_description
156 });
157 let response_body = json!({
158 "result": "ok",
159 "response": "entity",
160 "data": {
161 "id": group_id,
162 "type": "scanlation_group",
163 "attributes": {
164 "name": group_name,
165 "altNames": [],
166 "website": null,
167 "ircServer": null,
168 "ircChannel": null,
169 "discord": null,
170 "contactEmail": null,
171 "description": null,
172 "twitter": null,
173 "focusedLanguages": [],
174 "locked": false,
175 "official": false,
176 "verified": false,
177 "inactive": false,
178 "publishDelay": "P6WT5M",
179 "version": 1,
180 "createdAt": datetime.to_string(),
181 "updatedAt": datetime.to_string(),
182 },
183 "relationships": []
184 }
185 });
186
187 Mock::given(method("POST"))
188 .and(path(r"/group"))
189 .and(header("Authorization", "Bearer sessiontoken"))
190 .and(header("Content-Type", "application/json"))
191 .and(body_json(expected_body))
192 .respond_with(
193 ResponseTemplate::new(200)
194 .insert_header("x-ratelimit-retry-after", "1698723860")
195 .insert_header("x-ratelimit-limit", "40")
196 .insert_header("x-ratelimit-remaining", "39")
197 .set_body_json(response_body),
198 )
199 .expect(1)
200 .mount(&mock_server)
201 .await;
202
203 let _ = mangadex_client
204 .scanlation_group()
205 .post()
206 .name(group_name.as_str())
207 .description(group_description.as_str())
208 .send()
209 .await?;
210
211 Ok(())
212 }
213}