use super::ReqwestError;
use crate::http::base::Query;
use crate::HttpClient;
use async_trait::async_trait;
use reqwest::{Client, Method, RequestBuilder};
use serde_json::Value;
use std::fmt::Debug;
#[derive(Debug, Clone)]
pub struct ReqwestClient {
client: Client,
}
impl Default for ReqwestClient {
fn default() -> Self {
let client = reqwest::ClientBuilder::new().build().unwrap();
Self { client }
}
}
impl ReqwestClient {
async fn send_request<D: Fn(RequestBuilder) -> RequestBuilder>(
&self,
method: Method,
url: &str,
auth_key: &str,
add_data: D,
) -> Result<String, ReqwestError> {
let mut request = self
.client
.request(method.clone(), url)
.bearer_auth(auth_key)
.header("Content-Type", "application/json");
request = add_data(request);
log::info!("Making request: {:?}", request);
let response = request.send().await?;
if response.status().is_success() {
response.text().await.map_err(Into::into)
} else {
Err(ReqwestError::StatusCode(response))
}
}
}
#[async_trait]
impl HttpClient for ReqwestClient {
type Error = ReqwestError;
async fn get(
&self,
url: &str,
api_key: &str,
query: Option<&Query>,
) -> Result<String, Self::Error> {
self.send_request(Method::GET, url, api_key, |req| {
if let Some(query) = query {
req.query(query)
} else {
req
}
})
.await
}
async fn post(&self, url: &str, api_key: &str, body: &Value) -> Result<String, Self::Error> {
self.send_request(Method::POST, url, api_key, |req| req.json(body))
.await
}
async fn put(&self, url: &str, api_key: &str, body: &Value) -> Result<String, Self::Error> {
self.send_request(Method::PUT, url, api_key, |req| req.json(body))
.await
}
async fn delete(&self, url: &str, api_key: &str, body: &Value) -> Result<String, Self::Error> {
self.send_request(Method::DELETE, url, api_key, |req| req.json(body))
.await
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn reqwest_client_cannot_get_unauthorized() {
let api_key = String::from("fake-key");
let url = "https://api.paystack.co/transaction/initialize";
let client = ReqwestClient::default();
let res = client.get(url, api_key.as_str(), None).await;
assert!(res.is_err());
if let Err(e) = res {
match e {
ReqwestError::Reqwest(_) => {
}
ReqwestError::StatusCode(code) => {
assert_eq!(code.status(), 401);
}
}
}
}
#[tokio::test]
async fn reqwest_client_can_get() {
let api_key = "fake-key";
let url = "https://api.paystack.co/";
let client = ReqwestClient::default();
let res = client.get(url, api_key, None).await;
assert!(res.is_ok());
match res {
Ok(res) => {
assert!(res.contains("true"))
}
Err(_) => {
}
}
}
}