1use crate::v1::{Client, error, BASE_URL, standard};
2
3use serde::{Deserialize, Serialize};
4
5#[derive(Deserialize, Debug)]
8pub struct ListWebhooksResponse {
9 pub data: Vec<WebhookResource>,
11 pub links: ResponseLinks,
12}
13
14#[derive(Deserialize, Debug)]
15pub struct GetWebhookResponse {
16 pub data: WebhookResource,
18}
19
20#[derive(Deserialize, Debug)]
21pub struct CreateWebhookResponse {
22 pub data: WebhookResource,
24}
25
26#[derive(Deserialize, Debug)]
27pub struct WebhookResource {
28 pub r#type: String,
30 pub id: String,
32 pub attributes: Attributes,
33 pub relationships: Relationships,
34 pub links: WebhookResourceLinks,
35}
36
37#[derive(Deserialize, Debug)]
38#[serde(rename_all = "camelCase")]
39pub struct Attributes {
40 pub url: String,
42 pub description: Option<String>,
45 pub secret_key: Option<String>,
56 pub created_at: String,
58}
59
60#[derive(Deserialize, Debug)]
61pub struct Relationships {
62 pub logs: Logs,
63}
64
65#[derive(Deserialize, Debug)]
66pub struct Logs {
67 pub links: Option<LogsLinks>,
68}
69
70#[derive(Deserialize, Debug)]
71pub struct LogsLinks {
72 pub related: String,
74}
75
76#[derive(Deserialize, Debug)]
77pub struct WebhookResourceLinks {
78 #[serde(rename = "self")]
80 pub this: String,
81}
82
83#[derive(Deserialize, Debug)]
84pub struct ResponseLinks {
85 pub prev: Option<String>,
88 pub next: Option<String>,
91}
92
93#[derive(Deserialize, Debug)]
94pub struct PingWebhookResponse {
95 pub data: WebhookEventResource,
97}
98
99#[derive(Deserialize, Debug)]
100pub struct WebhookEventResource {
101 pub r#type: String,
103 pub id: String,
106 pub attributes: EventAttributes,
107 pub relationships: EventRelationships,
108}
109
110#[derive(Deserialize, Debug)]
111pub struct EventRelationships {
112 pub webhook: Webhook,
113 pub transaction: Option<Transaction>,
114}
115
116#[derive(Deserialize, Debug)]
117pub struct Transaction {
118 pub data: TransactionData,
119 pub links: Option<TransactionLinks>,
120}
121
122#[derive(Deserialize, Debug)]
123pub struct Webhook {
124 pub data: WebhookData,
125 pub links: Option<WebhookLinks>,
126}
127
128#[derive(Deserialize, Debug)]
129pub struct WebhookData {
130 pub r#type: String,
132 pub id: String,
134}
135
136#[derive(Deserialize, Debug)]
137pub struct WebhookLinks {
138 pub related: String,
140}
141
142#[derive(Deserialize, Debug)]
143pub struct TransactionData {
144 pub r#type: String,
146 pub id: String,
148}
149
150#[derive(Deserialize, Debug)]
151pub struct TransactionLinks {
152 pub related: String,
154}
155
156#[derive(Deserialize, Debug)]
157#[serde(rename_all = "camelCase")]
158pub struct EventAttributes {
159 pub event_type: standard::WebhookEventTypeEnum,
162 pub created_at: String,
164}
165
166
167#[derive(Deserialize, Debug)]
168pub struct ListWebhookLogsResponse {
169 pub data: Vec<WebhookDeliveryLogResource>,
171 pub links: LogsResponseLinks,
172}
173
174#[derive(Deserialize, Debug)]
175pub struct WebhookDeliveryLogResource {
176 pub r#type: String,
178 pub id: String,
180 pub attributes: DeliveryLogAttributes,
181 pub relationships: DeliveryLogRelationships,
182}
183
184#[derive(Deserialize, Debug)]
185#[serde(rename_all = "camelCase")]
186pub struct DeliveryLogRelationships {
187 pub webhook_event: WebhookEvent,
188}
189
190#[derive(Deserialize, Debug)]
191pub struct WebhookEvent {
192 pub data: WebhookEventData
193}
194
195#[derive(Deserialize, Debug)]
196pub struct WebhookEventData {
197 pub r#type: String,
199 pub id: String,
201}
202
203#[derive(Deserialize, Debug)]
204#[serde(rename_all = "camelCase")]
205pub struct DeliveryLogAttributes {
206 pub request: Request,
208 pub response: Option<Response>,
210 pub delivery_status: standard::WebhookDeliveryStatusEnum,
212 pub created_at: String,
214}
215
216#[derive(Deserialize, Debug)]
217pub struct Request {
218 pub body: String,
220}
221
222#[derive(Deserialize, Debug)]
223#[serde(rename_all = "camelCase")]
224pub struct Response {
225 pub status_code: i64,
227 pub body: String,
229}
230
231#[derive(Deserialize, Debug)]
232pub struct LogsResponseLinks {
233 pub prev: Option<String>,
236 pub next: Option<String>,
239}
240
241
242#[derive(Default)]
245pub struct ListWebhooksOptions {
246 page_size: Option<u8>,
248}
249
250impl ListWebhooksOptions {
251 pub fn page_size(&mut self, value: u8) {
253 self.page_size = Some(value);
254 }
255
256 fn add_params(&self, url: &mut reqwest::Url) {
257 let mut query = String::new();
258
259 if let Some(value) = &self.page_size {
260 if !query.is_empty() {
261 query.push('&');
262 }
263 query.push_str(&format!("page[size]={}", value));
264 }
265
266 if !query.is_empty() {
267 url.set_query(Some(&query));
268 }
269 }
270}
271
272#[derive(Default)]
273pub struct ListWebhookLogsOptions {
274 page_size: Option<u8>,
276}
277
278impl ListWebhookLogsOptions {
279 pub fn page_size(&mut self, value: u8) {
281 self.page_size = Some(value);
282 }
283
284 fn add_params(&self, url: &mut reqwest::Url) {
285 let mut query = String::new();
286
287 if let Some(value) = &self.page_size {
288 if !query.is_empty() {
289 query.push('&');
290 }
291 query.push_str(&format!("page[size]={}", value));
292 }
293
294 if !query.is_empty() {
295 url.set_query(Some(&query));
296 }
297 }
298}
299
300#[derive(Serialize)]
303pub struct CreateWebhookRequest {
304 pub data: WebhookInputResource,
306}
307
308#[derive(Serialize)]
309pub struct WebhookInputResource {
310 pub attributes: InputAttributes,
311}
312
313#[derive(Serialize)]
314pub struct InputAttributes {
315 pub url: String,
318 pub description: Option<String>,
320}
321
322impl Client {
323 pub async fn list_webhooks(
327 &self,
328 options: &ListWebhooksOptions,
329 ) -> Result<ListWebhooksResponse, error::Error> {
330 let mut url = reqwest::Url::parse(
331 &format!("{}/webhooks", BASE_URL)
332 ).map_err(error::Error::UrlParse)?;
333 options.add_params(&mut url);
334
335 let res = reqwest::Client::new()
336 .get(url)
337 .header("Authorization", self.auth_header())
338 .send()
339 .await
340 .map_err(error::Error::Request)?;
341
342 match res.status() {
343 reqwest::StatusCode::OK => {
344 let body =
345 res.text()
346 .await
347 .map_err(error::Error::BodyRead)?;
348 let webhook_response: ListWebhooksResponse =
349 serde_json::from_str(&body)
350 .map_err(error::Error::Json)?;
351
352 Ok(webhook_response)
353 },
354 _ => {
355 let body =
356 res.text()
357 .await
358 .map_err(error::Error::BodyRead)?;
359 let error: error::ErrorResponse =
360 serde_json::from_str(&body)
361 .map_err(error::Error::Json)?;
362
363 Err(error::Error::Api(error))
364 }
365 }
366 }
367
368 pub async fn get_webhook(
370 &self,
371 id: &str,
372 ) -> Result<GetWebhookResponse, error::Error> {
373 if id.is_empty() {
377 panic!("The provided webhook ID must not be empty.");
378 }
379
380 let url = reqwest::Url::parse(
381 &format!("{}/webhooks/{}", BASE_URL, id)
382 ).map_err(error::Error::UrlParse)?;
383
384 let res = reqwest::Client::new()
385 .get(url)
386 .header("Authorization", self.auth_header())
387 .send()
388 .await
389 .map_err(error::Error::Request)?;
390
391 match res.status() {
392 reqwest::StatusCode::OK => {
393 let body =
394 res.text()
395 .await
396 .map_err(error::Error::BodyRead)?;
397 let webhook_response: GetWebhookResponse =
398 serde_json::from_str(&body)
399 .map_err(error::Error::Json)?;
400
401 Ok(webhook_response)
402 },
403 _ => {
404 let body =
405 res.text()
406 .await
407 .map_err(error::Error::BodyRead)?;
408 let error: error::ErrorResponse =
409 serde_json::from_str(&body)
410 .map_err(error::Error::Json)?;
411
412 Err(error::Error::Api(error))
413 }
414 }
415 }
416
417 pub async fn create_webhook(
437 &self,
438 webhook_url: &str,
439 description: Option<String>,
440 ) -> Result<CreateWebhookResponse, error::Error> {
441 let url = reqwest::Url::parse(
442 &format!("{}/webhooks", BASE_URL)
443 ).map_err(error::Error::UrlParse)?;
444
445 let body = CreateWebhookRequest {
446 data: WebhookInputResource {
447 attributes: InputAttributes {
448 url: String::from(webhook_url),
449 description,
450 }
451 }
452 };
453
454 let body =
455 serde_json::to_string(&body)
456 .map_err(error::Error::Serialize)?;
457
458 let res = reqwest::Client::new()
459 .post(url)
460 .header("Authorization", self.auth_header())
461 .header("Content-Type", "application/json")
462 .body(body)
463 .send()
464 .await
465 .map_err(error::Error::Request)?;
466
467 match res.status() {
468 reqwest::StatusCode::CREATED => {
469 let body =
470 res.text()
471 .await
472 .map_err(error::Error::BodyRead)?;
473 let webhook_response: CreateWebhookResponse =
474 serde_json::from_str(&body)
475 .map_err(error::Error::Json)?;
476
477 Ok(webhook_response)
478 },
479 _ => {
480 let body =
481 res.text()
482 .await
483 .map_err(error::Error::BodyRead)?;
484 let error: error::ErrorResponse =
485 serde_json::from_str(&body)
486 .map_err(error::Error::Json)?;
487
488 Err(error::Error::Api(error))
489 }
490 }
491 }
492
493 pub async fn delete_webhook(&self, id: &str) -> Result<(), error::Error> {
496 let url = reqwest::Url::parse(
497 &format!("{}/webhooks/{}", BASE_URL, id)
498 ).map_err(error::Error::UrlParse)?;
499
500 let res = reqwest::Client::new()
501 .delete(url)
502 .header("Authorization", self.auth_header())
503 .send()
504 .await
505 .map_err(error::Error::Request)?;
506
507 match res.status() {
508 reqwest::StatusCode::NO_CONTENT => {
509 Ok(())
510 },
511 _ => {
512 let body =
513 res.text()
514 .await
515 .map_err(error::Error::BodyRead)?;
516 let error: error::ErrorResponse =
517 serde_json::from_str(&body)
518 .map_err(error::Error::Json)?;
519
520 Err(error::Error::Api(error))
521 }
522 }
523 }
524
525 pub async fn ping_webhook(
530 &self,
531 id: &str,
532 ) -> Result<PingWebhookResponse, error::Error> {
533 let url = reqwest::Url::parse(
534 &format!("{}/webhooks/{}/ping", BASE_URL, id)
535 ).map_err(error::Error::UrlParse)?;
536
537 let res = reqwest::Client::new()
538 .post(url)
539 .header("Authorization", self.auth_header())
540 .header("Content-Type", "application/json")
541 .header("Content-Length", "0")
542 .send()
543 .await
544 .map_err(error::Error::Request)?;
545
546 match res.status() {
547 reqwest::StatusCode::CREATED => {
548 let body =
549 res.text()
550 .await
551 .map_err(error::Error::BodyRead)?;
552 let webhook_response: PingWebhookResponse =
553 serde_json::from_str(&body)
554 .map_err(error::Error::Json)?;
555
556 Ok(webhook_response)
557 },
558 _ => {
559 let body =
560 res.text()
561 .await
562 .map_err(error::Error::BodyRead)?;
563 let error: error::ErrorResponse =
564 serde_json::from_str(&body)
565 .map_err(error::Error::Json)?;
566
567 Err(error::Error::Api(error))
568 }
569 }
570 }
571
572 pub async fn list_webhook_logs(
578 &self,
579 id: &str,
580 options: &ListWebhookLogsOptions,
581 ) -> Result<ListWebhookLogsResponse, error::Error> {
582 let mut url = reqwest::Url::parse(
583 &format!("{}/webhooks/{}/logs", BASE_URL, id)
584 ).map_err(error::Error::UrlParse)?;
585 options.add_params(&mut url);
586
587 let res = reqwest::Client::new()
588 .get(url)
589 .header("Authorization", self.auth_header())
590 .send()
591 .await
592 .map_err(error::Error::Request)?;
593
594 match res.status() {
595 reqwest::StatusCode::OK => {
596 let body =
597 res.text()
598 .await
599 .map_err(error::Error::BodyRead)?;
600 let webhook_response: ListWebhookLogsResponse =
601 serde_json::from_str(&body)
602 .map_err(error::Error::Json)?;
603
604 Ok(webhook_response)
605 },
606 _ => {
607 let body =
608 res.text()
609 .await
610 .map_err(error::Error::BodyRead)?;
611 let error: error::ErrorResponse =
612 serde_json::from_str(&body)
613 .map_err(error::Error::Json)?;
614
615 Err(error::Error::Api(error))
616 }
617 }
618 }
619}
620
621implement_pagination_v1!(ListWebhooksResponse);