use super::HttpClient;
use crate::error::{BotError, Result};
use crate::token::Token;
use reqwest::{
Method,
header::{HeaderMap, HeaderValue},
};
use serde::Serialize;
use tracing::debug;
impl HttpClient {
pub async fn get<Q>(
&self,
token: &Token,
path: &str,
query: Option<&Q>,
) -> Result<serde_json::Value>
where
Q: Serialize + ?Sized,
{
self.request(Method::GET, token, path, query, None::<&()>)
.await
}
pub async fn post<Q, B>(
&self,
token: &Token,
path: &str,
query: Option<&Q>,
body: Option<&B>,
) -> Result<serde_json::Value>
where
Q: Serialize + ?Sized,
B: Serialize + ?Sized,
{
self.request(Method::POST, token, path, query, body).await
}
pub(crate) async fn request_json_url<Q, B>(
&self,
token: &Token,
method: Method,
url: &str,
query: Option<&Q>,
body: Option<&B>,
) -> Result<serde_json::Value>
where
Q: Serialize + ?Sized,
B: Serialize + ?Sized,
{
self.request_json_url_with_headers(method, token, url, query, body, HeaderMap::new())
.await
}
pub async fn put<Q, B>(
&self,
token: &Token,
path: &str,
query: Option<&Q>,
body: Option<&B>,
) -> Result<serde_json::Value>
where
Q: Serialize + ?Sized,
B: Serialize + ?Sized,
{
self.request(Method::PUT, token, path, query, body).await
}
pub async fn put_with_headers<Q, B>(
&self,
token: &Token,
path: &str,
query: Option<&Q>,
body: Option<&B>,
headers: HeaderMap,
) -> Result<serde_json::Value>
where
Q: Serialize + ?Sized,
B: Serialize + ?Sized,
{
self.request_with_headers(Method::PUT, token, path, query, body, headers)
.await
}
pub async fn put_raw_with_headers<Q>(
&self,
token: &Token,
path: &str,
query: Option<&Q>,
body: impl Into<String>,
headers: HeaderMap,
) -> Result<serde_json::Value>
where
Q: Serialize + ?Sized,
{
self.request_raw_with_headers(Method::PUT, token, path, query, body, headers)
.await
}
pub async fn delete<Q>(
&self,
token: &Token,
path: &str,
query: Option<&Q>,
) -> Result<serde_json::Value>
where
Q: Serialize + ?Sized,
{
self.request(Method::DELETE, token, path, query, None::<&()>)
.await
}
pub async fn delete_with_body<Q, B>(
&self,
token: &Token,
path: &str,
query: Option<&Q>,
body: Option<&B>,
) -> Result<serde_json::Value>
where
Q: Serialize + ?Sized,
B: Serialize + ?Sized,
{
self.request(Method::DELETE, token, path, query, body).await
}
pub async fn patch<Q, B>(
&self,
token: &Token,
path: &str,
query: Option<&Q>,
body: Option<&B>,
) -> Result<serde_json::Value>
where
Q: Serialize + ?Sized,
B: Serialize + ?Sized,
{
self.request(Method::PATCH, token, path, query, body).await
}
pub async fn transport<B>(
&self,
token: &Token,
method: Method,
url: &str,
body: Option<&B>,
) -> Result<Vec<u8>>
where
B: Serialize + ?Sized,
{
let mut headers = self.authorized_headers(token, HeaderMap::new()).await?;
if body.is_some() {
headers.insert("Content-Type", "application/json".parse().unwrap());
}
let mut context = crate::openapi::FilterContext::request(method.clone(), url, headers);
crate::openapi::DoReqFilterChains(&mut context)?;
let request_headers = context.request_headers.clone();
let mut request = self
.client
.request(method.clone(), url)
.headers(request_headers.clone());
if let Some(body) = body {
request = request.json(body);
}
let response = request.send().await.map_err(BotError::Http)?;
self.handle_bytes_response(response, method, url, request_headers)
.await
}
pub(crate) async fn request<Q, B>(
&self,
method: Method,
token: &Token,
path: &str,
query: Option<&Q>,
body: Option<&B>,
) -> Result<serde_json::Value>
where
Q: Serialize + ?Sized,
B: Serialize + ?Sized,
{
self.request_with_headers(method, token, path, query, body, HeaderMap::new())
.await
}
pub(crate) async fn request_with_headers<Q, B>(
&self,
method: Method,
token: &Token,
path: &str,
query: Option<&Q>,
body: Option<&B>,
headers: HeaderMap,
) -> Result<serde_json::Value>
where
Q: Serialize + ?Sized,
B: Serialize + ?Sized,
{
let url = format!("{}{}", self.base_url, path);
self.request_json_url_with_headers(method, token, &url, query, body, headers)
.await
}
pub(crate) async fn request_json_url_with_headers<Q, B>(
&self,
method: Method,
token: &Token,
url: &str,
query: Option<&Q>,
body: Option<&B>,
headers: HeaderMap,
) -> Result<serde_json::Value>
where
Q: Serialize + ?Sized,
B: Serialize + ?Sized,
{
debug!("Making {} request to: {}", method, url);
let mut headers = self.authorized_headers(token, headers).await?;
if body.is_some() {
headers.insert("Content-Type", "application/json".parse().unwrap());
}
let mut context = crate::openapi::FilterContext::request(method.clone(), url, headers);
crate::openapi::DoReqFilterChains(&mut context)?;
let request_headers = context.request_headers.clone();
let mut request = self
.client
.request(method.clone(), url)
.headers(request_headers.clone());
if let Some(q) = query {
request = request.query(q);
}
if let Some(b) = body {
request = request.json(b);
}
let response = request.send().await.map_err(BotError::Http)?;
self.handle_response(response, method, url, request_headers)
.await
}
pub(crate) async fn request_raw_with_headers<Q>(
&self,
method: Method,
token: &Token,
path: &str,
query: Option<&Q>,
body: impl Into<String>,
headers: HeaderMap,
) -> Result<serde_json::Value>
where
Q: Serialize + ?Sized,
{
let url = format!("{}{}", self.base_url, path);
debug!("Making {} request to: {}", method, url);
let mut headers = self.authorized_headers(token, headers).await?;
headers.insert("Content-Type", "application/json".parse().unwrap());
let mut context = crate::openapi::FilterContext::request(method.clone(), &url, headers);
crate::openapi::DoReqFilterChains(&mut context)?;
let request_headers = context.request_headers.clone();
let mut request = self
.client
.request(method.clone(), &url)
.headers(request_headers.clone());
if let Some(q) = query {
request = request.query(q);
}
let response = request
.body(body.into())
.send()
.await
.map_err(BotError::Http)?;
self.handle_response(response, method, &url, request_headers)
.await
}
pub(crate) async fn authorized_headers(
&self,
token: &Token,
mut headers: HeaderMap,
) -> Result<HeaderMap> {
headers.insert(
"Authorization",
token.authorization_header().await?.parse().map_err(|err| {
BotError::internal(format!("invalid authorization header: {err}"))
})?,
);
let app_id = self
.union_app_id
.as_deref()
.unwrap_or_else(|| token.app_id());
if !app_id.is_empty() {
headers.insert(
"X-Union-Appid",
HeaderValue::from_str(app_id).map_err(|err| {
BotError::internal(format!("invalid X-Union-Appid header: {err}"))
})?,
);
}
Ok(headers)
}
}