1use super::ReqwestError;
2use crate::http::base::Query;
3use crate::HttpClient;
4use async_trait::async_trait;
5use reqwest::{Client, Method, RequestBuilder};
6use serde_json::Value;
7use std::fmt::Debug;
8
9#[derive(Debug, Clone)]
10pub struct ReqwestClient {
11 client: Client,
13}
14
15impl Default for ReqwestClient {
16 fn default() -> Self {
17 let client = reqwest::ClientBuilder::new().build().unwrap();
18
19 Self { client }
20 }
21}
22
23impl ReqwestClient {
24 async fn send_request<D: Fn(RequestBuilder) -> RequestBuilder>(
25 &self,
26 method: Method,
27 url: &str,
28 auth_key: &str,
29 add_data: D,
30 ) -> Result<String, ReqwestError> {
31 let mut request = self
33 .client
34 .request(method.clone(), url)
35 .bearer_auth(auth_key)
36 .header("Content-Type", "application/json");
37
38 request = add_data(request);
40
41 log::info!("Making request: {:?}", request);
43 let response = request.send().await?;
44
45 if response.status().is_success() {
47 response.text().await.map_err(Into::into)
48 } else {
49 Err(ReqwestError::StatusCode(response))
50 }
51 }
52}
53
54#[async_trait]
55impl HttpClient for ReqwestClient {
56 type Error = ReqwestError;
57
58 async fn get(
59 &self,
60 url: &str,
61 api_key: &str,
62 query: Option<&Query>,
63 ) -> Result<String, Self::Error> {
64 self.send_request(Method::GET, url, api_key, |req| {
65 if let Some(query) = query {
66 req.query(query)
67 } else {
68 req
69 }
70 })
71 .await
72 }
73
74 async fn post(&self, url: &str, api_key: &str, body: &Value) -> Result<String, Self::Error> {
75 self.send_request(Method::POST, url, api_key, |req| req.json(body))
76 .await
77 }
78
79 async fn put(&self, url: &str, api_key: &str, body: &Value) -> Result<String, Self::Error> {
80 self.send_request(Method::PUT, url, api_key, |req| req.json(body))
81 .await
82 }
83
84 async fn delete(&self, url: &str, api_key: &str, body: &Value) -> Result<String, Self::Error> {
85 self.send_request(Method::DELETE, url, api_key, |req| req.json(body))
86 .await
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93 #[tokio::test]
94 async fn reqwest_client_cannot_get_unauthorized() {
95 let api_key = String::from("fake-key");
97 let url = "https://api.paystack.co/transaction/initialize";
98
99 let client = ReqwestClient::default();
101 let res = client.get(url, api_key.as_str(), None).await;
102
103 assert!(res.is_err());
106 if let Err(e) = res {
107 match e {
108 ReqwestError::Reqwest(_) => {
109 }
111 ReqwestError::StatusCode(code) => {
112 assert_eq!(code.status(), 401);
113 }
114 }
115 }
116 }
117
118 #[tokio::test]
119 async fn reqwest_client_can_get() {
120 let api_key = "fake-key";
122 let url = "https://api.paystack.co/";
123
124 let client = ReqwestClient::default();
126 let res = client.get(url, api_key, None).await;
127
128 assert!(res.is_ok());
130 match res {
131 Ok(res) => {
132 assert!(res.contains("true"))
133 }
134 Err(_) => {
135 }
137 }
138 }
139}